Commit b00647d5e2be529b6295f07332cad949a1b8d032

Authored by m-holger
1 parent 4490c23f

Add `validate()` to `NNTree` helpers and implementations

- Introduced a `validate()` method in `QPDFNameTreeObjectHelper`, `QPDFNumberTreeObjectHelper`, and `NNTreeImpl`.
- Ensure proper validation of tree keys and values, detecting invalid entries, unsorted keys, and inconsistencies.
- Added support for auto-repair in case of validation errors.
include/qpdf/QPDFNameTreeObjectHelper.hh
... ... @@ -50,7 +50,13 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper
50 50 QPDFObjectHandle,
51 51 QPDF&,
52 52 std::function<bool(QPDFObjectHandle const&)> value_validator,
53   - bool auto_repair = true);
  53 + bool auto_repair);
  54 +
  55 + // Validate the name tree. Returns true if the tree is valid.
  56 + //
  57 + // If the tree is not valid and auto_repair is true, attempt to repair the tree.
  58 + QPDF_DLL
  59 + bool validate(bool repair = true);
54 60  
55 61 // Create an empty name tree
56 62 QPDF_DLL
... ...
include/qpdf/QPDFNumberTreeObjectHelper.hh
... ... @@ -48,7 +48,7 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper
48 48 QPDFObjectHandle,
49 49 QPDF&,
50 50 std::function<bool(QPDFObjectHandle const&)> value_validator,
51   - bool auto_repair = true);
  51 + bool auto_repair);
52 52  
53 53 QPDF_DLL
54 54 ~QPDFNumberTreeObjectHelper() override;
... ... @@ -59,6 +59,12 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper
59 59  
60 60 typedef long long int numtree_number;
61 61  
  62 + // Validate the name tree. Returns true if the tree is valid.
  63 + //
  64 + // If the tree is not valid and auto_repair is true, attempt to repair the tree.
  65 + QPDF_DLL
  66 + bool validate(bool repair = true);
  67 +
62 68 // Return overall minimum and maximum indices
63 69 QPDF_DLL
64 70 numtree_number getMin();
... ...
libqpdf/NNTree.cc
... ... @@ -891,3 +891,33 @@ NNTreeImpl::remove(QPDFObjectHandle const&amp; key, QPDFObjectHandle* value)
891 891 iter.remove();
892 892 return true;
893 893 }
  894 +
  895 +bool
  896 +NNTreeImpl::validate(bool a_repair)
  897 +{
  898 + bool first = true;
  899 + QPDFObjectHandle last_key;
  900 + try {
  901 + for (auto const& [key, value]: *this) {
  902 + if (!details.keyValid(key)) {
  903 + error(oh, "invalid key in validate");
  904 + }
  905 + if (!value_valid(value)) {
  906 + error(oh, "invalid value in validate");
  907 + }
  908 + if (first) {
  909 + first = false;
  910 + } else if (last_key && details.compareKeys(last_key, key) != -1) {
  911 + error(oh, "keys are not sorted in validate");
  912 + }
  913 + last_key = key;
  914 + }
  915 + } catch (QPDFExc& e) {
  916 + if (a_repair) {
  917 + warn(oh, std::string("attempting to repair after error: ") + e.what());
  918 + repair();
  919 + }
  920 + return false;
  921 + }
  922 + return true;
  923 +}
... ...
libqpdf/QPDFNameTreeObjectHelper.cc
... ... @@ -215,3 +215,9 @@ QPDFNameTreeObjectHelper::getAsMap() const
215 215 result.insert(begin(), end());
216 216 return result;
217 217 }
  218 +
  219 +bool
  220 +QPDFNameTreeObjectHelper::validate(bool repair)
  221 +{
  222 + return m->impl->validate(repair);
  223 +}
... ...
libqpdf/QPDFNumberTreeObjectHelper.cc
... ... @@ -251,3 +251,9 @@ QPDFNumberTreeObjectHelper::getAsMap() const
251 251 result.insert(begin(), end());
252 252 return result;
253 253 }
  254 +
  255 +bool
  256 +QPDFNumberTreeObjectHelper::validate(bool repair)
  257 +{
  258 + return m->impl->validate(repair);
  259 +}
... ...
libqpdf/qpdf/NNTree.hh
... ... @@ -109,6 +109,8 @@ class NNTreeImpl
109 109 iterator insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value);
110 110 bool remove(QPDFObjectHandle const& key, QPDFObjectHandle* value = nullptr);
111 111  
  112 + bool validate(bool repair = true);
  113 +
112 114 // Change the split threshold for easier testing. There's no real reason to expose this to
113 115 // downstream tree helpers, but it has to be public so we can call it from the test suite.
114 116 void setSplitThreshold(int split_threshold);
... ...