diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..92e2a7d
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,68 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+# ******** NOTE ********
+
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ main ]
+ schedule:
+ - cron: '19 1 * * 2'
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'cpp' ]
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
+ # Learn more...
+ # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ # âšī¸ Command-line programs to run using the OS shell.
+ # đ https://git.io/JvXDl
+
+ # âī¸ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml
new file mode 100644
index 0000000..3b1cbdb
--- /dev/null
+++ b/.github/workflows/linux-ci.yml
@@ -0,0 +1,22 @@
+name: Linux build and test
+
+on: [push, pull_request]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ compiler: [g++, clang++]
+ build_type: [Release, Debug]
+ use_sanitizers: [ON, OFF]
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: build and test
+ run: |
+ mkdir build && cd build
+ cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DUSE_SANITIZERS=${{ matrix.use_sanitizers }} ..
+ make -j $(nproc)
+ make test
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ab5c208
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,20 @@
+# Copyright 2020 Kevin Backhouse.
+#
+# This file is part of DBusParse.
+#
+# DBusParse is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DBusParse is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with DBusParse. If not, see .
+
+
+*~
+build
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..2bddcd4
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Copyright 2020 Kevin Backhouse.
+#
+# This file is part of DBusParse.
+#
+# DBusParse is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DBusParse is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with DBusParse. If not, see .
+
+
+cmake_minimum_required(VERSION 3.10)
+
+include(GNUInstallDirs)
+include(CMakePackageConfigHelpers)
+
+enable_testing()
+
+# set the project name
+project(DBusParse VERSION 1.0.0 DESCRIPTION "C++ Library for parsing D-Bus messages")
+
+# specify the C++ standard
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED True)
+
+option(USE_SANITIZERS "Enable ASAN and UBSAN" OFF)
+
+add_compile_options(-Wall -Wextra -pedantic -Werror)
+
+if (USE_SANITIZERS)
+ set(SANITIZER_FLAGS "-fsanitize=address,undefined")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS}")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SANITIZER_FLAGS}")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${SANITIZER_FLAGS}")
+endif()
+
+add_subdirectory(include)
+add_subdirectory(src)
+add_subdirectory(tests)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..44efb2c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,85 @@
+Copyright 2020 Kevin Backhouse.
+
+# DBusParse
+
+DBusParse is a C++ library for parsing
+[D-Bus](https://www.freedesktop.org/wiki/Software/dbus/) messages. It
+can also serialize a D-Bus message to an array of bytes, ready for
+sending. DBusParse includes some simple utility functions for sending
+and receiving D-Bus messages, but is not intended to be a fully
+fledged D-Bus message handler. For example, it does not include logic
+for generating message serial numbers. However, it is certainly
+suitable to be used as a sub-component of a complete D-Bus
+implementation.
+
+The parser is designed to be able to parse an incomplete input. You
+can feed it some bytes, then save its state until you have received
+more bytes. This is handy when reading bytes from a socket, because
+you might not receive the complete message in a single read. It means,
+for example, that the parser is well-suited for use in an
+[epoll](https://man7.org/linux/man-pages/man7/epoll.7.html) event
+loop. The parser implementation uses
+[continuation-passing style](https://en.wikipedia.org/wiki/Continuation-passing_style).
+The parser state includes a continuation function, which is invoked
+when more bytes are received.
+
+I wrote DBusParse because I was doing security research on
+[dbus-daemon](https://dbus.freedesktop.org/doc/dbus-daemon.1.html) and
+several D-Bus services, and wanted to gain a thorough understanding of
+the message format. I also, stupidly, thought that it wouldn't be
+much work. Initially, I only needed the ability to send messages and I
+wasn't aware of the
+[dbus-send](https://dbus.freedesktop.org/doc/dbus-send.1.html) tool,
+so I wrote a serializer. But I quickly discovered that I also needed
+to parse the reply messages, so I started implementing a parser
+too. Now that it has grown into a complete implementation, I figure I
+might as well make it open source.
+
+## Usage
+
+DBusParse is a library. For an example of how to use it in a simple
+application, please see the
+[DBusParseDemo](https://github.com/kevinbackhouse/DBusParseDemo) demo
+repository.
+
+## Building
+
+On Linux, you can build DBusParse as follows:
+
+```bash
+mkdir build
+cd build
+cmake ..
+make
+```
+
+To run the tests:
+
+```bash
+make test
+```
+
+You can install DBusParse on your system like this:
+
+```bash
+sudo make install
+```
+
+However, if you are not keen to use `sudo` to modify your system, you
+can instead use DBusParse by including it as a sub-module in your project.
+The [DBusParseDemo](https://github.com/kevinbackhouse/DBusParseDemo)
+project does exactly that.
+
+## Design notes
+
+[dbus.hpp](/include/DBusParse/dbus.hpp) defines a class named
+`DBusType` with sub-types such as `DBusTypeInt32` and `DBusTypeArray`.
+Similarly, it defines a class named `DBusObject` with sub-types such as
+`DBusObjectInt32` and `DBusObjectArray`. These class hierarchies
+correspond to the types and objects defined in the [D-Bus
+specification](http://dbus.freedesktop.org/doc/dbus-specification.html).
+
+Smart pointers are used to manage memory automatically. For example,
+composite objects such as `DBusObjectStruct` use
+[`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)
+to point to their child objects.
diff --git a/code_of_conduct.md b/code_of_conduct.md
new file mode 100644
index 0000000..90272fe
--- /dev/null
+++ b/code_of_conduct.md
@@ -0,0 +1,130 @@
+
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+[INSERT CONTACT METHOD].
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
+
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
new file mode 100644
index 0000000..9635939
--- /dev/null
+++ b/include/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright 2020 Kevin Backhouse.
+#
+# This file is part of DBusParse.
+#
+# DBusParse is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# DBusParse is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with DBusParse. If not, see .
+
+
+install(DIRECTORY DBusParseUtils/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/DBusParseUtils)
+install(DIRECTORY DBusParse/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/DBusParse)
diff --git a/include/DBusParse/dbus.hpp b/include/DBusParse/dbus.hpp
new file mode 100644
index 0000000..672ac07
--- /dev/null
+++ b/include/DBusParse/dbus.hpp
@@ -0,0 +1,1769 @@
+// Copyright 2020 Kevin Backhouse.
+//
+// This file is part of DBusParse.
+//
+// DBusParse is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DBusParse is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with DBusParse. If not, see .
+
+
+#pragma once
+
+#include "error.hpp"
+#include "parse.hpp"
+#include
+
+enum MessageType {
+ MSGTYPE_INVALID = 0,
+ MSGTYPE_METHOD_CALL = 1,
+ MSGTYPE_METHOD_RETURN = 2,
+ MSGTYPE_ERROR = 3,
+ MSGTYPE_SIGNAL = 4
+};
+
+enum MessageFlags {
+ MSGFLAGS_EMPTY = 0x0,
+ MSGFLAGS_NO_REPLY_EXPECTED = 0x1,
+ MSGFLAGS_NO_AUTO_START = 0x2,
+ MSGFLAGS_ALLOW_INTERACTIVE_AUTHORIZATION = 0x4
+};
+
+enum HeaderFieldName {
+ MSGHDR_INVALID = 0,
+ MSGHDR_PATH = 1,
+ MSGHDR_INTERFACE = 2,
+ MSGHDR_MEMBER = 3,
+ MSGHDR_ERROR_NAME = 4,
+ MSGHDR_REPLY_SERIAL = 5,
+ MSGHDR_DESTINATION = 6,
+ MSGHDR_SENDER = 7,
+ MSGHDR_SIGNATURE = 8,
+ MSGHDR_UNIX_FDS = 9
+};
+
+class Serializer {
+public:
+ Serializer() {}
+ virtual ~Serializer() {}
+
+ virtual void writeByte(char c) = 0;
+ virtual void writeBytes(const char* buf, size_t bufsize) = 0;
+ virtual void writeUint16(uint16_t x) = 0;
+ virtual void writeUint32(uint32_t x) = 0;
+ virtual void writeUint64(uint64_t x) = 0;
+ virtual void writeDouble(double d) = 0;
+
+ // Insert padding bytes until the position is at the next multiple
+ // of `alignment`. The alignment must be a power of 2.
+ virtual void insertPadding(size_t alignment) = 0;
+
+ // Number of bytes serialized so far.
+ virtual size_t getPos() const = 0;
+
+ virtual void recordArraySize(const std::function& f) = 0;
+};
+
+// Interface for pretty printing.
+class Printer {
+public:
+ virtual ~Printer() {}
+
+ virtual void printChar(char c) = 0;
+ virtual void printUint8(uint8_t x) = 0;
+ virtual void printInt8(int8_t x) = 0;
+ virtual void printUint16(uint16_t x) = 0;
+ virtual void printInt16(int16_t x) = 0;
+ virtual void printUint32(uint32_t x) = 0;
+ virtual void printInt32(int32_t x) = 0;
+ virtual void printUint64(uint64_t x) = 0;
+ virtual void printInt64(int64_t x) = 0;
+ virtual void printDouble(double x) = 0;
+ virtual void printString(const std::string& str) = 0;
+ virtual void printNewline(size_t indent) = 0;
+};
+
+class DBusType;
+class DBusTypeChar;
+class DBusTypeBoolean;
+class DBusTypeUint16;
+class DBusTypeInt16;
+class DBusTypeUint32;
+class DBusTypeInt32;
+class DBusTypeUint64;
+class DBusTypeInt64;
+class DBusTypeDouble;
+class DBusTypeUnixFD;
+class DBusTypeString;
+class DBusTypePath;
+class DBusTypeSignature;
+class DBusTypeVariant;
+class DBusTypeDictEntry;
+class DBusTypeArray;
+class DBusTypeStruct;
+
+class DBusTypeStorage;
+
+class DBusObject;
+class DBusObjectChar;
+class DBusObjectBoolean;
+class DBusObjectUint16;
+class DBusObjectInt16;
+class DBusObjectUint32;
+class DBusObjectInt32;
+class DBusObjectUint64;
+class DBusObjectInt64;
+class DBusObjectDouble;
+class DBusObjectUnixFD;
+class DBusObjectString;
+class DBusObjectPath;
+class DBusObjectSignature;
+class DBusObjectVariant;
+class DBusObjectDictEntry;
+class DBusObjectArray;
+class DBusObjectStruct;
+
+class DBusType {
+public:
+ // Visitor interface
+ class Visitor {
+ public:
+ virtual void visitChar(const DBusTypeChar&) = 0;
+ virtual void visitBoolean(const DBusTypeBoolean&) = 0;
+ virtual void visitUint16(const DBusTypeUint16&) = 0;
+ virtual void visitInt16(const DBusTypeInt16&) = 0;
+ virtual void visitUint32(const DBusTypeUint32&) = 0;
+ virtual void visitInt32(const DBusTypeInt32&) = 0;
+ virtual void visitUint64(const DBusTypeUint64&) = 0;
+ virtual void visitInt64(const DBusTypeInt64&) = 0;
+ virtual void visitDouble(const DBusTypeDouble&) = 0;
+ virtual void visitUnixFD(const DBusTypeUnixFD&) = 0;
+ virtual void visitString(const DBusTypeString&) = 0;
+ virtual void visitPath(const DBusTypePath&) = 0;
+ virtual void visitSignature(const DBusTypeSignature&) = 0;
+ virtual void visitVariant(const DBusTypeVariant&) = 0;
+ virtual void visitDictEntry(const DBusTypeDictEntry&) = 0;
+ virtual void visitArray(const DBusTypeArray&) = 0;
+ virtual void visitStruct(const DBusTypeStruct&) = 0;
+ };
+
+ // Continuation for parsing a type.
+ class ParseTypeCont {
+ public:
+ virtual ~ParseTypeCont() {}
+ virtual std::unique_ptr parse(
+ DBusTypeStorage& typeStorage, // Type allocator
+ const Parse::State& p,
+ const DBusType& t
+ ) = 0;
+ virtual std::unique_ptr parseCloseParen(
+ DBusTypeStorage& typeStorage, // Type allocator
+ const Parse::State& p
+ ) = 0;
+ };
+
+ // Continuation for parsing an object of this type.
+ template
+ class ParseObjectCont {
+ public:
+ virtual ~ParseObjectCont() {}
+ virtual std::unique_ptr parse(
+ const Parse::State& p, std::unique_ptr&& obj
+ ) = 0;
+ };
+
+ virtual ~DBusType() {}
+
+ // When D-Bus objects are serialized, they are aligned. For example
+ // a UINT32 is 32-bit aligned and a STRUCT is 64-bit aligned. This
+ // virtual method returns the alignment for the type. It corresponds
+ // to the alignment column of this table:
+ // https://dbus.freedesktop.org/doc/dbus-specification.html#idm694
+ virtual size_t alignment() const = 0;
+
+ virtual void serialize(Serializer& s) const = 0;
+
+ virtual void print(Printer& p) const = 0;
+
+ std::string toString() const;
+
+ // Create a parser for this type. The parameter is a continuation
+ // function, which will receive the `DBusObject` which was parsed.
+ // This method uses the template method design pattern to delegate
+ // most of the work to `mkObjectParserImpl` (below). But this
+ // wrapper takes care of alignment.
+ template
+ std::unique_ptr mkObjectParser(
+ const Parse::State& p,
+ std::unique_ptr>&& cont
+ ) const;
+
+ virtual void accept(Visitor& visitor) const = 0;
+
+protected:
+ // Little endian parser.
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const = 0;
+
+ // Big endian parser.
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const = 0;
+};
+
+class DBusTypeChar final : public DBusType {
+public:
+ virtual size_t alignment() const override { return sizeof(char); }
+
+ // DBusTypeChar is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeChar instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('y');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('y');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitChar(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeBoolean final : public DBusType {
+public:
+ // D-Bus Booleans are 32 bits.
+ // https://dbus.freedesktop.org/doc/dbus-specification.html#idm694
+ virtual size_t alignment() const override { return sizeof(uint32_t); }
+
+ // DBusTypeBoolean is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeBoolean instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('b');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('b');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitBoolean(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeUint16 final : public DBusType {
+public:
+ virtual size_t alignment() const override { return sizeof(uint16_t); }
+
+ // DBusTypeUint16 is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeUint16 instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('q');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('q');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitUint16(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeInt16 final : public DBusType {
+public:
+ virtual size_t alignment() const override { return sizeof(int16_t); }
+
+ // DBusTypeInt16 is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeInt16 instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('n');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('n');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitInt16(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeUint32 final : public DBusType {
+public:
+ virtual size_t alignment() const override { return sizeof(uint32_t); }
+
+ // DBusTypeUint32 is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeUint32 instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('u');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('u');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitUint32(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeInt32 final : public DBusType {
+public:
+ virtual size_t alignment() const override { return sizeof(int32_t); }
+
+ // DBusTypeInt32 is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeInt32 instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('i');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('i');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitInt32(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeUint64 final : public DBusType {
+public:
+ virtual size_t alignment() const override { return sizeof(uint64_t); }
+
+ // DBusTypeUint64 is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeUint64 instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('t');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('t');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitUint64(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeInt64 final : public DBusType {
+public:
+ virtual size_t alignment() const override { return sizeof(int64_t); }
+
+ // DBusTypeInt64 is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeInt64 instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('x');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('x');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitInt64(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeDouble final : public DBusType {
+public:
+ virtual size_t alignment() const override { return sizeof(int32_t); }
+
+ // DBusTypeDouble is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeDouble instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('d');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('d');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitDouble(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeUnixFD final : public DBusType {
+public:
+ virtual size_t alignment() const override { return sizeof(int32_t); }
+
+ // DBusTypeUnixFD is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeUnixFD instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('h');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('h');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitUnixFD(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeString final : public DBusType {
+public:
+ virtual size_t alignment() const override {
+ return sizeof(uint32_t); // For the length
+ }
+
+ // DBusTypeString is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeString instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('s');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('s');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitString(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypePath final : public DBusType {
+public:
+ virtual size_t alignment() const override {
+ return sizeof(uint32_t); // For the length
+ }
+
+ // DBusTypePath is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypePath instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('o');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('o');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitPath(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeSignature final : public DBusType {
+public:
+ virtual size_t alignment() const override {
+ return sizeof(char); // The length of a signature fits in a char
+ }
+
+ // DBusTypeSignature is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeSignature instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('g');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('g');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitSignature(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeVariant final : public DBusType {
+public:
+ virtual size_t alignment() const override {
+ // A serialized variant starts with a signature, which has a 1-byte
+ // alignment.
+ return sizeof(char);
+ }
+
+ // DBusTypeVariant is constant and doesn't have any parameters,
+ // so this instance is available for anyone to use.
+ static const DBusTypeVariant instance_;
+
+ virtual void serialize(Serializer& s) const override {
+ s.writeByte('v');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('v');
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitVariant(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const override;
+};
+
+class DBusTypeDictEntry : public DBusType {
+ // Reference to the key type which is not owned by this class.
+ const DBusType& keyType_;
+
+ // Reference to the value type which is not owned by this class.
+ const DBusType& valueType_;
+
+public:
+ // We keep references to `keyType` and `valueType`, but do not take
+ // ownership of them.
+ DBusTypeDictEntry(const DBusType& keyType, const DBusType& valueType) :
+ keyType_(keyType), valueType_(valueType)
+ {}
+
+ const DBusType& getKeyType() const { return keyType_; }
+ const DBusType& getValueType() const { return valueType_; }
+
+ virtual size_t alignment() const final override {
+ return sizeof(uint64_t); // Same as DBusTypeStruct
+ }
+
+ virtual void serialize(Serializer& s) const final override {
+ s.writeByte('{');
+ keyType_.serialize(s);
+ valueType_.serialize(s);
+ s.writeByte('}');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('{');
+ keyType_.print(p);
+ valueType_.print(p);
+ p.printChar('}');
+ }
+
+ virtual void accept(Visitor& visitor) const final override {
+ visitor.visitDictEntry(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const final override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const final override;
+};
+
+class DBusTypeArray : public DBusType {
+ // Reference to the base type which is not owned by this class.
+ const DBusType& baseType_;
+
+public:
+ // We keep a reference to the baseType, but do not take ownership of it.
+ explicit DBusTypeArray(const DBusType& baseType) : baseType_(baseType) {}
+
+ const DBusType& getBaseType() const { return baseType_; }
+
+ virtual size_t alignment() const final override {
+ return sizeof(uint32_t); // For the length
+ }
+
+ virtual void serialize(Serializer& s) const final override {
+ s.writeByte('a');
+ baseType_.serialize(s);
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('a');
+ baseType_.print(p);
+ }
+
+ virtual void accept(Visitor& visitor) const final override {
+ visitor.visitArray(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const final override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const final override;
+};
+
+class DBusTypeStruct : public DBusType {
+ // The vector of field types is owned by this class, but it contains
+ // references to types which we do not own.
+ const std::vector> fieldTypes_;
+
+public:
+ // We take ownership of the vector, but not the field types which it
+ // references.
+ explicit DBusTypeStruct(
+ std::vector>&& fieldTypes
+ ) :
+ fieldTypes_(std::move(fieldTypes))
+ {}
+
+ const std::vector>&
+ getFieldTypes() const {
+ return fieldTypes_;
+ }
+
+ virtual size_t alignment() const final override {
+ return sizeof(uint64_t);
+ }
+
+ virtual void serialize(Serializer& s) const final override {
+ s.writeByte('(');
+ for (const DBusType& i: fieldTypes_) {
+ i.serialize(s);
+ }
+ s.writeByte(')');
+ }
+
+ virtual void print(Printer& p) const override {
+ p.printChar('(');
+ for (const DBusType& i: fieldTypes_) {
+ i.print(p);
+ }
+ p.printChar(')');
+ }
+
+ virtual void accept(Visitor& visitor) const final override {
+ visitor.visitStruct(*this);
+ }
+
+protected:
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const final override;
+
+ virtual std::unique_ptr mkObjectParserImpl(
+ const Parse::State& p, std::unique_ptr>&& cont
+ ) const final override;
+};
+
+// `DBusType` uses references to refer to sub-types. This is because the
+// type is usually embedded in a `DBusObject`, so there is no need to store
+// it separately. But there are two occasions where we need to store types
+// separately:
+//
+// 1. During parsing the signature of a `DBusObjectVariant`.
+// 2. The element type of a `DBusObjectArray` with zero elements.
+//
+// Leaf types like `DBusTypeChar` do not need to be allocated because they
+// have a global constant instance. So we only need to allocate memory for
+// array and struct types, which are the only non-leaf types. This class
+// allocates and stores objects of type `DBusTypeArray` and
+// `DBusTypeStruct`. It is unlikely to be used much, so it just uses a pair
+// of simply linked lists.
+class DBusTypeStorage final {
+ class ArrayLink final : public DBusTypeArray {
+ const std::unique_ptr next_;
+
+ public:
+ ArrayLink(
+ const DBusType& baseType,
+ std::unique_ptr&& next
+ ) :
+ DBusTypeArray(baseType),
+ next_(std::move(next))
+ {}
+ };
+
+ class DictEntryLink final : public DBusTypeDictEntry {
+ const std::unique_ptr next_;
+
+ public:
+ DictEntryLink(
+ const DBusType& keyType,
+ const DBusType& valueType,
+ std::unique_ptr&& next
+ ) :
+ DBusTypeDictEntry(keyType, valueType),
+ next_(std::move(next))
+ {}
+ };
+
+ class StructLink final : public DBusTypeStruct {
+ const std::unique_ptr next_;
+
+ public:
+ StructLink(
+ std::vector>&& fieldTypes,
+ std::unique_ptr&& next
+ ) :
+ DBusTypeStruct(std::move(fieldTypes)),
+ next_(std::move(next))
+ {}
+ };
+
+ std::unique_ptr arrays_;
+ std::unique_ptr dict_entries_;
+ std::unique_ptr structs_;
+
+public:
+ DBusTypeStorage() {}
+
+ const DBusTypeArray& allocArray(const DBusType& baseType) {
+ arrays_ = std::make_unique(baseType, std::move(arrays_));
+ return *arrays_;
+ }
+
+ const DBusTypeDictEntry& allocDictEntry(
+ const DBusType& keyType, const DBusType& valueType
+ ) {
+ dict_entries_ =
+ std::make_unique(
+ keyType, valueType, std::move(dict_entries_)
+ );
+ return *dict_entries_;
+ }
+
+ const DBusTypeStruct& allocStruct(
+ std::vector>&& fieldTypes
+ ) {
+ structs_ = std::make_unique(
+ std::move(fieldTypes), std::move(structs_)
+ );
+ return *structs_;
+ }
+};
+
+class ObjectCastError : public Error {
+public:
+ explicit ObjectCastError(const char* name) :
+ Error(std::string("ObjectCastError:" + std::string(name)))
+ {}
+};
+
+class DBusObject {
+public:
+ // Visitor interface
+ class Visitor {
+ public:
+ virtual void visitChar(const DBusObjectChar&) = 0;
+ virtual void visitBoolean(const DBusObjectBoolean&) = 0;
+ virtual void visitUint16(const DBusObjectUint16&) = 0;
+ virtual void visitInt16(const DBusObjectInt16&) = 0;
+ virtual void visitUint32(const DBusObjectUint32&) = 0;
+ virtual void visitInt32(const DBusObjectInt32&) = 0;
+ virtual void visitUint64(const DBusObjectUint64&) = 0;
+ virtual void visitInt64(const DBusObjectInt64&) = 0;
+ virtual void visitDouble(const DBusObjectDouble&) = 0;
+ virtual void visitUnixFD(const DBusObjectUnixFD&) = 0;
+ virtual void visitString(const DBusObjectString&) = 0;
+ virtual void visitPath(const DBusObjectPath&) = 0;
+ virtual void visitSignature(const DBusObjectSignature&) = 0;
+ virtual void visitVariant(const DBusObjectVariant&) = 0;
+ virtual void visitDictEntry(const DBusObjectDictEntry&) = 0;
+ virtual void visitArray(const DBusObjectArray&) = 0;
+ virtual void visitStruct(const DBusObjectStruct&) = 0;
+ };
+
+ DBusObject() {}
+ virtual ~DBusObject() = default;
+
+ virtual const DBusType& getType() const = 0;
+
+ // Always call serializePadding before calling this method.
+ virtual void serializeAfterPadding(Serializer& s) const = 0;
+
+ virtual void print(Printer& p, size_t indent) const = 0;
+
+ virtual void accept(Visitor& visitor) const = 0;
+
+ virtual const DBusObjectChar& toChar() const {
+ throw ObjectCastError("Char");
+ }
+ virtual const DBusObjectBoolean& toBoolean() const {
+ throw ObjectCastError("Boolean");
+ }
+ virtual const DBusObjectUint16& toUint16() const {
+ throw ObjectCastError("Uint16");
+ }
+ virtual const DBusObjectInt16& toInt16() const {
+ throw ObjectCastError("Int16");
+ }
+ virtual const DBusObjectUint32& toUint32() const {
+ throw ObjectCastError("Uint32");
+ }
+ virtual const DBusObjectInt32& toInt32() const {
+ throw ObjectCastError("Int32");
+ }
+ virtual const DBusObjectUint64& toUint64() const {
+ throw ObjectCastError("Uint64");
+ }
+ virtual const DBusObjectInt64& toInt64() const {
+ throw ObjectCastError("Int64");
+ }
+ virtual const DBusObjectDouble& toDouble() const {
+ throw ObjectCastError("Double");
+ }
+ virtual const DBusObjectUnixFD& toUnixFD() const {
+ throw ObjectCastError("UnixFD");
+ }
+ virtual const DBusObjectString& toString() const {
+ throw ObjectCastError("String");
+ }
+ virtual const DBusObjectPath& toPath() const {
+ throw ObjectCastError("Path");
+ }
+ virtual const DBusObjectSignature& toSignature() const {
+ throw ObjectCastError("Signature");
+ }
+ virtual const DBusObjectVariant& toVariant() const {
+ throw ObjectCastError("Variant");
+ }
+ virtual const DBusObjectDictEntry& toDictEntry() const {
+ throw ObjectCastError("DictEntry");
+ }
+ virtual const DBusObjectArray& toArray() const {
+ throw ObjectCastError("Array");
+ }
+ virtual const DBusObjectStruct& toStruct() const {
+ throw ObjectCastError("Struct");
+ }
+
+ void print(Printer& p) const {
+ print(p, 0);
+ p.printNewline(0);
+ }
+
+ void serialize(Serializer& s) const {
+ s.insertPadding(getType().alignment());
+ serializeAfterPadding(s);
+ }
+
+ size_t serializedSize() const;
+};
+
+class DBusObjectChar final : public DBusObject {
+ const char c_;
+
+public:
+ explicit DBusObjectChar(char c);
+
+ static std::unique_ptr mk(char c) {
+ return std::make_unique(c);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeChar::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ s.writeByte(c_);
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitChar(*this);
+ }
+
+ const DBusObjectChar& toChar() const override { return *this; }
+
+ char getValue() const { return c_; }
+};
+
+class DBusObjectBoolean final : public DBusObject {
+ const bool b_;
+
+public:
+ explicit DBusObjectBoolean(bool b);
+
+ static std::unique_ptr mk(bool b) {
+ return std::make_unique(b);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeBoolean::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ // D-Bus Booleans are 32 bits.
+ // https://dbus.freedesktop.org/doc/dbus-specification.html#idm694
+ s.writeUint32(static_cast(b_));
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitBoolean(*this);
+ }
+
+ const DBusObjectBoolean& toBoolean() const override { return *this; }
+
+ bool getValue() const { return b_; }
+};
+
+class DBusObjectUint16 final : public DBusObject {
+ const uint16_t x_;
+
+public:
+ explicit DBusObjectUint16(uint16_t x);
+
+ static std::unique_ptr mk(uint16_t x) {
+ return std::make_unique(x);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeUint16::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ s.writeUint16(x_);
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitUint16(*this);
+ }
+
+ const DBusObjectUint16& toUint16() const override { return *this; }
+
+ uint16_t getValue() const { return x_; }
+};
+
+class DBusObjectInt16 final : public DBusObject {
+ const int16_t x_;
+
+public:
+ explicit DBusObjectInt16(int16_t x);
+
+ static std::unique_ptr mk(int16_t x) {
+ return std::make_unique(x);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeInt16::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ s.writeUint16(static_cast(x_));
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitInt16(*this);
+ }
+
+ const DBusObjectInt16& toInt16() const override { return *this; }
+
+ int16_t getValue() const { return x_; }
+};
+
+class DBusObjectUint32 final : public DBusObject {
+ const uint32_t x_;
+
+public:
+ explicit DBusObjectUint32(uint32_t x);
+
+ static std::unique_ptr mk(uint32_t x) {
+ return std::make_unique(x);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeUint32::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ s.writeUint32(x_);
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitUint32(*this);
+ }
+
+ const DBusObjectUint32& toUint32() const override { return *this; }
+
+ uint32_t getValue() const { return x_; }
+};
+
+class DBusObjectInt32 final : public DBusObject {
+ const int32_t x_;
+
+public:
+ explicit DBusObjectInt32(int32_t x);
+
+ static std::unique_ptr mk(int32_t x) {
+ return std::make_unique(x);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeInt32::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ s.writeUint32(static_cast(x_));
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitInt32(*this);
+ }
+
+ const DBusObjectInt32& toInt32() const override { return *this; }
+
+ int32_t getValue() const { return x_; }
+};
+
+class DBusObjectUint64 final : public DBusObject {
+ const uint64_t x_;
+
+public:
+ explicit DBusObjectUint64(uint64_t x);
+
+ static std::unique_ptr mk(uint64_t x) {
+ return std::make_unique(x);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeUint64::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ s.writeUint64(x_);
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitUint64(*this);
+ }
+
+ const DBusObjectUint64& toUint64() const override { return *this; }
+
+ uint64_t getValue() const { return x_; }
+};
+
+class DBusObjectInt64 final : public DBusObject {
+ const int64_t x_;
+
+public:
+ explicit DBusObjectInt64(int64_t x);
+
+ static std::unique_ptr mk(int64_t x) {
+ return std::make_unique(x);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeInt64::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ s.writeUint64(static_cast(x_));
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitInt64(*this);
+ }
+
+ const DBusObjectInt64& toInt64() const override { return *this; }
+
+ int64_t getValue() const { return x_; }
+};
+
+class DBusObjectDouble final : public DBusObject {
+ const double d_;
+
+public:
+ explicit DBusObjectDouble(double d);
+
+ static std::unique_ptr mk(double d) {
+ return std::make_unique(d);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeDouble::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ s.writeDouble(d_);
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitDouble(*this);
+ }
+
+ const DBusObjectDouble& toDouble() const override { return *this; }
+
+ double getValue() const { return d_; }
+};
+
+class DBusObjectUnixFD final : public DBusObject {
+ // Note: `i_` is not a file descriptor. It is an index into
+ // an array of file descriptors, which is passed out-of-band.
+ const uint32_t i_;
+
+public:
+ explicit DBusObjectUnixFD(uint32_t i);
+
+ static std::unique_ptr mk(uint32_t i) {
+ return std::make_unique(i);
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeUnixFD::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ s.writeUint32(i_);
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitUnixFD(*this);
+ }
+
+ const DBusObjectUnixFD& toUnixFD() const override { return *this; }
+
+ uint32_t getValue() const { return i_; }
+};
+
+class DBusObjectString final : public DBusObject {
+ const std::string str_;
+
+public:
+ explicit DBusObjectString(std::string&& str);
+
+ static std::unique_ptr mk(std::string&& str) {
+ return std::make_unique(std::move(str));
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeString::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ uint32_t len = str_.size();
+ s.writeUint32(len);
+ s.writeBytes(str_.c_str(), len+1);
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitString(*this);
+ }
+
+ const DBusObjectString& toString() const override { return *this; }
+
+ const std::string& getValue() const { return str_; }
+};
+
+class DBusObjectPath final : public DBusObject {
+ const std::string str_;
+
+public:
+ explicit DBusObjectPath(std::string&& str);
+
+ static std::unique_ptr mk(std::string&& str) {
+ return std::make_unique(std::move(str));
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypePath::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ uint32_t len = str_.size();
+ s.writeUint32(len);
+ s.writeBytes(str_.c_str(), len+1);
+ }
+
+ virtual void print(Printer& p, size_t) const override {
+ p.printString(str_);
+ }
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitPath(*this);
+ }
+
+ const DBusObjectPath& toPath() const override { return *this; }
+
+ const std::string& getValue() const { return str_; }
+};
+
+// Almost identical to DBusObjectString and DBusObjectPath, except that a
+// single byte is used to serialize the length. (The maximum length of a
+// signature is 255.)
+class DBusObjectSignature final : public DBusObject {
+ const std::string str_;
+
+public:
+ explicit DBusObjectSignature(std::string&& str);
+
+ static std::unique_ptr mk(std::string&& str) {
+ return std::make_unique(std::move(str));
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeSignature::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ uint8_t len = str_.size();
+ s.writeByte(len);
+ s.writeBytes(str_.c_str(), len+1);
+ }
+
+ virtual void print(Printer& p, size_t) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitSignature(*this);
+ }
+
+ const DBusObjectSignature& toSignature() const override { return *this; }
+
+ const std::string& getValue() const { return str_; }
+
+ // Parse the sequence of types from the signature string. You need to
+ // supply a `DBusTypeStorage` so that the parser can allocate new
+ // types. The return value of the function contains references (into the
+ // `DBusTypeStorage` object), so you need to make sure that the storage
+ // doesn't get deallocated until you are finished with the types.
+ std::vector> toTypes(
+ DBusTypeStorage& typeStorage // Type allocator
+ ) const;
+};
+
+class DBusObjectVariant final : public DBusObject {
+ const std::unique_ptr object_;
+ const DBusObjectSignature signature_;
+
+public:
+ explicit DBusObjectVariant(std::unique_ptr&& object);
+
+ static std::unique_ptr mk(
+ std::unique_ptr&& object
+ ) {
+ return std::make_unique(std::move(object));
+ }
+
+ virtual const DBusType& getType() const override {
+ return DBusTypeVariant::instance_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const override {
+ signature_.serialize(s);
+ object_->serialize(s);
+ }
+
+ virtual void print(Printer& p, size_t indent) const override;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitVariant(*this);
+ }
+
+ const DBusObjectVariant& toVariant() const override { return *this; }
+
+ const std::unique_ptr& getValue() const { return object_; }
+};
+
+class DBusObjectDictEntry : public DBusObject {
+ const std::unique_ptr key_;
+ const std::unique_ptr value_;
+ const DBusTypeDictEntry dictEntryType_;
+
+public:
+ DBusObjectDictEntry(
+ std::unique_ptr&& key,
+ std::unique_ptr&& value
+ );
+
+ static std::unique_ptr mk(
+ std::unique_ptr&& key,
+ std::unique_ptr&& value
+ ) {
+ return std::make_unique(
+ std::move(key), std::move(value)
+ );
+ }
+
+ virtual const DBusType& getType() const final override {
+ return dictEntryType_;
+ }
+
+ virtual void serializeAfterPadding(Serializer& s) const final override {
+ key_->serialize(s);
+ value_->serialize(s);
+ }
+
+ virtual void print(Printer& p, size_t indent) const override final;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitDictEntry(*this);
+ }
+
+ const DBusObjectDictEntry& toDictEntry() const override { return *this; }
+
+ const std::unique_ptr& getKey() const { return key_; }
+ const std::unique_ptr& getValue() const { return value_; }
+};
+
+class DBusObjectSeq final {
+ const std::vector> elements_;
+
+public:
+ explicit DBusObjectSeq(std::vector>&& elements);
+
+ size_t length() const { return elements_.size(); }
+
+ std::vector> elementTypes() const {
+ std::vector> types;
+ types.reserve(elements_.size());
+ for (auto& element : elements_) {
+ types.push_back(std::cref(element->getType()));
+ }
+ return types;
+ }
+
+ void print(Printer& p, size_t indent, char lbracket, char rbracket) const;
+
+ void serialize(Serializer& s) const {
+ for (auto& p: elements_) {
+ p->serialize(s);
+ }
+ }
+
+ const std::unique_ptr& getElement(size_t i) const {
+ return elements_.at(i);
+ }
+};
+
+class DBusObjectArray : public DBusObject {
+ const DBusObjectSeq seq_;
+
+ // Note: this type contains a reference to the base type of the array
+ // type. It doesn't own the base type, so we need to make sure that it
+ // cannot become a dangling pointer. That's easy when the array has
+ // at least one element because we can just make it a reference to the
+ // type of element zero. But if there are zero elements, then we need
+ // to make sure that we own the base type. This problem is solved by
+ // DBusObjectArray0, which owns any struct or array types that are used
+ // in the base type.
+ const DBusTypeArray arrayType_;
+
+public:
+ // DBusObjectArray keeps a reference to baseType, so the
+ // lifetime of baseType must exceed that of the DBusObjectArray.
+ DBusObjectArray(
+ const DBusType& baseType,
+ std::vector>&& elements
+ );
+
+ // Constructing an array with zero elements needs to be handled as
+ // a special case to avoid the `arrayType_` field containing a dangling
+ // pointer. See the comment on `arrayType_`.
+ static std::unique_ptr mk0(const DBusType& baseType);
+
+ // If the number of elements is non-zero, then we can deduce the base type
+ // from the zero'th element.
+ static std::unique_ptr mk1(
+ std::vector>&& elements
+ ) {
+ return std::make_unique(
+ elements.at(0)->getType(), std::move(elements)
+ );
+ }
+
+ static std::unique_ptr mk(
+ const DBusType& baseType,
+ std::vector>&& elements
+ ) {
+ if (elements.size() == 0) {
+ return mk0(baseType);
+ } else {
+ return mk1(std::move(elements));
+ }
+ }
+
+ virtual const DBusType& getType() const final override { return arrayType_; }
+
+ virtual void serializeAfterPadding(Serializer& s) const final override {
+ s.recordArraySize(
+ [this, &s](uint32_t arraySize) {
+ s.writeUint32(arraySize);
+ s.insertPadding(arrayType_.getBaseType().alignment());
+ const size_t posBefore = s.getPos();
+ seq_.serialize(s);
+ const size_t posAfter = s.getPos();
+ return posAfter - posBefore;
+ }
+ );
+ }
+
+ virtual void print(Printer& p, size_t indent) const override final;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitArray(*this);
+ }
+
+ const DBusObjectArray& toArray() const override { return *this; }
+
+ size_t numElements() const { return seq_.length(); }
+
+ const std::unique_ptr& getElement(size_t i) const {
+ return seq_.getElement(i);
+ }
+};
+
+// An array with zero elements.
+class DBusObjectArray0 final : public DBusObjectArray {
+ DBusTypeStorage typeStorage_;
+
+public:
+ DBusObjectArray0(
+ const DBusType& baseType,
+ std::vector>&& elements,
+ DBusTypeStorage&& typeStorage
+ );
+};
+
+class DBusObjectStruct : public DBusObject {
+ const DBusObjectSeq seq_;
+ const DBusTypeStruct structType_;
+
+public:
+ explicit DBusObjectStruct(std::vector>&& elements);
+
+ static std::unique_ptr mk(
+ std::vector>&& elements
+ ) {
+ return std::make_unique(std::move(elements));
+ }
+
+ virtual const DBusType& getType() const final override { return structType_; }
+
+ virtual void serializeAfterPadding(Serializer& s) const final override {
+ seq_.serialize(s);
+ }
+
+ virtual void print(Printer& p, size_t indent) const override final;
+
+ virtual void accept(Visitor& visitor) const override {
+ visitor.visitStruct(*this);
+ }
+
+ const DBusObjectStruct& toStruct() const override { return *this; }
+
+ size_t numFields() const { return seq_.length(); }
+
+ const std::unique_ptr& getElement(size_t i) const {
+ return seq_.getElement(i);
+ }
+};
+
+// Utility for constructing the message header.
+class DBusHeaderField final : public DBusObjectStruct {
+public:
+ DBusHeaderField(HeaderFieldName name, std::unique_ptr&& v);
+
+ static std::unique_ptr mk(
+ HeaderFieldName name, std::unique_ptr&& v
+ ) {
+ return std::make_unique(name, std::move(v));
+ }
+};
+
+class DBusMessageBody {
+ const DBusObjectSeq seq_;
+
+public:
+ explicit DBusMessageBody(std::vector>&& elements);
+
+ // Create an empty message body.
+ static std::unique_ptr mk0();
+
+ // Create a message body with 1 element.
+ static std::unique_ptr mk1(
+ std::unique_ptr&& element
+ );
+
+ // Create a message body with multiple elements.
+ static std::unique_ptr mk(
+ std::vector>&& elements
+ );
+
+ std::string signature() const;
+
+ void serialize(Serializer& s) const;
+
+ size_t serializedSize() const;
+
+ void print(Printer& p, size_t indent) const;
+
+ size_t numElements() const { return seq_.length(); }
+
+ const std::unique_ptr& getElement(size_t i) const {
+ return seq_.getElement(i);
+ }
+};
+
+class DBusMessage {
+ std::unique_ptr header_;
+ std::unique_ptr body_;
+
+public:
+ DBusMessage(
+ std::unique_ptr&& header,
+ std::unique_ptr&& body
+ ) :
+ header_(std::move(header)), body_(std::move(body))
+ {}
+
+ const DBusObjectStruct& getHeader() const {
+ return header_->toStruct();
+ }
+
+ const DBusMessageBody& getBody() const {
+ return *body_;
+ }
+
+ // Read the endianness value in the header.
+ char getHeader_endianness() const {
+ return getHeader().getElement(0)->toChar().getValue();
+ }
+
+ // Read the message type in the header.
+ MessageType getHeader_messageType() const {
+ return static_cast(
+ getHeader().getElement(1)->toChar().getValue()
+ );
+ }
+
+ // Read the message flags in the header.
+ MessageFlags getHeader_messageFlags() const {
+ return static_cast(
+ getHeader().getElement(2)->toChar().getValue()
+ );
+ }
+
+ // Read the major protocol version in the header.
+ uint8_t getHeader_protocolVersion() const {
+ return static_cast(
+ getHeader().getElement(3)->toChar().getValue()
+ );
+ }
+
+ // Read the body size value in the header.
+ uint32_t getHeader_bodySize() const {
+ return getHeader().getElement(4)->toUint32().getValue();
+ }
+
+ // Read the reply serial number in the header.
+ uint32_t getHeader_replySerial() const {
+ return getHeader().getElement(5)->toUint32().getValue();
+ }
+
+ const DBusObjectVariant& getHeader_lookupField(HeaderFieldName name) const {
+ const DBusObjectArray& fields = getHeader().getElement(6)->toArray();
+ const size_t n = fields.numElements();
+ for (size_t i = 0; i < n; i++) {
+ const DBusObjectStruct& field = fields.getElement(i)->toStruct();
+ if (field.getElement(0)->toChar().getValue() == name) {
+ return field.getElement(1)->toVariant();
+ }
+ }
+ throw ObjectCastError("DBusMessage::getHeader_lookupField");
+ }
+
+ // Parse a `DBusMessage`. On success the message is assigned
+ // to `result`.
+ template
+ static std::unique_ptr parse(
+ std::unique_ptr& result
+ );
+
+ // Shorthand for `parse`.
+ static std::unique_ptr parseLE(
+ std::unique_ptr& result
+ );
+
+ // Shorthand for `parse`.
+ static std::unique_ptr parseBE(
+ std::unique_ptr& result
+ );
+
+ void serialize(Serializer& s) const;
+
+ void print(Printer& p, size_t indent) const;
+};
+
+// The type of the header of a DBus message.
+extern const DBusTypeStruct headerType;
+
+// Utility for downcasting to std::unique_ptr.
+inline std::unique_ptr _obj(std::unique_ptr&& o) {
+ return std::unique_ptr(std::move(o));
+}
+
+// Make a deep copy of the type. The leaf types, like `DBusTypeChar` do not
+// need to be allocated because they have a global constant instance. But
+// we need to allocate memory for arrays and structs. This is done by adding
+// elements to the `arrays` or `structs` vectors.
+const DBusType& cloneType(
+ DBusTypeStorage& typeStorage, // Type allocator
+ const DBusType& t
+);
diff --git a/include/DBusParse/dbus_auth.hpp b/include/DBusParse/dbus_auth.hpp
new file mode 100644
index 0000000..f06f8ad
--- /dev/null
+++ b/include/DBusParse/dbus_auth.hpp
@@ -0,0 +1,23 @@
+// Copyright 2020 Kevin Backhouse.
+//
+// This file is part of DBusParse.
+//
+// DBusParse is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DBusParse is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with DBusParse. If not, see .
+
+
+#pragma once
+
+#include
+
+void dbus_sendauth(const uid_t uid, const int fd);
diff --git a/include/DBusParse/dbus_print.hpp b/include/DBusParse/dbus_print.hpp
new file mode 100644
index 0000000..fcd9d34
--- /dev/null
+++ b/include/DBusParse/dbus_print.hpp
@@ -0,0 +1,56 @@
+// Copyright 2020 Kevin Backhouse.
+//
+// This file is part of DBusParse.
+//
+// DBusParse is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DBusParse is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with DBusParse. If not, see .
+
+
+#pragma once
+
+#include "dbus.hpp"
+
+// Implementation of the `Printer` interface which sends its output to a
+// file descriptor.
+class PrinterFD final : public Printer {
+ // This class is not responsible for closing the file descriptor.
+ const int fd_;
+
+ // Decimal or hexadecimal output?
+ const int base_;
+
+ const size_t tabsize_;
+
+ void printBytes(const char* buf, size_t bufsize);
+
+public:
+ PrinterFD(int fd, size_t base, size_t tabsize) :
+ fd_(fd), base_(base), tabsize_(tabsize)
+ {}
+
+ void printChar(char c) override;
+ void printUint8(uint8_t x) override;
+ void printInt8(int8_t x) override;
+ void printUint16(uint16_t x) override;
+ void printInt16(int16_t x) override;
+ void printUint32(uint32_t x) override;
+ void printInt32(int32_t x) override;
+ void printUint64(uint64_t x) override;
+ void printInt64(int64_t x) override;
+ void printDouble(double x) override;
+ void printString(const std::string& str) override;
+
+ // Print a newline character, followed by `tabsize_ * indent`
+ // space chracters.
+ void printNewline(size_t indent) override;
+};
diff --git a/include/DBusParse/dbus_random.hpp b/include/DBusParse/dbus_random.hpp
new file mode 100644
index 0000000..482fb50
--- /dev/null
+++ b/include/DBusParse/dbus_random.hpp
@@ -0,0 +1,91 @@
+// Copyright 2020 Kevin Backhouse.
+//
+// This file is part of DBusParse.
+//
+// DBusParse is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DBusParse is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with DBusParse. If not, see .
+
+
+#pragma once
+
+#include
+#include "dbus.hpp"
+
+// Parameters for generating random DBus types and objects.
+class DBusRandom {
+public:
+ // Return a randomly chosen letter corresponding to a valid DBus type.
+ // If `maxdepth` is zero, then the result should not be a basic type,
+ // not a container type (like array or struct).
+ // https://dbus.freedesktop.org/doc/dbus-specification.html#idm487
+ virtual char randomType(const size_t maxdepth) = 0;
+
+ // Choose a random number of fields for a struct.
+ virtual size_t randomNumFields() = 0;
+
+ // Choose a random number of elements for an array.
+ virtual size_t randomArraySize() = 0;
+
+ virtual char randomChar() = 0;
+ virtual bool randomBoolean() = 0;
+ virtual uint16_t randomUint16() = 0;
+ virtual uint32_t randomUint32() = 0;
+ virtual uint64_t randomUint64() = 0;
+ virtual double randomDouble() = 0;
+ virtual std::string randomString() = 0;
+ virtual std::string randomPath() = 0;
+};
+
+// Implementation of DBusRandom, using a Mersenne Twister
+// pseudo random number generator.
+class DBusRandomMersenne : public DBusRandom {
+ std::mt19937_64 gen_; // Random number generator
+
+ // Keeps track of the total number of struct and array elements allocated
+ // so far, to prevent the total size from getting too big.
+ size_t maxsize_;
+
+public:
+ DBusRandomMersenne(std::uint_fast64_t seed, size_t maxsize);
+
+ char randomType(const size_t maxdepth) final override;
+
+ // Choose a random number of fields for a struct.
+ size_t randomNumFields() final override;
+
+ char randomChar() final override;
+ bool randomBoolean() final override;
+ uint16_t randomUint16() final override;
+ uint32_t randomUint32() final override;
+ uint64_t randomUint64() final override;
+ double randomDouble() final override;
+ std::string randomString() final override;
+ std::string randomPath() final override;
+
+ // Choose a random number of elements for an array.
+ size_t randomArraySize() final override;
+};
+
+// Generate a random DBusType.
+const DBusType& randomType(
+ DBusRandom& r,
+ DBusTypeStorage& typeStorage, // Type allocator
+ const size_t maxdepth
+);
+
+// Generate a random DBusObject.
+std::unique_ptr randomObject(
+ DBusRandom& r,
+ const DBusType& t,
+ const size_t maxdepth
+);
diff --git a/include/DBusParse/dbus_serialize.hpp b/include/DBusParse/dbus_serialize.hpp
new file mode 100644
index 0000000..ce2e7e6
--- /dev/null
+++ b/include/DBusParse/dbus_serialize.hpp
@@ -0,0 +1,242 @@
+// Copyright 2020 Kevin Backhouse.
+//
+// This file is part of DBusParse.
+//
+// DBusParse is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DBusParse is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with DBusParse. If not, see .
+
+
+#pragma once
+
+#include
+#include "dbus.hpp"
+
+// Round pos up to a multiple of alignment.
+inline size_t alignup(size_t pos, size_t alignment) {
+ // Check that alignment is a power of 2.
+ assert((alignment & (alignment - 1)) == 0);
+ return (pos + alignment - 1) & ~(alignment - 1);
+}
+
+// This implementation of the Serializer interface is
+// used to count how many bytes the output buffer will need.
+class SerializerDryRunBase : public Serializer {
+ size_t pos_;
+
+public:
+ SerializerDryRunBase() : pos_(0) {}
+
+ virtual void writeByte(char) override { pos_ += sizeof(char); }
+ virtual void writeBytes(const char*, size_t bufsize) override { pos_ += bufsize; }
+ virtual void writeUint16(uint16_t) override { pos_ += sizeof(uint16_t); }
+ virtual void writeUint32(uint32_t) override { pos_ += sizeof(uint32_t); }
+ virtual void writeUint64(uint64_t) override { pos_ += sizeof(uint64_t); }
+ virtual void writeDouble(double) override { pos_ += sizeof(double); }
+
+ virtual void insertPadding(size_t alignment) override;
+
+ virtual size_t getPos() const override { return pos_; }
+};
+
+// This implementation of the Serializer interface is
+// used to count how many bytes the output buffer will need.
+class SerializerDryRun final : public SerializerDryRunBase {
+ size_t arrayCount_;
+
+public:
+ SerializerDryRun() : arrayCount_(0) {}
+
+ size_t getArrayCount() const { return arrayCount_; }
+
+ virtual void recordArraySize(
+ const std::function& f
+ ) override;
+};
+
+class SerializerInitArraySizes final : public SerializerDryRunBase {
+ std::vector& arraySizes_; // Not owned
+
+public:
+ SerializerInitArraySizes(std::vector& arraySizes) :
+ arraySizes_(arraySizes)
+ {}
+
+ virtual void recordArraySize(
+ const std::function& f
+ ) override;
+};
+
+class SerializeToBufferBase : public Serializer {
+ size_t arrayCount_;
+ const std::vector& arraySizes_; // Not owned
+
+public:
+ SerializeToBufferBase(const std::vector& arraySizes) :
+ arrayCount_(0), arraySizes_(arraySizes)
+ {}
+
+ virtual void recordArraySize(
+ const std::function& f
+ ) override;
+};
+
+template
+class SerializeToBuffer final : public SerializeToBufferBase {
+ size_t pos_;
+ char* buf_; // Not owned by this class
+
+public:
+ SerializeToBuffer(const std::vector& arraySizes, char* buf) :
+ SerializeToBufferBase(arraySizes), pos_(0), buf_(buf)
+ {}
+
+ virtual void writeByte(char c) override {
+ buf_[pos_] = c;
+ pos_ += sizeof(char);
+ }
+
+ virtual void writeBytes(const char* buf, size_t bufsize) override {
+ memcpy(&buf_[pos_], buf, bufsize);
+ pos_ += bufsize;
+ }
+
+ virtual void writeUint16(uint16_t x) override {
+ static_assert(endianness == LittleEndian || endianness == BigEndian);
+ if (endianness == LittleEndian) {
+ *(uint16_t*)&buf_[pos_] = htole16(x);
+ } else {
+ *(uint16_t*)&buf_[pos_] = htobe16(x);
+ }
+ pos_ += sizeof(uint16_t);
+ }
+
+ virtual void writeUint32(uint32_t x) override {
+ static_assert(endianness == LittleEndian || endianness == BigEndian);
+ if (endianness == LittleEndian) {
+ *(uint32_t*)&buf_[pos_] = htole32(x);
+ } else {
+ *(uint32_t*)&buf_[pos_] = htobe32(x);
+ }
+ pos_ += sizeof(uint32_t);
+ }
+
+ virtual void writeUint64(uint64_t x) override {
+ static_assert(endianness == LittleEndian || endianness == BigEndian);
+ if (endianness == LittleEndian) {
+ *(uint64_t*)&buf_[pos_] = htole64(x);
+ } else {
+ *(uint64_t*)&buf_[pos_] = htobe64(x);
+ }
+ pos_ += sizeof(uint64_t);
+ }
+
+ virtual void writeDouble(double d) override {
+ // double is the same size as uint64_t, so we cast the value
+ // to uint64_t and use writeUint64.
+ static_assert(sizeof(double) == sizeof(uint64_t));
+ union Cast {
+ double d_;
+ uint64_t x_;
+ };
+ Cast c;
+ c.d_ = d;
+ writeUint64(c.x_);
+ }
+
+ virtual void insertPadding(size_t alignment) override {
+ // Calculate the new position.
+ const size_t newpos = alignup(pos_, alignment);
+ // Insert zero bytes.
+ memset(&buf_[pos_], 0, newpos - pos_);
+ pos_ = newpos;
+ }
+
+ virtual size_t getPos() const override { return pos_; }
+};
+
+// Warning: this serializer is only suitable for serializing types, not
+// objects. That's because you can't put '\0' bytes into a std::string, and
+// '\0' bytes are required for objects. (Types serialize to pure ASCII, so
+// they're ok.)
+template
+class SerializeToString final : public SerializeToBufferBase {
+ std::string& str_; // Not owned
+
+public:
+ SerializeToString(const std::vector& arraySizes, std::string& str) :
+ SerializeToBufferBase(arraySizes), str_(str)
+ {}
+
+ virtual void writeByte(char c) override {
+ str_.push_back(c);
+ }
+
+ virtual void writeBytes(const char* buf, size_t bufsize) override {
+ str_.append(buf, bufsize);
+ }
+
+ virtual void writeUint16(uint16_t x) override {
+ static_assert(endianness == LittleEndian || endianness == BigEndian);
+ if (endianness == LittleEndian) {
+ x = htole16(x);
+ } else {
+ x = htobe16(x);
+ }
+ writeBytes((const char*)&x, sizeof(x));
+ }
+
+ virtual void writeUint32(uint32_t x) override {
+ static_assert(endianness == LittleEndian || endianness == BigEndian);
+ if (endianness == LittleEndian) {
+ x = htole32(x);
+ } else {
+ x = htobe32(x);
+ }
+ writeBytes((const char*)&x, sizeof(x));
+ }
+
+ virtual void writeUint64(uint64_t x) override {
+ static_assert(endianness == LittleEndian || endianness == BigEndian);
+ if (endianness == LittleEndian) {
+ x = htole64(x);
+ } else {
+ x = htobe64(x);
+ }
+ writeBytes((const char*)&x, sizeof(x));
+ }
+
+ virtual void writeDouble(double d) override {
+ // double is the same size as uint64_t, so we cast the value
+ // to uint64_t and use writeUint64.
+ static_assert(sizeof(double) == sizeof(uint64_t));
+ union Cast {
+ double d_;
+ uint64_t x_;
+ };
+ Cast c;
+ c.d_ = d;
+ writeUint64(c.x_);
+ }
+
+ virtual void insertPadding(size_t alignment) override {
+ // Check that alignment is a power of 2.
+ assert((alignment & (alignment - 1)) == 0);
+ // Calculate the new position.
+ const size_t pos = getPos();
+ const size_t newpos = (pos + alignment - 1) & ~(alignment - 1);
+ // Insert zero bytes.
+ str_.append('\0', newpos - pos);
+ }
+
+ virtual size_t getPos() const override { return str_.length(); }
+};
diff --git a/include/DBusParse/dbus_utils.hpp b/include/DBusParse/dbus_utils.hpp
new file mode 100644
index 0000000..5ebc101
--- /dev/null
+++ b/include/DBusParse/dbus_utils.hpp
@@ -0,0 +1,67 @@
+// Copyright 2020 Kevin Backhouse.
+//
+// This file is part of DBusParse.
+//
+// DBusParse is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DBusParse is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with DBusParse. If not, see .
+
+
+#pragma once
+
+#include
+#include "dbus.hpp"
+
+void send_dbus_message_with_fds(
+ const int fd, const DBusMessage& message,
+ const size_t nfds, const int* fds
+);
+
+void send_dbus_message(const int fd, const DBusMessage& message);
+
+void print_dbus_object(const int fd, const DBusObject& obj);
+
+void print_dbus_message(const int fd, const DBusMessage& message);
+
+std::unique_ptr receive_dbus_message(const int fd);
+
+void dbus_method_call_with_fds(
+ const int fd,
+ const uint32_t serialNumber,
+ std::unique_ptr&& body,
+ std::string&& path,
+ std::string&& interface,
+ std::string&& destination,
+ std::string&& member,
+ const size_t nfds,
+ const int* fds
+);
+
+void dbus_method_call(
+ const int fd,
+ const uint32_t serialNumber,
+ std::unique_ptr&& body,
+ std::string&& path,
+ std::string&& interface,
+ std::string&& destination,
+ std::string&& member
+);
+
+void dbus_method_reply(
+ const int fd,
+ const uint32_t serialNumber,
+ const uint32_t replySerialNumber, // serial number that we are replying to
+ std::unique_ptr&& body,
+ std::string&& destination
+);
+
+void dbus_send_hello(const int fd);
diff --git a/include/DBusParseUtils/endianness.hpp b/include/DBusParseUtils/endianness.hpp
new file mode 100644
index 0000000..08fc42d
--- /dev/null
+++ b/include/DBusParseUtils/endianness.hpp
@@ -0,0 +1,24 @@
+// Copyright 2020 Kevin Backhouse.
+//
+// This file is part of DBusParse.
+//
+// DBusParse is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DBusParse is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with DBusParse. If not, see .
+
+
+#pragma once
+
+enum Endianness {
+ LittleEndian,
+ BigEndian
+};
diff --git a/include/DBusParseUtils/error.hpp b/include/DBusParseUtils/error.hpp
new file mode 100644
index 0000000..e3a4d45
--- /dev/null
+++ b/include/DBusParseUtils/error.hpp
@@ -0,0 +1,48 @@
+// Copyright 2020 Kevin Backhouse.
+//
+// This file is part of DBusParse.
+//
+// DBusParse is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DBusParse is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with DBusParse. If not, see .
+
+
+#pragma once
+
+#include
+
+// Exception class. Caught in main().
+class Error : public std::exception {
+ std::string msg_;
+
+public:
+ Error() = delete; // No default constructor.
+ explicit Error(const char* msg) : msg_(msg) {}
+ explicit Error(std::string&& msg) : msg_(std::move(msg)) {}
+
+ const char* what() const noexcept override {
+ return msg_.c_str();
+ }
+};
+
+// Exception class for system errors that include an errno. Caught in
+// main().
+class ErrorWithErrno : public Error {
+ const int err_;
+
+public:
+ ErrorWithErrno() = delete; // No default constructor.
+ explicit ErrorWithErrno(const char* msg) : Error(msg), err_(errno) {}
+ explicit ErrorWithErrno(std::string&& msg) : Error(std::move(msg)), err_(errno) {}
+
+ int getErrno() const { return err_; }
+};
diff --git a/include/DBusParseUtils/parse.hpp b/include/DBusParseUtils/parse.hpp
new file mode 100644
index 0000000..bd3696c
--- /dev/null
+++ b/include/DBusParseUtils/parse.hpp
@@ -0,0 +1,418 @@
+// Copyright 2020 Kevin Backhouse.
+//
+// This file is part of DBusParse.
+//
+// DBusParse is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// DBusParse is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with DBusParse. If not, see .
+
+
+#pragma once
+
+#include
+#include
+#include
+#include "endianness.hpp"
+
+// Continuation-passing-style parser implementation. The main class
+// is `Parse`. You initialize the parser with a continuation of
+// type `Parse::Cont`. The continuation consumes a fixed number of
+// bytes and returns a new continuation to keep parsing the rest of
+// the input. The continuation-passing design has several benefits:
+//
+// 1. It consumes the input in small chunks, so it is easy to feed it with
+// network data. This means that we can easily pause parsing while we
+// wait for more data, so it works well in conjunction with something
+// like epoll.
+// 2. Because the parser processes data incrementally it can reject
+// invalid messages early, without needing to wait for the whole
+// message to arrive.
+// 3. The implementation does not use recursion, so it is impossible for a
+// malicious input to trigger stack exhaustion in the parser. (Of course
+// a stack exhaustion could still happen later if we run a recursive
+// algorithm over the parsed object.) There is a still a parsing stack,
+// but it consists of a linked list of continuations on the heap.
+//
+// Implementing this design in C++ may have been a mistake.
+
+// Thrown as an exception when the parser encounters an invalid input.
+class ParseError final : public std::exception {
+ // Byte position of the parse error.
+ const size_t pos_;
+
+ // Error message.
+ std::string msg_;
+
+public:
+ ParseError() = delete; // No default constructor.
+ explicit ParseError(size_t pos, const char* msg) :
+ pos_(pos), msg_(msg)
+ {}
+ explicit ParseError(size_t pos, std::string&& msg) :
+ pos_(pos), msg_(std::move(msg))
+ {}
+
+ size_t getPos() const noexcept { return pos_; }
+
+ const char* what() const noexcept override {
+ return msg_.c_str();
+ }
+};
+
+class Parse final {
+public:
+ // This class has a virtual method which is the continuation function.
+ class Cont;
+
+ // The parsing state is currently just the number of bytes parsed so far,
+ // but it is wrapped in a class to make it easier to add other status
+ // information in the future. A const reference to the parsing state is
+ // passed as an argument to `Parse::Cont::parse()`, so that the parsers
+ // can inspect the current state. For example, this is often used to
+ // calculate the alignment of the current byte position.
+ class State {
+ friend Parse;
+
+ protected:
+ // The number of bytes parsed so far.
+ // This is used for calculating alignments.
+ size_t pos_;
+
+ explicit State(size_t pos) : pos_(pos) {}
+
+ public:
+ // No copy constructor
+ State(const State&) = delete;
+
+ size_t getPos() const { return pos_; }
+
+ static const State initialState_;
+ };
+
+private:
+ State state_;
+ std::unique_ptr cont_;
+
+public:
+ // No copy constructor
+ Parse(const Parse&) = delete;
+
+ // For initializing the parser.
+ explicit Parse(std::unique_ptr&& cont) :
+ state_(0),
+ cont_(std::move(cont))
+ {}
+
+ // Before calling this method, you should call `minRequiredBytes()`
+ // and `maxRequiredBytes()` to find
+ // out how many bytes the parser is prepared to accept. You must call
+ // this method with a value of `bufsize` that satisfies these rules:
+ //
+ // 1. minRequiredBytes() <= bufsize
+ // 2. bufsize <= maxRequiredBytes()
+ //
+ // The reason why the number of required bytes is specified as a range
+ // is to enable the caller to use a fixed-size buffer. A fixed size
+ // buffer of 255 bytes is guaranteed to be sufficient. So the caller
+ // can keep feeding the parser small chunks of bytes until parsing
+ // is complete.
+ void parse(const char* buf, size_t bufsize);
+
+ // The number of bytes parsed so far.
+ size_t getPos() const { return state_.pos_; }
+
+ // The minimum number of bytes that the parser needs to make
+ // progress. This simplifies the implementation of the parsers for simple
+ // types like `uint32_t`, because it means that they don't need to be
+ // able to accept partial input. For example, the parser for `uint32_t`
+ // can specify that it needs at least 4 bytes to make progress. Note
+ // that the result of this function is a `uint8_t`, which means that the
+ // caller doesn't need to implement an arbitrary sized buffer. A 255
+ // byte fixed-size buffer is guaranteed to be sufficient.
+ uint8_t minRequiredBytes() const;
+
+ // The maximum number of bytes that the parser is prepared to
+ // consume at this time. If this method returns 0 then it
+ // means that parsing is complete.
+ size_t maxRequiredBytes() const;
+};
+
+class Parse::Cont {
+public:
+ virtual ~Cont() {}
+ virtual std::unique_ptr parse(
+ const Parse::State& p, const char* buf, size_t bufsize
+ ) = 0;
+
+ // The minimum number of bytes that this continuation is willing to
+ // accept.
+ virtual uint8_t minRequiredBytes() const = 0;
+
+ // The maximum number of bytes that this continuation is willing to
+ // accept.
+ virtual size_t maxRequiredBytes() const = 0;
+};
+
+// This continuation is used to indicate that parsing is complete.
+// It does so by returning 0 in `maxRequiredBytes`.
+class ParseStop final : public Parse::Cont {
+public:
+ // This constructor is only public for std::make_unique's benefit.
+ // Use factory method `mk()` to construct.
+ ParseStop() {}
+
+ virtual std::unique_ptr parse(
+ const Parse::State& p, const char* buf, size_t
+ ) override;
+
+ // Factory method.
+ static std::unique_ptr mk();
+
+ uint8_t minRequiredBytes() const override { return 0; }
+ size_t maxRequiredBytes() const override { return 0; }
+};
+
+class ParseChar final : public Parse::Cont {
+public:
+ class Cont {
+ public:
+ virtual ~Cont() {}
+ virtual std::unique_ptr parse(
+ const Parse::State& p, char c
+ ) = 0;
+ };
+
+private:
+ // Continuation
+ const std::unique_ptr cont_;
+
+public:
+ // This constructor is only public for std::make_unique's benefit.
+ // Use factory method `mk()` to construct.
+ ParseChar(std::unique_ptr&& cont) : cont_(std::move(cont)) {}
+
+ virtual std::unique_ptr parse(
+ const Parse::State& p, const char* buf, size_t
+ ) override;
+
+ // Factory method.
+ static std::unique_ptr mk(std::unique_ptr&& cont);
+
+ uint8_t minRequiredBytes() const override { return sizeof(char); }
+ size_t maxRequiredBytes() const override { return sizeof(char); }
+};
+
+template
+class ParseUint16 final : public Parse::Cont {
+public:
+ class Cont {
+ public:
+ virtual ~Cont() {}
+ virtual std::unique_ptr parse(
+ const Parse::State& p, uint16_t c
+ ) = 0;
+ };
+
+private:
+ // Continuation
+ const std::unique_ptr cont_;
+
+public:
+ // This constructor is only public for std::make_unique's benefit.
+ // Use factory method `mk()` to construct.
+ ParseUint16(std::unique_ptr&& cont) : cont_(std::move(cont)) {}
+
+ virtual std::unique_ptr parse(
+ const Parse::State& p, const char* buf, size_t bufsize
+ ) override {
+ (void)bufsize;
+ assert(bufsize == sizeof(uint16_t));
+ static_assert(endianness == LittleEndian || endianness == BigEndian);
+ const uint16_t x =
+ endianness == LittleEndian ?
+ le16toh(*(uint16_t*)buf) :
+ be16toh(*(uint16_t*)buf);
+ return cont_->parse(p, x);
+ }
+
+ // Factory method.
+ static std::unique_ptr mk(std::unique_ptr&& cont) {
+ return std::make_unique(std::move(cont));
+ }
+
+ uint8_t minRequiredBytes() const override { return sizeof(uint16_t); }
+ size_t maxRequiredBytes() const override { return sizeof(uint16_t); }
+};
+
+template
+class ParseUint32 final : public Parse::Cont {
+public:
+ class Cont {
+ public:
+ virtual ~Cont() {}
+ virtual std::unique_ptr parse(
+ const Parse::State& p, uint32_t c
+ ) = 0;
+ };
+
+private:
+ // Continuation
+ const std::unique_ptr cont_;
+
+public:
+ // This constructor is only public for std::make_unique's benefit.
+ // Use factory method `mk()` to construct.
+ ParseUint32(std::unique_ptr&& cont) : cont_(std::move(cont)) {}
+
+ virtual std::unique_ptr parse(
+ const Parse::State& p, const char* buf, size_t bufsize
+ ) override {
+ (void)bufsize;
+ assert(bufsize == sizeof(uint32_t));
+ static_assert(endianness == LittleEndian || endianness == BigEndian);
+ const uint32_t x =
+ endianness == LittleEndian ?
+ le32toh(*(uint32_t*)buf) :
+ be32toh(*(uint32_t*)buf);
+ return cont_->parse(p, x);
+ }
+
+ // Factory method.
+ static std::unique_ptr mk(std::unique_ptr&& cont) {
+ return std::make_unique(std::move(cont));
+ }
+
+ uint8_t minRequiredBytes() const override { return sizeof(uint32_t); }
+ size_t maxRequiredBytes() const override { return sizeof(uint32_t); }
+};
+
+template