diff --git a/.gitignore b/.gitignore index 259148f..a2ec5b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +# Created by https://www.gitignore.io/api/c++,latex,cmake,netbeans,qtcreator,visualstudio # Prerequisites *.d @@ -30,3 +31,597 @@ *.exe *.out *.app + +### CMake ### +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +build + +### LaTeX ### +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.toc +*.fmt +*.fot +*.cb +*.cb2 + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync +*Notes.bib + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Comment the next line if you want to keep your tikz graphics files +*.tikz +*-tikzDictionary + +# listings +*.lol + +# makeidx +*.idx +*.ilg +*.ind +*.ist + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlo + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# easy-todo +*.lod + +# xindy +*.xdy + +# xypic precompiled matrices +*.xyc + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# Kile +*.backup + +# KBibTeX +*~[0-9]* + +# auto folder when using emacs and auctex +/auto/* + +# expex forward references with \gathertags +*-tags.tex + +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +### QtCreator ### +# gitignore for Qt Creator like IDE for pure C/C++ project without Qt +# +# Reference: http://doc.qt.io/qtcreator/creator-project-generic.html + + + +# Qt Creator autogenerated files + + +# A listing of all the files included in the project +*.files + +# Include directories +*.includes + +# Project configuration settings like predefined Macros +*.config + +# Qt Creator settings +*.creator + +# User project settings +*.creator.user* + +# Qt Creator backups +*.autosave + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Uncomment the next line to ignore your web deploy settings. +# By default, sensitive information, such as encrypted password +# should be stored in the .pubxml.user file. +#*.pubxml +*.pubxml.user +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +### VisualStudio Patch ### +# By default, sensitive information, such as encrypted password +# should be stored in the .pubxml.user file. + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# End of https://www.gitignore.io/api/c++,latex,cmake,netbeans,qtcreator,visualstudio diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..60a8f13 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.9) +project(networks) + +set(TBB_DIR lib/tbb) +set(CMAKE_CXX_STANDARD 11) + +include(cmake/TBBGet.cmake) +tbb_get(TBB_ROOT tbb_root CONFIG_DIR TBB_DIR) +find_package(TBB) + +add_subdirectory(server/) +add_subdirectory(client/) +add_subdirectory(common/) \ No newline at end of file diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100644 index 0000000..fc180cc --- /dev/null +++ b/client/CMakeLists.txt @@ -0,0 +1,5 @@ +project(client) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + +add_executable(client src/main.cpp ../common/src/Message.cpp ../common/include/Message.h ../common/include/ctpl.h src/ChatClient.cpp include/ChatClient.h include/utils.h) \ No newline at end of file diff --git a/client/include/ChatClient.h b/client/include/ChatClient.h new file mode 100644 index 0000000..1067d65 --- /dev/null +++ b/client/include/ChatClient.h @@ -0,0 +1,41 @@ +// +// Created by Владислав Калинин on 11/02/2019. +// + +#ifndef NETWORKS_CHATCLIENT_H +#define NETWORKS_CHATCLIENT_H + + +#include "../../common/include/Message.h" + +class ChatClient { +public: + ChatClient(const char *hostname, uint16_t port); + + void registry(std::string name, std::string password); + + void login(std::string name, std::string password); + + void sendBroadcastMessage(std::string message); + + void sendPrivateMessage(std::string reciever, std::string message); + + void kickUser(std::string name); + + void close_client(); + + TextMessage *recieveMessage(); + +private: + int sockfd; + std::string username = ""; + + void authorization(std::string msg); + + void sendMessage(std::string msg); + + char *recvMessage(); +}; + + +#endif //NETWORKS_CHATCLIENT_H diff --git a/client/include/utils.h b/client/include/utils.h new file mode 100644 index 0000000..a5ff6e3 --- /dev/null +++ b/client/include/utils.h @@ -0,0 +1,30 @@ +// +// Created by Владислав Калинин on 11/02/2019. +// + +#ifndef NETWORKS_UTILS_H +#define NETWORKS_UTILS_H + +#include + +void split(const std::string &txt, std::vector &strs, char delimetr) { + size_t pos = txt.find(delimetr); + size_t initialPos = 0; + strs.clear(); + + while (pos != std::string::npos) { + strs.push_back(txt.substr(initialPos, pos - initialPos)); + initialPos = pos + 1; + + pos = txt.find(delimetr, initialPos); + } + + strs.push_back(txt.substr(initialPos, std::min(pos, txt.size()) - initialPos + 1)); +} + +std::string firstWord(const std::string &txt) { + size_t pos = txt.find(' '); + return txt.substr(0, pos); +} + +#endif //NETWORKS_UTILS_H diff --git a/client/src/ChatClient.cpp b/client/src/ChatClient.cpp new file mode 100644 index 0000000..2ca520e --- /dev/null +++ b/client/src/ChatClient.cpp @@ -0,0 +1,104 @@ +// +// Created by Владислав Калинин on 11/02/2019. +// + +#include +#include +#include +#include +#include +#include "../include/ChatClient.h" +#include "../../common/include/constants.h" +#include "../../common/include/Message.h" + +ChatClient::ChatClient(const char *hostname, uint16_t port) { + sockaddr_in serv_addr; + hostent *server; + + this->sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (this->sockfd < 0) { + perror("ERROR opening socket"); + exit(1); + } + + server = gethostbyname(hostname); + + if (server == nullptr) { + fprintf(stderr, "ERROR, no such host\n"); + exit(0); + } + + bzero((char *) &serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + bcopy(server->h_addr, (char *) &serv_addr.sin_addr.s_addr, (size_t) server->h_length); + serv_addr.sin_port = htons(port); + + if (connect(this->sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR connecting"); + exit(1); + } +} + +void ChatClient::registry(std::string name, std::string password) { + std::string msg = RegistryMessage(name, password).to_json_format(); + authorization(msg); +} + +void ChatClient::login(std::string name, std::string password) { + if (this->username == "") { + std::string msg = AuthenticationMessage(name, password).to_json_format(); + authorization(msg); + this->username = name; + } else { + throw std::runtime_error("client already authentication"); + } +} + +void ChatClient::authorization(std::string msg) { + sendMessage(msg); + char *responseBuffer = recvMessage(); + json response = json::parse(responseBuffer); + delete[] responseBuffer; + if (response.at(STATUS).get() == STATUS_ERROR) { + throw std::runtime_error(response.at(CAUSE).get()); + } +} + +void ChatClient::sendMessage(std::string msg) { + std::string size = std::to_string(msg.size()); + write(sockfd, msg.c_str(), msg.size()); +} + +char *ChatClient::recvMessage() { + char *buffer = new char[1024]; + int size = read(sockfd, buffer, 1024); + buffer[size] = '\0'; + return buffer; +} + +void ChatClient::sendBroadcastMessage(std::string message) { + std::string msg = TextMessage(this->username, message).to_json_format(); + sendMessage(msg); +} + +TextMessage *ChatClient::recieveMessage() { + char *responseBuffer = recvMessage(); + json response = json::parse(responseBuffer); + delete[] responseBuffer; + return new TextMessage(response.at(SENDER).get(), response.at(MESSAGE).get()); +} + +void ChatClient::sendPrivateMessage(std::string reciever, std::string message) { + std::string msg = PrivateMessage(this->username, reciever, message).to_json_format(); + sendMessage(msg); +} + +void ChatClient::close_client() { + close(sockfd); +} + +void ChatClient::kickUser(std::string name) { + std::string msg = KickMessage(name).to_json_format(); + sendMessage(msg); +} \ No newline at end of file diff --git a/client/src/main.cpp b/client/src/main.cpp new file mode 100644 index 0000000..a4ebd4b --- /dev/null +++ b/client/src/main.cpp @@ -0,0 +1,127 @@ +#include +#include + +#include "../../common/include/json.hpp" +#include "../include/ChatClient.h" +#include "../include/utils.h" +#include "../../common/include/ctpl.h" +#include + +using namespace nlohmann; + +std::string register_format = "\tregister format: \\register "; +std::string login_format = "\tlogin format: \\login "; + +void authorization(ChatClient &client); + +std::string getUserName(std::string msg); + +void receiveResponses(int id, ChatClient &client); + +int main(int argc, char *argv[]) { + + if (argc < 3) { + fprintf(stderr, "usage %s hostname port\n", argv[0]); + exit(0); + } + + ChatClient client(argv[1], atoi(argv[2])); + authorization(client); + + ctpl::thread_pool thread_pool(1); + thread_pool.push(receiveResponses, client); + std::string msg; + + try { + while (true) { + getline(std::cin, msg); + if (msg == "") { + continue; + } + if (msg == "\\exit") { + break; + } + if (msg == "\\kick") { + std::cout << "please enter username: "; + std::string name; + std::cin >> name; + client.kickUser(name); + continue; + } + std::string username = getUserName(msg); + if (username == "") { + client.sendBroadcastMessage(msg); + } else { + client.sendPrivateMessage(username, msg); + } + } + } catch (std::exception e) { + std::cout << e.what() << std::endl; + } + + client.close_client(); + + return 0; +} + +void receiveResponses(int id, ChatClient &client) { + while (true) { + TextMessage *message = client.recieveMessage(); + std::cout << message->getSender() << ": " << message->getMessage() << std::endl; + delete message; + } +} + +std::string getUserName(std::string msg) { + std::string username = firstWord(msg); + if (username[0] == '@') { + return username.substr(1); + } + return ""; +} + +void authorization(ChatClient &client) { + std::cout << "please register or enter username and password:" << std::endl; + std::cout << register_format << std::endl; + std::cout << login_format << std::endl; + + std::string cmd_line; + std::string param; + std::vector params; + + while (true) { + getline(std::cin, cmd_line); + split(cmd_line, params, ' '); + if (params[0] == "\\register") { + if (params.size() != 3) { + std::cout << register_format << std::endl; + } else { + try { + client.registry(params[1], params[2]); + std::cout << "registration successfull" << std::endl; + } catch (std::runtime_error e) { + std::cout << e.what() << std::endl; + } catch (std::exception e) { + std::cout << e.what() << std::endl; + } + } + } else if (params[0] == "\\login") { + if (params.size() != 3) { + std::cout << login_format << std::endl; + } else { + try { + client.login(params[1], params[2]); + std::cout << "login successfull" << std::endl; + break; + } catch (std::runtime_error e) { + std::cout << e.what() << std::endl; + } catch (std::exception e) { + std::cout << e.what() << std::endl; + } + } + } else { + std::cout << "unknow command" << std::endl; + } + params.clear(); + } +} \ No newline at end of file diff --git a/cmake/TBBGet.cmake b/cmake/TBBGet.cmake new file mode 100644 index 0000000..9b9a885 --- /dev/null +++ b/cmake/TBBGet.cmake @@ -0,0 +1,298 @@ +# Copyright (c) 2017-2018 Intel Corporation +# +# 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(CMakeParseArguments) + +# Save the location of Intel TBB CMake modules here, as it will not be possible to do inside functions, +# see for details: https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_LIST_DIR.html +set(_tbb_cmake_module_path .) + +## +# Downloads file. +# +# Parameters: +# URL - URL to download data from; +# SAVE_AS - filename there to save downloaded data; +# INFO - text description of content to be downloaded; +# will be printed as message in format is "Downloading : ; +# FORCE - option to delete local file from SAVE_AS if it exists; +# +function(_tbb_download_file) + set(options FORCE) + set(oneValueArgs URL RELEASE SAVE_AS INFO) + cmake_parse_arguments(tbb_df "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (tbb_df_FORCE AND EXISTS "${tbb_df_SAVE_AS}") + file(REMOVE ${tbb_df_SAVE_AS}) + endif() + + if (NOT EXISTS "${tbb_df_SAVE_AS}") + set(_show_progress) + if (TBB_DOWNLOADING_PROGRESS) + set(_show_progress SHOW_PROGRESS) + endif() + + message(STATUS "Downloading ${tbb_df_INFO}: ${tbb_df_URL}") + file(DOWNLOAD ${tbb_df_URL} ${tbb_df_SAVE_AS} ${_show_progress} STATUS download_status) + + list(GET download_status 0 download_status_num) + if (NOT download_status_num EQUAL 0) + message(STATUS "Unsuccessful downloading: ${download_status}") + file(REMOVE ${tbb_df_SAVE_AS}) + return() + endif() + else() + message(STATUS "Needed file was found locally ${tbb_df_SAVE_AS}. Remove it if you still want to download a new one") + endif() +endfunction() + +## +# Checks if specified Intel TBB release is available on GitHub. +# +# tbb_check_git_release( ) +# Parameters: +# - release to be checked; +# - store result (TRUE/FALSE). +# +function(_tbb_check_git_release_tag _tbb_release_tag _tbb_release_tag_avail) + if (_tbb_release_tag STREQUAL LATEST) + set(${_tbb_release_tag_avail} TRUE PARENT_SCOPE) + return() + endif() + + set(tbb_releases_file "${CMAKE_CURRENT_BINARY_DIR}/tbb_releases.json") + + _tbb_download_file(URL "${tbb_github_api}/releases" + SAVE_AS ${tbb_releases_file} + INFO "information from GitHub about Intel TBB releases" + FORCE) + + if (NOT EXISTS "${tbb_releases_file}") + set(${_tbb_release_tag_avail} FALSE PARENT_SCOPE) + return() + endif() + + file(READ ${tbb_releases_file} tbb_releases) + + string(REPLACE "\"" "" tbb_releases ${tbb_releases}) + string(REGEX MATCHALL "tag_name: *([A-Za-z0-9_\\.]+)" tbb_releases ${tbb_releases}) + + set(_release_available FALSE) + foreach(tbb_rel ${tbb_releases}) + string(REGEX REPLACE "tag_name: *" "" tbb_rel_cut ${tbb_rel}) + list(REMOVE_ITEM tbb_releases ${tbb_rel}) + list(APPEND tbb_releases ${tbb_rel_cut}) + if (_tbb_release_tag STREQUAL tbb_rel_cut) + set(_release_available TRUE) + break() + endif() + endforeach() + + if (NOT _release_available) + string(REPLACE ";" ", " tbb_releases_str "${tbb_releases}") + message(STATUS "Requested release tag ${_tbb_release_tag} is not available. Available Intel TBB release tags: ${tbb_releases_str}") + endif() + + set(${_tbb_release_tag_avail} ${_release_available} PARENT_SCOPE) +endfunction() + +## +# Compares two Intel TBB releases and provides result +# TRUE if the first release is less than the second, FALSE otherwise. +# +# tbb_is_release_less( ) +# +function(_tbb_is_release_less rel1 rel2 result) + # Convert release to numeric representation to compare it using "if" with VERSION_LESS. + string(REGEX REPLACE "[A-Za-z]" "" rel1 "${rel1}") + string(REPLACE "_" "." rel1 "${rel1}") + string(REGEX REPLACE "[A-Za-z]" "" rel2 "${rel2}") + string(REPLACE "_" "." rel2 "${rel2}") + + if (${rel1} VERSION_LESS ${rel2}) + set(${result} TRUE PARENT_SCOPE) + return() + endif() + + set(${result} FALSE PARENT_SCOPE) +endfunction() + +## +# Finds exact URL to download Intel TBB basing on provided parameters. +# +# Usage: +# _tbb_get_url(URL RELEASE_TAG OS [SOURCE_CODE]) +# +function(_tbb_get_url) + set(oneValueArgs URL RELEASE_TAG OS) + set(options SOURCE_CODE) + cmake_parse_arguments(tbb_get_url "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(tbb_github_api "https://api.github.com/repos/01org/tbb") + + _tbb_check_git_release_tag(${tbb_get_url_RELEASE_TAG} tbb_release_available) + if (NOT tbb_release_available) + set(${tbb_download_FULL_PATH} ${tbb_download_FULL_PATH}-NOTFOUND PARENT_SCOPE) + return() + endif() + + if (tbb_get_url_RELEASE_TAG STREQUAL LATEST) + set(tbb_rel_info_api_url "${tbb_github_api}/releases/latest") + else() + set(tbb_rel_info_api_url "${tbb_github_api}/releases/tags/${tbb_get_url_RELEASE_TAG}") + endif() + + set(tbb_release_info_file "${CMAKE_CURRENT_BINARY_DIR}/tbb_${tbb_get_url_RELEASE_TAG}_info.json") + + _tbb_download_file(URL ${tbb_rel_info_api_url} + SAVE_AS ${tbb_release_info_file} + INFO "information from GitHub about packages for Intel TBB ${tbb_get_url_RELEASE_TAG}" + FORCE) + + if (NOT EXISTS "${tbb_release_info_file}") + set(${tbb_get_url_URL} ${tbb_get_url_URL}-NOTFOUND PARENT_SCOPE) + return() + endif() + + file(STRINGS ${tbb_release_info_file} tbb_release_info) + + if (tbb_get_url_SOURCE_CODE) + # Find name of the latest release to get link to source archive. + if (tbb_get_url_RELEASE_TAG STREQUAL LATEST) + string(REPLACE "\"" "" tbb_release_info ${tbb_release_info}) + string(REGEX REPLACE ".*tag_name: *([A-Za-z0-9_\\.]+).*" "\\1" tbb_get_url_RELEASE_TAG "${tbb_release_info}") + endif() + + set(${tbb_get_url_URL} "https://github.com/01org/tbb/archive/${tbb_get_url_RELEASE_TAG}.tar.gz" PARENT_SCOPE) + else() + if (tbb_get_url_OS MATCHES "Linux") + set(tbb_lib_archive_suffix lin.tgz) + elseif (tbb_get_url_OS MATCHES "Windows") + set(tbb_lib_archive_suffix win.zip) + elseif (tbb_get_url_OS MATCHES "Darwin") + set(tbb_lib_archive_suffix mac.tgz) + + # Since 2017_U4 release archive for Apple has suffix "mac.tgz" instead of "osx.tgz". + if (NOT tbb_get_url_RELEASE_TAG STREQUAL "LATEST") + _tbb_is_release_less(${tbb_get_url_RELEASE_TAG} 2017_U4 release_less) + if (release_less) + set(tbb_lib_archive_suffix osx.tgz) + endif() + endif() + elseif (tbb_get_url_OS MATCHES "Android") + set(tbb_lib_archive_suffix and.tgz) + else() + message(STATUS "Currently prebuilt Intel TBB is not available for your OS (${tbb_get_url_OS})") + set(${tbb_get_url_URL} ${tbb_get_url_URL}-NOTFOUND PARENT_SCOPE) + return() + endif() + + string(REGEX REPLACE ".*(https.*oss_${tbb_lib_archive_suffix}).*" "\\1" tbb_bin_url "${tbb_release_info}") + + set(${tbb_get_url_URL} ${tbb_bin_url} PARENT_SCOPE) + endif() +endfunction() + +function(tbb_get) + set(oneValueArgs RELEASE_TAG SYSTEM_NAME SAVE_TO TBB_ROOT CONFIG_DIR) + set(options SOURCE_CODE) + cmake_parse_arguments(tbb_get "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(tbb_os ${CMAKE_SYSTEM_NAME}) + if (tbb_get_SYSTEM_NAME) + set(tbb_os ${tbb_get_SYSTEM_NAME}) + endif() + + set(tbb_release_tag LATEST) + if (tbb_get_RELEASE_TAG) + set(tbb_release_tag ${tbb_get_RELEASE_TAG}) + endif() + + set(tbb_save_to ${CMAKE_CURRENT_BINARY_DIR}/tbb_downloaded) + if (tbb_get_SAVE_TO) + set(tbb_save_to ${tbb_get_SAVE_TO}) + endif() + + if (tbb_get_SOURCE_CODE) + _tbb_get_url(URL tbb_url RELEASE_TAG ${tbb_release_tag} OS ${tbb_os} SOURCE_CODE) + else() + _tbb_get_url(URL tbb_url RELEASE_TAG ${tbb_release_tag} OS ${tbb_os}) + endif() + + if (NOT tbb_url) + message(STATUS "URL to download Intel TBB has not been found") + set(${tbb_get_TBB_ROOT} ${tbb_get_TBB_ROOT}-NOTFOUND PARENT_SCOPE) + return() + endif() + + get_filename_component(filename ${tbb_url} NAME) + set(local_file "${CMAKE_CURRENT_BINARY_DIR}/${filename}") + + _tbb_download_file(URL ${tbb_url} + SAVE_AS ${local_file} + INFO "Intel TBB library") + + if (NOT EXISTS "${local_file}") + set(${tbb_get_TBB_ROOT} ${tbb_get_TBB_ROOT}-NOTFOUND PARENT_SCOPE) + return() + endif() + + get_filename_component(subdir_name ${filename} NAME_WE) + file(MAKE_DIRECTORY ${tbb_save_to}/${subdir_name}) + if (NOT EXISTS "${tbb_save_to}/${subdir_name}") + message(STATUS "${tbb_save_to}/${subdir_name} can not be created") + set(${tbb_get_TBB_ROOT} ${tbb_get_TBB_ROOT}-NOTFOUND PARENT_SCOPE) + return() + endif() + + message(STATUS "Unpacking ${local_file} to ${tbb_save_to}/${subdir_name}") + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${local_file} + WORKING_DIRECTORY ${tbb_save_to}/${subdir_name} + RESULT_VARIABLE unpacking_result) + + if (NOT unpacking_result EQUAL 0) + message(STATUS "Unsuccessful unpacking: ${unpacking_result}") + set(${tbb_get_TBB_ROOT} ${tbb_get_TBB_ROOT}-NOTFOUND PARENT_SCOPE) + return() + endif() + + file(GLOB_RECURSE tbb_h ${tbb_save_to}/${subdir_name}/*/include/tbb/tbb.h) + list(GET tbb_h 0 tbb_h) + + if (NOT EXISTS "${tbb_h}") + message(STATUS "tbb/tbb.h has not been found in the downloaded package") + set(${tbb_get_TBB_ROOT} ${tbb_get_TBB_ROOT}-NOTFOUND PARENT_SCOPE) + return() + endif() + + get_filename_component(tbb_root "${tbb_h}" PATH) + get_filename_component(tbb_root "${tbb_root}" PATH) + get_filename_component(tbb_root "${tbb_root}" PATH) + + if (NOT tbb_get_SOURCE_CODE) + set(tbb_config_dir ${tbb_root}/cmake) + + if (NOT EXISTS "${tbb_config_dir}") + tbb_make_config(TBB_ROOT ${tbb_root} CONFIG_DIR tbb_config_dir) + endif() + + set(${tbb_get_CONFIG_DIR} ${tbb_config_dir} PARENT_SCOPE) + endif() + + set(${tbb_get_TBB_ROOT} ${tbb_root} PARENT_SCOPE) +endfunction() \ No newline at end of file diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..8f3c576 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,5 @@ +project(common) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + +add_executable(common src/Message.cpp) diff --git a/common/include/Message.h b/common/include/Message.h new file mode 100644 index 0000000..b53e51f --- /dev/null +++ b/common/include/Message.h @@ -0,0 +1,92 @@ +// +// Created by Владислав Калинин on 11/02/2019. +// + +#ifndef NETWORKS_MESSAGE_H +#define NETWORKS_MESSAGE_H + +#include "json.hpp" + +using namespace nlohmann; + +class Message { +public: + virtual std::string to_json_format()= 0; +}; + +class RegistryMessage : public Message { +public: + RegistryMessage(std::string name_, std::string password_) : name(name_), password(password_) {} + + std::string to_json_format() override; + +private: + std::string name; + std::string password; +}; + +class AuthenticationMessage : public Message { +public: + AuthenticationMessage(std::string name_, std::string password_) : name(name_), password(password_) {} + + std::string to_json_format() override; + +private: + std::string name; + std::string password; +}; + +class ResponseMessage : public Message { +public: + ResponseMessage(int status_, std::string cause_) : status(status_), cause(cause_) {} + + ResponseMessage(int status_) : status(status_), cause("") {} + + std::string to_json_format() override; + +private: + int status; + std::string cause; +}; + +class TextMessage : public Message { +public: + TextMessage(std::string sender_, std::string message_) : sender(sender_), message(message_) {} + + std::string getSender() { + return sender; + } + + std::string getMessage() { + return message; + } + + std::string to_json_format() override; + +protected: + std::string sender; + std::string message; +}; + +class PrivateMessage : public TextMessage { +public: + PrivateMessage(std::string sender_, std::string reciever_, std::string message_) : TextMessage(sender_, message_), + reciever(reciever_) {} + + std::string to_json_format() override; + +private: + std::string reciever; +}; + +class KickMessage : public Message { +public: + explicit KickMessage(std::string name_) : name(name_) {} + + std::string to_json_format() override; + +private: + std::string name; +}; + +#endif //NETWORKS_MESSAGE_H diff --git a/common/include/constants.h b/common/include/constants.h new file mode 100644 index 0000000..2a9c431 --- /dev/null +++ b/common/include/constants.h @@ -0,0 +1,26 @@ +// +// Created by Владислав Калинин on 11/02/2019. +// + +#ifndef NETWORKS_JSONFIELDS_H +#define NETWORKS_JSONFIELDS_H + +#define NAME "name" +#define PASSWORD "password" +#define TASK_ID "task_id" +#define STATUS "status" +#define CAUSE "cause" +#define SENDER "sender" +#define RECEIVER "receiver" +#define MESSAGE "message" + +#define STATUS_OK 0 +#define STATUS_ERROR 1 + +#define REGISTRY_TASK 0 +#define AUTHORIZATION_TASK 1 +#define BROADCAST_TASK 2 +#define PRIVATE_TASK 3 +#define KICK_TASK 4 + +#endif //NETWORKS_JSONFIELDS_H diff --git a/common/include/ctpl.h b/common/include/ctpl.h new file mode 100644 index 0000000..36c18e4 --- /dev/null +++ b/common/include/ctpl.h @@ -0,0 +1,256 @@ +#ifndef NETWORKS_CTPL_H +#define NETWORKS_CTPL_H + +/********************************************************* +* +* Copyright (C) 2014 by Vitaliy Vitsentiy +* +* 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. +* +*********************************************************/ + + +#ifndef __ctpl_stl_thread_pool_H__ +#define __ctpl_stl_thread_pool_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +// thread pool to run user's functors with signature +// ret func(int id, other_params) +// where id is the index of the thread that runs the functor +// ret is some return type + + +namespace ctpl { + + namespace detail { + template + class Queue { + public: + bool push(T const & value) { + std::unique_lock lock(this->mutex); + this->q.push(value); + return true; + } + // deletes the retrieved element, do not use for non integral types + bool pop(T & v) { + std::unique_lock lock(this->mutex); + if (this->q.empty()) + return false; + v = this->q.front(); + this->q.pop(); + return true; + } + bool empty() { + std::unique_lock lock(this->mutex); + return this->q.empty(); + } + private: + std::queue q; + std::mutex mutex; + }; + } + + class thread_pool { + + public: + + thread_pool() { this->init(); } + thread_pool(int nThreads) { this->init(); this->resize(nThreads); } + + // the destructor waits for all the functions in the queue to be finished + ~thread_pool() { + this->stop(true); + } + + // get the number of running threads in the pool + int size() { return static_cast(this->threads.size()); } + + // number of idle threads + int n_idle() { return this->nWaiting; } + std::thread & get_thread(int i) { return *this->threads[i]; } + + // change the number of threads in the pool + // should be called from one thread, otherwise be careful to not interleave, also with this->stop() + // nThreads must be >= 0 + void resize(int nThreads) { + if (!this->isStop && !this->isDone) { + int oldNThreads = static_cast(this->threads.size()); + if (oldNThreads <= nThreads) { // if the number of threads is increased + this->threads.resize(nThreads); + this->flags.resize(nThreads); + + for (int i = oldNThreads; i < nThreads; ++i) { + this->flags[i] = std::make_shared>(false); + this->set_thread(i); + } + } + else { // the number of threads is decreased + for (int i = oldNThreads - 1; i >= nThreads; --i) { + *this->flags[i] = true; // this thread will finish + this->threads[i]->detach(); + } + { + // stop the detached threads that were waiting + std::unique_lock lock(this->mutex); + this->cv.notify_all(); + } + this->threads.resize(nThreads); // safe to delete because the threads are detached + this->flags.resize(nThreads); // safe to delete because the threads have copies of shared_ptr of the flags, not originals + } + } + } + + // empty the queue + void clear_queue() { + std::function * _f; + while (this->q.pop(_f)) + delete _f; // empty the queue + } + + // pops a functional wrapper to the original function + std::function pop() { + std::function * _f = nullptr; + this->q.pop(_f); + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + std::function f; + if (_f) + f = *_f; + return f; + } + + // wait for all computing threads to finish and stop all threads + // may be called asynchronously to not pause the calling thread while waiting + // if isWait == true, all the functions in the queue are run, otherwise the queue is cleared without running the functions + void stop(bool isWait = false) { + if (!isWait) { + if (this->isStop) + return; + this->isStop = true; + for (int i = 0, n = this->size(); i < n; ++i) { + *this->flags[i] = true; // command the threads to stop + } + this->clear_queue(); // empty the queue + } + else { + if (this->isDone || this->isStop) + return; + this->isDone = true; // give the waiting threads a command to finish + } + { + std::unique_lock lock(this->mutex); + this->cv.notify_all(); // stop all waiting threads + } + for (int i = 0; i < static_cast(this->threads.size()); ++i) { // wait for the computing threads to finish + if (this->threads[i]->joinable()) + this->threads[i]->join(); + } + // if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads + // therefore delete them here + this->clear_queue(); + this->threads.clear(); + this->flags.clear(); + } + + template + auto push(F && f, Rest&&... rest) ->std::future { + auto pck = std::make_shared>( + std::bind(std::forward(f), std::placeholders::_1, std::forward(rest)...) + ); + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + return pck->get_future(); + } + + // run the user's function that excepts argument int - id of the running thread. returned value is templatized + // operator returns std::future, where the user can get the result and rethrow the catched exceptins + template + auto push(F && f) ->std::future { + auto pck = std::make_shared>(std::forward(f)); + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + return pck->get_future(); + } + + + private: + + // deleted + thread_pool(const thread_pool &);// = delete; + thread_pool(thread_pool &&);// = delete; + thread_pool & operator=(const thread_pool &);// = delete; + thread_pool & operator=(thread_pool &&);// = delete; + + void set_thread(int i) { + std::shared_ptr> flag(this->flags[i]); // a copy of the shared ptr to the flag + auto f = [this, i, flag/* a copy of the shared ptr to the flag */]() { + std::atomic & _flag = *flag; + std::function * _f; + bool isPop = this->q.pop(_f); + while (true) { + while (isPop) { // if there is anything in the queue + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + (*_f)(i); + if (_flag) + return; // the thread is wanted to stop, return even if the queue is not empty yet + else + isPop = this->q.pop(_f); + } + // the queue is empty here, wait for the next command + std::unique_lock lock(this->mutex); + ++this->nWaiting; + this->cv.wait(lock, [this, &_f, &isPop, &_flag](){ isPop = this->q.pop(_f); return isPop || this->isDone || _flag; }); + --this->nWaiting; + if (!isPop) + return; // if the queue is empty and this->isDone == true or *flag then return + } + }; + this->threads[i].reset(new std::thread(f)); // compiler may not support std::make_unique() + } + + void init() { this->nWaiting = 0; this->isStop = false; this->isDone = false; } + + std::vector> threads; + std::vector>> flags; + detail::Queue *> q; + std::atomic isDone; + std::atomic isStop; + std::atomic nWaiting; // how many threads are waiting + + std::mutex mutex; + std::condition_variable cv; + }; + +} + +#endif // __ctpl_stl_thread_pool_H__ + +#endif //NETWORKS_CTPL_H diff --git a/common/include/json.hpp b/common/include/json.hpp new file mode 100644 index 0000000..665d0f0 --- /dev/null +++ b/common/include/json.hpp @@ -0,0 +1,20406 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.5.0 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2018 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 5 +#define NLOHMANN_JSON_VERSION_PATCH 0 + +#include // all_of, find, for_each +#include // assert +#include // and, not, or +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#include // istream, ostream +#include // random_access_iterator_tag +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap + +// #include +#ifndef NLOHMANN_JSON_FWD_HPP +#define NLOHMANN_JSON_FWD_HPP + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer> +class basic_json; + +/*! +@brief JSON Pointer + +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + +@since version 2.0.0 +*/ +template +class json_pointer; + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. + +@since version 1.0.0 +*/ +using json = basic_json<>; +} // namespace nlohmann + +#endif + +// #include + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// manual branch prediction +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_LIKELY(x) __builtin_expect(!!(x), 1) + #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else + #define JSON_LIKELY(x) x + #define JSON_UNLIKELY(x) x +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// #include + + +#include // not +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // not +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval + +// #include + +// #include + + +#include // random_access_iterator_tag + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} +} + +// #include + +// #include + + +#include + +// #include + + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + void operator=(nonesuch const&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template