Commit 38afdcea7b08226aa6cc12a8fcda93a6f35a0a18

Authored by Jay Berkenbilt
1 parent 07afb668

Add QPDFObjectHandle::unsafeShallowCopy

ChangeLog
1 1 2020-04-02 Jay Berkenbilt <ejb@ql.org>
2 2  
  3 + * Add method QPDFObjectHandle::unsafeShallowCopy for copying only
  4 + top-level dictionary keys or array items. See comments in
  5 + QPDFObjectHandle.hh for when this should be used.
  6 +
3 7 * Remove Members class indirection for QPDFObjectHandle. Those are
4 8 copied and assigned too often, and that change caused a very
5 9 substantial performance hit.
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -670,10 +670,24 @@ class QPDFObjectHandle
670 670 // traverse across indirect object boundaries. That means that,
671 671 // for dictionaries and arrays, any keys or items that were
672 672 // indirect objects will still be indirect objects that point to
673   - // the same place.
  673 + // the same place. In the strictest sense, this is not a shallow
  674 + // copy because it recursively descends arrays and dictionaries;
  675 + // it just doesn't cross over indirect objects. See also
  676 + // unsafeShallowCopy().
674 677 QPDF_DLL
675 678 QPDFObjectHandle shallowCopy();
676 679  
  680 + // Create a true shallow copy of an array or dictionary, just
  681 + // copying the immediate items (array) or keys (dictionary). This
  682 + // is "unsafe" because, if you *modify* any of the items in the
  683 + // copy, you are modifying the original, which is almost never
  684 + // what you want. However, if your intention is merely to
  685 + // *replace* top-level items or keys and not to modify lower-level
  686 + // items in the copy, this method is much faster than
  687 + // shallowCopy().
  688 + QPDF_DLL
  689 + QPDFObjectHandle unsafeShallowCopy();
  690 +
677 691 // Mutator methods. Use with caution.
678 692  
679 693 // Recursively copy this object, making it direct. Throws an
... ... @@ -1053,7 +1067,9 @@ class QPDFObjectHandle
1053 1067 void objectWarning(std::string const& warning);
1054 1068 void assertType(char const* type_name, bool istype);
1055 1069 void dereference();
1056   - void copyObject(std::set<QPDFObjGen>& visited, bool cross_indirect);
  1070 + void copyObject(std::set<QPDFObjGen>& visited, bool cross_indirect,
  1071 + bool first_level_only);
  1072 + void shallowCopyInternal(QPDFObjectHandle& oh, bool first_level_only);
1057 1073 void releaseResolved();
1058 1074 static void setObjectDescriptionFromInput(
1059 1075 QPDFObjectHandle, QPDF*, std::string const&,
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -2461,6 +2461,23 @@ QPDFObjectHandle::hasObjectDescription()
2461 2461 QPDFObjectHandle
2462 2462 QPDFObjectHandle::shallowCopy()
2463 2463 {
  2464 + QPDFObjectHandle result;
  2465 + shallowCopyInternal(result, false);
  2466 + return result;
  2467 +}
  2468 +
  2469 +QPDFObjectHandle
  2470 +QPDFObjectHandle::unsafeShallowCopy()
  2471 +{
  2472 + QPDFObjectHandle result;
  2473 + shallowCopyInternal(result, true);
  2474 + return result;
  2475 +}
  2476 +
  2477 +void
  2478 +QPDFObjectHandle::shallowCopyInternal(QPDFObjectHandle& new_obj,
  2479 + bool first_level_only)
  2480 +{
2464 2481 assertInitialized();
2465 2482  
2466 2483 if (isStream())
... ... @@ -2470,7 +2487,6 @@ QPDFObjectHandle::shallowCopy()
2470 2487 "attempt to make a shallow copy of a stream");
2471 2488 }
2472 2489  
2473   - QPDFObjectHandle new_obj;
2474 2490 if (isArray())
2475 2491 {
2476 2492 QTC::TC("qpdf", "QPDFObjectHandle shallow copy array");
... ... @@ -2491,13 +2507,12 @@ QPDFObjectHandle::shallowCopy()
2491 2507 }
2492 2508  
2493 2509 std::set<QPDFObjGen> visited;
2494   - new_obj.copyObject(visited, false);
2495   - return new_obj;
  2510 + new_obj.copyObject(visited, false, first_level_only);
2496 2511 }
2497 2512  
2498 2513 void
2499 2514 QPDFObjectHandle::copyObject(std::set<QPDFObjGen>& visited,
2500   - bool cross_indirect)
  2515 + bool cross_indirect, bool first_level_only)
2501 2516 {
2502 2517 assertInitialized();
2503 2518  
... ... @@ -2573,9 +2588,11 @@ QPDFObjectHandle::copyObject(std::set&lt;QPDFObjGen&gt;&amp; visited,
2573 2588 for (int i = 0; i < n; ++i)
2574 2589 {
2575 2590 items.push_back(getArrayItem(i));
2576   - if (cross_indirect || (! items.back().isIndirect()))
  2591 + if ((! first_level_only) &&
  2592 + (cross_indirect || (! items.back().isIndirect())))
2577 2593 {
2578   - items.back().copyObject(visited, cross_indirect);
  2594 + items.back().copyObject(
  2595 + visited, cross_indirect, first_level_only);
2579 2596 }
2580 2597 }
2581 2598 new_obj = new QPDF_Array(items);
... ... @@ -2589,9 +2606,11 @@ QPDFObjectHandle::copyObject(std::set&lt;QPDFObjGen&gt;&amp; visited,
2589 2606 iter != keys.end(); ++iter)
2590 2607 {
2591 2608 items[*iter] = getKey(*iter);
2592   - if (cross_indirect || (! items[*iter].isIndirect()))
  2609 + if ((! first_level_only) &&
  2610 + (cross_indirect || (! items[*iter].isIndirect())))
2593 2611 {
2594   - items[*iter].copyObject(visited, cross_indirect);
  2612 + items[*iter].copyObject(
  2613 + visited, cross_indirect, first_level_only);
2595 2614 }
2596 2615 }
2597 2616 new_obj = new QPDF_Dictionary(items);
... ... @@ -2614,7 +2633,7 @@ void
2614 2633 QPDFObjectHandle::makeDirect()
2615 2634 {
2616 2635 std::set<QPDFObjGen> visited;
2617   - copyObject(visited, true);
  2636 + copyObject(visited, true, false);
2618 2637 }
2619 2638  
2620 2639 void
... ...