Commit f3583bc0857eb61fd10dc7ee362174b8a5805fa1

Authored by Jay Berkenbilt
1 parent 9367eb8a

Use castxml on headers instead of special sizes.cc

Figuring out which classes are part of the public API by using library
symbols is fragile (dependent on specific compiler optimizations) and
unreliable (misses some inline things). Instead, use castxml, a tool
that parses C++ to an abstract syntax tree and generates XML, to get a
reliable accounting of public classes and their sizes.
CMakeLists.txt
@@ -39,9 +39,6 @@ CMAKE_DEPENDENT_OPTION( @@ -39,9 +39,6 @@ CMAKE_DEPENDENT_OPTION(
39 WERROR "Treat compiler warnings as errors" OFF 39 WERROR "Treat compiler warnings as errors" OFF
40 "NOT MAINTAINER_MODE; NOT CI_MODE" ON) 40 "NOT MAINTAINER_MODE; NOT CI_MODE" ON)
41 CMAKE_DEPENDENT_OPTION( 41 CMAKE_DEPENDENT_OPTION(
42 - CHECK_SIZES "Compare sizes.cc with classes in public API" OFF  
43 - "NOT MAINTAINER_MODE" ON)  
44 -CMAKE_DEPENDENT_OPTION(  
45 GENERATE_AUTO_JOB "Automatically regenerate job files" OFF 42 GENERATE_AUTO_JOB "Automatically regenerate job files" OFF
46 "NOT MAINTAINER_MODE" ON) 43 "NOT MAINTAINER_MODE" ON)
47 CMAKE_DEPENDENT_OPTION( 44 CMAKE_DEPENDENT_OPTION(
README-maintainer.md
@@ -862,6 +862,8 @@ RelWithDebInfo when using external-libs. @@ -862,6 +862,8 @@ RelWithDebInfo when using external-libs.
862 862
863 ## ABI checks 863 ## ABI checks
864 864
  865 +Note: the check_abi program requires [castxml](https://github.com/CastXML/CastXML) to be installed.
  866 +
865 Until the conversion of the build to cmake, we relied on running the 867 Until the conversion of the build to cmake, we relied on running the
866 test suite with old executables and a new library. When QPDFJob was 868 test suite with old executables and a new library. When QPDFJob was
867 introduced, this method got much less reliable since a lot of public 869 introduced, this method got much less reliable since a lot of public
@@ -891,9 +893,7 @@ still things that could potentially break ABI, such as @@ -891,9 +893,7 @@ still things that could potentially break ABI, such as
891 Not breaking ABI/API still requires care. 893 Not breaking ABI/API still requires care.
892 894
893 The check_abi script is responsible for performing many of these 895 The check_abi script is responsible for performing many of these
894 -steps. See comments in check_abi for additional notes. Running  
895 -"check_abi check-sizes" is run by ctest on Linux when CHECK_SIZES is  
896 -on. 896 +steps. See comments in check_abi for additional notes.
897 897
898 ## CODE FORMATTING 898 ## CODE FORMATTING
899 899
abi-perf-test
@@ -59,7 +59,7 @@ cmake --build build -j$(nproc) @@ -59,7 +59,7 @@ cmake --build build -j$(nproc)
59 59
60 echo "** saving old library and size information **" 60 echo "** saving old library and size information **"
61 61
62 -./build/qpdf/sizes >| $work/old/sizes 62 +$source/check_abi get-sizes --include include >| $work/old/sizes
63 cp build/libqpdf/libqpdf.so.*.* $work/old 63 cp build/libqpdf/libqpdf.so.*.* $work/old
64 64
65 if [ "$SKIP_PERF" != "1" ]; then 65 if [ "$SKIP_PERF" != "1" ]; then
@@ -74,12 +74,11 @@ git checkout abi-new @@ -74,12 +74,11 @@ git checkout abi-new
74 cmake -S . -B build \ 74 cmake -S . -B build \
75 -DMAINTAINER_MODE=1 -DBUILD_STATIC_LIBS=0 -DBUILD_DOC=0 \ 75 -DMAINTAINER_MODE=1 -DBUILD_STATIC_LIBS=0 -DBUILD_DOC=0 \
76 -DCMAKE_BUILD_TYPE=RelWithDebInfo 76 -DCMAKE_BUILD_TYPE=RelWithDebInfo
77 -cmake --build build -j$(nproc) --target sizes 77 +cmake --build build -j$(nproc) --target libqpdf
78 78
79 echo "** saving new library and size information **" 79 echo "** saving new library and size information **"
80 80
81 -$source/check_abi check-sizes --lib build/libqpdf/libqpdf.so  
82 -./build/qpdf/sizes >| $work/new/sizes 81 +$source/check_abi get-sizes --include include >| $work/new/sizes
83 cp build/libqpdf/libqpdf.so.*.* $work/new 82 cp build/libqpdf/libqpdf.so.*.* $work/new
84 83
85 echo "** running ABI comparison **" 84 echo "** running ABI comparison **"
cSpell.json
@@ -61,6 +61,7 @@ @@ -61,6 +61,7 @@
61 "bufsize", 61 "bufsize",
62 "buildrules", 62 "buildrules",
63 "calledgetallpages", 63 "calledgetallpages",
  64 + "castxml",
64 "ccase", 65 "ccase",
65 "ccitt", 66 "ccitt",
66 "cdef", 67 "cdef",
check_abi
@@ -4,9 +4,10 @@ import sys @@ -4,9 +4,10 @@ import sys
4 import argparse 4 import argparse
5 import subprocess 5 import subprocess
6 import re 6 import re
  7 +import xml.etree.ElementTree as ET
  8 +from operator import itemgetter
7 9
8 whoami = os.path.basename(sys.argv[0]) 10 whoami = os.path.basename(sys.argv[0])
9 -whereami = os.path.dirname(os.path.realpath(__file__))  
10 11
11 12
12 def warn(*args, **kwargs): 13 def warn(*args, **kwargs):
@@ -18,8 +19,8 @@ class Main: @@ -18,8 +19,8 @@ class Main:
18 options = self.parse_args(args, prog) 19 options = self.parse_args(args, prog)
19 if options.action == 'dump': 20 if options.action == 'dump':
20 self.dump(options) 21 self.dump(options)
21 - elif options.action == 'check-sizes':  
22 - self.check_sizes(options) 22 + elif options.action == 'get-sizes':
  23 + self.get_sizes(options)
23 elif options.action == 'compare': 24 elif options.action == 'compare':
24 self.compare(options) 25 self.compare(options)
25 else: 26 else:
@@ -43,10 +44,12 @@ class Main: @@ -43,10 +44,12 @@ class Main:
43 help='dump qpdf symbols in a library') 44 help='dump qpdf symbols in a library')
44 p_dump.add_argument(lib_arg[0], **lib_arg[1]) 45 p_dump.add_argument(lib_arg[0], **lib_arg[1])
45 46
46 - p_check_sizes = subparsers.add_parser(  
47 - 'check-sizes',  
48 - help='check consistency between library and sizes.cc')  
49 - p_check_sizes.add_argument(lib_arg[0], **lib_arg[1]) 47 + p_get_sizes = subparsers.add_parser(
  48 + 'get-sizes',
  49 + help='dump sizes of all public classes')
  50 + p_get_sizes.add_argument('--include',
  51 + help='include path',
  52 + required=True)
50 53
51 p_compare = subparsers.add_parser( 54 p_compare = subparsers.add_parser(
52 'compare', 55 'compare',
@@ -103,60 +106,73 @@ class Main: @@ -103,60 +106,73 @@ class Main:
103 for i in sorted(self.get_symbols(options.lib)): 106 for i in sorted(self.get_symbols(options.lib)):
104 print(i) 107 print(i)
105 108
106 - def check_sizes(self, options):  
107 - # Make sure that every class with methods in the public API  
108 - # appears in sizes.cc either explicitly ignored or in a  
109 - # print_size call. This enables us to reliably test whether  
110 - # any object changed size by following the ABI checking  
111 - # procedures outlined in README-maintainer.  
112 -  
113 - # To keep things up to date, whenever we add or remove  
114 - # objects, we have to update sizes.cc. The check-sizes option  
115 - # can be run at any time on an up-to-date build.  
116 -  
117 - lib = self.get_symbols(options.lib)  
118 - classes = set()  
119 - for i in sorted(lib):  
120 - # Find a symbol that looks like a class method.  
121 - m = re.match(  
122 - r'(((?:^\S*?::)?(?:[^:\s]+))::([^:\s]+))(?:\[[^\]]+\])?\(',  
123 - i)  
124 - if m:  
125 - full = m.group(1)  
126 - clas = m.group(2)  
127 - method = m.group(3)  
128 - if full.startswith('std::') or method.startswith('~'):  
129 - # Sometimes std:: template instantiations make it  
130 - # into the library. Ignore those. Also ignore  
131 - # classes whose only exported method is a  
132 - # destructor.  
133 - continue  
134 - # Otherwise, if the class exports a method, we  
135 - # potentially care about changes to its size, so add  
136 - # it.  
137 - classes.add(clas)  
138 - in_sizes = set()  
139 - # Read the sizes.cc to make sure everything's there.  
140 - with open(os.path.join(whereami, 'qpdf/sizes.cc'), 'r') as f:  
141 - for line in f.readlines():  
142 - m = re.search(r'^\s*(?:ignore_class|print_size)\((.*?)\)',  
143 - line)  
144 - if m:  
145 - in_sizes.add(m.group(1))  
146 - sizes_only = in_sizes - classes  
147 - classes_only = classes - in_sizes  
148 - if sizes_only or classes_only:  
149 - if sizes_only:  
150 - print("classes in sizes.cc but not in the library:")  
151 - for i in sorted(sizes_only):  
152 - print(' ', i)  
153 - if classes_only:  
154 - print("classes in the library but not in sizes.cc:")  
155 - for i in sorted(classes_only):  
156 - print(' ', i)  
157 - exit(f'{whoami}: mismatch between library and sizes.cc')  
158 - else:  
159 - print(f'{whoami}: sizes.cc is consistent with the library') 109 + @staticmethod
  110 + def get_header_sizes(include, filename):
  111 + print(f'{filename}...', file=sys.stderr)
  112 + p = subprocess.run([
  113 + 'castxml', '--castxml-output=1', f'-I{include}',
  114 + f'{include}/{filename}', '-o', '-',
  115 + ], stdout=subprocess.PIPE, check=True)
  116 + tree = ET.fromstring(p.stdout)
  117 + this_file = tree.find(f'.//File[@name="{include}/{filename}"]')
  118 + if this_file is None:
  119 + # This file doesn't define anything, e.g., DLL.h
  120 + return []
  121 + this_file_id = this_file.attrib["id"]
  122 + wanted = [
  123 + 'Namespace',
  124 + 'Struct', 'Union', 'Class', # records
  125 + ]
  126 +
  127 + by_id = {}
  128 + for elem in tree:
  129 + # Reference
  130 + # https://github.com/CastXML/CastXML/blob/master/doc/manual/castxml.xsd
  131 + if elem.tag not in wanted:
  132 + continue
  133 + is_record = elem.tag != 'Namespace'
  134 + record = {
  135 + 'is_record': is_record,
  136 + 'in_file': (elem.get('file') == this_file_id and
  137 + elem.get('incomplete') is None),
  138 + 'name': elem.get('name'),
  139 + 'access': elem.get('access'),
  140 + 'context': elem.get('context'),
  141 + 'size': elem.get('size'),
  142 + }
  143 + by_id[elem.attrib['id']] = record
  144 + classes = []
  145 + for id_, record in by_id.items():
  146 + cur = record
  147 + if not cur['in_file']:
  148 + continue
  149 + name = ''
  150 + private = False
  151 + while cur is not None:
  152 + if cur.get('access') == 'private':
  153 + private = True
  154 + parent = cur.get('context')
  155 + name = f'{cur["name"]}{name}'
  156 + if parent is None or parent == '_1':
  157 + break
  158 + name = f'::{name}'
  159 + cur = by_id.get(cur.get('context'))
  160 + if not private:
  161 + classes.append([name, record['size']])
  162 + return classes
  163 +
  164 + def get_sizes(self, options):
  165 + classes = []
  166 + for f in os.listdir(f'{options.include}/qpdf'):
  167 + if f.startswith('auto_') or f == 'QPDFObject.hh':
  168 + continue
  169 + if f.endswith('.h') or f.endswith('.hh'):
  170 + classes.extend(self.get_header_sizes(
  171 + options.include, f"qpdf/{f}"))
  172 +
  173 + classes.sort(key=itemgetter(0))
  174 + for c, s in classes:
  175 + print(c, s)
160 176
161 def read_sizes(self, filename): 177 def read_sizes(self, filename):
162 sizes = {} 178 sizes = {}
@@ -217,6 +233,12 @@ class Main: @@ -217,6 +233,12 @@ class Main:
217 233
218 if __name__ == '__main__': 234 if __name__ == '__main__':
219 try: 235 try:
  236 + subprocess.run(['castxml', '--version'],
  237 + stdout=subprocess.DEVNULL,
  238 + stderr=subprocess.DEVNULL)
  239 + except Exception:
  240 + exit('f{whoami}: castxml must be installed')
  241 + try:
220 Main().main() 242 Main().main()
221 except KeyboardInterrupt: 243 except KeyboardInterrupt:
222 exit(130) 244 exit(130)
job.sums
1 # Generated by generate_auto_job 1 # Generated by generate_auto_job
2 -CMakeLists.txt f0819695e4867e4f4389d38b0c124e79aa3ec9ace50f16ad8c751ff7f1ec6690 2 +CMakeLists.txt 88e8974a8b14e10c941a4bb04ff078c3d3063b98af3ea056e02b1dcdff783d22
3 generate_auto_job f64733b79dcee5a0e3e8ccc6976448e8ddf0e8b6529987a66a7d3ab2ebc10a86 3 generate_auto_job f64733b79dcee5a0e3e8ccc6976448e8ddf0e8b6529987a66a7d3ab2ebc10a86
4 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 4 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4
5 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 5 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42
libqpdf/CMakeLists.txt
@@ -599,13 +599,3 @@ if(INSTALL_CMAKE_PACKAGE) @@ -599,13 +599,3 @@ if(INSTALL_CMAKE_PACKAGE)
599 COMPONENT ${COMPONENT_DEV} 599 COMPONENT ${COMPONENT_DEV}
600 DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qpdf) 600 DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qpdf)
601 endif() 601 endif()
602 -  
603 -if(CHECK_SIZES AND BUILD_SHARED_LIBS AND (CMAKE_SYSTEM_NAME STREQUAL "Linux"))  
604 - # We can only do this check on a system with ELF shared libraries.  
605 - # Since this is a maintainer-only option, testing for Linux is a  
606 - # close enough approximation.  
607 - add_test(  
608 - NAME check-sizes  
609 - COMMAND ${qpdf_SOURCE_DIR}/check_abi check-sizes  
610 - --lib $<TARGET_FILE:libqpdf>)  
611 -endif()  
manual/installation.rst
@@ -80,6 +80,13 @@ made because developers have to set the environment variable @@ -80,6 +80,13 @@ made because developers have to set the environment variable
80 themselves now rather than setting it through the build. Either way, 80 themselves now rather than setting it through the build. Either way,
81 they are off by default. 81 they are off by default.
82 82
  83 +Maintainer Dependencies
  84 +~~~~~~~~~~~~~~~~~~~~~~~
  85 +
  86 +- To run ABI checks as a maintainer, you need `castxml
  87 + <https://github.com/CastXML/CastXML>`__, which is used by
  88 + ``check_abi`` to generate sizes of all public classes.
  89 +
83 Additional Requirements on Windows 90 Additional Requirements on Windows
84 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 91 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
85 92
@@ -301,14 +308,6 @@ ZOPFLI @@ -301,14 +308,6 @@ ZOPFLI
301 Options for Working on qpdf 308 Options for Working on qpdf
302 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 309 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
303 310
304 -CHECK_SIZES  
305 - The source file :file:`qpdf/sizes.cc` is used to display the sizes  
306 - of all objects in the public API. Consistency of its output between  
307 - releases is used as part of the check against accidental breakage of  
308 - the binary interface (ABI). Turning this on causes a test to be run  
309 - that ensures an exact match between classes in ``sizes.cc`` and  
310 - classes in the library's public API. This option requires Python 3.  
311 -  
312 ENABLE_COVERAGE 311 ENABLE_COVERAGE
313 Compile with ``--coverage``. See README-maintainer.md for 312 Compile with ``--coverage``. See README-maintainer.md for
314 information about generating coverage reports. 313 information about generating coverage reports.
@@ -361,8 +360,6 @@ MAINTAINER_MODE @@ -361,8 +360,6 @@ MAINTAINER_MODE
361 360
362 - ``BUILD_DOC`` 361 - ``BUILD_DOC``
363 362
364 - - ``CHECK_SIZES``  
365 -  
366 - ``ENABLE_QTC`` 363 - ``ENABLE_QTC``
367 364
368 - ``GENERATE_AUTO_JOB`` 365 - ``GENERATE_AUTO_JOB``
manual/release-notes.rst
@@ -132,6 +132,10 @@ more detail. @@ -132,6 +132,10 @@ more detail.
132 - There has also been significant refactoring of how qpdf internally iterates over 132 - There has also been significant refactoring of how qpdf internally iterates over
133 arrays and dictionaries. 133 arrays and dictionaries.
134 134
  135 + - The internal mechanism used to check object sizes for binary
  136 + compatibility between releases has been changed. As such, the
  137 + ``CHECK_SIZES`` maintainer-only build option has been removed.
  138 +
135 139
136 11.10.1: February 15, 2025 140 11.10.1: February 15, 2025
137 - Build fixes 141 - Build fixes
qpdf/CMakeLists.txt
@@ -2,7 +2,6 @@ set(MAIN_CXX_PROGRAMS @@ -2,7 +2,6 @@ set(MAIN_CXX_PROGRAMS
2 qpdf 2 qpdf
3 fix-qdf 3 fix-qdf
4 pdf_from_scratch 4 pdf_from_scratch
5 - sizes  
6 test_char_sign 5 test_char_sign
7 test_driver 6 test_driver
8 test_large_file 7 test_large_file
@@ -28,7 +27,6 @@ foreach(PROG ${MAIN_C_PROGRAMS}) @@ -28,7 +27,6 @@ foreach(PROG ${MAIN_C_PROGRAMS})
28 target_link_libraries(${PROG} libqpdf) 27 target_link_libraries(${PROG} libqpdf)
29 set_property(TARGET ${PROG} PROPERTY LINKER_LANGUAGE CXX) 28 set_property(TARGET ${PROG} PROPERTY LINKER_LANGUAGE CXX)
30 endforeach() 29 endforeach()
31 -target_include_directories(sizes PRIVATE ${JPEG_INCLUDE})  
32 30
33 set(needs_private_headers 31 set(needs_private_headers
34 test_large_file 32 test_large_file
qpdf/sizes.cc deleted
1 -// See "ABI checks" in README-maintainer and comments in check_abi.  
2 -  
3 -#include <iostream>  
4 -  
5 -#include <qpdf/Buffer.hh>  
6 -#include <qpdf/BufferInputSource.hh>  
7 -#include <qpdf/ClosedFileInputSource.hh>  
8 -#include <qpdf/FileInputSource.hh>  
9 -#include <qpdf/InputSource.hh>  
10 -#include <qpdf/JSON.hh>  
11 -#include <qpdf/PDFVersion.hh>  
12 -#include <qpdf/Pipeline.hh>  
13 -#include <qpdf/Pl_Buffer.hh>  
14 -#include <qpdf/Pl_Concatenate.hh>  
15 -#include <qpdf/Pl_Count.hh>  
16 -#include <qpdf/Pl_DCT.hh>  
17 -#include <qpdf/Pl_Discard.hh>  
18 -#include <qpdf/Pl_Flate.hh>  
19 -#include <qpdf/Pl_Function.hh>  
20 -#include <qpdf/Pl_OStream.hh>  
21 -#include <qpdf/Pl_QPDFTokenizer.hh>  
22 -#include <qpdf/Pl_RunLength.hh>  
23 -#include <qpdf/Pl_StdioFile.hh>  
24 -#include <qpdf/Pl_String.hh>  
25 -#include <qpdf/QPDF.hh>  
26 -#include <qpdf/QPDFAcroFormDocumentHelper.hh>  
27 -#include <qpdf/QPDFAnnotationObjectHelper.hh>  
28 -#include <qpdf/QPDFCryptoProvider.hh>  
29 -#include <qpdf/QPDFEFStreamObjectHelper.hh>  
30 -#include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>  
31 -#include <qpdf/QPDFExc.hh>  
32 -#include <qpdf/QPDFFileSpecObjectHelper.hh>  
33 -#include <qpdf/QPDFFormFieldObjectHelper.hh>  
34 -#include <qpdf/QPDFJob.hh>  
35 -#include <qpdf/QPDFLogger.hh>  
36 -#include <qpdf/QPDFMatrix.hh>  
37 -#include <qpdf/QPDFNameTreeObjectHelper.hh>  
38 -#include <qpdf/QPDFNumberTreeObjectHelper.hh>  
39 -#include <qpdf/QPDFObjGen.hh>  
40 -#include <qpdf/QPDFObjectHandle.hh>  
41 -#include <qpdf/QPDFOutlineDocumentHelper.hh>  
42 -#include <qpdf/QPDFOutlineObjectHelper.hh>  
43 -#include <qpdf/QPDFPageDocumentHelper.hh>  
44 -#include <qpdf/QPDFPageLabelDocumentHelper.hh>  
45 -#include <qpdf/QPDFPageObjectHelper.hh>  
46 -#include <qpdf/QPDFStreamFilter.hh>  
47 -#include <qpdf/QPDFSystemError.hh>  
48 -#include <qpdf/QPDFTokenizer.hh>  
49 -#include <qpdf/QPDFUsage.hh>  
50 -#include <qpdf/QPDFWriter.hh>  
51 -#include <qpdf/QPDFXRefEntry.hh>  
52 -  
53 -#define ignore_class(cls)  
54 -#define print_size(cls) std::cout << #cls << " " << sizeof(cls) << std::endl  
55 -  
56 -// These are not classes  
57 -// -------  
58 -ignore_class(QUtil);  
59 -ignore_class(QTC);  
60 -  
61 -int  
62 -main()  
63 -{  
64 - // Print the size of every class in the public API. This file is  
65 - // read by the check_abi script at the top of the repository as  
66 - // part of the binary compatibility checks performed before each  
67 - // release.  
68 - print_size(Buffer);  
69 - print_size(BufferInputSource);  
70 - print_size(ClosedFileInputSource);  
71 - print_size(FileInputSource);  
72 - print_size(InputSource);  
73 - print_size(JSON);  
74 - print_size(PDFVersion);  
75 - print_size(Pipeline);  
76 - print_size(Pl_Buffer);  
77 - print_size(Pl_Concatenate);  
78 - print_size(Pl_Count);  
79 - print_size(Pl_DCT);  
80 - print_size(Pl_Discard);  
81 - print_size(Pl_Flate);  
82 - print_size(Pl_Function);  
83 - print_size(Pl_OStream);  
84 - print_size(Pl_QPDFTokenizer);  
85 - print_size(Pl_RunLength);  
86 - print_size(Pl_StdioFile);  
87 - print_size(Pl_String);  
88 - print_size(QPDF);  
89 - print_size(QPDFAcroFormDocumentHelper);  
90 - print_size(QPDFAnnotationObjectHelper);  
91 - print_size(QPDFCryptoProvider);  
92 - print_size(QPDFEFStreamObjectHelper);  
93 - print_size(QPDFEmbeddedFileDocumentHelper);  
94 - print_size(QPDFExc);  
95 - print_size(QPDFFileSpecObjectHelper);  
96 - print_size(QPDFFormFieldObjectHelper);  
97 - print_size(QPDFJob);  
98 - print_size(QPDFJob::AttConfig);  
99 - print_size(QPDFJob::Config);  
100 - print_size(QPDFJob::CopyAttConfig);  
101 - print_size(QPDFJob::EncConfig);  
102 - print_size(QPDFJob::PagesConfig);  
103 - print_size(QPDFJob::UOConfig);  
104 - print_size(QPDFJob::PageLabelsConfig);  
105 - print_size(QPDFLogger);  
106 - print_size(QPDFMatrix);  
107 - print_size(QPDFNameTreeObjectHelper);  
108 - print_size(QPDFNameTreeObjectHelper::iterator);  
109 - print_size(QPDFNumberTreeObjectHelper);  
110 - print_size(QPDFNumberTreeObjectHelper::iterator);  
111 - print_size(QPDFObjectHandle);  
112 - print_size(QPDFObjectHandle::ParserCallbacks);  
113 - print_size(QPDFObjectHandle::QPDFArrayItems);  
114 - print_size(QPDFObjectHandle::QPDFArrayItems::iterator);  
115 - print_size(QPDFObjectHandle::QPDFDictItems);  
116 - print_size(QPDFObjectHandle::QPDFDictItems::iterator);  
117 - print_size(QPDFObjectHandle::StreamDataProvider);  
118 - print_size(QPDFObjectHandle::TokenFilter);  
119 - print_size(QPDFObjectHelper);  
120 - print_size(QPDFOutlineDocumentHelper);  
121 - print_size(QPDFOutlineObjectHelper);  
122 - print_size(QPDFPageDocumentHelper);  
123 - print_size(QPDFPageLabelDocumentHelper);  
124 - print_size(QPDFPageObjectHelper);  
125 - print_size(QPDFStreamFilter);  
126 - print_size(QPDFSystemError);  
127 - print_size(QPDFTokenizer);  
128 - print_size(QPDFTokenizer::Token);  
129 - print_size(QPDFUsage);  
130 - print_size(QPDFWriter);  
131 - print_size(QPDFWriter::FunctionProgressReporter);  
132 - print_size(QPDFXRefEntry);  
133 - return 0;  
134 -}