Split .hpp into .h and .cpp

This commit is contained in:
EntireTwix 2021-06-10 01:57:18 -07:00
parent e32dbbae03
commit 84f934f39d
19 changed files with 692 additions and 615 deletions

View file

@ -11,7 +11,15 @@ set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
find_package(Threads)
add_executable(${PROJECT_NAME} main.cpp include/xxhash.c)
add_executable(${PROJECT_NAME} main.cpp )
target_sources(${PROJECT_NAME} PRIVATE
src/bank_f.cpp
src/bank.cpp
src/log.cpp
src/transaction.cpp
src/user.cpp
src/xxhash.c
)
target_include_directories(${PROJECT_NAME} PUBLIC include)
target_include_directories(${PROJECT_NAME} PUBLIC third_party)

View file

@ -14,8 +14,7 @@ CCash solves these issues and adds a level of abstraction, the main philosophy o
## Build
drogon depedencies
drogon depedencies (varies by OS/distro)
```
sudo apt install libjsoncpp-dev uuid-dev openssl libssl-dev zlib1g-dev
```

61
include/bank.h Normal file
View file

@ -0,0 +1,61 @@
#pragma once
#include <fstream>
#include <iostream>
#include <shared_mutex>
#include "error_responses.hpp"
#include "parallel-hashmap/parallel_hashmap/phmap.h"
#include "user.h"
class Bank
{
private:
phmap::parallel_flat_hash_map<
std::string, User,
phmap::priv::hash_default_hash<std::string>,
phmap::priv::hash_default_eq<std::string>,
phmap::priv::Allocator<phmap::priv::Pair<const std::string, User>>,
4UL,
std::mutex>
users;
/**
* @brief size_l should be grabbed if the operation MODIFIES the size (shared), this is so that when save claims unique
*
*/
std::shared_mutex size_l;
/**
* @brief send_funds_l should be grabbed if balances are being MODIFIED (shared) or if an operation needs to READ without the intermediary states that sendfunds has (unique)
*
*/
std::shared_mutex send_funds_l;
public:
std::string admin_pass;
int_fast8_t AddUser(const std::string &name, std::string &&init_pass);
int_fast8_t AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass);
int_fast8_t DelUser(const std::string &name, const std::string &attempt);
int_fast8_t AdminDelUser(const std::string &name, const std::string &attempt);
int_fast8_t SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount, const std::string &attempt);
bool Contains(const std::string &name) const;
bool AdminVerifyPass(const std::string &attempt);
int_fast8_t SetBal(const std::string &name, const std::string &attempt, uint32_t amount);
int_fast64_t GetBal(const std::string &name) const;
int_fast8_t VerifyPassword(const std::string &name, const std::string &attempt) const;
int_fast8_t ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass);
Json::Value GetLogs(const std::string &name, const std::string &attempt);
void Save();
//NOT THREAD SAFE, BY NO MEANS SHOULD THIS BE CALLED WHILE RECEIEVING REQUESTS
void Load();
};
//TODO make branchless

View file

@ -1,329 +0,0 @@
#pragma once
#include <fstream>
#include <shared_mutex>
#include "xxhash.h"
#include "error_responses.hpp"
#include "parallel-hashmap/parallel_hashmap/phmap.h"
#include "user.hpp"
class
{
private:
phmap::parallel_flat_hash_map<
std::string, User,
phmap::priv::hash_default_hash<std::string>,
phmap::priv::hash_default_eq<std::string>,
phmap::priv::Allocator<phmap::priv::Pair<const std::string, User>>,
4UL,
std::mutex>
users;
/**
* @brief size_l should be grabbed if the operation MODIFIES the size (shared), this is so that when save claims unique
*
*/
std::shared_mutex size_l;
/**
* @brief send_funds_l should be grabbed if balances are being MODIFIED (shared) or if an operation needs to READ without the intermediary states that sendfunds has (unique)
*
*/
std::shared_mutex send_funds_l;
public:
std::string admin_pass;
int_fast8_t AddUser(const std::string &name, std::string &&init_pass)
{
if (name.size() > max_name_size)
{
return ErrorResponse::NameTooLong;
}
{
std::shared_lock<std::shared_mutex> lock{size_l};
if (!users.try_emplace_l(
name, [](User &) {}, std::move(init_pass)))
{
return ErrorResponse::UserAlreadyExists;
}
else
{
return true;
}
}
}
int_fast8_t AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass)
{
if (name.size() > max_name_size)
{
return ErrorResponse::NameTooLong;
}
if (admin_pass != attempt)
{
return ErrorResponse::WrongAdminPassword;
}
{
std::shared_lock<std::shared_mutex> lock{size_l};
if (!users.try_emplace_l(
name, [](User &) {}, init_bal, std::move(init_pass)))
{
return ErrorResponse::UserAlreadyExists;
}
else
{
return true;
}
}
}
int_fast8_t DelUser(const std::string &name, const std::string &attempt)
{
std::shared_lock<std::shared_mutex> lock{size_l};
bool state = false;
if (!users.erase_if(name, [&state, &attempt](User &u) { return state = (XXH3_64bits(attempt.data(), attempt.size()) == u.password); }))
{
return ErrorResponse::UserNotFound;
}
else
{
return state * ErrorResponse::WrongPassword;
}
}
int_fast8_t AdminDelUser(const std::string &name, const std::string &attempt)
{
std::shared_lock<std::shared_mutex> lock{size_l};
bool state = false;
if (!users.erase_if(name, [&state, this, &attempt](const User &) { return state = (admin_pass == attempt); }))
{
return ErrorResponse::UserNotFound;
}
else
{
return state * ErrorResponse::WrongAdminPassword;
}
}
int_fast8_t SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount, const std::string &attempt)
{
//cant send money to self, from self or amount is 0
if (a_name == b_name || !amount)
{
return ErrorResponse::InvalidRequest;
}
int_fast8_t state = false;
{
std::shared_lock<std::shared_mutex> lock{send_funds_l}; //because SendFunds requires 3 locking operations
if (users.modify_if(a_name, [&state, amount, &attempt](User &a) {
//if A exists, A can afford it, and A's password matches
if (a.balance < amount)
{
state = ErrorResponse::InsufficientFunds;
}
else
{
if (a.password != XXH3_64bits(attempt.data(), attempt.size()))
{
state = ErrorResponse::WrongPassword;
}
else
{
a.balance -= amount;
state = true;
}
}
}))
{
return ErrorResponse::UserNotFound;
}
else
{
if (!state)
{
return state;
}
else
{
//if B doesnt exist
if (!users.modify_if(b_name, [amount](User &b) {
b.balance += amount;
}))
{
//attempt to refund if A exist
users.modify_if(a_name, [amount](User &a) {
a.balance += amount;
});
return ErrorResponse::UserNotFound; //because had to refund transaction
}
else
{
if constexpr (max_log_size)
{
Transaction temp(a_name, b_name, amount);
Transaction temp2 = temp;
users.modify_if(a_name, [&temp](User &a) {
a.log.AddTrans(std::move(temp));
});
users.modify_if(b_name, [&temp2](User &b) {
b.log.AddTrans(std::move(temp2));
});
}
return true;
}
}
}
}
}
bool Contains(const std::string &name) const
{
return users.contains(name);
}
bool AdminVerifyPass(const std::string &attempt)
{
return (admin_pass != attempt);
}
int_fast8_t SetBal(const std::string &name, const std::string &attempt, uint32_t amount)
{
if (admin_pass != attempt)
{
return ErrorResponse::WrongAdminPassword;
}
else
{
if (!users.modify_if(name, [amount](User &u) {
u.balance = amount;
}))
{
return ErrorResponse::UserNotFound;
}
else
{
return true;
}
}
}
int_fast64_t GetBal(const std::string &name) const
{
int_fast64_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res](const User &u) {
res = u.balance;
});
return res;
}
int_fast8_t VerifyPassword(const std::string &name, const std::string &attempt) const
{
int_fast8_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res, &attempt](const User &u) {
res = u.password == XXH3_64bits(attempt.data(), attempt.size());
});
return res;
}
int_fast8_t ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass)
{
int_fast8_t res = ErrorResponse::UserNotFound;
users.modify_if(name, [&res, &attempt, &new_pass](User &u) {
if (u.password != XXH3_64bits(attempt.data(), attempt.size()))
{
res = ErrorResponse::WrongPassword;
}
else
{
u.password = XXH3_64bits(new_pass.data(), new_pass.size());
}
});
return res;
}
Json::Value GetLogs(const std::string &name, const std::string &attempt)
{
Json::Value res;
if (!users.if_contains(name, [&res, &attempt](const User &u) {
if (u.password != XXH3_64bits(attempt.data(), attempt.size()))
{
res = ErrorResponse::WrongPassword;
}
else
{
Json::Value temp;
for (uint32_t i = u.log.data.size(); i > 0; --i)
{
temp[i - 1]["to"] = u.log.data[u.log.data.size() - i].to;
temp[i - 1]["from"] = u.log.data[u.log.data.size() - i].from;
temp[i - 1]["amount"] = (Json::UInt)u.log.data[u.log.data.size() - i].amount;
temp[i - 1]["time"] = (Json::UInt64)u.log.data[u.log.data.size() - i].time;
}
res = std::move(temp);
}
}))
{
return ErrorResponse::UserNotFound;
}
return res;
}
void Save()
{
Json::Value temp;
//loading info into json temp
{
std::scoped_lock<std::shared_mutex, std::shared_mutex> lock{size_l, send_funds_l};
for (const auto &u : users)
{
//we know it contains this key but we call this func to grab mutex
users.if_contains(u.first, [&temp, &u](const User &u_val) {
temp[u.first] = u_val.Serialize();
});
}
}
if (!temp.isNull())
{
std::ofstream user_save("../users.json");
Json::StreamWriterBuilder builder;
const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
writer->write(temp, &user_save);
user_save.close();
}
else
{
throw std::invalid_argument("Saving Failed\n");
}
}
//NOT THREAD SAFE, BY NO MEANS SHOULD THIS BE CALLED WHILE RECEIEVING REQUESTS
void Load()
{
Json::CharReaderBuilder builder;
Json::Value temp;
std::ifstream user_save("../users.json");
builder["collectComments"] = true;
JSONCPP_STRING errs;
if (!parseFromStream(builder, user_save, &temp, &errs))
{
std::cerr << errs << '\n';
user_save.close();
throw std::invalid_argument("Parsing Failed\n");
}
else
{
user_save.close();
for (const auto &u : temp.getMemberNames())
{
if constexpr (max_log_size)
{
users.try_emplace(u, temp[u]["balance"].asUInt(), std::move(temp[u]["password"].asUInt64()), temp[u]["log"]);
}
else
{
users.try_emplace(u, temp[u]["balance"].asUInt(), std::move(temp[u]["password"].asUInt64()));
}
}
}
}
} bank;
//TODO make branchless

55
include/bank_f.h Normal file
View file

@ -0,0 +1,55 @@
#pragma once
#include <drogon/HttpController.h>
#include "bank.h"
using namespace drogon;
#define req_args const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback
class BankF : public HttpController<BankF, false>
{
Bank &bank;
public:
BankF(Bank *b);
void Help(req_args) const;
void Close(req_args) const;
void AddUser(req_args, std::string &&name) const;
void AdminAddUser(req_args, std::string &&name, uint32_t init_bal) const;
void DelUser(req_args, const std::string &name) const;
void AdminDelUser(req_args, const std::string &name) const;
void SendFunds(req_args, const std::string name, const std::string to, uint32_t amount) const;
void ChangePassword(req_args, const std::string &name) const;
void Contains(req_args, const std::string &name) const;
void GetBal(req_args, const std::string &name) const;
void VerifyPassword(req_args, const std::string &name) const;
void SetBal(req_args, const std::string &name, uint32_t amount) const;
void AdminVerifyPass(req_args);
void GetLog(req_args, const std::string &name);
METHOD_LIST_BEGIN
//Usage
METHOD_ADD(BankF::GetBal, "/{name}/bal", Get, Options);
METHOD_ADD(BankF::GetLog, "/{name}/log", Get, Options);
METHOD_ADD(BankF::SendFunds, "/{name}/send/{to}/amount={amount}", Post, Options);
METHOD_ADD(BankF::VerifyPassword, "/{name}/pass/verify", Get, Options);
//Meta Usage
METHOD_ADD(BankF::ChangePassword, "/{name}/pass/change", Patch, Options);
METHOD_ADD(BankF::SetBal, "/admin/{name}/bal/amount={amount}", Patch, Options);
//System Usage
METHOD_ADD(BankF::Help, "/help", Get, Options);
METHOD_ADD(BankF::Close, "/admin/close", Post, Options);
METHOD_ADD(BankF::Contains, "/contains/{name}", Get, Options);
METHOD_ADD(BankF::AdminVerifyPass, "/admin/verify", Get, Options);
//User Managment
METHOD_ADD(BankF::AddUser, "/user/{name}", Post, Options);
METHOD_ADD(BankF::AdminAddUser, "/admin/user/{name}?init_bal={init_bal}", Post, Options);
METHOD_ADD(BankF::DelUser, "/user/{name}", Delete, Options);
METHOD_ADD(BankF::AdminDelUser, "/admin/user/{name}", Delete, Options);
METHOD_LIST_END
};

File diff suppressed because one or more lines are too long

13
include/log.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include <json/json.h>
#include <array>
#include <algorithm>
#include "consts.hpp"
#include "transaction.h"
struct Log
{
std::vector<Transaction> data;
void AddTrans(Transaction &&t);
Json::Value Serialize() const;
};

View file

@ -1,40 +0,0 @@
#pragma once
#include <array>
#include <algorithm>
#include "consts.hpp"
#include "transactions.hpp"
struct Log
{
std::vector<Transaction> data;
void AddTrans(Transaction &&t)
{
if (data.size() == max_log_size) // If we hit the max size
{
for (uint32_t i = 1; i < data.size(); i++) // Make room at the back
{
std::cout << i << '\n';
data[i - 1] = std::move(data[i]); // Shifts everything left
}
data[data.size() - 1] = std::move(t); // Place new in opened spot
return;
}
else if (data.size() == data.capacity()) // If we haven't hit the max but hit capacity
{
data.reserve(data.capacity() + pre_log_size); // Reserve more memory
}
data.push_back(std::move(t)); // In either case we have space under max length, move to new spot
}
Json::Value Serialize() const
{
Json::Value res;
for (uint32_t i = 0; i < data.size(); ++i)
{
res[i]["to"] = data[i].to;
res[i]["from"] = data[i].from;
res[i]["amount"] = (Json::UInt)data[i].amount;
res[i]["time"] = (Json::UInt64)data[i].time;
}
return res;
}
};

15
include/transaction.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include <string>
#include <chrono>
#include <cstdint>
struct Transaction
{
std::string from = "", to = "";
uint32_t amount = 0;
uint64_t time = 0;
Transaction();
Transaction(std::string from_str, std::string to_str, uint32_t amount, uint64_t time);
Transaction(std::string from_str, std::string to_str, uint32_t amount);
};

View file

@ -1,24 +0,0 @@
#pragma once
#include <chrono>
#include <cstdint>
struct Transaction
{
std::string from = "", to = "";
uint32_t amount = 0;
uint64_t time = 0;
Transaction() = default;
Transaction(std::string from_str, std::string to_str, uint32_t amount, uint64_t time) : amount(amount), time(time)
{
from = std::move(from_str);
to = std::move(to_str);
}
Transaction(std::string from_str, std::string to_str, uint32_t amount) : amount(amount)
{
using namespace std::chrono;
from = std::move(from_str);
to = std::move(to_str);
time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
};

20
include/user.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#include <json/json.h>
#include <string>
#include <algorithm>
#include "xxhash.h"
#include "log.h"
struct User
{
uint32_t balance = 0;
uint64_t password;
Log log;
User(std::string &&init_pass);
User(uint32_t init_bal, std::string &&init_pass);
User(uint32_t init_bal, uint64_t init_pass);
User(uint32_t init_bal, uint64_t init_pass, const Json::Value &log_j);
Json::Value Serialize() const;
};

View file

@ -1,62 +0,0 @@
#pragma once
#include <json/json.h>
#include <string>
#include <algorithm>
#include "log.hpp"
struct User
{
uint32_t balance = 0;
uint64_t password;
Log log;
/**
* @brief User constructor
*
* @param init_pass initial password
*/
User(std::string &&init_pass) : password(XXH3_64bits(init_pass.data(), init_pass.size())) {}
/**
* @brief User Constructor for admins
*
* @param init_bal initial balance
* @param init_pass initial password
*/
User(uint32_t init_bal, std::string &&init_pass) : balance(init_bal), password(XXH3_64bits(init_pass.data(), init_pass.size())) {}
/**
* @brief User Constructor for loading
*
* @param init_bal
* @param init_pass
*/
User(uint32_t init_bal, uint64_t init_pass) : balance(init_bal), password(init_pass) {}
User(uint32_t init_bal, uint64_t init_pass, const Json::Value &log_j) : balance(init_bal), password(init_pass)
{
if (log_j.size())
{
log.data.reserve(std::min(pre_log_size * ((log_j.size() / pre_log_size) + 1), max_log_size));
for (uint32_t i = (log_j.size() - max_log_size) * (log_j.size() > max_log_size); i < log_j.size(); i++)
{
log.data.push_back(std::move(Transaction(
log_j[i]["from"].asCString(),
log_j[i]["to"].asCString(),
log_j[i]["amount"].asUInt(),
log_j[i]["time"].asUInt64())));
}
}
}
Json::Value Serialize() const
{
Json::Value res;
res["balance"] = (Json::UInt)balance;
res["password"] = (Json::UInt64)password;
if constexpr (max_log_size)
{
res["log"] = log.Serialize();
}
return res;
}
};

View file

@ -3,7 +3,7 @@
#include <thread>
#include <sys/types.h>
#include <unistd.h>
#include "bank_f.hpp"
#include "bank_f.h"
#include <signal.h>
#include <stdlib.h>
@ -13,6 +13,8 @@
using namespace std::chrono;
using namespace drogon;
static Bank bank;
void SaveSig(int s)
{
bank.Save();
@ -66,7 +68,7 @@ int main(int argc, char **argv)
}).detach();
}
auto API = std::make_shared<BankF>();
auto API = std::make_shared<BankF>(&bank);
app().registerPostHandlingAdvice(
[](const drogon::HttpRequestPtr &req, const drogon::HttpResponsePtr &resp) {
resp->addHeader("Access-Control-Allow-Origin", "*");

295
src/bank.cpp Normal file
View file

@ -0,0 +1,295 @@
#include "bank.h"
int_fast8_t Bank::AddUser(const std::string &name, std::string &&init_pass)
{
if (name.size() > max_name_size)
{
return ErrorResponse::NameTooLong;
}
{
std::shared_lock<std::shared_mutex> lock{size_l};
if (!users.try_emplace_l(
name, [](User &) {}, std::move(init_pass)))
{
return ErrorResponse::UserAlreadyExists;
}
else
{
return true;
}
}
}
int_fast8_t Bank::AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass)
{
if (name.size() > max_name_size)
{
return ErrorResponse::NameTooLong;
}
if (admin_pass != attempt)
{
return ErrorResponse::WrongAdminPassword;
}
{
std::shared_lock<std::shared_mutex> lock{size_l};
if (!users.try_emplace_l(
name, [](User &) {}, init_bal, std::move(init_pass)))
{
return ErrorResponse::UserAlreadyExists;
}
else
{
return true;
}
}
}
int_fast8_t Bank::DelUser(const std::string &name, const std::string &attempt)
{
std::shared_lock<std::shared_mutex> lock{size_l};
bool state = false;
if (!users.erase_if(name, [&state, &attempt](User &u) { return state = (XXH3_64bits(attempt.data(), attempt.size()) == u.password); }))
{
return ErrorResponse::UserNotFound;
}
else
{
return state * ErrorResponse::WrongPassword;
}
}
int_fast8_t Bank::AdminDelUser(const std::string &name, const std::string &attempt)
{
std::shared_lock<std::shared_mutex> lock{size_l};
bool state = false;
if (!users.erase_if(name, [this, &state, &attempt](const User &) { return state = (admin_pass == attempt); }))
{
return ErrorResponse::UserNotFound;
}
else
{
return state * ErrorResponse::WrongAdminPassword;
}
}
int_fast8_t Bank::SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount, const std::string &attempt)
{
//cant send money to self, from self or amount is 0
if (a_name == b_name || !amount)
{
return ErrorResponse::InvalidRequest;
}
int_fast8_t state = false;
{
std::shared_lock<std::shared_mutex> lock{send_funds_l}; //because SendFunds requires 3 locking operations
if (users.modify_if(a_name, [&state, amount, &attempt](User &a) {
//if A exists, A can afford it, and A's password matches
if (a.balance < amount)
{
state = ErrorResponse::InsufficientFunds;
}
else
{
if (a.password != XXH3_64bits(attempt.data(), attempt.size()))
{
state = ErrorResponse::WrongPassword;
}
else
{
a.balance -= amount;
state = true;
}
}
}))
{
return ErrorResponse::UserNotFound;
}
else
{
if (!state)
{
return state;
}
else
{
//if B doesnt exist
if (!users.modify_if(b_name, [amount](User &b) {
b.balance += amount;
}))
{
//attempt to refund if A exist
users.modify_if(a_name, [amount](User &a) {
a.balance += amount;
});
return ErrorResponse::UserNotFound; //because had to refund transaction
}
else
{
if constexpr (max_log_size)
{
Transaction temp(a_name, b_name, amount);
Transaction temp2 = temp;
users.modify_if(a_name, [&temp](User &a) {
a.log.AddTrans(std::move(temp));
});
users.modify_if(b_name, [&temp2](User &b) {
b.log.AddTrans(std::move(temp2));
});
}
return true;
}
}
}
}
}
bool Bank::Contains(const std::string &name) const
{
return users.contains(name);
}
bool Bank::AdminVerifyPass(const std::string &attempt)
{
return (admin_pass != attempt);
}
int_fast8_t Bank::SetBal(const std::string &name, const std::string &attempt, uint32_t amount)
{
if (admin_pass != attempt)
{
return ErrorResponse::WrongAdminPassword;
}
else
{
if (!users.modify_if(name, [amount](User &u) {
u.balance = amount;
}))
{
return ErrorResponse::UserNotFound;
}
else
{
return true;
}
}
}
int_fast64_t Bank::GetBal(const std::string &name) const
{
int_fast64_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res](const User &u) {
res = u.balance;
});
return res;
}
int_fast8_t Bank::VerifyPassword(const std::string &name, const std::string &attempt) const
{
int_fast8_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res, &attempt](const User &u) {
res = u.password == XXH3_64bits(attempt.data(), attempt.size());
});
return res;
}
int_fast8_t Bank::ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass)
{
int_fast8_t res = ErrorResponse::UserNotFound;
users.modify_if(name, [&res, &attempt, &new_pass](User &u) {
if (u.password != XXH3_64bits(attempt.data(), attempt.size()))
{
res = ErrorResponse::WrongPassword;
}
else
{
u.password = XXH3_64bits(new_pass.data(), new_pass.size());
}
});
return res;
}
Json::Value Bank::GetLogs(const std::string &name, const std::string &attempt)
{
Json::Value res;
if (!users.if_contains(name, [&res, &attempt](const User &u) {
if (u.password != XXH3_64bits(attempt.data(), attempt.size()))
{
res = ErrorResponse::WrongPassword;
}
else
{
Json::Value temp;
for (uint32_t i = u.log.data.size(); i > 0; --i)
{
temp[i - 1]["to"] = u.log.data[u.log.data.size() - i].to;
temp[i - 1]["from"] = u.log.data[u.log.data.size() - i].from;
temp[i - 1]["amount"] = (Json::UInt)u.log.data[u.log.data.size() - i].amount;
temp[i - 1]["time"] = (Json::UInt64)u.log.data[u.log.data.size() - i].time;
}
res = std::move(temp);
}
}))
{
return ErrorResponse::UserNotFound;
}
return res;
}
void Bank::Save()
{
Json::Value temp;
//loading info into json temp
{
std::scoped_lock<std::shared_mutex, std::shared_mutex> lock{size_l, send_funds_l};
for (const auto &u : users)
{
//we know it contains this key but we call this func to grab mutex
users.if_contains(u.first, [&temp, &u](const User &u_val) {
temp[u.first] = u_val.Serialize();
});
}
}
if (!temp.isNull())
{
std::ofstream user_save("../users.json");
Json::StreamWriterBuilder builder;
const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
writer->write(temp, &user_save);
user_save.close();
}
else
{
throw std::invalid_argument("Saving Failed\n");
}
}
//NOT THREAD SAFE, BY NO MEANS SHOULD THIS BE CALLED WHILE RECEIEVING REQUESTS
void Bank::Load()
{
Json::CharReaderBuilder builder;
Json::Value temp;
std::ifstream user_save("../users.json");
builder["collectComments"] = true;
JSONCPP_STRING errs;
if (!parseFromStream(builder, user_save, &temp, &errs))
{
std::cerr << errs << '\n';
user_save.close();
throw std::invalid_argument("Parsing Failed\n");
}
else
{
user_save.close();
for (const auto &u : temp.getMemberNames())
{
if constexpr (max_log_size)
{
users.try_emplace(u, temp[u]["balance"].asUInt(), std::move(temp[u]["password"].asUInt64()), temp[u]["log"]);
}
else
{
users.try_emplace(u, temp[u]["balance"].asUInt(), std::move(temp[u]["password"].asUInt64()));
}
}
}
}
//TODO make branchless

122
src/bank_f.cpp Normal file

File diff suppressed because one or more lines are too long

31
src/log.cpp Normal file
View file

@ -0,0 +1,31 @@
#include "log.h"
void Log::AddTrans(Transaction &&t)
{
if (data.size() == max_log_size) // If we hit the max size
{
for (uint32_t i = 1; i < data.size(); i++) // Make room at the back
{
data[i - 1] = std::move(data[i]); // Shifts everything left
}
data[data.size() - 1] = std::move(t); // Place new in opened spot
return;
}
else if (data.size() == data.capacity()) // If we haven't hit the max but hit capacity
{
data.reserve(data.capacity() + pre_log_size); // Reserve more memory
}
data.push_back(std::move(t)); // In either case we have space under max length, move to new spot
}
Json::Value Log::Serialize() const
{
Json::Value res;
for (uint32_t i = 0; i < data.size(); ++i)
{
res[i]["to"] = data[i].to;
res[i]["from"] = data[i].from;
res[i]["amount"] = (Json::UInt)data[i].amount;
res[i]["time"] = (Json::UInt64)data[i].time;
}
return res;
}

15
src/transaction.cpp Normal file
View file

@ -0,0 +1,15 @@
#include "transaction.h"
Transaction::Transaction() = default;
Transaction::Transaction(std::string from_str, std::string to_str, uint32_t amount, uint64_t time) : amount(amount), time(time)
{
from = std::move(from_str);
to = std::move(to_str);
}
Transaction::Transaction(std::string from_str, std::string to_str, uint32_t amount) : amount(amount)
{
using namespace std::chrono;
from = std::move(from_str);
to = std::move(to_str);
time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}

51
src/user.cpp Normal file
View file

@ -0,0 +1,51 @@
#include "user.h"
/**
* @brief User constructor
*
* @param init_pass initial password
*/
User::User(std::string &&init_pass) : password(XXH3_64bits(init_pass.data(), init_pass.size())) {}
/**
* @brief User Constructor for admins
*
* @param init_bal initial balance
* @param init_pass initial password
*/
User::User(uint32_t init_bal, std::string &&init_pass) : balance(init_bal), password(XXH3_64bits(init_pass.data(), init_pass.size())) {}
/**
* @brief User Constructor for loading
*
* @param init_bal
* @param init_pass
*/
User::User(uint32_t init_bal, uint64_t init_pass) : balance(init_bal), password(init_pass) {}
User::User(uint32_t init_bal, uint64_t init_pass, const Json::Value &log_j) : balance(init_bal), password(init_pass)
{
if (log_j.size())
{
log.data.reserve(std::min(pre_log_size * ((log_j.size() / pre_log_size) + 1), max_log_size));
for (uint32_t i = (log_j.size() - max_log_size) * (log_j.size() > max_log_size); i < log_j.size(); i++)
{
log.data.push_back(std::move(Transaction(
log_j[i]["from"].asCString(),
log_j[i]["to"].asCString(),
log_j[i]["amount"].asUInt(),
log_j[i]["time"].asUInt64())));
}
}
}
Json::Value User::Serialize() const
{
Json::Value res;
res["balance"] = (Json::UInt)balance;
res["password"] = (Json::UInt64)password;
if constexpr (max_log_size)
{
res["log"] = log.Serialize();
}
return res;
}