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 | 3 | #include <qpdf/NNTree.hh> |
| 2 | 4 | |
| 5 | +#include <qpdf/QPDFObjectHandle_private.hh> | |
| 3 | 6 | #include <qpdf/QPDF_private.hh> |
| 4 | 7 | #include <qpdf/QTC.hh> |
| 5 | 8 | #include <qpdf/QUtil.hh> |
| 6 | 9 | |
| 10 | +#include <bit> | |
| 7 | 11 | #include <exception> |
| 12 | +#include <utility> | |
| 8 | 13 | |
| 9 | 14 | static std::string |
| 10 | 15 | get_description(QPDFObjectHandle& node) |
| ... | ... | @@ -718,59 +723,44 @@ int |
| 718 | 723 | NNTreeImpl::binarySearch( |
| 719 | 724 | QPDFObjectHandle key, |
| 720 | 725 | QPDFObjectHandle items, |
| 721 | - int num_items, | |
| 726 | + size_t num_items, | |
| 722 | 727 | bool return_prev_if_not_found, |
| 723 | 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 | 734 | int idx = step; |
| 733 | 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 | 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 | 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 | 766 | int |
| ... | ... | @@ -826,28 +816,19 @@ NNTreeImpl::find(QPDFObjectHandle key, bool return_prev_if_not_found) |
| 826 | 816 | } |
| 827 | 817 | |
| 828 | 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 | 821 | auto first_item = begin(); |
| 832 | 822 | auto last_item = end(); |
| 833 | 823 | if (first_item == end()) { |
| 834 | - // Empty | |
| 835 | 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 | 827 | details.compareKeys(key, first_item->first) < 0) { |
| 839 | 828 | // Before the first key |
| 840 | 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 | 833 | QPDFObjGen::set seen; |
| 853 | 834 | auto node = oh; |
| ... | ... | @@ -855,36 +836,33 @@ NNTreeImpl::findInternal(QPDFObjectHandle key, bool return_prev_if_not_found) |
| 855 | 836 | |
| 856 | 837 | while (true) { |
| 857 | 838 | if (!seen.add(node)) { |
| 858 | - QTC::TC("qpdf", "NNTree loop in find"); | |
| 859 | 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 | 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 | 845 | int idx = binarySearch( |
| 868 | 846 | key, items, nitems / 2, return_prev_if_not_found, &NNTreeImpl::compareKeyItem); |
| 869 | 847 | if (idx >= 0) { |
| 870 | 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 | 856 | int idx = binarySearch(key, kids, nkids, true, &NNTreeImpl::compareKeyKid); |
| 875 | 857 | if (idx == -1) { |
| 876 | - QTC::TC("qpdf", "NNTree -1 in binary search"); | |
| 877 | 858 | error(node, "unexpected -1 from binary search of kids; limits may by wrong"); |
| 878 | 859 | } |
| 879 | 860 | result.addPathElement(node, idx); |
| 880 | - node = kids.getArrayItem(idx); | |
| 861 | + node = kids[idx]; | |
| 881 | 862 | } else { |
| 882 | - QTC::TC("qpdf", "NNTree bad node during find"); | |
| 883 | 863 | error(node, "bad node during find"); |
| 884 | 864 | } |
| 885 | 865 | } |
| 886 | - | |
| 887 | - return result; | |
| 888 | 866 | } |
| 889 | 867 | |
| 890 | 868 | NNTreeImpl::iterator | ... | ... |
libqpdf/qpdf/NNTree.hh
| ... | ... | @@ -110,12 +110,12 @@ class NNTreeImpl |
| 110 | 110 | |
| 111 | 111 | private: |
| 112 | 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 | 114 | int withinLimits(QPDFObjectHandle key, QPDFObjectHandle node); |
| 115 | 115 | int binarySearch( |
| 116 | 116 | QPDFObjectHandle key, |
| 117 | 117 | QPDFObjectHandle items, |
| 118 | - int num_items, | |
| 118 | + size_t num_items, | |
| 119 | 119 | bool return_prev_if_not_found, |
| 120 | 120 | int (NNTreeImpl::*compare)(QPDFObjectHandle& key, QPDFObjectHandle& arr, int item)); |
| 121 | 121 | int compareKeyItem(QPDFObjectHandle& key, QPDFObjectHandle& items, int idx); | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -543,9 +543,6 @@ NNTree split second half kid 0 |
| 543 | 543 | NNTree missing limits 0 |
| 544 | 544 | NNTree item is wrong type 0 |
| 545 | 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 | 546 | NNTree node is not a dictionary 0 |
| 550 | 547 | NNTree limits didn't change 0 |
| 551 | 548 | NNTree increment end() 0 | ... | ... |