Commit 5816fb44b8ce24e8bb58cb30792e1c763d6cb163
1 parent
16a9bb3f
name/number trees: insertAfter
Showing
9 changed files
with
82 additions
and
1 deletions
include/qpdf/QPDFNameTreeObjectHelper.hh
| @@ -112,6 +112,19 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper | @@ -112,6 +112,19 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper | ||
| 112 | return ! operator==(other); | 112 | return ! operator==(other); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | + // DANGER: this method can create inconsistent trees if not | ||
| 116 | + // used properly! Insert a new item immediately after the | ||
| 117 | + // current iterator and increment so that it points to the new | ||
| 118 | + // item. If the current iterator is end(), insert at the | ||
| 119 | + // beginning. This method does not check for proper ordering, | ||
| 120 | + // so if you use it, you must ensure that the item you are | ||
| 121 | + // inserting belongs where you are putting it. The reason for | ||
| 122 | + // this method is that it is more efficient than insert() and | ||
| 123 | + // can be used safely when you are creating a new tree and | ||
| 124 | + // inserting items in sorted order. | ||
| 125 | + QPDF_DLL | ||
| 126 | + void insertAfter(std::string const& key, QPDFObjectHandle value); | ||
| 127 | + | ||
| 115 | private: | 128 | private: |
| 116 | iterator(std::shared_ptr<NNTreeIterator> const&); | 129 | iterator(std::shared_ptr<NNTreeIterator> const&); |
| 117 | std::shared_ptr<NNTreeIterator> impl; | 130 | std::shared_ptr<NNTreeIterator> impl; |
include/qpdf/QPDFNumberTreeObjectHelper.hh
| @@ -131,6 +131,19 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper | @@ -131,6 +131,19 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper | ||
| 131 | return ! operator==(other); | 131 | return ! operator==(other); |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | + // DANGER: this method can create inconsistent trees if not | ||
| 135 | + // used properly! Insert a new item immediately after the | ||
| 136 | + // current iterator and increment so that it points to the new | ||
| 137 | + // item. If the current iterator is end(), insert at the | ||
| 138 | + // beginning. This method does not check for proper ordering, | ||
| 139 | + // so if you use it, you must ensure that the item you are | ||
| 140 | + // inserting belongs where you are putting it. The reason for | ||
| 141 | + // this method is that it is more efficient than insert() and | ||
| 142 | + // can be used safely when you are creating a new tree and | ||
| 143 | + // inserting items in sorted order. | ||
| 144 | + QPDF_DLL | ||
| 145 | + void insertAfter(numtree_number key, QPDFObjectHandle value); | ||
| 146 | + | ||
| 134 | private: | 147 | private: |
| 135 | iterator(std::shared_ptr<NNTreeIterator> const&); | 148 | iterator(std::shared_ptr<NNTreeIterator> const&); |
| 136 | std::shared_ptr<NNTreeIterator> impl; | 149 | std::shared_ptr<NNTreeIterator> impl; |
libqpdf/NNTree.cc
| @@ -444,6 +444,14 @@ NNTreeIterator::lastPathElement() | @@ -444,6 +444,14 @@ NNTreeIterator::lastPathElement() | ||
| 444 | void | 444 | void |
| 445 | NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value) | 445 | NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value) |
| 446 | { | 446 | { |
| 447 | + if (! valid()) | ||
| 448 | + { | ||
| 449 | + QTC::TC("qpdf", "NNTree insertAfter inserts first"); | ||
| 450 | + impl.insertFirst(key, value); | ||
| 451 | + deepen(impl.oh, true, false); | ||
| 452 | + return; | ||
| 453 | + } | ||
| 454 | + | ||
| 447 | auto items = this->node.getKey(impl.details.itemsKey()); | 455 | auto items = this->node.getKey(impl.details.itemsKey()); |
| 448 | if (! items.isArray()) | 456 | if (! items.isArray()) |
| 449 | { | 457 | { |
| @@ -457,6 +465,7 @@ NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value) | @@ -457,6 +465,7 @@ NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value) | ||
| 457 | items.insertItem(this->item_number + 3, value); | 465 | items.insertItem(this->item_number + 3, value); |
| 458 | resetLimits(this->node, lastPathElement()); | 466 | resetLimits(this->node, lastPathElement()); |
| 459 | split(this->node, lastPathElement()); | 467 | split(this->node, lastPathElement()); |
| 468 | + increment(false); | ||
| 460 | } | 469 | } |
| 461 | 470 | ||
| 462 | NNTreeIterator& | 471 | NNTreeIterator& |
| @@ -968,7 +977,6 @@ NNTreeImpl::insert(QPDFObjectHandle key, QPDFObjectHandle value) | @@ -968,7 +977,6 @@ NNTreeImpl::insert(QPDFObjectHandle key, QPDFObjectHandle value) | ||
| 968 | { | 977 | { |
| 969 | QTC::TC("qpdf", "NNTree insert inserts after"); | 978 | QTC::TC("qpdf", "NNTree insert inserts after"); |
| 970 | iter.insertAfter(key, value); | 979 | iter.insertAfter(key, value); |
| 971 | - ++iter; | ||
| 972 | } | 980 | } |
| 973 | return iter; | 981 | return iter; |
| 974 | } | 982 | } |
libqpdf/QPDFNameTreeObjectHelper.cc
| @@ -102,6 +102,13 @@ QPDFNameTreeObjectHelper::iterator::operator==(iterator const& other) const | @@ -102,6 +102,13 @@ QPDFNameTreeObjectHelper::iterator::operator==(iterator const& other) const | ||
| 102 | return *(impl) == *(other.impl); | 102 | return *(impl) == *(other.impl); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | +void | ||
| 106 | +QPDFNameTreeObjectHelper::iterator::insertAfter( | ||
| 107 | + std::string const& key, QPDFObjectHandle value) | ||
| 108 | +{ | ||
| 109 | + impl->insertAfter(QPDFObjectHandle::newUnicodeString(key), value); | ||
| 110 | +} | ||
| 111 | + | ||
| 105 | QPDFNameTreeObjectHelper::iterator | 112 | QPDFNameTreeObjectHelper::iterator |
| 106 | QPDFNameTreeObjectHelper::begin() const | 113 | QPDFNameTreeObjectHelper::begin() const |
| 107 | { | 114 | { |
libqpdf/QPDFNumberTreeObjectHelper.cc
| @@ -98,6 +98,13 @@ QPDFNumberTreeObjectHelper::iterator::operator==(iterator const& other) const | @@ -98,6 +98,13 @@ QPDFNumberTreeObjectHelper::iterator::operator==(iterator const& other) const | ||
| 98 | return *(impl) == *(other.impl); | 98 | return *(impl) == *(other.impl); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | +void | ||
| 102 | +QPDFNumberTreeObjectHelper::iterator::insertAfter( | ||
| 103 | + numtree_number key, QPDFObjectHandle value) | ||
| 104 | +{ | ||
| 105 | + impl->insertAfter(QPDFObjectHandle::newInteger(key), value); | ||
| 106 | +} | ||
| 107 | + | ||
| 101 | QPDFNumberTreeObjectHelper::iterator | 108 | QPDFNumberTreeObjectHelper::iterator |
| 102 | QPDFNumberTreeObjectHelper::begin() const | 109 | QPDFNumberTreeObjectHelper::begin() const |
| 103 | { | 110 | { |
qpdf/qpdf.testcov
| @@ -552,3 +552,4 @@ NNTree bad node during find 0 | @@ -552,3 +552,4 @@ NNTree bad node during find 0 | ||
| 552 | NNTree node is not a dictionary 0 | 552 | NNTree node is not a dictionary 0 |
| 553 | NNTree limits didn't change 0 | 553 | NNTree limits didn't change 0 |
| 554 | NNTree increment end() 0 | 554 | NNTree increment end() 0 |
| 555 | +NNTree insertAfter inserts first 0 |
qpdf/qtest/qpdf/name-tree.out
| @@ -16,6 +16,9 @@ | @@ -16,6 +16,9 @@ | ||
| 16 | 20 twenty -> twenty. | 16 | 20 twenty -> twenty. |
| 17 | 22 twenty-two -> twenty-two! | 17 | 22 twenty-two -> twenty-two! |
| 18 | 29 twenty-nine -> twenty-nine! | 18 | 29 twenty-nine -> twenty-nine! |
| 19 | +insertAfter | ||
| 20 | +3 (3!) | ||
| 21 | +4 (4!) | ||
| 19 | /Empty1 | 22 | /Empty1 |
| 20 | /Empty2 | 23 | /Empty2 |
| 21 | /Bad1: deprecated API | 24 | /Bad1: deprecated API |
qpdf/qtest/qpdf/number-tree.out
| @@ -26,6 +26,9 @@ | @@ -26,6 +26,9 @@ | ||
| 26 | 22 twenty-two | 26 | 22 twenty-two |
| 27 | 23 twenty-three | 27 | 23 twenty-three |
| 28 | 29 twenty-nine | 28 | 29 twenty-nine |
| 29 | +insertAfter | ||
| 30 | +3 (3!) | ||
| 31 | +4 (4!) | ||
| 29 | /Bad1: deprecated API | 32 | /Bad1: deprecated API |
| 30 | /Bad1 | 33 | /Bad1 |
| 31 | WARNING: number-tree.pdf (Name/Number tree node (object 14)): name/number tree node has neither non-empty /Nums nor /Kids | 34 | WARNING: number-tree.pdf (Name/Number tree node (object 14)): name/number tree node has neither non-empty /Nums nor /Kids |
qpdf/test_driver.cc
| @@ -1802,6 +1802,19 @@ void runtest(int n, char const* filename1, char const* arg2) | @@ -1802,6 +1802,19 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 1802 | --iter1; | 1802 | --iter1; |
| 1803 | assert((*iter1).first == 2); | 1803 | assert((*iter1).first == 2); |
| 1804 | 1804 | ||
| 1805 | + std::cout << "insertAfter" << std::endl; | ||
| 1806 | + auto new2 = QPDFNumberTreeObjectHelper::newEmpty(pdf); | ||
| 1807 | + auto iter2 = new2.begin(); | ||
| 1808 | + assert(iter2 == new2.end()); | ||
| 1809 | + iter2.insertAfter(3, QPDFObjectHandle::newString("3!")); | ||
| 1810 | + assert((*iter2).first == 3); | ||
| 1811 | + iter2.insertAfter(4, QPDFObjectHandle::newString("4!")); | ||
| 1812 | + assert((*iter2).first == 4); | ||
| 1813 | + for (auto i: new2) | ||
| 1814 | + { | ||
| 1815 | + std::cout << i.first << " " << i.second.unparse() << std::endl; | ||
| 1816 | + } | ||
| 1817 | + | ||
| 1805 | // Exercise deprecated API until qpdf 11 | 1818 | // Exercise deprecated API until qpdf 11 |
| 1806 | std::cout << "/Bad1: deprecated API" << std::endl; | 1819 | std::cout << "/Bad1: deprecated API" << std::endl; |
| 1807 | auto bad1 = QPDFNumberTreeObjectHelper( | 1820 | auto bad1 = QPDFNumberTreeObjectHelper( |
| @@ -1961,6 +1974,19 @@ void runtest(int n, char const* filename1, char const* arg2) | @@ -1961,6 +1974,19 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 1961 | --iter1; | 1974 | --iter1; |
| 1962 | assert((*iter1).first == "2"); | 1975 | assert((*iter1).first == "2"); |
| 1963 | 1976 | ||
| 1977 | + std::cout << "insertAfter" << std::endl; | ||
| 1978 | + auto new2 = QPDFNameTreeObjectHelper::newEmpty(pdf); | ||
| 1979 | + auto iter2 = new2.begin(); | ||
| 1980 | + assert(iter2 == new2.end()); | ||
| 1981 | + iter2.insertAfter("3", QPDFObjectHandle::newString("3!")); | ||
| 1982 | + assert((*iter2).first == "3"); | ||
| 1983 | + iter2.insertAfter("4", QPDFObjectHandle::newString("4!")); | ||
| 1984 | + assert((*iter2).first == "4"); | ||
| 1985 | + for (auto i: new2) | ||
| 1986 | + { | ||
| 1987 | + std::cout << i.first << " " << i.second.unparse() << std::endl; | ||
| 1988 | + } | ||
| 1989 | + | ||
| 1964 | std::vector<std::string> empties = {"/Empty1", "/Empty2"}; | 1990 | std::vector<std::string> empties = {"/Empty1", "/Empty2"}; |
| 1965 | for (auto const& k: empties) | 1991 | for (auto const& k: empties) |
| 1966 | { | 1992 | { |