Commit f3a1139fd7deed394ff093e05a999176becaf63e
1 parent
f42de31c
Refactor `NNTreeImpl`: simplify `binarySearch` logic, use `size_t` for indices, …
…and improve `findInternal` readability.
Showing
3 changed files
with
42 additions
and
67 deletions
libqpdf/NNTree.cc
| 1 | +#include <qpdf/assert_debug.h> | ||
| 2 | + | ||
| 1 | #include <qpdf/NNTree.hh> | 3 | #include <qpdf/NNTree.hh> |
| 2 | 4 | ||
| 5 | +#include <qpdf/QPDFObjectHandle_private.hh> | ||
| 3 | #include <qpdf/QPDF_private.hh> | 6 | #include <qpdf/QPDF_private.hh> |
| 4 | #include <qpdf/QTC.hh> | 7 | #include <qpdf/QTC.hh> |
| 5 | #include <qpdf/QUtil.hh> | 8 | #include <qpdf/QUtil.hh> |
| 6 | 9 | ||
| 10 | +#include <bit> | ||
| 7 | #include <exception> | 11 | #include <exception> |
| 12 | +#include <utility> | ||
| 8 | 13 | ||
| 9 | static std::string | 14 | static std::string |
| 10 | get_description(QPDFObjectHandle& node) | 15 | get_description(QPDFObjectHandle& node) |
| @@ -718,59 +723,44 @@ int | @@ -718,59 +723,44 @@ int | ||
| 718 | NNTreeImpl::binarySearch( | 723 | NNTreeImpl::binarySearch( |
| 719 | QPDFObjectHandle key, | 724 | QPDFObjectHandle key, |
| 720 | QPDFObjectHandle items, | 725 | QPDFObjectHandle items, |
| 721 | - int num_items, | 726 | + size_t num_items, |
| 722 | bool return_prev_if_not_found, | 727 | bool return_prev_if_not_found, |
| 723 | int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item)) | 728 | int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item)) |
| 724 | { | 729 | { |
| 725 | - int max_idx = 1; | ||
| 726 | - while (max_idx < num_items) { | ||
| 727 | - max_idx <<= 1; | ||
| 728 | - } | 730 | + size_t max_idx = std::bit_ceil(num_items); |
| 729 | 731 | ||
| 730 | - int step = max_idx / 2; | ||
| 731 | - int checks = max_idx; | 732 | + int step = static_cast<int>(max_idx / 2); |
| 733 | + size_t checks = max_idx; | ||
| 732 | int idx = step; | 734 | int idx = step; |
| 733 | int found_idx = -1; | 735 | int found_idx = -1; |
| 734 | - bool found = false; | ||
| 735 | - bool found_leq = false; | ||
| 736 | - int status = 0; | ||
| 737 | 736 | ||
| 738 | - while ((!found) && (checks > 0)) { | ||
| 739 | - if (idx < num_items) { | 737 | + while (checks > 0) { |
| 738 | + int status = -1; | ||
| 739 | + if (std::cmp_less(idx, num_items)) { | ||
| 740 | status = (this->*compare)(key, items, idx); | 740 | status = (this->*compare)(key, items, idx); |
| 741 | - if (status >= 0) { | ||
| 742 | - found_leq = true; | 741 | + if (status == 0) { |
| 742 | + return idx; | ||
| 743 | + } | ||
| 744 | + if (status > 0) { | ||
| 743 | found_idx = idx; | 745 | found_idx = idx; |
| 744 | } | 746 | } |
| 745 | - } else { | ||
| 746 | - // consider item to be below anything after the top | ||
| 747 | - status = -1; | ||
| 748 | } | 747 | } |
| 748 | + checks >>= 1; | ||
| 749 | + if (checks > 0) { | ||
| 750 | + step >>= 1; | ||
| 751 | + if (step == 0) { | ||
| 752 | + step = 1; | ||
| 753 | + } | ||
| 749 | 754 | ||
| 750 | - if (status == 0) { | ||
| 751 | - found = true; | ||
| 752 | - } else { | ||
| 753 | - checks >>= 1; | ||
| 754 | - if (checks > 0) { | ||
| 755 | - step >>= 1; | ||
| 756 | - if (step == 0) { | ||
| 757 | - step = 1; | ||
| 758 | - } | ||
| 759 | - | ||
| 760 | - if (status < 0) { | ||
| 761 | - idx -= step; | ||
| 762 | - } else { | ||
| 763 | - idx += step; | ||
| 764 | - } | 755 | + if (status < 0) { |
| 756 | + idx -= step; | ||
| 757 | + } else { | ||
| 758 | + idx += step; | ||
| 765 | } | 759 | } |
| 766 | } | 760 | } |
| 767 | } | 761 | } |
| 768 | 762 | ||
| 769 | - if (found || (found_leq && return_prev_if_not_found)) { | ||
| 770 | - return found_idx; | ||
| 771 | - } else { | ||
| 772 | - return -1; | ||
| 773 | - } | 763 | + return return_prev_if_not_found ? found_idx : -1; |
| 774 | } | 764 | } |
| 775 | 765 | ||
| 776 | int | 766 | int |
| @@ -826,28 +816,19 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) | @@ -826,28 +816,19 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) | ||
| 826 | } | 816 | } |
| 827 | 817 | ||
| 828 | NNTreeImpl::iterator | 818 | NNTreeImpl::iterator |
| 829 | -NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found) | 819 | +NNTreeImpl::findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found) |
| 830 | { | 820 | { |
| 831 | auto first_item = begin(); | 821 | auto first_item = begin(); |
| 832 | auto last_item = end(); | 822 | auto last_item = end(); |
| 833 | if (first_item == end()) { | 823 | if (first_item == end()) { |
| 834 | - // Empty | ||
| 835 | return end(); | 824 | return end(); |
| 836 | - } else if ( | ||
| 837 | - first_item.valid() && details.keyValid(first_item->first) && | 825 | + } |
| 826 | + if (first_item.valid() && details.keyValid(first_item->first) && | ||
| 838 | details.compareKeys(key, first_item->first) < 0) { | 827 | details.compareKeys(key, first_item->first) < 0) { |
| 839 | // Before the first key | 828 | // Before the first key |
| 840 | return end(); | 829 | return end(); |
| 841 | - } else if ( | ||
| 842 | - last_item.valid() && details.keyValid(last_item->first) && | ||
| 843 | - details.compareKeys(key, last_item->first) > 0) { | ||
| 844 | - // After the last key | ||
| 845 | - if (return_prev_if_not_found) { | ||
| 846 | - return last_item; | ||
| 847 | - } else { | ||
| 848 | - return end(); | ||
| 849 | - } | ||
| 850 | } | 830 | } |
| 831 | + qpdf_assert_debug(!last_item.valid()); | ||
| 851 | 832 | ||
| 852 | QPDFObjGen::set seen; | 833 | QPDFObjGen::set seen; |
| 853 | auto node = oh; | 834 | auto node = oh; |
| @@ -855,36 +836,33 @@ NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found) | @@ -855,36 +836,33 @@ NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found) | ||
| 855 | 836 | ||
| 856 | while (true) { | 837 | while (true) { |
| 857 | if (!seen.add(node)) { | 838 | if (!seen.add(node)) { |
| 858 | - QTC::TC("qpdf", "NNTree loop in find"); | ||
| 859 | error(node, "loop detected in find"); | 839 | error(node, "loop detected in find"); |
| 860 | } | 840 | } |
| 861 | 841 | ||
| 862 | - auto kids = node.getKey("/Kids"); | ||
| 863 | - int nkids = kids.isArray() ? kids.getArrayNItems() : 0; | ||
| 864 | auto items = node.getKey(details.itemsKey()); | 842 | auto items = node.getKey(details.itemsKey()); |
| 865 | - int nitems = items.isArray() ? items.getArrayNItems() : 0; | ||
| 866 | - if (nitems > 0) { | 843 | + size_t nitems = items.size(); |
| 844 | + if (nitems > 1) { | ||
| 867 | int idx = binarySearch( | 845 | int idx = binarySearch( |
| 868 | key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); | 846 | key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); |
| 869 | if (idx >= 0) { | 847 | if (idx >= 0) { |
| 870 | result.setItemNumber(node, 2 * idx); | 848 | result.setItemNumber(node, 2 * idx); |
| 871 | } | 849 | } |
| 872 | - break; | ||
| 873 | - } else if (nkids > 0) { | 850 | + return result; |
| 851 | + } | ||
| 852 | + | ||
| 853 | + auto kids = node.getKey("/Kids"); | ||
| 854 | + size_t nkids = kids.isArray() ? kids.size() : 0; | ||
| 855 | + if (nkids > 0) { | ||
| 874 | int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid); | 856 | int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid); |
| 875 | if (idx == -1) { | 857 | if (idx == -1) { |
| 876 | - QTC::TC("qpdf", "NNTree -1 in binary search"); | ||
| 877 | error(node, "unexpected -1 from binary search of kids; limits may by wrong"); | 858 | error(node, "unexpected -1 from binary search of kids; limits may by wrong"); |
| 878 | } | 859 | } |
| 879 | result.addPathElement(node, idx); | 860 | result.addPathElement(node, idx); |
| 880 | - node = kids.getArrayItem(idx); | 861 | + node = kids[idx]; |
| 881 | } else { | 862 | } else { |
| 882 | - QTC::TC("qpdf", "NNTree bad node during find"); | ||
| 883 | error(node, "bad node during find"); | 863 | error(node, "bad node during find"); |
| 884 | } | 864 | } |
| 885 | } | 865 | } |
| 886 | - | ||
| 887 | - return result; | ||
| 888 | } | 866 | } |
| 889 | 867 | ||
| 890 | NNTreeImpl::iterator | 868 | NNTreeImpl::iterator |
libqpdf/qpdf/NNTree.hh
| @@ -110,12 +110,12 @@ class NNTreeImpl | @@ -110,12 +110,12 @@ class NNTreeImpl | ||
| 110 | 110 | ||
| 111 | private: | 111 | private: |
| 112 | void repair(); | 112 | void repair(); |
| 113 | - iterator findInternal(QPDFObjectHandle key, bool return_prev_if_not_found = false); | 113 | + iterator findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found = false); |
| 114 | int withinLimits(QPDFObjectHandle key, QPDFObjectHandle node); | 114 | int withinLimits(QPDFObjectHandle key, QPDFObjectHandle node); |
| 115 | int binarySearch( | 115 | int binarySearch( |
| 116 | QPDFObjectHandle key, | 116 | QPDFObjectHandle key, |
| 117 | QPDFObjectHandle items, | 117 | QPDFObjectHandle items, |
| 118 | - int num_items, | 118 | + size_t num_items, |
| 119 | bool return_prev_if_not_found, | 119 | bool return_prev_if_not_found, |
| 120 | int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item)); | 120 | int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item)); |
| 121 | int compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx); | 121 | int compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx); |
qpdf/qpdf.testcov
| @@ -543,9 +543,6 @@ NNTree split second half kid 0 | @@ -543,9 +543,6 @@ NNTree split second half kid 0 | ||
| 543 | NNTree missing limits 0 | 543 | NNTree missing limits 0 |
| 544 | NNTree item is wrong type 0 | 544 | NNTree item is wrong type 0 |
| 545 | NNTree kid is invalid 0 | 545 | NNTree kid is invalid 0 |
| 546 | -NNTree loop in find 0 | ||
| 547 | -NNTree -1 in binary search 0 | ||
| 548 | -NNTree bad node during find 0 | ||
| 549 | NNTree node is not a dictionary 0 | 546 | NNTree node is not a dictionary 0 |
| 550 | NNTree limits didn't change 0 | 547 | NNTree limits didn't change 0 |
| 551 | NNTree increment end() 0 | 548 | NNTree increment end() 0 |