name/number trees: insertAfter

This commit is contained in:
Jay Berkenbilt 2021-01-24 04:16:48 -05:00
parent 16a9bb3f6f
commit 5816fb44b8
9 changed files with 82 additions and 1 deletions

View File

@ -112,6 +112,19 @@ class QPDFNameTreeObjectHelper: public QPDFObjectHelper
return ! operator==(other);
}
// DANGER: this method can create inconsistent trees if not
// used properly! Insert a new item immediately after the
// current iterator and increment so that it points to the new
// item. If the current iterator is end(), insert at the
// beginning. This method does not check for proper ordering,
// so if you use it, you must ensure that the item you are
// inserting belongs where you are putting it. The reason for
// this method is that it is more efficient than insert() and
// can be used safely when you are creating a new tree and
// inserting items in sorted order.
QPDF_DLL
void insertAfter(std::string const& key, QPDFObjectHandle value);
private:
iterator(std::shared_ptr<NNTreeIterator> const&);
std::shared_ptr<NNTreeIterator> impl;

View File

@ -131,6 +131,19 @@ class QPDFNumberTreeObjectHelper: public QPDFObjectHelper
return ! operator==(other);
}
// DANGER: this method can create inconsistent trees if not
// used properly! Insert a new item immediately after the
// current iterator and increment so that it points to the new
// item. If the current iterator is end(), insert at the
// beginning. This method does not check for proper ordering,
// so if you use it, you must ensure that the item you are
// inserting belongs where you are putting it. The reason for
// this method is that it is more efficient than insert() and
// can be used safely when you are creating a new tree and
// inserting items in sorted order.
QPDF_DLL
void insertAfter(numtree_number key, QPDFObjectHandle value);
private:
iterator(std::shared_ptr<NNTreeIterator> const&);
std::shared_ptr<NNTreeIterator> impl;

View File

@ -444,6 +444,14 @@ NNTreeIterator::lastPathElement()
void
NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value)
{
if (! valid())
{
QTC::TC("qpdf", "NNTree insertAfter inserts first");
impl.insertFirst(key, value);
deepen(impl.oh, true, false);
return;
}
auto items = this->node.getKey(impl.details.itemsKey());
if (! items.isArray())
{
@ -457,6 +465,7 @@ NNTreeIterator::insertAfter(QPDFObjectHandle key, QPDFObjectHandle value)
items.insertItem(this->item_number + 3, value);
resetLimits(this->node, lastPathElement());
split(this->node, lastPathElement());
increment(false);
}
NNTreeIterator&
@ -968,7 +977,6 @@ NNTreeImpl::insert(QPDFObjectHandle key, QPDFObjectHandle value)
{
QTC::TC("qpdf", "NNTree insert inserts after");
iter.insertAfter(key, value);
++iter;
}
return iter;
}

View File

@ -102,6 +102,13 @@ QPDFNameTreeObjectHelper::iterator::operator==(iterator const& other) const
return *(impl) == *(other.impl);
}
void
QPDFNameTreeObjectHelper::iterator::insertAfter(
std::string const& key, QPDFObjectHandle value)
{
impl->insertAfter(QPDFObjectHandle::newUnicodeString(key), value);
}
QPDFNameTreeObjectHelper::iterator
QPDFNameTreeObjectHelper::begin() const
{

View File

@ -98,6 +98,13 @@ QPDFNumberTreeObjectHelper::iterator::operator==(iterator const& other) const
return *(impl) == *(other.impl);
}
void
QPDFNumberTreeObjectHelper::iterator::insertAfter(
numtree_number key, QPDFObjectHandle value)
{
impl->insertAfter(QPDFObjectHandle::newInteger(key), value);
}
QPDFNumberTreeObjectHelper::iterator
QPDFNumberTreeObjectHelper::begin() const
{

View File

@ -552,3 +552,4 @@ NNTree bad node during find 0
NNTree node is not a dictionary 0
NNTree limits didn't change 0
NNTree increment end() 0
NNTree insertAfter inserts first 0

View File

@ -16,6 +16,9 @@
20 twenty -> twenty.
22 twenty-two -> twenty-two!
29 twenty-nine -> twenty-nine!
insertAfter
3 (3!)
4 (4!)
/Empty1
/Empty2
/Bad1: deprecated API

View File

@ -26,6 +26,9 @@
22 twenty-two
23 twenty-three
29 twenty-nine
insertAfter
3 (3!)
4 (4!)
/Bad1: deprecated API
/Bad1
WARNING: number-tree.pdf (Name/Number tree node (object 14)): name/number tree node has neither non-empty /Nums nor /Kids

View File

@ -1802,6 +1802,19 @@ void runtest(int n, char const* filename1, char const* arg2)
--iter1;
assert((*iter1).first == 2);
std::cout << "insertAfter" << std::endl;
auto new2 = QPDFNumberTreeObjectHelper::newEmpty(pdf);
auto iter2 = new2.begin();
assert(iter2 == new2.end());
iter2.insertAfter(3, QPDFObjectHandle::newString("3!"));
assert((*iter2).first == 3);
iter2.insertAfter(4, QPDFObjectHandle::newString("4!"));
assert((*iter2).first == 4);
for (auto i: new2)
{
std::cout << i.first << " " << i.second.unparse() << std::endl;
}
// Exercise deprecated API until qpdf 11
std::cout << "/Bad1: deprecated API" << std::endl;
auto bad1 = QPDFNumberTreeObjectHelper(
@ -1961,6 +1974,19 @@ void runtest(int n, char const* filename1, char const* arg2)
--iter1;
assert((*iter1).first == "2");
std::cout << "insertAfter" << std::endl;
auto new2 = QPDFNameTreeObjectHelper::newEmpty(pdf);
auto iter2 = new2.begin();
assert(iter2 == new2.end());
iter2.insertAfter("3", QPDFObjectHandle::newString("3!"));
assert((*iter2).first == "3");
iter2.insertAfter("4", QPDFObjectHandle::newString("4!"));
assert((*iter2).first == "4");
for (auto i: new2)
{
std::cout << i.first << " " << i.second.unparse() << std::endl;
}
std::vector<std::string> empties = {"/Empty1", "/Empty2"};
for (auto const& k: empties)
{