text:
#include "StructToRLBotFlatbuffer.hpp"

#define NOMINMAX
#include <algorithm>
#define _USE_MATH_DEFINES
#include <math.h>
#include <rlbot_generated.h>

#include <codecvt>
#include <cwchar>
#include <iostream>
#include <boost/locale/encoding_utf.hpp>

/*
Note: The code in this file looks goofy because flatbuffers requires us to construct things 'pre-order',
i.e. you have to fully create all the sub-objects before you start creating a parent object.

https://github.com/google/flatbuffers/blob/master/samples/sample_binary.cpp

*/
namespace StructToRLBotFlatbuffer
{
	flatbuffers::Offset<rlbot::flat::GameInfo> createGameInfo(flatbuffers::FlatBufferBuilder* builder, GameInfo gameInfo)
	{
		return rlbot::flat::CreateGameInfo(
			*builder,
			gameInfo.timeSeconds,
			gameInfo.gameTimeRemaining,
			gameInfo.overTime,
			gameInfo.unlimitedTime,
			gameInfo.roundActive,
			gameInfo.kickoffPause,
			gameInfo.matchEnded,
			gameInfo.worldGravityZ,
			gameInfo.gameSpeed);
	}

	rlbot::flat::Vector3 createVector3(PyStruct::Vector3 structVec)
	{
		return rlbot::flat::Vector3(structVec.x, structVec.y, structVec.z);
	}

	float convertURot(int rotation)
	{
		return rotation * M_PI / 32768;
	}

	rlbot::flat::Rotator createRotator(PyStruct::Rotator structRot)
	{
		return rlbot::flat::Rotator(
			convertURot(structRot.pitch),
			convertURot(structRot.yaw),
			convertURot(structRot.roll));
	}

	flatbuffers::Offset<rlbot::flat::BoxShape> createBoxShape(flatbuffers::FlatBufferBuilder & builder, BoxShape boxshape)
	{
		return rlbot::flat::CreateBoxShape(builder, boxshape.length, boxshape.width, boxshape.height);
	}

	flatbuffers::Offset<rlbot::flat::SphereShape> createSphereShape(flatbuffers::FlatBufferBuilder & builder, SphereShape sphereshape)
	{
		return rlbot::flat::CreateSphereShape(builder, sphereshape.diameter);
	}

	flatbuffers::Offset<rlbot::flat::CylinderShape> createCylinderShape(flatbuffers::FlatBufferBuilder & builder, CylinderShape cylindershape)
	{
		return rlbot::flat::CreateCylinderShape(builder, cylindershape.diameter, cylindershape.height);
	}

	flatbuffers::Offset<rlbot::flat::BoostPad> createBoostPad(flatbuffers::FlatBufferBuilder* builder, BoostPad boostPad)
	{
		auto location = createVector3(boostPad.location);
		return rlbot::flat::CreateBoostPad(*builder, &location, boostPad.fullBoost);
	}

	flatbuffers::Offset<rlbot::flat::BoostPadState> createBoostPadState(flatbuffers::FlatBufferBuilder* builder, BoostInfo gameBoost)
	{
		return rlbot::flat::CreateBoostPadState(*builder, gameBoost.active, gameBoost.timer);
	}

	flatbuffers::Offset<rlbot::flat::DropshotTile> createDropshotTile(flatbuffers::FlatBufferBuilder* builder, TileInfo dropshotTile)
	{
		rlbot::flat::TileState state;
		if (dropshotTile.tileState == 3)
			state = rlbot::flat::TileState::TileState_Open;
		else if (dropshotTile.tileState == 2)
			state = rlbot::flat::TileState::TileState_Damaged;

		state = rlbot::flat::TileState::TileState_Filled;

		return rlbot::flat::CreateDropshotTile(*builder, state);
	}

	bool FillFieldInfo(flatbuffers::FlatBufferBuilder* builder, FieldInfo fieldInfo)
	{
		std::vector<flatbuffers::Offset<rlbot::flat::BoostPad>> boostPads;

		for (int i = 0; i < fieldInfo.numBoosts; i++)
		{
			boostPads.push_back(createBoostPad(builder, fieldInfo.boostPads[i]));
		}

		std::vector<flatbuffers::Offset<rlbot::flat::GoalInfo>> goals;

		for (int i = 0; i < fieldInfo.numGoals; i++)
		{
			auto location = createVector3(fieldInfo.goals[i].location);
			auto direction = createVector3(fieldInfo.goals[i].direction);
			goals.push_back(rlbot::flat::CreateGoalInfo(*builder,
														fieldInfo.goals[i].teamNum,
														&location,
														&direction,
														fieldInfo.goals[i].width,
														fieldInfo.goals[i].height));
		}

		auto fieldInfoBuffer = rlbot::flat::CreateFieldInfo(*builder, builder->CreateVector(boostPads), builder->CreateVector(goals));
		builder->Finish(fieldInfoBuffer);

		return true;
	}

	flatbuffers::Offset<rlbot::flat::Physics> createPhysics(flatbuffers::FlatBufferBuilder* builder, Physics physics)
	{
		auto location = createVector3(physics.location);
		auto rotation = createRotator(physics.rotation);
		auto velocity = createVector3(physics.velocity);
		auto angular = createVector3(physics.angularVelocity);

		return rlbot::flat::CreatePhysics(*builder, &location, &rotation, &velocity, &angular);
	}

	std::string convertString(wchar_t* w)
	{
		std::wstring ws(w);
		return boost::locale::conv::utf_to_utf<char>(ws.c_str(), ws.c_str() + ws.size());
	}

	flatbuffers::Offset<rlbot::flat::PlayerInfo> createPlayerInfo(flatbuffers::FlatBufferBuilder* builder, PlayerInfo playerInfo)
	{
		rlbot::flat::ScoreInfoBuilder sib(*builder);

		sib.add_score(playerInfo.score.score);
		sib.add_goals(playerInfo.score.goals);
		sib.add_ownGoals(playerInfo.score.ownGoals);
		sib.add_assists(playerInfo.score.assists);
		sib.add_saves(playerInfo.score.saves);
		sib.add_shots(playerInfo.score.shots);
		sib.add_demolitions(playerInfo.score.demolitions);

		auto scoreInfo = sib.Finish();

		std::string name = convertString(playerInfo.name);
		auto flatName = builder->CreateString(name); // Must do this before PlayerInfoBuilder is started.
		auto physics = createPhysics(builder, playerInfo.physics);
		auto hitbox = createBoxShape(*builder, playerInfo.hitbox);
		auto hitboxOffset = createVector3(playerInfo.hitboxOffset);

		rlbot::flat::PlayerInfoBuilder pib(*builder);

		pib.add_scoreInfo(scoreInfo);
		pib.add_isBot(playerInfo.bot);
		pib.add_name(flatName);
		pib.add_isDemolished(playerInfo.demolished);
		pib.add_physics(physics);
		pib.add_hasWheelContact(playerInfo.onGround);
		pib.add_isSupersonic(playerInfo.superSonic);
		pib.add_jumped(playerInfo.jumped);
		pib.add_doubleJumped(playerInfo.doubleJumped);
		pib.add_team(playerInfo.team);
		pib.add_boost(playerInfo.boost);
		pib.add_hitbox(hitbox);
		pib.add_hitboxOffset(&hitboxOffset);
		pib.add_spawnId(playerInfo.spawnId);

		return pib.Finish();
	}

	flatbuffers::Offset<void> createCollisionShape(flatbuffers::FlatBufferBuilder & builder, CollisionShape collisionShape)
	{
		switch (collisionShape.type)
		{
		case BoxType:
			return createBoxShape(builder, collisionShape.box).Union();

		case SphereType:
			return createSphereShape(builder, collisionShape.sphere).Union();

		case CylinderType:
			return createCylinderShape(builder, collisionShape.cylinder).Union();
		}
	}

	rlbot::flat::CollisionShape createCollisionShapeType(CollisionShapeType collisionShapeType)
	{
		switch (collisionShapeType)
		{
		case BoxType:
			return rlbot::flat::CollisionShape_BoxShape;

		case SphereType:
			return rlbot::flat::CollisionShape_SphereShape;

		case CylinderType:
			return rlbot::flat::CollisionShape_CylinderShape;
		}
	}

	flatbuffers::Offset<rlbot::flat::BallInfo> createBallInfo(flatbuffers::FlatBufferBuilder* builder, BallInfo ballInfo)
	{
		bool hasTouch = ballInfo.latestTouch.timeSeconds > 0;
		flatbuffers::Offset<rlbot::flat::Touch> touchOffset;

		if (hasTouch)
		{
			std::string name = convertString(ballInfo.latestTouch.playerName);
			auto flatName = builder->CreateString(name); // Must do this before TouchBuilder is started

			auto touch = rlbot::flat::TouchBuilder(*builder);
			touch.add_playerName(flatName);
			touch.add_gameSeconds(ballInfo.latestTouch.timeSeconds);

			auto location = createVector3(ballInfo.latestTouch.hitLocation);
			touch.add_location(&location);

			auto normal = createVector3(ballInfo.latestTouch.hitNormal);
			touch.add_normal(&normal);

			touch.add_team(ballInfo.latestTouch.team);

			touch.add_playerIndex(ballInfo.latestTouch.playerIndex);

			touchOffset = touch.Finish();
		}

		auto physics = createPhysics(builder, ballInfo.physics);

		flatbuffers::Offset<rlbot::flat::DropShotBallInfo> dropShotBallOffset;

		rlbot::flat::DropShotBallInfoBuilder dbib(*builder);
		dbib.add_absorbedForce(ballInfo.dropShotInfo.absorbedForce);
		dbib.add_damageIndex(ballInfo.dropShotInfo.damageIndex);
		dbib.add_forceAccumRecent(ballInfo.dropShotInfo.forceAccumRecent);

		dropShotBallOffset = dbib.Finish();

		auto collisionshape = createCollisionShape(*builder, ballInfo.collisionShape);

		rlbot::flat::BallInfoBuilder bib(*builder);
		bib.add_physics(physics);

		if (hasTouch)
			bib.add_latestTouch(touchOffset);

		bib.add_dropShotInfo(dropShotBallOffset);
		bib.add_shape(collisionshape);
		bib.add_shape_type(createCollisionShapeType(ballInfo.collisionShape.type));

		return bib.Finish();
	}

	bool FillGameDataPacket(flatbuffers::FlatBufferBuilder* builder, LiveDataPacket liveDataPacket)
	{

		std::vector<flatbuffers::Offset<rlbot::flat::PlayerInfo>> players;
		flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<rlbot::flat::PlayerInfo>>> playersOffset;

		for (int i = 0; i < liveDataPacket.numCars; i++)
		{
			players.push_back(createPlayerInfo(builder, liveDataPacket.gameCars[i]));
		}

		playersOffset = builder->CreateVector(players);

		auto gameInfo = createGameInfo(builder, liveDataPacket.gameInfo);

		std::vector<flatbuffers::Offset<rlbot::flat::BoostPadState>> boosts;
		flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<rlbot::flat::BoostPadState>>> boostsOffset;

		for (int i = 0; i < liveDataPacket.numBoosts; i++)
		{
			boosts.push_back(createBoostPadState(builder, liveDataPacket.gameBoosts[i]));
		}

		boostsOffset = builder->CreateVector(boosts);

		std::vector<flatbuffers::Offset<rlbot::flat::DropshotTile>> tiles;
		flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<rlbot::flat::DropshotTile>>> tilesOffset;

		for (int i = 0; i < liveDataPacket.numTiles; i++)
		{
			tiles.push_back(createDropshotTile(builder, liveDataPacket.gameTiles[i]));
		}

		tilesOffset = builder->CreateVector(tiles);

		flatbuffers::Offset<rlbot::flat::BallInfo> ballOffset;

		ballOffset = createBallInfo(builder, liveDataPacket.gameBall);

		std::vector<flatbuffers::Offset<rlbot::flat::TeamInfo>> teamInfos;
		teamInfos.push_back(rlbot::flat::CreateTeamInfo(*builder, liveDataPacket.teams[0].teamIndex, liveDataPacket.teams[0].score));
		teamInfos.push_back(rlbot::flat::CreateTeamInfo(*builder, liveDataPacket.teams[1].teamIndex, liveDataPacket.teams[1].score));
		auto teamInfosOffset = builder->CreateVector(teamInfos);

		rlbot::flat::GameTickPacketBuilder gtb(*builder);
		gtb.add_gameInfo(gameInfo);

		gtb.add_players(playersOffset);
		gtb.add_boostPadStates(boostsOffset);
		gtb.add_tileInformation(tilesOffset);
		gtb.add_ball(ballOffset);
		gtb.add_teams(teamInfosOffset);

		builder->Finish(gtb.Finish());

		return true;
	}

	flatbuffers::Offset<rlbot::flat::Color> buildColor(flatbuffers::FlatBufferBuilder* builder, Color structColor)
	{
		return rlbot::flat::CreateColor(*builder, structColor.a, structColor.r, structColor.g, structColor.b);
	}

	flatbuffers::Offset<rlbot::flat::PlayerLoadout> buildPlayerLoadout(flatbuffers::FlatBufferBuilder* builder, PlayerConfiguration structPlayerConfig)
	{
		rlbot::flat::LoadoutPaintBuilder paintBuilder(*builder);
		paintBuilder.add_carPaintId(structPlayerConfig.carPaintID);
		paintBuilder.add_decalPaintId(structPlayerConfig.decalPaintID);
		paintBuilder.add_wheelsPaintId(structPlayerConfig.wheelsPaintID);
		paintBuilder.add_boostPaintId(structPlayerConfig.boostPaintID);
		paintBuilder.add_antennaPaintId(structPlayerConfig.antennaPaintID);
		paintBuilder.add_hatPaintId(structPlayerConfig.hatPaintID);
		paintBuilder.add_trailsPaintId(structPlayerConfig.trailsPaintID);
		paintBuilder.add_goalExplosionPaintId(structPlayerConfig.goalExplosionPaintID);
		auto paintOffset = paintBuilder.Finish();

		auto primaryColorOffset = buildColor(builder, structPlayerConfig.primaryColorLookup);
		auto secondaryColorOffset = buildColor(builder, structPlayerConfig.secondaryColorLookup);

		rlbot::flat::PlayerLoadoutBuilder loadoutBuilder(*builder);
		loadoutBuilder.add_teamColorId(structPlayerConfig.teamColorID);
		loadoutBuilder.add_customColorId(structPlayerConfig.customColorID);
		loadoutBuilder.add_carId(structPlayerConfig.carID);
		loadoutBuilder.add_decalId(structPlayerConfig.decalID);
		loadoutBuilder.add_wheelsId(structPlayerConfig.wheelsID);
		loadoutBuilder.add_boostId(structPlayerConfig.boostID);
		loadoutBuilder.add_antennaId(structPlayerConfig.antennaID);
		loadoutBuilder.add_hatId(structPlayerConfig.hatID);
		loadoutBuilder.add_paintFinishId(structPlayerConfig.paintFinishID);
		loadoutBuilder.add_customFinishId(structPlayerConfig.customFinishID);
		loadoutBuilder.add_engineAudioId(structPlayerConfig.engineAudioID);
		loadoutBuilder.add_trailsId(structPlayerConfig.trailsID);
		loadoutBuilder.add_goalExplosionId(structPlayerConfig.goalExplosionID);
		loadoutBuilder.add_loadoutPaint(paintOffset);
		if (structPlayerConfig.useRgbLookup)
		{
			loadoutBuilder.add_primaryColorLookup(primaryColorOffset);
			loadoutBuilder.add_secondaryColorLookup(secondaryColorOffset);
		}

		return loadoutBuilder.Finish();
	}

	flatbuffers::Offset<rlbot::flat::PlayerConfiguration> buildPlayerConfiguration(flatbuffers::FlatBufferBuilder* builder, PlayerConfiguration structPlayerConfig)
	{
		auto name = builder->CreateString(convertString(structPlayerConfig.name));
		auto loadout = buildPlayerLoadout(builder, structPlayerConfig);

		flatbuffers::Offset<void> player;
		rlbot::flat::PlayerClass variety;
		if (structPlayerConfig.bot)
		{
			if (structPlayerConfig.rlbotControlled)
			{
				variety = rlbot::flat::PlayerClass_RLBotPlayer;
				player = rlbot::flat::CreateRLBotPlayer(*builder).Union();
			}
			else
			{
				variety = rlbot::flat::PlayerClass_PsyonixBotPlayer;
				player = rlbot::flat::CreatePsyonixBotPlayer(*builder, structPlayerConfig.botSkill).Union();
			}
		}
		else
		{
			if (structPlayerConfig.rlbotControlled)
			{
				variety = rlbot::flat::PlayerClass_PartyMemberBotPlayer;
				player = rlbot::flat::CreatePartyMemberBotPlayer(*builder).Union();
			}
			else
			{
				variety = rlbot::flat::PlayerClass_HumanPlayer;
				player = rlbot::flat::CreateHumanPlayer(*builder).Union();
			}
		}

		rlbot::flat::PlayerConfigurationBuilder configBuilder(*builder);
		configBuilder.add_name(name);
		configBuilder.add_loadout(loadout);
		configBuilder.add_team(structPlayerConfig.team);
		configBuilder.add_variety(player);
		configBuilder.add_variety_type(variety);
		configBuilder.add_spawnId(structPlayerConfig.spawnId);

		return configBuilder.Finish();
	}

	flatbuffers::Offset<rlbot::flat::MutatorSettings> buildMutatorSettings(flatbuffers::FlatBufferBuilder* builder, MutatorSettings structMutators)
	{
		rlbot::flat::MutatorSettingsBuilder mutatorBuilder(*builder);
		// We rely on the enums in the flatbuffer being in the exact same order as the ones in the struct, and therefore being numbered the same.
		mutatorBuilder.add_matchLength(static_cast<rlbot::flat::MatchLength>(structMutators.matchLength));
		mutatorBuilder.add_maxScore(static_cast<rlbot::flat::MaxScore>(structMutators.maxScore));
		mutatorBuilder.add_overtimeOption(static_cast<rlbot::flat::OvertimeOption>(structMutators.overtimeOptions));
		mutatorBuilder.add_seriesLengthOption(static_cast<rlbot::flat::SeriesLengthOption>(structMutators.seriesLengthOptions));
		mutatorBuilder.add_gameSpeedOption(static_cast<rlbot::flat::GameSpeedOption>(structMutators.gameSpeedOptions));
		mutatorBuilder.add_ballMaxSpeedOption(static_cast<rlbot::flat::BallMaxSpeedOption>(structMutators.ballMaxSpeedOptions));
		mutatorBuilder.add_ballTypeOption(static_cast<rlbot::flat::BallTypeOption>(structMutators.ballTypeOptions));
		mutatorBuilder.add_ballWeightOption(static_cast<rlbot::flat::BallWeightOption>(structMutators.ballWeightOptions));
		mutatorBuilder.add_ballSizeOption(static_cast<rlbot::flat::BallSizeOption>(structMutators.ballSizeOptions));
		mutatorBuilder.add_ballBouncinessOption(static_cast<rlbot::flat::BallBouncinessOption>(structMutators.ballBouncinessOptions));
		mutatorBuilder.add_boostOption(static_cast<rlbot::flat::BoostOption>(structMutators.boostOptions));
		mutatorBuilder.add_rumbleOption(static_cast<rlbot::flat::RumbleOption>(structMutators.rumbleOptions));
		mutatorBuilder.add_boostStrengthOption(static_cast<rlbot::flat::BoostStrengthOption>(structMutators.boostStrengthOptions));
		mutatorBuilder.add_gravityOption(static_cast<rlbot::flat::GravityOption>(structMutators.gravityOptions));
		mutatorBuilder.add_demolishOption(static_cast<rlbot::flat::DemolishOption>(structMutators.demolishOptions));
		mutatorBuilder.add_respawnTimeOption(static_cast<rlbot::flat::RespawnTimeOption>(structMutators.respawnTimeOptions));
		return mutatorBuilder.Finish();
	}

	bool BuildStartMatchMessage(flatbuffers::FlatBufferBuilder* builder, MatchSettings matchSettings)
	{
		std::vector<flatbuffers::Offset<rlbot::flat::PlayerConfiguration>> playerConfigOffsets;

		for (int i = 0; i < matchSettings.numPlayers; i++)
		{
			playerConfigOffsets.push_back(buildPlayerConfiguration(builder, matchSettings.playerConfiguration[i]));
		}

		auto matchSettingsFlat = rlbot::flat::CreateMatchSettings(
			*builder,
			builder->CreateVector(playerConfigOffsets),
			static_cast<rlbot::flat::GameMode>(matchSettings.gameMode),
			static_cast<rlbot::flat::GameMap>(matchSettings.gameMap),
			matchSettings.skipReplays,
			matchSettings.instantStart,
			buildMutatorSettings(builder, matchSettings.mutatorSettings),
			static_cast<rlbot::flat::ExistingMatchBehavior>(matchSettings.existingMatchBehavior),
			matchSettings.enableLockstep,
			matchSettings.enableRendering,
			matchSettings.enableStateSetting,
			matchSettings.autoSaveReplay);

		builder->Finish(matchSettingsFlat);

		return true;
	}

	rlbot::flat::Quaternion createQuaternion(Quaternion structQuaternion)
	{
		return rlbot::flat::Quaternion(structQuaternion.x, structQuaternion.y, structQuaternion.z, structQuaternion.w);
	}

	flatbuffers::Offset<rlbot::flat::RigidBodyState> createRigidBodyState(flatbuffers::FlatBufferBuilder* builder, RigidBodyState state)
	{
		auto location = createVector3(state.location);
		auto rotation = createQuaternion(state.rotation);
		auto velocity = createVector3(state.velocity);
		auto angular = createVector3(state.angularVelocity);

		return rlbot::flat::CreateRigidBodyState(*builder, state.frame, &location, &rotation, &velocity, &angular);
	}

	flatbuffers::Offset<rlbot::flat::ControllerState> createControllerState(flatbuffers::FlatBufferBuilder* builder, PlayerInput structPlayerInput)
	{
		return rlbot::flat::CreateControllerState(*builder,
			structPlayerInput.throttle,
			structPlayerInput.steer,
			structPlayerInput.pitch,
			structPlayerInput.yaw,
			structPlayerInput.roll,
			structPlayerInput.jump,
			structPlayerInput.boost,
			structPlayerInput.handbrake,
			structPlayerInput.useItem);
	}

	flatbuffers::Offset<rlbot::flat::PlayerRigidBodyState> createPlayerRigid(flatbuffers::FlatBufferBuilder* builder, PlayerRigidBodyState stateStruct)
	{
		auto state = createRigidBodyState(builder, stateStruct.state);
		auto input = createControllerState(builder, stateStruct.input);

		return rlbot::flat::CreatePlayerRigidBodyState(*builder, state, input);

	}

	flatbuffers::Offset<rlbot::flat::BallRigidBodyState> createBallRigidBody(flatbuffers::FlatBufferBuilder* builder, BallRigidBodyState structBallState)
	{
		return rlbot::flat::CreateBallRigidBodyState(*builder, createRigidBodyState(builder, structBallState.state));
	}

	bool FillRigidBody(flatbuffers::FlatBufferBuilder* builder, RigidBodyTick rigidBodyTick)
	{
		std::vector<flatbuffers::Offset<rlbot::flat::PlayerRigidBodyState>> players;
		flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<rlbot::flat::PlayerRigidBodyState>>> playersOffset;

		for (int i = 0; i < rigidBodyTick.numPlayers; i++)
		{
			players.push_back(createPlayerRigid(builder, rigidBodyTick.players[i]));
		}

		playersOffset = builder->CreateVector(players);

		flatbuffers::Offset<rlbot::flat::BallRigidBodyState> ballOffset;

		ballOffset = createBallRigidBody(builder, rigidBodyTick.ball);

		auto tickOffset = rlbot::flat::CreateRigidBodyTick(*builder, ballOffset, playersOffset);

		builder->Finish(tickOffset);

		return true;
	}
}


text:
/*
 * Copyright (c) 2014 Florian Behrens
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "encoder.h"
#include <decof/conversion.h>
#include <decof/exceptions.h>
#include <decof/types.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <iomanip>
#include <map>
#include <ostream>
#include <variant>

namespace {

/// Base64 string encoder function.
std::string base64_encode(const std::string& bin)
{
    // The following is based on code from
    // http://stackoverflow.com/questions/10521581/base64-encode-using-boost-throw-exception/10973348#10973348
    using namespace boost::archive::iterators;

    typedef base64_from_binary<transform_width<std::string::const_iterator, 6, 8>> it_base64_t;

    unsigned int writePaddChars = (3 - bin.length() % 3) % 3;
    std::string  base64(it_base64_t(bin.begin()), it_base64_t(bin.end()));
    base64.append(writePaddChars, '=');

    return base64;
}

} // Anonymous namespace

namespace decof {

namespace cli {

encoder::encoder(std::ostream& out) : m_out(out)
{
}

void encoder::operator()(const scalar_t& arg) const
{
    std::visit(*this, arg);
}

void encoder::operator()(const sequence_t& arg) const
{
    m_out << '[';

    auto it = std::cbegin(arg);
    for (; it != std::cend(arg); ++it) {
        if (it != std::cbegin(arg))
            m_out.put(',');
        std::visit(*this, *it);
    }

    m_out << ']';
}

void encoder::operator()(const tuple_t& arg) const
{
    m_out << '{';

    auto it = std::cbegin(arg);
    for (; it != std::cend(arg); ++it) {
        if (it != std::cbegin(arg))
            m_out.put(',');
        std::visit(*this, *it);
    }

    m_out << '}';
}

void encoder::operator()(const boolean_t& arg) const
{
    m_out << (arg ? "#t" : "#f");
}

void encoder::operator()(const integer_t& arg) const
{
    m_out << arg;
}

void encoder::operator()(const real_t& arg) const
{
    // Use minimum value without loss of precision in conversion between binary
    // and decimal number representation and vice versa.
    m_out << std::setprecision(17) << arg;
}

void encoder::operator()(const string_t& arg) const
{
    static const std::map<char, char> escape_characters = {{'\a', 'a'},
                                                           {'\b', 'b'},
                                                           {'\f', 'f'},
                                                           {'\n', 'n'},
                                                           {'\r', 'r'},
                                                           {'\t', 't'},
                                                           {'\v', 'v'},
                                                           {'\\', '\\'},
                                                           {'\'', '\''},
                                                           {'\"', '"'},
                                                           {'\?', '?'}};

    m_out << '"';

    for (const unsigned char ch : arg) {
        auto it = escape_characters.cbegin();

        if (ch >= 0x20 && ch <= 0x7F && ch != '\\' && ch != '\'' && ch != '\"' && ch != '\?') {
            m_out << ch;
        } else if ((it = escape_characters.find(ch)) != escape_characters.cend()) {
            m_out << "\\" << it->second;
        } else {
            m_out << "\\x" << std::setw(2) << std::setfill('0') << std::hex << unsigned(ch);
        }
    }

    m_out << '"';
}

void encoder::operator()(const binary_t& arg) const
{
    m_out << '&' << base64_encode(arg);
}

} // namespace cli

} // namespace decof

ext: cpp


text:
#include "mainwindow2.h"
#include "ui_MainWindow1.h"

#include "filtersDialog.h"
#include "ui_filtersDialog.h"

#include "Boyer_Moore.h"

#include <QDirIterator>
#include <QThread>
#include <QCheckBox>

#include <QMessageBox>
#include <QtConcurrent/QtConcurrent>
#include <QtConcurrent/QtConcurrentRun>
#include <QtConcurrent/QtConcurrentMap>
#include "my_functions.h"
#include <functional>
#include <QCommonStyle>
#include <QDesktopWidget>
#include <QGuiApplication>
#include <QScreen>
//#include <boost/tokenizer.hpp>

subStringFinder::subStringFinder(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow1),
        filtersWindow(new filtersDialog(this)),
        fsWatcher(new QFileSystemWatcher(this)) {

    ui->setupUi(this);
    filtersWindow->setWindowModality(Qt::WindowModality::WindowModal);
    setWindowTitle("substring finder");
    ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch);
    ui->treeWidget->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
    ui->progressBar->setValue(0);
    ui->indexBar->setValue(0);
    ui->treeWidget->setUniformRowHeights(true);
    ui->treeWidget->setSelectionMode(QAbstractItemView::MultiSelection);

    setGeometry(
        QStyle::alignedRect(
            Qt::LeftToRight,
            Qt::AlignCenter,
            this->size(),
            qApp->desktop()->availableGeometry()
        )
    );

    ui->fileList->setSelectionMode(QAbstractItemView::MultiSelection);
    QCommonStyle style;
    ui->stopButton->setIcon(style.standardIcon(QCommonStyle::SP_MediaStop));
    ui->continueButton->setIcon(style.standardIcon(QCommonStyle::SP_MediaPlay));
    ui->pauseButton->setIcon(style.standardIcon(QCommonStyle::SP_MediaPause));
    ui->action_open_dir->setIcon(style.standardIcon(QCommonStyle::SP_DialogOpenButton));
    ui->action_open_file->setIcon(style.standardIcon(QCommonStyle::SP_FileIcon));
    connect(ui->action_open_file, &QAction::triggered, this, &subStringFinder::add_path);
    connect(ui->searchButton, &QPushButton::clicked, this, &subStringFinder::search);
    connect(ui->action_open_dir, &QAction::triggered, this, &subStringFinder::add_dir);
    connect(ui->expandButton, &QPushButton::clicked, this, &subStringFinder::expand);
    connect(ui->collapseButton, &QPushButton::clicked, this, &subStringFinder::collapse);
    connect(ui->removeButton, &QPushButton::clicked, this, &subStringFinder::remove_selected);
    connect(ui->pauseButton, &QPushButton::clicked, this, &subStringFinder::pause_thread);
    connect(ui->continueButton, &QPushButton::clicked, this, &subStringFinder::restart_thread);
    connect(ui->stopButton, &QPushButton::clicked, this, &subStringFinder::interrupt_thread);
    connect(ui->actionchoose_filters, &QAction::triggered, this, &subStringFinder::show_filters);
    connect(ui->lineEdit, SIGNAL(textChanged(
                                         const QString &)), this, SLOT(pattern_updated(
                                                                               const QString &)));

    connect(ui->undoButton, &QPushButton::clicked, this, &subStringFinder::undo_selecting);
    connect(ui->switchButton, &QPushButton::clicked, this, &subStringFinder::switch_widget);
    connect(ui->treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(buttons_control()));
    connect(ui->fileList, SIGNAL(itemSelectionChanged()), this, SLOT(list_buttons_control()));
    connect(ui->indexStopButton, SIGNAL(clicked()), this, SLOT(stop_indexing()));
    connect(this, SIGNAL(to_log(QString)), this, SLOT(analyze_log(QString)));
    connect(ui->clearLogButton, SIGNAL(clicked()), this, SLOT(clear_log()));
    connect(ui->logDumpButton, SIGNAL(clicked()), this, SLOT(dump_log()));

    connect(this, SIGNAL(indexing_finishing(QString)), this, SLOT(indexing_has_finished(QString)));
    connect(this, SIGNAL(increase_index_bar(int)), this, SLOT(update_increase_bar(int)));
    connect(this, SIGNAL(send_max_index_bar(int)), this, SLOT(set_max_index_bar(int)));

    qRegisterMetaType<MyArray2>("MyArray2");
    qRegisterMetaType<std::string>("std::string");
    qRegisterMetaType<std::set<FilesTrigram, FilesTrigram::cmp>>("std::set<FilesTrigram, FilesTrigram::cmp>");

    connect(fsWatcher.get(), SIGNAL(fileChanged(QString)), this, SLOT(changed(QString)));

    show_filters();
}

void subStringFinder::dump_log() {
    //
}

void subStringFinder::clear_log() {
    ui->listWidget->clear();
}

void subStringFinder::analyze_log(QString l) {
    auto *item = new QListWidgetItem();
    QTime curr_time = QTime::currentTime();
    item->setText(curr_time.toString("hh:mm:ss") + " log: " + l);
    ui->listWidget->addItem(item);
}

void subStringFinder::set_max_index_bar(int val) {
    ui->indexBar->setMaximum(val);
}

void subStringFinder::update_increase_bar(int val) {
    ui->indexBar->setValue(ui->indexBar->value() + val);
}

void subStringFinder::stop_indexing() {
    if (is_indexing) {
        is_indexing = false;
        emit to_log("Indexing interrupted");
        index_map.cancel();
        future.waitForFinished();
        tmp_trigram_list.clear();
        ui->indexStopButton->setEnabled(false);
    }
}

void subStringFinder::list_buttons_control() {
    ui->removeButton->setEnabled(!ui->fileList->selectedItems().empty());
}

void subStringFinder::undo_selecting() {
    ui->treeWidget->clearSelection();
}

void subStringFinder::switch_widget() {
    int index = (ui->stackedWidget->currentIndex() + 1) % 2;
    ui->stackedWidget->setCurrentIndex(index);
    switch (index) {
        case 0:
            ui->switchButton->setText("Go to Log and Errors");
            break;
        case 1:
            ui->switchButton->setText("Go to duplicates tree");
            break;
        default:;
    }
}

void subStringFinder::buttons_control() {
    if (ui->treeWidget->selectedItems().empty()) {
        ui->undoButton->setEnabled(false);
        ui->dumpButton->setEnabled(false);
    } else {
        ui->undoButton->setEnabled(true);
        ui->dumpButton->setEnabled(true);
    }
}

void subStringFinder::closeEvent(QCloseEvent *event) {
    interrupt_thread();
    stop_indexing();
    event->accept();
}

void subStringFinder::pattern_updated(const QString &) {
    interrupt_thread();
    search();
}

void subStringFinder::reload_files() {
    if (!_isRunning) {
        QMessageBox::StandardButton reply = QMessageBox::question(this, "reload changed files",
                                                                  "Do you want to reload all files now?",
                                                                  QMessageBox::Yes | QMessageBox::No);
        if (reply == QMessageBox::Yes) {
            for (auto u : changed_files) {
                for (auto iter = _filesTrigrams.begin(); iter != _filesTrigrams.end(); iter++) {
                    if (iter->get_string_name() == u) {
                        _filesTrigrams.erase(iter);
                        _filesTrigrams.emplace(u);
                        break;
                    }
                }
            }
        } else {
            //do nothing
        }
    }
}

void subStringFinder::changed(const QString &flName) {
    ui->statusBar->showMessage(flName + QString(" changed"));
    changed_files.emplace(flName.toStdString());
    reload_files();
}

void subStringFinder::show_filters() {
    try {
        if (filtersWindow->exec() == QDialog::Accepted) {
            _filters.clear();

            QListIterator<QObject *> i(filtersWindow->ui->groupBox->children());
            while (i.hasNext()) {
                auto *b = qobject_cast<QCheckBox *>(i.next());
                if (b != nullptr && b->isChecked()) {
                    std::string tmp = '*' + b->text().toStdString();
                    _filters << tmp.c_str();
                }
            }
            //add regex here further <?>
            //add list of filters from lineEdit
            std::string list_users_filters = filtersWindow->ui->lineEdit->text().toStdString();
            //char_separator<char> sep(", ");
            std::string own_filter = '*' + list_users_filters;
            _filters << own_filter.c_str();
        }
    }
    catch (std::exception &ex) {
        error(ex.what());
    }
}

void subStringFinder::interrupt_thread() {
    if (thread != nullptr && _isRunning) {
        thread->requestInterruption();
    }
    _isRunning = false;
}

void subStringFinder::update_bar(int val) {
    ui->progressBar->setValue(ui->progressBar->value() + val);
}

void subStringFinder::scan_has_finished() {
    _isRunning = false;
    //ui->selectButton->setEnabled(true);
    ui->stopButton->setEnabled(false);

    ui->collapseButton->setEnabled(true);
    ui->expandButton->setEnabled(true);

    ui->pauseButton->setEnabled(false);
    ui->continueButton->setEnabled(false);

    /*if (ui->treeWidget->topLevelItemCount() == 0) {
        show_message(QString(tr("no copies here")));
    }*/
    clock_t timeOut = clock();
    double timeSpent = ((timeOut - _timeIn) * 1000) / CLOCKS_PER_SEC; //NOLINT

    //TODO: add like QString.args
    ui->statusBar->showMessage(tr("Time spent ") + QString::number(timeSpent) + tr("ms") +
                               " - " + QString::number(timeSpent / 1000) + tr("sec  "));

    if (!changed_files.empty()) {
        reload_files();
    }
}

void subStringFinder::merge_pack(MyArray2 pack) {
    //typedef std::pair<std::string, std::vector< std::pair<int, std::vector<int>> >>  MyArray;
    if (!pack.second.empty()) {
        auto *file_item = new QTreeWidgetItem();
        file_item->setText(0, QString::fromStdString(pack.first));
        size_t matches = 0;
        for (auto in_file : pack.second) {
            auto *item = new QTreeWidgetItem(file_item);
            int row = in_file.first;
            item->setText(0, QString("at ") + QString::number(row) + my_functions::add_suffix(row) + QString(" row"));
            item->setText(1, QString::number(in_file.second.size()) + QString(" matches"));
            for (auto index : in_file.second) {
                auto *item_child = new QTreeWidgetItem(item);
                item_child->setText(0, "pos: " + QString::number(index));
            }
            matches += in_file.second.size();
        }
        file_item->setText(1, QString::number(matches) + QString(" matches"));
        ui->treeWidget->addTopLevelItem(file_item);
    }
}

void subStringFinder::pause_thread() {
    ui->continueButton->setEnabled(true);
    ui->pauseButton->setEnabled(false);
    worker->_pause_required = true;
}

void subStringFinder::restart_thread() {
    ui->continueButton->setEnabled(false);
    ui->pauseButton->setEnabled(true);
    worker->_pause_required = false;
    worker->_pause_Manager.wakeAll();
}

void subStringFinder::error(QString err) {
    auto *item = new QListWidgetItem();
    QTime curr_time = QTime::currentTime();
    item->setText(curr_time.toString("hh:mm:ss") + " err: " + err);
    ui->listWidget->addItem(item);
}

void subStringFinder::analyze_error(QString err) {
    scan_has_finished();
    error(std::move(err));
}

void subStringFinder::remove_selected() {
    for (auto item : ui->fileList->selectedItems()) {
        auto name = item->text();
        if (QDir(name).exists()) {
            QDirIterator it(name, QDir::Hidden | QDir::Files, QDirIterator::Subdirectories);
            while (it.hasNext()) {
                auto file_name = it.next();
                fsWatcher->removePath(file_name);
                //make binsearch here
                for (auto iter = _filesTrigrams.begin(); iter != _filesTrigrams.end(); iter++) {
                    if (iter->get_qstring_name() == file_name) {
                        _filesTrigrams.erase(iter);
                        break;
                    }
                }
            }
            delete item;
        } else {
            //make binsearch here
            for (auto iter = _filesTrigrams.begin(); iter != _filesTrigrams.end(); iter++) {
                if (iter->get_qstring_name() == name) {
                    fsWatcher->removePath(iter->get_qstring_name());
                    _filesTrigrams.erase(iter);
                    break;
                }
            }
            //_filesTrigrams.erase(_filesTrigrams.find(name.toStdString()));
            //ask q about finding with other comparator
            delete item;
        }
    }
}

void subStringFinder::expand() {
    ui->treeWidget->expandAll();
}

void subStringFinder::collapse() {
    ui->treeWidget->collapseAll();
}

void subStringFinder::indexing_has_finished(QString dir) {
    if (!is_indexing) {
        return;
    }
    is_indexing = false;
    ui->indexStopButton->setEnabled(false);
    size_t cur_size = _filesTrigrams.size();

    for (auto &u : tmp_trigram_list) {
        if (u.isValid) {
            fsWatcher->addPath(u.get_qstring_name());
            _filesTrigrams.insert(std::move(u));
        }
    }

    size_t last_size = _filesTrigrams.size();

    if (cur_size == last_size) {
        //show message "this file already added or couldn't open"
    } else {
        ui->fileList->insertItem(0, QString("%1").arg(dir));
    }
}

void subStringFinder::start_indexing(QString dir) {
    QDirIterator it(dir, _filters, QDir::Hidden | QDir::Files, QDirIterator::Subdirectories);

    std::set<std::string> files_push_to_trigrams;
    while (it.hasNext()) {
        if (!is_indexing) { return; }
        QString cur_path = it.next();
        if (QFileInfo(cur_path).size() == 0) { continue; }
        files_push_to_trigrams.insert(cur_path.toStdString());
    }
    emit send_max_index_bar(static_cast<int>(files_push_to_trigrams.size()));
    std::function<FilesTrigram(std::string)> f = [this](std::string s) -> FilesTrigram {
        //if error occurs here -> isValid = false;
        FilesTrigram ft(s, &is_indexing);
        emit increase_index_bar(1);
        if (!ft.isValid) {
            emit to_log(QString("Can't open ") + QString::fromStdString(s));
        }
        return ft;
    };

    index_map = QtConcurrent::mapped(files_push_to_trigrams, f);
    tmp_trigram_list = index_map.results();
    emit indexing_finishing(dir);
}

void subStringFinder::add_dir() {
    ui->indexBar->setValue(0);
    QString dir = QFileDialog::getExistingDirectory(this, tr("Select Directory for Scanning"),
                                                    QString(),
                                                    QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    if (dir == "") { return; }
    ui->indexStopButton->setEnabled(true);

    tmp_trigram_list.clear();
    is_indexing = true;

    future = QtConcurrent::run(this, &subStringFinder::start_indexing, dir);
}

void subStringFinder::add_path() {
    QString path = QFileDialog::getOpenFileName(this, tr("Select file where to find substring"));
    if (path == "") { return; }
    size_t last_size = _filesTrigrams.size();
    try {
        _filesTrigrams.emplace(path.toStdString());
    } catch (std::exception &ex) {
        error(ex.what());
    }
    size_t cur_size = _filesTrigrams.size();
    if (cur_size == last_size) {
        //show message "this file already added or couldn't open"
    } else {
        ui->fileList->insertItem(0, QString("%1").arg(path));
        fsWatcher->addPath(path);
    }

}

void subStringFinder::search() {
    interrupt_thread();
    std::string pattern = ui->lineEdit->text().toStdString();
    if (_filesTrigrams.empty() || pattern.empty()) return;

    ui->statusBar->showMessage(tr("Searching substring..."));
    ui->progressBar->setValue(0);
    ui->progressBar->setMaximum(static_cast<int>(_filesTrigrams.size()));
    ui->treeWidget->clear();

    ui->pauseButton->setEnabled(true);
    ui->stopButton->setEnabled(true);
    ui->collapseButton->setEnabled(false);
    ui->expandButton->setEnabled(false);

    if (_filesTrigrams.empty()) {
        //show no files message
        return;
    }

    _timeIn = clock();
    thread = new QThread;
    worker = new substring_finder();
    worker->moveToThread(thread);
    _isRunning = true;

    connect(this, &subStringFinder::start_substring_finder, worker, &substring_finder::process);
    //connect(thread, SIGNAL(started()), worker, SLOT(process()));
    connect(worker, SIGNAL(finished()), this, SLOT(scan_has_finished()));
    //connect(worker, SIGNAL(set_max_bar(int)), this, SLOT(save_max_bar(int)));
    connect(worker, SIGNAL(increase_bar(int)), this, SLOT(update_bar(int)));
    connect(worker, SIGNAL(update_tree(MyArray2)), this, SLOT(merge_pack(MyArray2)));
    connect(worker, SIGNAL(error(QString)), this, SLOT(analyze_error(QString)));
    connect(worker, SIGNAL(push_to_log(QString)), this, SLOT(analyze_log(QString)));

    connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

    thread->start();
    emit start_substring_finder(pattern, &_filesTrigrams);
}

subStringFinder::~subStringFinder() = default;

max_stars_repo_name: Timoniche/FileUtilities

max_issues_repo_name: Timoniche/FileUtilities

max_forks_repo_name: Timoniche/FileUtilities


text:
#include <badem/lib/blocks.hpp>
#include <badem/lib/memory.hpp>
#include <badem/lib/work.hpp>
#include <badem/node/common.hpp>
#include <badem/node/election.hpp>
#include <badem/node/wallet.hpp>

#include <boost/endian/conversion.hpp>
#include <boost/pool/pool_alloc.hpp>

std::bitset<16> constexpr badem::message_header::block_type_mask;
std::bitset<16> constexpr badem::message_header::count_mask;
namespace
{
badem::protocol_constants const & get_protocol_constants ()
{
	static badem::network_params params;
	return params.protocol;
}
}
badem::message_header::message_header (badem::message_type type_a) :
version_max (get_protocol_constants ().protocol_version),
version_using (get_protocol_constants ().protocol_version),
version_min (get_protocol_constants ().protocol_version_min),
type (type_a)
{
}

badem::message_header::message_header (bool & error_a, badem::stream & stream_a)
{
	if (!error_a)
	{
		error_a = deserialize (stream_a);
	}
}

void badem::message_header::serialize (badem::stream & stream_a) const
{
	static badem::network_params network_params;
	badem::write (stream_a, network_params.header_magic_number);
	badem::write (stream_a, version_max);
	badem::write (stream_a, version_using);
	badem::write (stream_a, version_min);
	badem::write (stream_a, type);
	badem::write (stream_a, static_cast<uint16_t> (extensions.to_ullong ()));
}

bool badem::message_header::deserialize (badem::stream & stream_a)
{
	auto error (false);
	try
	{
		static badem::network_params network_params;
		uint16_t extensions_l;
		std::array<uint8_t, 2> magic_number_l;
		read (stream_a, magic_number_l);
		if (magic_number_l != network_params.header_magic_number)
		{
			throw std::runtime_error ("Magic numbers do not match");
		}

		badem::read (stream_a, version_max);
		badem::read (stream_a, version_using);
		badem::read (stream_a, version_min);
		badem::read (stream_a, type);
		badem::read (stream_a, extensions_l);
		extensions = extensions_l;
	}
	catch (std::runtime_error const &)
	{
		error = true;
	}

	return error;
}

badem::message::message (badem::message_type type_a) :
header (type_a)
{
}

badem::message::message (badem::message_header const & header_a) :
header (header_a)
{
}

badem::block_type badem::message_header::block_type () const
{
	return static_cast<badem::block_type> (((extensions & block_type_mask) >> 8).to_ullong ());
}

void badem::message_header::block_type_set (badem::block_type type_a)
{
	extensions &= ~block_type_mask;
	extensions |= std::bitset<16> (static_cast<unsigned long long> (type_a) << 8);
}

uint8_t badem::message_header::count_get () const
{
	return static_cast<uint8_t> (((extensions & count_mask) >> 12).to_ullong ());
}

void badem::message_header::count_set (uint8_t count_a)
{
	assert (count_a < 16);
	extensions &= ~count_mask;
	extensions |= std::bitset<16> (static_cast<unsigned long long> (count_a) << 12);
}

void badem::message_header::flag_set (uint8_t flag_a)
{
	// Flags from 8 are block_type & count
	assert (flag_a < 8);
	extensions.set (flag_a, true);
}

bool badem::message_header::bulk_pull_is_count_present () const
{
	auto result (false);
	if (type == badem::message_type::bulk_pull)
	{
		if (extensions.test (bulk_pull_count_present_flag))
		{
			result = true;
		}
	}
	return result;
}

bool badem::message_header::node_id_handshake_is_query () const
{
	auto result (false);
	if (type == badem::message_type::node_id_handshake)
	{
		if (extensions.test (node_id_handshake_query_flag))
		{
			result = true;
		}
	}
	return result;
}

bool badem::message_header::node_id_handshake_is_response () const
{
	auto result (false);
	if (type == badem::message_type::node_id_handshake)
	{
		if (extensions.test (node_id_handshake_response_flag))
		{
			result = true;
		}
	}
	return result;
}

size_t badem::message_header::payload_length_bytes () const
{
	switch (type)
	{
		case badem::message_type::bulk_pull:
		{
			return badem::bulk_pull::size + (bulk_pull_is_count_present () ? badem::bulk_pull::extended_parameters_size : 0);
		}
		case badem::message_type::bulk_push:
		{
			// bulk_push doesn't have a payload
			return 0;
		}
		case badem::message_type::frontier_req:
		{
			return badem::frontier_req::size;
		}
		case badem::message_type::bulk_pull_account:
		{
			return badem::bulk_pull_account::size;
		}
		case badem::message_type::keepalive:
		{
			return badem::keepalive::size;
		}
		case badem::message_type::publish:
		{
			return badem::block::size (block_type ());
		}
		case badem::message_type::confirm_ack:
		{
			return badem::confirm_ack::size (block_type (), count_get ());
		}
		case badem::message_type::confirm_req:
		{
			return badem::confirm_req::size (block_type (), count_get ());
		}
		case badem::message_type::node_id_handshake:
		{
			return badem::node_id_handshake::size (*this);
		}
		default:
		{
			assert (false);
			return 0;
		}
	}
}

// MTU - IP header - UDP header
const size_t badem::message_parser::max_safe_udp_message_size = 508;

std::string badem::message_parser::status_string ()
{
	switch (status)
	{
		case badem::message_parser::parse_status::success:
		{
			return "success";
		}
		case badem::message_parser::parse_status::insufficient_work:
		{
			return "insufficient_work";
		}
		case badem::message_parser::parse_status::invalid_header:
		{
			return "invalid_header";
		}
		case badem::message_parser::parse_status::invalid_message_type:
		{
			return "invalid_message_type";
		}
		case badem::message_parser::parse_status::invalid_keepalive_message:
		{
			return "invalid_keepalive_message";
		}
		case badem::message_parser::parse_status::invalid_publish_message:
		{
			return "invalid_publish_message";
		}
		case badem::message_parser::parse_status::invalid_confirm_req_message:
		{
			return "invalid_confirm_req_message";
		}
		case badem::message_parser::parse_status::invalid_confirm_ack_message:
		{
			return "invalid_confirm_ack_message";
		}
		case badem::message_parser::parse_status::invalid_node_id_handshake_message:
		{
			return "invalid_node_id_handshake_message";
		}
		case badem::message_parser::parse_status::outdated_version:
		{
			return "outdated_version";
		}
		case badem::message_parser::parse_status::invalid_magic:
		{
			return "invalid_magic";
		}
		case badem::message_parser::parse_status::invalid_network:
		{
			return "invalid_network";
		}
	}

	assert (false);

	return "[unknown parse_status]";
}

badem::message_parser::message_parser (badem::block_uniquer & block_uniquer_a, badem::vote_uniquer & vote_uniquer_a, badem::message_visitor & visitor_a, badem::work_pool & pool_a) :
block_uniquer (block_uniquer_a),
vote_uniquer (vote_uniquer_a),
visitor (visitor_a),
pool (pool_a),
status (parse_status::success)
{
}

void badem::message_parser::deserialize_buffer (uint8_t const * buffer_a, size_t size_a)
{
	static badem::network_constants network_constants;
	status = parse_status::success;
	auto error (false);
	if (size_a <= max_safe_udp_message_size)
	{
		// Guaranteed to be deliverable
		badem::bufferstream stream (buffer_a, size_a);
		badem::message_header header (error, stream);
		if (!error)
		{
			if (header.version_using < get_protocol_constants ().protocol_version_min)
			{
				status = parse_status::outdated_version;
			}
			else
			{
				switch (header.type)
				{
					case badem::message_type::keepalive:
					{
						deserialize_keepalive (stream, header);
						break;
					}
					case badem::message_type::publish:
					{
						deserialize_publish (stream, header);
						break;
					}
					case badem::message_type::confirm_req:
					{
						deserialize_confirm_req (stream, header);
						break;
					}
					case badem::message_type::confirm_ack:
					{
						deserialize_confirm_ack (stream, header);
						break;
					}
					case badem::message_type::node_id_handshake:
					{
						deserialize_node_id_handshake (stream, header);
						break;
					}
					default:
					{
						status = parse_status::invalid_message_type;
						break;
					}
				}
			}
		}
		else
		{
			status = parse_status::invalid_header;
		}
	}
}

void badem::message_parser::deserialize_keepalive (badem::stream & stream_a, badem::message_header const & header_a)
{
	auto error (false);
	badem::keepalive incoming (error, stream_a, header_a);
	if (!error && at_end (stream_a))
	{
		visitor.keepalive (incoming);
	}
	else
	{
		status = parse_status::invalid_keepalive_message;
	}
}

void badem::message_parser::deserialize_publish (badem::stream & stream_a, badem::message_header const & header_a)
{
	auto error (false);
	badem::publish incoming (error, stream_a, header_a, &block_uniquer);
	if (!error && at_end (stream_a))
	{
		if (!badem::work_validate (*incoming.block))
		{
			visitor.publish (incoming);
		}
		else
		{
			status = parse_status::insufficient_work;
		}
	}
	else
	{
		status = parse_status::invalid_publish_message;
	}
}

void badem::message_parser::deserialize_confirm_req (badem::stream & stream_a, badem::message_header const & header_a)
{
	auto error (false);
	badem::confirm_req incoming (error, stream_a, header_a, &block_uniquer);
	if (!error && at_end (stream_a))
	{
		if (incoming.block == nullptr || !badem::work_validate (*incoming.block))
		{
			visitor.confirm_req (incoming);
		}
		else
		{
			status = parse_status::insufficient_work;
		}
	}
	else
	{
		status = parse_status::invalid_confirm_req_message;
	}
}

void badem::message_parser::deserialize_confirm_ack (badem::stream & stream_a, badem::message_header const & header_a)
{
	auto error (false);
	badem::confirm_ack incoming (error, stream_a, header_a, &vote_uniquer);
	if (!error && at_end (stream_a))
	{
		for (auto & vote_block : incoming.vote->blocks)
		{
			if (!vote_block.which ())
			{
				auto block (boost::get<std::shared_ptr<badem::block>> (vote_block));
				if (badem::work_validate (*block))
				{
					status = parse_status::insufficient_work;
					break;
				}
			}
		}
		if (status == parse_status::success)
		{
			visitor.confirm_ack (incoming);
		}
	}
	else
	{
		status = parse_status::invalid_confirm_ack_message;
	}
}

void badem::message_parser::deserialize_node_id_handshake (badem::stream & stream_a, badem::message_header const & header_a)
{
	bool error_l (false);
	badem::node_id_handshake incoming (error_l, stream_a, header_a);
	if (!error_l && at_end (stream_a))
	{
		visitor.node_id_handshake (incoming);
	}
	else
	{
		status = parse_status::invalid_node_id_handshake_message;
	}
}

bool badem::message_parser::at_end (badem::stream & stream_a)
{
	uint8_t junk;
	auto end (badem::try_read (stream_a, junk));
	return end;
}

badem::keepalive::keepalive () :
message (badem::message_type::keepalive)
{
	badem::endpoint endpoint (boost::asio::ip::address_v6{}, 0);
	for (auto i (peers.begin ()), n (peers.end ()); i != n; ++i)
	{
		*i = endpoint;
	}
}

badem::keepalive::keepalive (bool & error_a, badem::stream & stream_a, badem::message_header const & header_a) :
message (header_a)
{
	if (!error_a)
	{
		error_a = deserialize (stream_a);
	}
}

void badem::keepalive::visit (badem::message_visitor & visitor_a) const
{
	visitor_a.keepalive (*this);
}

void badem::keepalive::serialize (badem::stream & stream_a) const
{
	header.serialize (stream_a);
	for (auto i (peers.begin ()), j (peers.end ()); i != j; ++i)
	{
		assert (i->address ().is_v6 ());
		auto bytes (i->address ().to_v6 ().to_bytes ());
		write (stream_a, bytes);
		write (stream_a, i->port ());
	}
}

bool badem::keepalive::deserialize (badem::stream & stream_a)
{
	assert (header.type == badem::message_type::keepalive);
	auto error (false);
	for (auto i (peers.begin ()), j (peers.end ()); i != j && !error; ++i)
	{
		std::array<uint8_t, 16> address;
		uint16_t port;
		if (!try_read (stream_a, address) && !try_read (stream_a, port))
		{
			*i = badem::endpoint (boost::asio::ip::address_v6 (address), port);
		}
		else
		{
			error = true;
		}
	}
	return error;
}

bool badem::keepalive::operator== (badem::keepalive const & other_a) const
{
	return peers == other_a.peers;
}

badem::publish::publish (bool & error_a, badem::stream & stream_a, badem::message_header const & header_a, badem::block_uniquer * uniquer_a) :
message (header_a)
{
	if (!error_a)
	{
		error_a = deserialize (stream_a, uniquer_a);
	}
}

badem::publish::publish (std::shared_ptr<badem::block> block_a) :
message (badem::message_type::publish),
block (block_a)
{
	header.block_type_set (block->type ());
}

void badem::publish::serialize (badem::stream & stream_a) const
{
	assert (block != nullptr);
	header.serialize (stream_a);
	block->serialize (stream_a);
}

bool badem::publish::deserialize (badem::stream & stream_a, badem::block_uniquer * uniquer_a)
{
	assert (header.type == badem::message_type::publish);
	block = badem::deserialize_block (stream_a, header.block_type (), uniquer_a);
	auto result (block == nullptr);
	return result;
}

void badem::publish::visit (badem::message_visitor & visitor_a) const
{
	visitor_a.publish (*this);
}

bool badem::publish::operator== (badem::publish const & other_a) const
{
	return *block == *other_a.block;
}

badem::confirm_req::confirm_req (bool & error_a, badem::stream & stream_a, badem::message_header const & header_a, badem::block_uniquer * uniquer_a) :
message (header_a)
{
	if (!error_a)
	{
		error_a = deserialize (stream_a, uniquer_a);
	}
}

badem::confirm_req::confirm_req (std::shared_ptr<badem::block> block_a) :
message (badem::message_type::confirm_req),
block (block_a)
{
	header.block_type_set (block->type ());
}

badem::confirm_req::confirm_req (std::vector<std::pair<badem::block_hash, badem::root>> const & roots_hashes_a) :
message (badem::message_type::confirm_req),
roots_hashes (roots_hashes_a)
{
	// not_a_block (1) block type for hashes + roots request
	header.block_type_set (badem::block_type::not_a_block);
	assert (roots_hashes.size () < 16);
	header.count_set (static_cast<uint8_t> (roots_hashes.size ()));
}

badem::confirm_req::confirm_req (badem::block_hash const & hash_a, badem::root const & root_a) :
message (badem::message_type::confirm_req),
roots_hashes (std::vector<std::pair<badem::block_hash, badem::root>> (1, std::make_pair (hash_a, root_a)))
{
	assert (!roots_hashes.empty ());
	// not_a_block (1) block type for hashes + roots request
	header.block_type_set (badem::block_type::not_a_block);
	assert (roots_hashes.size () < 16);
	header.count_set (static_cast<uint8_t> (roots_hashes.size ()));
}

void badem::confirm_req::visit (badem::message_visitor & visitor_a) const
{
	visitor_a.confirm_req (*this);
}

void badem::confirm_req::serialize (badem::stream & stream_a) const
{
	header.serialize (stream_a);
	if (header.block_type () == badem::block_type::not_a_block)
	{
		assert (!roots_hashes.empty ());
		// Write hashes & roots
		for (auto & root_hash : roots_hashes)
		{
			write (stream_a, root_hash.first);
			write (stream_a, root_hash.second);
		}
	}
	else
	{
		assert (block != nullptr);
		block->serialize (stream_a);
	}
}

bool badem::confirm_req::deserialize (badem::stream & stream_a, badem::block_uniquer * uniquer_a)
{
	bool result (false);
	assert (header.type == badem::message_type::confirm_req);
	try
	{
		if (header.block_type () == badem::block_type::not_a_block)
		{
			uint8_t count (header.count_get ());
			for (auto i (0); i != count && !result; ++i)
			{
				badem::block_hash block_hash (0);
				badem::block_hash root (0);
				read (stream_a, block_hash);
				read (stream_a, root);
				if (!block_hash.is_zero () || !root.is_zero ())
				{
					roots_hashes.emplace_back (block_hash, root);
				}
			}

			result = roots_hashes.empty () || (roots_hashes.size () != count);
		}
		else
		{
			block = badem::deserialize_block (stream_a, header.block_type (), uniquer_a);
			result = block == nullptr;
		}
	}
	catch (const std::runtime_error &)
	{
		result = true;
	}

	return result;
}

bool badem::confirm_req::operator== (badem::confirm_req const & other_a) const
{
	bool equal (false);
	if (block != nullptr && other_a.block != nullptr)
	{
		equal = *block == *other_a.block;
	}
	else if (!roots_hashes.empty () && !other_a.roots_hashes.empty ())
	{
		equal = roots_hashes == other_a.roots_hashes;
	}
	return equal;
}

std::string badem::confirm_req::roots_string () const
{
	std::string result;
	for (auto & root_hash : roots_hashes)
	{
		result += root_hash.first.to_string ();
		result += ":";
		result += root_hash.second.to_string ();
		result += ", ";
	}
	return result;
}

size_t badem::confirm_req::size (badem::block_type type_a, size_t count)
{
	size_t result (0);
	if (type_a != badem::block_type::invalid && type_a != badem::block_type::not_a_block)
	{
		result = badem::block::size (type_a);
	}
	else if (type_a == badem::block_type::not_a_block)
	{
		result = count * (sizeof (badem::uint256_union) + sizeof (badem::block_hash));
	}
	return result;
}

badem::confirm_ack::confirm_ack (bool & error_a, badem::stream & stream_a, badem::message_header const & header_a, badem::vote_uniquer * uniquer_a) :
message (header_a),
vote (badem::make_shared<badem::vote> (error_a, stream_a, header.block_type ()))
{
	if (!error_a && uniquer_a)
	{
		vote = uniquer_a->unique (vote);
	}
}

badem::confirm_ack::confirm_ack (std::shared_ptr<badem::vote> vote_a) :
message (badem::message_type::confirm_ack),
vote (vote_a)
{
	assert (!vote_a->blocks.empty ());
	auto & first_vote_block (vote_a->blocks[0]);
	if (first_vote_block.which ())
	{
		header.block_type_set (badem::block_type::not_a_block);
		assert (vote_a->blocks.size () < 16);
		header.count_set (static_cast<uint8_t> (vote_a->blocks.size ()));
	}
	else
	{
		header.block_type_set (boost::get<std::shared_ptr<badem::block>> (first_vote_block)->type ());
	}
}

void badem::confirm_ack::serialize (badem::stream & stream_a) const
{
	assert (header.block_type () == badem::block_type::not_a_block || header.block_type () == badem::block_type::send || header.block_type () == badem::block_type::receive || header.block_type () == badem::block_type::open || header.block_type () == badem::block_type::change || header.block_type () == badem::block_type::state);
	header.serialize (stream_a);
	vote->serialize (stream_a, header.block_type ());
}

bool badem::confirm_ack::operator== (badem::confirm_ack const & other_a) const
{
	auto result (*vote == *other_a.vote);
	return result;
}

void badem::confirm_ack::visit (badem::message_visitor & visitor_a) const
{
	visitor_a.confirm_ack (*this);
}

size_t badem::confirm_ack::size (badem::block_type type_a, size_t count)
{
	size_t result (sizeof (badem::account) + sizeof (badem::signature) + sizeof (uint64_t));
	if (type_a != badem::block_type::invalid && type_a != badem::block_type::not_a_block)
	{
		result += badem::block::size (type_a);
	}
	else if (type_a == badem::block_type::not_a_block)
	{
		result += count * sizeof (badem::block_hash);
	}
	return result;
}

badem::frontier_req::frontier_req () :
message (badem::message_type::frontier_req)
{
}

badem::frontier_req::frontier_req (bool & error_a, badem::stream & stream_a, badem::message_header const & header_a) :
message (header_a)
{
	if (!error_a)
	{
		error_a = deserialize (stream_a);
	}
}

void badem::frontier_req::serialize (badem::stream & stream_a) const
{
	header.serialize (stream_a);
	write (stream_a, start.bytes);
	write (stream_a, age);
	write (stream_a, count);
}

bool badem::frontier_req::deserialize (badem::stream & stream_a)
{
	assert (header.type == badem::message_type::frontier_req);
	auto error (false);
	try
	{
		badem::read (stream_a, start.bytes);
		badem::read (stream_a, age);
		badem::read (stream_a, count);
	}
	catch (std::runtime_error const &)
	{
		error = true;
	}

	return error;
}

void badem::frontier_req::visit (badem::message_visitor & visitor_a) const
{
	visitor_a.frontier_req (*this);
}

bool badem::frontier_req::operator== (badem::frontier_req const & other_a) const
{
	return start == other_a.start && age == other_a.age && count == other_a.count;
}

badem::bulk_pull::bulk_pull () :
message (badem::message_type::bulk_pull)
{
}

badem::bulk_pull::bulk_pull (bool & error_a, badem::stream & stream_a, badem::message_header const & header_a) :
message (header_a)
{
	if (!error_a)
	{
		error_a = deserialize (stream_a);
	}
}

void badem::bulk_pull::visit (badem::message_visitor & visitor_a) const
{
	visitor_a.bulk_pull (*this);
}

void badem::bulk_pull::serialize (badem::stream & stream_a) const
{
	/*
	 * Ensure the "count_present" flag is set if there
	 * is a limit specifed.  Additionally, do not allow
	 * the "count_present" flag with a value of 0, since
	 * that is a sentinel which we use to mean "all blocks"
	 * and that is the behavior of not having the flag set
	 * so it is wasteful to do this.
	 */
	assert ((count == 0 && !is_count_present ()) || (count != 0 && is_count_present ()));

	header.serialize (stream_a);
	write (stream_a, start);
	write (stream_a, end);

	if (is_count_present ())
	{
		std::array<uint8_t, extended_parameters_size> count_buffer{ { 0 } };
		decltype (count) count_little_endian;
		static_assert (sizeof (count_little_endian) < (count_buffer.size () - 1), "count must fit within buffer");

		count_little_endian = boost::endian::native_to_little (count);
		memcpy (count_buffer.data () + 1, &count_little_endian, sizeof (count_little_endian));

		write (stream_a, count_buffer);
	}
}

bool badem::bulk_pull::deserialize (badem::stream & stream_a)
{
	assert (header.type == badem::message_type::bulk_pull);
	auto error (false);
	try
	{
		badem::read (stream_a, start);
		badem::read (stream_a, end);

		if (is_count_present ())
		{
			std::array<uint8_t, extended_parameters_size> extended_parameters_buffers;
			static_assert (sizeof (count) < (extended_parameters_buffers.size () - 1), "count must fit within buffer");

			badem::read (stream_a, extended_parameters_buffers);
			if (extended_parameters_buffers.front () != 0)
			{
				error = true;
			}
			else
			{
				memcpy (&count, extended_parameters_buffers.data () + 1, sizeof (count));
				boost::endian::little_to_native_inplace (count);
			}
		}
		else
		{
			count = 0;
		}
	}
	catch (std::runtime_error const &)
	{
		error = true;
	}

	return error;
}

bool badem::bulk_pull::is_count_present () const
{
	return header.extensions.test (count_present_flag);
}

void badem::bulk_pull::set_count_present (bool value_a)
{
	header.extensions.set (count_present_flag, value_a);
}

badem::bulk_pull_account::bulk_pull_account () :
message (badem::message_type::bulk_pull_account)
{
}

badem::bulk_pull_account::bulk_pull_account (bool & error_a, badem::stream & stream_a, badem::message_header const & header_a) :
message (header_a)
{
	if (!error_a)
	{
		error_a = deserialize (stream_a);
	}
}

void badem::bulk_pull_account::visit (badem::message_visitor & visitor_a) const
{
	visitor_a.bulk_pull_account (*this);
}

void badem::bulk_pull_account::serialize (badem::stream & stream_a) const
{
	header.serialize (stream_a);
	write (stream_a, account);
	write (stream_a, minimum_amount);
	write (stream_a, flags);
}

bool badem::bulk_pull_account::deserialize (badem::stream & stream_a)
{
	assert (header.type == badem::message_type::bulk_pull_account);
	auto error (false);
	try
	{
		badem::read (stream_a, account);
		badem::read (stream_a, minimum_amount);
		badem::read (stream_a, flags);
	}
	catch (std::runtime_error const &)
	{
		error = true;
	}

	return error;
}

badem::bulk_push::bulk_push () :
message (badem::message_type::bulk_push)
{
}

badem::bulk_push::bulk_push (badem::message_header const & header_a) :
message (header_a)
{
}

bool badem::bulk_push::deserialize (badem::stream & stream_a)
{
	assert (header.type == badem::message_type::bulk_push);
	return false;
}

void badem::bulk_push::serialize (badem::stream & stream_a) const
{
	header.serialize (stream_a);
}

void badem::bulk_push::visit (badem::message_visitor & visitor_a) const
{
	visitor_a.bulk_push (*this);
}

badem::node_id_handshake::node_id_handshake (bool & error_a, badem::stream & stream_a, badem::message_header const & header_a) :
message (header_a),
query (boost::none),
response (boost::none)
{
	error_a = deserialize (stream_a);
}

badem::node_id_handshake::node_id_handshake (boost::optional<badem::uint256_union> query, boost::optional<std::pair<badem::account, badem::signature>> response) :
message (badem::message_type::node_id_handshake),
query (query),
response (response)
{
	if (query)
	{
		header.flag_set (badem::message_header::node_id_handshake_query_flag);
	}
	if (response)
	{
		header.flag_set (badem::message_header::node_id_handshake_response_flag);
	}
}

void badem::node_id_handshake::serialize (badem::stream & stream_a) const
{
	header.serialize (stream_a);
	if (query)
	{
		write (stream_a, *query);
	}
	if (response)
	{
		write (stream_a, response->first);
		write (stream_a, response->second);
	}
}

bool badem::node_id_handshake::deserialize (badem::stream & stream_a)
{
	assert (header.type == badem::message_type::node_id_handshake);
	auto error (false);
	try
	{
		if (header.node_id_handshake_is_query ())
		{
			badem::uint256_union query_hash;
			read (stream_a, query_hash);
			query = query_hash;
		}

		if (header.node_id_handshake_is_response ())
		{
			badem::account response_account;
			read (stream_a, response_account);
			badem::signature response_signature;
			read (stream_a, response_signature);
			response = std::make_pair (response_account, response_signature);
		}
	}
	catch (std::runtime_error const &)
	{
		error = true;
	}

	return error;
}

bool badem::node_id_handshake::operator== (badem::node_id_handshake const & other_a) const
{
	auto result (*query == *other_a.query && *response == *other_a.response);
	return result;
}

void badem::node_id_handshake::visit (badem::message_visitor & visitor_a) const
{
	visitor_a.node_id_handshake (*this);
}

size_t badem::node_id_handshake::size () const
{
	return size (header);
}

size_t badem::node_id_handshake::size (badem::message_header const & header_a)
{
	size_t result (0);
	if (header_a.node_id_handshake_is_query ())
	{
		result = sizeof (badem::uint256_union);
	}
	if (header_a.node_id_handshake_is_response ())
	{
		result += sizeof (badem::account) + sizeof (badem::signature);
	}
	return result;
}

badem::message_visitor::~message_visitor ()
{
}

bool badem::parse_port (std::string const & string_a, uint16_t & port_a)
{
	bool result = false;
	try
	{
		port_a = boost::lexical_cast<uint16_t> (string_a);
	}
	catch (...)
	{
		result = true;
	}
	return result;
}

bool badem::parse_address_port (std::string const & string, boost::asio::ip::address & address_a, uint16_t & port_a)
{
	auto result (false);
	auto port_position (string.rfind (':'));
	if (port_position != std::string::npos && port_position > 0)
	{
		std::string port_string (string.substr (port_position + 1));
		try
		{
			uint16_t port;
			result = parse_port (port_string, port);
			if (!result)
			{
				boost::system::error_code ec;
				auto address (boost::asio::ip::address_v6::from_string (string.substr (0, port_position), ec));
				if (!ec)
				{
					address_a = address;
					port_a = port;
				}
				else
				{
					result = true;
				}
			}
			else
			{
				result = true;
			}
		}
		catch (...)
		{
			result = true;
		}
	}
	else
	{
		result = true;
	}
	return result;
}

bool badem::parse_endpoint (std::string const & string, badem::endpoint & endpoint_a)
{
	boost::asio::ip::address address;
	uint16_t port;
	auto result (parse_address_port (string, address, port));
	if (!result)
	{
		endpoint_a = badem::endpoint (address, port);
	}
	return result;
}

bool badem::parse_tcp_endpoint (std::string const & string, badem::tcp_endpoint & endpoint_a)
{
	boost::asio::ip::address address;
	uint16_t port;
	auto result (parse_address_port (string, address, port));
	if (!result)
	{
		endpoint_a = badem::tcp_endpoint (address, port);
	}
	return result;
}

badem::node_singleton_memory_pool_purge_guard::node_singleton_memory_pool_purge_guard () :
cleanup_guard ({ badem::block_memory_pool_purge, badem::purge_singleton_pool_memory<badem::vote>, badem::purge_singleton_pool_memory<badem::election> })
{
}

max_stars_repo_name: bademcurrency/badem-node

max_issues_repo_name: bademcurrency/badem-node

max_forks_repo_name: bademcurrency/badem-node


text:
#define BOOST_TEST_MODULE test_standard_license

#include <boost/test/unit_test.hpp>
#include <boost/filesystem.hpp>

#include <licensecc/licensecc.h>
#include <licensecc_properties_test.h>
#include <licensecc_properties.h>

#include "../../src/library/ini/SimpleIni.h"
#include "generate-license.h"
#include "../../src/library/base/file_utils.hpp"

using namespace std;
namespace fs = boost::filesystem;

namespace license {
namespace test {

/**
 * Test a generic license with no expiry neither client id. The license is read from file
 */
BOOST_AUTO_TEST_CASE(test_generic_license_read_file) {
	const vector<string> extraArgs;
	const string licLocation = generate_license("standard_license", extraArgs);
	/* */
	LicenseInfo license;
	LicenseLocation location = {LICENSE_PATH};
	std::copy(licLocation.begin(), licLocation.end(), location.licenseData);
	const LCC_EVENT_TYPE result = acquire_license(nullptr, &location, &license);
	BOOST_CHECK_EQUAL(result, LICENSE_OK);
	BOOST_CHECK_EQUAL(license.has_expiry, false);
	BOOST_CHECK_EQUAL(license.linked_to_pc, false);
}

/**
 * Test a generic license with no expiry neither client id. The license is passed in trhough the licenseData structure.
 */
BOOST_AUTO_TEST_CASE(test_read_license_data) {
	const vector<string> extraArgs;
	const fs::path licLocation = fs::path(generate_license("standard_license1", extraArgs));
	const string licLocationStr = licLocation.string();
	string license_data = get_file_contents(licLocationStr.c_str(), 65536);
	LicenseInfo license;
	LicenseLocation location = {LICENSE_PLAIN_DATA};
	std::copy(license_data.begin(), license_data.end(), location.licenseData);
	const LCC_EVENT_TYPE result = acquire_license(nullptr, &location, &license);
	BOOST_CHECK_EQUAL(result, LICENSE_OK);
	BOOST_CHECK_EQUAL(license.has_expiry, false);
	BOOST_CHECK_EQUAL(license.linked_to_pc, false);
}

/**
 * Pass the license data to the application.
 */

// BOOST_AUTO_TEST_CASE( b64_environment_variable ) {
//	const string licLocation(PROJECT_TEST_TEMP_DIR "/standard_env_license.lic");
//	const vector<string> extraArgs;
//	generate_license(licLocation, extraArgs);
//	const string licensestr(license::get_file_contents(licLocation.c_str(), MAX_LICENSE_LENGTH));
//	/* */
//	LicenseInfo license;
//	LicenseLocation licenseLocation;
//	licenseLocation.licenseFileLocation = nullptr;
//	licenseLocation.licenseData = licensestr.c_str();
//	const EVENT_TYPE result = acquire_license("TEST", &licenseLocation,
//			&license);
//	BOOST_CHECK_EQUAL(result, LICENSE_OK);
//	BOOST_CHECK_EQUAL(license.has_expiry, false);
//	BOOST_CHECK_EQUAL(license.linked_to_pc, false);
//}
//
// BOOST_AUTO_TEST_CASE( hw_identifier ) {
//	const string licLocation(PROJECT_TEST_TEMP_DIR "/hw_identifier.lic");
//	const vector<string> extraArgs = { "-s", "Jaaa-aaaa-MG9F-ZhB1" };
//	generate_license(licLocation, extraArgs);
//
//	LicenseInfo license;
//	LicenseLocation licenseLocation;
//	licenseLocation.licenseFileLocation = licLocation.c_str();
//	licenseLocation.licenseData = "";
//	const EVENT_TYPE result = acquire_license("TEST", &licenseLocation,
//			&license);
//	BOOST_CHECK_EQUAL(result, IDENTIFIERS_MISMATCH);
//	BOOST_CHECK_EQUAL(license.has_expiry, false);
//	BOOST_CHECK_EQUAL(license.linked_to_pc, true);
//}
}
}  // namespace license

max_stars_repo_name: BotellaA/open-license-manager

max_issues_repo_name: BotellaA/open-license-manager

max_forks_repo_name: BotellaA/open-license-manager


text:
#pragma once
#include "config.hpp"
#include "input_pin.hpp"
#include "output_pin.hpp"
#include "input_interrupt_pin.hpp"
#include "peripheral_types.hpp"
#include <boost/hana/string.hpp>
#include "opt/option.hpp"

namespace drivers::gpio
{
	namespace detail
	{
        MAKE_OPTION_KEY(OUTPUT_TYPE);
        MAKE_OPTION_KEY(PUPD);
        MAKE_OPTION_KEY(ALT_FUNCTION);
        MAKE_OPTION_KEY(SPEED);
        MAKE_OPTION_KEY(INTERRUPT);

		template<std::uint8_t pin>
        constexpr auto getInterrupt(uint8_<pin>)
        {
			static_assert(pin < 16, "Invalid pin number (should be < 16)");
            if constexpr (pin == 0)
                return board::Interrupts::EXTI0;
            else if constexpr (pin == 1)
                return board::Interrupts::EXTI1;
            else if constexpr (pin == 2)
                return board::Interrupts::EXTI2;
            else if constexpr (pin == 3)
                return board::Interrupts::EXTI3;
            else if constexpr (pin == 4)
                return board::Interrupts::EXTI4;
            else if constexpr (pin <= 9)
                return board::Interrupts::EXTI9_5;
			else if constexpr (pin <= 16)
            	return board::Interrupts::EXTI15_10;
        }
	}

	namespace AltFn
	{
		constexpr auto SYSTEM             = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::SYSTEM>);
		constexpr auto TIMER1_2           = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::TIMER1_2>);
		constexpr auto TIMER3_5           = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::TIMER3_5>);
		constexpr auto TIMER8_11          = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::TIMER8_11>);
		constexpr auto I2C                = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::I2C>);
		constexpr auto SPI1_2             = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::SPI1_2>);
		constexpr auto SPI3               = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::SPI3>);
		constexpr auto USART1_3           = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::USART1_3>);
		constexpr auto USART4_6           = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::USART4_6>);
		constexpr auto CAN1_2_TIMER_12_14 = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::CAN1_2_TIMER_12_14>);
		constexpr auto OTG_FS_HS          = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::OTG_FS_HS>);
		constexpr auto ETH                = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::ETH>);
		constexpr auto FSMC_SDIO_OTG_HS   = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::FSMC_SDIO_OTG_HS>);
		constexpr auto DCMI               = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::DCMI>);
		constexpr auto EVENTOUT           = opt::makeOption(detail::ALT_FUNCTION, constant_c<detail::AfrVal::EVENTOUT>);
	}

    namespace OutType
	{
        constexpr auto PUSH_PULL  = opt::makeOption(detail::OUTPUT_TYPE, constant_c<detail::OtyperVal::PUSH_PULL>);
		constexpr auto OPEN_DRAIN = opt::makeOption(detail::OUTPUT_TYPE, constant_c<detail::OtyperVal::OPEN_DRAIN>);
	}

	namespace PuPd
	{
		constexpr auto NO_PULL   = opt::makeOption(detail::PUPD, constant_c<detail::PupdrVal::NO_PULL>);
		constexpr auto PULL_UP   = opt::makeOption(detail::PUPD, constant_c<detail::PupdrVal::PULL_UP>);;
		constexpr auto PULL_DOWN = opt::makeOption(detail::PUPD, constant_c<detail::PupdrVal::PULL_DOWN>);;
	}

	namespace Speed
	{
		constexpr auto LOW       = opt::makeOption(detail::SPEED, constant_c<detail::OspeedrVal::LOW>);
		constexpr auto MEDIUM    = opt::makeOption(detail::SPEED, constant_c<detail::OspeedrVal::MEDIUM>);
		constexpr auto HIGH      = opt::makeOption(detail::SPEED, constant_c<detail::OspeedrVal::HIGH>);
		constexpr auto VERY_HIGH = opt::makeOption(detail::SPEED, constant_c<detail::OspeedrVal::VERY_HIGH>);
	}

	namespace Interrupt
	{
		constexpr auto RISING_EDGE = opt::makeOption(detail::INTERRUPT, hana::type_c<detail::RisingEdgeInterrupt>);
		constexpr auto FALLING_EDGE = opt::makeOption(detail::INTERRUPT, hana::type_c<detail::FallingEdgeInterrupt>);
		constexpr auto RISING_FALLING_EDGE = opt::makeOption(detail::INTERRUPT, hana::type_c<detail::RisingFallingEdgeInterrupt>);
	}

    /**
     * @brief Create an output pin gpio
     **/
	template<class Board, std::uint8_t portNo, std::uint8_t pinNo, class ... Options>
	constexpr auto makeOutputPin(
		Board board,
		Pin<portNo, pinNo>,
		Options && ... opts)
	{
		constexpr auto gpioX = board.getPeripheral(PeripheralTypes::GPIO<portNo>);

        auto options = opt::makeOptionSet(
			opt::fields(detail::OUTPUT_TYPE, detail::SPEED, detail::PUPD),
			std::forward<Options>(opts)...);

        auto speed   = opt::getOrDefault(options, detail::SPEED, Speed::LOW);
        auto outType = opt::getOrDefault(options, detail::OUTPUT_TYPE, OutType::PUSH_PULL);
        auto pupd    = opt::getOrDefault(options, detail::PUPD, PuPd::NO_PULL);

        detail::OutputPinConfig<portNo, pinNo,
			hana::value(speed),
			hana::value(pupd),
			hana::value(outType)>::apply(gpioX);

		return OutputPin<decltype(gpioX), portNo, pinNo>{};
	}

	template<class Board,
		std::uint8_t portNo,
		std::uint8_t pinNo,
		class ... Options>
	auto makeInputPin(Board board, Pin<portNo, pinNo>, Options && ... opts)
	{
		constexpr auto gpioX = board.getPeripheral(PeripheralTypes::GPIO<portNo>);

        auto options = opt::makeOptionSet(opt::fields(detail::PUPD), std::forward<Options>(opts)...);
        auto pupd = opt::getOrDefault(options, detail::PUPD, PuPd::NO_PULL);

        detail::InputPinConfig<portNo, pinNo, hana::value(pupd)>::apply(gpioX);

		return InputPin<decltype(gpioX), portNo, pinNo>{};
	}

	template<class Board,std::uint8_t portNo, std::uint8_t pinNo>
	void makeAnalogPin(Board board, Pin<portNo, pinNo>)
	{
        detail::AnalogPinConfig<portNo, pinNo>::apply(board.getPeripheral(PeripheralTypes::GPIO<portNo>));
    }

	template<class Board, std::uint8_t portNo, std::uint8_t pinNo, class ... Options>
	void makeAltFnPin(Board board, Pin<portNo, pinNo>, Options && ... opts)
	{
		constexpr auto gpioX = board.getPeripheral(PeripheralTypes::GPIO<portNo>);

		auto options = opt::makeOptionSet(
			opt::fields(detail::OUTPUT_TYPE, detail::SPEED, detail::PUPD, detail::ALT_FUNCTION),
			std::forward<Options>(opts)...);

        auto speed   = opt::getOrDefault(options, detail::SPEED, Speed::LOW);
        auto outType = opt::getOrDefault(options, detail::OUTPUT_TYPE, OutType::PUSH_PULL);
        auto pupd    = opt::getOrDefault(options, detail::PUPD, PuPd::NO_PULL);
		auto altFn   = opt::get(options, detail::ALT_FUNCTION);

        detail::AltFnPinConfig<portNo, pinNo,
            hana::value(altFn),
            hana::value(speed),
            hana::value(pupd),
            hana::value(outType)>::apply(gpioX);
	}

	template<class Board, std::uint8_t portNo, std::uint8_t pinNo, class ... Options>
	auto makeInputInterruptPin(Board board, Pin<portNo, pinNo>, Options && ... opts)
	{
		constexpr auto gpioX = board.getPeripheral(PeripheralTypes::GPIO<portNo>);
		constexpr auto exti = board.getPeripheral(PeripheralTypes::EXTI);
		constexpr auto syscfg = board.getPeripheral(PeripheralTypes::SYSCFG);

		auto options = opt::makeOptionSet(
			opt::fields(detail::PUPD, detail::INTERRUPT),
			std::forward<Options>(opts)...);

		static_assert(hana::contains(options, detail::INTERRUPT), "Interrupt type must be supplied");

        auto pupd = opt::getOrDefault(options, detail::PUPD, PuPd::NO_PULL);
		auto interruptType = opt::get(options, detail::INTERRUPT);
		using InterruptConfig = typename decltype(interruptType)::type;

		detail::InputPinConfig<portNo, pinNo, hana::value(pupd)>::apply(gpioX);
		detail::InterruptBaseConfig::apply(exti, syscfg, uint8_c<portNo>, uint8_c<pinNo>);
		InterruptConfig::apply(exti, syscfg, uint8_c<portNo>, uint8_c<pinNo>);

		constexpr auto interrupt = detail::getInterrupt(uint8_c<pinNo>);
		board.enableIRQ(interrupt);

		return InputInterruptPin<
			decltype(gpioX),
			decltype(exti),
			portNo,
			pinNo>(board.getInterruptEvent(interrupt));
	}

}

ext: hpp

max_stars_repo_name: niclasberg/asfw

max_issues_repo_name: niclasberg/asfw

max_forks_repo_name: niclasberg/asfw


text:
/*
 * FileManager.hpp
 *
 *  Created on: 20/apr/2013
 *      Author: alessandro
 */

#ifndef FILEMANAGER_HPP_
#define FILEMANAGER_HPP_

#include "DatabaseHandler.hpp"
#include "Parameters.h"
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <fstream>
#include <map>

using namespace std;
using namespace boost::filesystem;
using namespace boost;

/*
 * Class FileManager
 */
class FileManager {
public:
	typedef boost::filesystem::path path;

	/*
	 * Saves the registered pdf map
	 */
	static void saveRegisteredPdfMap(const RegisteredPdfMap& registeredPdfMap, const path& registeredPdfMapPath) {
		std::ofstream ofs(registeredPdfMapPath.c_str());
		if (ofs.is_open()) {
			archive::binary_oarchive oa(ofs);

			// Saves the map
			oa << registeredPdfMap;
		} else {
			throw std::logic_error(string("ERROR: Failed to open file \"") + registeredPdfMapPath.string());
		}
	}

	/*
	 * Loads the registered pdf map
	 */
	static void loadRegisteredPdfMap(RegisteredPdfMap& registeredPdfMap, const path& registeredPdfMapPath) {
		if (exists(registeredPdfMapPath)) {
			std::ifstream ifs(registeredPdfMapPath.c_str(), std::ios::binary);
			archive::binary_iarchive ia(ifs);

			// Loads the map
			ia >> registeredPdfMap;
		}
	}

	/*
	 * Saves the converted pdf map
	 */
	static void saveConvertedPdfMap(const ConvertedPdfMap& convertedPdfMap, const path& convertedPdfMapPath) {
		std::ofstream ofs(convertedPdfMapPath.c_str());
		if (ofs.is_open()) {
			archive::binary_oarchive oa(ofs);

			// Saves the map
			oa << convertedPdfMap;
		} else {
			throw std::logic_error(string("ERROR: Failed to open file \"") + convertedPdfMapPath.string());
		}
	}

	/*
	 * Loads the converted pdf map
	 */
	static void loadConvertedPdfMap(ConvertedPdfMap& convertedPdfMap, const path& convertedPdfMapPath) {
		if (exists(convertedPdfMapPath)) {
			std::ifstream ifs(convertedPdfMapPath.c_str(), std::ios::binary);
			archive::binary_iarchive ia(ifs);

			// Loads the map
			ia >> convertedPdfMap;
		}
	}

	/*
	 * Checks if filePath is a pdf
	 */
	static bool isPdf(const path& filePath) {
		string extension = filePath.extension().string();
		to_lower(extension);
		if (is_regular_file(filePath) && (extension == ".pdf" || extension == ".PDF")) {
			return true;
		} else
			return false;
	}

	/*
	 * Checks if filePath is a jpg
	 */
	static bool isJpg(const path& filePath) {
		string extension = filePath.extension().string();
		to_lower(extension);
		if (is_regular_file(filePath) && (extension == ".jpg" || extension == ".JPG")) {
			return true;
		} else
			return false;
	}

	/*
	 * Validates parentPath and creates subdirectories
	 */
	static void parentPathValidation(const path& parentPath, const path& childPath) {
		if (!exists(parentPath)) {
			if (!create_directory(parentPath)) {
				throw std::logic_error(
						string("ERROR: can't create directory \"") + parentPath.string() + string(
								"\". Permission denied"));
			} else {
				std::cout << "\tCreated directory \"" << parentPath.string() << "\"\n";
			}
		}

		if (!exists(childPath)) {
			if (!create_directory(childPath)) {
				throw std::logic_error(
						string("ERROR: can't create directory \"") + childPath.string() + string(
								"\". Permission denied"));
			} else {
				std::cout << "\tCreated directory \"" << childPath.string() << "\"\n";
			}
		}
	}

	/*
	 * Validates path and creates directory
	 */
	static void pathValidation(const path& path) {
		if (!exists(path)) {
			if (!create_directory(path)) {
				throw std::logic_error(
						string("ERROR: can't create directory \"") + path.string() + string("\". Permission denied"));
			} else {
				std::cout << "\tCreated directory \"" << path.string() << "\"\n";
			}
		}
	}

	/*
	 * Creates a list of pdf from pdfPath
	 */
	static void getPdfListFromPath(const path& pdfPath, list<string>& pdfList) {
		if (exists(pdfPath)) {
			if (is_directory(pdfPath)) {
				directory_iterator endItr;
				for (directory_iterator beginItr(pdfPath); beginItr != endItr; ++beginItr) {
					if (FileManager::isPdf(beginItr->path())) {
						std::string filePath = (absolute(beginItr->path())).string();
						pdfList.push_back(filePath);
					}
				}
			} else if (FileManager::isPdf(pdfPath)) {
				std::string filePath = pdfPath.string();
				pdfList.push_back(filePath);
			} else {
				throw std::logic_error(
						string("ERROR: \"") + pdfPath.string() + string("\" is not a directory or a PDF"));
			}
		} else {
			throw std::logic_error(string("ERROR: path \"") + pdfPath.string() + string("\" does not exists"));
		}
	}

	/*
	 * Creates a list of jpg from jpgPath
	 */
	static void getJpgVectorFromPath(const path& jpgPath, vector<string>& jpgList) {
		if (exists(jpgPath)) {
			if (is_directory(jpgPath)) {
				directory_iterator endItr;
				for (directory_iterator beginItr(jpgPath); beginItr != endItr; ++beginItr) {
					if (FileManager::isJpg(beginItr->path())) {
						std::string filePath = (absolute(beginItr->path())).string();
						jpgList.push_back(filePath);
					}
				}
			} else if (FileManager::isJpg(jpgPath)) {
				std::string filePath = jpgPath.string();
				jpgList.push_back(filePath);
			} else {
				throw std::logic_error(
						string("ERROR: \"") + jpgPath.string() + string("\" is not a directory or a JPG"));
			}
		} else {
			throw std::logic_error(string("ERROR: path \"") + jpgPath.string() + string("\" does not exists"));
		}

		sort(jpgList.begin(), jpgList.end());
	}

	/*
	 * Calls system for a conversion from pdf to a list of jpg images
	 */
	static int createImagesFromPdf(const string pdfPath, const string jpgPath, list<string>& jpgList) {
		int numPages = 0;

		// Utils
		string jpgFileName = path(jpgPath).stem().string(); // Without extension
		string jpgExtension = path(jpgPath).extension().string();
		string jpgParentPath = path(jpgPath).parent_path().string();

		// Pdf info command definition
		stringstream pagesCommand;
		pagesCommand << "pdfinfo " << pdfPath << " | grep Pages: | awk '{print $2}'";
		string verbose = Utils::executeAndGetVerbose(pagesCommand.str());

		try {
			// Erasing line feed and carriage return
			verbose.erase(remove(verbose.begin(), verbose.end(), '\n'), verbose.end());
			verbose.erase(remove(verbose.begin(), verbose.end(), '\r'), verbose.end());

			numPages = boost::lexical_cast<int>(verbose);
		} catch (boost::bad_lexical_cast const&) {
			std::cout << "ERROR: input string \"" << verbose << "\" was not valid";
		}

		// Checks if the pdf was already converted
		bool alreadyConverted = true;
		int i = 0;
		while (i < numPages && alreadyConverted != false) {
			stringstream convertedJpgFilePath;
			convertedJpgFilePath << jpgParentPath << "/" << jpgFileName << "-" << i << jpgExtension;

			if (!exists(convertedJpgFilePath.str())) {
				alreadyConverted = false;
			}
			i++;
		}

		if (!alreadyConverted) {
			// Conversion command definition
			stringstream conversionCommand;
			conversionCommand << "convert -colorspace RGB -interlace none ";
			conversionCommand << "-density " << Parameters::CONVERT_DENSITY << "x" << Parameters::CONVERT_DENSITY
					<< " ";
			conversionCommand << "-quality " << Parameters::CONVERT_QUALITY << " ";

			conversionCommand << pdfPath << " " << jpgPath;

			int errorCode = std::system(conversionCommand.str().c_str());

			// If the conversion ends successfully, creates list of jpg pages
			if (errorCode != 0) {
				numPages = 0;
			}
		}

		// Creating the list of converted jpg
		for (int i = 0; i < numPages; i++) {
			stringstream jpgFilePath;
			jpgFilePath << jpgParentPath << "/" << jpgFileName << "-" << i << jpgExtension;

			// Final check and push
			if (exists(path(jpgFilePath.str()))) {
				jpgList.push_back(jpgFilePath.str());
			}
		}

		return numPages;
	}

	/*
	 * Calls system for a conversion from a list of jpg images to a pdf
	 */
	static int createPdfFromImages(const path& imagesPrefixPath, const path& pdfPath) {
		// Conversion command definition
		stringstream conversionCommand;
		conversionCommand << "convert ";
		conversionCommand << imagesPrefixPath.string() << "*" << ".jpg" << " " << pdfPath.string();

		int errorCode = std::system(conversionCommand.str().c_str());

		return errorCode;
	}

	/*
	 * Loads calibrated annotation thresholds from a file
	 */
	static void loadAnnotationThresholds(const string calibrationFilePath) {
		FileStorage fs(calibrationFilePath, FileStorage::READ);

		if (fs.isOpened()) {
			int numberOfChannels;
			fs["number"] >> numberOfChannels;

			for (int ch = 0; ch < numberOfChannels; ch++) {
				Parameters::ThresholdStructure ts;

				stringstream ss;
				ss << "thresh" << "_" << ch;

				fs[ss.str()]["channel_id"] >> ts.channel;
				fs[ss.str()]["thresh"]["first"] >> ts.thresh.first;
				fs[ss.str()]["thresh"]["second"] >> ts.thresh.second;

				Parameters::annotationThresholds.push_back(ts);
			}
		} else {
			throw std::logic_error(string("ERROR: Can't open thresholds file \"") + calibrationFilePath);
		}
	}
};

#endif /* FILEMANAGER_HPP_ */

max_stars_repo_name: marcorighini/tbdannotation

max_issues_repo_name: marcorighini/tbdannotation

max_forks_repo_name: marcorighini/tbdannotation