Commit b00647d5e2be529b6295f07332cad949a1b8d032
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.
Showing
6 changed files
with
58 additions
and
2 deletions
include/qpdf/QPDFNameTreeObjectHelper.hh
| @@ -50,7 +50,13 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper | @@ -50,7 +50,13 @@ class QPDF_DLL_CLASS QPDFNameTreeObjectHelper: public QPDFObjectHelper | ||
| 50 | QPDFObjectHandle, | 50 | QPDFObjectHandle, |
| 51 | QPDF&, | 51 | QPDF&, |
| 52 | std::function<bool(QPDFObjectHandle const&)> value_validator, | 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 | // Create an empty name tree | 61 | // Create an empty name tree |
| 56 | QPDF_DLL | 62 | QPDF_DLL |
include/qpdf/QPDFNumberTreeObjectHelper.hh
| @@ -48,7 +48,7 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper | @@ -48,7 +48,7 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper | ||
| 48 | QPDFObjectHandle, | 48 | QPDFObjectHandle, |
| 49 | QPDF&, | 49 | QPDF&, |
| 50 | std::function<bool(QPDFObjectHandle const&)> value_validator, | 50 | std::function<bool(QPDFObjectHandle const&)> value_validator, |
| 51 | - bool auto_repair = true); | 51 | + bool auto_repair); |
| 52 | 52 | ||
| 53 | QPDF_DLL | 53 | QPDF_DLL |
| 54 | ~QPDFNumberTreeObjectHelper() override; | 54 | ~QPDFNumberTreeObjectHelper() override; |
| @@ -59,6 +59,12 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper | @@ -59,6 +59,12 @@ class QPDF_DLL_CLASS QPDFNumberTreeObjectHelper: public QPDFObjectHelper | ||
| 59 | 59 | ||
| 60 | typedef long long int numtree_number; | 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 | // Return overall minimum and maximum indices | 68 | // Return overall minimum and maximum indices |
| 63 | QPDF_DLL | 69 | QPDF_DLL |
| 64 | numtree_number getMin(); | 70 | numtree_number getMin(); |
libqpdf/NNTree.cc
| @@ -891,3 +891,33 @@ NNTreeImpl::remove(QPDFObjectHandle const& key, QPDFObjectHandle* value) | @@ -891,3 +891,33 @@ NNTreeImpl::remove(QPDFObjectHandle const& key, QPDFObjectHandle* value) | ||
| 891 | iter.remove(); | 891 | iter.remove(); |
| 892 | return true; | 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,3 +215,9 @@ QPDFNameTreeObjectHelper::getAsMap() const | ||
| 215 | result.insert(begin(), end()); | 215 | result.insert(begin(), end()); |
| 216 | return result; | 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,3 +251,9 @@ QPDFNumberTreeObjectHelper::getAsMap() const | ||
| 251 | result.insert(begin(), end()); | 251 | result.insert(begin(), end()); |
| 252 | return result; | 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,6 +109,8 @@ class NNTreeImpl | ||
| 109 | iterator insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value); | 109 | iterator insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value); |
| 110 | bool remove(QPDFObjectHandle const& key, QPDFObjectHandle* value = nullptr); | 110 | bool remove(QPDFObjectHandle const& key, QPDFObjectHandle* value = nullptr); |
| 111 | 111 | ||
| 112 | + bool validate(bool repair = true); | ||
| 113 | + | ||
| 112 | // Change the split threshold for easier testing. There's no real reason to expose this to | 114 | // Change the split threshold for easier testing. There's no real reason to expose this to |
| 113 | // downstream tree helpers, but it has to be public so we can call it from the test suite. | 115 | // downstream tree helpers, but it has to be public so we can call it from the test suite. |
| 114 | void setSplitThreshold(int split_threshold); | 116 | void setSplitThreshold(int split_threshold); |