Commit f8e39253be9d1806ccdb88d8117b7418f2045da1
Committed by
GitHub
Merge pull request #863 from m-holger/array
Refactor QPDF_Array
Showing
15 changed files
with
383 additions
and
404 deletions
include/qpdf/QPDFObjectHandle.hh
| ... | ... | @@ -1496,11 +1496,10 @@ class QPDFObjectHandle |
| 1496 | 1496 | { |
| 1497 | 1497 | friend class QPDF_Dictionary; |
| 1498 | 1498 | friend class QPDF_Stream; |
| 1499 | - friend class SparseOHArray; | |
| 1500 | 1499 | |
| 1501 | 1500 | private: |
| 1502 | 1501 | static void |
| 1503 | - disconnect(QPDFObjectHandle& o) | |
| 1502 | + disconnect(QPDFObjectHandle o) | |
| 1504 | 1503 | { |
| 1505 | 1504 | o.disconnect(); |
| 1506 | 1505 | } |
| ... | ... | @@ -1577,6 +1576,11 @@ class QPDFObjectHandle |
| 1577 | 1576 | { |
| 1578 | 1577 | return obj; |
| 1579 | 1578 | } |
| 1579 | + std::shared_ptr<QPDFObject> | |
| 1580 | + getObj() const | |
| 1581 | + { | |
| 1582 | + return obj; | |
| 1583 | + } | |
| 1580 | 1584 | QPDFObject* |
| 1581 | 1585 | getObjectPtr() |
| 1582 | 1586 | { | ... | ... |
libqpdf/CMakeLists.txt
libqpdf/QPDFObjectHandle.cc
| ... | ... | @@ -23,7 +23,6 @@ |
| 23 | 23 | #include <qpdf/QPDF_Stream.hh> |
| 24 | 24 | #include <qpdf/QPDF_String.hh> |
| 25 | 25 | #include <qpdf/QPDF_Unresolved.hh> |
| 26 | -#include <qpdf/SparseOHArray.hh> | |
| 27 | 26 | |
| 28 | 27 | #include <qpdf/QIntC.hh> |
| 29 | 28 | #include <qpdf/QTC.hh> |
| ... | ... | @@ -789,9 +788,8 @@ QPDFObjectHandle::aitems() |
| 789 | 788 | int |
| 790 | 789 | QPDFObjectHandle::getArrayNItems() |
| 791 | 790 | { |
| 792 | - auto array = asArray(); | |
| 793 | - if (array) { | |
| 794 | - return array->getNItems(); | |
| 791 | + if (auto array = asArray()) { | |
| 792 | + return array->size(); | |
| 795 | 793 | } else { |
| 796 | 794 | typeWarning("array", "treating as empty"); |
| 797 | 795 | QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); |
| ... | ... | @@ -802,104 +800,101 @@ QPDFObjectHandle::getArrayNItems() |
| 802 | 800 | QPDFObjectHandle |
| 803 | 801 | QPDFObjectHandle::getArrayItem(int n) |
| 804 | 802 | { |
| 805 | - auto array = asArray(); | |
| 806 | - if (array && (n < array->getNItems()) && (n >= 0)) { | |
| 807 | - return array->getItem(n); | |
| 808 | - } else { | |
| 809 | - if (array) { | |
| 803 | + if (auto array = asArray()) { | |
| 804 | + if (auto result = array->at(n); result.obj != nullptr) { | |
| 805 | + return result; | |
| 806 | + } else { | |
| 810 | 807 | objectWarning("returning null for out of bounds array access"); |
| 811 | 808 | QTC::TC("qpdf", "QPDFObjectHandle array bounds"); |
| 812 | - } else { | |
| 813 | - typeWarning("array", "returning null"); | |
| 814 | - QTC::TC("qpdf", "QPDFObjectHandle array null for non-array"); | |
| 815 | 809 | } |
| 816 | - static auto constexpr msg = | |
| 817 | - " -> null returned from invalid array access"sv; | |
| 818 | - return QPDF_Null::create(obj, msg, ""); | |
| 810 | + } else { | |
| 811 | + typeWarning("array", "returning null"); | |
| 812 | + QTC::TC("qpdf", "QPDFObjectHandle array null for non-array"); | |
| 819 | 813 | } |
| 814 | + static auto constexpr msg = " -> null returned from invalid array access"sv; | |
| 815 | + return QPDF_Null::create(obj, msg, ""); | |
| 820 | 816 | } |
| 821 | 817 | |
| 822 | 818 | bool |
| 823 | 819 | QPDFObjectHandle::isRectangle() |
| 824 | 820 | { |
| 825 | - auto array = asArray(); | |
| 826 | - if ((array == nullptr) || (array->getNItems() != 4)) { | |
| 827 | - return false; | |
| 828 | - } | |
| 829 | - for (int i = 0; i < 4; ++i) { | |
| 830 | - if (!array->getItem(i).isNumber()) { | |
| 831 | - return false; | |
| 821 | + if (auto array = asArray()) { | |
| 822 | + for (int i = 0; i < 4; ++i) { | |
| 823 | + if (auto item = array->at(i); !(item.obj && item.isNumber())) { | |
| 824 | + return false; | |
| 825 | + } | |
| 832 | 826 | } |
| 827 | + return array->size() == 4; | |
| 833 | 828 | } |
| 834 | - return true; | |
| 829 | + return false; | |
| 835 | 830 | } |
| 836 | 831 | |
| 837 | 832 | bool |
| 838 | 833 | QPDFObjectHandle::isMatrix() |
| 839 | 834 | { |
| 840 | - auto array = asArray(); | |
| 841 | - if ((array == nullptr) || (array->getNItems() != 6)) { | |
| 842 | - return false; | |
| 843 | - } | |
| 844 | - for (int i = 0; i < 6; ++i) { | |
| 845 | - if (!array->getItem(i).isNumber()) { | |
| 846 | - return false; | |
| 835 | + if (auto array = asArray()) { | |
| 836 | + for (int i = 0; i < 6; ++i) { | |
| 837 | + if (auto item = array->at(i); !(item.obj && item.isNumber())) { | |
| 838 | + return false; | |
| 839 | + } | |
| 847 | 840 | } |
| 841 | + return array->size() == 6; | |
| 848 | 842 | } |
| 849 | - return true; | |
| 843 | + return false; | |
| 850 | 844 | } |
| 851 | 845 | |
| 852 | 846 | QPDFObjectHandle::Rectangle |
| 853 | 847 | QPDFObjectHandle::getArrayAsRectangle() |
| 854 | 848 | { |
| 855 | - Rectangle result; | |
| 856 | - if (isRectangle()) { | |
| 857 | - auto array = asArray(); | |
| 858 | - // Rectangle coordinates are always supposed to be llx, lly, | |
| 859 | - // urx, ury, but files have been found in the wild where | |
| 860 | - // llx > urx or lly > ury. | |
| 861 | - double i0 = array->getItem(0).getNumericValue(); | |
| 862 | - double i1 = array->getItem(1).getNumericValue(); | |
| 863 | - double i2 = array->getItem(2).getNumericValue(); | |
| 864 | - double i3 = array->getItem(3).getNumericValue(); | |
| 865 | - result = Rectangle( | |
| 866 | - std::min(i0, i2), | |
| 867 | - std::min(i1, i3), | |
| 868 | - std::max(i0, i2), | |
| 869 | - std::max(i1, i3)); | |
| 849 | + if (auto array = asArray()) { | |
| 850 | + if (array->size() != 4) { | |
| 851 | + return {}; | |
| 852 | + } | |
| 853 | + double items[4]; | |
| 854 | + for (int i = 0; i < 4; ++i) { | |
| 855 | + if (!array->at(i).getValueAsNumber(items[i])) { | |
| 856 | + return {}; | |
| 857 | + } | |
| 858 | + } | |
| 859 | + return Rectangle( | |
| 860 | + std::min(items[0], items[2]), | |
| 861 | + std::min(items[1], items[3]), | |
| 862 | + std::max(items[0], items[2]), | |
| 863 | + std::max(items[1], items[3])); | |
| 870 | 864 | } |
| 871 | - return result; | |
| 865 | + return {}; | |
| 872 | 866 | } |
| 873 | 867 | |
| 874 | 868 | QPDFObjectHandle::Matrix |
| 875 | 869 | QPDFObjectHandle::getArrayAsMatrix() |
| 876 | 870 | { |
| 877 | - Matrix result; | |
| 878 | - if (isMatrix()) { | |
| 879 | - auto array = asArray(); | |
| 880 | - result = Matrix( | |
| 881 | - array->getItem(0).getNumericValue(), | |
| 882 | - array->getItem(1).getNumericValue(), | |
| 883 | - array->getItem(2).getNumericValue(), | |
| 884 | - array->getItem(3).getNumericValue(), | |
| 885 | - array->getItem(4).getNumericValue(), | |
| 886 | - array->getItem(5).getNumericValue()); | |
| 871 | + if (auto array = asArray()) { | |
| 872 | + if (array->size() != 6) { | |
| 873 | + return {}; | |
| 874 | + } | |
| 875 | + double items[6]; | |
| 876 | + for (int i = 0; i < 6; ++i) { | |
| 877 | + if (!array->at(i).getValueAsNumber(items[i])) { | |
| 878 | + return {}; | |
| 879 | + } | |
| 880 | + } | |
| 881 | + return Matrix( | |
| 882 | + items[0], items[1], items[2], items[3], items[4], items[5]); | |
| 887 | 883 | } |
| 888 | - return result; | |
| 884 | + return {}; | |
| 889 | 885 | } |
| 890 | 886 | |
| 891 | 887 | std::vector<QPDFObjectHandle> |
| 892 | 888 | QPDFObjectHandle::getArrayAsVector() |
| 893 | 889 | { |
| 894 | - std::vector<QPDFObjectHandle> result; | |
| 895 | 890 | auto array = asArray(); |
| 896 | 891 | if (array) { |
| 897 | - array->getAsVector(result); | |
| 892 | + return array->getAsVector(); | |
| 898 | 893 | } else { |
| 899 | 894 | typeWarning("array", "treating as empty"); |
| 900 | 895 | QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector"); |
| 901 | 896 | } |
| 902 | - return result; | |
| 897 | + return {}; | |
| 903 | 898 | } |
| 904 | 899 | |
| 905 | 900 | // Array mutators |
| ... | ... | @@ -907,24 +902,20 @@ QPDFObjectHandle::getArrayAsVector() |
| 907 | 902 | void |
| 908 | 903 | QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) |
| 909 | 904 | { |
| 910 | - auto array = asArray(); | |
| 911 | - if (array) { | |
| 912 | - checkOwnership(item); | |
| 913 | - array->setItem(n, item); | |
| 905 | + if (auto array = asArray()) { | |
| 906 | + if (!array->setAt(n, item)) { | |
| 907 | + objectWarning("ignoring attempt to set out of bounds array item"); | |
| 908 | + QTC::TC("qpdf", "QPDFObjectHandle set array bounds"); | |
| 909 | + } | |
| 914 | 910 | } else { |
| 915 | 911 | typeWarning("array", "ignoring attempt to set item"); |
| 916 | 912 | QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item"); |
| 917 | 913 | } |
| 918 | 914 | } |
| 919 | - | |
| 920 | 915 | void |
| 921 | 916 | QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) |
| 922 | 917 | { |
| 923 | - auto array = asArray(); | |
| 924 | - if (array) { | |
| 925 | - for (auto const& item: items) { | |
| 926 | - checkOwnership(item); | |
| 927 | - } | |
| 918 | + if (auto array = asArray()) { | |
| 928 | 919 | array->setFromVector(items); |
| 929 | 920 | } else { |
| 930 | 921 | typeWarning("array", "ignoring attempt to replace items"); |
| ... | ... | @@ -935,9 +926,12 @@ QPDFObjectHandle::setArrayFromVector(std::vector<QPDFObjectHandle> const& items) |
| 935 | 926 | void |
| 936 | 927 | QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) |
| 937 | 928 | { |
| 938 | - auto array = asArray(); | |
| 939 | - if (array) { | |
| 940 | - array->insertItem(at, item); | |
| 929 | + if (auto array = asArray()) { | |
| 930 | + if (!array->insert(at, item)) { | |
| 931 | + objectWarning( | |
| 932 | + "ignoring attempt to insert out of bounds array item"); | |
| 933 | + QTC::TC("qpdf", "QPDFObjectHandle insert array bounds"); | |
| 934 | + } | |
| 941 | 935 | } else { |
| 942 | 936 | typeWarning("array", "ignoring attempt to insert item"); |
| 943 | 937 | QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item"); |
| ... | ... | @@ -954,10 +948,8 @@ QPDFObjectHandle::insertItemAndGetNew(int at, QPDFObjectHandle const& item) |
| 954 | 948 | void |
| 955 | 949 | QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) |
| 956 | 950 | { |
| 957 | - auto array = asArray(); | |
| 958 | - if (array) { | |
| 959 | - checkOwnership(item); | |
| 960 | - array->appendItem(item); | |
| 951 | + if (auto array = asArray()) { | |
| 952 | + array->push_back(item); | |
| 961 | 953 | } else { |
| 962 | 954 | typeWarning("array", "ignoring attempt to append item"); |
| 963 | 955 | QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item"); |
| ... | ... | @@ -974,28 +966,23 @@ QPDFObjectHandle::appendItemAndGetNew(QPDFObjectHandle const& item) |
| 974 | 966 | void |
| 975 | 967 | QPDFObjectHandle::eraseItem(int at) |
| 976 | 968 | { |
| 977 | - auto array = asArray(); | |
| 978 | - if (array && (at < array->getNItems()) && (at >= 0)) { | |
| 979 | - array->eraseItem(at); | |
| 980 | - } else { | |
| 981 | - if (array) { | |
| 969 | + if (auto array = asArray()) { | |
| 970 | + if (!array->erase(at)) { | |
| 982 | 971 | objectWarning("ignoring attempt to erase out of bounds array item"); |
| 983 | 972 | QTC::TC("qpdf", "QPDFObjectHandle erase array bounds"); |
| 984 | - } else { | |
| 985 | - typeWarning("array", "ignoring attempt to erase item"); | |
| 986 | - QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item"); | |
| 987 | 973 | } |
| 974 | + } else { | |
| 975 | + typeWarning("array", "ignoring attempt to erase item"); | |
| 976 | + QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item"); | |
| 988 | 977 | } |
| 989 | 978 | } |
| 990 | 979 | |
| 991 | 980 | QPDFObjectHandle |
| 992 | 981 | QPDFObjectHandle::eraseItemAndGetOld(int at) |
| 993 | 982 | { |
| 994 | - auto result = QPDFObjectHandle::newNull(); | |
| 995 | 983 | auto array = asArray(); |
| 996 | - if (array && (at < array->getNItems()) && (at >= 0)) { | |
| 997 | - result = array->getItem(at); | |
| 998 | - } | |
| 984 | + auto result = | |
| 985 | + (array && at < array->size() && at >= 0) ? array->at(at) : newNull(); | |
| 999 | 986 | eraseItem(at); |
| 1000 | 987 | return result; |
| 1001 | 988 | } |
| ... | ... | @@ -1515,11 +1502,10 @@ QPDFObjectHandle::arrayOrStreamToStreamArray( |
| 1515 | 1502 | { |
| 1516 | 1503 | all_description = description; |
| 1517 | 1504 | std::vector<QPDFObjectHandle> result; |
| 1518 | - auto array = asArray(); | |
| 1519 | - if (array) { | |
| 1520 | - int n_items = array->getNItems(); | |
| 1505 | + if (auto array = asArray()) { | |
| 1506 | + int n_items = array->size(); | |
| 1521 | 1507 | for (int i = 0; i < n_items; ++i) { |
| 1522 | - QPDFObjectHandle item = array->getItem(i); | |
| 1508 | + QPDFObjectHandle item = array->at(i); | |
| 1523 | 1509 | if (item.isStream()) { |
| 1524 | 1510 | result.push_back(item); |
| 1525 | 1511 | } else { |
| ... | ... | @@ -2217,9 +2203,9 @@ QPDFObjectHandle::makeDirect( |
| 2217 | 2203 | } else if (isArray()) { |
| 2218 | 2204 | std::vector<QPDFObjectHandle> items; |
| 2219 | 2205 | auto array = asArray(); |
| 2220 | - int n = array->getNItems(); | |
| 2206 | + int n = array->size(); | |
| 2221 | 2207 | for (int i = 0; i < n; ++i) { |
| 2222 | - items.push_back(array->getItem(i)); | |
| 2208 | + items.push_back(array->at(i)); | |
| 2223 | 2209 | items.back().makeDirect(visited, stop_at_streams); |
| 2224 | 2210 | } |
| 2225 | 2211 | this->obj = QPDF_Array::create(items); | ... | ... |
libqpdf/QPDFParser.cc
| ... | ... | @@ -27,16 +27,15 @@ namespace |
| 27 | 27 | struct StackFrame |
| 28 | 28 | { |
| 29 | 29 | StackFrame(std::shared_ptr<InputSource> input) : |
| 30 | - offset(input->tell()), | |
| 31 | - contents_string(""), | |
| 32 | - contents_offset(-1) | |
| 30 | + offset(input->tell()) | |
| 33 | 31 | { |
| 34 | 32 | } |
| 35 | 33 | |
| 36 | 34 | std::vector<std::shared_ptr<QPDFObject>> olist; |
| 37 | 35 | qpdf_offset_t offset; |
| 38 | - std::string contents_string; | |
| 39 | - qpdf_offset_t contents_offset; | |
| 36 | + std::string contents_string{""}; | |
| 37 | + qpdf_offset_t contents_offset{-1}; | |
| 38 | + int null_count{0}; | |
| 40 | 39 | }; |
| 41 | 40 | } // namespace |
| 42 | 41 | |
| ... | ... | @@ -50,6 +49,7 @@ QPDFParser::parse(bool& empty, bool content_stream) |
| 50 | 49 | // this, it will cause a logic error to be thrown from |
| 51 | 50 | // QPDF::inParse(). |
| 52 | 51 | |
| 52 | + const static std::shared_ptr<QPDFObject> null_oh = QPDF_Null::create(); | |
| 53 | 53 | QPDF::ParseGuard pg(context); |
| 54 | 54 | |
| 55 | 55 | empty = false; |
| ... | ... | @@ -67,7 +67,6 @@ QPDFParser::parse(bool& empty, bool content_stream) |
| 67 | 67 | int good_count = 0; |
| 68 | 68 | bool b_contents = false; |
| 69 | 69 | bool is_null = false; |
| 70 | - auto null_oh = QPDF_Null::create(); | |
| 71 | 70 | |
| 72 | 71 | while (!done) { |
| 73 | 72 | bool bad = false; |
| ... | ... | @@ -156,6 +155,8 @@ QPDFParser::parse(bool& empty, bool content_stream) |
| 156 | 155 | |
| 157 | 156 | case QPDFTokenizer::tt_null: |
| 158 | 157 | is_null = true; |
| 158 | + ++frame.null_count; | |
| 159 | + | |
| 159 | 160 | break; |
| 160 | 161 | |
| 161 | 162 | case QPDFTokenizer::tt_integer: |
| ... | ... | @@ -301,9 +302,11 @@ QPDFParser::parse(bool& empty, bool content_stream) |
| 301 | 302 | |
| 302 | 303 | case st_dictionary: |
| 303 | 304 | case st_array: |
| 304 | - if (!indirect_ref && !is_null) { | |
| 305 | - // No need to set description for direct nulls - they will | |
| 306 | - // become implicit. | |
| 305 | + if (is_null) { | |
| 306 | + object = null_oh; | |
| 307 | + // No need to set description for direct nulls - they probably | |
| 308 | + // will become implicit. | |
| 309 | + } else if (!indirect_ref) { | |
| 307 | 310 | setDescription(object, input->getLastOffset()); |
| 308 | 311 | } |
| 309 | 312 | set_offset = true; |
| ... | ... | @@ -326,7 +329,8 @@ QPDFParser::parse(bool& empty, bool content_stream) |
| 326 | 329 | parser_state_e old_state = state_stack.back(); |
| 327 | 330 | state_stack.pop_back(); |
| 328 | 331 | if (old_state == st_array) { |
| 329 | - object = QPDF_Array::create(std::move(olist)); | |
| 332 | + object = QPDF_Array::create( | |
| 333 | + std::move(olist), frame.null_count > 100); | |
| 330 | 334 | setDescription(object, offset - 1); |
| 331 | 335 | // The `offset` points to the next of "[". Set the rewind |
| 332 | 336 | // offset to point to the beginning of "[". This has been |
| ... | ... | @@ -381,7 +385,7 @@ QPDFParser::parse(bool& empty, bool content_stream) |
| 381 | 385 | // Calculate value. |
| 382 | 386 | std::shared_ptr<QPDFObject> val; |
| 383 | 387 | if (iter != olist.end()) { |
| 384 | - val = *iter ? *iter : QPDF_Null::create(); | |
| 388 | + val = *iter; | |
| 385 | 389 | ++iter; |
| 386 | 390 | } else { |
| 387 | 391 | QTC::TC("qpdf", "QPDFParser no val for last key"); | ... | ... |
libqpdf/QPDF_Array.cc
| 1 | 1 | #include <qpdf/QPDF_Array.hh> |
| 2 | 2 | |
| 3 | -#include <qpdf/QIntC.hh> | |
| 3 | +#include <qpdf/QPDFObjectHandle.hh> | |
| 4 | 4 | #include <qpdf/QPDFObject_private.hh> |
| 5 | -#include <qpdf/QUtil.hh> | |
| 6 | -#include <stdexcept> | |
| 7 | 5 | |
| 8 | -QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) : | |
| 9 | - QPDFValue(::ot_array, "array") | |
| 6 | +static const QPDFObjectHandle null_oh = QPDFObjectHandle::newNull(); | |
| 7 | + | |
| 8 | +inline void | |
| 9 | +QPDF_Array::checkOwnership(QPDFObjectHandle const& item) const | |
| 10 | 10 | { |
| 11 | - setFromVector(v); | |
| 11 | + if (auto obj = item.getObjectPtr()) { | |
| 12 | + if (qpdf) { | |
| 13 | + if (auto item_qpdf = obj->getQPDF()) { | |
| 14 | + if (qpdf != item_qpdf) { | |
| 15 | + throw std::logic_error( | |
| 16 | + "Attempting to add an object from a different QPDF. " | |
| 17 | + "Use QPDF::copyForeignObject to add objects from " | |
| 18 | + "another file."); | |
| 19 | + } | |
| 20 | + } | |
| 21 | + } | |
| 22 | + } else { | |
| 23 | + throw std::logic_error( | |
| 24 | + "Attempting to add an uninitialized object to a QPDF_Array."); | |
| 25 | + } | |
| 12 | 26 | } |
| 13 | 27 | |
| 14 | -QPDF_Array::QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& v) : | |
| 28 | +QPDF_Array::QPDF_Array() : | |
| 15 | 29 | QPDFValue(::ot_array, "array") |
| 16 | 30 | { |
| 17 | - setFromVector(std::move(v)); | |
| 18 | 31 | } |
| 19 | 32 | |
| 20 | -QPDF_Array::QPDF_Array(SparseOHArray const& items) : | |
| 33 | +QPDF_Array::QPDF_Array(QPDF_Array const& other) : | |
| 21 | 34 | QPDFValue(::ot_array, "array"), |
| 22 | - elements(items) | |
| 35 | + sparse(other.sparse), | |
| 36 | + sp_size(other.sp_size), | |
| 37 | + sp_elements(other.sp_elements), | |
| 38 | + elements(other.elements) | |
| 23 | 39 | { |
| 24 | 40 | } |
| 25 | 41 | |
| 26 | -std::shared_ptr<QPDFObject> | |
| 27 | -QPDF_Array::create(std::vector<QPDFObjectHandle> const& items) | |
| 42 | +QPDF_Array::QPDF_Array(std::vector<QPDFObjectHandle> const& v) : | |
| 43 | + QPDFValue(::ot_array, "array") | |
| 28 | 44 | { |
| 29 | - return do_create(new QPDF_Array(items)); | |
| 45 | + setFromVector(v); | |
| 30 | 46 | } |
| 31 | 47 | |
| 32 | -std::shared_ptr<QPDFObject> | |
| 33 | -QPDF_Array::create(std::vector<std::shared_ptr<QPDFObject>>&& items) | |
| 48 | +QPDF_Array::QPDF_Array( | |
| 49 | + std::vector<std::shared_ptr<QPDFObject>>&& v, bool sparse) : | |
| 50 | + QPDFValue(::ot_array, "array"), | |
| 51 | + sparse(sparse) | |
| 34 | 52 | { |
| 35 | - return do_create(new QPDF_Array(std::move(items))); | |
| 53 | + if (sparse) { | |
| 54 | + for (auto&& item: v) { | |
| 55 | + if (item->getTypeCode() != ::ot_null || | |
| 56 | + item->getObjGen().isIndirect()) { | |
| 57 | + sp_elements[sp_size] = std::move(item); | |
| 58 | + } | |
| 59 | + ++sp_size; | |
| 60 | + } | |
| 61 | + } else { | |
| 62 | + elements = std::move(v); | |
| 63 | + } | |
| 36 | 64 | } |
| 37 | 65 | |
| 38 | 66 | std::shared_ptr<QPDFObject> |
| 39 | -QPDF_Array::create(SparseOHArray const& items) | |
| 67 | +QPDF_Array::create(std::vector<QPDFObjectHandle> const& items) | |
| 40 | 68 | { |
| 41 | 69 | return do_create(new QPDF_Array(items)); |
| 42 | 70 | } |
| 43 | 71 | |
| 44 | 72 | std::shared_ptr<QPDFObject> |
| 73 | +QPDF_Array::create( | |
| 74 | + std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse) | |
| 75 | +{ | |
| 76 | + return do_create(new QPDF_Array(std::move(items), sparse)); | |
| 77 | +} | |
| 78 | + | |
| 79 | +std::shared_ptr<QPDFObject> | |
| 45 | 80 | QPDF_Array::copy(bool shallow) |
| 46 | 81 | { |
| 47 | - return create(shallow ? elements : elements.copy()); | |
| 82 | + if (shallow) { | |
| 83 | + return do_create(new QPDF_Array(*this)); | |
| 84 | + } else { | |
| 85 | + if (sparse) { | |
| 86 | + QPDF_Array* result = new QPDF_Array(); | |
| 87 | + result->sp_size = sp_size; | |
| 88 | + for (auto const& element: sp_elements) { | |
| 89 | + auto const& obj = element.second; | |
| 90 | + result->sp_elements[element.first] = | |
| 91 | + obj->getObjGen().isIndirect() ? obj : obj->copy(); | |
| 92 | + } | |
| 93 | + return do_create(result); | |
| 94 | + } else { | |
| 95 | + std::vector<std::shared_ptr<QPDFObject>> result; | |
| 96 | + result.reserve(elements.size()); | |
| 97 | + for (auto const& element: elements) { | |
| 98 | + result.push_back( | |
| 99 | + element | |
| 100 | + ? (element->getObjGen().isIndirect() ? element | |
| 101 | + : element->copy()) | |
| 102 | + : element); | |
| 103 | + } | |
| 104 | + return create(std::move(result), false); | |
| 105 | + } | |
| 106 | + } | |
| 48 | 107 | } |
| 49 | 108 | |
| 50 | 109 | void |
| 51 | 110 | QPDF_Array::disconnect() |
| 52 | 111 | { |
| 53 | - elements.disconnect(); | |
| 112 | + if (sparse) { | |
| 113 | + for (auto& item: sp_elements) { | |
| 114 | + auto& obj = item.second; | |
| 115 | + if (!obj->getObjGen().isIndirect()) { | |
| 116 | + obj->disconnect(); | |
| 117 | + } | |
| 118 | + } | |
| 119 | + } else { | |
| 120 | + for (auto& obj: elements) { | |
| 121 | + if (!obj->getObjGen().isIndirect()) { | |
| 122 | + obj->disconnect(); | |
| 123 | + } | |
| 124 | + } | |
| 125 | + } | |
| 54 | 126 | } |
| 55 | 127 | |
| 56 | 128 | std::string |
| 57 | 129 | QPDF_Array::unparse() |
| 58 | 130 | { |
| 59 | 131 | std::string result = "[ "; |
| 60 | - size_t size = this->elements.size(); | |
| 61 | - for (size_t i = 0; i < size; ++i) { | |
| 62 | - result += this->elements.at(i).unparse(); | |
| 63 | - result += " "; | |
| 132 | + if (sparse) { | |
| 133 | + int next = 0; | |
| 134 | + for (auto& item: sp_elements) { | |
| 135 | + int key = item.first; | |
| 136 | + for (int j = next; j < key; ++j) { | |
| 137 | + result += "null "; | |
| 138 | + } | |
| 139 | + item.second->resolve(); | |
| 140 | + auto og = item.second->getObjGen(); | |
| 141 | + result += og.isIndirect() ? og.unparse(' ') + " R " | |
| 142 | + : item.second->unparse() + " "; | |
| 143 | + next = ++key; | |
| 144 | + } | |
| 145 | + for (int j = next; j < sp_size; ++j) { | |
| 146 | + result += "null "; | |
| 147 | + } | |
| 148 | + } else { | |
| 149 | + for (auto const& item: elements) { | |
| 150 | + item->resolve(); | |
| 151 | + auto og = item->getObjGen(); | |
| 152 | + result += og.isIndirect() ? og.unparse(' ') + " R " | |
| 153 | + : item->unparse() + " "; | |
| 154 | + } | |
| 64 | 155 | } |
| 65 | 156 | result += "]"; |
| 66 | 157 | return result; |
| ... | ... | @@ -69,96 +160,157 @@ QPDF_Array::unparse() |
| 69 | 160 | JSON |
| 70 | 161 | QPDF_Array::getJSON(int json_version) |
| 71 | 162 | { |
| 72 | - JSON j = JSON::makeArray(); | |
| 73 | - size_t size = this->elements.size(); | |
| 74 | - for (size_t i = 0; i < size; ++i) { | |
| 75 | - j.addArrayElement(this->elements.at(i).getJSON(json_version)); | |
| 163 | + static const JSON j_null = JSON::makeNull(); | |
| 164 | + JSON j_array = JSON::makeArray(); | |
| 165 | + if (sparse) { | |
| 166 | + int next = 0; | |
| 167 | + for (auto& item: sp_elements) { | |
| 168 | + int key = item.first; | |
| 169 | + for (int j = next; j < key; ++j) { | |
| 170 | + j_array.addArrayElement(j_null); | |
| 171 | + } | |
| 172 | + auto og = item.second->getObjGen(); | |
| 173 | + j_array.addArrayElement( | |
| 174 | + og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R") | |
| 175 | + : item.second->getJSON(json_version)); | |
| 176 | + next = ++key; | |
| 177 | + } | |
| 178 | + for (int j = next; j < sp_size; ++j) { | |
| 179 | + j_array.addArrayElement(j_null); | |
| 180 | + } | |
| 181 | + } else { | |
| 182 | + for (auto const& item: elements) { | |
| 183 | + auto og = item->getObjGen(); | |
| 184 | + j_array.addArrayElement( | |
| 185 | + og.isIndirect() ? JSON::makeString(og.unparse(' ') + " R") | |
| 186 | + : item->getJSON(json_version)); | |
| 187 | + } | |
| 76 | 188 | } |
| 77 | - return j; | |
| 78 | -} | |
| 79 | - | |
| 80 | -int | |
| 81 | -QPDF_Array::getNItems() const | |
| 82 | -{ | |
| 83 | - // This should really return a size_t, but changing it would break | |
| 84 | - // a lot of code. | |
| 85 | - return QIntC::to_int(this->elements.size()); | |
| 189 | + return j_array; | |
| 86 | 190 | } |
| 87 | 191 | |
| 88 | 192 | QPDFObjectHandle |
| 89 | -QPDF_Array::getItem(int n) const | |
| 193 | +QPDF_Array::at(int n) const noexcept | |
| 90 | 194 | { |
| 91 | - if ((n < 0) || (n >= QIntC::to_int(elements.size()))) { | |
| 92 | - throw std::logic_error( | |
| 93 | - "INTERNAL ERROR: bounds error accessing QPDF_Array element"); | |
| 195 | + if (n < 0 || n >= size()) { | |
| 196 | + return {}; | |
| 197 | + } else if (sparse) { | |
| 198 | + auto const& iter = sp_elements.find(n); | |
| 199 | + return iter == sp_elements.end() ? null_oh : (*iter).second; | |
| 200 | + } else { | |
| 201 | + return elements[size_t(n)]; | |
| 94 | 202 | } |
| 95 | - return this->elements.at(QIntC::to_size(n)); | |
| 96 | 203 | } |
| 97 | 204 | |
| 98 | -void | |
| 99 | -QPDF_Array::getAsVector(std::vector<QPDFObjectHandle>& v) const | |
| 205 | +std::vector<QPDFObjectHandle> | |
| 206 | +QPDF_Array::getAsVector() const | |
| 100 | 207 | { |
| 101 | - size_t size = this->elements.size(); | |
| 102 | - for (size_t i = 0; i < size; ++i) { | |
| 103 | - v.push_back(this->elements.at(i)); | |
| 208 | + if (sparse) { | |
| 209 | + std::vector<QPDFObjectHandle> v; | |
| 210 | + v.reserve(size_t(size())); | |
| 211 | + for (auto const& item: sp_elements) { | |
| 212 | + v.resize(size_t(item.first), null_oh); | |
| 213 | + v.push_back(item.second); | |
| 214 | + } | |
| 215 | + v.resize(size_t(size()), null_oh); | |
| 216 | + return v; | |
| 217 | + } else { | |
| 218 | + return {elements.cbegin(), elements.cend()}; | |
| 104 | 219 | } |
| 105 | 220 | } |
| 106 | 221 | |
| 107 | -void | |
| 108 | -QPDF_Array::setItem(int n, QPDFObjectHandle const& oh) | |
| 222 | +bool | |
| 223 | +QPDF_Array::setAt(int at, QPDFObjectHandle const& oh) | |
| 109 | 224 | { |
| 110 | - this->elements.setAt(QIntC::to_size(n), oh); | |
| 225 | + if (at < 0 || at >= size()) { | |
| 226 | + return false; | |
| 227 | + } | |
| 228 | + checkOwnership(oh); | |
| 229 | + if (sparse) { | |
| 230 | + sp_elements[at] = oh.getObj(); | |
| 231 | + } else { | |
| 232 | + elements[size_t(at)] = oh.getObj(); | |
| 233 | + } | |
| 234 | + return true; | |
| 111 | 235 | } |
| 112 | 236 | |
| 113 | 237 | void |
| 114 | 238 | QPDF_Array::setFromVector(std::vector<QPDFObjectHandle> const& v) |
| 115 | 239 | { |
| 116 | - this->elements = SparseOHArray(); | |
| 117 | - for (auto const& iter: v) { | |
| 118 | - this->elements.append(iter); | |
| 240 | + elements.resize(0); | |
| 241 | + elements.reserve(v.size()); | |
| 242 | + for (auto const& item: v) { | |
| 243 | + checkOwnership(item); | |
| 244 | + elements.push_back(item.getObj()); | |
| 119 | 245 | } |
| 120 | 246 | } |
| 121 | 247 | |
| 122 | -void | |
| 123 | -QPDF_Array::setFromVector(std::vector<std::shared_ptr<QPDFObject>>&& v) | |
| 248 | +bool | |
| 249 | +QPDF_Array::insert(int at, QPDFObjectHandle const& item) | |
| 124 | 250 | { |
| 125 | - this->elements = SparseOHArray(); | |
| 126 | - for (auto&& item: v) { | |
| 127 | - if (item) { | |
| 128 | - this->elements.append(item); | |
| 251 | + int sz = size(); | |
| 252 | + if (at < 0 || at > sz) { | |
| 253 | + // As special case, also allow insert beyond the end | |
| 254 | + return false; | |
| 255 | + } else if (at == sz) { | |
| 256 | + push_back(item); | |
| 257 | + } else { | |
| 258 | + checkOwnership(item); | |
| 259 | + if (sparse) { | |
| 260 | + auto iter = sp_elements.crbegin(); | |
| 261 | + while (iter != sp_elements.crend()) { | |
| 262 | + auto key = (iter++)->first; | |
| 263 | + if (key >= at) { | |
| 264 | + auto nh = sp_elements.extract(key); | |
| 265 | + ++nh.key(); | |
| 266 | + sp_elements.insert(std::move(nh)); | |
| 267 | + } else { | |
| 268 | + break; | |
| 269 | + } | |
| 270 | + } | |
| 271 | + sp_elements[at] = item.getObj(); | |
| 272 | + ++sp_size; | |
| 129 | 273 | } else { |
| 130 | - ++this->elements.n_elements; | |
| 274 | + elements.insert(elements.cbegin() + at, item.getObj()); | |
| 131 | 275 | } |
| 132 | 276 | } |
| 277 | + return true; | |
| 133 | 278 | } |
| 134 | 279 | |
| 135 | 280 | void |
| 136 | -QPDF_Array::insertItem(int at, QPDFObjectHandle const& item) | |
| 281 | +QPDF_Array::push_back(QPDFObjectHandle const& item) | |
| 137 | 282 | { |
| 138 | - // As special case, also allow insert beyond the end | |
| 139 | - if ((at < 0) || (at > QIntC::to_int(this->elements.size()))) { | |
| 140 | - throw std::logic_error( | |
| 141 | - "INTERNAL ERROR: bounds error accessing QPDF_Array element"); | |
| 283 | + checkOwnership(item); | |
| 284 | + if (sparse) { | |
| 285 | + sp_elements[sp_size++] = item.getObj(); | |
| 286 | + } else { | |
| 287 | + elements.push_back(item.getObj()); | |
| 142 | 288 | } |
| 143 | - this->elements.insert(QIntC::to_size(at), item); | |
| 144 | 289 | } |
| 145 | 290 | |
| 146 | -void | |
| 147 | -QPDF_Array::appendItem(QPDFObjectHandle const& item) | |
| 291 | +bool | |
| 292 | +QPDF_Array::erase(int at) | |
| 148 | 293 | { |
| 149 | - this->elements.append(item); | |
| 150 | -} | |
| 151 | - | |
| 152 | -void | |
| 153 | -QPDF_Array::eraseItem(int at) | |
| 154 | -{ | |
| 155 | - this->elements.erase(QIntC::to_size(at)); | |
| 156 | -} | |
| 294 | + if (at < 0 || at >= size()) { | |
| 295 | + return false; | |
| 296 | + } | |
| 297 | + if (sparse) { | |
| 298 | + auto end = sp_elements.end(); | |
| 299 | + if (auto iter = sp_elements.lower_bound(at); iter != end) { | |
| 300 | + if (iter->first == at) { | |
| 301 | + iter++; | |
| 302 | + sp_elements.erase(at); | |
| 303 | + } | |
| 157 | 304 | |
| 158 | -void | |
| 159 | -QPDF_Array::addExplicitElementsToList(std::list<QPDFObjectHandle>& l) const | |
| 160 | -{ | |
| 161 | - for (auto const& iter: this->elements) { | |
| 162 | - l.push_back(iter.second); | |
| 305 | + while (iter != end) { | |
| 306 | + auto nh = sp_elements.extract(iter++); | |
| 307 | + --nh.key(); | |
| 308 | + sp_elements.insert(std::move(nh)); | |
| 309 | + } | |
| 310 | + } | |
| 311 | + --sp_size; | |
| 312 | + } else { | |
| 313 | + elements.erase(elements.cbegin() + at); | |
| 163 | 314 | } |
| 315 | + return true; | |
| 164 | 316 | } | ... | ... |
libqpdf/QPDF_Null.cc
libqpdf/SparseOHArray.cc deleted
| 1 | -#include <qpdf/SparseOHArray.hh> | |
| 2 | - | |
| 3 | -#include <qpdf/QPDFObjectHandle.hh> | |
| 4 | -#include <qpdf/QPDFObject_private.hh> | |
| 5 | - | |
| 6 | -#include <stdexcept> | |
| 7 | - | |
| 8 | -SparseOHArray::SparseOHArray() : | |
| 9 | - n_elements(0) | |
| 10 | -{ | |
| 11 | -} | |
| 12 | - | |
| 13 | -size_t | |
| 14 | -SparseOHArray::size() const | |
| 15 | -{ | |
| 16 | - return this->n_elements; | |
| 17 | -} | |
| 18 | - | |
| 19 | -void | |
| 20 | -SparseOHArray::append(QPDFObjectHandle oh) | |
| 21 | -{ | |
| 22 | - if (!oh.isDirectNull()) { | |
| 23 | - this->elements[this->n_elements] = oh; | |
| 24 | - } | |
| 25 | - ++this->n_elements; | |
| 26 | -} | |
| 27 | - | |
| 28 | -void | |
| 29 | -SparseOHArray::append(std::shared_ptr<QPDFObject>&& obj) | |
| 30 | -{ | |
| 31 | - if (obj->getTypeCode() != ::ot_null || !obj->getObjGen().isIndirect()) { | |
| 32 | - this->elements[this->n_elements] = std::move(obj); | |
| 33 | - } | |
| 34 | - ++this->n_elements; | |
| 35 | -} | |
| 36 | - | |
| 37 | -QPDFObjectHandle | |
| 38 | -SparseOHArray::at(size_t idx) const | |
| 39 | -{ | |
| 40 | - if (idx >= this->n_elements) { | |
| 41 | - throw std::logic_error( | |
| 42 | - "INTERNAL ERROR: bounds error accessing SparseOHArray element"); | |
| 43 | - } | |
| 44 | - auto const& iter = this->elements.find(idx); | |
| 45 | - if (iter == this->elements.end()) { | |
| 46 | - return QPDFObjectHandle::newNull(); | |
| 47 | - } else { | |
| 48 | - return (*iter).second; | |
| 49 | - } | |
| 50 | -} | |
| 51 | - | |
| 52 | -void | |
| 53 | -SparseOHArray::remove_last() | |
| 54 | -{ | |
| 55 | - if (this->n_elements == 0) { | |
| 56 | - throw std::logic_error("INTERNAL ERROR: attempt to remove" | |
| 57 | - " last item from empty SparseOHArray"); | |
| 58 | - } | |
| 59 | - --this->n_elements; | |
| 60 | - this->elements.erase(this->n_elements); | |
| 61 | -} | |
| 62 | - | |
| 63 | -void | |
| 64 | -SparseOHArray::disconnect() | |
| 65 | -{ | |
| 66 | - for (auto& iter: this->elements) { | |
| 67 | - QPDFObjectHandle::DisconnectAccess::disconnect(iter.second); | |
| 68 | - } | |
| 69 | -} | |
| 70 | - | |
| 71 | -void | |
| 72 | -SparseOHArray::setAt(size_t idx, QPDFObjectHandle oh) | |
| 73 | -{ | |
| 74 | - if (idx >= this->n_elements) { | |
| 75 | - throw std::logic_error("bounds error setting item in SparseOHArray"); | |
| 76 | - } | |
| 77 | - if (oh.isDirectNull()) { | |
| 78 | - this->elements.erase(idx); | |
| 79 | - } else { | |
| 80 | - this->elements[idx] = oh; | |
| 81 | - } | |
| 82 | -} | |
| 83 | - | |
| 84 | -void | |
| 85 | -SparseOHArray::erase(size_t idx) | |
| 86 | -{ | |
| 87 | - if (idx >= this->n_elements) { | |
| 88 | - throw std::logic_error("bounds error erasing item from SparseOHArray"); | |
| 89 | - } | |
| 90 | - decltype(this->elements) dest; | |
| 91 | - for (auto const& iter: this->elements) { | |
| 92 | - if (iter.first < idx) { | |
| 93 | - dest.insert(iter); | |
| 94 | - } else if (iter.first > idx) { | |
| 95 | - dest[iter.first - 1] = iter.second; | |
| 96 | - } | |
| 97 | - } | |
| 98 | - this->elements = dest; | |
| 99 | - --this->n_elements; | |
| 100 | -} | |
| 101 | - | |
| 102 | -void | |
| 103 | -SparseOHArray::insert(size_t idx, QPDFObjectHandle oh) | |
| 104 | -{ | |
| 105 | - if (idx > this->n_elements) { | |
| 106 | - throw std::logic_error("bounds error inserting item to SparseOHArray"); | |
| 107 | - } else if (idx == this->n_elements) { | |
| 108 | - // Allow inserting to the last position | |
| 109 | - append(oh); | |
| 110 | - } else { | |
| 111 | - decltype(this->elements) dest; | |
| 112 | - for (auto const& iter: this->elements) { | |
| 113 | - if (iter.first < idx) { | |
| 114 | - dest.insert(iter); | |
| 115 | - } else { | |
| 116 | - dest[iter.first + 1] = iter.second; | |
| 117 | - } | |
| 118 | - } | |
| 119 | - this->elements = dest; | |
| 120 | - this->elements[idx] = oh; | |
| 121 | - ++this->n_elements; | |
| 122 | - } | |
| 123 | -} | |
| 124 | - | |
| 125 | -SparseOHArray | |
| 126 | -SparseOHArray::copy() | |
| 127 | -{ | |
| 128 | - SparseOHArray result; | |
| 129 | - result.n_elements = this->n_elements; | |
| 130 | - for (auto const& element: this->elements) { | |
| 131 | - auto value = element.second; | |
| 132 | - result.elements[element.first] = | |
| 133 | - value.isIndirect() ? value : value.shallowCopy(); | |
| 134 | - } | |
| 135 | - return result; | |
| 136 | -} | |
| 137 | - | |
| 138 | -SparseOHArray::const_iterator | |
| 139 | -SparseOHArray::begin() const | |
| 140 | -{ | |
| 141 | - return this->elements.begin(); | |
| 142 | -} | |
| 143 | - | |
| 144 | -SparseOHArray::const_iterator | |
| 145 | -SparseOHArray::end() const | |
| 146 | -{ | |
| 147 | - return this->elements.end(); | |
| 148 | -} |
libqpdf/qpdf/QPDF_Array.hh
| ... | ... | @@ -3,8 +3,7 @@ |
| 3 | 3 | |
| 4 | 4 | #include <qpdf/QPDFValue.hh> |
| 5 | 5 | |
| 6 | -#include <qpdf/SparseOHArray.hh> | |
| 7 | -#include <list> | |
| 6 | +#include <map> | |
| 8 | 7 | #include <vector> |
| 9 | 8 | |
| 10 | 9 | class QPDF_Array: public QPDFValue |
| ... | ... | @@ -14,34 +13,37 @@ class QPDF_Array: public QPDFValue |
| 14 | 13 | static std::shared_ptr<QPDFObject> |
| 15 | 14 | create(std::vector<QPDFObjectHandle> const& items); |
| 16 | 15 | static std::shared_ptr<QPDFObject> |
| 17 | - create(std::vector<std::shared_ptr<QPDFObject>>&& items); | |
| 18 | - static std::shared_ptr<QPDFObject> create(SparseOHArray const& items); | |
| 16 | + create(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); | |
| 19 | 17 | virtual std::shared_ptr<QPDFObject> copy(bool shallow = false); |
| 20 | 18 | virtual std::string unparse(); |
| 21 | 19 | virtual JSON getJSON(int json_version); |
| 22 | 20 | virtual void disconnect(); |
| 23 | 21 | |
| 24 | - int getNItems() const; | |
| 25 | - QPDFObjectHandle getItem(int n) const; | |
| 26 | - void getAsVector(std::vector<QPDFObjectHandle>&) const; | |
| 27 | - | |
| 28 | - void setItem(int, QPDFObjectHandle const&); | |
| 22 | + int | |
| 23 | + size() const noexcept | |
| 24 | + { | |
| 25 | + return sparse ? sp_size : int(elements.size()); | |
| 26 | + } | |
| 27 | + QPDFObjectHandle at(int n) const noexcept; | |
| 28 | + bool setAt(int n, QPDFObjectHandle const& oh); | |
| 29 | + std::vector<QPDFObjectHandle> getAsVector() const; | |
| 29 | 30 | void setFromVector(std::vector<QPDFObjectHandle> const& items); |
| 30 | - void setFromVector(std::vector<std::shared_ptr<QPDFObject>>&& items); | |
| 31 | - void insertItem(int at, QPDFObjectHandle const& item); | |
| 32 | - void appendItem(QPDFObjectHandle const& item); | |
| 33 | - void eraseItem(int at); | |
| 34 | - | |
| 35 | - // Helper methods for QPDF and QPDFObjectHandle -- these are | |
| 36 | - // public methods since the whole class is not part of the public | |
| 37 | - // API. Otherwise, these would be wrapped in accessor classes. | |
| 38 | - void addExplicitElementsToList(std::list<QPDFObjectHandle>&) const; | |
| 31 | + bool insert(int at, QPDFObjectHandle const& item); | |
| 32 | + void push_back(QPDFObjectHandle const& item); | |
| 33 | + bool erase(int at); | |
| 39 | 34 | |
| 40 | 35 | private: |
| 36 | + QPDF_Array(); | |
| 37 | + QPDF_Array(QPDF_Array const&); | |
| 41 | 38 | QPDF_Array(std::vector<QPDFObjectHandle> const& items); |
| 42 | - QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items); | |
| 43 | - QPDF_Array(SparseOHArray const& items); | |
| 44 | - SparseOHArray elements; | |
| 39 | + QPDF_Array(std::vector<std::shared_ptr<QPDFObject>>&& items, bool sparse); | |
| 40 | + | |
| 41 | + void checkOwnership(QPDFObjectHandle const& item) const; | |
| 42 | + | |
| 43 | + bool sparse{false}; | |
| 44 | + int sp_size{0}; | |
| 45 | + std::map<int, std::shared_ptr<QPDFObject>> sp_elements; | |
| 46 | + std::vector<std::shared_ptr<QPDFObject>> elements; | |
| 45 | 47 | }; |
| 46 | 48 | |
| 47 | 49 | #endif // QPDF_ARRAY_HH | ... | ... |
libqpdf/qpdf/SparseOHArray.hh deleted
| 1 | -#ifndef QPDF_SPARSEOHARRAY_HH | |
| 2 | -#define QPDF_SPARSEOHARRAY_HH | |
| 3 | - | |
| 4 | -#include <qpdf/QPDFObjectHandle.hh> | |
| 5 | -#include <unordered_map> | |
| 6 | - | |
| 7 | -class QPDF_Array; | |
| 8 | - | |
| 9 | -class SparseOHArray | |
| 10 | -{ | |
| 11 | - public: | |
| 12 | - SparseOHArray(); | |
| 13 | - size_t size() const; | |
| 14 | - void append(QPDFObjectHandle oh); | |
| 15 | - void append(std::shared_ptr<QPDFObject>&& obj); | |
| 16 | - QPDFObjectHandle at(size_t idx) const; | |
| 17 | - void remove_last(); | |
| 18 | - void setAt(size_t idx, QPDFObjectHandle oh); | |
| 19 | - void erase(size_t idx); | |
| 20 | - void insert(size_t idx, QPDFObjectHandle oh); | |
| 21 | - SparseOHArray copy(); | |
| 22 | - void disconnect(); | |
| 23 | - | |
| 24 | - typedef std::unordered_map<size_t, QPDFObjectHandle>::const_iterator | |
| 25 | - const_iterator; | |
| 26 | - const_iterator begin() const; | |
| 27 | - const_iterator end() const; | |
| 28 | - | |
| 29 | - private: | |
| 30 | - friend class QPDF_Array; | |
| 31 | - std::unordered_map<size_t, QPDFObjectHandle> elements; | |
| 32 | - size_t n_elements; | |
| 33 | -}; | |
| 34 | - | |
| 35 | -#endif // QPDF_SPARSEOHARRAY_HH |
libtests/sparse_array.cc
| 1 | 1 | #include <qpdf/assert_test.h> |
| 2 | 2 | |
| 3 | -#include <qpdf/SparseOHArray.hh> | |
| 3 | +#include <qpdf/QPDFObjectHandle.hh> | |
| 4 | +#include <qpdf/QPDFObject_private.hh> | |
| 5 | +#include <qpdf/QPDF_Array.hh> | |
| 4 | 6 | #include <iostream> |
| 5 | 7 | |
| 6 | 8 | int |
| 7 | 9 | main() |
| 8 | 10 | { |
| 9 | - SparseOHArray a; | |
| 11 | + auto obj = QPDF_Array::create({}, true); | |
| 12 | + QPDF_Array& a = *obj->as<QPDF_Array>(); | |
| 13 | + | |
| 10 | 14 | assert(a.size() == 0); |
| 11 | 15 | |
| 12 | - a.append(QPDFObjectHandle::parse("1")); | |
| 13 | - a.append(QPDFObjectHandle::parse("(potato)")); | |
| 14 | - a.append(QPDFObjectHandle::parse("null")); | |
| 15 | - a.append(QPDFObjectHandle::parse("null")); | |
| 16 | - a.append(QPDFObjectHandle::parse("/Quack")); | |
| 16 | + a.push_back(QPDFObjectHandle::parse("1")); | |
| 17 | + a.push_back(QPDFObjectHandle::parse("(potato)")); | |
| 18 | + a.push_back(QPDFObjectHandle::parse("null")); | |
| 19 | + a.push_back(QPDFObjectHandle::parse("null")); | |
| 20 | + a.push_back(QPDFObjectHandle::parse("/Quack")); | |
| 17 | 21 | assert(a.size() == 5); |
| 18 | 22 | assert(a.at(0).isInteger() && (a.at(0).getIntValue() == 1)); |
| 19 | 23 | assert(a.at(1).isString() && (a.at(1).getStringValue() == "potato")); |
| ... | ... | @@ -60,20 +64,20 @@ main() |
| 60 | 64 | a.setAt(4, QPDFObjectHandle::newNull()); |
| 61 | 65 | assert(a.at(4).isNull()); |
| 62 | 66 | |
| 63 | - a.remove_last(); | |
| 67 | + a.erase(a.size() - 1); | |
| 64 | 68 | assert(a.size() == 5); |
| 65 | 69 | assert(a.at(0).isName() && (a.at(0).getName() == "/First")); |
| 66 | 70 | assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); |
| 67 | 71 | assert(a.at(3).isName() && (a.at(3).getName() == "/Third")); |
| 68 | 72 | assert(a.at(4).isNull()); |
| 69 | 73 | |
| 70 | - a.remove_last(); | |
| 74 | + a.erase(a.size() - 1); | |
| 71 | 75 | assert(a.size() == 4); |
| 72 | 76 | assert(a.at(0).isName() && (a.at(0).getName() == "/First")); |
| 73 | 77 | assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); |
| 74 | 78 | assert(a.at(3).isName() && (a.at(3).getName() == "/Third")); |
| 75 | 79 | |
| 76 | - a.remove_last(); | |
| 80 | + a.erase(a.size() - 1); | |
| 77 | 81 | assert(a.size() == 3); |
| 78 | 82 | assert(a.at(0).isName() && (a.at(0).getName() == "/First")); |
| 79 | 83 | assert(a.at(1).isInteger() && (a.at(1).getIntValue() == 1)); | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -303,8 +303,10 @@ QPDFObjectHandle array treating as empty 0 |
| 303 | 303 | QPDFObjectHandle array null for non-array 0 |
| 304 | 304 | QPDFObjectHandle array treating as empty vector 0 |
| 305 | 305 | QPDFObjectHandle array ignoring set item 0 |
| 306 | +QPDFObjectHandle set array bounds 0 | |
| 306 | 307 | QPDFObjectHandle array ignoring replace items 0 |
| 307 | 308 | QPDFObjectHandle array ignoring insert item 0 |
| 309 | +QPDFObjectHandle insert array bounds 0 | |
| 308 | 310 | QPDFObjectHandle array ignoring append item 0 |
| 309 | 311 | QPDFObjectHandle array ignoring erase item 0 |
| 310 | 312 | QPDFObjectHandle dictionary false for hasKey 0 | ... | ... |
qpdf/qtest/qpdf/object-types-os.out
| ... | ... | @@ -5,6 +5,8 @@ WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operatio |
| 5 | 5 | WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to append item |
| 6 | 6 | WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item |
| 7 | 7 | WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to erase out of bounds array item |
| 8 | +WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to insert out of bounds array item | |
| 9 | +WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 384: ignoring attempt to set out of bounds array item | |
| 8 | 10 | WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to erase item |
| 9 | 11 | WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to insert item |
| 10 | 12 | WARNING: object-types-os.pdf object stream 1, object 7 0 at offset 429: operation for array attempted on object of type integer: ignoring attempt to replace items | ... | ... |
qpdf/qtest/qpdf/object-types.out
| ... | ... | @@ -5,6 +5,8 @@ WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempt |
| 5 | 5 | WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to append item |
| 6 | 6 | WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item |
| 7 | 7 | WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to erase out of bounds array item |
| 8 | +WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to insert out of bounds array item | |
| 9 | +WARNING: object-types.pdf, object 8 0 at offset 717: ignoring attempt to set out of bounds array item | |
| 8 | 10 | WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to erase item |
| 9 | 11 | WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to insert item |
| 10 | 12 | WARNING: object-types.pdf, object 8 0 at offset 669: operation for array attempted on object of type integer: ignoring attempt to replace items | ... | ... |
qpdf/qtest/qpdf/test88.out
qpdf/test_driver.cc
| ... | ... | @@ -1506,6 +1506,8 @@ test_42(QPDF& pdf, char const* arg2) |
| 1506 | 1506 | integer.appendItem(null); |
| 1507 | 1507 | array.eraseItem(-1); |
| 1508 | 1508 | array.eraseItem(16059); |
| 1509 | + array.insertItem(42, "/Dontpanic"_qpdf); | |
| 1510 | + array.setArrayItem(42, "/Dontpanic"_qpdf); | |
| 1509 | 1511 | integer.eraseItem(0); |
| 1510 | 1512 | integer.insertItem(0, null); |
| 1511 | 1513 | integer.setArrayFromVector(std::vector<QPDFObjectHandle>()); |
| ... | ... | @@ -3282,6 +3284,7 @@ test_88(QPDF& pdf, char const* arg2) |
| 3282 | 3284 | auto arr2 = pdf.getRoot().replaceKeyAndGetNew("/QTest", "[1 2]"_qpdf); |
| 3283 | 3285 | arr2.setObjectDescription(&pdf, "test array"); |
| 3284 | 3286 | assert(arr2.eraseItemAndGetOld(50).isNull()); |
| 3287 | + assert(pdf.getRoot().eraseItemAndGetOld(0).isNull()); | |
| 3285 | 3288 | } |
| 3286 | 3289 | |
| 3287 | 3290 | static void | ... | ... |