Commit 38afdcea7b08226aa6cc12a8fcda93a6f35a0a18

Authored by Jay Berkenbilt
1 parent 07afb668

Add QPDFObjectHandle::unsafeShallowCopy

ChangeLog
1 2020-04-02 Jay Berkenbilt <ejb@ql.org> 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 * Remove Members class indirection for QPDFObjectHandle. Those are 7 * Remove Members class indirection for QPDFObjectHandle. Those are
4 copied and assigned too often, and that change caused a very 8 copied and assigned too often, and that change caused a very
5 substantial performance hit. 9 substantial performance hit.
include/qpdf/QPDFObjectHandle.hh
@@ -670,10 +670,24 @@ class QPDFObjectHandle @@ -670,10 +670,24 @@ class QPDFObjectHandle
670 // traverse across indirect object boundaries. That means that, 670 // traverse across indirect object boundaries. That means that,
671 // for dictionaries and arrays, any keys or items that were 671 // for dictionaries and arrays, any keys or items that were
672 // indirect objects will still be indirect objects that point to 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 QPDF_DLL 677 QPDF_DLL
675 QPDFObjectHandle shallowCopy(); 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 // Mutator methods. Use with caution. 691 // Mutator methods. Use with caution.
678 692
679 // Recursively copy this object, making it direct. Throws an 693 // Recursively copy this object, making it direct. Throws an
@@ -1053,7 +1067,9 @@ class QPDFObjectHandle @@ -1053,7 +1067,9 @@ class QPDFObjectHandle
1053 void objectWarning(std::string const& warning); 1067 void objectWarning(std::string const& warning);
1054 void assertType(char const* type_name, bool istype); 1068 void assertType(char const* type_name, bool istype);
1055 void dereference(); 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 void releaseResolved(); 1073 void releaseResolved();
1058 static void setObjectDescriptionFromInput( 1074 static void setObjectDescriptionFromInput(
1059 QPDFObjectHandle, QPDF*, std::string const&, 1075 QPDFObjectHandle, QPDF*, std::string const&,
libqpdf/QPDFObjectHandle.cc
@@ -2461,6 +2461,23 @@ QPDFObjectHandle::hasObjectDescription() @@ -2461,6 +2461,23 @@ QPDFObjectHandle::hasObjectDescription()
2461 QPDFObjectHandle 2461 QPDFObjectHandle
2462 QPDFObjectHandle::shallowCopy() 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 assertInitialized(); 2481 assertInitialized();
2465 2482
2466 if (isStream()) 2483 if (isStream())
@@ -2470,7 +2487,6 @@ QPDFObjectHandle::shallowCopy() @@ -2470,7 +2487,6 @@ QPDFObjectHandle::shallowCopy()
2470 "attempt to make a shallow copy of a stream"); 2487 "attempt to make a shallow copy of a stream");
2471 } 2488 }
2472 2489
2473 - QPDFObjectHandle new_obj;  
2474 if (isArray()) 2490 if (isArray())
2475 { 2491 {
2476 QTC::TC("qpdf", "QPDFObjectHandle shallow copy array"); 2492 QTC::TC("qpdf", "QPDFObjectHandle shallow copy array");
@@ -2491,13 +2507,12 @@ QPDFObjectHandle::shallowCopy() @@ -2491,13 +2507,12 @@ QPDFObjectHandle::shallowCopy()
2491 } 2507 }
2492 2508
2493 std::set<QPDFObjGen> visited; 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 void 2513 void
2499 QPDFObjectHandle::copyObject(std::set<QPDFObjGen>& visited, 2514 QPDFObjectHandle::copyObject(std::set<QPDFObjGen>& visited,
2500 - bool cross_indirect) 2515 + bool cross_indirect, bool first_level_only)
2501 { 2516 {
2502 assertInitialized(); 2517 assertInitialized();
2503 2518
@@ -2573,9 +2588,11 @@ QPDFObjectHandle::copyObject(std::set&lt;QPDFObjGen&gt;&amp; visited, @@ -2573,9 +2588,11 @@ QPDFObjectHandle::copyObject(std::set&lt;QPDFObjGen&gt;&amp; visited,
2573 for (int i = 0; i < n; ++i) 2588 for (int i = 0; i < n; ++i)
2574 { 2589 {
2575 items.push_back(getArrayItem(i)); 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 new_obj = new QPDF_Array(items); 2598 new_obj = new QPDF_Array(items);
@@ -2589,9 +2606,11 @@ QPDFObjectHandle::copyObject(std::set&lt;QPDFObjGen&gt;&amp; visited, @@ -2589,9 +2606,11 @@ QPDFObjectHandle::copyObject(std::set&lt;QPDFObjGen&gt;&amp; visited,
2589 iter != keys.end(); ++iter) 2606 iter != keys.end(); ++iter)
2590 { 2607 {
2591 items[*iter] = getKey(*iter); 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 new_obj = new QPDF_Dictionary(items); 2616 new_obj = new QPDF_Dictionary(items);
@@ -2614,7 +2633,7 @@ void @@ -2614,7 +2633,7 @@ void
2614 QPDFObjectHandle::makeDirect() 2633 QPDFObjectHandle::makeDirect()
2615 { 2634 {
2616 std::set<QPDFObjGen> visited; 2635 std::set<QPDFObjGen> visited;
2617 - copyObject(visited, true); 2636 + copyObject(visited, true, false);
2618 } 2637 }
2619 2638
2620 void 2639 void