NOTICK - Initial work on a non JVM (C++) serialiser (#5368)

Currently, it's more of a blob inspector than a full-blown
implementation, but this code does have the ability to understand a Corda
serialized blob and build a set of deserialisers to handle a blob. It
does this in the same way the native java implementation works by
interrogating the types (as per the definition in the envelope) and
building recursive type readers that are able to pull the relevant
information out of the byte stream.

Lots of future work to undertake, but as a starting point its a little
bit useful with the scope it could be made much more useful with some
work.
This commit is contained in:
Katelyn Baker 2019-08-15 21:16:20 +01:00 committed by GitHub
parent a477d59c15
commit 913ba1d46e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 5288 additions and 0 deletions

178
.idea/compiler.xml generated
View File

@ -29,6 +29,8 @@
<module name="buildSrc_test" target="1.8" />
<module name="canonicalizer_main" target="1.8" />
<module name="canonicalizer_test" target="1.8" />
<module name="capsule.net.corda.capsule.main" target="1.6" />
<module name="capsule.net.corda.capsule.test" target="1.6" />
<module name="checkpoint-agent_main" target="1.8" />
<module name="checkpoint-agent_test" target="1.8" />
<module name="cli_main" target="1.8" />
@ -37,6 +39,8 @@
<module name="client_test" target="1.8" />
<module name="cliutils_main" target="1.8" />
<module name="cliutils_test" target="1.8" />
<module name="com.r3cev.prototyping.experimental.main" target="1.8" />
<module name="com.r3cev.prototyping.experimental.test" target="1.8" />
<module name="common-configuration-parsing_main" target="1.8" />
<module name="common-configuration-parsing_test" target="1.8" />
<module name="common-logging_main" target="1.8" />
@ -50,6 +54,8 @@
<module name="contracts-states_integrationTest" target="1.8" />
<module name="contracts-states_main" target="1.8" />
<module name="contracts-states_test" target="1.8" />
<module name="contracts.net.corda.contracts.main" target="1.8" />
<module name="contracts.net.corda.contracts.test" target="1.8" />
<module name="contracts_main" target="1.8" />
<module name="contracts_test" target="1.8" />
<module name="corda-core_integrationTest" target="1.8" />
@ -255,6 +261,8 @@
<module name="corda-webserver_test" target="1.8" />
<module name="cordapp-configuration-workflows_main" target="1.8" />
<module name="cordapp-configuration-workflows_test" target="1.8" />
<module name="cordapp-configuration.workflows.net.corda.workflows.main" target="1.8" />
<module name="cordapp-configuration.workflows.net.corda.workflows.test" target="1.8" />
<module name="cordapp-configuration_main" target="1.8" />
<module name="cordapp-configuration_test" target="1.8" />
<module name="cordapp_integrationTest" target="1.8" />
@ -359,8 +367,168 @@
<module name="net.corda-isolated_test" target="1.8" />
<module name="net.corda-verifier_main" target="1.8" />
<module name="net.corda-verifier_test" target="1.8" />
<module name="net.corda.attachment-demo.integrationTest" target="1.8" />
<module name="net.corda.attachment-demo.main" target="1.8" />
<module name="net.corda.attachment-demo.test" target="1.8" />
<module name="net.corda.avalanche.main" target="1.8" />
<module name="net.corda.avalanche.test" target="1.8" />
<module name="net.corda.bank-of-corda-demo.main" target="1.8" />
<module name="net.corda.bank-of-corda-demo.test" target="1.8" />
<module name="net.corda.blobinspector.main" target="1.8" />
<module name="net.corda.blobinspector.test" target="1.8" />
<module name="net.corda.bootstrapper.main" target="1.8" />
<module name="net.corda.bootstrapper.test" target="1.8" />
<module name="net.corda.buildSrc.main" target="1.8" />
<module name="net.corda.buildSrc.test" target="1.8" />
<module name="net.corda.capsule.main" target="1.8" />
<module name="net.corda.capsule.test" target="1.8" />
<module name="net.corda.checkpoint-agent.main" target="1.8" />
<module name="net.corda.checkpoint-agent.test" target="1.8" />
<module name="net.corda.client.main" target="1.8" />
<module name="net.corda.client.test" target="1.8" />
<module name="net.corda.cliutils.main" target="1.8" />
<module name="net.corda.cliutils.test" target="1.8" />
<module name="net.corda.common-configuration-parsing.main" target="1.8" />
<module name="net.corda.common-configuration-parsing.test" target="1.8" />
<module name="net.corda.common-logging.main" target="1.8" />
<module name="net.corda.common-logging.test" target="1.8" />
<module name="net.corda.common-validation.main" target="1.8" />
<module name="net.corda.common-validation.test" target="1.8" />
<module name="net.corda.confidential-identities.main" target="1.8" />
<module name="net.corda.confidential-identities.test" target="1.8" />
<module name="net.corda.contracts-irs.main" target="1.8" />
<module name="net.corda.contracts-irs.test" target="1.8" />
<module name="net.corda.contracts-states.main" target="1.8" />
<module name="net.corda.contracts-states.test" target="1.8" />
<module name="net.corda.contracts.main" target="1.8" />
<module name="net.corda.contracts.test" target="1.8" />
<module name="net.corda.corda-project.main" target="1.8" />
<module name="net.corda.corda-project.test" target="1.8" />
<module name="net.corda.corda-utils.integrationTest" target="1.8" />
<module name="net.corda.corda-utils.main" target="1.8" />
<module name="net.corda.corda-utils.test" target="1.8" />
<module name="net.corda.cordapp-configuration.main" target="1.8" />
<module name="net.corda.cordapp-configuration.test" target="1.8" />
<module name="net.corda.cordapp.integrationTest" target="1.8" />
<module name="net.corda.cordapp.main" target="1.8" />
<module name="net.corda.cordapp.test" target="1.8" />
<module name="net.corda.core-deterministic.main" target="1.8" />
<module name="net.corda.core-deterministic.test" target="1.8" />
<module name="net.corda.core-tests.integrationTest" target="1.8" />
<module name="net.corda.core-tests.main" target="1.8" />
<module name="net.corda.core-tests.smokeTest" target="1.8" />
<module name="net.corda.core-tests.test" target="1.8" />
<module name="net.corda.core.main" target="1.8" />
<module name="net.corda.core.test" target="1.8" />
<module name="net.corda.data.main" target="1.8" />
<module name="net.corda.data.test" target="1.8" />
<module name="net.corda.demobench.main" target="1.8" />
<module name="net.corda.demobench.test" target="1.8" />
<module name="net.corda.docker.main" target="1.8" />
<module name="net.corda.docker.test" target="1.8" />
<module name="net.corda.docs.main" target="1.8" />
<module name="net.corda.docs.test" target="1.8" />
<module name="net.corda.example-code.integrationTest" target="1.8" />
<module name="net.corda.example-code.main" target="1.8" />
<module name="net.corda.example-code.test" target="1.8" />
<module name="net.corda.explorer.main" target="1.8" />
<module name="net.corda.explorer.test" target="1.8" />
<module name="net.corda.finance.main" target="1.8" />
<module name="net.corda.finance.test" target="1.8" />
<module name="net.corda.flows.main" target="1.8" />
<module name="net.corda.flows.test" target="1.8" />
<module name="net.corda.graphs.main" target="1.8" />
<module name="net.corda.graphs.test" target="1.8" />
<module name="net.corda.irs-demo.integrationTest" target="1.8" />
<module name="net.corda.irs-demo.main" target="1.8" />
<module name="net.corda.irs-demo.systemTest" target="1.8" />
<module name="net.corda.irs-demo.test" target="1.8" />
<module name="net.corda.irs-demo.web.main" target="1.8" />
<module name="net.corda.irs-demo.web.test" target="1.8" />
<module name="net.corda.isolated.main" target="1.8" />
<module name="net.corda.isolated.test" target="1.8" />
<module name="net.corda.jackson.main" target="1.8" />
<module name="net.corda.jackson.test" target="1.8" />
<module name="net.corda.jdk8u-deterministic.main" target="1.8" />
<module name="net.corda.jdk8u-deterministic.test" target="1.8" />
<module name="net.corda.jfx.integrationTest" target="1.8" />
<module name="net.corda.jfx.main" target="1.8" />
<module name="net.corda.jfx.test" target="1.8" />
<module name="net.corda.loadtest.main" target="1.8" />
<module name="net.corda.loadtest.test" target="1.8" />
<module name="net.corda.mock.main" target="1.8" />
<module name="net.corda.mock.test" target="1.8" />
<module name="net.corda.netparams.main" target="1.8" />
<module name="net.corda.netparams.test" target="1.8" />
<module name="net.corda.network-builder.main" target="1.8" />
<module name="net.corda.network-builder.test" target="1.8" />
<module name="net.corda.network-verifier.main" target="1.8" />
<module name="net.corda.network-verifier.test" target="1.8" />
<module name="net.corda.node-api.main" target="1.8" />
<module name="net.corda.node-api.test" target="1.8" />
<module name="net.corda.node-driver.integrationTest" target="1.8" />
<module name="net.corda.node-driver.main" target="1.8" />
<module name="net.corda.node-driver.test" target="1.8" />
<module name="net.corda.node.integrationTest" target="1.8" />
<module name="net.corda.node.main" target="1.8" />
<module name="net.corda.node.test" target="1.8" />
<module name="net.corda.nodeinfo.main" target="1.8" />
<module name="net.corda.nodeinfo.test" target="1.8" />
<module name="net.corda.notary-demo.main" target="1.8" />
<module name="net.corda.notary-demo.test" target="1.8" />
<module name="net.corda.quasar-hook.main" target="1.8" />
<module name="net.corda.quasar-hook.test" target="1.8" />
<module name="net.corda.rpc.integrationTest" target="1.8" />
<module name="net.corda.rpc.main" target="1.8" />
<module name="net.corda.rpc.smokeTest" target="1.8" />
<module name="net.corda.rpc.test" target="1.8" />
<module name="net.corda.samples.main" target="1.8" />
<module name="net.corda.samples.test" target="1.8" />
<module name="net.corda.serialization-deterministic.main" target="1.8" />
<module name="net.corda.serialization-deterministic.test" target="1.8" />
<module name="net.corda.serialization.main" target="1.8" />
<module name="net.corda.serialization.test" target="1.8" />
<module name="net.corda.shell-cli.main" target="1.8" />
<module name="net.corda.shell-cli.test" target="1.8" />
<module name="net.corda.shell.integrationTest" target="1.8" />
<module name="net.corda.shell.main" target="1.8" />
<module name="net.corda.shell.test" target="1.8" />
<module name="net.corda.simm-valuation-demo.integrationTest" target="1.8" />
<module name="net.corda.simm-valuation-demo.main" target="1.8" />
<module name="net.corda.simm-valuation-demo.test" target="1.8" />
<module name="net.corda.smoke-test-utils.main" target="1.8" />
<module name="net.corda.smoke-test-utils.test" target="1.8" />
<module name="net.corda.test-cli.main" target="1.8" />
<module name="net.corda.test-cli.test" target="1.8" />
<module name="net.corda.test-common.main" target="1.8" />
<module name="net.corda.test-common.test" target="1.8" />
<module name="net.corda.test-db.main" target="1.8" />
<module name="net.corda.test-db.test" target="1.8" />
<module name="net.corda.test-utils.main" target="1.8" />
<module name="net.corda.test-utils.test" target="1.8" />
<module name="net.corda.testing.main" target="1.8" />
<module name="net.corda.testing.test" target="1.8" />
<module name="net.corda.tools.main" target="1.8" />
<module name="net.corda.tools.test" target="1.8" />
<module name="net.corda.trader-demo.integrationTest" target="1.8" />
<module name="net.corda.trader-demo.main" target="1.8" />
<module name="net.corda.trader-demo.test" target="1.8" />
<module name="net.corda.verifier.main" target="1.8" />
<module name="net.corda.verifier.test" target="1.8" />
<module name="net.corda.webcapsule.main" target="1.6" />
<module name="net.corda.webcapsule.test" target="1.6" />
<module name="net.corda.webserver.integrationTest" target="1.8" />
<module name="net.corda.webserver.main" target="1.8" />
<module name="net.corda.webserver.test" target="1.8" />
<module name="net.corda.workflows-irs.main" target="1.8" />
<module name="net.corda.workflows-irs.test" target="1.8" />
<module name="net.corda.workflows-trader.main" target="1.8" />
<module name="net.corda.workflows-trader.test" target="1.8" />
<module name="net.corda.workflows.integrationTest" target="1.8" />
<module name="net.corda.workflows.main" target="1.8" />
<module name="net.corda.workflows.test" target="1.8" />
<module name="net.corda.worldmap.main" target="1.8" />
<module name="net.corda.worldmap.test" target="1.8" />
<module name="net.corda_buildSrc_main" target="1.8" />
<module name="net.corda_buildSrc_test" target="1.8" />
<module name="net.corda_canonicalizer_main" target="1.8" />
@ -375,6 +543,10 @@
<module name="network-verifier-contracts_test" target="1.8" />
<module name="network-verifier-workflows_main" target="1.8" />
<module name="network-verifier-workflows_test" target="1.8" />
<module name="network-verifier.contracts.net.corda.contracts.main" target="1.8" />
<module name="network-verifier.contracts.net.corda.contracts.test" target="1.8" />
<module name="network-verifier.workflows.net.corda.workflows.main" target="1.8" />
<module name="network-verifier.workflows.net.corda.workflows.test" target="1.8" />
<module name="network-verifier_main" target="1.8" />
<module name="network-verifier_test" target="1.8" />
<module name="network-visualiser_main" target="1.8" />
@ -400,6 +572,10 @@
<module name="notary-demo-contracts_test" target="1.8" />
<module name="notary-demo-workflows_main" target="1.8" />
<module name="notary-demo-workflows_test" target="1.8" />
<module name="notary-demo.contracts.net.corda.contracts.main" target="1.8" />
<module name="notary-demo.contracts.net.corda.contracts.test" target="1.8" />
<module name="notary-demo.workflows.net.corda.workflows.main" target="1.8" />
<module name="notary-demo.workflows.net.corda.workflows.test" target="1.8" />
<module name="notary-demo_main" target="1.8" />
<module name="notary-demo_test" target="1.8" />
<module name="notary-raft_main" target="1.8" />
@ -484,6 +660,8 @@
<module name="workflows-trader_integrationTest" target="1.8" />
<module name="workflows-trader_main" target="1.8" />
<module name="workflows-trader_test" target="1.8" />
<module name="workflows.net.corda.workflows.main" target="1.8" />
<module name="workflows.net.corda.workflows.test" target="1.8" />
<module name="workflows_integrationTest" target="1.8" />
<module name="workflows_main" target="1.8" />
<module name="workflows_test" target="1.8" />

View File

@ -0,0 +1,38 @@
PROJECT (BLOB-INSPECTOR)
cmake_minimum_required(VERSION 3.10)
SET (${PROJECT_NAME}_MAJOR_VERSION 0)
SET (${PROJECT_NAME}_MINOR_VERSION 0)
SET (${PROJECT_NAME}_PATCH_LEVEL 1)
#ADD_DEFINITIONS ("-Wall -g -ansi -pedantic")
ADD_DEFINITIONS ("-Wall -g -ansi")
#ADD_DEFINITIONS ("-DSRC_DEBUG")
#
#
#
set (CMAKE_VERBOSE_MAKEFILE "FALSE" CACHE BOOL "" FORCE)
#
# For "reasons" something to do with the way the comand line compiler
# is invoked on MacOS /usr/local/include is removed from the
# system include path so add it back in or we will fail to find
# qpid-python files as brew installs it there
#
if(APPLE)
include_directories (/usr/local/include)
link_directories (/usr/local/lib)
endif()
#
# Interface include files
#
include_directories (${BLOB-INSPECTOR_SOURCE_DIR}/include)
add_compile_options(-std=c++17)
ADD_SUBDIRECTORY (src)
ADD_SUBDIRECTORY (bin)

View File

@ -0,0 +1,43 @@
# Corda Blob Inspection
This is a best effort attempt at a C++ implemention of the CORDA serialization framework in a non JVM language. The initial target is a working "blob inspector", that is an exe that can take a serialised blob from a vault (or other) and decode it using C++.
## Currently Working
An implementation of a "blob inspector" that can take a serialised blob and decode it into a printable JSON format where that blob contains a constrained set of types. The current limitation with this implementation is that it does not understand associative containers (maps).
## Fututre Work
* Encode and decode of local C++ types
* Decpdable encode of native types
* Some schema generation from the JVM canonical source
## Dependencies
* qpid-proton
* C++17
* gtest
* cmake
## Setup
### MacOS
* brew install cmake
* brew install qpid-proton
Google Test
* git clone https://github.com/google/googletest
* cd googletest
* mkdir build
* cd build
* cmake ..
* make
* make install
### Linux
* sudo apt-get install cmake
* sudo apt-get install libqpid-proton8-dev
* sudi apt-get install libgtest-dev

View File

@ -0,0 +1 @@
blob-inspector

View File

@ -0,0 +1,9 @@
include_directories (${BLOB-INSPECTOR_SOURCE_DIR}/src)
include_directories (${BLOB-INSPECTOR_SOURCE_DIR}/src/amqp)
link_directories (${BLOB-INSPECTOR_BINARY_DIR}/src/amqp)
link_directories (${BLOB-INSPECTOR_BINARY_DIR}/src/proton)
add_executable (blob-inspector main)
target_link_libraries (blob-inspector amqp proton qpid-proton)

View File

@ -0,0 +1,115 @@
#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstddef>
#include <assert.h>
#include <string.h>
#include <proton/types.h>
#include <proton/codec.h>
#include <sys/stat.h>
#import "debug.h"
#include "proton/proton_wrapper.h"
#include "amqp/AMQPHeader.h"
#include "amqp/AMQPSectionId.h"
#include "amqp/descriptors/AMQPDescriptorRegistory.h"
#include "amqp/schema/Envelope.h"
#include "CompositeFactory.h"
/******************************************************************************/
void
data_and_stop(std::ifstream & f_, ssize_t sz) {
char * blob = new char[sz];
memset (blob, 0, sz);
f_.read(blob, sz);
pn_data_t * d = pn_data(sz);
// returns how many bytes we processed which right now we don't care
// about but I assume there is a case where it doesn't process the
// entire file
auto rtn = pn_data_decode (d, blob, sz);
assert (rtn == sz);
std::unique_ptr<amqp::internal::schema::Envelope> envelope;
if (pn_data_is_described(d)) {
proton::auto_enter p (d);
auto a = pn_data_get_ulong(d);
envelope.reset (
dynamic_cast<amqp::internal::schema::Envelope *> (
amqp::AMQPDescriptorRegistory[a]->build(d).release()));
DBG (std::cout << std::endl << "Types in schema: " << std::endl
<< *envelope << std::endl); // NOLINT
}
CompositeFactory cf;
cf.process (envelope->schema());
auto reader = cf.byDescriptor (envelope->descriptor());
assert (reader);
{
// move to the actual blob entry in the tree - ideally we'd have
// saved this on the Envelope but that's not easily doable as we
// can't grab an actual copy of our data pointer
proton::auto_enter p (d);
pn_data_next (d);
proton::is_list (d);
assert (pn_data_get_list (d) == 3);
{
proton::auto_enter p (d);
// We wrap our output like this to make sure it's valid JSON to
// facilitate easy pretty printing
std::cout
<< reader->dump ("{ Parsed", d, envelope->schema())->dump()
<< " }" << std::endl;
}
}
}
/******************************************************************************/
int
main (int argc, char **argv) {
struct stat results { };
if (stat(argv[1], &results) != 0) {
return EXIT_FAILURE;
}
std::ifstream f (argv[1], std::ios::in | std::ios::binary);
std::array<char, 7> header { };
f.read(header.data(), 7);
if (header != amqp::AMQP_HEADER) {
std::cerr << "Bad Header in blob" << std::endl;
return EXIT_FAILURE;
}
amqp::amqp_section_id_t encoding;
f.read((char *)&encoding, 1);
if (encoding == amqp::DATA_AND_STOP) {
data_and_stop(f, results.st_size - 8);
} else {
std::cerr << "BAD ENCODING " << encoding << " != " << amqp::DATA_AND_STOP << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/******************************************************************************/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,65 @@
#pragma once
/******************************************************************************/
#include <map>
#include <set>
#include "types.h"
#include "amqp/schema/Schema.h"
#include "amqp/schema/Envelope.h"
#include "amqp/schema/Composite.h"
#include "amqp/consumer/PropertyReader.h"
#include "amqp/consumer/CompositeReader.h"
/******************************************************************************/
class CompositeFactory {
private :
using SchemaPtr = uPtr<amqp::internal::schema::Schema>;
using CompositePtr = uPtr<amqp::internal::schema::Composite>;
using EnvelopePtr = uPtr<amqp::internal::schema::Envelope>;
/**
*
*/
spStrMap_t<amqp::Reader> m_readersByType;
spStrMap_t<amqp::Reader> m_readersByDescriptor;
public :
CompositeFactory() = default;
void process (const SchemaPtr &);
const std::shared_ptr<amqp::Reader> byType (const std::string &);
const std::shared_ptr<amqp::Reader> byDescriptor (const std::string &);
private :
std::shared_ptr<amqp::Reader> process(const amqp::internal::schema::AMQPTypeNotation &);
std::shared_ptr<amqp::Reader>
processComposite (const amqp::internal::schema::AMQPTypeNotation &);
std::shared_ptr<amqp::Reader>
processRestricted (const amqp::internal::schema::AMQPTypeNotation &);
/*
std::shared_ptr<amqp::Reader>
process (
amqp::internal::schema::OrderedTypeNotations::const_iterator,
std::set<std::string> &);
std::shared_ptr<amqp::Reader>
processComposite (
amqp::internal::schema::Schema::SchemaSet::const_iterator,
std::set<std::string> &);
std::shared_ptr<amqp::Reader>
processRestricted (
amqp::internal::schema::Schema::SchemaSet::const_iterator,
std::set<std::string> &);
*/
};
/******************************************************************************/

View File

@ -0,0 +1,20 @@
#pragma once
/******************************************************************************/
#include <array>
/******************************************************************************/
namespace amqp {
/**
* The 8th byte is used to store weather the stream is compressed or
* not
*/
std::array<char, 7> AMQP_HEADER = { { 'c', 'o', 'r', 'd', 'a', 1, 0 } };
}
/******************************************************************************/

View File

@ -0,0 +1,20 @@
#pragma once
/******************************************************************************/
/*
* Once things are further along this should move into internal
*/
namespace amqp {
enum amqp_section_id_t {
DATA_AND_STOP = 0,
ALT_DATA_AND_STOP = 1,
ENCODING = 2
};
}
/******************************************************************************/

View File

@ -0,0 +1,22 @@
//
// Created by Katelyn Baker on 2019-07-04.
//
#ifndef BLOB_INSPECTOR_COLOURS_H
#define BLOB_INSPECTOR_COLOURS_H
/* FOREGROUND */
#define RESET "\x1B[0m"
#define RED "\x1B[31m"
#define GREEN "\x1B[32m"
#define YELLOW "\x1B[33m"
#define BLUE "\x1B[34m"
#define MAGENTA "\x1B[35m"
#define CYAN "\x1B[36m"
#define WHITE "\x1B[37m"
#define BOLD(x) "\x1B[1m" x RESET
#define UNDL(x) "\x1B[4m" x RESET
#endif //BLOB_INSPECTOR_COLOURS_H

View File

@ -0,0 +1,15 @@
#pragma once
/******************************************************************************/
#define AMQP_DEBUG 0
/******************************************************************************/
#if defined AMQP_DEBUG && AMQP_DEBUG >= 1
#define DBG(X) std::cout << X
#else
#define DBG(X)
#endif
/******************************************************************************/

View File

@ -0,0 +1,15 @@
#pragma once
/******************************************************************************/
namespace serialiser {
class Serialiser {
};
}
/******************************************************************************/

View File

@ -0,0 +1,24 @@
#pragma once
/******************************************************************************/
#include <map>
#include <memory>
/******************************************************************************/
template<typename T>
using uPtr = std::unique_ptr<T>;
template<typename T>
using sPtr = std::shared_ptr<T>;
template<typename T>
using upStrMap_t = std::map<std::string, uPtr<T>>;
template<typename T>
using spStrMap_t = std::map<std::string, sPtr<T>>;
/******************************************************************************/

View File

@ -0,0 +1,21 @@
#!/bin/bash
function cmake_rem_func {
rm -f $1/CMakeCache.txt
rm $1/Makefile
rm $1/cmake_install.cmake
rm -rf $1/CMakeFiles
}
rm -f BLOB-INSPECTOR.cbp
cmake_rem_func .
cmake_rem_func ./bin
cmake_rem_func ./src
cmake_rem_func ./src/amqp
cmake_rem_func ./src/amqp/test
cmake_rem_func ./src/proton
cmake_rem_func ./src/serialiser

View File

@ -0,0 +1 @@
lib*.a

View File

@ -0,0 +1,6 @@
include_directories (${BLOB-INSPECTOR_SOURCE_DIR}/src)
ADD_SUBDIRECTORY (proton)
ADD_SUBDIRECTORY (amqp)
ADD_SUBDIRECTORY (serialiser)

View File

@ -0,0 +1,16 @@
# Sub Dirs
## proton
C++ utility functions for the qpid-proton library and some auto objects to make working with
the library a little nicer
## amqp
The Corda AMQP Schema represtnation, both the described versino as it exists within the
stream and an instantiated set of C++ classes representing that structure.
## serialiser
Able to take the blob element of an Envelope and extract class data from it in a
menainful way.

View File

@ -0,0 +1,15 @@
#pragma once
/******************************************************************************/
namespace amqp::internal {
class AMQPDescribed {
public :
virtual ~AMQPDescribed() { }
};
}
/******************************************************************************/

View File

@ -0,0 +1,54 @@
#pragma once
/******************************************************************************/
#include <map>
#include <string>
#include <iostream>
#include "AMQPDescribed.h"
/******************************************************************************
*
* Forward type declarations
*
******************************************************************************/
struct pn_data_t;
/******************************************************************************
*
* amqp::internal::AMQPDescriptor
*
******************************************************************************/
namespace amqp::internal {
class AMQPDescriptor {
protected :
std::string m_symbol;
int32_t m_val;
public :
AMQPDescriptor()
: m_symbol ("ERROR")
, m_val (-1)
{ }
AMQPDescriptor(std::string symbol_, int val_)
: m_symbol (std::move (symbol_))
, m_val (val_)
{ }
virtual ~AMQPDescriptor() = default;
const std::string & symbol() const { return m_symbol; }
void validateAndNext (pn_data_t *) const;
virtual std::unique_ptr<AMQPDescribed> build (pn_data_t * data_) const = 0;
};
}
/******************************************************************************/

View File

@ -0,0 +1,26 @@
include_directories (descriptors)
include_directories (schema)
include_directories (consumer)
include_directories (.)
set (amqp_sources
descriptors/AMQPDescriptors.cxx
descriptors/AMQPDescriptorRegistory.cxx
schema/Schema.cxx
schema/Field.cxx
schema/Envelope.cxx
schema/Composite.cxx
schema/Descriptor.cxx
schema/restricted-types/Restricted.cxx
schema/restricted-types/List.cxx
schema/AMQPTypeNotation.cxx
consumer/Reader.cxx
consumer/PropertyReader.cxx
consumer/RestrictedReader.cxx
consumer/CompositeReader.cxx
CompositeFactory.cxx
)
ADD_LIBRARY ( amqp ${amqp_sources} )
ADD_SUBDIRECTORY (test)

View File

@ -0,0 +1,212 @@
#include "CompositeFactory.h"
#include <set>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
#include <assert.h>
#include "debug.h"
#include "consumer/Reader.h"
#include "consumer/PropertyReader.h"
#include "consumer/CompositeReader.h"
#include "consumer/RestrictedReader.h"
#include "schema/restricted-types/List.h"
/******************************************************************************/
/**
*
*/
template <typename T>
std::shared_ptr<T> &
computeIfAbsent (
spStrMap_t<T> & map_,
const std::string & k_,
std::function<std::shared_ptr<T>(void)> f_
) {
auto it = map_.find (k_);
if (it == map_.end()) {
DBG ("ComputeIfAbsent \"" << k_ << "\" - missing" << std::endl); // NOLINT
map_[k_] = std::move (f_());
DBG (" \"" << k_ << "\" - RTN: " << map_[k_]->name() << " : " << map_[k_]->type() << std::endl); // NOLINT
assert (map_[k_]);
assert (map_[k_] != nullptr);
assert (k_ == map_[k_]->type());
return map_[k_];
}
else {
DBG ("ComputeIfAbsent \"" << k_ << "\" - found it" << std::endl); // NOLINT
DBG (" \"" << k_ << "\" - RTN: " << map_[k_]->name() << std::endl); // NOLINT
assert (it->second != nullptr);
return it->second;
}
}
/******************************************************************************
*
* CompositeFactory
*
******************************************************************************/
/**
*
* Walk through the types in a Schema and produce readers for them.
*
* We are making the large assumption that the contents of [schema_]
* are strictly ordered by dependency so we can construct types
* as we go without needing to provide look ahead for types
* we haven't built yet
*
*/
void
CompositeFactory::process (const SchemaPtr & schema_) {
for (auto i = schema_->begin() ; i != schema_->end() ; ++i) {
for (const auto & j : *i) {
process(*j);
m_readersByDescriptor[j->descriptor()] = m_readersByType[j->name()];
}
}
}
/******************************************************************************/
std::shared_ptr<amqp::Reader>
CompositeFactory::process(
const amqp::internal::schema::AMQPTypeNotation & schema_)
{
return computeIfAbsent<amqp::Reader> (
m_readersByType,
schema_.name(),
[& schema_, this] () -> std::shared_ptr<amqp::Reader> {
switch (schema_.type()) {
case amqp::internal::schema::AMQPTypeNotation::Composite : {
return processComposite(schema_);
}
case amqp::internal::schema::AMQPTypeNotation::Restricted : {
return processRestricted(schema_);
}
}
});
}
/******************************************************************************/
std::shared_ptr<amqp::Reader>
CompositeFactory::processComposite (
const amqp::internal::schema::AMQPTypeNotation & type_)
{
std::vector<std::weak_ptr<amqp::Reader>> readers;
const auto & fields = dynamic_cast<const amqp::internal::schema::Composite &> (
type_).fields();
readers.reserve(fields.size());
for (const auto & field : fields) {
DBG (" Field: " << field->name() << ": " << field->type() << std::endl); // NOLINT
switch (field->fieldType()) {
case amqp::internal::schema::FieldType::PrimitiveProperty : {
auto reader = computeIfAbsent<amqp::Reader>(
m_readersByType,
field->type(),
[&field]() -> std::shared_ptr<amqp::PropertyReader> {
return amqp::PropertyReader::make(field);
});
assert (reader);
readers.emplace_back(reader);
assert (readers.back().lock());
break;
}
case amqp::internal::schema::FieldType::CompositeProperty : {
auto reader = m_readersByType[field->type()];
assert (reader);
readers.emplace_back(reader);
assert (readers.back().lock());
break;
}
case amqp::internal::schema::FieldType::RestrictedProperty : {
auto reader = m_readersByType[field->requires().front()];
assert (reader);
readers.emplace_back(reader);
assert (readers.back().lock());
break;
}
}
assert (readers.back().lock());
}
return std::make_shared<amqp::CompositeReader> (type_.name(), readers);
}
/******************************************************************************/
std::shared_ptr<amqp::Reader>
CompositeFactory::processRestricted (
const amqp::internal::schema::AMQPTypeNotation & type_)
{
DBG ("processRestricted - " << type_.name() << std::endl); // NOLINT
const auto & restricted = dynamic_cast<const amqp::internal::schema::Restricted &> (
type_);
if (restricted.restrictedType() ==
amqp::internal::schema::Restricted::RestrictedTypes::List)
{
const auto & list = dynamic_cast<const amqp::internal::schema::List &> (restricted);
DBG ("Processing List - " << list.listOf() << std::endl); // NOLINT
if (amqp::internal::schema::Field::typeIsPrimitive(list.listOf())) {
DBG (" List of Primitives" << std::endl); // NOLINT
auto reader = computeIfAbsent<amqp::Reader> (
m_readersByType,
list.listOf(),
[& list] () -> std::shared_ptr<amqp::PropertyReader> {
return amqp::PropertyReader::make (list.listOf());
});
return std::make_shared<amqp::ListReader> (type_.name(), reader);
} else {
DBG (" List of Composite - " << list.listOf() << std::endl); // NOLINT
auto reader = m_readersByType[list.listOf()];
return std::make_shared<amqp::ListReader> (list.name(), reader);
}
}
DBG (" ProcessRestricted: Returning nullptr"); // NOLINT
return nullptr;
}
/******************************************************************************/
const std::shared_ptr<amqp::Reader>
CompositeFactory::byType (const std::string & type_) {
auto it = m_readersByType.find (type_);
return (it == m_readersByType.end()) ? nullptr : it->second;
}
/******************************************************************************/
const std::shared_ptr<amqp::Reader>
CompositeFactory::byDescriptor (const std::string & descriptor_) {
auto it = m_readersByDescriptor.find (descriptor_);
return (it == m_readersByDescriptor.end()) ? nullptr : it->second;
}
/******************************************************************************/

View File

@ -0,0 +1,149 @@
#include "CompositeReader.h"
#include <string>
#include <iostream>
#include <assert.h>
#include <proton/codec.h>
#include <sstream>
#include "debug.h"
#include "proton/proton_wrapper.h"
/******************************************************************************/
const std::string amqp::CompositeReader::m_name { // NOLINT
"Composite Reader"
};
/******************************************************************************
*
*
******************************************************************************/
amqp::
CompositeReader::CompositeReader (
std::string type_,
std::vector<std::weak_ptr<amqp::Reader>> & readers_
) : m_readers (readers_)
, m_type (std::move (type_))
{
DBG ("MAKE CompositeReader: " << m_type << ": " << m_readers.size() << std::endl); // NOLINT
for (auto reader : m_readers) {
assert (reader.lock());
if (auto r = reader.lock()) {
DBG (" prop: " << r->name() << " " << r->type() << std::endl); // NOLINT
}
}
}
/******************************************************************************/
const std::string &
amqp::
CompositeReader::name() const {
return m_name;
}
/******************************************************************************/
const std::string &
amqp::
CompositeReader::type() const {
return m_type;
}
/******************************************************************************/
std::any
amqp::
CompositeReader::read (pn_data_t * data_) const {
return std::any(1);
}
/******************************************************************************/
std::string
amqp::
CompositeReader::readString (pn_data_t * data_) const {
pn_data_next (data_);
proton::auto_enter ae (data_);
return "Composite";
}
/******************************************************************************/
std::vector<std::unique_ptr<amqp::Value>>
amqp::
CompositeReader::_dump (
pn_data_t * data_,
const std::unique_ptr<amqp::internal::schema::Schema> & schema_
) const {
DBG ("Read Composite: " << m_name << " : " << type() << std::endl); // NOLINT
proton::is_described (data_);
proton::auto_enter ae (data_);
const auto & it = schema_->fromDescriptor(proton::get_symbol<std::string>(data_));
auto & fields = dynamic_cast<amqp::internal::schema::Composite &>(*(it->second.get())).fields();
assert (fields.size() == m_readers.size());
pn_data_next (data_);
std::vector<std::unique_ptr<amqp::Value>> read;
read.reserve (fields.size());
proton::is_list (data_);
{
proton::auto_enter ae (data_);
for (int i (0) ; i < m_readers.size() ; ++i) {
if (auto l = m_readers[i].lock()) {
DBG (fields[i]->name() << " " << (l ? "true" : "false") << std::endl); // NOLINT
read.emplace_back(l->dump(fields[i]->name(), data_, schema_));
} else {
std::stringstream s;
s << "null field reader: " << fields[i]->name();
throw std::runtime_error(s.str());
}
}
}
return read;
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
CompositeReader::dump (
const std::string & name_,
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<amqp::TypedPair<std::vector<std::unique_ptr<amqp::Value>>>> (
name_,
_dump(data_, schema_));
}
/******************************************************************************/
/**
*
*/
std::unique_ptr<amqp::Value>
amqp::
CompositeReader::dump (
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<amqp::TypedSingle<std::vector<std::unique_ptr<amqp::Value>>>> (
_dump (data_, schema_));
}
/******************************************************************************/

View File

@ -0,0 +1,55 @@
#pragma once
/******************************************************************************/
#include "Reader.h"
#include <any>
#include <vector>
#include <iostream>
/******************************************************************************/
namespace amqp {
class CompositeReader : public Reader {
private :
std::vector<std::weak_ptr<amqp::Reader>> m_readers;
static const std::string m_name;
std::string m_type;
std::vector<std::unique_ptr<amqp::Value>> _dump (
pn_data_t * data_,
const std::unique_ptr<amqp::internal::schema::Schema> & schema_) const;
public :
CompositeReader (
std::string type_,
std::vector<std::weak_ptr<amqp::Reader>> & readers_
);
~CompositeReader() = default;
std::any read (pn_data_t *) const override;
std::string readString(pn_data_t *) const override;
std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &) const override;
std::unique_ptr<Value> dump(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &) const override;
const std::string & name() const override;
const std::string & type() const override;
};
}
/******************************************************************************/

View File

@ -0,0 +1,343 @@
#include "PropertyReader.h"
#include <map>
#include <string>
#include <iostream>
#include <functional>
#include <proton/codec.h>
#include "proton/proton_wrapper.h"
/******************************************************************************/
namespace {
std::map<std::string, std::shared_ptr<amqp::PropertyReader>(*)() > propertyMap = {
{
"int", []() -> std::shared_ptr<amqp::PropertyReader> {
return std::make_shared<amqp::IntPropertyReader> ();
}
},
{
"string", []() -> std::shared_ptr<amqp::PropertyReader> {
return std::make_shared<amqp::StringPropertyReader> (
amqp::StringPropertyReader());
}
},
{
"boolean", []() -> std::shared_ptr<amqp::PropertyReader> {
return std::make_shared<amqp::BoolPropertyReader> (
amqp::BoolPropertyReader());
}
},
{
"long", []() -> std::shared_ptr<amqp::PropertyReader> {
return std::make_shared<amqp::LongPropertyReader> (
amqp::LongPropertyReader());
}
},
{
"double", []() -> std::shared_ptr<amqp::PropertyReader> {
return std::make_shared<amqp::DoublePropertyReader> (
amqp::DoublePropertyReader());
}
}
};
}
/******************************************************************************/
const std::string amqp::StringPropertyReader::m_name { // NOLINT
"String Reader"
};
const std::string amqp::StringPropertyReader::m_type { // NOLINT
"string"
};
const std::string amqp::IntPropertyReader::m_name { // NOLINT
"Int Reader"
};
const std::string amqp::IntPropertyReader::m_type { // NOLINT
"int"
};
const std::string amqp::BoolPropertyReader::m_name { // NOLINT
"Bool Reader"
};
const std::string amqp::BoolPropertyReader::m_type { // NOLINT
"bool"
};
const std::string amqp::LongPropertyReader::m_name { // NOLINT
"Long Reader"
};
const std::string amqp::LongPropertyReader::m_type { // NOLINT
"long"
};
const std::string amqp::DoublePropertyReader::m_name { // NOLINT
"Double Reader"
};
const std::string amqp::DoublePropertyReader::m_type { // NOLINT
"double"
};
/******************************************************************************/
/**
* Static factory method
*/
std::shared_ptr<amqp::PropertyReader>
amqp::
PropertyReader::make (const FieldPtr & field_) {
return propertyMap[field_->type()]();
}
/******************************************************************************/
std::shared_ptr<amqp::PropertyReader>
amqp::
PropertyReader::make (const std::string & type_) {
return propertyMap[type_]();
}
/******************************************************************************
*
* StringPropertyReader
*
******************************************************************************/
std::any
amqp::
StringPropertyReader::read (pn_data_t * data_) const {
return std::any ("hello");
}
/******************************************************************************/
std::string
amqp::
StringPropertyReader::readString (pn_data_t * data_) const {
return proton::readAndNext<std::string> (data_);
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
StringPropertyReader::dump (
const std::string & name_,
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedPair<std::string>> (
name_,
"\"" + proton::readAndNext<std::string> (data_) + "\"");
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
StringPropertyReader::dump (
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedSingle<std::string>> (
"\"" + proton::readAndNext<std::string> (data_) + "\"");
}
/******************************************************************************
*
* IntPropertyReader
*
******************************************************************************/
std::any
amqp::
IntPropertyReader::read (pn_data_t * data_) const {
return std::any (1);
}
/******************************************************************************/
std::string
amqp::
IntPropertyReader::readString (pn_data_t * data_) const {
return std::to_string (proton::readAndNext<int> (data_));
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
IntPropertyReader::dump (
const std::string & name_,
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedPair<std::string>> (
name_,
std::to_string (proton::readAndNext<int> (data_)));
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
IntPropertyReader::dump (
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedSingle<std::string>> (
std::to_string (proton::readAndNext<int> (data_)));
}
/******************************************************************************
*
* BoolPropertyReader
*
******************************************************************************/
std::any
amqp::
BoolPropertyReader::read (pn_data_t * data_) const {
return std::any (true);
}
/******************************************************************************/
std::string
amqp::
BoolPropertyReader::readString (pn_data_t * data_) const {
return std::to_string (proton::readAndNext<bool> (data_));
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
BoolPropertyReader::dump (
const std::string & name_,
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedPair<std::string>> (
name_,
std::to_string (proton::readAndNext<bool> (data_)));
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
BoolPropertyReader::dump (
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedSingle<std::string>> (
std::to_string (proton::readAndNext<bool> (data_)));
}
/******************************************************************************
*
* LongPropertyReader
*
******************************************************************************/
std::any
amqp::
LongPropertyReader::read (pn_data_t * data_) const {
return std::any (10L);
}
/******************************************************************************/
std::string
amqp::
LongPropertyReader::readString (pn_data_t * data_) const {
return std::to_string (proton::readAndNext<long> (data_));
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
LongPropertyReader::dump (
const std::string & name_,
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedPair<std::string>> (
name_,
std::to_string (proton::readAndNext<long> (data_)));
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
LongPropertyReader::dump (
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedSingle<std::string>> (
std::to_string (proton::readAndNext<long> (data_)));
}
/******************************************************************************
*
* DoublePropertyReader
*
******************************************************************************/
std::any
amqp::
DoublePropertyReader::read (pn_data_t * data_) const {
return std::any (10.0);
}
/******************************************************************************/
std::string
amqp::
DoublePropertyReader::readString (pn_data_t * data_) const {
return std::to_string (proton::readAndNext<double> (data_));
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
DoublePropertyReader::dump (
const std::string & name_,
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedPair<std::string>> (
name_,
std::to_string (proton::readAndNext<double> (data_)));
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
DoublePropertyReader::dump (
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_) const
{
return std::make_unique<TypedSingle<std::string>> (
std::to_string (proton::readAndNext<double> (data_)));
}
/******************************************************************************/

View File

@ -0,0 +1,204 @@
#pragma once
/******************************************************************************/
#include <iostream>
#include "Reader.h"
#include "amqp/schema/Field.h"
/******************************************************************************/
namespace amqp {
class PropertyReader : public Reader {
private :
using FieldPtr = std::unique_ptr<internal::schema::Field>;
public :
/**
* Static Factory method for creating appropriate derived types
*/
static std::shared_ptr<PropertyReader> make (const FieldPtr &);
static std::shared_ptr<PropertyReader> make (const std::string &);
~PropertyReader() override = default;
std::string readString(pn_data_t *) const override = 0;
std::any read (pn_data_t *) const override = 0;
std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override = 0;
std::unique_ptr<Value> dump(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override = 0;
const std::string & name() const override = 0;
const std::string & type() const override = 0;
};
class StringPropertyReader : public PropertyReader {
private :
static const std::string m_name;
static const std::string m_type;
public :
std::string readString (pn_data_t *) const override;
std::any read (pn_data_t *) const override;
std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
std::unique_ptr<Value> dump(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
const std::string & name() const override {
return m_name;
}
const std::string & type() const override {
return m_type;
}
};
class IntPropertyReader : public PropertyReader {
private :
static const std::string m_name;
static const std::string m_type;
public :
~IntPropertyReader() override = default;
std::string readString (pn_data_t *) const override;
std::any read (pn_data_t *) const override;
std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
std::unique_ptr<Value> dump(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
const std::string & name() const override {
return m_name;
}
const std::string & type() const override {
return m_type;
}
};
class BoolPropertyReader : public PropertyReader {
private :
static const std::string m_name;
static const std::string m_type;
public :
std::string readString (pn_data_t *) const override;
std::any read (pn_data_t *) const override;
std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
std::unique_ptr<Value> dump(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
const std::string & name() const override {
return m_name;
}
const std::string & type() const override {
return m_type;
}
};
class LongPropertyReader : public PropertyReader {
private :
static const std::string m_name;
static const std::string m_type;
public :
std::string readString (pn_data_t *) const override;
std::any read (pn_data_t *) const override;
std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
std::unique_ptr<Value> dump(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
const std::string & name() const override {
return m_name;
}
const std::string & type() const override {
return m_type;
}
};
class DoublePropertyReader : public PropertyReader {
private :
static const std::string m_name;
static const std::string m_type;
public :
std::string readString (pn_data_t *) const override;
std::any read (pn_data_t *) const override;
std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
std::unique_ptr<Value> dump(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &
) const override;
const std::string & name() const override {
return m_name;
}
const std::string & type() const override {
return m_type;
}
};
}
/******************************************************************************/

View File

@ -0,0 +1,136 @@
#include "Reader.h"
#include <memory>
#include <sstream>
/******************************************************************************/
namespace {
struct AutoMap {
std::stringstream & m_stream;
AutoMap (const std::string & s, std::stringstream & stream_) : m_stream (stream_) {
m_stream << s << " : { ";
}
AutoMap (std::stringstream & stream_) : m_stream (stream_) {
m_stream << "{ ";
}
~AutoMap() {
m_stream << " }";
}
};
struct AutoList {
std::stringstream & m_stream;
AutoList (const std::string & s, std::stringstream & stream_) : m_stream (stream_) {
m_stream << s << " : [ ";
}
AutoList (std::stringstream & stream_) : m_stream (stream_) {
m_stream << "[ ";
}
~AutoList() {
m_stream << " ]";
}
};
template<class Auto, class T>
std::string
dumpPair(const std::string & name_, const T & begin_, const T & end_) {
std::stringstream rtn;
{
Auto am (name_, rtn);
rtn << (*(begin_))->dump();
for (auto it(std::next(begin_)) ; it != end_; ++it) {
rtn << ", " << (*it)->dump();
}
}
return rtn.str();
}
template<class Auto, class T>
std::string
dumpSingle(const T & begin_, const T & end_) {
std::stringstream rtn;
{
Auto am (rtn);
rtn << (*(begin_))->dump();
for (auto it(std::next(begin_)) ; it != end_; ++it) {
rtn << ", " << (*it)->dump();
}
}
return rtn.str();
}
}
/******************************************************************************
*
*
*
******************************************************************************/
template<>
std::string
amqp::TypedPair<std::vector<std::unique_ptr<amqp::Pair>>>::dump() const {
return ::dumpPair<AutoMap> (m_property, m_value.begin(), m_value.end());
}
template<>
std::string
amqp::TypedPair<std::list<std::unique_ptr<amqp::Pair>>>::dump() const {
return ::dumpPair<AutoMap> (m_property, m_value.begin(), m_value.end());
}
template<>
std::string
amqp::TypedPair<std::vector<std::unique_ptr<amqp::Value>>>::dump() const {
return ::dumpPair<AutoMap> (m_property, m_value.begin(), m_value.end());
}
template<>
std::string
amqp::TypedPair<std::list<std::unique_ptr<amqp::Value>>>::dump() const {
return ::dumpPair<AutoList> (m_property, m_value.begin(), m_value.end());
}
/******************************************************************************
*
*
*
******************************************************************************/
template<>
std::string
amqp::TypedSingle<std::list<std::unique_ptr<amqp::Value>>>::dump() const {
return ::dumpSingle<AutoList> (m_value.begin(), m_value.end());
}
template<>
std::string
amqp::TypedSingle<std::vector<std::unique_ptr<amqp::Value>>>::dump() const {
return ::dumpSingle<AutoMap> (m_value.begin(), m_value.end());
}
template<>
std::string
amqp::TypedSingle<std::list<std::unique_ptr<amqp::Single>>>::dump() const {
return ::dumpSingle<AutoList> (m_value.begin(), m_value.end());
}
template<>
std::string
amqp::TypedSingle<std::vector<std::unique_ptr<amqp::Single>>>::dump() const {
return ::dumpSingle<AutoMap> (m_value.begin(), m_value.end());
}
/******************************************************************************/

View File

@ -0,0 +1,211 @@
#pragma once
/******************************************************************************/
#include <any>
#include <list>
#include <string>
#include <vector>
#include <memory>
#include "schema/Schema.h"
/******************************************************************************/
struct pn_data_t;
/******************************************************************************/
namespace amqp {
class Value {
public :
virtual std::string dump() const = 0;
virtual ~Value() = default;
};
class Single : public Value {
public :
std::string dump() const override = 0;
};
template<typename T>
class TypedSingle : public Single {
private:
T m_value;
public:
explicit TypedSingle (const T & value_)
: Single()
, m_value (value_)
{ }
explicit TypedSingle (T && value_)
: Single()
, m_value { std::move (value_) }
{ }
TypedSingle (const TypedSingle && value_) noexcept
: Single()
, m_value { std::move (value_.m_value) }
{ }
const T & value() const {
return m_value;
}
std::string dump() const override;
};
class Pair : public Value {
protected :
std::string m_property;
public:
explicit Pair(const std::string & property_)
: m_property (property_)
{ }
virtual ~Pair() = default;
Pair (amqp::Pair && pair_) noexcept
: m_property (std::move (pair_.m_property))
{ }
std::string dump() const override = 0;
};
template<typename T>
class TypedPair : public Pair {
private:
T m_value;
public:
TypedPair (const std::string & property_, T & value_)
: Pair (property_)
, m_value (value_)
{ }
TypedPair (const std::string & property_, T && value_)
: Pair (property_)
, m_value (std::move (value_))
{ }
TypedPair (TypedPair && pair_)
: Pair (std::move (pair_.m_property))
, m_value (std::move (pair_.m_value))
{ }
const T & value() const {
return m_value;
}
std::string dump() const override;
};
}
/******************************************************************************
*
* amqp::TypeSingle
*
******************************************************************************/
template<typename T>
inline std::string
amqp::TypedSingle<T>::dump() const {
return std::to_string(m_value);
}
template<>
inline std::string
amqp::TypedSingle<std::string>::dump() const {
return m_value;
}
template<>
std::string
amqp::TypedSingle<std::vector<std::unique_ptr<amqp::Value>>>::dump() const;
template<>
std::string
amqp::TypedSingle<std::list<std::unique_ptr<amqp::Value>>>::dump() const;
template<>
std::string
amqp::TypedSingle<std::vector<std::unique_ptr<amqp::Single>>>::dump() const;
template<>
std::string
amqp::TypedSingle<std::list<std::unique_ptr<amqp::Single>>>::dump() const;
/******************************************************************************
*
* amqp::TypedPair
*
******************************************************************************/
template<typename T>
inline std::string
amqp::TypedPair<T>::dump() const {
return m_property + " : " + std::to_string (m_value);
}
template<>
inline std::string
amqp::TypedPair<std::string>::dump() const {
return m_property + " : " + m_value;
}
template<>
std::string
amqp::TypedPair<std::vector<std::unique_ptr<amqp::Value>>>::dump() const;
template<>
std::string
amqp::TypedPair<std::list<std::unique_ptr<amqp::Value>>>::dump() const;
template<>
std::string
amqp::TypedPair<std::vector<std::unique_ptr<amqp::Pair>>>::dump() const;
template<>
std::string
amqp::TypedPair<std::list<std::unique_ptr<amqp::Pair>>>::dump() const;
/******************************************************************************
*
*
*
*
******************************************************************************/
namespace amqp {
class Reader {
public :
virtual ~Reader() = default;
virtual const std::string & name() const = 0;
virtual const std::string & type() const = 0;
virtual std::any read(pn_data_t *) const = 0;
virtual std::string readString(pn_data_t *) const = 0;
virtual std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &) const = 0;
virtual std::unique_ptr<Value> dump(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &) const = 0;
};
}
/******************************************************************************/

View File

@ -0,0 +1,116 @@
#include "RestrictedReader.h"
#include <iostream>
#include "proton/proton_wrapper.h"
/******************************************************************************/
const std::string amqp::RestrictedReader::m_name { // NOLINT
"Restricted Reader"
};
/******************************************************************************/
std::any
amqp::
RestrictedReader::read(pn_data_t *) const {
return std::any(1);
}
/******************************************************************************/
std::string
amqp::
RestrictedReader::readString(pn_data_t * data_) const {
return "hello";
}
/******************************************************************************/
std::list<std::unique_ptr<amqp::Value>>
amqp::
ListReader::dump_(
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_
) const {
proton::is_described (data_);
std::list<std::unique_ptr<amqp::Value>> read;
{
proton::auto_enter ae (data_);
auto it = schema_->fromDescriptor(proton::readAndNext<std::string>(data_));
{
proton::auto_list_enter ale (data_, true);
for (size_t i { 0 } ; i < ale.elements() ; ++i) {
read.emplace_back (m_reader.lock()->dump(data_, schema_));
}
}
}
return read;
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
ListReader::dump (
const std::string & name_,
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_
) const {
proton::auto_next an (data_);
return std::make_unique<amqp::TypedPair<std::list<std::unique_ptr<amqp::Value>>>>(
name_,
dump_ (data_, schema_));
}
/******************************************************************************/
std::unique_ptr<amqp::Value>
amqp::
ListReader::dump(
pn_data_t * data_,
const std::unique_ptr<internal::schema::Schema> & schema_
) const {
proton::auto_next an (data_);
return std::make_unique<amqp::TypedSingle<std::list<std::unique_ptr<amqp::Value>>>>(
dump_ (data_, schema_));
}
/******************************************************************************/
const std::string &
amqp::
RestrictedReader::name() const {
return m_name;
}
/******************************************************************************/
const std::string &
amqp::
RestrictedReader::type() const {
return m_type;
}
/******************************************************************************
*
*
*
******************************************************************************/
amqp::internal::schema::Restricted::RestrictedTypes
amqp::
ListReader::restrictedType() const {
return internal::schema::Restricted::RestrictedTypes::List;
}
/******************************************************************************/

View File

@ -0,0 +1,85 @@
#pragma once
/******************************************************************************/
#include "Reader.h"
#include <any>
#include <vector>
#include "amqp/schema/restricted-types/Restricted.h"
/******************************************************************************/
struct pn_data_t;
/******************************************************************************/
namespace amqp {
class RestrictedReader : public Reader {
private :
static const std::string m_name;
const std::string m_type;
public :
RestrictedReader (const std::string & type_)
: m_type (type_)
{ }
~RestrictedReader() = default;
std::any read(pn_data_t *) const override ;
std::string readString(pn_data_t *) const override;
std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &) const override = 0;
const std::string & name() const override;
const std::string & type() const override;
};
}
/******************************************************************************/
namespace amqp {
class ListReader : public RestrictedReader {
private :
// How to read the underlying types
std::weak_ptr<amqp::Reader> m_reader;
std::list<std::unique_ptr<amqp::Value>> dump_(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &) const;
public :
ListReader (
const std::string & type_,
std::weak_ptr<amqp::Reader> reader_
) : RestrictedReader (type_)
, m_reader (reader_)
{ }
~ListReader() final = default;
internal::schema::Restricted::RestrictedTypes restrictedType() const;
std::unique_ptr<Value> dump(
const std::string &,
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &) const override;
std::unique_ptr<Value> dump(
pn_data_t *,
const std::unique_ptr<internal::schema::Schema> &) const override;
};
}
/******************************************************************************/

View File

@ -0,0 +1,161 @@
#include "AMQPDescriptorRegistory.h"
#include "AMQPDescriptors.h"
#include <limits>
#include <climits>
/******************************************************************************/
namespace amqp {
namespace internal {
const uint64_t DESCRIPTOR_TOP_32BITS = 0xc562L << (32 + 16);
}
}
/******************************************************************************/
namespace amqp {
namespace internal {
const int ENVELOPE = 1;
const int SCHEMA = 2;
const int OBJECT = 3;
const int FIELD = 4;
const int COMPOSITE_TYPE = 5;
const int RESTRICTED_TYPE = 6;
const int CHOICE = 7;
const int REFERENCED_OBJECT = 8;
const int TRANSFORM_SCHEMA = 9;
const int TRANSFORM_ELEMENT = 10;
const int TRANSFORM_ELEMENT_KEY = 11;
}
}
/******************************************************************************/
/**
*
*/
namespace amqp {
std::map<uint64_t, std::shared_ptr<internal::AMQPDescriptor>>
AMQPDescriptorRegistory = {
{
1L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::EnvelopeDescriptor> (
internal::EnvelopeDescriptor (
"ENVELOPE",
internal::ENVELOPE))
},
{
2L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::SchemaDescriptor> (
internal::SchemaDescriptor (
"SCHEMA",
internal::SCHEMA))
},
{
3L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::ObjectDescriptor> (
internal::ObjectDescriptor (
"OBJECT_DESCRIPTOR",
internal::OBJECT))
},
{
4L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::FieldDescriptor> (
internal::FieldDescriptor (
"FIELD",
internal::FIELD))
},
{
5L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::CompositeDescriptor> (
internal::CompositeDescriptor (
"COMPOSITE_TYPE",
internal::COMPOSITE_TYPE))
},
{
6L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::RestrictedDescriptor> (
internal::RestrictedDescriptor (
"RESTRICTED_TYPE",
internal::RESTRICTED_TYPE))
},
{
7L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::ChoiceDescriptor> (
internal::ChoiceDescriptor (
"CHOICE",
internal::CHOICE))
},
{
8L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::ReferencedObjectDescriptor> (
internal::ReferencedObjectDescriptor (
"REFERENCED_OBJECT",
internal::REFERENCED_OBJECT))
},
{
9L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::TransformSchemaDescriptor> (
internal::TransformSchemaDescriptor (
"TRANSFORM_SCHEMA",
internal::TRANSFORM_SCHEMA))
},
{
10L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::TransformElementDescriptor> (
internal::TransformElementDescriptor (
"TRANSFORM_ELEMENT",
internal::TRANSFORM_ELEMENT))
},
{
11L | internal::DESCRIPTOR_TOP_32BITS,
std::make_shared<internal::TransformElementKeyDescriptor> (
internal::TransformElementKeyDescriptor (
"TRANSFORM_ELEMENT_KEY",
internal::TRANSFORM_ELEMENT_KEY))
}
};
}
/******************************************************************************/
uint32_t
amqp::stripCorda (uint64_t id) {
return static_cast<uint32_t>(id & (uint64_t)UINT_MAX);
}
/******************************************************************************/
std::string
amqp::describedToString(uint64_t val_) {
switch (val_) {
case (1L | internal::DESCRIPTOR_TOP_32BITS) : return "ENVELOPE";
case (2L | internal::DESCRIPTOR_TOP_32BITS) : return "SCHEMA";
case (3L | internal::DESCRIPTOR_TOP_32BITS) : return "OBJECT_DESCRIPTOR";
case (4L | internal::DESCRIPTOR_TOP_32BITS) : return "FIELD";
case (5L | internal::DESCRIPTOR_TOP_32BITS) : return "COMPOSITE_TYPE";
case (6L | internal::DESCRIPTOR_TOP_32BITS) : return "RESTRICTED_TYPE";
case (7L | internal::DESCRIPTOR_TOP_32BITS) : return "CHOICE";
case (8L | internal::DESCRIPTOR_TOP_32BITS) : return "REFERENCED_OBJECT";
case (9L | internal::DESCRIPTOR_TOP_32BITS) : return "TRANSFORM_SCHEMA";
case (10L | internal::DESCRIPTOR_TOP_32BITS) : return "TRANSFORM_ELEMENT";
case (11L | internal::DESCRIPTOR_TOP_32BITS) : return "TRANSFORM_ELEMENT_KEY";
default : return "UNKNOWN";
};
}
/******************************************************************************/
std::string
amqp::describedToString(uint32_t val_) {
return describedToString(val_ | internal::DESCRIPTOR_TOP_32BITS);
}
/******************************************************************************/

View File

@ -0,0 +1,78 @@
#pragma once
/******************************************************************************/
#include <map>
#include <memory>
/******************************************************************************/
#include "amqp/AMQPDescriptor.h"
/******************************************************************************/
/**
* R3 AMQP assigned enterprise number
*
* see [here](https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers)
*
* Repeated here for brevity:
* 50530 - R3 - Mike Hearn - mike&r3.com
*/
namespace amqp::internal {
extern const uint64_t DESCRIPTOR_TOP_32BITS;
}
/******************************************************************************/
namespace amqp::internal {
extern const int ENVELOPE;
extern const int SCHEMA;
extern const int OBJECT_DESCRIPTOR;
extern const int FIELD;
extern const int COMPOSITE_TYPE;
extern const int RESTRICTED_TYPE;
extern const int CHOICE;
extern const int REFERENCED_OBJECT;
extern const int TRANSFORM_SCHEMA;
extern const int TRANSFORM_ELEMENT;
extern const int TRANSFORM_ELEMENT_KEY;
}
/******************************************************************************/
/**
*
*/
namespace amqp {
extern std::map<uint64_t, std::shared_ptr<internal::AMQPDescriptor>> AMQPDescriptorRegistory;
}
/******************************************************************************
*
* Some basic utlility functions
*
******************************************************************************/
namespace amqp {
/**
* the top 32 bits of a Corda AMQP descriptor is the assigned CORDA identifier.
*
* Utility function to strip that off and return a simple integer that maps
* to our described types.
*/
uint32_t stripCorda (uint64_t id);
std::string describedToString(uint64_t);
std::string describedToString(uint32_t);
}
/******************************************************************************/

View File

@ -0,0 +1,398 @@
#include "AMQPDescriptors.h"
#include "AMQPDescriptorRegistory.h"
#include <string>
#include <iostream>
#include <proton/types.h>
#include <proton/codec.h>
#include "colours.h"
#include "debug.h"
#include "Field.h"
#include "Schema.h"
#include "Envelope.h"
#include "Composite.h"
#include "amqp/schema/restricted-types/Restricted.h"
#include "amqp/schema/OrderedTypeNotations.h"
#include "AMQPDescribed.h"
#include "proton/proton_wrapper.h"
#include "AMQPDescriptorRegistory.h"
/******************************************************************************
*
*
*
******************************************************************************/
namespace {
/**
* Look up a described type by its ID in the AMQPDescriptorRegistory and
* return the coresponding schema type. Specialised below to avoid
* the cast and re-owning of the unigue pointer when we're happy
* with a simple std::unique_ptr<AMQPDescribed>
*/
template<class T>
std::unique_ptr<T>
dispatchDescribed (pn_data_t * data_) {
proton::is_described(data_);
proton::auto_enter p (data_);
proton::is_ulong(data_);
auto id = pn_data_get_ulong(data_);
return std::unique_ptr<T> (
static_cast<T *>(amqp::AMQPDescriptorRegistory[id]->build(data_).release()));
}
}
/******************************************************************************/
void
amqp::internal::
AMQPDescriptor::validateAndNext (pn_data_t * const data_) const {
if (pn_data_type(data_) != PN_ULONG) {
throw std::runtime_error ("Bad type for a descriptor");
}
if ( (m_val == -1)
|| (pn_data_get_ulong(data_) != (static_cast<uint32_t>(m_val) | amqp::internal::DESCRIPTOR_TOP_32BITS)))
{
throw std::runtime_error ("Invalid Type");
}
pn_data_next(data_);
}
/******************************************************************************/
namespace {
const std::string
consumeBlob (pn_data_t * data_) {
proton::is_described (data_);
proton::auto_enter p (data_);
return proton::get_symbol<std::string> (data_);
}
}
/******************************************************************************
*
* amqp::internal::EnvelopeDescriptor
*
******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
EnvelopeDescriptor::build(pn_data_t * data_) const {
DBG ("ENVELOPE" << std::endl); // NOLINT
validateAndNext(data_);
proton::auto_enter p (data_);
/*
* The actual blob... if this was java we would use the type symbols
* in the blob to look up serialisers in the cache... but we don't
* have any so we are actually going to need to use the schema
* which we parse *after* this to be able to read any data!
*/
std::string outerType = consumeBlob(data_);
pn_data_next (data_);
/*
* The scehama
*/
auto schema = dispatchDescribed<schema::Schema> (data_);
pn_data_next(data_);
/*
* The transforms schema
*/
// Skip for now
// dispatchDescribed (data_);
return std::make_unique<schema::Envelope> (schema::Envelope (schema, outerType));
}
/******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
SchemaDescriptor::build(pn_data_t * data_) const {
DBG ("SCHEMA" << std::endl); // NOLINT
validateAndNext(data_);
schema::OrderedTypeNotations<schema::AMQPTypeNotation> schemas;
/*
* The Schema is stored as a list of lists of described objects
*/
{
proton::auto_list_enter ale (data_);
for (int i { 1 } ; pn_data_next(data_) ; ++i) {
DBG (" " << i << "/" << ale.elements() << std::endl); // NOLINT
proton::auto_list_enter ale2 (data_);
while (pn_data_next(data_)) {
schemas.insert (dispatchDescribed<schema::AMQPTypeNotation>(data_));
}
}
}
return std::make_unique<schema::Schema> (std::move (schemas));
}
/******************************************************************************
*
* amqp::internal::ObjectDescriptor
*
******************************************************************************/
/**
*
*/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
ObjectDescriptor::build(pn_data_t * data_) const {
DBG ("DESCRIPTOR" << std::endl); // NOLINT
validateAndNext(data_);
proton::auto_enter p (data_);
auto symbol = proton::get_symbol<std::string> (data_);
return std::make_unique<schema::Descriptor> (symbol);
}
/******************************************************************************
*
* amqp::internal::FieldDescriptor
*
******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
FieldDescriptor::build(pn_data_t * data_) const {
DBG ("FIELD" << std::endl); // NOLINT
validateAndNext(data_);
proton::auto_enter ae (data_);
/* name: String */
auto name = proton::get_string(data_);
pn_data_next(data_);
/* type: String */
auto type = proton::get_string(data_);
pn_data_next(data_);
/* requires: List<String> */
std::list<std::string> requires;
{
proton::auto_list_enter ale (data_);
while (pn_data_next(data_)) {
requires.push_back (proton::get_string(data_));
}
}
pn_data_next(data_);
/* default: String? */
auto def = proton::get_string(data_, true);
pn_data_next(data_);
/* label: String? */
auto label = proton::get_string(data_, true);
pn_data_next(data_);
/* mandatory: Boolean - copes with the Kotlin concept of nullability.
If something is mandatory then it cannot be null */
auto mandatory = proton::get_boolean(data_);
pn_data_next(data_);
/* multiple: Boolean */
auto multiple = proton::get_boolean(data_);
return std::make_unique<schema::Field> (name, type, requires, def, label,
mandatory, multiple);
}
/******************************************************************************
*
* amqp::internal::CompositeDescriptor
*
******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
CompositeDescriptor::build(pn_data_t * data_) const {
DBG ("COMPOSITE" << std::endl); // NOLINT
validateAndNext(data_);
proton::auto_enter p (data_);
/* Class Name - String */
auto name = proton::get_string(data_);
pn_data_next(data_);
/* Label Name - Nullable String */
auto label = proton::get_string(data_, true);
pn_data_next(data_);
/* provides: List<String> */
std::list<std::string> provides;
{
proton::auto_list_enter p2 (data_);
while (pn_data_next(data_)) {
provides.push_back (proton::get_string (data_));
}
}
pn_data_next(data_);
/* descriptor: Descriptor */
auto descriptor = dispatchDescribed<schema::Descriptor>(data_);
pn_data_next(data_);
/* fields: List<Described>*/
std::vector<std::unique_ptr<schema::Field>> fields;
fields.reserve (pn_data_get_list (data_));
{
proton::auto_list_enter p2 (data_);
while (pn_data_next(data_)) {
fields.emplace_back (dispatchDescribed<schema::Field>(data_));
}
}
return std::make_unique<schema::Composite> (
schema::Composite (name, label, provides, descriptor, fields));
}
/******************************************************************************
*
* Restricted types represent lists and maps
*
* NOTE: The Corda serialization scheme doesn't support all container classes
* as it has the requiremnt that iteration order be deterministic for purposes
* of signing over data.
*
* name : String
* label : String?
* provides : List<String>
* source : String
* descriptor : Descriptor
* choices : List<Choice>
*
******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
RestrictedDescriptor::build(pn_data_t * data_) const {
DBG ("RESTRICTED" << std::endl); // NOLINT
validateAndNext(data_);
proton::auto_enter ae (data_);
auto name = proton::readAndNext<std::string>(data_);
auto label = proton::readAndNext<std::string>(data_, true);
std::vector<std::string> provides;
{
proton::auto_list_enter ae2 (data_);
while (pn_data_next(data_)) {
provides.push_back (proton::get_string (data_));
}
}
pn_data_next (data_);
auto source = proton::readAndNext<std::string>(data_);
auto descriptor = dispatchDescribed<schema::Descriptor>(data_);
// SKIP the choices section **FOR NOW**
return schema::Restricted::make (descriptor, name,
label, provides, source);
}
/******************************************************************************
*
* Essentially, an enum.
*
******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
ChoiceDescriptor::build(pn_data_t * data_) const {
validateAndNext(data_);
DBG ("CHOICE " << data_ << std::endl); // NOLINT
return std::unique_ptr<amqp::internal::AMQPDescribed> (nullptr);
}
/******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
ReferencedObjectDescriptor::build(pn_data_t * data_) const {
validateAndNext(data_);
DBG ("REFERENCED OBJECT " << data_ << std::endl); // NOLINT
return std::unique_ptr<amqp::internal::AMQPDescribed> (nullptr);
}
/******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
TransformSchemaDescriptor::build(pn_data_t * data_) const {
validateAndNext(data_);
DBG ("TRANSFORM SCHEMA " << data_ << std::endl); // NOLINT
return std::unique_ptr<amqp::internal::AMQPDescribed> (nullptr);
}
/******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
TransformElementDescriptor::build(pn_data_t * data_) const {
validateAndNext(data_);
DBG ("TRANSFORM ELEMENT " << data_ << std::endl); // NOLINT
return std::unique_ptr<amqp::internal::AMQPDescribed> (nullptr);
}
/******************************************************************************/
std::unique_ptr<amqp::internal::AMQPDescribed>
amqp::internal::
TransformElementKeyDescriptor::build(pn_data_t * data_) const {
validateAndNext(data_);
DBG ("TRANSFORM ELEMENT KEY" << data_ << std::endl); // NOLINT
return std::unique_ptr<amqp::internal::AMQPDescribed> (nullptr);
}
/******************************************************************************/

View File

@ -0,0 +1,228 @@
#pragma once
/******************************************************************************/
#include <set>
#include <map>
#include <string>
#include <memory>
#include <iostream>
#include "amqp/AMQPDescribed.h"
#include "amqp/AMQPDescriptor.h"
#include "amqp/schema/Descriptor.h"
/******************************************************************************/
struct pn_data_t;
/******************************************************************************/
namespace amqp:: internal {
class EnvelopeDescriptor : public AMQPDescriptor {
public :
EnvelopeDescriptor() : AMQPDescriptor() { }
EnvelopeDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~EnvelopeDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class SchemaDescriptor : public AMQPDescriptor {
public :
SchemaDescriptor() : AMQPDescriptor() { }
SchemaDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~SchemaDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class ObjectDescriptor : public AMQPDescriptor {
public :
ObjectDescriptor() : AMQPDescriptor() { }
ObjectDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~ObjectDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class FieldDescriptor : public AMQPDescriptor {
public :
FieldDescriptor() : AMQPDescriptor() { }
FieldDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~FieldDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class CompositeDescriptor : public AMQPDescriptor {
public :
CompositeDescriptor() : AMQPDescriptor() { }
CompositeDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~CompositeDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class RestrictedDescriptor : public AMQPDescriptor {
public :
RestrictedDescriptor() : AMQPDescriptor() { }
RestrictedDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~RestrictedDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class ChoiceDescriptor : public AMQPDescriptor {
public :
ChoiceDescriptor() : AMQPDescriptor() { }
ChoiceDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~ChoiceDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class ReferencedObjectDescriptor : public AMQPDescriptor {
public :
ReferencedObjectDescriptor() : AMQPDescriptor() { }
ReferencedObjectDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~ReferencedObjectDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class TransformSchemaDescriptor : public AMQPDescriptor {
public :
TransformSchemaDescriptor() : AMQPDescriptor() { }
TransformSchemaDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~TransformSchemaDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class TransformElementDescriptor : public AMQPDescriptor {
public :
TransformElementDescriptor() : AMQPDescriptor() { }
TransformElementDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~TransformElementDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/
namespace amqp::internal {
class TransformElementKeyDescriptor : public AMQPDescriptor {
public :
TransformElementKeyDescriptor() : AMQPDescriptor() { }
TransformElementKeyDescriptor(const std::string & symbol_, int val_)
: AMQPDescriptor(symbol_, val_)
{ }
~TransformElementKeyDescriptor() final = default;
std::unique_ptr<AMQPDescribed> build (pn_data_t *) const override;
};
}
/******************************************************************************/

View File

@ -0,0 +1,62 @@
#include <iostream>
#include "AMQPTypeNotation.h"
#include "colours.h"
#include "Composite.h"
#include "amqp/schema/restricted-types/Restricted.h"
/******************************************************************************
*
*
*
******************************************************************************/
namespace amqp::internal::schema {
/**
* Provide nice mechanism by which Composite and Restricted types, both
* derived types of [AMQPTypeNotation], can be printed
*
* @param stream_ where the output should go
* @param clazz_ what we want to print
* @return the stream to allow proper io chaining
*/
std::ostream &
operator << (std::ostream & stream_, const AMQPTypeNotation & clazz_) {
switch (clazz_.type()) {
case AMQPTypeNotation::Type::Composite : {
stream_ << dynamic_cast<const Composite &>(clazz_);
break;
}
case AMQPTypeNotation::Type::Restricted : {
stream_ << dynamic_cast<const Restricted &>(clazz_);
break;
}
}
return stream_;
}
}
/******************************************************************************
*
* amqp::internal::schema::AMQPTypeNotation
*
******************************************************************************/
const std::string &
amqp::internal::schema::
AMQPTypeNotation::descriptor() const {
return m_descriptor->name();
}
/******************************************************************************/
const std::string &
amqp::internal::schema::
AMQPTypeNotation::name() const {
return m_name;
}
/******************************************************************************/

View File

@ -0,0 +1,65 @@
#pragma once
/******************************************************************************/
#include <memory>
#include <types.h>
/******************************************************************************/
#include "Descriptor.h"
#include "OrderedTypeNotations.h"
/******************************************************************************
*
* Forward class declarations
*
******************************************************************************/
namespace amqp::internal::schema {
class Restricted;
class Composite;
}
/******************************************************************************
*
*
*
******************************************************************************/
namespace amqp::internal::schema {
class AMQPTypeNotation : public AMQPDescribed, public OrderedTypeNotation {
public :
friend std::ostream & operator << (std::ostream &, const AMQPTypeNotation &);
enum Type { Composite, Restricted };
private :
std::string m_name;
std::unique_ptr<Descriptor> m_descriptor;
public :
AMQPTypeNotation (
const std::string & name_,
std::unique_ptr<Descriptor> & descriptor_
) : m_name (name_)
, m_descriptor (std::move(descriptor_))
{ }
const std::string & descriptor() const;
const std::string & name() const;
virtual Type type() const = 0;
int dependsOn (const OrderedTypeNotation &) const override = 0;
virtual int dependsOn (const class Restricted &) const = 0;
virtual int dependsOn (const class Composite &) const = 0;
};
}
/******************************************************************************/

View File

@ -0,0 +1,148 @@
#include "Composite.h"
#include "debug.h"
#include "colours.h"
#include "amqp/schema/restricted-types/Restricted.h"
#include <iomanip>
#include <iostream>
/******************************************************************************/
namespace amqp::internal::schema {
std::ostream &
operator << (std::ostream & stream_, const Composite & clazz_) {
stream_
<< "name : " << clazz_.name() << std::endl
<< "label : " << clazz_.m_label << std::endl
<< "descriptor : " << clazz_.descriptor() << std::endl
<< "fields : ";
for (auto const & i : clazz_.m_fields) stream_ << *i << std::setw (13) << " ";
stream_ << std::setw(0);
return stream_;
}
}
/******************************************************************************
*
* amqp::internal::schema::Composite
*
******************************************************************************/
amqp::internal::schema::
Composite::Composite (
const std::string & name_,
std::string label_,
const std::list<std::string> & provides_,
std::unique_ptr<Descriptor> & descriptor_,
std::vector<std::unique_ptr<Field>> & fields_
) : AMQPTypeNotation (name_, descriptor_)
, m_label (std::move (label_))
, m_provides (provides_)
, m_fields (std::move (fields_))
{ }
/******************************************************************************/
const std::vector<std::unique_ptr<amqp::internal::schema::Field>> &
amqp::internal::schema::
Composite::fields() const {
return m_fields;
}
/******************************************************************************/
amqp::internal::schema::AMQPTypeNotation::Type
amqp::internal::schema::
Composite::type() const {
return AMQPTypeNotation::Type::Composite;
}
/******************************************************************************/
/**
* Use a visitor style pattern to work out weather two types, composite or
* restricted, are "less than" one or not. In this case we define being
* "less than" not having a type that the other depends on. This will
* eventually give us a set ordered in such a way we can simply create
* each element in turn
*
* "...This object determines the order of the elements in the container: it is
* a function pointer or a function object that takes two arguments of the same
* type as the container elements, and returns true if the first argument is
* considered to go before the second in the strict weak ordering it defines,
* and false otherwise. ..."
*
* @param rhs
* @return
*/
int
amqp::internal::schema::
Composite::dependsOn (const OrderedTypeNotation & rhs) const {
return dynamic_cast<const AMQPTypeNotation &>(rhs).dependsOn(*this);
}
/******************************************************************************/
int
amqp::internal::schema::
Composite::dependsOn (const amqp::internal::schema::Restricted & lhs_) const {
// does the left hand side depend on us
auto rtn { 0 };
for (const auto i : lhs_) {
DBG (" C/R a) " << i << " == " << name() << std::endl); // NOLINT
if (i == name()) {
rtn = 1;
}
}
// does this depend on the left hand side
for (auto const & field : m_fields) {
DBG (" C/R b) " << field->resolvedType() << " == " << lhs_.name() << std::endl); // NOLINT
if (field->resolvedType() == lhs_.name()) {
rtn = 2;
}
}
return rtn;
}
/*********************************************************o*********************/
int
amqp::internal::schema::
Composite::dependsOn (const amqp::internal::schema::Composite & lhs_) const {
auto rtn { 0 };
// do we depend on the lhs, i.e. is one of our fields it
for (auto const & field : lhs_) {
DBG (" C/C a) " << field->resolvedType() << " == " << name() << std::endl); // NOLINT
if (field->resolvedType() == name()) {
rtn = 1;
}
}
// does the left hand side depend on us. i.e. is one of it's fields
// us
for (const auto & field : m_fields) {
DBG (" C/C b) " << field->resolvedType() << " == " << lhs_.name() << std::endl); // NOLINT
if (field->resolvedType() == lhs_.name()) {
rtn = 2;
}
}
return rtn;
}
/******************************************************************************/

View File

@ -0,0 +1,85 @@
#pragma once
/******************************************************************************/
#include <list>
#include <vector>
#include <iosfwd>
#include <string>
#include <types.h>
#include "Field.h"
#include "Descriptor.h"
#include "AMQPTypeNotation.h"
#include "amqp/AMQPDescribed.h"
/******************************************************************************
*
* Forward class declarations
*
******************************************************************************/
namespace amqp::internal::schema {
class Restricted;
}
/******************************************************************************/
namespace amqp::internal::schema {
/*
* A Corda AMQP Schema Composite type has:
*
* val name: String,
* val label: String?,
* val provides: List<String>,
* val descriptor: Descriptor,
* val fields: List<Field>
*/
class Composite : public AMQPTypeNotation {
public :
friend std::ostream & operator << (std::ostream &, const Composite&);
private :
// could be null in the stream... not sure that information is
// worth preserving beyond an empty string here.
std::string m_label;
// interfaces the class implements... again since we can't
// use Karen to dynamically construct a class
// we don't know about knowing the interfaces (java concept)
// that this class implemented isn't al that useful but we'll
// at least preserve the list
std::list<std::string> m_provides;
/**
* The properties of the Class
*/
std::vector<std::unique_ptr<Field>> m_fields;
public :
Composite (
const std::string & name_,
std::string label_,
const std::list<std::string> & provides_,
std::unique_ptr<Descriptor> & descriptor_,
std::vector<std::unique_ptr<Field>> & fields_);
const std::vector<std::unique_ptr<Field>> & fields() const;
Type type() const override;
int dependsOn (const OrderedTypeNotation &) const override;
int dependsOn (const class Restricted &) const override;
int dependsOn (const Composite &) const override;
decltype(m_fields)::const_iterator begin() const { return m_fields.cbegin();}
decltype(m_fields)::const_iterator end() const { return m_fields.cend(); }
};
}
/******************************************************************************/

View File

@ -0,0 +1,34 @@
#include "Descriptor.h"
/******************************************************************************/
namespace amqp::internal::schema {
std::ostream &
operator << (std::ostream & stream_, const Descriptor & desc_) {
stream_ << desc_.m_name;
return stream_;
}
}
/******************************************************************************
*
* Descriptor Implementation
*
******************************************************************************/
amqp::internal::schema::
Descriptor::Descriptor (std::string name_)
: m_name (std::move (name_))
{ }
/******************************************************************************/
const std::string &
amqp::internal::schema::
Descriptor::name() const {
return m_name;
}
/******************************************************************************/

View File

@ -0,0 +1,32 @@
#pragma once
/******************************************************************************/
#include <iosfwd>
#include <string>
#include "amqp/AMQPDescribed.h"
/******************************************************************************/
namespace amqp::internal::schema {
class Descriptor : public AMQPDescribed {
public :
friend std::ostream & operator << (std::ostream &, const Descriptor&);
private :
std::string m_name;
public :
Descriptor() = default;
explicit Descriptor (std::string);
const std::string & name() const;
};
}
/******************************************************************************/

View File

@ -0,0 +1,50 @@
#include "Envelope.h"
#include <iostream>
/******************************************************************************/
namespace amqp::internal::schema {
std::ostream &
operator << (
std::ostream & stream_,
const amqp::internal::schema::Envelope & e_
) {
stream_ << *(e_.m_schema);
return stream_;
}
}
/******************************************************************************
*
* amqp::internal::schema::Envelope
*
******************************************************************************/
amqp::internal::schema::
Envelope::Envelope (
std::unique_ptr<Schema> & schema_,
std::string descriptor_
) : m_schema (std::move (schema_))
, m_descriptor (std::move (descriptor_))
{ }
/******************************************************************************/
const std::unique_ptr<amqp::internal::schema::Schema> &
amqp::internal::schema::
Envelope::schema() const {
return m_schema;
}
/******************************************************************************/
const std::string &
amqp::internal::schema::
Envelope::descriptor() const {
return m_descriptor;
}
/******************************************************************************/

View File

@ -0,0 +1,38 @@
#pragma once
/******************************************************************************/
#include "amqp/AMQPDescribed.h"
#include "Schema.h"
#include <iosfwd>
/******************************************************************************/
namespace amqp::internal::schema {
class Envelope : public AMQPDescribed {
public :
friend std::ostream & operator << (std::ostream &, const Envelope &);
private :
std::unique_ptr<Schema> m_schema;
std::string m_descriptor;
public :
Envelope() = delete;
Envelope (
std::unique_ptr<Schema> & schema_,
std::string descriptor_);
const std::unique_ptr<Schema> & schema() const;
const std::string & descriptor() const;
};
}
/******************************************************************************/

View File

@ -0,0 +1,109 @@
#include "Field.h"
#include <sstream>
#include <iostream>
/******************************************************************************/
namespace amqp::internal::schema {
std::ostream &
operator << (std::ostream & stream_, const Field & field_) {
std::stringstream ss;
for (auto &i: field_.m_requires) { ss << i; }
stream_ << field_.m_name << " : " << field_.m_type.first << " : [" << ss.str() << "]" << std::endl;
return stream_;
}
}
/******************************************************************************/
amqp::internal::schema::
Field::Field (
const std::string & name_,
const std::string & type_,
const std::list<std::string> & requires_,
const std::string & default_,
const std::string & label_,
bool mandatory_,
bool multiple_
) : m_name (name_)
, m_requires (requires_)
, m_default (default_)
, m_label (label_)
, m_mandatory (mandatory_)
, m_multiple (multiple_)
{
if (typeIsPrimitive(type_)) {
m_type = std::make_pair(type_, FieldType::PrimitiveProperty);
} else if (type_ == "*") {
m_type = std::make_pair(type_, FieldType::RestrictedProperty);
} else {
m_type = std::make_pair(type_, FieldType::CompositeProperty);
}
}
/******************************************************************************/
bool
amqp::internal::schema::
Field::typeIsPrimitive(const std::string & type_) {
return (type_ == "string" ||
type_ == "long" ||
type_ == "boolean" ||
type_ == "int" ||
type_ == "double");
}
/******************************************************************************/
const std::string &
amqp::internal::schema::
Field::name() const {
return m_name;
}
/******************************************************************************/
const std::string &
amqp::internal::schema::
Field::type() const {
return m_type.first;
}
/******************************************************************************/
const std::string &
amqp::internal::schema::
Field::resolvedType() const {
return (type() == "*") ? requires().front() : type();
}
/******************************************************************************/
amqp::internal::schema::FieldType
amqp::internal::schema::
Field::fieldType() const {
return m_type.second;
}
/******************************************************************************/
const std::list<std::string> &
amqp::internal::schema::
Field::requires() const {
return m_requires;
}
/******************************************************************************/
bool
amqp::internal::schema::
Field::primitive() const {
return m_type.second == PrimitiveProperty;
}
/******************************************************************************/

View File

@ -0,0 +1,63 @@
#pragma once
/******************************************************************************/
#include "Descriptor.h"
#include "amqp/AMQPDescribed.h"
#include <list>
#include <string>
#include <iosfwd>
/******************************************************************************/
namespace amqp::internal::schema {
enum FieldType { PrimitiveProperty, CompositeProperty, RestrictedProperty };
/**
*
* A Corda AMQP Scehma Field type has:
* - name : String
* - type : String
* - requires : List<String>
* - default : nullable String
* - label : nullable String
* - mandatory : Boolean
* - multiple : Boolean
*/
class Field : public AMQPDescribed {
public :
friend std::ostream & operator << (std::ostream &, const Field &);
static bool typeIsPrimitive(const std::string &);
private :
std::string m_name;
std::pair<std::string, FieldType> m_type;
std::list<std::string> m_requires;
std::string m_default;
std::string m_label;
bool m_mandatory;
bool m_multiple;
public :
Field (const std::string & name_,
const std::string & type_,
const std::list<std::string> & requires_,
const std::string & default_,
const std::string & label_,
bool mandatory_,
bool multiple_);
const std::string & name() const;
const std::string & type() const;
const std::string & resolvedType() const;
FieldType fieldType() const;
const std::list<std::string> & requires() const;
bool primitive() const;
};
}
/******************************************************************************/

View File

@ -0,0 +1,206 @@
#pragma once
#include <list>
#include <ostream>
#include <iostream>
#include "types.h"
#include "colours.h"
/******************************************************************************
*
* Forward declarations
*
******************************************************************************/
namespace amqp::internal::schema {
template<class T>
class OrderedTypeNotations;
}
template<class T>
std::ostream & operator << (
std::ostream &,
const amqp::internal::schema::OrderedTypeNotations<T> &);
/******************************************************************************
*
* OrderedTypeNotation
*
******************************************************************************/
namespace amqp::internal::schema {
class OrderedTypeNotation {
public :
virtual ~OrderedTypeNotation() = default;
virtual int dependsOn (const OrderedTypeNotation &) const = 0;
};
}
/******************************************************************************/
namespace amqp::internal::schema {
template<class T>
class OrderedTypeNotations {
private:
std::list<std::list<uPtr<T>>> m_schemas;
public :
typedef decltype(m_schemas.begin()) iterator;
private:
void insert (uPtr<T> &&, iterator);
void insertNewList (uPtr<T> &&);
void insertNewList (
uPtr<T> &&,
typename std::list<std::list<uPtr<T>>>::iterator &);
public :
void insert(uPtr<T> && ptr);
friend std::ostream & ::operator << <> (
std::ostream &,
const amqp::internal::schema::OrderedTypeNotations<T> &);
decltype (m_schemas.crbegin()) begin() const {
return m_schemas.crbegin();
}
decltype (m_schemas.crend()) end() const {
return m_schemas.crend();
}
};
}
/******************************************************************************/
template<class T>
std::ostream &
operator << (
std::ostream &stream_,
const amqp::internal::schema::OrderedTypeNotations<T> &otn_
) {
int idx1{0};
for (const auto &i : otn_.m_schemas) {
stream_ << "level " << ++idx1 << std::endl;
for (const auto &j : i) {
stream_ << " * " << j->name() << std::endl;
}
stream_ << std::endl;
}
return stream_;
}
/******************************************************************************/
template<class T>
void
amqp::internal::schema::
OrderedTypeNotations<T>::insertNewList(uPtr<T> && ptr) {
std::list<uPtr<T>> l;
l.emplace_back (std::move (ptr));
m_schemas.emplace_back(std::move (l));
}
/******************************************************************************/
/**
* This could be a bit more space efficient by checking the previous element
* for dependendies again as its possible we are moving multiple elements "up"
* but the extra checks probably don't make it worth it.
*/
template<class T>
void
amqp::internal::schema::
OrderedTypeNotations<T>::insertNewList(
uPtr<T> && ptr,
typename std::list<std::list<uPtr<T>>>::iterator & here_)
{
std::list<uPtr<T>> l;
l.emplace_back (std::move (ptr));
m_schemas.insert(here_, std::move (l));
}
/******************************************************************************/
template<class T>
void
amqp::internal::schema::
OrderedTypeNotations<T>::insert (uPtr<T> && ptr) {
return insert (std::move (ptr), m_schemas.begin());
}
/******************************************************************************/
template<class T>
void
amqp::internal::schema::
OrderedTypeNotations<T>::insert (
uPtr<T> && ptr,
amqp::internal::schema::OrderedTypeNotations<T>::iterator l_
) {
/*
* First we find where this element needs to be added
*/
amqp::internal::schema::OrderedTypeNotations<T>::iterator insertionPoint { l_ };
for (auto i = l_ ; i != m_schemas.end() ; ++i) {
for (const auto & j : *i) {
/*
* A score of 0 means no dependencies at all
* A score of 1 means "j" has a dependency on what's being inserted
* A score of 2 means what's being inserted depends on "j"
*/
auto score = j->dependsOn(*ptr);
if (score == 1) {
insertionPoint = std::next(i);
} else if (score == 2) {
insertionPoint = i;
goto done;
}
}
}
done:
/*
* Now we insert it and work out if anything requires shuffling
*/
if (insertionPoint == m_schemas.end()) {
insertNewList (std::move(ptr));
} else {
const auto & insertedPtr = insertionPoint->emplace_front (std::move(ptr));
for (auto j = std::next (insertionPoint->begin()) ; j != insertionPoint->end() ; ) {
auto toErase = j++;
auto score { insertedPtr->dependsOn (**toErase) };
if (score > 0) {
uPtr<T> tmpPtr{std::move(*toErase)};
insertionPoint->erase (toErase);
switch (score) {
// Needs to go after the element we're adding
case 1: {
insert(std::move(tmpPtr), std::next(insertionPoint));
break;
}
// Needs to go before the element we're adding
case 2: {
insertNewList (std::move(tmpPtr), insertionPoint);
break;
}
}
}
}
}
}
/******************************************************************************/

View File

@ -0,0 +1,75 @@
#include "Schema.h"
#include "debug.h"
#include <memory>
#include <iostream>
/******************************************************************************
*
* Non member related functions
*
******************************************************************************/
namespace amqp::internal::schema {
std::ostream &
operator << (std::ostream & stream_, const Schema & schema_) {
for (auto i { schema_.m_types.begin() } ; i != schema_.m_types.end() ; ++i) {
for (auto & j : *i) {
stream_ << j->name() << " " << j->type() << std::endl;
}
}
return stream_;
}
}
/******************************************************************************
*
* amqp::internal::schema::Schema
*
******************************************************************************/
amqp::internal::schema::
Schema::Schema (
OrderedTypeNotations<AMQPTypeNotation> types_
) : m_types (std::move (types_)) {
for (auto i { m_types.begin() } ; i != m_types.end() ; ++i) {
for (auto & j : *i) {
DBG ("Schema: " << j->descriptor() << " " << j->name() << std::endl); // NOLINT
m_descriptorToType.emplace(j->descriptor(), std::ref (j));
m_typeToDescriptor.emplace(j->name(), std::ref (j));
}
}
}
/******************************************************************************/
const amqp::internal::schema::OrderedTypeNotations<amqp::internal::schema::AMQPTypeNotation> &
amqp::internal::schema::
Schema::types() const {
return m_types;
}
/******************************************************************************/
amqp::internal::schema::Schema::SchemaMap::const_iterator
amqp::internal::schema::
Schema::fromType (const std::string & type_) const {
return m_typeToDescriptor.find(type_);
}
/******************************************************************************/
amqp::internal::schema::Schema::SchemaMap::const_iterator
amqp::internal::schema::
Schema::fromDescriptor (const std::string & descriptor_) const {
return m_descriptorToType.find (descriptor_);
}
/******************************************************************************/

View File

@ -0,0 +1,63 @@
#pragma once
/******************************************************************************/
#include <set>
#include <map>
#include <iosfwd>
#include "types.h"
#include "Composite.h"
#include "Descriptor.h"
#include "OrderedTypeNotations.h"
#include "amqp/AMQPDescribed.h"
/******************************************************************************/
namespace amqp::internal::schema {
typedef std::function <bool(const uPtr<AMQPTypeNotation> &, const uPtr<AMQPTypeNotation> &)> const SetSort;
}
/******************************************************************************/
namespace amqp::internal::schema {
}
/******************************************************************************/
namespace amqp::internal::schema {
/*
*/
class Schema : public AMQPDescribed {
public :
friend std::ostream & operator << (std::ostream &, const Schema &);
typedef std::map<std::string, const std::reference_wrapper<const uPtr<AMQPTypeNotation>>> SchemaMap;
private :
OrderedTypeNotations<AMQPTypeNotation> m_types;
SchemaMap m_descriptorToType;
SchemaMap m_typeToDescriptor;
public :
Schema (OrderedTypeNotations<AMQPTypeNotation> types_);
const OrderedTypeNotations<AMQPTypeNotation> & types() const;
SchemaMap::const_iterator fromType (const std::string &) const;
SchemaMap::const_iterator fromDescriptor (const std::string &) const;
decltype(m_types.begin()) begin() const { return m_types.begin(); }
decltype(m_types.end()) end() const { return m_types.end(); }
};
}
/******************************************************************************/

View File

@ -0,0 +1,116 @@
#include <iostream>
#include "List.h"
#include "debug.h"
#include "colours.h"
#include "schema/Composite.h"
/******************************************************************************/
namespace {
std::pair<std::string, std::string>
listType (const std::string & list_) {
auto pos = list_.find ('<');
return std::make_pair (
std::string { list_.substr (0, pos) },
std::string { list_.substr(pos + 1, list_.size() - pos - 2) }
);
}
}
/******************************************************************************/
amqp::internal::schema::
List::List (
uPtr<Descriptor> & descriptor_,
const std::string & name_,
const std::string & label_,
const std::vector<std::string> & provides_,
const std::string & source_
) : Restricted (
descriptor_,
name_,
label_,
provides_,
amqp::internal::schema::Restricted::RestrictedTypes::List)
, m_listOf { listType(name_).second }
{
}
/******************************************************************************/
std::vector<std::string>::const_iterator
amqp::internal::schema::
List::begin() const {
return m_listOf.begin();
}
/******************************************************************************/
std::vector<std::string>::const_iterator
amqp::internal::schema::
List::end() const {
return m_listOf.end();
}
/******************************************************************************/
const std::string &
amqp::internal::schema::
List::listOf() const {
return m_listOf[0];
}
/******************************************************************************/
int
amqp::internal::schema::
List::dependsOn (const amqp::internal::schema::Restricted & lhs_) const {
auto rtn { 0 };
switch (lhs_.restrictedType()) {
case RestrictedTypes::List : {
const auto & list { dynamic_cast<const List &>(lhs_) };
// does the left hand side depend on us
DBG (" L/L a) " << list.listOf() << " == " << name() << std::endl); // NOLINT
if (list.listOf() == name()) {
rtn = 1;
}
// do we depend on the lhs
DBG (" L/L b) " << listOf() << " == " << list.name() << std::endl); // NOLINT
if (listOf() == list.name()) {
rtn = 2;
}
}
}
return rtn;
}
/******************************************************************************/
int
amqp::internal::schema::
List::dependsOn (const amqp::internal::schema::Composite & lhs_) const {
auto rtn { 0 };
for (const auto & field : lhs_.fields()) {
DBG (" L/C a) " << field->resolvedType() << " == " << name() << std::endl); // NOLINT
if (field->resolvedType() == name()) {
rtn = 1;
}
}
DBG (" L/C b) " << listOf() << " == " << lhs_.name() << std::endl); // NOLINT
if (listOf() == lhs_.name()) {
rtn = 2;
}
return rtn;
}
/*********************************************************o*********************/

View File

@ -0,0 +1,32 @@
#pragma once
#include "Restricted.h"
/******************************************************************************/
namespace amqp::internal::schema {
class List : public Restricted {
private :
std::vector<std::string> m_listOf;
public :
List (
uPtr<Descriptor> & descriptor_,
const std::string &,
const std::string &,
const std::vector<std::string> &,
const std::string &);
std::vector<std::string>::const_iterator begin() const override;
std::vector<std::string>::const_iterator end() const override;
const std::string & listOf() const;
int dependsOn (const Restricted &) const override;
int dependsOn (const class Composite &) const override;
};
}
/******************************************************************************/

View File

@ -0,0 +1,131 @@
#include "Restricted.h"
#include "List.h"
#include <string>
#include <vector>
#include <iostream>
/******************************************************************************/
namespace amqp::internal::schema {
std::ostream &
operator << (
std::ostream & stream_,
const amqp::internal::schema::Restricted & clazz_)
{
stream_
<< "name : " << clazz_.name() << std::endl
<< "label : " << clazz_.m_label << std::endl
<< "descriptor : " << clazz_.descriptor() << std::endl
<< "source : " << clazz_.m_source << std::endl
<< "provides : [" << std::endl;
for (auto & provides : clazz_.m_provides) {
stream_ << " " << provides << std::endl;
}
stream_<< " ]" << std::endl;
return stream_;
}
}
/******************************************************************************/
namespace amqp::internal::schema {
std::ostream &
operator << (
std::ostream & stream_,
const amqp::internal::schema::Restricted::RestrictedTypes & type_)
{
switch (type_) {
case Restricted::RestrictedTypes::List : {
stream_ << "list";
break;
}
case Restricted::RestrictedTypes::Map : {
stream_ << "map";
break;
}
}
return stream_;
}
}
/******************************************************************************
*
* amqp::internal::schema::Restricted
*
******************************************************************************/
/**
* Named constructor
*
* @param descriptor_
* @param name_
* @param label_
* @param provides_
* @param source_
* @return
*/
std::unique_ptr<amqp::internal::schema::Restricted>
amqp::internal::schema::
Restricted::make(
uPtr<Descriptor> & descriptor_,
const std::string & name_,
const std::string & label_,
const std::vector<std::string> & provides_,
const std::string & source_)
{
if (source_ == "list") {
return std::make_unique<amqp::internal::schema::List> (
descriptor_, name_, label_, provides_, source_);
}
}
/******************************************************************************/
amqp::internal::schema::
Restricted::Restricted (
uPtr<Descriptor> & descriptor_,
const std::string & name_,
std::string label_,
const std::vector<std::string> & provides_,
const amqp::internal::schema::Restricted::RestrictedTypes & source_
) : AMQPTypeNotation (name_, descriptor_)
, m_label (std::move (label_))
, m_provides (provides_)
, m_source (source_)
{
}
/******************************************************************************/
amqp::internal::schema::AMQPTypeNotation::Type
amqp::internal::schema::
Restricted::type() const {
return AMQPTypeNotation::Type::Restricted;
}
/******************************************************************************/
amqp::internal::schema::Restricted::RestrictedTypes
amqp::internal::schema::
Restricted::restrictedType() const {
return m_source;
}
/******************************************************************************/
int
amqp::internal::schema::
Restricted::dependsOn (const OrderedTypeNotation & rhs_) const {
return dynamic_cast<const AMQPTypeNotation &>(rhs_).dependsOn(*this);
}
/*********************************************************o*********************/

View File

@ -0,0 +1,100 @@
#pragma once
/******************************************************************************/
#include <list>
#include <vector>
#include <iosfwd>
#include <string>
#include "Field.h"
#include "Descriptor.h"
#include "AMQPTypeNotation.h"
#include "amqp/AMQPDescribed.h"
/******************************************************************************
*
* Forward class declarations
*
******************************************************************************/
namespace amqp::internal::schema {
class Composite;
class OrderedTypeNotation;
}
/******************************************************************************/
namespace amqp::internal::schema {
class Restricted : public AMQPTypeNotation {
public :
friend std::ostream & operator << (std::ostream &, const Restricted&);
enum RestrictedTypes { List, Map };
private :
// could be null in the stream... not sure that information is
// worth preserving beyond an empty string here.
std::string m_label;
/**
* Which Java interfaces the type implemented when serialised within
* the JVM. Not really useful for C++ but we're keepign it for
* the sense of completeness
*/
std::vector<std::string> m_provides;
/**
* Is it a map or list
*/
RestrictedTypes m_source;
protected :
/**
* keep main constructor private to force use of the named constructor
*/
Restricted (
std::unique_ptr<Descriptor> & descriptor_,
const std::string &,
std::string,
const std::vector<std::string> &,
const RestrictedTypes &);
public :
static std::unique_ptr<Restricted> make(
std::unique_ptr<Descriptor> & descriptor_,
const std::string &,
const std::string &,
const std::vector<std::string> &,
const std::string &);
Restricted (Restricted&) = delete;
Type type() const override;
RestrictedTypes restrictedType() const;
/**
* @return an iterator over the types the restricted class represents.
* In the case of a list, the element this is a list of, in the
* case of a map the key and value types etc.
*/
virtual std::vector<std::string>::const_iterator begin() const = 0;
virtual std::vector<std::string>::const_iterator end() const = 0;
int dependsOn (const OrderedTypeNotation &) const override;
int dependsOn (const Restricted &) const override = 0;
int dependsOn (const class Composite &) const override = 0;
};
std::ostream & operator << (std::ostream &, const Restricted::RestrictedTypes &);
}
/******************************************************************************/

View File

@ -0,0 +1 @@
amqp-test

View File

@ -0,0 +1,18 @@
set (EXE "amqp-test")
set (amqp-test-sources
main.cxx
Pair.cxx
Single.cxx
OrderedTypeNotationTest.cxx
)
link_directories (${BLOB-INSPECTOR_BINARY_DIR}/src/amqp)
add_executable (${EXE} ${amqp-test-sources})
target_link_libraries (${EXE} gtest amqp)
if (UNIX)
target_link_libraries (${EXE} pthread qpid-proton proton)
endif (UNIX)

View File

@ -0,0 +1,222 @@
#include <gtest/gtest.h>
#include "OrderedTypeNotations.h"
/******************************************************************************/
namespace {
class OTN : public amqp::internal::schema::OrderedTypeNotation {
private :
std::string m_name;
std::vector<std::string> m_dependsOn;
public :
OTN(std::string name_, std::vector<std::string> dependsOn_)
: m_name (std::move (name_))
, m_dependsOn (std::move (dependsOn_))
{ }
int dependsOn (const OrderedTypeNotation & otn_) const override {
const auto & otn = dynamic_cast<const OTN &>(otn_);
// does the "left hand side" depend on us (in this case
// the lhs is us as we're not inverting
if (std::find (
m_dependsOn.begin(),
m_dependsOn.end(),
otn.name()) != m_dependsOn.end())
{
return 1;
}
// do we depend on the left hand side
if (std::find (
otn.begin(),
otn.end(),
name()) != otn.end())
{
return 2;
}
return 0;
}
const std::string & name() const { return m_name; }
decltype(m_dependsOn.cbegin()) begin() const { return m_dependsOn.cbegin(); }
decltype(m_dependsOn.cend()) end() const { return m_dependsOn.cend(); }
};
inline
std::string
str (const amqp::internal::schema::OrderedTypeNotations<OTN> & list_) {
std::stringstream ss;
ss << list_;
return ss.str();
}
}
/******************************************************************************/
/**
* Makes testing easier if we compress the list into a flat series rather than
* being all fancy with our output
*/
template<>
std::ostream &
operator << (
std::ostream &stream_,
const amqp::internal::schema::OrderedTypeNotations<OTN> &otn_
) {
auto first { true };
for (const auto & i : otn_.m_schemas) {
for (const auto & j : i) {
if (first) {
first = false;
} else {
stream_ << " ";
}
stream_ << j->name();
}
}
return stream_;
}
/******************************************************************************/
TEST (OTNTest, singleInsert) { // NOLINT
amqp::internal::schema::OrderedTypeNotations<OTN> list;
list.insert(std::make_unique<OTN>("A", std::vector<std::string>()));
ASSERT_EQ("A", str (list));
}
/******************************************************************************/
TEST (OTNTest, twoInserts) { // NOLINT
std::cout << std::endl;
amqp::internal::schema::OrderedTypeNotations<OTN> list;
list.insert(std::make_unique<OTN>("A", std::vector<std::string>()));
list.insert(std::make_unique<OTN>("B", std::vector<std::string>()));
ASSERT_EQ("A B", str (list));
}
/******************************************************************************/
TEST (OTNTest, A_depends_on_B) { // NOLINT
std::cout << std::endl;
amqp::internal::schema::OrderedTypeNotations<OTN> list;
std::vector<std::string> aDeps = { "B" };
list.insert(std::make_unique<OTN>("A", aDeps));
list.insert(std::make_unique<OTN>("B", std::vector<std::string>()));
ASSERT_EQ("A B", str (list));
}
/******************************************************************************/
TEST (OTNTest, B_depends_on_A) { // NOLINT
std::cout << std::endl;
amqp::internal::schema::OrderedTypeNotations<OTN> list;
std::vector<std::string> aDeps = { };
std::vector<std::string> bDeps = { "A" };
list.insert(std::make_unique<OTN>("A", aDeps));
list.insert(std::make_unique<OTN>("B", bDeps));
ASSERT_EQ ("B A", str (list));
}
/******************************************************************************/
TEST (OTNTest, three_1) { // NOLINT
std::cout << std::endl;
amqp::internal::schema::OrderedTypeNotations<OTN> list;
std::vector<std::string> aDeps = { };
std::vector<std::string> bDeps = { "A" };
std::vector<std::string> cDeps = { "A" };
list.insert(std::make_unique<OTN>("A", aDeps));
list.insert(std::make_unique<OTN>("B", bDeps));
list.insert(std::make_unique<OTN>("C", cDeps));
ASSERT_EQ ("B C A", str (list));
}
/******************************************************************************/
TEST (OTNTest, three_2) { // NOLINT
std::cout << std::endl;
amqp::internal::schema::OrderedTypeNotations<OTN> list;
std::vector<std::string> aDeps = { "B" };
std::vector<std::string> bDeps = { "C" };
std::vector<std::string> cDeps = { };
list.insert(std::make_unique<OTN>("A", aDeps));
list.insert(std::make_unique<OTN>("B", bDeps));
list.insert(std::make_unique<OTN>("C", cDeps));
EXPECT_EQ ("A B C", str (list));
}
/******************************************************************************/
TEST (OTNTest, three_3) { // NOLINT
std::cout << std::endl;
amqp::internal::schema::OrderedTypeNotations<OTN> list;
std::vector<std::string> aDeps = { "B" };
std::vector<std::string> bDeps = { "C" };
std::vector<std::string> cDeps = { };
list.insert(std::make_unique<OTN>("C", cDeps));
list.insert(std::make_unique<OTN>("A", aDeps));
list.insert(std::make_unique<OTN>("B", bDeps));
EXPECT_EQ ("A B C", str (list));
}
/******************************************************************************/
TEST (OTNTest, three_4) { // NOLINT
std::cout << std::endl;
amqp::internal::schema::OrderedTypeNotations<OTN> list;
std::vector<std::string> aDeps = { "B" };
std::vector<std::string> bDeps = { "C" };
std::vector<std::string> cDeps = { };
list.insert(std::make_unique<OTN>("C", cDeps));
list.insert(std::make_unique<OTN>("B", bDeps));
list.insert(std::make_unique<OTN>("A", aDeps));
EXPECT_EQ ("A B C", str (list));
}
/******************************************************************************/
TEST (OTNTest, three_5) { // NOLINT
std::cout << std::endl;
amqp::internal::schema::OrderedTypeNotations<OTN> list;
std::vector<std::string> aDeps = { "B" };
std::vector<std::string> bDeps = { "C" };
std::vector<std::string> cDeps = { };
list.insert(std::make_unique<OTN>("B", bDeps));
list.insert(std::make_unique<OTN>("C", cDeps));
list.insert(std::make_unique<OTN>("A", aDeps));
EXPECT_EQ ("A B C", str (list));
}
/******************************************************************************/

View File

@ -0,0 +1,56 @@
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include "Reader.h"
/******************************************************************************/
TEST (Pair, string) { // NOLINT
amqp::TypedPair<std::string> str_test ("Left", "Hello");
EXPECT_EQ("Left : Hello", str_test.dump());
}
/******************************************************************************/
TEST (Pair, int) { // NOLINT
amqp::TypedPair<int> int_test ("Left", 101);
EXPECT_EQ("Left : 101", int_test.dump());
}
/******************************************************************************/
TEST (Pair, UP1) { // NOLINT
std::unique_ptr<amqp::TypedPair<double>> test =
std::make_unique<amqp::TypedPair<double>> ("property", 10.0);
EXPECT_EQ("property : 10.000000", test->dump());
}
/******************************************************************************/
TEST (Pair, UP2) { // NOLINT
struct builder {
static std::unique_ptr<amqp::Pair>
build (const std::string & prop_, int val_) {
return std::make_unique<amqp::TypedPair<int>> (prop_, val_);
}
};
std::vector<std::unique_ptr<amqp::Value>> vec;
vec.reserve(2);
vec.emplace_back (builder::build ("first", 1));
vec.emplace_back (builder::build ("second", 2));
std::unique_ptr<amqp::Pair> test =
std::make_unique<amqp::TypedPair<std::vector<std::unique_ptr<amqp::Value>>>> (
"Vector", std::move (vec));
EXPECT_EQ("Vector : { first : 1, second : 2 }", test->dump());
}
/******************************************************************************/

View File

@ -0,0 +1,35 @@
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include "Reader.h"
TEST (Single, string) { // NOLINT
amqp::TypedSingle<std::string> str_test ("Hello");
EXPECT_EQ("Hello", str_test.dump());
}
TEST (Single, list) { // NOLINT
struct builder {
static std::unique_ptr<amqp::Value>
build (int val_) {
return std::make_unique<amqp::TypedSingle<int>> (val_);
}
};
std::list<std::unique_ptr<amqp::Value>> list;
list.emplace_back (builder::build (1));
list.emplace_back (builder::build (2));
list.emplace_back (builder::build (3));
list.emplace_back (builder::build (4));
list.emplace_back (builder::build (5));
std::unique_ptr<amqp::Single> test =
std::make_unique<amqp::TypedSingle<std::list<std::unique_ptr<amqp::Value>>>> (
std::move (list));
EXPECT_EQ("[ 1, 2, 3, 4, 5 ]", test->dump());
}

View File

@ -0,0 +1,7 @@
#include <gtest/gtest.h>
int
main (int argc, char ** argv){
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,6 @@
set (proton_sources
proton_wrapper.cxx
)
ADD_LIBRARY ( proton ${proton_sources} )

View File

@ -0,0 +1,321 @@
#include "proton_wrapper.h"
#include <sstream>
#include <iomanip>
#include <iostream>
#include <proton/types.h>
#include <proton/codec.h>
/******************************************************************************/
std::ostream&
operator << (std::ostream& stream, pn_data_t * data_) {
auto type = pn_data_type(data_);
stream << std::setw (2) << type << " " << pn_type_name (type);
switch (type) {
case PN_ULONG :
{
stream << " " << pn_data_get_ulong (data_);
break;
}
case PN_LIST :
{
stream << " #entries: " << pn_data_get_list (data_);
break;
}
case PN_STRING :
{
auto str = pn_data_get_string (data_);
stream << " " << std::string (str.start, str.size);
break;
}
case PN_INT :
{
stream << " " << pn_data_get_int (data_);
break;
}
case PN_BOOL :
{
stream << " " << (pn_data_get_bool (data_) ? "true" : "false");
break;
}
case PN_SYMBOL :
{
stream << " " << pn_data_get_symbol (data_).size;
stream << std::endl << " -> ";
auto v = pn_data_get_symbol (data_);
for (size_t i (0) ; i < v.size ; ++i) {
stream << *(v.start + i) << " ";
}
break;
}
default : break;
}
return stream;
}
/******************************************************************************/
/**
* pn_data_enter always places the current pointer before the first node. This
* is a simple convienience function to avoid having to move to the first
* element in addition to entering a child.
*/
bool
proton::pn_data_enter(pn_data_t * data_) {
::pn_data_enter(data_);
return pn_data_next(data_);
}
/******************************************************************************/
void
proton::is_described (pn_data_t * data_) {
if (pn_data_type(data_) != PN_DESCRIBED) {
throw std::runtime_error ("Expected a described type");
}
}
/******************************************************************************/
void
proton::is_ulong (pn_data_t * data_) {
auto t = pn_data_type(data_);
if (t != PN_ULONG) {
std::stringstream ss;
ss << "Expected an unsigned long but recieved " << pn_type_name (t);
throw std::runtime_error (ss.str());
}
}
/******************************************************************************/
inline void
proton::is_symbol (pn_data_t * data_) {
if (pn_data_type(data_) != PN_SYMBOL) {
throw std::runtime_error ("Expected an unsigned long");
}
}
/******************************************************************************/
void
proton::is_list (pn_data_t * data_) {
if (pn_data_type(data_) != PN_LIST) {
throw std::runtime_error ("Expected a list");
}
}
/******************************************************************************/
inline void
proton::is_string (pn_data_t * data_, bool allowNull) {
if (pn_data_type(data_) != PN_STRING) {
if (allowNull && pn_data_type(data_) != PN_NULL) {
throw std::runtime_error ("Expected a String");
}
}
}
/******************************************************************************/
std::string
proton::get_string (pn_data_t * data_, bool allowNull) {
if (pn_data_type(data_) == PN_STRING) {
auto str = pn_data_get_string (data_);
return std::string (str.start, str.size);
} else if (allowNull && pn_data_type(data_) == PN_NULL) {
return "";
}
throw std::runtime_error ("Expected a String");
}
/******************************************************************************/
template<>
std::string
proton::get_symbol<std::string> (pn_data_t * data_) {
is_symbol (data_);
auto symbol = pn_data_get_symbol(data_);
return std::string (symbol.start, symbol.size);
}
template<>
pn_bytes_t
proton::get_symbol (pn_data_t * data_) {
is_symbol (data_);
return pn_data_get_symbol(data_);
}
/******************************************************************************/
bool
proton::get_boolean (pn_data_t * data_) {
if (pn_data_type(data_) == PN_BOOL) {
return pn_data_get_bool (data_);
}
throw std::runtime_error ("Expected a boolean");
}
/******************************************************************************
*
* proton::auto_enter
*
******************************************************************************/
proton::
auto_enter::auto_enter (pn_data_t * data_, bool next_)
: m_data (data_)
{
proton::pn_data_enter(m_data);
if (next_) pn_data_next(m_data);
}
/******************************************************************************/
proton::
auto_enter::~auto_enter() {
pn_data_exit(m_data);
}
/******************************************************************************
*
* proton::auto_next
*
******************************************************************************/
proton::
auto_next::auto_next (
pn_data_t * data_
) : m_data (data_) {
}
/******************************************************************************/
proton::
auto_next::~auto_next() {
pn_data_next (m_data);
}
/******************************************************************************
*
* proton::auto_list_enter
*
******************************************************************************/
proton::
auto_list_enter::auto_list_enter (pn_data_t * data_, bool next_)
: m_elements (pn_data_get_list (data_))
, m_data (data_)
{
::pn_data_enter(m_data);
if (next_) {
pn_data_next (m_data);
}
}
/******************************************************************************/
proton::
auto_list_enter::~auto_list_enter() {
pn_data_exit(m_data);
}
/******************************************************************************/
size_t
proton::
auto_list_enter::elements() const {
return m_elements;
}
/******************************************************************************
*
*
*
******************************************************************************/
template<>
int32_t
proton::
readAndNext<int32_t> (
pn_data_t * data_,
bool tolerateDeviance_
) {
int rtn = pn_data_get_int (data_);
pn_data_next(data_);
return rtn;
}
/******************************************************************************/
template<>
std::string
proton::
readAndNext<std::string> (
pn_data_t * data_,
bool tolerateDeviance_
) {
auto_next an (data_);
if (pn_data_type(data_) == PN_STRING) {
auto str = pn_data_get_string(data_);
return std::string(str.start, str.size);
} else if (pn_data_type(data_) == PN_SYMBOL) {
auto symbol = pn_data_get_symbol(data_);
return std::string(symbol.start, symbol.size);
} else if (tolerateDeviance_ && pn_data_type(data_) == PN_NULL) {
return "";
}
throw std::runtime_error ("Expected a String");
}
/******************************************************************************/
template<>
bool
proton::
readAndNext<bool> (
pn_data_t * data_,
bool tolerateDeviance_
) {
bool rtn = pn_data_get_bool (data_);
pn_data_next(data_);
return rtn;
}
/******************************************************************************/
template<>
double
proton::
readAndNext<double> (
pn_data_t * data_,
bool tolerateDeviance_
) {
auto_next an (data_);
return pn_data_get_double (data_);
}
/******************************************************************************/
template<>
long
proton::
readAndNext<long> (
pn_data_t * data_,
bool tolerateDeviance_
) {
long rtn = pn_data_get_long (data_);
pn_data_next(data_);
return rtn;
}
/******************************************************************************/

View File

@ -0,0 +1,90 @@
#pragma once
/******************************************************************************/
#include <iosfwd>
#include <string>
#include <proton/types.h>
#include <proton/codec.h>
/******************************************************************************/
/**
* Friendly ostream operator for a pn_data_t type
*/
std::ostream& operator << (std::ostream& stream, pn_data_t * data_);
/******************************************************************************/
namespace proton {
/**
* Wrap enter so we automatically move to the first child node rather
* than starting on an invalid one
*/
bool pn_data_enter(pn_data_t *);
void is_list (pn_data_t *);
void is_ulong (pn_data_t *);
void is_symbol (pn_data_t *);
void is_string (pn_data_t *, bool allowNull = false);
void is_described (pn_data_t *);
template<typename T>
T get_symbol (pn_data_t * data_) {
return T {};
}
std::string get_symbol (pn_data_t *);
bool get_boolean (pn_data_t *);
std::string get_string (pn_data_t *, bool allowNull = false);
class auto_enter {
private :
pn_data_t * m_data;
public :
auto_enter (pn_data_t *, bool next_ = false);
~auto_enter();
};
class auto_next {
private :
pn_data_t * m_data;
public :
auto_next (pn_data_t *);
auto_next (const auto_next &) = delete;
~auto_next();
};
class auto_list_enter {
private :
size_t m_elements;
pn_data_t * m_data;
public :
auto_list_enter (pn_data_t *, bool next_ = false);
~auto_list_enter();
size_t elements() const;
};
}
/******************************************************************************/
namespace proton {
template<typename T>
T
readAndNext (pn_data_t * data_, bool tolerateDeviance_ = false) {
return T{};
}
}
/******************************************************************************/

View File

@ -0,0 +1,19 @@
#pragma once
/******************************************************************************/
namespace amqp {
namespace internal {
namespace serialiser {
template <typename T>
class Composite : public ProtonReader<T> {
};
}
}
}
/******************************************************************************/

View File

@ -0,0 +1,24 @@
#pragma once
/******************************************************************************/
struct pn_data_t;
/******************************************************************************/
namespace amqp {
namespace internal {
namespace serialiser {
class ProtonReader {
public :
template<typename T>
virtual T read (pn_data_t *) {
}
}
}
}
}
/******************************************************************************/