Commit f3a1139fd7deed394ff093e05a999176becaf63e

Authored by m-holger
1 parent f42de31c

Refactor `NNTreeImpl`: simplify `binarySearch` logic, use `size_t` for indices, …

…and improve `findInternal` readability.
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