Commit 884d36becd8403eefa9cbaceb7d85b37ecaa61c5

Authored by m-holger
Committed by GitHub
2 parents 00ad6f55 f78b9993

Merge pull request #1380 from m-holger/object

Move QPDFObject::copy, disconnect, unparse and write_json to BaseHandle
include/qpdf/ObjectHandle.hh
@@ -47,6 +47,8 @@ namespace qpdf @@ -47,6 +47,8 @@ namespace qpdf
47 // QPDFObjGen and bool. 47 // QPDFObjGen and bool.
48 class BaseHandle 48 class BaseHandle
49 { 49 {
  50 + friend class QPDF;
  51 +
50 public: 52 public:
51 explicit inline operator bool() const; 53 explicit inline operator bool() const;
52 inline operator QPDFObjectHandle() const; 54 inline operator QPDFObjectHandle() const;
@@ -54,12 +56,18 @@ namespace qpdf @@ -54,12 +56,18 @@ namespace qpdf
54 56
55 // The rest of the header file is for qpdf internal use only. 57 // The rest of the header file is for qpdf internal use only.
56 58
  59 + std::shared_ptr<QPDFObject> copy(bool shallow = false) const;
  60 + // Recursively remove association with any QPDF object. This method may only be called
  61 + // during final destruction.
  62 + void disconnect(bool only_direct = true);
57 inline QPDFObjGen id_gen() const; 63 inline QPDFObjGen id_gen() const;
58 inline bool indirect() const; 64 inline bool indirect() const;
59 inline bool null() const; 65 inline bool null() const;
60 inline QPDF* qpdf() const; 66 inline QPDF* qpdf() const;
61 inline qpdf_object_type_e raw_type_code() const; 67 inline qpdf_object_type_e raw_type_code() const;
62 inline qpdf_object_type_e type_code() const; 68 inline qpdf_object_type_e type_code() const;
  69 + std::string unparse() const;
  70 + void write_json(int json_version, JSON::Writer& p) const;
63 71
64 protected: 72 protected:
65 BaseHandle() = default; 73 BaseHandle() = default;
include/qpdf/QPDFObjectHandle.hh
@@ -1242,19 +1242,6 @@ class QPDFObjectHandle final: public qpdf::BaseHandle @@ -1242,19 +1242,6 @@ class QPDFObjectHandle final: public qpdf::BaseHandle
1242 QPDF_DLL 1242 QPDF_DLL
1243 void warnIfPossible(std::string const& warning) const; 1243 void warnIfPossible(std::string const& warning) const;
1244 1244
1245 - // Provide access to specific classes for recursive disconnected().  
1246 - class DisconnectAccess  
1247 - {  
1248 - friend class QPDFObject;  
1249 -  
1250 - private:  
1251 - static void  
1252 - disconnect(QPDFObjectHandle o)  
1253 - {  
1254 - o.disconnect();  
1255 - }  
1256 - };  
1257 -  
1258 // Convenience routine: Throws if the assumption is violated. Your code will be better if you 1245 // Convenience routine: Throws if the assumption is violated. Your code will be better if you
1259 // call one of the isType methods and handle the case of the type being wrong, but these can be 1246 // call one of the isType methods and handle the case of the type being wrong, but these can be
1260 // convenient if you have already verified the type. 1247 // convenient if you have already verified the type.
@@ -1354,7 +1341,6 @@ class QPDFObjectHandle final: public qpdf::BaseHandle @@ -1354,7 +1341,6 @@ class QPDFObjectHandle final: public qpdf::BaseHandle
1354 void objectWarning(std::string const& warning) const; 1341 void objectWarning(std::string const& warning) const;
1355 void assertType(char const* type_name, bool istype) const; 1342 void assertType(char const* type_name, bool istype) const;
1356 void makeDirect(QPDFObjGen::set& visited, bool stop_at_streams); 1343 void makeDirect(QPDFObjGen::set& visited, bool stop_at_streams);
1357 - void disconnect();  
1358 void setParsedOffset(qpdf_offset_t offset); 1344 void setParsedOffset(qpdf_offset_t offset);
1359 void parseContentStream_internal(std::string const& description, ParserCallbacks* callbacks); 1345 void parseContentStream_internal(std::string const& description, ParserCallbacks* callbacks);
1360 static void parseContentStream_data( 1346 static void parseContentStream_data(
libqpdf/QPDF.cc
@@ -212,6 +212,25 @@ QPDF::QPDF() : @@ -212,6 +212,25 @@ QPDF::QPDF() :
212 m->unique_id = unique_id.fetch_add(1ULL); 212 m->unique_id = unique_id.fetch_add(1ULL);
213 } 213 }
214 214
  215 +// Provide access to disconnect(). Disconnect will in due course be merged into the current ObjCache
  216 +// (future Objects::Entry) to centralize all QPDF access to QPDFObject.
  217 +class Disconnect: BaseHandle
  218 +{
  219 + public:
  220 + Disconnect(std::shared_ptr<QPDFObject> const& obj) :
  221 + BaseHandle(obj)
  222 + {
  223 + }
  224 + void
  225 + disconnect()
  226 + {
  227 + BaseHandle::disconnect(false);
  228 + if (raw_type_code() != ::ot_null) {
  229 + obj->value = QPDF_Destroyed();
  230 + }
  231 + }
  232 +};
  233 +
215 QPDF::~QPDF() 234 QPDF::~QPDF()
216 { 235 {
217 // If two objects are mutually referential (through each object having an array or dictionary 236 // If two objects are mutually referential (through each object having an array or dictionary
@@ -228,10 +247,7 @@ QPDF::~QPDF() @@ -228,10 +247,7 @@ QPDF::~QPDF()
228 // the xref table anyway just to prevent any possibility of resolve() succeeding. 247 // the xref table anyway just to prevent any possibility of resolve() succeeding.
229 m->xref_table.clear(); 248 m->xref_table.clear();
230 for (auto const& iter: m->obj_cache) { 249 for (auto const& iter: m->obj_cache) {
231 - iter.second.object->disconnect();  
232 - if (iter.second.object->getTypeCode() != ::ot_null) {  
233 - iter.second.object->destroy();  
234 - } 250 + Disconnect(iter.second.object).disconnect();
235 } 251 }
236 } 252 }
237 253
libqpdf/QPDFObjectHandle.cc
@@ -289,29 +289,29 @@ Name::normalize(std::string const&amp; name) @@ -289,29 +289,29 @@ Name::normalize(std::string const&amp; name)
289 } 289 }
290 290
291 std::shared_ptr<QPDFObject> 291 std::shared_ptr<QPDFObject>
292 -QPDFObject::copy(bool shallow) 292 +BaseHandle::copy(bool shallow) const
293 { 293 {
294 - switch (getResolvedTypeCode()) { 294 + switch (type_code()) {
295 case ::ot_uninitialized: 295 case ::ot_uninitialized:
296 throw std::logic_error("QPDFObjectHandle: attempting to copy an uninitialized object"); 296 throw std::logic_error("QPDFObjectHandle: attempting to copy an uninitialized object");
297 return {}; // does not return 297 return {}; // does not return
298 case ::ot_reserved: 298 case ::ot_reserved:
299 - return create<QPDF_Reserved>(); 299 + return QPDFObject::create<QPDF_Reserved>();
300 case ::ot_null: 300 case ::ot_null:
301 - return create<QPDF_Null>(); 301 + return QPDFObject::create<QPDF_Null>();
302 case ::ot_boolean: 302 case ::ot_boolean:
303 - return create<QPDF_Bool>(std::get<QPDF_Bool>(value).val); 303 + return QPDFObject::create<QPDF_Bool>(std::get<QPDF_Bool>(obj->value).val);
304 case ::ot_integer: 304 case ::ot_integer:
305 - return create<QPDF_Integer>(std::get<QPDF_Integer>(value).val); 305 + return QPDFObject::create<QPDF_Integer>(std::get<QPDF_Integer>(obj->value).val);
306 case ::ot_real: 306 case ::ot_real:
307 - return create<QPDF_Real>(std::get<QPDF_Real>(value).val); 307 + return QPDFObject::create<QPDF_Real>(std::get<QPDF_Real>(obj->value).val);
308 case ::ot_string: 308 case ::ot_string:
309 - return create<QPDF_String>(std::get<QPDF_String>(value).val); 309 + return QPDFObject::create<QPDF_String>(std::get<QPDF_String>(obj->value).val);
310 case ::ot_name: 310 case ::ot_name:
311 - return create<QPDF_Name>(std::get<QPDF_Name>(value).name); 311 + return QPDFObject::create<QPDF_Name>(std::get<QPDF_Name>(obj->value).name);
312 case ::ot_array: 312 case ::ot_array:
313 { 313 {
314 - auto const& a = std::get<QPDF_Array>(value); 314 + auto const& a = std::get<QPDF_Array>(obj->value);
315 if (shallow) { 315 if (shallow) {
316 return QPDFObject::create<QPDF_Array>(a); 316 return QPDFObject::create<QPDF_Array>(a);
317 } else { 317 } else {
@@ -321,7 +321,7 @@ QPDFObject::copy(bool shallow) @@ -321,7 +321,7 @@ QPDFObject::copy(bool shallow)
321 result.sp = std::make_unique<QPDF_Array::Sparse>(); 321 result.sp = std::make_unique<QPDF_Array::Sparse>();
322 result.sp->size = a.sp->size; 322 result.sp->size = a.sp->size;
323 for (auto const& [idx, oh]: a.sp->elements) { 323 for (auto const& [idx, oh]: a.sp->elements) {
324 - result.sp->elements[idx] = oh.indirect() ? oh : oh.getObj()->copy(); 324 + result.sp->elements[idx] = oh.indirect() ? oh : oh.copy();
325 } 325 }
326 return QPDFObject::create<QPDF_Array>(std::move(result)); 326 return QPDFObject::create<QPDF_Array>(std::move(result));
327 } else { 327 } else {
@@ -329,8 +329,7 @@ QPDFObject::copy(bool shallow) @@ -329,8 +329,7 @@ QPDFObject::copy(bool shallow)
329 result.reserve(a.elements.size()); 329 result.reserve(a.elements.size());
330 for (auto const& element: a.elements) { 330 for (auto const& element: a.elements) {
331 result.emplace_back( 331 result.emplace_back(
332 - element ? (element.indirect() ? element : element.getObj()->copy())  
333 - : element); 332 + element ? (element.indirect() ? element : element.copy()) : element);
334 } 333 }
335 return QPDFObject::create<QPDF_Array>(std::move(result), false); 334 return QPDFObject::create<QPDF_Array>(std::move(result), false);
336 } 335 }
@@ -338,13 +337,13 @@ QPDFObject::copy(bool shallow) @@ -338,13 +337,13 @@ QPDFObject::copy(bool shallow)
338 } 337 }
339 case ::ot_dictionary: 338 case ::ot_dictionary:
340 { 339 {
341 - auto const& d = std::get<QPDF_Dictionary>(value); 340 + auto const& d = std::get<QPDF_Dictionary>(obj->value);
342 if (shallow) { 341 if (shallow) {
343 return QPDFObject::create<QPDF_Dictionary>(d.items); 342 return QPDFObject::create<QPDF_Dictionary>(d.items);
344 } else { 343 } else {
345 std::map<std::string, QPDFObjectHandle> new_items; 344 std::map<std::string, QPDFObjectHandle> new_items;
346 for (auto const& [key, val]: d.items) { 345 for (auto const& [key, val]: d.items) {
347 - new_items[key] = val.indirect() ? val : val.getObj()->copy(); 346 + new_items[key] = val.indirect() ? val : val.copy();
348 } 347 }
349 return QPDFObject::create<QPDF_Dictionary>(new_items); 348 return QPDFObject::create<QPDF_Dictionary>(new_items);
350 } 349 }
@@ -354,9 +353,9 @@ QPDFObject::copy(bool shallow) @@ -354,9 +353,9 @@ QPDFObject::copy(bool shallow)
354 throw std::runtime_error("stream objects cannot be cloned"); 353 throw std::runtime_error("stream objects cannot be cloned");
355 return {}; // does not return 354 return {}; // does not return
356 case ::ot_operator: 355 case ::ot_operator:
357 - return create<QPDF_Operator>(std::get<QPDF_Operator>(value).val); 356 + return QPDFObject::create<QPDF_Operator>(std::get<QPDF_Operator>(obj->value).val);
358 case ::ot_inlineimage: 357 case ::ot_inlineimage:
359 - return create<QPDF_InlineImage>(std::get<QPDF_InlineImage>(value).val); 358 + return QPDFObject::create<QPDF_InlineImage>(std::get<QPDF_InlineImage>(obj->value).val);
360 case ::ot_unresolved: 359 case ::ot_unresolved:
361 throw std::logic_error("QPDFObjectHandle: attempting to unparse a reserved object"); 360 throw std::logic_error("QPDFObjectHandle: attempting to unparse a reserved object");
362 return {}; // does not return 361 return {}; // does not return
@@ -364,15 +363,15 @@ QPDFObject::copy(bool shallow) @@ -364,15 +363,15 @@ QPDFObject::copy(bool shallow)
364 throw std::logic_error("attempted to shallow copy QPDFObjectHandle from destroyed QPDF"); 363 throw std::logic_error("attempted to shallow copy QPDFObjectHandle from destroyed QPDF");
365 return {}; // does not return 364 return {}; // does not return
366 case ::ot_reference: 365 case ::ot_reference:
367 - return qpdf->getObject(og).getObj(); 366 + return obj->qpdf->getObject(obj->og).getObj();
368 } 367 }
369 return {}; // unreachable 368 return {}; // unreachable
370 } 369 }
371 370
372 std::string 371 std::string
373 -QPDFObject::unparse() 372 +BaseHandle::unparse() const
374 { 373 {
375 - switch (getResolvedTypeCode()) { 374 + switch (type_code()) {
376 case ::ot_uninitialized: 375 case ::ot_uninitialized:
377 throw std::logic_error("QPDFObjectHandle: attempting to unparse an uninitialized object"); 376 throw std::logic_error("QPDFObjectHandle: attempting to unparse an uninitialized object");
378 return ""; // does not return 377 return ""; // does not return
@@ -382,18 +381,18 @@ QPDFObject::unparse() @@ -382,18 +381,18 @@ QPDFObject::unparse()
382 case ::ot_null: 381 case ::ot_null:
383 return "null"; 382 return "null";
384 case ::ot_boolean: 383 case ::ot_boolean:
385 - return std::get<QPDF_Bool>(value).val ? "true" : "false"; 384 + return std::get<QPDF_Bool>(obj->value).val ? "true" : "false";
386 case ::ot_integer: 385 case ::ot_integer:
387 - return std::to_string(std::get<QPDF_Integer>(value).val); 386 + return std::to_string(std::get<QPDF_Integer>(obj->value).val);
388 case ::ot_real: 387 case ::ot_real:
389 - return std::get<QPDF_Real>(value).val; 388 + return std::get<QPDF_Real>(obj->value).val;
390 case ::ot_string: 389 case ::ot_string:
391 - return std::get<QPDF_String>(value).unparse(false); 390 + return std::get<QPDF_String>(obj->value).unparse(false);
392 case ::ot_name: 391 case ::ot_name:
393 - return Name::normalize(std::get<QPDF_Name>(value).name); 392 + return Name::normalize(std::get<QPDF_Name>(obj->value).name);
394 case ::ot_array: 393 case ::ot_array:
395 { 394 {
396 - auto const& a = std::get<QPDF_Array>(value); 395 + auto const& a = std::get<QPDF_Array>(obj->value);
397 std::string result = "[ "; 396 std::string result = "[ ";
398 if (a.sp) { 397 if (a.sp) {
399 int next = 0; 398 int next = 0;
@@ -402,9 +401,7 @@ QPDFObject::unparse() @@ -402,9 +401,7 @@ QPDFObject::unparse()
402 for (int j = next; j < key; ++j) { 401 for (int j = next; j < key; ++j) {
403 result += "null "; 402 result += "null ";
404 } 403 }
405 - auto item_og = item.second.id_gen();  
406 - result += item_og.isIndirect() ? item_og.unparse(' ') + " R "  
407 - : item.second.getObj()->unparse() + " "; 404 + result += item.second.unparse() + " ";
408 next = ++key; 405 next = ++key;
409 } 406 }
410 for (int j = next; j < a.sp->size; ++j) { 407 for (int j = next; j < a.sp->size; ++j) {
@@ -412,9 +409,7 @@ QPDFObject::unparse() @@ -412,9 +409,7 @@ QPDFObject::unparse()
412 } 409 }
413 } else { 410 } else {
414 for (auto const& item: a.elements) { 411 for (auto const& item: a.elements) {
415 - auto item_og = item.id_gen();  
416 - result += item_og.isIndirect() ? item_og.unparse(' ') + " R "  
417 - : item.getObj()->unparse() + " "; 412 + result += item.unparse() + " ";
418 } 413 }
419 } 414 }
420 result += "]"; 415 result += "]";
@@ -422,7 +417,7 @@ QPDFObject::unparse() @@ -422,7 +417,7 @@ QPDFObject::unparse()
422 } 417 }
423 case ::ot_dictionary: 418 case ::ot_dictionary:
424 { 419 {
425 - auto const& items = std::get<QPDF_Dictionary>(value).items; 420 + auto const& items = std::get<QPDF_Dictionary>(obj->value).items;
426 std::string result = "<< "; 421 std::string result = "<< ";
427 for (auto& iter: items) { 422 for (auto& iter: items) {
428 if (!iter.second.null()) { 423 if (!iter.second.null()) {
@@ -433,11 +428,11 @@ QPDFObject::unparse() @@ -433,11 +428,11 @@ QPDFObject::unparse()
433 return result; 428 return result;
434 } 429 }
435 case ::ot_stream: 430 case ::ot_stream:
436 - return og.unparse(' ') + " R"; 431 + return obj->og.unparse(' ') + " R";
437 case ::ot_operator: 432 case ::ot_operator:
438 - return std::get<QPDF_Operator>(value).val; 433 + return std::get<QPDF_Operator>(obj->value).val;
439 case ::ot_inlineimage: 434 case ::ot_inlineimage:
440 - return std::get<QPDF_InlineImage>(value).val; 435 + return std::get<QPDF_InlineImage>(obj->value).val;
441 case ::ot_unresolved: 436 case ::ot_unresolved:
442 throw std::logic_error("QPDFObjectHandle: attempting to unparse a unresolved object"); 437 throw std::logic_error("QPDFObjectHandle: attempting to unparse a unresolved object");
443 return ""; // does not return 438 return ""; // does not return
@@ -445,15 +440,15 @@ QPDFObject::unparse() @@ -445,15 +440,15 @@ QPDFObject::unparse()
445 throw std::logic_error("attempted to unparse a QPDFObjectHandle from a destroyed QPDF"); 440 throw std::logic_error("attempted to unparse a QPDFObjectHandle from a destroyed QPDF");
446 return ""; // does not return 441 return ""; // does not return
447 case ::ot_reference: 442 case ::ot_reference:
448 - return og.unparse(' ') + " R"; 443 + return obj->og.unparse(' ') + " R";
449 } 444 }
450 return {}; // unreachable 445 return {}; // unreachable
451 } 446 }
452 447
453 void 448 void
454 -QPDFObject::write_json(int json_version, JSON::Writer& p) 449 +BaseHandle::write_json(int json_version, JSON::Writer& p) const
455 { 450 {
456 - switch (getResolvedTypeCode()) { 451 + switch (type_code()) {
457 case ::ot_uninitialized: 452 case ::ot_uninitialized:
458 throw std::logic_error( 453 throw std::logic_error(
459 "QPDFObjectHandle: attempting to get JSON from a uninitialized object"); 454 "QPDFObjectHandle: attempting to get JSON from a uninitialized object");
@@ -464,14 +459,14 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p) @@ -464,14 +459,14 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p)
464 p << "null"; 459 p << "null";
465 break; 460 break;
466 case ::ot_boolean: 461 case ::ot_boolean:
467 - p << std::get<QPDF_Bool>(value).val; 462 + p << std::get<QPDF_Bool>(obj->value).val;
468 break; 463 break;
469 case ::ot_integer: 464 case ::ot_integer:
470 - p << std::to_string(std::get<QPDF_Integer>(value).val); 465 + p << std::to_string(std::get<QPDF_Integer>(obj->value).val);
471 break; 466 break;
472 case ::ot_real: 467 case ::ot_real:
473 { 468 {
474 - auto const& val = std::get<QPDF_Real>(value).val; 469 + auto const& val = std::get<QPDF_Real>(obj->value).val;
475 if (val.length() == 0) { 470 if (val.length() == 0) {
476 // Can't really happen... 471 // Can't really happen...
477 p << "0"; 472 p << "0";
@@ -488,11 +483,11 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p) @@ -488,11 +483,11 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p)
488 } 483 }
489 break; 484 break;
490 case ::ot_string: 485 case ::ot_string:
491 - std::get<QPDF_String>(value).writeJSON(json_version, p); 486 + std::get<QPDF_String>(obj->value).writeJSON(json_version, p);
492 break; 487 break;
493 case ::ot_name: 488 case ::ot_name:
494 { 489 {
495 - auto const& n = std::get<QPDF_Name>(value); 490 + auto const& n = std::get<QPDF_Name>(obj->value);
496 // For performance reasons this code is duplicated in QPDF_Dictionary::writeJSON. When 491 // For performance reasons this code is duplicated in QPDF_Dictionary::writeJSON. When
497 // updating this method make sure QPDF_Dictionary is also update. 492 // updating this method make sure QPDF_Dictionary is also update.
498 if (json_version == 1) { 493 if (json_version == 1) {
@@ -512,7 +507,7 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p) @@ -512,7 +507,7 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p)
512 break; 507 break;
513 case ::ot_array: 508 case ::ot_array:
514 { 509 {
515 - auto const& a = std::get<QPDF_Array>(value); 510 + auto const& a = std::get<QPDF_Array>(obj->value);
516 p.writeStart('['); 511 p.writeStart('[');
517 if (a.sp) { 512 if (a.sp) {
518 int next = 0; 513 int next = 0;
@@ -526,7 +521,7 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p) @@ -526,7 +521,7 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p)
526 if (item_og.isIndirect()) { 521 if (item_og.isIndirect()) {
527 p << "\"" << item_og.unparse(' ') << " R\""; 522 p << "\"" << item_og.unparse(' ') << " R\"";
528 } else { 523 } else {
529 - item.second.getObj()->write_json(json_version, p); 524 + item.second.write_json(json_version, p);
530 } 525 }
531 next = ++key; 526 next = ++key;
532 } 527 }
@@ -540,7 +535,7 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p) @@ -540,7 +535,7 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p)
540 if (item_og.isIndirect()) { 535 if (item_og.isIndirect()) {
541 p << "\"" << item_og.unparse(' ') << " R\""; 536 p << "\"" << item_og.unparse(' ') << " R\"";
542 } else { 537 } else {
543 - item.getObj()->write_json(json_version, p); 538 + item.write_json(json_version, p);
544 } 539 }
545 } 540 }
546 } 541 }
@@ -549,7 +544,7 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p) @@ -549,7 +544,7 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p)
549 break; 544 break;
550 case ::ot_dictionary: 545 case ::ot_dictionary:
551 { 546 {
552 - auto const& d = std::get<QPDF_Dictionary>(value); 547 + auto const& d = std::get<QPDF_Dictionary>(obj->value);
553 p.writeStart('{'); 548 p.writeStart('{');
554 for (auto& iter: d.items) { 549 for (auto& iter: d.items) {
555 if (!iter.second.null()) { 550 if (!iter.second.null()) {
@@ -574,10 +569,10 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p) @@ -574,10 +569,10 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p)
574 } 569 }
575 break; 570 break;
576 case ::ot_stream: 571 case ::ot_stream:
577 - std::get<QPDF_Stream>(value).m->stream_dict.writeJSON(json_version, p); 572 + std::get<QPDF_Stream>(obj->value).m->stream_dict.writeJSON(json_version, p);
578 break; 573 break;
579 case ::ot_reference: 574 case ::ot_reference:
580 - p << "\"" << getObjGen().unparse(' ') << " R\""; 575 + p << "\"" << obj->og.unparse(' ') << " R\"";
581 break; 576 break;
582 default: 577 default:
583 throw std::logic_error("attempted to write an unsuitable object as JSON"); 578 throw std::logic_error("attempted to write an unsuitable object as JSON");
@@ -585,48 +580,49 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p) @@ -585,48 +580,49 @@ QPDFObject::write_json(int json_version, JSON::Writer&amp; p)
585 } 580 }
586 581
587 void 582 void
588 -QPDFObject::disconnect() 583 +BaseHandle::disconnect(bool only_direct)
589 { 584 {
590 - // Disconnect an object from its owning QPDF. This is called by QPDF's destructor. 585 + // QPDF::~QPDF() calls disconnect for indirect objects, so we don't do that here.
  586 + if (only_direct && indirect()) {
  587 + return;
  588 + }
591 589
592 - switch (getTypeCode()) { 590 + switch (raw_type_code()) {
593 case ::ot_array: 591 case ::ot_array:
594 { 592 {
595 - auto& a = std::get<QPDF_Array>(value); 593 + auto& a = std::get<QPDF_Array>(obj->value);
596 if (a.sp) { 594 if (a.sp) {
597 for (auto& item: a.sp->elements) { 595 for (auto& item: a.sp->elements) {
598 - auto& obj = item.second;  
599 - if (!obj.indirect()) {  
600 - obj.getObj()->disconnect();  
601 - } 596 + item.second.disconnect();
602 } 597 }
603 } else { 598 } else {
604 - for (auto& obj: a.elements) {  
605 - if (!obj.indirect()) {  
606 - obj.getObj()->disconnect();  
607 - } 599 + for (auto& oh: a.elements) {
  600 + oh.disconnect();
608 } 601 }
609 } 602 }
610 } 603 }
611 break; 604 break;
612 case ::ot_dictionary: 605 case ::ot_dictionary:
613 - for (auto& iter: std::get<QPDF_Dictionary>(value).items) {  
614 - QPDFObjectHandle::DisconnectAccess::disconnect(iter.second); 606 + for (auto& iter: std::get<QPDF_Dictionary>(obj->value).items) {
  607 + iter.second.disconnect();
615 } 608 }
616 break; 609 break;
617 case ::ot_stream: 610 case ::ot_stream:
618 { 611 {
619 - auto& s = std::get<QPDF_Stream>(value); 612 + auto& s = std::get<QPDF_Stream>(obj->value);
620 s.m->stream_provider = nullptr; 613 s.m->stream_provider = nullptr;
621 - QPDFObjectHandle::DisconnectAccess::disconnect(s.m->stream_dict); 614 + s.m->stream_dict.disconnect();
622 } 615 }
623 break; 616 break;
  617 + case ::ot_uninitialized:
  618 + return;
624 default: 619 default:
625 break; 620 break;
626 } 621 }
627 - qpdf = nullptr;  
628 - og = QPDFObjGen(); 622 + obj->qpdf = nullptr;
  623 + obj->og = QPDFObjGen();
629 } 624 }
  625 +
630 std::string 626 std::string
631 QPDFObject::getStringValue() const 627 QPDFObject::getStringValue() const
632 { 628 {
@@ -654,16 +650,6 @@ QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const&amp; rhs) const @@ -654,16 +650,6 @@ QPDFObjectHandle::isSameObjectAs(QPDFObjectHandle const&amp; rhs) const
654 { 650 {
655 return this->obj == rhs.obj; 651 return this->obj == rhs.obj;
656 } 652 }
657 -void  
658 -QPDFObjectHandle::disconnect()  
659 -{  
660 - // Recursively remove association with any QPDF object. This method may only be called during  
661 - // final destruction. QPDF::~QPDF() calls it for indirect objects using the object pointer  
662 - // itself, so we don't do that here. Other objects call it through this method.  
663 - if (obj && !isIndirect()) {  
664 - this->obj->disconnect();  
665 - }  
666 -}  
667 653
668 qpdf_object_type_e 654 qpdf_object_type_e
669 QPDFObjectHandle::getTypeCode() const 655 QPDFObjectHandle::getTypeCode() const
@@ -1439,7 +1425,7 @@ QPDFObjectHandle::unparseResolved() const @@ -1439,7 +1425,7 @@ QPDFObjectHandle::unparseResolved() const
1439 if (!obj) { 1425 if (!obj) {
1440 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); 1426 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1441 } 1427 }
1442 - return obj->unparse(); 1428 + return BaseHandle::unparse();
1443 } 1429 }
1444 1430
1445 std::string 1431 std::string
@@ -1476,7 +1462,7 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer&amp; p, bool dereference_ @@ -1476,7 +1462,7 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer&amp; p, bool dereference_
1476 } else if (!obj) { 1462 } else if (!obj) {
1477 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); 1463 throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle");
1478 } else { 1464 } else {
1479 - obj->write_json(json_version, p); 1465 + write_json(json_version, p);
1480 } 1466 }
1481 } 1467 }
1482 1468
@@ -1902,7 +1888,7 @@ QPDFObjectHandle::shallowCopy() @@ -1902,7 +1888,7 @@ QPDFObjectHandle::shallowCopy()
1902 if (!obj) { 1888 if (!obj) {
1903 throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle"); 1889 throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
1904 } 1890 }
1905 - return {obj->copy()}; 1891 + return {copy()};
1906 } 1892 }
1907 1893
1908 QPDFObjectHandle 1894 QPDFObjectHandle
@@ -1911,7 +1897,7 @@ QPDFObjectHandle::unsafeShallowCopy() @@ -1911,7 +1897,7 @@ QPDFObjectHandle::unsafeShallowCopy()
1911 if (!obj) { 1897 if (!obj) {
1912 throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle"); 1898 throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle");
1913 } 1899 }
1914 - return {obj->copy(true)}; 1900 + return {copy(true)};
1915 } 1901 }
1916 1902
1917 void 1903 void
@@ -1926,7 +1912,7 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams) @@ -1926,7 +1912,7 @@ QPDFObjectHandle::makeDirect(QPDFObjGen::set&amp; visited, bool stop_at_streams)
1926 } 1912 }
1927 1913
1928 if (isBool() || isInteger() || isName() || isNull() || isReal() || isString()) { 1914 if (isBool() || isInteger() || isName() || isNull() || isReal() || isString()) {
1929 - this->obj = obj->copy(true); 1915 + this->obj = copy(true);
1930 } else if (auto a = as_array(strict)) { 1916 } else if (auto a = as_array(strict)) {
1931 std::vector<QPDFObjectHandle> items; 1917 std::vector<QPDFObjectHandle> items;
1932 for (auto const& item: a) { 1918 for (auto const& item: a) {
libqpdf/qpdf/QPDFObjectHandle_private.hh
@@ -358,7 +358,7 @@ namespace qpdf @@ -358,7 +358,7 @@ namespace qpdf
358 inline bool 358 inline bool
359 BaseHandle::null() const 359 BaseHandle::null() const
360 { 360 {
361 - return !obj || obj->getResolvedTypeCode() == ::ot_null; 361 + return !obj || type_code() == ::ot_null;
362 } 362 }
363 363
364 inline QPDF* 364 inline QPDF*
@@ -383,7 +383,7 @@ namespace qpdf @@ -383,7 +383,7 @@ namespace qpdf
383 return QPDF::Resolver::resolved(obj->qpdf, obj->og)->getTypeCode(); 383 return QPDF::Resolver::resolved(obj->qpdf, obj->og)->getTypeCode();
384 } 384 }
385 if (raw_type_code() == ::ot_reference) { 385 if (raw_type_code() == ::ot_reference) {
386 - return std::get<QPDF_Reference>(obj->value).obj->getResolvedTypeCode(); 386 + return std::get<QPDF_Reference>(obj->value).obj->getTypeCode();
387 } 387 }
388 return raw_type_code(); 388 return raw_type_code();
389 } 389 }
libqpdf/qpdf/QPDFObject_private.hh
@@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
18 #include <variant> 18 #include <variant>
19 #include <vector> 19 #include <vector>
20 20
  21 +class Disconnect;
21 class QPDFObject; 22 class QPDFObject;
22 class QPDFObjectHandle; 23 class QPDFObjectHandle;
23 24
@@ -50,7 +51,9 @@ class QPDF_Array final @@ -50,7 +51,9 @@ class QPDF_Array final
50 51
51 private: 52 private:
52 friend class QPDFObject; 53 friend class QPDFObject;
  54 + friend class qpdf::BaseHandle;
53 friend class qpdf::Array; 55 friend class qpdf::Array;
  56 +
54 QPDF_Array(std::vector<QPDFObjectHandle> const& items) : 57 QPDF_Array(std::vector<QPDFObjectHandle> const& items) :
55 elements(items) 58 elements(items)
56 { 59 {
@@ -75,6 +78,7 @@ class QPDF_Array final @@ -75,6 +78,7 @@ class QPDF_Array final
75 class QPDF_Bool final 78 class QPDF_Bool final
76 { 79 {
77 friend class QPDFObject; 80 friend class QPDFObject;
  81 + friend class qpdf::BaseHandle;
78 friend class QPDFObjectHandle; 82 friend class QPDFObjectHandle;
79 83
80 explicit QPDF_Bool(bool val) : 84 explicit QPDF_Bool(bool val) :
@@ -92,6 +96,7 @@ class QPDF_Dictionary final @@ -92,6 +96,7 @@ class QPDF_Dictionary final
92 { 96 {
93 friend class QPDFObject; 97 friend class QPDFObject;
94 friend class qpdf::BaseDictionary; 98 friend class qpdf::BaseDictionary;
  99 + friend class qpdf::BaseHandle;
95 100
96 QPDF_Dictionary(std::map<std::string, QPDFObjectHandle> const& items) : 101 QPDF_Dictionary(std::map<std::string, QPDFObjectHandle> const& items) :
97 items(items) 102 items(items)
@@ -105,6 +110,7 @@ class QPDF_Dictionary final @@ -105,6 +110,7 @@ class QPDF_Dictionary final
105 class QPDF_InlineImage final 110 class QPDF_InlineImage final
106 { 111 {
107 friend class QPDFObject; 112 friend class QPDFObject;
  113 + friend class qpdf::BaseHandle;
108 114
109 explicit QPDF_InlineImage(std::string val) : 115 explicit QPDF_InlineImage(std::string val) :
110 val(std::move(val)) 116 val(std::move(val))
@@ -116,6 +122,7 @@ class QPDF_InlineImage final @@ -116,6 +122,7 @@ class QPDF_InlineImage final
116 class QPDF_Integer final 122 class QPDF_Integer final
117 { 123 {
118 friend class QPDFObject; 124 friend class QPDFObject;
  125 + friend class qpdf::BaseHandle;
119 friend class QPDFObjectHandle; 126 friend class QPDFObjectHandle;
120 127
121 QPDF_Integer(long long val) : 128 QPDF_Integer(long long val) :
@@ -128,6 +135,7 @@ class QPDF_Integer final @@ -128,6 +135,7 @@ class QPDF_Integer final
128 class QPDF_Name final 135 class QPDF_Name final
129 { 136 {
130 friend class QPDFObject; 137 friend class QPDFObject;
  138 + friend class qpdf::BaseHandle;
131 139
132 explicit QPDF_Name(std::string name) : 140 explicit QPDF_Name(std::string name) :
133 name(std::move(name)) 141 name(std::move(name))
@@ -139,6 +147,7 @@ class QPDF_Name final @@ -139,6 +147,7 @@ class QPDF_Name final
139 class QPDF_Null final 147 class QPDF_Null final
140 { 148 {
141 friend class QPDFObject; 149 friend class QPDFObject;
  150 + friend class qpdf::BaseHandle;
142 151
143 public: 152 public:
144 static inline std::shared_ptr<QPDFObject> create( 153 static inline std::shared_ptr<QPDFObject> create(
@@ -150,6 +159,7 @@ class QPDF_Null final @@ -150,6 +159,7 @@ class QPDF_Null final
150 class QPDF_Operator final 159 class QPDF_Operator final
151 { 160 {
152 friend class QPDFObject; 161 friend class QPDFObject;
  162 + friend class qpdf::BaseHandle;
153 163
154 QPDF_Operator(std::string val) : 164 QPDF_Operator(std::string val) :
155 val(std::move(val)) 165 val(std::move(val))
@@ -162,6 +172,7 @@ class QPDF_Operator final @@ -162,6 +172,7 @@ class QPDF_Operator final
162 class QPDF_Real final 172 class QPDF_Real final
163 { 173 {
164 friend class QPDFObject; 174 friend class QPDFObject;
  175 + friend class qpdf::BaseHandle;
165 176
166 QPDF_Real(std::string val) : 177 QPDF_Real(std::string val) :
167 val(std::move(val)) 178 val(std::move(val))
@@ -199,6 +210,7 @@ class QPDF_Stream final @@ -199,6 +210,7 @@ class QPDF_Stream final
199 friend class QPDF_Stream; 210 friend class QPDF_Stream;
200 friend class QPDFObject; 211 friend class QPDFObject;
201 friend class qpdf::Stream; 212 friend class qpdf::Stream;
  213 + friend class qpdf::BaseHandle;
202 214
203 public: 215 public:
204 Members(QPDFObjectHandle stream_dict, size_t length) : 216 Members(QPDFObjectHandle stream_dict, size_t length) :
@@ -217,6 +229,7 @@ class QPDF_Stream final @@ -217,6 +229,7 @@ class QPDF_Stream final
217 }; 229 };
218 230
219 friend class QPDFObject; 231 friend class QPDFObject;
  232 + friend class qpdf::BaseHandle;
220 friend class qpdf::Stream; 233 friend class qpdf::Stream;
221 234
222 QPDF_Stream(QPDFObjectHandle stream_dict, size_t length) : 235 QPDF_Stream(QPDFObjectHandle stream_dict, size_t length) :
@@ -235,6 +248,7 @@ class QPDF_Stream final @@ -235,6 +248,7 @@ class QPDF_Stream final
235 class QPDF_String final 248 class QPDF_String final
236 { 249 {
237 friend class QPDFObject; 250 friend class QPDFObject;
  251 + friend class qpdf::BaseHandle;
238 friend class QPDFWriter; 252 friend class QPDFWriter;
239 253
240 public: 254 public:
@@ -284,10 +298,6 @@ class QPDFObject @@ -284,10 +298,6 @@ class QPDFObject
284 qpdf, og, std::forward<T>(T(std::forward<Args>(args)...))); 298 qpdf, og, std::forward<T>(T(std::forward<Args>(args)...)));
285 } 299 }
286 300
287 - std::shared_ptr<QPDFObject> copy(bool shallow = false);  
288 - std::string unparse();  
289 - void write_json(int json_version, JSON::Writer& p);  
290 - void disconnect();  
291 std::string getStringValue() const; 301 std::string getStringValue() const;
292 302
293 // Return a unique type code for the resolved object 303 // Return a unique type code for the resolved object
@@ -298,7 +308,7 @@ class QPDFObject @@ -298,7 +308,7 @@ class QPDFObject
298 return QPDF::Resolver::resolved(qpdf, og)->getTypeCode(); 308 return QPDF::Resolver::resolved(qpdf, og)->getTypeCode();
299 } 309 }
300 if (getTypeCode() == ::ot_reference) { 310 if (getTypeCode() == ::ot_reference) {
301 - return std::get<QPDF_Reference>(value).obj->getResolvedTypeCode(); 311 + return std::get<QPDF_Reference>(value).obj->getTypeCode();
302 } 312 }
303 return getTypeCode(); 313 return getTypeCode();
304 } 314 }
@@ -344,23 +354,12 @@ class QPDFObject @@ -344,23 +354,12 @@ class QPDFObject
344 qpdf = a_qpdf; 354 qpdf = a_qpdf;
345 og = a_og; 355 og = a_og;
346 } 356 }
347 - // Mark an object as destroyed. Used by QPDF's destructor for its indirect objects.  
348 - void  
349 - destroy()  
350 - {  
351 - value = QPDF_Destroyed();  
352 - }  
353 357
354 bool 358 bool
355 isUnresolved() const 359 isUnresolved() const
356 { 360 {
357 return getTypeCode() == ::ot_unresolved; 361 return getTypeCode() == ::ot_unresolved;
358 } 362 }
359 - const QPDFObject*  
360 - resolved_object() const  
361 - {  
362 - return isUnresolved() ? QPDF::Resolver::resolved(qpdf, og).get() : this;  
363 - }  
364 363
365 struct JSON_Descr 364 struct JSON_Descr
366 { 365 {
@@ -457,6 +456,7 @@ class QPDFObject @@ -457,6 +456,7 @@ class QPDFObject
457 private: 456 private:
458 friend class QPDF_Stream; 457 friend class QPDF_Stream;
459 friend class qpdf::BaseHandle; 458 friend class qpdf::BaseHandle;
  459 + friend class Disconnect;
460 460
461 typedef std::variant< 461 typedef std::variant<
462 std::monostate, 462 std::monostate,
libtests/sparse_array.cc
@@ -95,12 +95,14 @@ main() @@ -95,12 +95,14 @@ main()
95 assert(b.at(3).second.isNull()); 95 assert(b.at(3).second.isNull());
96 assert(b.at(8).second.isNull()); 96 assert(b.at(8).second.isNull());
97 assert(b.at(5).second.isIndirect()); 97 assert(b.at(5).second.isIndirect());
98 - assert(obj->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");  
99 - auto c = obj->copy(true);  
100 - auto d = obj->copy(false); 98 + assert(
  99 + QPDFObjectHandle(obj).unparse() ==
  100 + "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");
  101 + auto c = QPDFObjectHandle(obj).unsafeShallowCopy();
  102 + auto d = QPDFObjectHandle(obj).shallowCopy();
101 b.at(7).second.setArrayItem(2, "42"_qpdf); 103 b.at(7).second.setArrayItem(2, "42"_qpdf);
102 - assert(c->unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]");  
103 - assert(d->unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]"); 104 + assert(c.unparse() == "[ null null null null null 3 0 R null [ 0 1 42 3 ] null null ]");
  105 + assert(d.unparse() == "[ null null null null null 3 0 R null [ 0 1 2 3 ] null null ]");
104 106
105 try { 107 try {
106 b.setAt(3, {}); 108 b.setAt(3, {});