Merge pull request #30 from EntireTwix/Refractor

Complete Refactor
This commit is contained in:
William Katz 2021-07-19 17:39:36 -07:00 committed by GitHub
commit 50a0aeb0c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
73 changed files with 44770 additions and 758 deletions

View file

@ -1,9 +1,6 @@
/build
/config.json
/users.json
/help.md
/services.md
/APIs.md
/README.md
/benchmarking.cpp
/.github
/benchmarking.cpp

3
.gitignore vendored
View file

@ -1,4 +1,3 @@
.vscode
build
config.json
users.json
ccash_config.hpp

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "drogon"]
path = third_party/drogon
url = https://github.com/an-tao/drogon
[submodule "third_party/base64"]
path = third_party/base64
url = https://github.com/aklomp/base64

View file

@ -6,25 +6,86 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS "-Wall -Wextra -march=native")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
find_package(Threads REQUIRED)
add_executable(${PROJECT_NAME} main.cpp )
target_sources(${PROJECT_NAME} PRIVATE
src/xxhash.c
src/bank_f.cpp
src/bank_api.cpp
src/bank_resp.cpp
src/bank.cpp
src/change_flag.cpp
src/json_filter.cpp
src/log.cpp
src/simdjson.cpp
src/str_intrusion.cpp
src/transaction.cpp
src/user_filter.cpp
src/user.cpp
src/xxhash_str.cpp
src/xxhash.c
fbe/user_model/bank_dom_final_models.cpp
fbe/user_model/bank_dom_models.cpp
fbe/user_model/bank_dom.cpp
fbe/user_model/fbe_final_models.cpp
fbe/user_model/fbe_models.cpp
fbe/user_model/fbe.cpp
)
if(DEFINED USER_SAVE_LOC)
set(USER_SAVE ${USER_SAVE_LOC})
else()
set(USER_SAVE "\"users.dat\"")
endif()
if(DEFINED DROGON_CONFIG_LOC)
set(DROGON_CONFIG ${DROGON_CONFIG_LOC})
else()
set(DROGON_CONFIG "\"config.json\"")
endif()
if(DEFINED MAX_LOG_SIZE)
set(MAX_LOG_SIZE_VAL ${MAX_LOG_SIZE})
else()
set(MAX_LOG_SIZE_VAL 100)
endif()
if(DEFINED CONSERVATIVE_DISK_SAVE)
set(CONSERVATIVE_DISK_SAVE_VAL ${CONSERVATIVE_DISK_SAVE})
else()
set(CONSERVATIVE_DISK_SAVE_VAL true)
endif()
if(DEFINED MULTI_THREADED)
set(MULTI_THREADED_VAL ${MULTI_THREADED})
else()
set(MULTI_THREADED_VAL true)
endif()
if(DEFINED RETURN_ON_DEL_NAME)
set(RETURN_ON_DEL_VAL true)
set(RETURN_ON_DEL_NAME_VAL ${RETURN_ON_DEL_NAME})
else()
set(RETURN_ON_DEL_VAL false)
set(RETURN_ON_DEL_NAME_VAL "\"\"")
endif()
configure_file(ccash_config.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/include/ccash_config.hpp)
target_include_directories(${PROJECT_NAME} PUBLIC include)
target_include_directories(${PROJECT_NAME} PUBLIC fbe/user_model)
target_include_directories(${PROJECT_NAME} PUBLIC third_party)
target_include_directories(${PROJECT_NAME} PUBLIC third_party/drogon/lib/inc)
target_include_directories(${PROJECT_NAME} PUBLIC third_party/base64/include)
add_subdirectory(third_party/drogon)
target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_THREAD_LIBS_INIT} )
#AVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.o
target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/third_party/base64/lib/libbase64.o)

View file

@ -1,16 +1,20 @@
FROM debian:latest
FROM alpine:3.11
WORKDIR /ccash
WORKDIR /
RUN apt update && apt -y install build-essential g++ cmake protobuf-compiler libjsoncpp-dev uuid-dev openssl libssl-dev zlib1g-dev
RUN apk update && apk add git cmake g++ make protobuf jsoncpp-dev openssl libressl-dev zlib-dev util-linux-dev libtool autoconf automake python3
COPY . .
RUN mkdir build
WORKDIR /ccash/build
RUN cmake ..
RUN git clone --recurse-submodules https://github.com/EntireTwix/CCash.git --branch Refractor
WORKDIR /CCash/third_party/base64/
RUN AVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.o
RUN mkdir /CCash/build
WORKDIR /CCash/build
RUN cmake -DDROGON_CONFIG_LOC=\"\/CCash\/config\/config.json\" -DUSER_SAVE_LOC=\"\/CCash\/config\/users.json\" ..
RUN make -j$(nproc)
ENTRYPOINT ["/ccash/build/bank"]
ARG ADMIN_A=admin
ARG SAVE_FREQ=2
RUN ["chmod", "+x", "/CCash/config/ssl.sh"]
CMD ["sh", "-c", "/CCash/config/ssl.sh && /CCash/build/bank ${ADMIN_A} ${SAVE_FREQ}"]

124
README.md
View file

@ -1,110 +1,14 @@
# CCash
A webserver hosting a bank system for Minecraft, able to be used from web browser or from CC/OC if you're playing modded.
the currency model most Minecraft Servers adopt if any, is resource based, usually diamonds, this model is fraught with issues however:
- the primary issue is minecraft worlds are infinite leading to hyper inflation as everyone accrues more diamonds
- there is no central authority minting the currency, any consumer can introduce more diamonds to the system
- some resources are passively reapable, making the generation of currency a larger focus then of products
- locality is required for transaction
- theft is possible, ownership is possession based
CCash solves these issues and adds a level of abstraction, the main philosophy of CCash is to have fast core operations that other services build on
## Build
drogon depedencies (varies by OS/distro)
```
# Debian
sudo apt install libjsoncpp-dev uuid-dev openssl libssl-dev zlib1g-dev
# macOS
brew install jsoncpp ossp-uuid openssl zlib
```
building the project
```
git clone --recurse-submodule https://github.com/EntireTwix/CCash/
cd CCash
mkdir build
cd build
cmake ..
make -j<threads>
```
then edit config.json to include the paths to your certs for HTTPS (I use certbot), or just remove the listener for port 443.
```
vim ../config.json
```
finally, run the program
```
sudo ./bank <admin password> <saving frequency in minutes> <threads>
```
## Connected Services
Using the Bank's API allows (you/others) to (make/use) connected services that utilize the bank, a couple ideas can be found [here](docs/services.md)
## Developing for
as a dev check out
* [APIs](https://github.com/EntireTwix/CCash/blob/main/docs/APIs.md)
* [endpoints](https://github.com/EntireTwix/CCash/blob/main/docs/help.md)
## FAQ
**Q:** how is money initially injected into the economy
**A:** you can take any approach you want, one that I recommend is using a one way exchange via the CC ATM above to have players mine the initial currency, this rewards early adopters and has a sunk cost effect in that the resource is promptly burned
## [Contributions](https://github.com/EntireTwix/CCash/graphs/contributors)
Thank you to the contributors
| Name | Project Work | Connected Service Work |
| :------------------------------------------ | ----------------------------------------------------------------------- | ---------------------- |
| [Expand](https://github.com/Expand-sys) | Slight docker changes | Frontend |
| [React](https://github.com/Reactified) | CC API, Logo | CC Shop, CC ATM. |
| [Doggo](https://github.com/FearlessDoggo21) | Logs loading/adding Optimized, HTTP convention suggestions, Python API | `N/A` |
| [Luke](https://github.com/LukeeeeBennett) | JS API, Docker, Slight Doc edits | `N/A` |
| [Jolly](https://github.com/STBoyden) | Slight Doc edits | `N/A` |
## Features
### Performance
- In memory database instead of on disk
- **NOT** written in Lua, like a OC/CC implementation
- written in **C++**, arguably the fastest language
- **multi-threaded**
- **parallel hashmaps** a far [superior](https://greg7mdp.github.io/parallel-hashmap/) HashMap implementation to the STD, that also benefits from multi-threaded
- **Drogon** is a very fast [web framework](https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=composite)
- **xxHash** for the hashing of passwords, it is very fast: [graph](https://user-images.githubusercontent.com/750081/61976089-aedeab00-af9f-11e9-9239-e5375d6c080f.png)
- **Lightweight**, anecodotally I experienced (on my laptop's i7 6700K, 8 threads):
- memory usage of 8.5 MB (with 0 users)
- 0.0% CPU usage idle
- <1% CPU on average
- 1000 requests in parallel completed in 0.85s which spiked CPU usage to 7%
### Safety
- **Tamper Proof** relative to an in-game implementation
- **Auto-Saving** and Saves on close
- All passwords are **Hashed**
- **HTTPS** (OpenSSL)
### Accessibility
- **RESTful** API for connected services like a market, gambling, or anything else you can think of
- able to be used millions of blocks away, across dimensions, servers, **vanilla or modded**.
- **Logging** of all transactions, configurable in [consts.hpp](include/consts.hpp)
### Other
- **return balance on deletion**, configurable in [consts.hpp](include/consts.hpp)
## Dependencies
- [Parallel HashMap](https://github.com/greg7mdp/parallel-hashmap/tree/master)
- [Drogon](https://github.com/an-tao/drogon/tree/master)
- [XXHASH](https://github.com/Cyan4973/xxHash)
* [problem/solution](docs/idea.md)
* connected services
* how to contribute
* [explanation](docs/connected_services/how_to/explanation.md)
* [language APIs](docs/connected_services/how_to/APIs.md)
* [API endpoints](docs/connected_services/how_to/endpoints.md)
* [existing services](docs/connected_services/existing_services.md)
* features
* [user side](docs/features/user_side.md)
* [implementation](docs/features/implementation.md)
* [building](docs/building.md)
* [FAQ](docs/FAQ.md)
* [contributors](docs/contributors.md)
* [Patreon](https://www.patreon.com/twoxx) if you wanna support this and/or future projects.

View file

@ -3,7 +3,9 @@
#include <thread>
#include <sys/types.h>
#include <unistd.h>
#include "bank_f.h"
#include <random>
#include "xxhash_str.h"
#include "bank.h"
#include <signal.h>
#include <stdlib.h>
@ -11,7 +13,6 @@
#include <unistd.h>
using namespace std::chrono;
using namespace drogon;
static Bank bank;
@ -62,26 +63,49 @@ static Bank bank;
int main(int argc, char **argv)
{
bank.AddUser("twix", "root");
bank.AddUser("jolly", "root");
bank.admin_pass = "root";
Op_a(bank.AddUser("", ""), "add user: ", 1000000, bank.DelUser("", ""));
Op_a(bank.AdminAddUser("root", "", 0, ""), "admin add user: ", 1000000, bank.DelUser("", ""));
Op(bank.SetBal("twix", "root", 1000000), "set bal: ", 1000000);
Op(bank.SendFunds("twix", "jolly", 1, "root"), "send funds: ", 1000000);
bank.AddUser("", "");
Op_a(bank.DelUser("", ""), "del user: ", 1000000, bank.AddUser("", ""));
Op_a(bank.AdminDelUser("", "root"), "admin del user: ", 1000000, bank.AddUser("", ""));
bank.DelUser("", "");
bank.AddUser("twix", 0, "root");
bank.AddUser("jolly", 0, "root");
bank.admin_account = "twix";
const std::string data("this string is quite long which is relevant when testing the speed of a hasing function");
Op(std::hash<std::string>{}(data), "hash<string>: ", 1000000);
Op(xxHashStringGen{}(data), "xxHashStringGen: ", 1000000);
Op_a(bank.AddUser("abc", 0, "abc"), "add user: ", 1000000, bank.DelUser("abc"));
Op(bank.ImpactBal("twix", 1), "impact bal: ", 1000000);
Op(bank.SetBal("twix", 1000000), "set bal: ", 1000000);
Op(bank.SendFunds("twix", "jolly", 1), "send funds: ", 1000000);
Op(bank.SendFunds("", "", 1), "invalid send funds: ", 1000000);
bank.AddUser("abc", 0, "abc");
Op_a(bank.DelUser("abc"), "del user: ", 1000000, bank.AddUser("abc", 0, "abc"));
Op_a(bank.DelSelf("abc"), "del self: ", 1000000, bank.AddUser("abc", 0, "abc"));
bank.DelUser("abc");
Op(bank.Contains("twix"), "contains: ", 1000000);
Op(bank.AdminVerifyPass("root"), "admin verify pass: ", 1000000);
Op(bank.AdminVerifyAccount("twix"), "admin verify account: ", 1000000);
Op(bank.GetBal("twix"), "get bal: ", 1000000);
Op(bank.VerifyPassword("twix", "root"), "verify pass: ", 1000000);
Op(bank.ChangePassword("twix", "root", "root"), "change pass: ", 1000000);
Op(bank.GetLogs("twix", "root"), "get logs: ", 10000);
Op(bank.ChangePassword("twix", "root"), "change pass: ", 1000000);
#if MAX_LOG_SIZE > 0
Op(bank.GetLogs("twix"), "get logs: ", 1000000);
#endif
Op(bank.Save(), "saving: ", 1);
//GetBal scalining test
//std::default_random_engine generator;
//std::uniform_real_distribution<double> distribution(0.0, 1.0);
// for (size_t i = 0; i < 10000000; ++i)
// {
// bank.AddUser(std::to_string(i), 100000, "root");
// if (i % 10000 == 0)
// {
// auto u = std::to_string((int)(distribution(generator) * i));
// Op(bank.GetBal(u), std::to_string(i) + ", ", 100000);
// }
// }
return 0;
}

40
ccash_config.hpp.in Normal file
View file

@ -0,0 +1,40 @@
#pragma once
// Setting to 0 does not compile logging (useful for if disk/memory is very valuable)
#define MAX_LOG_SIZE @MAX_LOG_SIZE_VAL@
//default to minecraft usernames
constexpr unsigned min_name_size = 3;
constexpr unsigned max_name_size = 16;
constexpr const char *users_location = @USER_SAVE@;
constexpr const char *config_location = @DROGON_CONFIG@;
// Returns money to an account on deletion (useful if you dont want any money to leave the economy)
#define RETURN_ON_DEL @RETURN_ON_DEL_VAL@
constexpr const char *return_account = @RETURN_ON_DEL_NAME_VAL@;
/*
if true, when frequency is hit AND changes have happened then save
pros
LOW disk usage
cons
LOW atomic overhead
if false, when frequency is hit save
pros
ZERO atomic overhead
cons
HIGH disk usage
*/
#define CONSERVATIVE_DISK_SAVE @CONSERVATIVE_DISK_SAVE_VAL@
/*
example, when set to 2
version 1 will not work
version 2 will work
version 3 will work
etc
*/
#define API_VERSION 1
#define MULTI_THREADED @MULTI_THREADED_VAL@

16
config/config.json Normal file
View file

@ -0,0 +1,16 @@
{
"listeners": [
{
"address": "0.0.0.0",
"port": 80,
"https": false
},
{
"address": "0.0.0.0",
"port": 443,
"https": true,
"cert": "/CCash/config/cert.cert",
"key": "/CCash/config/key.key"
}
]
}

9
config/ssl.sh Normal file
View file

@ -0,0 +1,9 @@
#!/bin/bash
openssl genrsa -out server.pass.key 2048
openssl rsa -in server.pass.key -out /CCash/config/key.key
rm server.pass.key
openssl req -new -key /CCash/config/key.key -out server.csr \
-subj "/C=US/ST=CCashland/L=NEW CCASH/O=CCash/OU=Devs/CN=localhost"
openssl x509 -req -days 365 -in server.csr -signkey /CCash/config/key.key -out /CCash/config/cert.cert

7
config/users.json Normal file
View file

@ -0,0 +1,7 @@
{
"": {
"balance": 0,
"log": null,
"password": 0
}
}

1
docs/FAQ.md Normal file
View file

@ -0,0 +1 @@
[PREVIOUS PAGE](building.md)

136
docs/building.md Normal file
View file

@ -0,0 +1,136 @@
# Building
[PREVIOUS PAGE](features/implementation.md) | [NEXT PAGE](FAQ.md)
## Advice
as CCash is very lightweight it can run on practically any device but here are some tips:
* single core machines should toggle `MULTI_THREADED` to `false`
* if your server is sufficiently active as so that each save frequency saving is highly likely then `CONSERVATIVE_DISK_SAVE` should be toggled to `false`
* `MAX_LOG_SIZE` should be adjusted as it takes up the most memory usage/storage of the ledger's features at ~202 bytes in memory and ~104 bytes in disk at default settings. Setting to 0 will not even compile logs
* with no users memory usage is 8.6 Mb
## Drogon Depedencies
### Linux
#### Debian
```
sudo apt install libjsoncpp-dev uuid-dev openssl libssl-dev zlib1g-dev
```
#### CentOS 7.5
```
yum install git gcc gcc-c++
git clone https://github.com/Kitware/CMake
cd CMake/
./bootstrap
make
make install
yum install centos-release-scl devtoolset-8
scl enable devtoolset-8 bash
git clone https://github.com/open-source-parsers/jsoncpp
cd jsoncpp/
mkdir build
cd build
cmake ..
make
make install
yum install libuuid-devel openssl-devel zlib-devel
```
### MacOS
```
brew install jsoncpp ossp-uuid openssl zlib
```
### Docker
Docker Package can be found [here](https://github.com/EntireTwix/CCash/packages/851105)
## Actually, building
```
git clone --recurse-submodule https://github.com/EntireTwix/CCash/
cd CCash
cd third_party/base64
AVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.o
cd ../..
mkdir build
cd build
mv ../config.json config.json
```
### CMake Flags
there are multiple flags responsible configuring CCash:
| name | default | description | pros | cons |
| :--------------------- | :--------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | -------------------------------------------------------- |
| USER_SAVE_LOC | "../users.json" | where the users are saved | `N/A` | `N/A` |
| DROGON_CONFIG_LOC | "../config.json" | where the config is located | `N/A` | `N/A` |
| MAX_LOG_SIZE | 100 | max number of logs per user, last `n` transactions. If both this and pre log are toggled to 0 logs will not be compiled. | large history | higher memory usage |
| CONSERVATIVE_DISK_SAVE | `true` | when `true` only saves when changes are made | low # of disk operations | some atomic overhead |
| MULTI_THREADED | `true` | when `true` the program is compiled to utilize `n` threads which corresponds to how many Cores your CPU has, plus 1 for saving | speed | memory lock overhead is wasteful on single core machines |
| RETURN_ON_DEL_NAME | `N/A` | when defined, return on delete will be toggled and any accounts deleted will send their funds to the defined account, this prevent currency destruction | prevents destruction of currency | deleting accounts is made slower |
EXAMPLE:
```
cmake ..
```
sets these flags to their defaults, an example of setting a flag would be
```
cmake -DMULTI_THREADING=false ..
```
with `-D`
### Finishing building
lastly type in
```
cmake <flags of your choice or none> ..
make -j<threads>
./bank
```
## Certs
make sure to edit `config.json` adding the certificate location if you're using HTTPS, I personally use [certbot](https://certbot.eff.org/).
```json
{
"listeners": [
{
"address": "0.0.0.0",
"port": 80,
"https": false
},
{
"address": "0.0.0.0",
"port": 443,
"https": true,
"cert": "",
"key": ""
}
]
}
```
editing
```json
"cert": "pubkey",
"key": "privkey"
```
Alternatively you can delete this entire section (Disabling HTTPS in the proccess)
```json
{
"address": "0.0.0.0",
"port": 443,
"https": true,
"cert": "",
"key": ""
}
```
leaving
```json
{
"listeners": [
{
"address": "0.0.0.0",
"port": 80,
"https": false
}
]
}
```
**it is Highly recommened you secure your server**

View file

@ -0,0 +1 @@
[PREVIOUS PAGE](how_to/endpoints.md) | [NEXT PAGE](../features/user_side.md)

View file

@ -0,0 +1,8 @@
[PREVIOUS PAGE](explanation.md) | [NEXT PAGE](endpoints.md)
| language | v1 |
| ---------------------------------------------------------------------------------------- | :----------------------: |
| [Computer Craft](https://github.com/Reactified/rpm/blob/main/packages/ccash-api/api.lua) | :heavy_multiplication_x: |
| [JS](https://github.com/LukeeeeBennett/ccash-client-js) | :heavy_multiplication_x: |
| [Python](https://github.com/FearlessDoggo21/CCashPythonClient) | :heavy_multiplication_x: |
| [Rust](https://git.stboyden.com/STBoyden/ccash-rs) | :heavy_multiplication_x: |

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -0,0 +1,120 @@
# API endpoints
[PREVIOUS PAGE](APIs.md) | [NEXT PAGE](../existing_services.md)
## KEY
`Jresp` - Json Response, json must be accepted in the `Accept` header, be that via `application/json` or `*/*`, failing to do so results in error `406`
`Jreq` - Json Request, requires `application/json` as `content-type`, failing to do so results in error `406`
`U` - User, requires [basic auth](https://en.wikipedia.org/wiki/Basic_access_authentication) in the header `Authorization`. This credential must be a valid user, failing to do so results in error `401`
`A` - Admin, same as `U` but in addition requires username supplied be equal to the admin account username
:heavy_check_mark:
:heavy_multiplication_x:
## all error responses have JSON string along with them to describe
### Usage endpoints
| name | purpose | json input | path | HTTP Method | correct status | return type | return value | Jresp | Jreq | A | U |
| :------------- | ------------------------------------------------------------------------------ | -------------------------------- | ------------------------------- | :---------: | :------------: | :--------------: | :--------------------------------------------: | :----------------: | :----------------------: | :----------------------: | :----------------------: |
| GetBal | retrieving the balance of a given user, `{name}` | `N/A` | api/v1/user/balance?name={name} | `GET` | 200 | uint32 | the user's balance | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x: |
| GetLog | retrieves the logs of a given user, length varies by server configuration | `N/A` | api/v1/user/log | `GET` | 200 | array of objects | [{"to":string, "amount":uint32, "time":int64}] | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_check_mark: |
| SendFunds | sends funds from the authenticated user to the user `{name}` given in the json | {"name":string, "amount":uint32} | api/v1/user/transfer | `POST` | 200 | uint32 | the user's balance after the transaction | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: |
| VerifyPassword | verifies the credentials, used for connected services for ease of use | `N/A` | api/v1/user/verify_password | `POST` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_check_mark: |
### Usage enpoint errors
| name | 400 | 401 | 404 | 406 |
| :------------- | :----------------------: | :----------------------: | :----------------------: | :----------------: |
| GetBal | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_check_mark: |
| GetLog | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| SendFunds | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| VerifyPassword | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: |
### Usage endpoint support
`v` denoting the API version
| name | v1 |
| :------------- | :----------------: |
| GetBal | :heavy_check_mark: |
| GetLog | :heavy_check_mark: |
| SendFunds | :heavy_check_mark: |
| VerifyPassword | :heavy_check_mark: |
### Meta Usage endpoints
| name | purpose | json input | path | HTTP Method | correct status | return type | return value | Jresp | Jreq | A | U |
| :------------------ | -------------------------------------------------------------------------------------------------- | ------------------------------- | --------------------------------- | :---------: | :------------: | :---------: | :----------------------------: | :----------------: | :----------------: | :----------------------: | :----------------------: |
| ChangePassword | changes the password of the Authenticated user | {"pass":string} | api/v1/user/change_password | `PATCH` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: |
| AdminChangePassword | changes the password of a given user `{name}` | {"name":string,"pass":string} | api/v1/admin/user/change_password | `PATCH` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: |
| SetBal | sets the balance of a given user `{name}` | {"name":string,"amount":uint32} | api/v1/admin/set_balance | `PATCH` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: |
| ImpactBal | modifies the user `{name}`'s balance by `{amount}` if positive itll add, if negative itll subtract | {"name":string,"amount":uint32} | api/v1/admin/impact_balance | `POST` | 200 | uint32 | new balance after modification | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: |
### Meta Usage endpoint errors
| name | 400 | 401 | 404 | 406 |
| :------------------ | :----------------: | :----------------: | :----------------------: | :----------------: |
| ChangePassword | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: |
| AdminChangePassword | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| SetBal | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| ImpactBal | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
### Meta Usage endpoint support
| name | v1 |
| :------------------ | :----------------: |
| ChangePassword | :heavy_check_mark: |
| AdminChangePassword | :heavy_check_mark: |
| SetBal | :heavy_check_mark: |
| ImpactBal | :heavy_check_mark: |
### Sytem Usage endpoints
| name | purpose | json input | path | HTTP Method | correct status | return type | return value | Jresp | Jreq | A | U |
| :----------------- | ---------------------------------- | ---------- | ------------------------------ | :---------: | :------------: | :---------: | :----------: | :----------------------: | :----------------------: | :----------------------: | :----------------------: |
| Help | redirects to GitHub projects Docs | `N/A` | api/v1/help | `GET` | 301 | `N/A` | `N/A` | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x: |
| Close | saves & closes the CCash webserver | `N/A` | api/v1/admin/shutdown | `POST` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_multiplication_x: |
| Contains | checks wether a user exists | `N/A` | api/v1/user/exists?name={name} | `GET` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x: |
| AdminVerifyAccount | checks wether a user is the admin | `N/A` | api/v1/admin/verify_account | `POST` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_multiplication_x: |
### System Usage endpoin errors
| name | 401 | 404 | 406 |
| :----------------- | :----------------------: | :----------------------: | :----------------------: |
| Help | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_multiplication_x: |
| Close | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: |
| Contains | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| AdminVerifyAccount | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: |
### System Usage endpoint support
| name | v1 |
| :----------------- | :----------------: |
| Help | :heavy_check_mark: |
| Close | :heavy_check_mark: |
| Contains | :heavy_check_mark: |
| AdminVerifyAccount | :heavy_check_mark: |
### Username Requirements
Valid
* lowercase letters
* numbers
* _
* Length must be atleast 3 and at most 16 characters.
### User Management endpoints
| name | purpose | json input | path | HTTP Method | correct status | return type | return value | Jresp | Jreq | A | U |
| :----------- | --------------------------------------- | --------------------------------------------- | -------------------------- | :---------: | :------------: | :---------: | :----------: | :----------------: | :----------------------: | :----------------------: | :----------------------: |
| AddUser | adding a user with a user of 0 | {"name":string,"pass":string} | api/v1/user/register | `POST` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: |
| AdminAddUser | adding a user with an arbitrary balance | {"name":string,"amount":uint32,"pass":string} | api/v1/admin/user/register | `POST` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: |
| DelSelf | deletes a user | `N/A` | api/v1/user/delete | `DELETE` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_check_mark: |
| AdminDelUser | deletes a given user `{name}` | {"name":string} | api/v1/admin/user/delete | `DELETE` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: |
### User Management endpoint errors
| name | 400 | 401 | 404 | 406 | 409 |
| :----------- | :----------------------: | :----------------------: | :----------------------: | :----------------: | :----------------------: |
| AddUser | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_check_mark: |
| AdminAddUser | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_check_mark: |
| DelSelf | :heavy_multiplication_x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: |
| AdminDelUser | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_multiplication_x: |
### User Management endpoint support
| name | v1 |
| :----------- | :----------------: |
| AddUser | :heavy_check_mark: |
| AdminAddUser | :heavy_check_mark: |
| DelSelf | :heavy_check_mark: |
| AdminDelUser | :heavy_check_mark: |

View file

@ -0,0 +1,21 @@
[PREVIOUS PAGE](../../idea.md) | [NEXT PAGE](APIs.md)
Using the ledger's API allows (you/others) to (make/use) connected services that utilize the ledger, below is a visual represenation of connected services:
![image](connected_a.png)
ideally as the ecosystem develops, connected services become inner connected
![image](connected_b.png)
the aim of any of these services being to provide some kind of functionality that relies on the ground truth of who has what, this information is centrally secured in CCash.
if you want to browse the currently available services, check out [existing_services.md](../existing_services.md)
to make a connected service yourself you can do so by using one of the [langauge specific APIs](APIs.md), the way they work is by providing a clean in langauge way to interface with the CCash instance
![image](connected_c.png)
if an API does not exist for your language it is simple to make one, and I encourage you to add it to this list if you do decide to.
while developing make sure to reference the list of [endpoints](endpoints.md)

9
docs/contributors.md Normal file
View file

@ -0,0 +1,9 @@
# Contributors
| name | work |
| :------------------------------------------ | --------------------------------------- |
| [Luke](https://github.com/LukeeeeBennett) | Docker package, JS API |
| [React](https://github.com/Reactified) | Logo, ComputerCraft API |
| [Doggo](https://github.com/FearlessDoggo21) | HTTP suggestions, Python API, CCash CLI |
| [Expand](https://github.com/Expand-sys) | Fixed docker package |
| [Jolly](https://github.com/STBoyden) | Rust API |
| Caesay | Restful API suggestions |

BIN
docs/external_diagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,48 @@
[PREVIOUS PAGE](user_side.md) | [NEXT PAGE](../building.md)
# Implementation Features
## [Parallel Hashmap](https://github.com/greg7mdp/parallel-hashmap)
<!-- memory vs database -->
<!-- and while changes arent made on the basis of speed alone it does seem to fit the problem better as we only need to save every `n` minutes/on close. -->
<!-- phmap vs std hash map -->
#### STD vs phmap
this parallel hashmap implementation is the basis of CCash, its where all the user data is stored, compared to the STD's `std::unordered_map<T>` its much faster, this, multi threading support, and more can be found in the [writeup](https://greg7mdp.github.io/parallel-hashmap/).
![image](https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/master/html/img/stl_flat_both.PNG)
![image](https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/master/html/img/lock_various_sizes.PNG)
## [xxHash](https://github.com/Cyan4973/xxHash)
xxhash is used for both hashing of passwords for storage aswell as the usernames for indexing the phmap, its speed is ridiculous at faster then `memcpy` rates of Gb/s.
| Hash Name | Width | Bandwidth (GB/s) | Small Data Velocity | Quality | Comment |
| --------------------- | ----- | ---------------- | ------------------- | ------- | --------------- |
| __XXH3__ (SSE2) | 64 | 31.5 GB/s | 133.1 | 10 |
| _RAM sequential read_ | N/A | 28.0 GB/s | N/A | N/A | _for reference_ |
## [Base64](https://github.com/aklomp/base64)
base64 decoding is required for Basic Auth so I used this clean and fast solution which uses SIMD.
![image](https://github.com/aklomp/base64/blob/master/base64-benchmarks.png)
## [Simdjson](https://github.com/simdjson/simdjson)
simdjson was the fastest JSON parsing I could find, its used for request parsing.
![image](https://github.com/simdjson/simdjson/blob/master/doc/rome.png)
## [Drogon webframework](https://github.com/an-tao/drogon)
at the time of making this doc Drogon is the 3rd fastest web framework as per [this](https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=composite) sites metric of measuring web frameworks, it also has multi threading support.
![image](https://user-images.githubusercontent.com/31377881/125891266-570c5154-8ae2-4358-9d7b-ccd82a18b132.png)
## Sparse saving
#### Saving on close
when the program is interupted with CONTROL + C it will save before closing the webserver, **it will not however save during a crash**.
#### Auto Saving
every `n` minutes, a configurable amount at launch, CCash will save.
#### Changes
for the above two cases, it will only save to disk if changes have been made since last save.
#### [Binary Encoding](https://github.com/chronoxor/FastBinaryEncoding)
saving is done using FBE, this slightly reduces file size compared to JSON and is much faster.
| Protocol | Message size | Serialization time | Deserialization time |
| :-------------------------------------------------------------------: | -----------: | -----------------: | -------------------: |
| [Cap'n'Proto](https://capnproto.org) | 208 bytes | 558 ns | 359 ns |
| [FastBinaryEncoding](https://github.com/chronoxor/FastBinaryEncoding) | 234 bytes | 66 ns | 82 ns |
| [FlatBuffers](https://google.github.io/flatbuffers) | 280 bytes | 830 ns | 290 ns |
| [Protobuf](https://developers.google.com/protocol-buffers) | 120 bytes | 628 ns | 759 ns |
| [JSON](http://rapidjson.org) | 301 bytes | 740 ns | 500 ns |
## Multi-threading support
considering phmap and drogon both massively benefit from being multi-threaded it seemed obvious that the entire program should be, this is enabled by default and manually settable at `MULTI_THREADED`. Below are some graphs visualizing the gain of doing so:
<!-- graph -->
## Backwards Compatible API
versioning is implemented by the endpoints path, for example `/v1`. Breaking changes will ideally be sparse and backwards compatability will be maintained, for example ideally API `v3` instance can still run `v1` endpoints.

View file

@ -0,0 +1,7 @@
[PREVIOUS PAGE](../connected_services/existing_services.md) | [NEXT PAGE](implementation.md)
# Features
## Performance
## Accessibility
## Security
## Other

29
docs/idea.md Normal file
View file

@ -0,0 +1,29 @@
[PREVIOUS PAGE](../README.md) | [NEXT PAGE](connected_services/how_to/explanation.md)
CCash is an external ledger for in-game economies, running on a webserver with a RESTful API, exceptionally fast and lightweight written in C++.
While CCash can be used for anything that can interact with its API I think minecraft is a good example:
the currency model most Minecraft Servers adopt if any, is resource based, usually diamonds, this model is fraught with issues however:
* the primary issue is minecraft worlds are infinite leading to hyper inflation as everyone accrues more diamonds
* there is no central authority minting the currency, any consumer can introduce more diamonds to the system
* some resources are passively reapable, making the generation of currency a larger focus then of products
* locality is required for transaction
* theft is possible, ownership is possession based
CCash solves these issues and adds a level of abstraction, the main philosophy of CCash is to have fast core operations that other services build on
the CCash instance can be external to the game server
![image](external_diagram.png)
or on localhost:
![image](localhost_diagram.png)
running it local to the game server reduces latency for ComputerCraft connected services, fortunately CCash is sufficiently lightweight as to not impact performance on most setups.
**DISCLAIMER: if you are to run it locally and want to use ComputerCraft with it, make sure to add `127.0.0.1` to ComputerCraft's config section `allowed_domains`**
as CCash is just a means of keeping track of who has what, the economic system you use is self decided, ideally an admin should manage the instance to lower incentive to manipulate as they concievably already have supreme power over the given game.

BIN
docs/localhost_diagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

27
fbe/user_model.fbe Normal file
View file

@ -0,0 +1,27 @@
package bank_dom
struct Transaction
{
string from = "";
string to = "";
uint32 amount = 0;
timestamp time;
}
struct Logs
{
Transaction[] data;
}
struct User
{
uint32 balance = 0;
uint64 password = 0;
Logs? logs = null;
}
struct Global
{
string[] keys;
User[] users;
}

197
fbe/user_model/bank_dom.cpp Normal file
View file

@ -0,0 +1,197 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// Version: 1.7.0.0
#include "bank_dom.h"
namespace bank_dom {
Transaction::Transaction()
: from("")
, to("")
, amount((uint32_t)0ull)
, time((uint64_t)0ull)
{}
Transaction::Transaction(const std::string& arg_from, const std::string& arg_to, uint32_t arg_amount, uint64_t arg_time)
: from(arg_from)
, to(arg_to)
, amount(arg_amount)
, time(arg_time)
{}
bool Transaction::operator==(const Transaction& other) const noexcept
{
return (
true
);
}
bool Transaction::operator<(const Transaction& other) const noexcept
{
return false;
}
void Transaction::swap(Transaction& other) noexcept
{
using std::swap;
swap(from, other.from);
swap(to, other.to);
swap(amount, other.amount);
swap(time, other.time);
}
std::ostream& operator<<(std::ostream& stream, const Transaction& value)
{
stream << "Transaction(";
stream << "from="; stream << "\"" << value.from << "\"";
stream << ",to="; stream << "\"" << value.to << "\"";
stream << ",amount="; stream << value.amount;
stream << ",time="; stream << value.time;
stream << ")";
return stream;
}
Logs::Logs()
: data()
{}
Logs::Logs(const std::vector<::bank_dom::Transaction>& arg_data)
: data(arg_data)
{}
bool Logs::operator==(const Logs& other) const noexcept
{
return (
true
);
}
bool Logs::operator<(const Logs& other) const noexcept
{
return false;
}
void Logs::swap(Logs& other) noexcept
{
using std::swap;
swap(data, other.data);
}
std::ostream& operator<<(std::ostream& stream, const Logs& value)
{
stream << "Logs(";
{
bool first = true;
stream << "data=[" << value.data.size() << "][";
for (const auto& it : value.data)
{
stream << std::string(first ? "" : ",") << it;
first = false;
}
stream << "]";
}
stream << ")";
return stream;
}
User::User()
: balance((uint32_t)0ull)
, password((uint64_t)0ull)
, logs(std::nullopt)
{}
User::User(uint32_t arg_balance, uint64_t arg_password, const std::optional<::bank_dom::Logs>& arg_logs)
: balance(arg_balance)
, password(arg_password)
, logs(arg_logs)
{}
bool User::operator==(const User& other) const noexcept
{
return (
true
);
}
bool User::operator<(const User& other) const noexcept
{
return false;
}
void User::swap(User& other) noexcept
{
using std::swap;
swap(balance, other.balance);
swap(password, other.password);
swap(logs, other.logs);
}
std::ostream& operator<<(std::ostream& stream, const User& value)
{
stream << "User(";
stream << "balance="; stream << value.balance;
stream << ",password="; stream << value.password;
stream << ",logs="; if (value.logs) stream << *value.logs; else stream << "null";
stream << ")";
return stream;
}
Global::Global()
: keys()
, users()
{}
Global::Global(const std::vector<std::string>& arg_keys, const std::vector<::bank_dom::User>& arg_users)
: keys(arg_keys)
, users(arg_users)
{}
bool Global::operator==(const Global& other) const noexcept
{
return (
true
);
}
bool Global::operator<(const Global& other) const noexcept
{
return false;
}
void Global::swap(Global& other) noexcept
{
using std::swap;
swap(keys, other.keys);
swap(users, other.users);
}
std::ostream& operator<<(std::ostream& stream, const Global& value)
{
stream << "Global(";
{
bool first = true;
stream << "keys=[" << value.keys.size() << "][";
for (const auto& it : value.keys)
{
stream << std::string(first ? "" : ",") << "\"" << it << "\"";
first = false;
}
stream << "]";
}
{
bool first = true;
stream << ",users=[" << value.users.size() << "][";
for (const auto& it : value.users)
{
stream << std::string(first ? "" : ",") << it;
first = false;
}
stream << "]";
}
stream << ")";
return stream;
}
} // namespace bank_dom

238
fbe/user_model/bank_dom.h Normal file
View file

@ -0,0 +1,238 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// Version: 1.7.0.0
#pragma once
#if defined(__clang__)
#pragma clang system_header
#elif defined(__GNUC__)
#pragma GCC system_header
#elif defined(_MSC_VER)
#pragma system_header
#endif
#include "fbe.h"
namespace bank_dom {
using namespace FBE;
} // namespace bank_dom
namespace FBE {
using namespace ::bank_dom;
} // namespace FBE
namespace bank_dom {
struct Transaction
{
std::string from;
std::string to;
uint32_t amount;
uint64_t time;
size_t fbe_type() const noexcept { return 1; }
Transaction();
Transaction(const std::string& arg_from, const std::string& arg_to, uint32_t arg_amount, uint64_t arg_time);
Transaction(const Transaction& other) = default;
Transaction(Transaction&& other) = default;
~Transaction() = default;
Transaction& operator=(const Transaction& other) = default;
Transaction& operator=(Transaction&& other) = default;
bool operator==(const Transaction& other) const noexcept;
bool operator!=(const Transaction& other) const noexcept { return !operator==(other); }
bool operator<(const Transaction& other) const noexcept;
bool operator<=(const Transaction& other) const noexcept { return operator<(other) || operator==(other); }
bool operator>(const Transaction& other) const noexcept { return !operator<=(other); }
bool operator>=(const Transaction& other) const noexcept { return !operator<(other); }
std::string string() const { std::stringstream ss; ss << *this; return ss.str(); }
friend std::ostream& operator<<(std::ostream& stream, const Transaction& value);
void swap(Transaction& other) noexcept;
friend void swap(Transaction& value1, Transaction& value2) noexcept { value1.swap(value2); }
};
} // namespace bank_dom
namespace std {
template<>
struct hash<bank_dom::Transaction>
{
typedef bank_dom::Transaction argument_type;
typedef size_t result_type;
result_type operator() (const argument_type& value) const
{
result_type result = 17;
return result;
}
};
} // namespace std
namespace bank_dom {
struct Logs
{
std::vector<::bank_dom::Transaction> data;
size_t fbe_type() const noexcept { return 2; }
Logs();
explicit Logs(const std::vector<::bank_dom::Transaction>& arg_data);
Logs(const Logs& other) = default;
Logs(Logs&& other) = default;
~Logs() = default;
Logs& operator=(const Logs& other) = default;
Logs& operator=(Logs&& other) = default;
bool operator==(const Logs& other) const noexcept;
bool operator!=(const Logs& other) const noexcept { return !operator==(other); }
bool operator<(const Logs& other) const noexcept;
bool operator<=(const Logs& other) const noexcept { return operator<(other) || operator==(other); }
bool operator>(const Logs& other) const noexcept { return !operator<=(other); }
bool operator>=(const Logs& other) const noexcept { return !operator<(other); }
std::string string() const { std::stringstream ss; ss << *this; return ss.str(); }
friend std::ostream& operator<<(std::ostream& stream, const Logs& value);
void swap(Logs& other) noexcept;
friend void swap(Logs& value1, Logs& value2) noexcept { value1.swap(value2); }
};
} // namespace bank_dom
namespace std {
template<>
struct hash<bank_dom::Logs>
{
typedef bank_dom::Logs argument_type;
typedef size_t result_type;
result_type operator() (const argument_type& value) const
{
result_type result = 17;
return result;
}
};
} // namespace std
namespace bank_dom {
struct User
{
uint32_t balance;
uint64_t password;
std::optional<::bank_dom::Logs> logs;
size_t fbe_type() const noexcept { return 3; }
User();
User(uint32_t arg_balance, uint64_t arg_password, const std::optional<::bank_dom::Logs>& arg_logs);
User(const User& other) = default;
User(User&& other) = default;
~User() = default;
User& operator=(const User& other) = default;
User& operator=(User&& other) = default;
bool operator==(const User& other) const noexcept;
bool operator!=(const User& other) const noexcept { return !operator==(other); }
bool operator<(const User& other) const noexcept;
bool operator<=(const User& other) const noexcept { return operator<(other) || operator==(other); }
bool operator>(const User& other) const noexcept { return !operator<=(other); }
bool operator>=(const User& other) const noexcept { return !operator<(other); }
std::string string() const { std::stringstream ss; ss << *this; return ss.str(); }
friend std::ostream& operator<<(std::ostream& stream, const User& value);
void swap(User& other) noexcept;
friend void swap(User& value1, User& value2) noexcept { value1.swap(value2); }
};
} // namespace bank_dom
namespace std {
template<>
struct hash<bank_dom::User>
{
typedef bank_dom::User argument_type;
typedef size_t result_type;
result_type operator() (const argument_type& value) const
{
result_type result = 17;
return result;
}
};
} // namespace std
namespace bank_dom {
struct Global
{
std::vector<std::string> keys;
std::vector<::bank_dom::User> users;
size_t fbe_type() const noexcept { return 4; }
Global();
Global(const std::vector<std::string>& arg_keys, const std::vector<::bank_dom::User>& arg_users);
Global(const Global& other) = default;
Global(Global&& other) = default;
~Global() = default;
Global& operator=(const Global& other) = default;
Global& operator=(Global&& other) = default;
bool operator==(const Global& other) const noexcept;
bool operator!=(const Global& other) const noexcept { return !operator==(other); }
bool operator<(const Global& other) const noexcept;
bool operator<=(const Global& other) const noexcept { return operator<(other) || operator==(other); }
bool operator>(const Global& other) const noexcept { return !operator<=(other); }
bool operator>=(const Global& other) const noexcept { return !operator<(other); }
std::string string() const { std::stringstream ss; ss << *this; return ss.str(); }
friend std::ostream& operator<<(std::ostream& stream, const Global& value);
void swap(Global& other) noexcept;
friend void swap(Global& value1, Global& value2) noexcept { value1.swap(value2); }
};
} // namespace bank_dom
namespace std {
template<>
struct hash<bank_dom::Global>
{
typedef bank_dom::Global argument_type;
typedef size_t result_type;
result_type operator() (const argument_type& value) const
{
result_type result = 17;
return result;
}
};
} // namespace std
namespace bank_dom {
} // namespace bank_dom

View file

@ -0,0 +1,638 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// Version: 1.7.0.0
#include "bank_dom_final_models.h"
namespace FBE {
FinalModel<::bank_dom::Transaction>::FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, from(buffer, 0)
, to(buffer, 0)
, amount(buffer, 0)
, time(buffer, 0)
{}
size_t FinalModel<::bank_dom::Transaction>::fbe_allocation_size(const ::bank_dom::Transaction& fbe_value) const noexcept
{
size_t fbe_result = 0
+ from.fbe_allocation_size(fbe_value.from)
+ to.fbe_allocation_size(fbe_value.to)
+ amount.fbe_allocation_size(fbe_value.amount)
+ time.fbe_allocation_size(fbe_value.time)
;
return fbe_result;
}
size_t FinalModel<::bank_dom::Transaction>::verify() const noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = verify_fields();
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::Transaction>::verify_fields() const noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_field_size;
from.fbe_offset(fbe_current_offset);
fbe_field_size = from.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
to.fbe_offset(fbe_current_offset);
fbe_field_size = to.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
amount.fbe_offset(fbe_current_offset);
fbe_field_size = amount.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
time.fbe_offset(fbe_current_offset);
fbe_field_size = time.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
return fbe_current_offset;
}
size_t FinalModel<::bank_dom::Transaction>::get(::bank_dom::Transaction& fbe_value) const noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = get_fields(fbe_value);
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::Transaction>::get_fields(::bank_dom::Transaction& fbe_value) const noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_current_size = 0;
size_t fbe_field_size;
from.fbe_offset(fbe_current_offset);
fbe_field_size = from.get(fbe_value.from);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
to.fbe_offset(fbe_current_offset);
fbe_field_size = to.get(fbe_value.to);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
amount.fbe_offset(fbe_current_offset);
fbe_field_size = amount.get(fbe_value.amount);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
time.fbe_offset(fbe_current_offset);
fbe_field_size = time.get(fbe_value.time);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
return fbe_current_size;
}
size_t FinalModel<::bank_dom::Transaction>::set(const ::bank_dom::Transaction& fbe_value) noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = set_fields(fbe_value);
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::Transaction>::set_fields(const ::bank_dom::Transaction& fbe_value) noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_current_size = 0;
size_t fbe_field_size;
from.fbe_offset(fbe_current_offset);
fbe_field_size = from.set(fbe_value.from);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
to.fbe_offset(fbe_current_offset);
fbe_field_size = to.set(fbe_value.to);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
amount.fbe_offset(fbe_current_offset);
fbe_field_size = amount.set(fbe_value.amount);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
time.fbe_offset(fbe_current_offset);
fbe_field_size = time.set(fbe_value.time);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
return fbe_current_size;
}
namespace bank_dom {
bool TransactionFinalModel::verify()
{
if ((this->buffer().offset() + _model.fbe_offset()) > this->buffer().size())
return false;
size_t fbe_struct_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8));
size_t fbe_struct_type = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4));
if ((fbe_struct_size == 0) || (fbe_struct_type != fbe_type()))
return false;
return ((8 + _model.verify()) == fbe_struct_size);
}
size_t TransactionFinalModel::serialize(const ::bank_dom::Transaction& value)
{
size_t fbe_initial_size = this->buffer().size();
uint32_t fbe_struct_type = (uint32_t)fbe_type();
uint32_t fbe_struct_size = (uint32_t)(8 + _model.fbe_allocation_size(value));
uint32_t fbe_struct_offset = (uint32_t)(this->buffer().allocate(fbe_struct_size) - this->buffer().offset());
assert(((this->buffer().offset() + fbe_struct_offset + fbe_struct_size) <= this->buffer().size()) && "Model is broken!");
if ((this->buffer().offset() + fbe_struct_offset + fbe_struct_size) > this->buffer().size())
return 0;
fbe_struct_size = (uint32_t)(8 + _model.set(value));
this->buffer().resize(fbe_initial_size + fbe_struct_size);
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8)) = fbe_struct_size;
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4)) = fbe_struct_type;
return fbe_struct_size;
}
size_t TransactionFinalModel::deserialize(::bank_dom::Transaction& value) const noexcept
{
assert(((this->buffer().offset() + _model.fbe_offset()) <= this->buffer().size()) && "Model is broken!");
if ((this->buffer().offset() + _model.fbe_offset()) > this->buffer().size())
return 0;
size_t fbe_struct_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8));
size_t fbe_struct_type = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4));
assert(((fbe_struct_size > 0) && (fbe_struct_type == fbe_type())) && "Model is broken!");
if ((fbe_struct_size == 0) || (fbe_struct_type != fbe_type()))
return 8;
return 8 + _model.get(value);
}
} // namespace bank_dom
FinalModel<::bank_dom::Logs>::FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, data(buffer, 0)
{}
size_t FinalModel<::bank_dom::Logs>::fbe_allocation_size(const ::bank_dom::Logs& fbe_value) const noexcept
{
size_t fbe_result = 0
+ data.fbe_allocation_size(fbe_value.data)
;
return fbe_result;
}
size_t FinalModel<::bank_dom::Logs>::verify() const noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = verify_fields();
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::Logs>::verify_fields() const noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_field_size;
data.fbe_offset(fbe_current_offset);
fbe_field_size = data.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
return fbe_current_offset;
}
size_t FinalModel<::bank_dom::Logs>::get(::bank_dom::Logs& fbe_value) const noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = get_fields(fbe_value);
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::Logs>::get_fields(::bank_dom::Logs& fbe_value) const noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_current_size = 0;
size_t fbe_field_size;
data.fbe_offset(fbe_current_offset);
fbe_field_size = data.get(fbe_value.data);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
return fbe_current_size;
}
size_t FinalModel<::bank_dom::Logs>::set(const ::bank_dom::Logs& fbe_value) noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = set_fields(fbe_value);
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::Logs>::set_fields(const ::bank_dom::Logs& fbe_value) noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_current_size = 0;
size_t fbe_field_size;
data.fbe_offset(fbe_current_offset);
fbe_field_size = data.set(fbe_value.data);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
return fbe_current_size;
}
namespace bank_dom {
bool LogsFinalModel::verify()
{
if ((this->buffer().offset() + _model.fbe_offset()) > this->buffer().size())
return false;
size_t fbe_struct_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8));
size_t fbe_struct_type = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4));
if ((fbe_struct_size == 0) || (fbe_struct_type != fbe_type()))
return false;
return ((8 + _model.verify()) == fbe_struct_size);
}
size_t LogsFinalModel::serialize(const ::bank_dom::Logs& value)
{
size_t fbe_initial_size = this->buffer().size();
uint32_t fbe_struct_type = (uint32_t)fbe_type();
uint32_t fbe_struct_size = (uint32_t)(8 + _model.fbe_allocation_size(value));
uint32_t fbe_struct_offset = (uint32_t)(this->buffer().allocate(fbe_struct_size) - this->buffer().offset());
assert(((this->buffer().offset() + fbe_struct_offset + fbe_struct_size) <= this->buffer().size()) && "Model is broken!");
if ((this->buffer().offset() + fbe_struct_offset + fbe_struct_size) > this->buffer().size())
return 0;
fbe_struct_size = (uint32_t)(8 + _model.set(value));
this->buffer().resize(fbe_initial_size + fbe_struct_size);
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8)) = fbe_struct_size;
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4)) = fbe_struct_type;
return fbe_struct_size;
}
size_t LogsFinalModel::deserialize(::bank_dom::Logs& value) const noexcept
{
assert(((this->buffer().offset() + _model.fbe_offset()) <= this->buffer().size()) && "Model is broken!");
if ((this->buffer().offset() + _model.fbe_offset()) > this->buffer().size())
return 0;
size_t fbe_struct_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8));
size_t fbe_struct_type = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4));
assert(((fbe_struct_size > 0) && (fbe_struct_type == fbe_type())) && "Model is broken!");
if ((fbe_struct_size == 0) || (fbe_struct_type != fbe_type()))
return 8;
return 8 + _model.get(value);
}
} // namespace bank_dom
FinalModel<::bank_dom::User>::FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, balance(buffer, 0)
, password(buffer, 0)
, logs(buffer, 0)
{}
size_t FinalModel<::bank_dom::User>::fbe_allocation_size(const ::bank_dom::User& fbe_value) const noexcept
{
size_t fbe_result = 0
+ balance.fbe_allocation_size(fbe_value.balance)
+ password.fbe_allocation_size(fbe_value.password)
+ logs.fbe_allocation_size(fbe_value.logs)
;
return fbe_result;
}
size_t FinalModel<::bank_dom::User>::verify() const noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = verify_fields();
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::User>::verify_fields() const noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_field_size;
balance.fbe_offset(fbe_current_offset);
fbe_field_size = balance.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
password.fbe_offset(fbe_current_offset);
fbe_field_size = password.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
logs.fbe_offset(fbe_current_offset);
fbe_field_size = logs.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
return fbe_current_offset;
}
size_t FinalModel<::bank_dom::User>::get(::bank_dom::User& fbe_value) const noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = get_fields(fbe_value);
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::User>::get_fields(::bank_dom::User& fbe_value) const noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_current_size = 0;
size_t fbe_field_size;
balance.fbe_offset(fbe_current_offset);
fbe_field_size = balance.get(fbe_value.balance);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
password.fbe_offset(fbe_current_offset);
fbe_field_size = password.get(fbe_value.password);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
logs.fbe_offset(fbe_current_offset);
fbe_field_size = logs.get(fbe_value.logs);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
return fbe_current_size;
}
size_t FinalModel<::bank_dom::User>::set(const ::bank_dom::User& fbe_value) noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = set_fields(fbe_value);
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::User>::set_fields(const ::bank_dom::User& fbe_value) noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_current_size = 0;
size_t fbe_field_size;
balance.fbe_offset(fbe_current_offset);
fbe_field_size = balance.set(fbe_value.balance);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
password.fbe_offset(fbe_current_offset);
fbe_field_size = password.set(fbe_value.password);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
logs.fbe_offset(fbe_current_offset);
fbe_field_size = logs.set(fbe_value.logs);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
return fbe_current_size;
}
namespace bank_dom {
bool UserFinalModel::verify()
{
if ((this->buffer().offset() + _model.fbe_offset()) > this->buffer().size())
return false;
size_t fbe_struct_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8));
size_t fbe_struct_type = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4));
if ((fbe_struct_size == 0) || (fbe_struct_type != fbe_type()))
return false;
return ((8 + _model.verify()) == fbe_struct_size);
}
size_t UserFinalModel::serialize(const ::bank_dom::User& value)
{
size_t fbe_initial_size = this->buffer().size();
uint32_t fbe_struct_type = (uint32_t)fbe_type();
uint32_t fbe_struct_size = (uint32_t)(8 + _model.fbe_allocation_size(value));
uint32_t fbe_struct_offset = (uint32_t)(this->buffer().allocate(fbe_struct_size) - this->buffer().offset());
assert(((this->buffer().offset() + fbe_struct_offset + fbe_struct_size) <= this->buffer().size()) && "Model is broken!");
if ((this->buffer().offset() + fbe_struct_offset + fbe_struct_size) > this->buffer().size())
return 0;
fbe_struct_size = (uint32_t)(8 + _model.set(value));
this->buffer().resize(fbe_initial_size + fbe_struct_size);
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8)) = fbe_struct_size;
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4)) = fbe_struct_type;
return fbe_struct_size;
}
size_t UserFinalModel::deserialize(::bank_dom::User& value) const noexcept
{
assert(((this->buffer().offset() + _model.fbe_offset()) <= this->buffer().size()) && "Model is broken!");
if ((this->buffer().offset() + _model.fbe_offset()) > this->buffer().size())
return 0;
size_t fbe_struct_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8));
size_t fbe_struct_type = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4));
assert(((fbe_struct_size > 0) && (fbe_struct_type == fbe_type())) && "Model is broken!");
if ((fbe_struct_size == 0) || (fbe_struct_type != fbe_type()))
return 8;
return 8 + _model.get(value);
}
} // namespace bank_dom
FinalModel<::bank_dom::Global>::FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, keys(buffer, 0)
, users(buffer, 0)
{}
size_t FinalModel<::bank_dom::Global>::fbe_allocation_size(const ::bank_dom::Global& fbe_value) const noexcept
{
size_t fbe_result = 0
+ keys.fbe_allocation_size(fbe_value.keys)
+ users.fbe_allocation_size(fbe_value.users)
;
return fbe_result;
}
size_t FinalModel<::bank_dom::Global>::verify() const noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = verify_fields();
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::Global>::verify_fields() const noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_field_size;
keys.fbe_offset(fbe_current_offset);
fbe_field_size = keys.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
users.fbe_offset(fbe_current_offset);
fbe_field_size = users.verify();
if (fbe_field_size == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_current_offset += fbe_field_size;
return fbe_current_offset;
}
size_t FinalModel<::bank_dom::Global>::get(::bank_dom::Global& fbe_value) const noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = get_fields(fbe_value);
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::Global>::get_fields(::bank_dom::Global& fbe_value) const noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_current_size = 0;
size_t fbe_field_size;
keys.fbe_offset(fbe_current_offset);
fbe_field_size = keys.get(fbe_value.keys);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
users.fbe_offset(fbe_current_offset);
fbe_field_size = users.get(fbe_value.users);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
return fbe_current_size;
}
size_t FinalModel<::bank_dom::Global>::set(const ::bank_dom::Global& fbe_value) noexcept
{
_buffer.shift(fbe_offset());
size_t fbe_result = set_fields(fbe_value);
_buffer.unshift(fbe_offset());
return fbe_result;
}
size_t FinalModel<::bank_dom::Global>::set_fields(const ::bank_dom::Global& fbe_value) noexcept
{
size_t fbe_current_offset = 0;
size_t fbe_current_size = 0;
size_t fbe_field_size;
keys.fbe_offset(fbe_current_offset);
fbe_field_size = keys.set(fbe_value.keys);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
users.fbe_offset(fbe_current_offset);
fbe_field_size = users.set(fbe_value.users);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
return fbe_current_size;
}
namespace bank_dom {
bool GlobalFinalModel::verify()
{
if ((this->buffer().offset() + _model.fbe_offset()) > this->buffer().size())
return false;
size_t fbe_struct_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8));
size_t fbe_struct_type = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4));
if ((fbe_struct_size == 0) || (fbe_struct_type != fbe_type()))
return false;
return ((8 + _model.verify()) == fbe_struct_size);
}
size_t GlobalFinalModel::serialize(const ::bank_dom::Global& value)
{
size_t fbe_initial_size = this->buffer().size();
uint32_t fbe_struct_type = (uint32_t)fbe_type();
uint32_t fbe_struct_size = (uint32_t)(8 + _model.fbe_allocation_size(value));
uint32_t fbe_struct_offset = (uint32_t)(this->buffer().allocate(fbe_struct_size) - this->buffer().offset());
assert(((this->buffer().offset() + fbe_struct_offset + fbe_struct_size) <= this->buffer().size()) && "Model is broken!");
if ((this->buffer().offset() + fbe_struct_offset + fbe_struct_size) > this->buffer().size())
return 0;
fbe_struct_size = (uint32_t)(8 + _model.set(value));
this->buffer().resize(fbe_initial_size + fbe_struct_size);
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8)) = fbe_struct_size;
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4)) = fbe_struct_type;
return fbe_struct_size;
}
size_t GlobalFinalModel::deserialize(::bank_dom::Global& value) const noexcept
{
assert(((this->buffer().offset() + _model.fbe_offset()) <= this->buffer().size()) && "Model is broken!");
if ((this->buffer().offset() + _model.fbe_offset()) > this->buffer().size())
return 0;
size_t fbe_struct_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 8));
size_t fbe_struct_type = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + _model.fbe_offset() - 4));
assert(((fbe_struct_size > 0) && (fbe_struct_type == fbe_type())) && "Model is broken!");
if ((fbe_struct_size == 0) || (fbe_struct_type != fbe_type()))
return 8;
return 8 + _model.get(value);
}
} // namespace bank_dom
} // namespace FBE

View file

@ -0,0 +1,320 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// Version: 1.7.0.0
#pragma once
#if defined(__clang__)
#pragma clang system_header
#elif defined(__GNUC__)
#pragma GCC system_header
#elif defined(_MSC_VER)
#pragma system_header
#endif
#include "fbe_final_models.h"
#include "bank_dom.h"
namespace FBE {
// Fast Binary Encoding ::bank_dom::Transaction final model
template <>
class FinalModel<::bank_dom::Transaction>
{
public:
FinalModel(FBEBuffer& buffer, size_t offset) noexcept;
// Get the allocation size
size_t fbe_allocation_size(const ::bank_dom::Transaction& fbe_value) const noexcept;
// Get the final offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the final offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Get the final type
static constexpr size_t fbe_type() noexcept { return 1; }
// Shift the current final offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current final offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the struct value is valid
size_t verify() const noexcept;
// Check if the struct fields are valid
size_t verify_fields() const noexcept;
// Get the struct value
size_t get(::bank_dom::Transaction& fbe_value) const noexcept;
// Get the struct fields values
size_t get_fields(::bank_dom::Transaction& fbe_value) const noexcept;
// Set the struct value
size_t set(const ::bank_dom::Transaction& fbe_value) noexcept;
// Set the struct fields values
size_t set_fields(const ::bank_dom::Transaction& fbe_value) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
public:
FinalModel<std::string> from;
FinalModel<std::string> to;
FinalModel<uint32_t> amount;
FinalModel<uint64_t> time;
};
namespace bank_dom {
// Fast Binary Encoding Transaction final model
class TransactionFinalModel : public FBE::Model
{
public:
TransactionFinalModel() : _model(this->buffer(), 8) {}
TransactionFinalModel(const std::shared_ptr<FBEBuffer>& buffer) : FBE::Model(buffer), _model(this->buffer(), 8) {}
// Get the model type
static constexpr size_t fbe_type() noexcept { return FinalModel<::bank_dom::Transaction>::fbe_type(); }
// Check if the struct value is valid
bool verify();
// Serialize the struct value
size_t serialize(const ::bank_dom::Transaction& value);
// Deserialize the struct value
size_t deserialize(::bank_dom::Transaction& value) const noexcept;
// Move to the next struct value
void next(size_t prev) noexcept { _model.fbe_shift(prev); }
private:
FinalModel<::bank_dom::Transaction> _model;
};
} // namespace bank_dom
// Fast Binary Encoding ::bank_dom::Logs final model
template <>
class FinalModel<::bank_dom::Logs>
{
public:
FinalModel(FBEBuffer& buffer, size_t offset) noexcept;
// Get the allocation size
size_t fbe_allocation_size(const ::bank_dom::Logs& fbe_value) const noexcept;
// Get the final offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the final offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Get the final type
static constexpr size_t fbe_type() noexcept { return 2; }
// Shift the current final offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current final offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the struct value is valid
size_t verify() const noexcept;
// Check if the struct fields are valid
size_t verify_fields() const noexcept;
// Get the struct value
size_t get(::bank_dom::Logs& fbe_value) const noexcept;
// Get the struct fields values
size_t get_fields(::bank_dom::Logs& fbe_value) const noexcept;
// Set the struct value
size_t set(const ::bank_dom::Logs& fbe_value) noexcept;
// Set the struct fields values
size_t set_fields(const ::bank_dom::Logs& fbe_value) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
public:
FinalModelVector<::bank_dom::Transaction> data;
};
namespace bank_dom {
// Fast Binary Encoding Logs final model
class LogsFinalModel : public FBE::Model
{
public:
LogsFinalModel() : _model(this->buffer(), 8) {}
LogsFinalModel(const std::shared_ptr<FBEBuffer>& buffer) : FBE::Model(buffer), _model(this->buffer(), 8) {}
// Get the model type
static constexpr size_t fbe_type() noexcept { return FinalModel<::bank_dom::Logs>::fbe_type(); }
// Check if the struct value is valid
bool verify();
// Serialize the struct value
size_t serialize(const ::bank_dom::Logs& value);
// Deserialize the struct value
size_t deserialize(::bank_dom::Logs& value) const noexcept;
// Move to the next struct value
void next(size_t prev) noexcept { _model.fbe_shift(prev); }
private:
FinalModel<::bank_dom::Logs> _model;
};
} // namespace bank_dom
// Fast Binary Encoding ::bank_dom::User final model
template <>
class FinalModel<::bank_dom::User>
{
public:
FinalModel(FBEBuffer& buffer, size_t offset) noexcept;
// Get the allocation size
size_t fbe_allocation_size(const ::bank_dom::User& fbe_value) const noexcept;
// Get the final offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the final offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Get the final type
static constexpr size_t fbe_type() noexcept { return 3; }
// Shift the current final offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current final offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the struct value is valid
size_t verify() const noexcept;
// Check if the struct fields are valid
size_t verify_fields() const noexcept;
// Get the struct value
size_t get(::bank_dom::User& fbe_value) const noexcept;
// Get the struct fields values
size_t get_fields(::bank_dom::User& fbe_value) const noexcept;
// Set the struct value
size_t set(const ::bank_dom::User& fbe_value) noexcept;
// Set the struct fields values
size_t set_fields(const ::bank_dom::User& fbe_value) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
public:
FinalModel<uint32_t> balance;
FinalModel<uint64_t> password;
FinalModel<std::optional<::bank_dom::Logs>> logs;
};
namespace bank_dom {
// Fast Binary Encoding User final model
class UserFinalModel : public FBE::Model
{
public:
UserFinalModel() : _model(this->buffer(), 8) {}
UserFinalModel(const std::shared_ptr<FBEBuffer>& buffer) : FBE::Model(buffer), _model(this->buffer(), 8) {}
// Get the model type
static constexpr size_t fbe_type() noexcept { return FinalModel<::bank_dom::User>::fbe_type(); }
// Check if the struct value is valid
bool verify();
// Serialize the struct value
size_t serialize(const ::bank_dom::User& value);
// Deserialize the struct value
size_t deserialize(::bank_dom::User& value) const noexcept;
// Move to the next struct value
void next(size_t prev) noexcept { _model.fbe_shift(prev); }
private:
FinalModel<::bank_dom::User> _model;
};
} // namespace bank_dom
// Fast Binary Encoding ::bank_dom::Global final model
template <>
class FinalModel<::bank_dom::Global>
{
public:
FinalModel(FBEBuffer& buffer, size_t offset) noexcept;
// Get the allocation size
size_t fbe_allocation_size(const ::bank_dom::Global& fbe_value) const noexcept;
// Get the final offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the final offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Get the final type
static constexpr size_t fbe_type() noexcept { return 4; }
// Shift the current final offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current final offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the struct value is valid
size_t verify() const noexcept;
// Check if the struct fields are valid
size_t verify_fields() const noexcept;
// Get the struct value
size_t get(::bank_dom::Global& fbe_value) const noexcept;
// Get the struct fields values
size_t get_fields(::bank_dom::Global& fbe_value) const noexcept;
// Set the struct value
size_t set(const ::bank_dom::Global& fbe_value) noexcept;
// Set the struct fields values
size_t set_fields(const ::bank_dom::Global& fbe_value) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
public:
FinalModelVector<std::string> keys;
FinalModelVector<::bank_dom::User> users;
};
namespace bank_dom {
// Fast Binary Encoding Global final model
class GlobalFinalModel : public FBE::Model
{
public:
GlobalFinalModel() : _model(this->buffer(), 8) {}
GlobalFinalModel(const std::shared_ptr<FBEBuffer>& buffer) : FBE::Model(buffer), _model(this->buffer(), 8) {}
// Get the model type
static constexpr size_t fbe_type() noexcept { return FinalModel<::bank_dom::Global>::fbe_type(); }
// Check if the struct value is valid
bool verify();
// Serialize the struct value
size_t serialize(const ::bank_dom::Global& value);
// Deserialize the struct value
size_t deserialize(::bank_dom::Global& value) const noexcept;
// Move to the next struct value
void next(size_t prev) noexcept { _model.fbe_shift(prev); }
private:
FinalModel<::bank_dom::Global> _model;
};
} // namespace bank_dom
} // namespace FBE

View file

@ -0,0 +1,930 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// Version: 1.7.0.0
#include "bank_dom_models.h"
namespace FBE {
FieldModel<::bank_dom::Transaction>::FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, from(buffer, 4 + 4)
, to(buffer, from.fbe_offset() + from.fbe_size())
, amount(buffer, to.fbe_offset() + to.fbe_size())
, time(buffer, amount.fbe_offset() + amount.fbe_size())
{}
size_t FieldModel<::bank_dom::Transaction>::fbe_body() const noexcept
{
size_t fbe_result = 4 + 4
+ from.fbe_size()
+ to.fbe_size()
+ amount.fbe_size()
+ time.fbe_size()
;
return fbe_result;
}
size_t FieldModel<::bank_dom::Transaction>::fbe_extra() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4) > _buffer.size()))
return 0;
_buffer.shift(fbe_struct_offset);
size_t fbe_result = fbe_body()
+ from.fbe_extra()
+ to.fbe_extra()
+ amount.fbe_extra()
+ time.fbe_extra()
;
_buffer.unshift(fbe_struct_offset);
return fbe_result;
}
bool FieldModel<::bank_dom::Transaction>::verify(bool fbe_verify_type) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return true;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4 + 4) > _buffer.size()))
return false;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset));
if (fbe_struct_size < (4 + 4))
return false;
uint32_t fbe_struct_type = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset + 4));
if (fbe_verify_type && (fbe_struct_type != fbe_type()))
return false;
_buffer.shift(fbe_struct_offset);
bool fbe_result = verify_fields(fbe_struct_size);
_buffer.unshift(fbe_struct_offset);
return fbe_result;
}
bool FieldModel<::bank_dom::Transaction>::verify_fields(size_t fbe_struct_size) const noexcept
{
size_t fbe_current_size = 4 + 4;
if ((fbe_current_size + from.fbe_size()) > fbe_struct_size)
return true;
if (!from.verify())
return false;
fbe_current_size += from.fbe_size();
if ((fbe_current_size + to.fbe_size()) > fbe_struct_size)
return true;
if (!to.verify())
return false;
fbe_current_size += to.fbe_size();
if ((fbe_current_size + amount.fbe_size()) > fbe_struct_size)
return true;
if (!amount.verify())
return false;
fbe_current_size += amount.fbe_size();
if ((fbe_current_size + time.fbe_size()) > fbe_struct_size)
return true;
if (!time.verify())
return false;
fbe_current_size += time.fbe_size();
return true;
}
size_t FieldModel<::bank_dom::Transaction>::get_begin() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((fbe_struct_offset > 0) && ((_buffer.offset() + fbe_struct_offset + 4 + 4) <= _buffer.size())) && "Model is broken!");
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4 + 4) > _buffer.size()))
return 0;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset));
assert((fbe_struct_size >= (4 + 4)) && "Model is broken!");
if (fbe_struct_size < (4 + 4))
return 0;
_buffer.shift(fbe_struct_offset);
return fbe_struct_offset;
}
void FieldModel<::bank_dom::Transaction>::get_end(size_t fbe_begin) const noexcept
{
_buffer.unshift(fbe_begin);
}
void FieldModel<::bank_dom::Transaction>::get(::bank_dom::Transaction& fbe_value) const noexcept
{
size_t fbe_begin = get_begin();
if (fbe_begin == 0)
return;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset()));
get_fields(fbe_value, fbe_struct_size);
get_end(fbe_begin);
}
void FieldModel<::bank_dom::Transaction>::get_fields(::bank_dom::Transaction& fbe_value, size_t fbe_struct_size) const noexcept
{
size_t fbe_current_size = 4 + 4;
if ((fbe_current_size + from.fbe_size()) <= fbe_struct_size)
from.get(fbe_value.from, "");
else
fbe_value.from = "";
fbe_current_size += from.fbe_size();
if ((fbe_current_size + to.fbe_size()) <= fbe_struct_size)
to.get(fbe_value.to, "");
else
fbe_value.to = "";
fbe_current_size += to.fbe_size();
if ((fbe_current_size + amount.fbe_size()) <= fbe_struct_size)
amount.get(fbe_value.amount, (uint32_t)0ull);
else
fbe_value.amount = (uint32_t)0ull;
fbe_current_size += amount.fbe_size();
if ((fbe_current_size + time.fbe_size()) <= fbe_struct_size)
time.get(fbe_value.time);
else
fbe_value.time = (uint64_t)0ull;
fbe_current_size += time.fbe_size();
}
size_t FieldModel<::bank_dom::Transaction>::set_begin()
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_size = (uint32_t)fbe_body();
uint32_t fbe_struct_offset = (uint32_t)(_buffer.allocate(fbe_struct_size) - _buffer.offset());
assert(((fbe_struct_offset > 0) && ((_buffer.offset() + fbe_struct_offset + fbe_struct_size) <= _buffer.size())) && "Model is broken!");
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + fbe_struct_size) > _buffer.size()))
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_struct_offset;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset)) = fbe_struct_size;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset + 4)) = (uint32_t)fbe_type();
_buffer.shift(fbe_struct_offset);
return fbe_struct_offset;
}
void FieldModel<::bank_dom::Transaction>::set_end(size_t fbe_begin)
{
_buffer.unshift(fbe_begin);
}
void FieldModel<::bank_dom::Transaction>::set(const ::bank_dom::Transaction& fbe_value) noexcept
{
size_t fbe_begin = set_begin();
if (fbe_begin == 0)
return;
set_fields(fbe_value);
set_end(fbe_begin);
}
void FieldModel<::bank_dom::Transaction>::set_fields(const ::bank_dom::Transaction& fbe_value) noexcept
{
from.set(fbe_value.from);
to.set(fbe_value.to);
amount.set(fbe_value.amount);
time.set(fbe_value.time);
}
namespace bank_dom {
bool TransactionModel::verify()
{
if ((this->buffer().offset() + model.fbe_offset() - 4) > this->buffer().size())
return false;
uint32_t fbe_full_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4));
if (fbe_full_size < model.fbe_size())
return false;
return model.verify();
}
size_t TransactionModel::create_begin()
{
size_t fbe_begin = this->buffer().allocate(4 + model.fbe_size());
return fbe_begin;
}
size_t TransactionModel::create_end(size_t fbe_begin)
{
size_t fbe_end = this->buffer().size();
uint32_t fbe_full_size = (uint32_t)(fbe_end - fbe_begin);
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4)) = fbe_full_size;
return fbe_full_size;
}
size_t TransactionModel::serialize(const ::bank_dom::Transaction& value)
{
size_t fbe_begin = create_begin();
model.set(value);
size_t fbe_full_size = create_end(fbe_begin);
return fbe_full_size;
}
size_t TransactionModel::deserialize(::bank_dom::Transaction& value) const noexcept
{
if ((this->buffer().offset() + model.fbe_offset() - 4) > this->buffer().size())
return 0;
uint32_t fbe_full_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4));
assert((fbe_full_size >= model.fbe_size()) && "Model is broken!");
if (fbe_full_size < model.fbe_size())
return 0;
model.get(value);
return fbe_full_size;
}
} // namespace bank_dom
FieldModel<::bank_dom::Logs>::FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, data(buffer, 4 + 4)
{}
size_t FieldModel<::bank_dom::Logs>::fbe_body() const noexcept
{
size_t fbe_result = 4 + 4
+ data.fbe_size()
;
return fbe_result;
}
size_t FieldModel<::bank_dom::Logs>::fbe_extra() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4) > _buffer.size()))
return 0;
_buffer.shift(fbe_struct_offset);
size_t fbe_result = fbe_body()
+ data.fbe_extra()
;
_buffer.unshift(fbe_struct_offset);
return fbe_result;
}
bool FieldModel<::bank_dom::Logs>::verify(bool fbe_verify_type) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return true;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4 + 4) > _buffer.size()))
return false;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset));
if (fbe_struct_size < (4 + 4))
return false;
uint32_t fbe_struct_type = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset + 4));
if (fbe_verify_type && (fbe_struct_type != fbe_type()))
return false;
_buffer.shift(fbe_struct_offset);
bool fbe_result = verify_fields(fbe_struct_size);
_buffer.unshift(fbe_struct_offset);
return fbe_result;
}
bool FieldModel<::bank_dom::Logs>::verify_fields(size_t fbe_struct_size) const noexcept
{
size_t fbe_current_size = 4 + 4;
if ((fbe_current_size + data.fbe_size()) > fbe_struct_size)
return true;
if (!data.verify())
return false;
fbe_current_size += data.fbe_size();
return true;
}
size_t FieldModel<::bank_dom::Logs>::get_begin() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((fbe_struct_offset > 0) && ((_buffer.offset() + fbe_struct_offset + 4 + 4) <= _buffer.size())) && "Model is broken!");
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4 + 4) > _buffer.size()))
return 0;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset));
assert((fbe_struct_size >= (4 + 4)) && "Model is broken!");
if (fbe_struct_size < (4 + 4))
return 0;
_buffer.shift(fbe_struct_offset);
return fbe_struct_offset;
}
void FieldModel<::bank_dom::Logs>::get_end(size_t fbe_begin) const noexcept
{
_buffer.unshift(fbe_begin);
}
void FieldModel<::bank_dom::Logs>::get(::bank_dom::Logs& fbe_value) const noexcept
{
size_t fbe_begin = get_begin();
if (fbe_begin == 0)
return;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset()));
get_fields(fbe_value, fbe_struct_size);
get_end(fbe_begin);
}
void FieldModel<::bank_dom::Logs>::get_fields(::bank_dom::Logs& fbe_value, size_t fbe_struct_size) const noexcept
{
size_t fbe_current_size = 4 + 4;
if ((fbe_current_size + data.fbe_size()) <= fbe_struct_size)
data.get(fbe_value.data);
else
fbe_value.data.clear();
fbe_current_size += data.fbe_size();
}
size_t FieldModel<::bank_dom::Logs>::set_begin()
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_size = (uint32_t)fbe_body();
uint32_t fbe_struct_offset = (uint32_t)(_buffer.allocate(fbe_struct_size) - _buffer.offset());
assert(((fbe_struct_offset > 0) && ((_buffer.offset() + fbe_struct_offset + fbe_struct_size) <= _buffer.size())) && "Model is broken!");
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + fbe_struct_size) > _buffer.size()))
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_struct_offset;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset)) = fbe_struct_size;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset + 4)) = (uint32_t)fbe_type();
_buffer.shift(fbe_struct_offset);
return fbe_struct_offset;
}
void FieldModel<::bank_dom::Logs>::set_end(size_t fbe_begin)
{
_buffer.unshift(fbe_begin);
}
void FieldModel<::bank_dom::Logs>::set(const ::bank_dom::Logs& fbe_value) noexcept
{
size_t fbe_begin = set_begin();
if (fbe_begin == 0)
return;
set_fields(fbe_value);
set_end(fbe_begin);
}
void FieldModel<::bank_dom::Logs>::set_fields(const ::bank_dom::Logs& fbe_value) noexcept
{
data.set(fbe_value.data);
}
namespace bank_dom {
bool LogsModel::verify()
{
if ((this->buffer().offset() + model.fbe_offset() - 4) > this->buffer().size())
return false;
uint32_t fbe_full_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4));
if (fbe_full_size < model.fbe_size())
return false;
return model.verify();
}
size_t LogsModel::create_begin()
{
size_t fbe_begin = this->buffer().allocate(4 + model.fbe_size());
return fbe_begin;
}
size_t LogsModel::create_end(size_t fbe_begin)
{
size_t fbe_end = this->buffer().size();
uint32_t fbe_full_size = (uint32_t)(fbe_end - fbe_begin);
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4)) = fbe_full_size;
return fbe_full_size;
}
size_t LogsModel::serialize(const ::bank_dom::Logs& value)
{
size_t fbe_begin = create_begin();
model.set(value);
size_t fbe_full_size = create_end(fbe_begin);
return fbe_full_size;
}
size_t LogsModel::deserialize(::bank_dom::Logs& value) const noexcept
{
if ((this->buffer().offset() + model.fbe_offset() - 4) > this->buffer().size())
return 0;
uint32_t fbe_full_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4));
assert((fbe_full_size >= model.fbe_size()) && "Model is broken!");
if (fbe_full_size < model.fbe_size())
return 0;
model.get(value);
return fbe_full_size;
}
} // namespace bank_dom
FieldModel<::bank_dom::User>::FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, balance(buffer, 4 + 4)
, password(buffer, balance.fbe_offset() + balance.fbe_size())
, logs(buffer, password.fbe_offset() + password.fbe_size())
{}
size_t FieldModel<::bank_dom::User>::fbe_body() const noexcept
{
size_t fbe_result = 4 + 4
+ balance.fbe_size()
+ password.fbe_size()
+ logs.fbe_size()
;
return fbe_result;
}
size_t FieldModel<::bank_dom::User>::fbe_extra() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4) > _buffer.size()))
return 0;
_buffer.shift(fbe_struct_offset);
size_t fbe_result = fbe_body()
+ balance.fbe_extra()
+ password.fbe_extra()
+ logs.fbe_extra()
;
_buffer.unshift(fbe_struct_offset);
return fbe_result;
}
bool FieldModel<::bank_dom::User>::verify(bool fbe_verify_type) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return true;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4 + 4) > _buffer.size()))
return false;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset));
if (fbe_struct_size < (4 + 4))
return false;
uint32_t fbe_struct_type = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset + 4));
if (fbe_verify_type && (fbe_struct_type != fbe_type()))
return false;
_buffer.shift(fbe_struct_offset);
bool fbe_result = verify_fields(fbe_struct_size);
_buffer.unshift(fbe_struct_offset);
return fbe_result;
}
bool FieldModel<::bank_dom::User>::verify_fields(size_t fbe_struct_size) const noexcept
{
size_t fbe_current_size = 4 + 4;
if ((fbe_current_size + balance.fbe_size()) > fbe_struct_size)
return true;
if (!balance.verify())
return false;
fbe_current_size += balance.fbe_size();
if ((fbe_current_size + password.fbe_size()) > fbe_struct_size)
return true;
if (!password.verify())
return false;
fbe_current_size += password.fbe_size();
if ((fbe_current_size + logs.fbe_size()) > fbe_struct_size)
return true;
if (!logs.verify())
return false;
fbe_current_size += logs.fbe_size();
return true;
}
size_t FieldModel<::bank_dom::User>::get_begin() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((fbe_struct_offset > 0) && ((_buffer.offset() + fbe_struct_offset + 4 + 4) <= _buffer.size())) && "Model is broken!");
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4 + 4) > _buffer.size()))
return 0;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset));
assert((fbe_struct_size >= (4 + 4)) && "Model is broken!");
if (fbe_struct_size < (4 + 4))
return 0;
_buffer.shift(fbe_struct_offset);
return fbe_struct_offset;
}
void FieldModel<::bank_dom::User>::get_end(size_t fbe_begin) const noexcept
{
_buffer.unshift(fbe_begin);
}
void FieldModel<::bank_dom::User>::get(::bank_dom::User& fbe_value) const noexcept
{
size_t fbe_begin = get_begin();
if (fbe_begin == 0)
return;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset()));
get_fields(fbe_value, fbe_struct_size);
get_end(fbe_begin);
}
void FieldModel<::bank_dom::User>::get_fields(::bank_dom::User& fbe_value, size_t fbe_struct_size) const noexcept
{
size_t fbe_current_size = 4 + 4;
if ((fbe_current_size + balance.fbe_size()) <= fbe_struct_size)
balance.get(fbe_value.balance, (uint32_t)0ull);
else
fbe_value.balance = (uint32_t)0ull;
fbe_current_size += balance.fbe_size();
if ((fbe_current_size + password.fbe_size()) <= fbe_struct_size)
password.get(fbe_value.password, (uint64_t)0ull);
else
fbe_value.password = (uint64_t)0ull;
fbe_current_size += password.fbe_size();
if ((fbe_current_size + logs.fbe_size()) <= fbe_struct_size)
logs.get(fbe_value.logs, std::nullopt);
else
fbe_value.logs = std::nullopt;
fbe_current_size += logs.fbe_size();
}
size_t FieldModel<::bank_dom::User>::set_begin()
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_size = (uint32_t)fbe_body();
uint32_t fbe_struct_offset = (uint32_t)(_buffer.allocate(fbe_struct_size) - _buffer.offset());
assert(((fbe_struct_offset > 0) && ((_buffer.offset() + fbe_struct_offset + fbe_struct_size) <= _buffer.size())) && "Model is broken!");
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + fbe_struct_size) > _buffer.size()))
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_struct_offset;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset)) = fbe_struct_size;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset + 4)) = (uint32_t)fbe_type();
_buffer.shift(fbe_struct_offset);
return fbe_struct_offset;
}
void FieldModel<::bank_dom::User>::set_end(size_t fbe_begin)
{
_buffer.unshift(fbe_begin);
}
void FieldModel<::bank_dom::User>::set(const ::bank_dom::User& fbe_value) noexcept
{
size_t fbe_begin = set_begin();
if (fbe_begin == 0)
return;
set_fields(fbe_value);
set_end(fbe_begin);
}
void FieldModel<::bank_dom::User>::set_fields(const ::bank_dom::User& fbe_value) noexcept
{
balance.set(fbe_value.balance);
password.set(fbe_value.password);
logs.set(fbe_value.logs);
}
namespace bank_dom {
bool UserModel::verify()
{
if ((this->buffer().offset() + model.fbe_offset() - 4) > this->buffer().size())
return false;
uint32_t fbe_full_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4));
if (fbe_full_size < model.fbe_size())
return false;
return model.verify();
}
size_t UserModel::create_begin()
{
size_t fbe_begin = this->buffer().allocate(4 + model.fbe_size());
return fbe_begin;
}
size_t UserModel::create_end(size_t fbe_begin)
{
size_t fbe_end = this->buffer().size();
uint32_t fbe_full_size = (uint32_t)(fbe_end - fbe_begin);
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4)) = fbe_full_size;
return fbe_full_size;
}
size_t UserModel::serialize(const ::bank_dom::User& value)
{
size_t fbe_begin = create_begin();
model.set(value);
size_t fbe_full_size = create_end(fbe_begin);
return fbe_full_size;
}
size_t UserModel::deserialize(::bank_dom::User& value) const noexcept
{
if ((this->buffer().offset() + model.fbe_offset() - 4) > this->buffer().size())
return 0;
uint32_t fbe_full_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4));
assert((fbe_full_size >= model.fbe_size()) && "Model is broken!");
if (fbe_full_size < model.fbe_size())
return 0;
model.get(value);
return fbe_full_size;
}
} // namespace bank_dom
FieldModel<::bank_dom::Global>::FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, keys(buffer, 4 + 4)
, users(buffer, keys.fbe_offset() + keys.fbe_size())
{}
size_t FieldModel<::bank_dom::Global>::fbe_body() const noexcept
{
size_t fbe_result = 4 + 4
+ keys.fbe_size()
+ users.fbe_size()
;
return fbe_result;
}
size_t FieldModel<::bank_dom::Global>::fbe_extra() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4) > _buffer.size()))
return 0;
_buffer.shift(fbe_struct_offset);
size_t fbe_result = fbe_body()
+ keys.fbe_extra()
+ users.fbe_extra()
;
_buffer.unshift(fbe_struct_offset);
return fbe_result;
}
bool FieldModel<::bank_dom::Global>::verify(bool fbe_verify_type) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return true;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4 + 4) > _buffer.size()))
return false;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset));
if (fbe_struct_size < (4 + 4))
return false;
uint32_t fbe_struct_type = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset + 4));
if (fbe_verify_type && (fbe_struct_type != fbe_type()))
return false;
_buffer.shift(fbe_struct_offset);
bool fbe_result = verify_fields(fbe_struct_size);
_buffer.unshift(fbe_struct_offset);
return fbe_result;
}
bool FieldModel<::bank_dom::Global>::verify_fields(size_t fbe_struct_size) const noexcept
{
size_t fbe_current_size = 4 + 4;
if ((fbe_current_size + keys.fbe_size()) > fbe_struct_size)
return true;
if (!keys.verify())
return false;
fbe_current_size += keys.fbe_size();
if ((fbe_current_size + users.fbe_size()) > fbe_struct_size)
return true;
if (!users.verify())
return false;
fbe_current_size += users.fbe_size();
return true;
}
size_t FieldModel<::bank_dom::Global>::get_begin() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((fbe_struct_offset > 0) && ((_buffer.offset() + fbe_struct_offset + 4 + 4) <= _buffer.size())) && "Model is broken!");
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + 4 + 4) > _buffer.size()))
return 0;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset));
assert((fbe_struct_size >= (4 + 4)) && "Model is broken!");
if (fbe_struct_size < (4 + 4))
return 0;
_buffer.shift(fbe_struct_offset);
return fbe_struct_offset;
}
void FieldModel<::bank_dom::Global>::get_end(size_t fbe_begin) const noexcept
{
_buffer.unshift(fbe_begin);
}
void FieldModel<::bank_dom::Global>::get(::bank_dom::Global& fbe_value) const noexcept
{
size_t fbe_begin = get_begin();
if (fbe_begin == 0)
return;
uint32_t fbe_struct_size = *((const uint32_t*)(_buffer.data() + _buffer.offset()));
get_fields(fbe_value, fbe_struct_size);
get_end(fbe_begin);
}
void FieldModel<::bank_dom::Global>::get_fields(::bank_dom::Global& fbe_value, size_t fbe_struct_size) const noexcept
{
size_t fbe_current_size = 4 + 4;
if ((fbe_current_size + keys.fbe_size()) <= fbe_struct_size)
keys.get(fbe_value.keys);
else
fbe_value.keys.clear();
fbe_current_size += keys.fbe_size();
if ((fbe_current_size + users.fbe_size()) <= fbe_struct_size)
users.get(fbe_value.users);
else
fbe_value.users.clear();
fbe_current_size += users.fbe_size();
}
size_t FieldModel<::bank_dom::Global>::set_begin()
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_struct_size = (uint32_t)fbe_body();
uint32_t fbe_struct_offset = (uint32_t)(_buffer.allocate(fbe_struct_size) - _buffer.offset());
assert(((fbe_struct_offset > 0) && ((_buffer.offset() + fbe_struct_offset + fbe_struct_size) <= _buffer.size())) && "Model is broken!");
if ((fbe_struct_offset == 0) || ((_buffer.offset() + fbe_struct_offset + fbe_struct_size) > _buffer.size()))
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_struct_offset;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset)) = fbe_struct_size;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_struct_offset + 4)) = (uint32_t)fbe_type();
_buffer.shift(fbe_struct_offset);
return fbe_struct_offset;
}
void FieldModel<::bank_dom::Global>::set_end(size_t fbe_begin)
{
_buffer.unshift(fbe_begin);
}
void FieldModel<::bank_dom::Global>::set(const ::bank_dom::Global& fbe_value) noexcept
{
size_t fbe_begin = set_begin();
if (fbe_begin == 0)
return;
set_fields(fbe_value);
set_end(fbe_begin);
}
void FieldModel<::bank_dom::Global>::set_fields(const ::bank_dom::Global& fbe_value) noexcept
{
keys.set(fbe_value.keys);
users.set(fbe_value.users);
}
namespace bank_dom {
bool GlobalModel::verify()
{
if ((this->buffer().offset() + model.fbe_offset() - 4) > this->buffer().size())
return false;
uint32_t fbe_full_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4));
if (fbe_full_size < model.fbe_size())
return false;
return model.verify();
}
size_t GlobalModel::create_begin()
{
size_t fbe_begin = this->buffer().allocate(4 + model.fbe_size());
return fbe_begin;
}
size_t GlobalModel::create_end(size_t fbe_begin)
{
size_t fbe_end = this->buffer().size();
uint32_t fbe_full_size = (uint32_t)(fbe_end - fbe_begin);
*((uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4)) = fbe_full_size;
return fbe_full_size;
}
size_t GlobalModel::serialize(const ::bank_dom::Global& value)
{
size_t fbe_begin = create_begin();
model.set(value);
size_t fbe_full_size = create_end(fbe_begin);
return fbe_full_size;
}
size_t GlobalModel::deserialize(::bank_dom::Global& value) const noexcept
{
if ((this->buffer().offset() + model.fbe_offset() - 4) > this->buffer().size())
return 0;
uint32_t fbe_full_size = *((const uint32_t*)(this->buffer().data() + this->buffer().offset() + model.fbe_offset() - 4));
assert((fbe_full_size >= model.fbe_size()) && "Model is broken!");
if (fbe_full_size < model.fbe_size())
return 0;
model.get(value);
return fbe_full_size;
}
} // namespace bank_dom
} // namespace FBE

View file

@ -0,0 +1,396 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// Version: 1.7.0.0
#pragma once
#if defined(__clang__)
#pragma clang system_header
#elif defined(__GNUC__)
#pragma GCC system_header
#elif defined(_MSC_VER)
#pragma system_header
#endif
#include "fbe_models.h"
#include "bank_dom.h"
namespace FBE {
// Fast Binary Encoding ::bank_dom::Transaction field model
template <>
class FieldModel<::bank_dom::Transaction>
{
public:
FieldModel(FBEBuffer& buffer, size_t offset) noexcept;
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 4; }
// Get the field body size
size_t fbe_body() const noexcept;
// Get the field extra size
size_t fbe_extra() const noexcept;
// Get the field type
static constexpr size_t fbe_type() noexcept { return 1; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the struct value is valid
bool verify(bool fbe_verify_type = true) const noexcept;
// Check if the struct fields are valid
bool verify_fields(size_t fbe_struct_size) const noexcept;
// Get the struct value (begin phase)
size_t get_begin() const noexcept;
// Get the struct value (end phase)
void get_end(size_t fbe_begin) const noexcept;
// Get the struct value
void get(::bank_dom::Transaction& fbe_value) const noexcept;
// Get the struct fields values
void get_fields(::bank_dom::Transaction& fbe_value, size_t fbe_struct_size) const noexcept;
// Set the struct value (begin phase)
size_t set_begin();
// Set the struct value (end phase)
void set_end(size_t fbe_begin);
// Set the struct value
void set(const ::bank_dom::Transaction& fbe_value) noexcept;
// Set the struct fields values
void set_fields(const ::bank_dom::Transaction& fbe_value) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
public:
FieldModel<std::string> from;
FieldModel<std::string> to;
FieldModel<uint32_t> amount;
FieldModel<uint64_t> time;
};
namespace bank_dom {
// Fast Binary Encoding Transaction model
class TransactionModel : public FBE::Model
{
public:
TransactionModel() : model(this->buffer(), 4) {}
TransactionModel(const std::shared_ptr<FBEBuffer>& buffer) : FBE::Model(buffer), model(this->buffer(), 4) {}
// Get the model size
size_t fbe_size() const noexcept { return model.fbe_size() + model.fbe_extra(); }
// Get the model type
static constexpr size_t fbe_type() noexcept { return FieldModel<::bank_dom::Transaction>::fbe_type(); }
// Check if the struct value is valid
bool verify();
// Create a new model (begin phase)
size_t create_begin();
// Create a new model (end phase)
size_t create_end(size_t fbe_begin);
// Serialize the struct value
size_t serialize(const ::bank_dom::Transaction& value);
// Deserialize the struct value
size_t deserialize(::bank_dom::Transaction& value) const noexcept;
// Move to the next struct value
void next(size_t prev) noexcept { model.fbe_shift(prev); }
public:
FieldModel<::bank_dom::Transaction> model;
};
} // namespace bank_dom
// Fast Binary Encoding ::bank_dom::Logs field model
template <>
class FieldModel<::bank_dom::Logs>
{
public:
FieldModel(FBEBuffer& buffer, size_t offset) noexcept;
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 4; }
// Get the field body size
size_t fbe_body() const noexcept;
// Get the field extra size
size_t fbe_extra() const noexcept;
// Get the field type
static constexpr size_t fbe_type() noexcept { return 2; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the struct value is valid
bool verify(bool fbe_verify_type = true) const noexcept;
// Check if the struct fields are valid
bool verify_fields(size_t fbe_struct_size) const noexcept;
// Get the struct value (begin phase)
size_t get_begin() const noexcept;
// Get the struct value (end phase)
void get_end(size_t fbe_begin) const noexcept;
// Get the struct value
void get(::bank_dom::Logs& fbe_value) const noexcept;
// Get the struct fields values
void get_fields(::bank_dom::Logs& fbe_value, size_t fbe_struct_size) const noexcept;
// Set the struct value (begin phase)
size_t set_begin();
// Set the struct value (end phase)
void set_end(size_t fbe_begin);
// Set the struct value
void set(const ::bank_dom::Logs& fbe_value) noexcept;
// Set the struct fields values
void set_fields(const ::bank_dom::Logs& fbe_value) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
public:
FieldModelVector<::bank_dom::Transaction> data;
};
namespace bank_dom {
// Fast Binary Encoding Logs model
class LogsModel : public FBE::Model
{
public:
LogsModel() : model(this->buffer(), 4) {}
LogsModel(const std::shared_ptr<FBEBuffer>& buffer) : FBE::Model(buffer), model(this->buffer(), 4) {}
// Get the model size
size_t fbe_size() const noexcept { return model.fbe_size() + model.fbe_extra(); }
// Get the model type
static constexpr size_t fbe_type() noexcept { return FieldModel<::bank_dom::Logs>::fbe_type(); }
// Check if the struct value is valid
bool verify();
// Create a new model (begin phase)
size_t create_begin();
// Create a new model (end phase)
size_t create_end(size_t fbe_begin);
// Serialize the struct value
size_t serialize(const ::bank_dom::Logs& value);
// Deserialize the struct value
size_t deserialize(::bank_dom::Logs& value) const noexcept;
// Move to the next struct value
void next(size_t prev) noexcept { model.fbe_shift(prev); }
public:
FieldModel<::bank_dom::Logs> model;
};
} // namespace bank_dom
// Fast Binary Encoding ::bank_dom::User field model
template <>
class FieldModel<::bank_dom::User>
{
public:
FieldModel(FBEBuffer& buffer, size_t offset) noexcept;
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 4; }
// Get the field body size
size_t fbe_body() const noexcept;
// Get the field extra size
size_t fbe_extra() const noexcept;
// Get the field type
static constexpr size_t fbe_type() noexcept { return 3; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the struct value is valid
bool verify(bool fbe_verify_type = true) const noexcept;
// Check if the struct fields are valid
bool verify_fields(size_t fbe_struct_size) const noexcept;
// Get the struct value (begin phase)
size_t get_begin() const noexcept;
// Get the struct value (end phase)
void get_end(size_t fbe_begin) const noexcept;
// Get the struct value
void get(::bank_dom::User& fbe_value) const noexcept;
// Get the struct fields values
void get_fields(::bank_dom::User& fbe_value, size_t fbe_struct_size) const noexcept;
// Set the struct value (begin phase)
size_t set_begin();
// Set the struct value (end phase)
void set_end(size_t fbe_begin);
// Set the struct value
void set(const ::bank_dom::User& fbe_value) noexcept;
// Set the struct fields values
void set_fields(const ::bank_dom::User& fbe_value) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
public:
FieldModel<uint32_t> balance;
FieldModel<uint64_t> password;
FieldModel<std::optional<::bank_dom::Logs>> logs;
};
namespace bank_dom {
// Fast Binary Encoding User model
class UserModel : public FBE::Model
{
public:
UserModel() : model(this->buffer(), 4) {}
UserModel(const std::shared_ptr<FBEBuffer>& buffer) : FBE::Model(buffer), model(this->buffer(), 4) {}
// Get the model size
size_t fbe_size() const noexcept { return model.fbe_size() + model.fbe_extra(); }
// Get the model type
static constexpr size_t fbe_type() noexcept { return FieldModel<::bank_dom::User>::fbe_type(); }
// Check if the struct value is valid
bool verify();
// Create a new model (begin phase)
size_t create_begin();
// Create a new model (end phase)
size_t create_end(size_t fbe_begin);
// Serialize the struct value
size_t serialize(const ::bank_dom::User& value);
// Deserialize the struct value
size_t deserialize(::bank_dom::User& value) const noexcept;
// Move to the next struct value
void next(size_t prev) noexcept { model.fbe_shift(prev); }
public:
FieldModel<::bank_dom::User> model;
};
} // namespace bank_dom
// Fast Binary Encoding ::bank_dom::Global field model
template <>
class FieldModel<::bank_dom::Global>
{
public:
FieldModel(FBEBuffer& buffer, size_t offset) noexcept;
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 4; }
// Get the field body size
size_t fbe_body() const noexcept;
// Get the field extra size
size_t fbe_extra() const noexcept;
// Get the field type
static constexpr size_t fbe_type() noexcept { return 4; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the struct value is valid
bool verify(bool fbe_verify_type = true) const noexcept;
// Check if the struct fields are valid
bool verify_fields(size_t fbe_struct_size) const noexcept;
// Get the struct value (begin phase)
size_t get_begin() const noexcept;
// Get the struct value (end phase)
void get_end(size_t fbe_begin) const noexcept;
// Get the struct value
void get(::bank_dom::Global& fbe_value) const noexcept;
// Get the struct fields values
void get_fields(::bank_dom::Global& fbe_value, size_t fbe_struct_size) const noexcept;
// Set the struct value (begin phase)
size_t set_begin();
// Set the struct value (end phase)
void set_end(size_t fbe_begin);
// Set the struct value
void set(const ::bank_dom::Global& fbe_value) noexcept;
// Set the struct fields values
void set_fields(const ::bank_dom::Global& fbe_value) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
public:
FieldModelVector<std::string> keys;
FieldModelVector<::bank_dom::User> users;
};
namespace bank_dom {
// Fast Binary Encoding Global model
class GlobalModel : public FBE::Model
{
public:
GlobalModel() : model(this->buffer(), 4) {}
GlobalModel(const std::shared_ptr<FBEBuffer>& buffer) : FBE::Model(buffer), model(this->buffer(), 4) {}
// Get the model size
size_t fbe_size() const noexcept { return model.fbe_size() + model.fbe_extra(); }
// Get the model type
static constexpr size_t fbe_type() noexcept { return FieldModel<::bank_dom::Global>::fbe_type(); }
// Check if the struct value is valid
bool verify();
// Create a new model (begin phase)
size_t create_begin();
// Create a new model (end phase)
size_t create_end(size_t fbe_begin);
// Serialize the struct value
size_t serialize(const ::bank_dom::Global& value);
// Deserialize the struct value
size_t deserialize(::bank_dom::Global& value) const noexcept;
// Move to the next struct value
void next(size_t prev) noexcept { model.fbe_shift(prev); }
public:
FieldModel<::bank_dom::Global> model;
};
} // namespace bank_dom
} // namespace FBE

401
fbe/user_model/fbe.cpp Normal file
View file

@ -0,0 +1,401 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// Version: 1.7.0.0
#include "fbe.h"
namespace FBE {
std::string buffer_t::base64encode() const
{
const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string result;
int val = 0;
int valb = -6;
for (auto c : _data)
{
val = (val << 8) + c;
valb += 8;
while (valb >= 0)
{
result.push_back(base64[(val >> valb) & 0x3F]);
valb -= 6;
}
}
if (valb > -6)
result.push_back(base64[((val << 8) >> (valb + 8)) & 0x3F]);
while (result.size() % 4)
result.push_back('=');
return result;
}
buffer_t buffer_t::base64decode(const std::string& str)
{
const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
buffer_t result;
std::vector<int> pattern(256, -1);
for (int i = 0; i < 64; ++i)
pattern[base64[i]] = i;
int val = 0;
int valb = -8;
for (auto c : str)
{
if (pattern[c] == -1)
break;
val = (val << 6) + pattern[c];
valb += 6;
if (valb >= 0)
{
result.push_back((uint8_t)((val >> valb) & 0xFF));
valb -= 8;
}
}
return result;
}
uint64_t utc()
{
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
struct timespec timestamp;
if (clock_gettime(CLOCK_REALTIME, &timestamp) != 0)
throw std::runtime_error("Cannot get value of CLOCK_REALTIME timer!");
return (timestamp.tv_sec * 1000000000) + timestamp.tv_nsec;
#elif defined(_WIN32) || defined(_WIN64)
FILETIME ft;
GetSystemTimePreciseAsFileTime(&ft);
ULARGE_INTEGER result;
result.LowPart = ft.dwLowDateTime;
result.HighPart = ft.dwHighDateTime;
return (result.QuadPart - 116444736000000000ull) * 100;
#endif
}
uint8_t unhex(char ch)
{
if ((ch >= '0') && (ch <= '9'))
return ch - '0';
else if ((ch >= 'a') && (ch <= 'f'))
return 10 + ch - 'a';
else if ((ch >= 'A') && (ch <= 'F'))
return 10 + ch - 'A';
else
return 255;
}
uuid_t::uuid_t(const std::string& uuid)
{
char v1 = 0;
char v2 = 0;
bool pack = false;
size_t index = 0;
// Parse UUID string
for (auto ch : uuid)
{
if ((ch == '-') || (ch == '{') || (ch == '}'))
continue;
if (pack)
{
v2 = ch;
pack = false;
uint8_t ui1 = unhex(v1);
uint8_t ui2 = unhex(v2);
if ((ui1 > 15) || (ui2 > 15))
throw std::invalid_argument("Invalid UUID string: " + uuid);
_data[index++] = ui1 * 16 + ui2;
if (index >= 16)
break;
}
else
{
v1 = ch;
pack = true;
}
}
// Fill remaining data with zeros
for (; index < 16; ++index)
_data[index++] = 0;
}
std::string uuid_t::string() const
{
const char* digits = "0123456789abcdef";
std::string result(36, '0');
int index = 0;
for (auto value : _data)
{
result[index++] = digits[(value >> 4) & 0x0F];
result[index++] = digits[(value >> 0) & 0x0F];
if ((index == 8) || (index == 13) || (index == 18) || (index == 23))
result[index++] = '-';
}
return result;
}
uuid_t uuid_t::sequential()
{
uuid_t result;
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
::uuid_t uuid;
uuid_generate_time(uuid);
result._data[0] = uuid[0];
result._data[1] = uuid[1];
result._data[2] = uuid[2];
result._data[3] = uuid[3];
result._data[4] = uuid[4];
result._data[5] = uuid[5];
result._data[6] = uuid[6];
result._data[7] = uuid[7];
result._data[8] = uuid[8];
result._data[9] = uuid[9];
result._data[10] = uuid[10];
result._data[11] = uuid[11];
result._data[12] = uuid[12];
result._data[13] = uuid[13];
result._data[14] = uuid[14];
result._data[15] = uuid[15];
#elif defined(_WIN32) || defined(_WIN64)
::UUID uuid;
if (UuidCreateSequential(&uuid) != RPC_S_OK)
throw std::runtime_error("Cannot generate sequential UUID!");
result._data[0] = (uuid.Data1 >> 24) & 0xFF;
result._data[1] = (uuid.Data1 >> 16) & 0xFF;
result._data[2] = (uuid.Data1 >> 8) & 0xFF;
result._data[3] = (uuid.Data1 >> 0) & 0xFF;
result._data[4] = (uuid.Data2 >> 8) & 0xFF;
result._data[5] = (uuid.Data2 >> 0) & 0xFF;
result._data[6] = (uuid.Data3 >> 8) & 0xFF;
result._data[7] = (uuid.Data3 >> 0) & 0xFF;
result._data[8] = uuid.Data4[0];
result._data[9] = uuid.Data4[1];
result._data[10] = uuid.Data4[2];
result._data[11] = uuid.Data4[3];
result._data[12] = uuid.Data4[4];
result._data[13] = uuid.Data4[5];
result._data[14] = uuid.Data4[6];
result._data[15] = uuid.Data4[7];
#endif
return result;
}
uuid_t uuid_t::random()
{
uuid_t result;
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
::uuid_t uuid;
uuid_generate_random(uuid);
result._data[0] = uuid[0];
result._data[1] = uuid[1];
result._data[2] = uuid[2];
result._data[3] = uuid[3];
result._data[4] = uuid[4];
result._data[5] = uuid[5];
result._data[6] = uuid[6];
result._data[7] = uuid[7];
result._data[8] = uuid[8];
result._data[9] = uuid[9];
result._data[10] = uuid[10];
result._data[11] = uuid[11];
result._data[12] = uuid[12];
result._data[13] = uuid[13];
result._data[14] = uuid[14];
result._data[15] = uuid[15];
#elif defined(_WIN32) || defined(_WIN64)
::UUID uuid;
if (UuidCreate(&uuid) != RPC_S_OK)
throw std::runtime_error("Cannot generate random UUID!");
result._data[0] = (uuid.Data1 >> 24) & 0xFF;
result._data[1] = (uuid.Data1 >> 16) & 0xFF;
result._data[2] = (uuid.Data1 >> 8) & 0xFF;
result._data[3] = (uuid.Data1 >> 0) & 0xFF;
result._data[4] = (uuid.Data2 >> 8) & 0xFF;
result._data[5] = (uuid.Data2 >> 0) & 0xFF;
result._data[6] = (uuid.Data3 >> 8) & 0xFF;
result._data[7] = (uuid.Data3 >> 0) & 0xFF;
result._data[8] = uuid.Data4[0];
result._data[9] = uuid.Data4[1];
result._data[10] = uuid.Data4[2];
result._data[11] = uuid.Data4[3];
result._data[12] = uuid.Data4[4];
result._data[13] = uuid.Data4[5];
result._data[14] = uuid.Data4[6];
result._data[15] = uuid.Data4[7];
#endif
return result;
}
#if defined(LOGGING_PROTOCOL)
CppLogging::Record& operator<<(CppLogging::Record& record, const uuid_t& uuid)
{
const char* digits = "0123456789abcdef";
std::array<char, 36> result;
int index = 0;
for (auto value : uuid.data())
{
result[index++] = digits[(value >> 4) & 0x0F];
result[index++] = digits[(value >> 0) & 0x0F];
if ((index == 8) || (index == 13) || (index == 18) || (index == 23))
result[index++] = '-';
}
return record.StoreCustom(std::string_view(result.data(), result.size()));
}
#endif
void FBEBuffer::attach(const void* data, size_t size, size_t offset)
{
assert((data != nullptr) && "Invalid buffer!");
if (data == nullptr)
throw std::invalid_argument("Invalid buffer!");
assert((size > 0) && "Invalid size!");
if (size == 0)
throw std::invalid_argument("Invalid size!");
assert((offset <= size) && "Invalid offset!");
if (offset > size)
throw std::invalid_argument("Invalid offset!");
_data = (uint8_t*)data;
_capacity = 0;
_size = size;
_offset = offset;
}
void FBEBuffer::attach(const std::vector<uint8_t>& buffer, size_t offset)
{
assert((buffer.data() != nullptr) && "Invalid buffer!");
if (buffer.data() == nullptr)
throw std::invalid_argument("Invalid buffer!");
assert((buffer.size() > 0) && "Invalid size!");
if (buffer.size() == 0)
throw std::invalid_argument("Invalid size!");
assert((offset <= buffer.size()) && "Invalid offset!");
if (offset > buffer.size())
throw std::invalid_argument("Invalid offset!");
_data = (uint8_t*)buffer.data();
_capacity = 0;
_size = buffer.size();
_offset = offset;
}
void FBEBuffer::clone(const void* data, size_t size, size_t offset)
{
assert((offset <= size) && "Invalid offset!");
if (offset > size)
throw std::invalid_argument("Invalid offset!");
reserve(size);
std::memcpy(_data, data, size);
_capacity = size;
_size = size;
_offset = offset;
}
void FBEBuffer::clone(const std::vector<uint8_t>& buffer, size_t offset)
{
assert((offset <= buffer.size()) && "Invalid offset!");
if (offset > buffer.size())
throw std::invalid_argument("Invalid offset!");
size_t size = buffer.size();
reserve(size);
std::memcpy(_data, buffer.data(), size);
_capacity = size;
_size = size;
_offset = offset;
}
size_t FBEBuffer::allocate(size_t size)
{
size_t offset = _size;
// Calculate a new buffer size
size_t total = _size + size;
if (total <= _capacity)
{
_size = total;
return offset;
}
_capacity = std::max(total, 2 * _capacity);
uint8_t* data = (uint8_t*)std::malloc(_capacity);
std::memcpy(data, _data, _size);
std::free(_data);
_data = data;
_size = total;
return offset;
}
void FBEBuffer::remove(size_t offset, size_t size)
{
assert(((offset + size) <= _size) && "Invalid offset & size!");
if ((offset + size) > _size)
throw std::invalid_argument("Invalid offset & size!");
std::memcpy(_data + offset, _data + offset + size, _size - size - offset);
_size -= size;
if (_offset >= (offset + size))
_offset -= size;
else if (_offset >= offset)
{
_offset -= _offset - offset;
if (_offset > _size)
_offset = _size;
}
}
void FBEBuffer::reserve(size_t capacity)
{
if (capacity > _capacity)
{
_capacity = std::max(capacity, 2 * _capacity);
uint8_t* data = (uint8_t*)std::malloc(_capacity);
std::memcpy(data, _data, _size);
std::free(_data);
_data = data;
}
}
void FBEBuffer::resize(size_t size)
{
reserve(size);
_size = size;
if (_offset > _size)
_offset = _size;
}
void FBEBuffer::reset()
{
_size = 0;
_offset = 0;
}
} // namespace FBE

650
fbe/user_model/fbe.h Normal file
View file

@ -0,0 +1,650 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// Version: 1.7.0.0
#pragma once
#if defined(__clang__)
#pragma clang system_header
#elif defined(__GNUC__)
#pragma GCC system_header
#elif defined(_MSC_VER)
#pragma system_header
#endif
#include <array>
#include <bitset>
#include <cassert>
#include <cmath>
#include <cstring>
#include <cctype>
#include <future>
#include <iomanip>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <vector>
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
#include <time.h>
#include <uuid/uuid.h>
#undef HOST_NOT_FOUND
#elif defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#undef DELETE
#undef ERROR
#undef HOST_NOT_FOUND
#undef Yield
#undef min
#undef max
#undef uuid_t
#endif
namespace FBE {
//! Bytes buffer type
/*!
Represents bytes buffer which is a lightweight wrapper around std::vector<uint8_t>
with similar interface.
*/
class buffer_t
{
public:
typedef std::vector<uint8_t>::iterator iterator;
typedef std::vector<uint8_t>::const_iterator const_iterator;
typedef std::vector<uint8_t>::reverse_iterator reverse_iterator;
typedef std::vector<uint8_t>::const_reverse_iterator const_reverse_iterator;
buffer_t() = default;
buffer_t(size_t capacity) { reserve(capacity); }
buffer_t(const std::string& str) { assign(str); }
buffer_t(size_t size, uint8_t value) { assign(size, value); }
buffer_t(const uint8_t* data, size_t size) { assign(data, size); }
buffer_t(const std::vector<uint8_t>& other) : _data(other) {}
buffer_t(std::vector<uint8_t>&& other) : _data(std::move(other)) {}
buffer_t(const buffer_t& other) = default;
buffer_t(buffer_t&& other) = default;
~buffer_t() = default;
buffer_t& operator=(const std::string& str) { assign(str); return *this; }
buffer_t& operator=(const std::vector<uint8_t>& other) { _data = other; return *this; }
buffer_t& operator=(std::vector<uint8_t>&& other) { _data = std::move(other); return *this; }
buffer_t& operator=(const buffer_t& other) = default;
buffer_t& operator=(buffer_t&& other) = default;
uint8_t& operator[](size_t index) { return _data[index]; }
const uint8_t& operator[](size_t index) const { return _data[index]; }
bool empty() const { return _data.empty(); }
size_t capacity() const { return _data.capacity(); }
size_t size() const { return _data.size(); }
size_t max_size() const { return _data.max_size(); }
std::vector<uint8_t>& buffer() noexcept { return _data; }
const std::vector<uint8_t>& buffer() const noexcept { return _data; }
uint8_t* data() noexcept { return _data.data(); }
const uint8_t* data() const noexcept { return _data.data(); }
uint8_t& at(size_t index) { return _data.at(index); }
const uint8_t& at(size_t index) const { return _data.at(index); }
uint8_t& front() { return _data.front(); }
const uint8_t& front() const { return _data.front(); }
uint8_t& back() { return _data.back(); }
const uint8_t& back() const { return _data.back(); }
void reserve(size_t capacity) { _data.reserve(capacity); }
void resize(size_t size, uint8_t value = 0) { _data.resize(size, value); }
void shrink_to_fit() { _data.shrink_to_fit(); }
void assign(const std::string& str) { assign((const uint8_t*)str.c_str(), str.size()); }
void assign(const std::vector<uint8_t>& vec) { assign(vec.begin(), vec.end()); }
void assign(size_t size, uint8_t value) { _data.assign(size, value); }
void assign(const uint8_t* data, size_t size) { _data.assign(data, data + size); }
template <class InputIterator>
void assign(InputIterator first, InputIterator last) { _data.assign(first, last); }
iterator insert(const_iterator position, uint8_t value) { return _data.insert(position, value); }
iterator insert(const_iterator position, const std::string& str) { return insert(position, (const uint8_t*)str.c_str(), str.size()); }
iterator insert(const_iterator position, const std::vector<uint8_t>& vec) { return insert(position, vec.begin(), vec.end()); }
iterator insert(const_iterator position, size_t size, uint8_t value) { return _data.insert(position, size, value); }
iterator insert(const_iterator position, const uint8_t* data, size_t size) { return _data.insert(position, data, data + size); }
template <class InputIterator>
iterator insert(const_iterator position, InputIterator first, InputIterator last) { return _data.insert(position, first, last); }
iterator erase(const_iterator position) { return _data.erase(position); }
iterator erase(const_iterator first, const_iterator last) { return _data.erase(first, last); }
void clear() noexcept { _data.clear(); }
void push_back(uint8_t value) { _data.push_back(value); }
void pop_back() { _data.pop_back(); }
template <class... Args>
iterator emplace(const_iterator position, Args&&... args) { return _data.emplace(position, args...); }
template <class... Args>
void emplace_back(Args&&... args) { _data.emplace_back(args...); }
iterator begin() noexcept { return _data.begin(); }
const_iterator begin() const noexcept { return _data.begin(); }
const_iterator cbegin() const noexcept { return _data.cbegin(); }
reverse_iterator rbegin() noexcept { return _data.rbegin(); }
const_reverse_iterator rbegin() const noexcept { return _data.rbegin(); }
const_reverse_iterator crbegin() const noexcept { return _data.crbegin(); }
iterator end() noexcept { return _data.end(); }
const_iterator end() const noexcept { return _data.end(); }
const_iterator cend() const noexcept { return _data.cend(); }
reverse_iterator rend() noexcept { return _data.rend(); }
const_reverse_iterator rend() const noexcept { return _data.rend(); }
const_reverse_iterator crend() const noexcept { return _data.crend(); }
//! Get the string equivalent from the bytes buffer
std::string string() const { return std::string(_data.begin(), _data.end()); }
//! Encode the Base64 string from the bytes buffer
std::string base64encode() const;
//! Decode the bytes buffer from the Base64 string
static buffer_t base64decode(const std::string& str);
//! Swap two instances
void swap(buffer_t& value) noexcept
{ using std::swap; swap(_data, value._data); }
friend void swap(buffer_t& value1, buffer_t& value2) noexcept
{ value1.swap(value2); }
private:
std::vector<uint8_t> _data;
};
//! Decimal type
/*!
Represents decimal type using double and provides basic arithmetic operations.
*/
class decimal_t
{
public:
decimal_t() noexcept { _value = 0.0; }
decimal_t(int8_t value) noexcept { _value = (double)value; }
decimal_t(uint8_t value) noexcept { _value = (double)value; }
decimal_t(int16_t value) noexcept { _value = (double)value; }
decimal_t(uint16_t value) noexcept { _value = (double)value; }
decimal_t(int32_t value) noexcept { _value = (double)value; }
decimal_t(uint32_t value) noexcept { _value = (double)value; }
decimal_t(int64_t value) noexcept { _value = (double)value; }
decimal_t(uint64_t value) noexcept { _value = (double)value; }
decimal_t(float value) noexcept { _value = (double)value; }
decimal_t(double value) noexcept { _value = value; }
template <typename T>
explicit decimal_t(const T& value) noexcept { _value = (double)value; }
decimal_t(const decimal_t& value) noexcept = default;
decimal_t(decimal_t&& value) noexcept = default;
~decimal_t() noexcept = default;
template <typename T>
decimal_t& operator=(const T& value) noexcept { _value = (double)value; return *this; }
decimal_t& operator=(const decimal_t& value) noexcept = default;
decimal_t& operator=(decimal_t&& value) noexcept = default;
// Arithmetic operators
decimal_t operator+() const noexcept { return decimal_t(_value); }
decimal_t operator-() const noexcept { return decimal_t(-_value); }
decimal_t& operator++() noexcept { return *this += 1; }
decimal_t operator++(int) noexcept { decimal_t temp(*this); ++*this; return temp; }
decimal_t& operator--() noexcept { return *this -= 1; }
decimal_t operator--(int) noexcept { decimal_t temp(*this); --*this; return temp; }
decimal_t& operator+=(const decimal_t& value) noexcept { return *this = *this + value; }
decimal_t& operator-=(const decimal_t& value) noexcept { return *this = *this - value; }
decimal_t& operator*=(const decimal_t& value) noexcept { return *this = *this * value; }
decimal_t& operator/=(const decimal_t& value) { return *this = *this / value; }
template <typename T>
decimal_t& operator+=(const T& value) noexcept { return *this = *this + decimal_t(value); }
template <typename T>
decimal_t& operator-=(const T& value) noexcept { return *this = *this - decimal_t(value); }
template <typename T>
decimal_t& operator*=(const T& value) noexcept { return *this = *this * decimal_t(value); }
template <typename T>
decimal_t& operator/=(const T& value) { return *this = *this / decimal_t(value); }
template <typename T>
friend T& operator+=(T& value1, const decimal_t& value2) noexcept { return value1 = (T)(decimal_t(value1) + value2); }
template <typename T>
friend T& operator-=(T& value1, const decimal_t& value2) noexcept { return value1 = (T)(decimal_t(value1) - value2); }
template <typename T>
friend T& operator*=(T& value1, const decimal_t& value2) noexcept { return value1 = (T)(decimal_t(value1) * value2); }
template <typename T>
friend T& operator/=(T& value1, const decimal_t& value2) { return value1 = (T)(decimal_t(value1) / value2); }
template <typename T>
friend decimal_t operator+(const T& value1, const decimal_t& value2) noexcept { return decimal_t(value1) + value2; }
template <typename T>
friend decimal_t operator+(const decimal_t& value1, const T& value2) noexcept { return value1 + decimal_t(value2); }
friend decimal_t operator+(const decimal_t& value1, const decimal_t& value2) noexcept { return decimal_t(value1._value + value2._value); }
template <typename T>
friend decimal_t operator-(const T& value1, const decimal_t& value2) noexcept { return decimal_t(value1) - value2; }
template <typename T>
friend decimal_t operator-(const decimal_t& value1, const T& value2) noexcept { return value1 - decimal_t(value2); }
friend decimal_t operator-(const decimal_t& value1, const decimal_t& value2) noexcept { return decimal_t(value1._value - value2._value); }
template <typename T>
friend decimal_t operator*(const T& value1, const decimal_t& value2) noexcept { return decimal_t(value1) * value2; }
template <typename T>
friend decimal_t operator*(const decimal_t& value1, const T& value2) noexcept { return value1 * decimal_t(value2); }
friend decimal_t operator*(const decimal_t& value1, const decimal_t& value2) noexcept { return decimal_t(value1._value * value2._value); }
template <typename T>
friend decimal_t operator/(const T& value1, const decimal_t& value2) { return decimal_t(value1) / value2; }
template <typename T>
friend decimal_t operator/(const decimal_t& value1, const T& value2) { return value1 / decimal_t(value2); }
friend decimal_t operator/(const decimal_t& value1, const decimal_t& value2) { return decimal_t(value1._value / value2._value); }
// Comparison operators
template <typename T>
friend bool operator==(const T& value1, const decimal_t& value2) noexcept { return decimal_t(value1) == value2; }
template <typename T>
friend bool operator==(const decimal_t& value1, const T& value2) noexcept { return value1 == decimal_t(value2); }
friend bool operator==(const decimal_t& value1, const decimal_t& value2) noexcept { return value1._value == value2._value; }
template <typename T>
friend bool operator!=(const T& value1, const decimal_t& value2) noexcept { return decimal_t(value1) != value2; }
template <typename T>
friend bool operator!=(const decimal_t& value1, const T& value2) noexcept { return value1 != decimal_t(value2); }
friend bool operator!=(const decimal_t& value1, const decimal_t& value2) noexcept { return value1._value != value2._value; }
template <typename T>
friend bool operator<(const T& value1, const decimal_t& value2) noexcept { return decimal_t(value1) < value2; }
template <typename T>
friend bool operator<(const decimal_t& value1, const T& value2) noexcept { return value1 < decimal_t(value2); }
friend bool operator<(const decimal_t& value1, const decimal_t& value2) noexcept { return value1._value < value2._value; }
template <typename T>
friend bool operator>(const T& value1, const decimal_t& value2) noexcept { return decimal_t(value1) > value2; }
template <typename T>
friend bool operator>(const decimal_t& value1, const T& value2) noexcept { return value1 > decimal_t(value2); }
friend bool operator>(const decimal_t& value1, const decimal_t& value2) noexcept { return value1._value > value2._value; }
template <typename T>
friend bool operator<=(const T& value1, const decimal_t& value2) noexcept { return decimal_t(value1) <= value2; }
template <typename T>
friend bool operator<=(const decimal_t& value1, const T& value2) noexcept { return value1 <= decimal_t(value2); }
friend bool operator<=(const decimal_t& value1, const decimal_t& value2) noexcept { return value1._value <= value2._value; }
template <typename T>
friend bool operator>=(const T& value1, const decimal_t& value2) noexcept { return decimal_t(value1) >= value2; }
template <typename T>
friend bool operator>=(const decimal_t& value1, const T& value2) noexcept { return value1 >= decimal_t(value2); }
friend bool operator>=(const decimal_t& value1, const decimal_t& value2) noexcept { return value1._value >= value2._value; }
// Type cast
operator bool() const noexcept { return (_value != 0.0); }
operator uint8_t() const noexcept { return (uint8_t)_value; }
operator uint16_t() const noexcept { return (uint16_t)_value; }
operator uint32_t() const noexcept { return (uint32_t)_value; }
operator uint64_t() const noexcept { return (uint64_t)_value; }
operator float() const noexcept { return (float)_value; }
operator double() const noexcept { return (double)_value; }
//! Get string from the current decimal value
std::string string() const { return std::to_string(_value); }
//! Input instance from the given input stream
friend std::istream& operator>>(std::istream& is, decimal_t& value)
{ is >> value._value; return is; }
//! Output instance into the given output stream
friend std::ostream& operator<<(std::ostream& os, const decimal_t& value)
{ os << value.string(); return os; }
#if defined(LOGGING_PROTOCOL)
//! Store logging format
friend CppLogging::Record& operator<<(CppLogging::Record& record, const decimal_t& value)
{ return record.StoreCustom(value._value); }
#endif
//! Swap two instances
void swap(decimal_t& value) noexcept
{ using std::swap; swap(_value, value._value); }
friend void swap(decimal_t& value1, decimal_t& value2) noexcept
{ value1.swap(value2); }
private:
double _value;
};
} // namespace FBE
namespace std {
template <>
struct hash<FBE::decimal_t>
{
typedef FBE::decimal_t argument_type;
typedef size_t result_type;
result_type operator() (const argument_type& value) const
{
result_type result = 17;
result = result * 31 + std::hash<double>()((double)value);
return result;
}
};
} // namespace std
namespace FBE {
// Register a new enum-based flags macro
#define FBE_ENUM_FLAGS(type)\
inline FBE::Flags<type> operator|(type f1, type f2) noexcept { return FBE::Flags<type>(f1) | FBE::Flags<type>(f2); }\
inline FBE::Flags<type> operator&(type f1, type f2) noexcept { return FBE::Flags<type>(f1) & FBE::Flags<type>(f2); }\
inline FBE::Flags<type> operator^(type f1, type f2) noexcept { return FBE::Flags<type>(f1) ^ FBE::Flags<type>(f2); }
// Enum-based flags
template <typename TEnum>
class Flags
{
// Enum underlying type
typedef typename std::make_unsigned<typename std::underlying_type<TEnum>::type>::type type;
public:
Flags() noexcept : _value(0) {}
explicit Flags(type value) noexcept : _value(value) {}
explicit Flags(TEnum value) noexcept : _value((type)value) {}
Flags(const Flags&) noexcept = default;
Flags(Flags&&) noexcept = default;
~Flags() noexcept = default;
Flags& operator=(type value) noexcept
{ _value = value; return *this; }
Flags& operator=(TEnum value) noexcept
{ _value = (type)value; return *this; }
Flags& operator=(const Flags&) noexcept = default;
Flags& operator=(Flags&&) noexcept = default;
// Is any flag set?
explicit operator bool() const noexcept { return isset(); }
// Is no flag set?
bool operator!() const noexcept { return !isset(); }
// Reverse all flags
Flags operator~() const noexcept { return Flags(~_value); }
// Flags logical assign operators
Flags& operator&=(const Flags& flags) noexcept
{ _value &= flags._value; return *this; }
Flags& operator|=(const Flags& flags) noexcept
{ _value |= flags._value; return *this; }
Flags& operator^=(const Flags& flags) noexcept
{ _value ^= flags._value; return *this; }
// Flags logical friend operators
friend Flags operator&(const Flags& flags1, const Flags& flags2) noexcept
{ return Flags(flags1._value & flags2._value); }
friend Flags operator|(const Flags& flags1, const Flags& flags2) noexcept
{ return Flags(flags1._value | flags2._value); }
friend Flags operator^(const Flags& flags1, const Flags& flags2) noexcept
{ return Flags(flags1._value ^ flags2._value); }
// Flags comparison
friend bool operator==(const Flags& flags1, const Flags& flags2) noexcept
{ return flags1._value == flags2._value; }
friend bool operator!=(const Flags& flags1, const Flags& flags2) noexcept
{ return flags1._value != flags2._value; }
// Convert to the enum value
operator TEnum() const noexcept { return (TEnum)_value; }
//! Is any flag set?
bool isset() const noexcept { return (_value != 0); }
//! Is the given flag set?
bool isset(type value) const noexcept { return (_value & value) != 0; }
//! Is the given flag set?
bool isset(TEnum value) const noexcept { return (_value & (type)value) != 0; }
// Get the enum value
TEnum value() const noexcept { return (TEnum)_value; }
// Get the underlying enum value
type underlying() const noexcept { return _value; }
// Get the bitset value
std::bitset<sizeof(type) * 8> bitset() const noexcept { return {_value}; }
// Swap two instances
void swap(Flags& flags) noexcept { using std::swap; swap(_value, flags._value); }
template <typename UEnum>
friend void swap(Flags<UEnum>& flags1, Flags<UEnum>& flags2) noexcept;
private:
type _value;
};
template <typename TEnum>
inline void swap(Flags<TEnum>& flags1, Flags<TEnum>& flags2) noexcept
{
flags1.swap(flags2);
}
// Get Epoch timestamp
inline uint64_t epoch() { return 0ull; }
// Get UTC timestamp
uint64_t utc();
//! Universally unique identifier (UUID)
/*!
A universally unique identifier (UUID) is an identifier standard used
in software construction. This implementation generates the following
UUID types:
- Nil UUID0 (all bits set to zero)
- Sequential UUID1 (time based version)
- Random UUID4 (randomly or pseudo-randomly generated version)
A UUID is simply a 128-bit value: "123e4567-e89b-12d3-a456-426655440000"
https://en.wikipedia.org/wiki/Universally_unique_identifier
https://www.ietf.org/rfc/rfc4122.txt
*/
class uuid_t
{
public:
//! Default constructor
uuid_t() : _data() { _data.fill(0); }
//! Initialize UUID with a given string
/*!
\param uuid - UUID string
*/
explicit uuid_t(const std::string& uuid);
//! Initialize UUID with a given 16 bytes data buffer
/*!
\param data - UUID 16 bytes data buffer
*/
explicit uuid_t(const std::array<uint8_t, 16>& data) : _data(data) {}
uuid_t(const uuid_t&) = default;
uuid_t(uuid_t&&) noexcept = default;
~uuid_t() = default;
uuid_t& operator=(const std::string& uuid)
{ _data = uuid_t(uuid).data(); return *this; }
uuid_t& operator=(const std::array<uint8_t, 16>& data)
{ _data = data; return *this; }
uuid_t& operator=(const uuid_t&) = default;
uuid_t& operator=(uuid_t&&) noexcept = default;
// UUID comparison
friend bool operator==(const uuid_t& uuid1, const uuid_t& uuid2)
{ return uuid1._data == uuid2._data; }
friend bool operator!=(const uuid_t& uuid1, const uuid_t& uuid2)
{ return uuid1._data != uuid2._data; }
friend bool operator<(const uuid_t& uuid1, const uuid_t& uuid2)
{ return uuid1._data < uuid2._data; }
friend bool operator>(const uuid_t& uuid1, const uuid_t& uuid2)
{ return uuid1._data > uuid2._data; }
friend bool operator<=(const uuid_t& uuid1, const uuid_t& uuid2)
{ return uuid1._data <= uuid2._data; }
friend bool operator>=(const uuid_t& uuid1, const uuid_t& uuid2)
{ return uuid1._data >= uuid2._data; }
//! Check if the UUID is nil UUID0 (all bits set to zero)
explicit operator bool() const noexcept { return *this != nil(); }
//! Get the UUID data buffer
std::array<uint8_t, 16>& data() noexcept { return _data; }
//! Get the UUID data buffer
const std::array<uint8_t, 16>& data() const noexcept { return _data; }
//! Get string from the current UUID in format "00000000-0000-0000-0000-000000000000"
std::string string() const;
//! Generate nil UUID0 (all bits set to zero)
static uuid_t nil() { return uuid_t(); }
//! Generate sequential UUID1 (time based version)
static uuid_t sequential();
//! Generate random UUID4 (randomly or pseudo-randomly generated version)
static uuid_t random();
//! Output instance into the given output stream
friend std::ostream& operator<<(std::ostream& os, const uuid_t& uuid)
{ os << uuid.string(); return os; }
#if defined(LOGGING_PROTOCOL)
//! Store logging format
friend CppLogging::Record& operator<<(CppLogging::Record& record, const uuid_t& uuid);
#endif
//! Swap two instances
void swap(uuid_t& uuid) noexcept
{ using std::swap; swap(_data, uuid._data); }
friend void swap(uuid_t& uuid1, uuid_t& uuid2) noexcept
{ uuid1.swap(uuid2); }
private:
std::array<uint8_t, 16> _data;
};
} // namespace FBE
namespace std {
template <>
struct hash<FBE::uuid_t>
{
typedef FBE::uuid_t argument_type;
typedef size_t result_type;
result_type operator() (const argument_type& value) const
{
result_type result = 17;
std::hash<uint8_t> hasher;
for (size_t i = 0; i < value.data().size(); ++i)
result = result * 31 + hasher(value.data()[i]);
return result;
}
};
} // namespace std
namespace FBE {
// Fast Binary Encoding buffer based on the dynamic byte buffer
class FBEBuffer
{
public:
FBEBuffer() : _data(nullptr), _capacity(0), _size(0), _offset(0) {}
// Initialize the read buffer with the given byte buffer and offset
explicit FBEBuffer(const void* data, size_t size, size_t offset = 0) { attach(data, size, offset); }
// Initialize the read buffer with the given byte vector and offset
explicit FBEBuffer(const std::vector<uint8_t>& buffer, size_t offset = 0) { attach(buffer, offset); }
// Initialize the read buffer with another buffer and offset
explicit FBEBuffer(const FBEBuffer& buffer, size_t offset = 0) { attach(buffer.data(), buffer.size(), offset); }
// Initialize the write buffer with the given capacity
explicit FBEBuffer(size_t capacity) : FBEBuffer() { reserve(capacity); }
FBEBuffer(const FBEBuffer&) = delete;
FBEBuffer(FBEBuffer&&) noexcept = delete;
~FBEBuffer() { if (_capacity > 0) std::free(_data); }
FBEBuffer& operator=(const FBEBuffer&) = delete;
FBEBuffer& operator=(FBEBuffer&&) noexcept = delete;
bool empty() const noexcept { return (_data == nullptr) || (_size == 0); }
const uint8_t* data() const noexcept { return _data; }
uint8_t* data() noexcept { return _data; }
size_t capacity() const noexcept { return _capacity; }
size_t size() const noexcept { return _size; }
size_t offset() const noexcept { return _offset; }
// Attach the given buffer with a given offset to the current read buffer
void attach(const void* data, size_t size, size_t offset = 0);
// Attach the given byte vector with a given offset to the current read buffer
void attach(const std::vector<uint8_t>& buffer, size_t offset = 0);
// Clone the given buffer with a given offset to the current buffer
void clone(const void* data, size_t size, size_t offset = 0);
// Clone the given vector with a given offset to the current buffer
void clone(const std::vector<uint8_t>& buffer, size_t offset = 0);
// Allocate memory in the current write buffer and return offset to the allocated memory block
size_t allocate(size_t size);
// Remove some memory of the given size from the current write buffer
void remove(size_t offset, size_t size);
// Reserve memory of the given capacity in the current write buffer
void reserve(size_t capacity);
// Resize the current write buffer
void resize(size_t size);
// Reset the current write buffer and its offset
void reset();
// Shift the current write buffer offset
void shift(size_t offset) { _offset += offset; }
// Unshift the current write buffer offset
void unshift(size_t offset) { _offset -= offset; }
private:
uint8_t* _data;
size_t _capacity;
size_t _size;
size_t _offset;
};
// Fast Binary Encoding base model
class Model
{
public:
Model() : Model(nullptr) {}
Model(const std::shared_ptr<FBEBuffer>& buffer) { _buffer = buffer ? buffer : std::make_shared<FBEBuffer>(); }
Model(const Model&) = default;
Model(Model&&) noexcept = default;
~Model() = default;
Model& operator=(const Model&) = default;
Model& operator=(Model&&) noexcept = default;
// Get the model buffer
FBEBuffer& buffer() noexcept { return *_buffer; }
const FBEBuffer& buffer() const noexcept { return *_buffer; }
// Attach the model buffer
void attach(const void* data, size_t size, size_t offset = 0) { _buffer->attach(data, size, offset); }
void attach(const std::vector<uint8_t>& buffer, size_t offset = 0) { _buffer->attach(buffer, offset); }
void attach(const FBEBuffer& buffer, size_t offset = 0) { _buffer->attach(buffer.data(), buffer.size(), offset); }
// Model buffer operations
size_t allocate(size_t size) { return _buffer->allocate(size); }
void remove(size_t offset, size_t size) { _buffer->remove(offset, size); }
void reserve(size_t capacity) { _buffer->reserve(capacity); }
void resize(size_t size) { _buffer->resize(size); }
void reset() { _buffer->reset(); }
void shift(size_t offset) { _buffer->shift(offset); }
void unshift(size_t offset) { _buffer->unshift(offset); }
private:
std::shared_ptr<FBEBuffer> _buffer;
};
} // namespace FBE

View file

@ -0,0 +1,436 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// Version: 1.7.0.0
#include "fbe_final_models.h"
namespace FBE {
uint64_t FinalModel<decimal_t>::extract(double a) noexcept
{
uint64_t result;
std::memcpy(&result, &a, sizeof(double));
return result;
}
uint64_t FinalModel<decimal_t>::uint32x32(uint32_t a, uint32_t b) noexcept
{
return (uint64_t)a * (uint64_t)b;
}
void FinalModel<decimal_t>::uint64x64(uint64_t a, uint64_t b, uint64_t& low64, uint32_t& high32) noexcept
{
uint64_t low = uint32x32((uint32_t)a, (uint32_t)b);
uint64_t mid = uint32x32((uint32_t)a, (uint32_t)(b >> 32));
uint64_t high = uint32x32((uint32_t)(a >> 32), (uint32_t)(b >> 32));
high += (mid >> 32);
low += (mid <<= 32);
// Test for carry
if (low < mid)
high++;
mid = uint32x32((uint32_t)(a >> 32), (uint32_t)b);
high += (mid >> 32);
low += (mid <<= 32);
// Test for carry
if (low < mid)
high++;
if (high > 0xFFFFFFFFu)
{
low64 = 0;
high32 = 0;
}
low64 = low;
high32 = (uint32_t)high;
}
size_t FinalModel<decimal_t>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
return fbe_size();
}
size_t FinalModel<decimal_t>::get(decimal_t& value) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
// Value taken via reverse engineering the double that corresponds to 2^64
const double ds2to64 = 1.8446744073709552e+019;
// Read decimal parts
uint64_t low = *((const uint64_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
uint32_t high = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 8));
uint32_t flags = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 12));
// Calculate decimal value
double dValue = ((double)low + (double)high * ds2to64) / pow(10.0, (uint8_t)(flags >> 16));
if (flags & 0x80000000)
dValue = -dValue;
value = dValue;
return fbe_size();
}
size_t FinalModel<decimal_t>::set(decimal_t value) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
// The most we can scale by is 10^28, which is just slightly more
// than 2^93. So a float with an exponent of -94 could just
// barely reach 0.5, but smaller exponents will always round to zero.
const uint32_t DBLBIAS = 1022;
// Get exponent value
double dValue = (double)value;
int32_t iExp = (int32_t)(((uint32_t)(extract(dValue) >> 52) & 0x7FFu) - DBLBIAS);
if ((iExp < -94) || (iExp > 96))
{
// Value too big for .NET Decimal (exponent is limited to [-94, 96])
memset((uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()), 0, 16);
return fbe_size();
}
uint32_t flags = 0;
if (dValue < 0)
{
dValue = -dValue;
flags = 0x80000000;
}
// Round the input to a 15-digit integer. The R8 format has
// only 15 digits of precision, and we want to keep garbage digits
// out of the Decimal were making.
// Calculate max power of 10 input value could have by multiplying
// the exponent by log10(2). Using scaled integer multiplcation,
// log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
int32_t iPower = 14 - ((iExp * 19728) >> 16);
// iPower is between -14 and 43
if (iPower >= 0)
{
// We have less than 15 digits, scale input up.
if (iPower > 28)
iPower = 28;
dValue *= pow(10.0, iPower);
}
else
{
if ((iPower != -1) || (dValue >= 1E15))
dValue /= pow(10.0, -iPower);
else
iPower = 0; // didn't scale it
}
assert(dValue < 1E15);
if ((dValue < 1E14) && (iPower < 28))
{
dValue *= 10;
iPower++;
assert(dValue >= 1E14);
}
// Round to int64
uint64_t ulMant;
ulMant = (uint64_t)(int64_t)dValue;
dValue -= (int64_t)ulMant; // difference between input & integer
if ((dValue > 0.5) || ((dValue == 0.5) && ((ulMant & 1) != 0)))
ulMant++;
if (ulMant == 0)
{
// Mantissa is 0
memset((uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()), 0, 16);
return fbe_size();
}
if (iPower < 0)
{
// Add -iPower factors of 10, -iPower <= (29 - 15) = 14
iPower = -iPower;
if (iPower < 10)
{
double pow10 = (double)powl(10.0, iPower);
uint64_t low64 = uint32x32((uint32_t)ulMant, (uint32_t)pow10);
uint64_t high64 = uint32x32((uint32_t)(ulMant >> 32), (uint32_t)pow10);
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = (uint32_t)low64;
high64 += low64 >> 32;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 4)) = (uint32_t)high64;
high64 >>= 32;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 8)) = (uint32_t)high64;
}
else
{
// Have a big power of 10.
assert(iPower <= 14);
uint64_t low64;
uint32_t high32;
uint64x64(ulMant, (uint64_t)pow(10.0, iPower), low64, high32);
*((uint64_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = low64;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 8)) = high32;
}
}
else
{
// Factor out powers of 10 to reduce the scale, if possible.
// The maximum number we could factor out would be 14. This
// comes from the fact we have a 15-digit number, and the
// MSD must be non-zero -- but the lower 14 digits could be
// zero. Note also the scale factor is never negative, so
// we can't scale by any more than the power we used to
// get the integer.
int lmax = iPower;
if (lmax > 14)
lmax = 14;
if ((((uint8_t)ulMant) == 0) && (lmax >= 8))
{
const uint32_t den = 100000000;
uint64_t div = ulMant / den;
if ((uint32_t)ulMant == (uint32_t)(div * den))
{
ulMant = div;
iPower -= 8;
lmax -= 8;
}
}
if ((((uint32_t)ulMant & 0xF) == 0) && (lmax >= 4))
{
const uint32_t den = 10000;
uint64_t div = ulMant / den;
if ((uint32_t)ulMant == (uint32_t)(div * den))
{
ulMant = div;
iPower -= 4;
lmax -= 4;
}
}
if ((((uint32_t)ulMant & 3) == 0) && (lmax >= 2))
{
const uint32_t den = 100;
uint64_t div = ulMant / den;
if ((uint32_t)ulMant == (uint32_t)(div * den))
{
ulMant = div;
iPower -= 2;
lmax -= 2;
}
}
if ((((uint32_t)ulMant & 1) == 0) && (lmax >= 1))
{
const uint32_t den = 10;
uint64_t div = ulMant / den;
if ((uint32_t)ulMant == (uint32_t)(div * den))
{
ulMant = div;
iPower--;
}
}
flags |= (uint32_t)iPower << 16;
*((uint64_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = ulMant;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 8)) = 0;
}
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 12)) = flags;
return fbe_size();
}
size_t FinalModel<uuid_t>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
return fbe_size();
}
size_t FinalModel<uuid_t>::get(uuid_t& value) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
std::memcpy(value.data().data(), (const uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()), fbe_size());
return fbe_size();
}
size_t FinalModel<uuid_t>::set(uuid_t value) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
std::memcpy((uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()), value.data().data(), fbe_size());
return fbe_size();
}
size_t FinalModel<buffer_t>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
uint32_t fbe_bytes_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((_buffer.offset() + fbe_offset() + 4 + fbe_bytes_size) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
return 4 + fbe_bytes_size;
}
size_t FinalModel<buffer_t>::get(void* data, size_t size) const noexcept
{
assert(((size == 0) || (data != nullptr)) && "Invalid buffer!");
if ((size > 0) && (data == nullptr))
return 0;
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
uint32_t fbe_bytes_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((_buffer.offset() + fbe_offset() + 4 + fbe_bytes_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4 + fbe_bytes_size) > _buffer.size())
return 4;
size_t result = std::min(size, (size_t)fbe_bytes_size);
memcpy(data, (const char*)(_buffer.data() + _buffer.offset() + fbe_offset() + 4), result);
return 4 + fbe_bytes_size;
}
size_t FinalModel<buffer_t>::get(std::vector<uint8_t>& value) const noexcept
{
value.clear();
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
uint32_t fbe_bytes_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((_buffer.offset() + fbe_offset() + 4 + fbe_bytes_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4 + fbe_bytes_size) > _buffer.size())
return 4;
const char* fbe_bytes = (const char*)(_buffer.data() + _buffer.offset() + fbe_offset() + 4);
value.assign(fbe_bytes, fbe_bytes + fbe_bytes_size);
return 4 + fbe_bytes_size;
}
size_t FinalModel<buffer_t>::set(const void* data, size_t size)
{
assert(((size == 0) || (data != nullptr)) && "Invalid buffer!");
if ((size > 0) && (data == nullptr))
return 0;
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
uint32_t fbe_bytes_size = (uint32_t)size;
assert(((_buffer.offset() + fbe_offset() + 4 + fbe_bytes_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4 + fbe_bytes_size) > _buffer.size())
return 4;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_bytes_size;
memcpy((char*)(_buffer.data() + _buffer.offset() + fbe_offset() + 4), data, fbe_bytes_size);
return 4 + fbe_bytes_size;
}
size_t FinalModel<std::string>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
uint32_t fbe_string_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((_buffer.offset() + fbe_offset() + 4 + fbe_string_size) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
return 4 + fbe_string_size;
}
size_t FinalModel<std::string>::get(char* data, size_t size) const noexcept
{
assert(((size == 0) || (data != nullptr)) && "Invalid buffer!");
if ((size > 0) && (data == nullptr))
return 0;
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
uint32_t fbe_string_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((_buffer.offset() + fbe_offset() + 4 + fbe_string_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4 + fbe_string_size) > _buffer.size())
return 4;
size_t result = std::min(size, (size_t)fbe_string_size);
memcpy(data, (const char*)(_buffer.data() + _buffer.offset() + fbe_offset() + 4), result);
return 4 + fbe_string_size;
}
size_t FinalModel<std::string>::get(std::string& value) const noexcept
{
value.clear();
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
uint32_t fbe_string_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((_buffer.offset() + fbe_offset() + 4 + fbe_string_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4 + fbe_string_size) > _buffer.size())
return 4;
value.assign((const char*)(_buffer.data() + _buffer.offset() + fbe_offset() + 4), fbe_string_size);
return 4 + fbe_string_size;
}
size_t FinalModel<std::string>::set(const char* data, size_t size)
{
assert(((size == 0) || (data != nullptr)) && "Invalid buffer!");
if ((size > 0) && (data == nullptr))
return 0;
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
uint32_t fbe_string_size = (uint32_t)size;
assert(((_buffer.offset() + fbe_offset() + 4 + fbe_string_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4 + fbe_string_size) > _buffer.size())
return 4;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_string_size;
memcpy((char*)(_buffer.data() + _buffer.offset() + fbe_offset() + 4), data, fbe_string_size);
return 4 + fbe_string_size;
}
size_t FinalModel<std::string>::set(const std::string& value)
{
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
uint32_t fbe_string_size = (uint32_t)value.size();
assert(((_buffer.offset() + fbe_offset() + 4 + fbe_string_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4 + fbe_string_size) > _buffer.size())
return 4;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_string_size;
memcpy((char*)(_buffer.data() + _buffer.offset() + fbe_offset() + 4), value.data(), fbe_string_size);
return 4 + fbe_string_size;
}
} // namespace FBE

View file

@ -0,0 +1,457 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// Version: 1.7.0.0
#pragma once
#if defined(__clang__)
#pragma clang system_header
#elif defined(__GNUC__)
#pragma GCC system_header
#elif defined(_MSC_VER)
#pragma system_header
#endif
#include "fbe.h"
namespace FBE {
// Fast Binary Encoding base final model
template <typename T, typename TBase = T>
class FinalModelBase
{
public:
FinalModelBase(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the allocation size
size_t fbe_allocation_size(T value) const noexcept { return fbe_size(); }
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the field offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Get the final size
size_t fbe_size() const noexcept { return sizeof(TBase); }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the value is valid
size_t verify() const noexcept;
// Get the field value
size_t get(T& value) const noexcept;
// Set the field value
size_t set(T value) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
};
// Fast Binary Encoding final model
template <typename T>
class FinalModel : public FinalModelBase<T>
{
public:
using FinalModelBase<T>::FinalModelBase;
};
// Fast Binary Encoding final model bool specialization
template <>
class FinalModel<bool> : public FinalModelBase<bool, uint8_t>
{
public:
using FinalModelBase<bool, uint8_t>::FinalModelBase;
};
// Fast Binary Encoding final model char specialization
template <>
class FinalModel<char> : public FinalModelBase<char, uint8_t>
{
public:
using FinalModelBase<char, uint8_t>::FinalModelBase;
};
// Fast Binary Encoding final model wchar specialization
template <>
class FinalModel<wchar_t> : public FinalModelBase<wchar_t, uint32_t>
{
public:
using FinalModelBase<wchar_t, uint32_t>::FinalModelBase;
};
// Fast Binary Encoding final model decimal specialization
template <>
class FinalModel<decimal_t>
{
public:
FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the allocation size
size_t fbe_allocation_size(decimal_t value) const noexcept { return fbe_size(); }
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the field offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Get the final size
size_t fbe_size() const noexcept { return 16; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the decimal value is valid
size_t verify() const noexcept;
// Get the decimal value
size_t get(decimal_t& value) const noexcept;
// Set the decimal value
size_t set(decimal_t value) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
static uint64_t extract(double a) noexcept;
static uint64_t uint32x32(uint32_t a, uint32_t b) noexcept;
static void uint64x64(uint64_t a, uint64_t b, uint64_t& low64, uint32_t& high32) noexcept;
};
// Fast Binary Encoding final model UUID specialization
template <>
class FinalModel<uuid_t>
{
public:
FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the allocation size
size_t fbe_allocation_size(uuid_t value) const noexcept { return fbe_size(); }
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the field offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Get the final size
size_t fbe_size() const noexcept { return 16; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the UUID value is valid
size_t verify() const noexcept;
// Get the UUID value
size_t get(uuid_t& value) const noexcept;
// Set the UUID value
size_t set(uuid_t value) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
};
// Fast Binary Encoding final model bytes specialization
template <>
class FinalModel<buffer_t>
{
public:
FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the allocation size
size_t fbe_allocation_size(const void* data, size_t size) const noexcept { return 4 + size; }
template <size_t N>
size_t fbe_allocation_size(const uint8_t (&data)[N]) const noexcept { return 4 + N; }
template <size_t N>
size_t fbe_allocation_size(const std::array<uint8_t, N>& data) const noexcept { return 4 + N; }
size_t fbe_allocation_size(const std::vector<uint8_t>& value) const noexcept { return 4 + value.size(); }
size_t fbe_allocation_size(const buffer_t& value) const noexcept { return 4 + value.size(); }
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the field offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the bytes value is valid
size_t verify() const noexcept;
// Get the bytes value
size_t get(void* data, size_t size) const noexcept;
// Get the bytes value
template <size_t N>
size_t get(uint8_t (&data)[N]) const noexcept { return get(data, N); }
// Get the bytes value
template <size_t N>
size_t get(std::array<uint8_t, N>& data) const noexcept { return get(data.data(), data.size()); }
// Get the bytes value
size_t get(std::vector<uint8_t>& value) const noexcept;
// Get the bytes value
size_t get(buffer_t& value) const noexcept { return get(value.buffer()); }
// Set the bytes value
size_t set(const void* data, size_t size);
// Set the bytes value
template <size_t N>
size_t set(const uint8_t (&data)[N]) { return set(data, N); }
// Set the bytes value
template <size_t N>
size_t set(const std::array<uint8_t, N>& data) { return set(data.data(), data.size()); }
// Set the bytes value
size_t set(const std::vector<uint8_t>& value) { return set(value.data(), value.size()); }
// Set the bytes value
size_t set(const buffer_t& value) { return set(value.buffer()); }
private:
FBEBuffer& _buffer;
mutable size_t _offset;
};
// Fast Binary Encoding final model string specialization
template <>
class FinalModel<std::string>
{
public:
FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the allocation size
size_t fbe_allocation_size(const char* data, size_t size) const noexcept { return 4 + size; }
template <size_t N>
size_t fbe_allocation_size(const char (&data)[N]) const noexcept { return 4 + N; }
template <size_t N>
size_t fbe_allocation_size(const std::array<char, N>& data) const noexcept { return 4 + N; }
size_t fbe_allocation_size(const std::string& value) const noexcept { return 4 + value.size(); }
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the field offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the string value is valid
size_t verify() const noexcept;
// Get the string value
size_t get(char* data, size_t size) const noexcept;
// Get the string value
template <size_t N>
size_t get(char (&data)[N]) const noexcept { return get(data, N); }
// Get the string value
template <size_t N>
size_t get(std::array<char, N>& data) const noexcept { return get(data.data(), data.size()); }
// Get the string value
size_t get(std::string& value) const noexcept;
// Set the string value
size_t set(const char* data, size_t size);
// Set the string value
template <size_t N>
size_t set(const char (&data)[N]) { return set(data, N); }
// Set the string value
template <size_t N>
size_t set(const std::array<char, N>& data) { return set(data.data(), data.size()); }
// Set the string value
size_t set(const std::string& value);
private:
FBEBuffer& _buffer;
mutable size_t _offset;
};
// Fast Binary Encoding final model optional specialization
template <typename T>
class FinalModel<std::optional<T>>
{
public:
FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset), value(buffer, 0) {}
// Get the allocation size
size_t fbe_allocation_size(const std::optional<T>& opt) const noexcept { return 1 + (opt ? value.fbe_allocation_size(opt.value()) : 0); }
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the field offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
//! Is the value present?
explicit operator bool() const noexcept { return has_value(); }
// Checks if the object contains a value
bool has_value() const noexcept;
// Check if the optional value is valid
size_t verify() const noexcept;
// Get the optional value
size_t get(std::optional<T>& opt) const noexcept;
// Set the optional value
size_t set(const std::optional<T>& opt);
private:
FBEBuffer& _buffer;
mutable size_t _offset;
public:
// Base final model value
FinalModel<T> value;
};
// Fast Binary Encoding final model array
template <typename T, size_t N>
class FinalModelArray
{
public:
FinalModelArray(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the allocation size
template <size_t S>
size_t fbe_allocation_size(const T (&values)[S]) const noexcept;
template <size_t S>
size_t fbe_allocation_size(const std::array<T, S>& values) const noexcept;
size_t fbe_allocation_size(const std::vector<T>& values) const noexcept;
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the field offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the array is valid
size_t verify() const noexcept;
// Get the array as C-array
template <size_t S>
size_t get(T (&values)[S]) const noexcept;
// Get the array as std::array
template <size_t S>
size_t get(std::array<T, S>& values) const noexcept;
// Get the array as std::vector
size_t get(std::vector<T>& values) const noexcept;
// Set the array as C-array
template <size_t S>
size_t set(const T (&values)[S]) noexcept;
// Set the array as std::array
template <size_t S>
size_t set(const std::array<T, S>& values) noexcept;
// Set the array as std::vector
size_t set(const std::vector<T>& values) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
};
// Fast Binary Encoding final model vector
template <typename T>
class FinalModelVector
{
public:
FinalModelVector(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the allocation size
size_t fbe_allocation_size(const std::vector<T>& values) const noexcept;
size_t fbe_allocation_size(const std::list<T>& values) const noexcept;
size_t fbe_allocation_size(const std::set<T>& values) const noexcept;
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the field offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the vector is valid
size_t verify() const noexcept;
// Get the vector as std::vector
size_t get(std::vector<T>& values) const noexcept;
// Get the vector as std::list
size_t get(std::list<T>& values) const noexcept;
// Get the vector as std::set
size_t get(std::set<T>& values) const noexcept;
// Set the vector as std::vector
size_t set(const std::vector<T>& values) noexcept;
// Set the vector as std::list
size_t set(const std::list<T>& values) noexcept;
// Set the vector as std::set
size_t set(const std::set<T>& values) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
};
// Fast Binary Encoding final model map
template <typename TKey, typename TValue>
class FinalModelMap
{
public:
FinalModelMap(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the allocation size
size_t fbe_allocation_size(const std::map<TKey, TValue>& values) const noexcept;
size_t fbe_allocation_size(const std::unordered_map<TKey, TValue>& values) const noexcept;
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Set the field offset
size_t fbe_offset(size_t offset) const noexcept { return _offset = offset; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the map is valid
size_t verify() const noexcept;
// Get the map as std::map
size_t get(std::map<TKey, TValue>& values) const noexcept;
// Get the map as std::unordered_map
size_t get(std::unordered_map<TKey, TValue>& values) const noexcept;
// Set the map as std::map
size_t set(const std::map<TKey, TValue>& values) noexcept;
// Set the map as std::unordered_map
size_t set(const std::unordered_map<TKey, TValue>& values) noexcept;
private:
FBEBuffer& _buffer;
mutable size_t _offset;
};
} // namespace FBE
#include "fbe_final_models.inl"

View file

@ -0,0 +1,635 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// Version: 1.7.0.0
namespace FBE {
template <typename T, typename TBase>
inline size_t FinalModelBase<T, TBase>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
return fbe_size();
}
template <typename T, typename TBase>
inline size_t FinalModelBase<T, TBase>::get(T& value) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
value = (T)(*((const TBase*)(_buffer.data() + _buffer.offset() + fbe_offset())));
return fbe_size();
}
template <typename T, typename TBase>
inline size_t FinalModelBase<T, TBase>::set(T value) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
*((TBase*)(_buffer.data() + _buffer.offset() + fbe_offset())) = (TBase)value;
return fbe_size();
}
template <typename T>
inline bool FinalModel<std::optional<T>>::has_value() const noexcept
{
if ((_buffer.offset() + fbe_offset() + 1) > _buffer.size())
return false;
uint8_t fbe_has_value = *((const uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
return (fbe_has_value != 0);
}
template <typename T>
inline size_t FinalModel<std::optional<T>>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + 1) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
uint8_t fbe_has_value = *((const uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_has_value == 0)
return 1;
_buffer.shift(fbe_offset() + 1);
size_t fbe_result = value.verify();
_buffer.unshift(fbe_offset() + 1);
return 1 + fbe_result;
}
template <typename T>
inline size_t FinalModel<std::optional<T>>::get(std::optional<T>& opt) const noexcept
{
opt = std::nullopt;
assert(((_buffer.offset() + fbe_offset() + 1) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 1) > _buffer.size())
return 0;
if (!has_value())
return 1;
_buffer.shift(fbe_offset() + 1);
T temp = T();
size_t size = value.get(temp);
opt.emplace(temp);
_buffer.unshift(fbe_offset() + 1);
return 1 + size;
}
template <typename T>
inline size_t FinalModel<std::optional<T>>::set(const std::optional<T>& opt)
{
assert(((_buffer.offset() + fbe_offset() + 1) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 1) > _buffer.size())
return 0;
uint8_t fbe_has_value = opt ? 1 : 0;
*((uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_has_value;
if (fbe_has_value == 0)
return 1;
_buffer.shift(fbe_offset() + 1);
size_t size = 0;
if (opt)
size = value.set(opt.value());
_buffer.unshift(fbe_offset() + 1);
return 1 + size;
}
template <typename T, size_t N>
template <size_t S>
inline size_t FinalModelArray<T, N>::fbe_allocation_size(const T (&values)[S]) const noexcept
{
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = 0; (i < S) && (i < N); ++i)
size += fbe_model.fbe_allocation_size(values[i]);
return size;
}
template <typename T, size_t N>
template <size_t S>
inline size_t FinalModelArray<T, N>::fbe_allocation_size(const std::array<T, S>& values) const noexcept
{
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = 0; (i < S) && (i < N); ++i)
size += fbe_model.fbe_allocation_size(values[i]);
return size;
}
template <typename T, size_t N>
inline size_t FinalModelArray<T, N>::fbe_allocation_size(const std::vector<T>& values) const noexcept
{
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = 0; (i < values.size()) && (i < N); ++i)
size += fbe_model.fbe_allocation_size(values[i]);
return size;
}
template <typename T, size_t N>
inline size_t FinalModelArray<T, N>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset()) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = N; i-- > 0;)
{
size_t offset = fbe_model.verify();
if (offset == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T, size_t N>
template <size_t S>
inline size_t FinalModelArray<T, N>::get(T (&values)[S]) const noexcept
{
assert(((_buffer.offset() + fbe_offset()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset()) > _buffer.size())
return 0;
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = 0; (i < S) && (i < N); ++i)
{
size_t offset = fbe_model.get(values[i]);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T, size_t N>
template <size_t S>
inline size_t FinalModelArray<T, N>::get(std::array<T, S>& values) const noexcept
{
assert(((_buffer.offset() + fbe_offset()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset()) > _buffer.size())
return 0;
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = 0; (i < S) && (i < N); ++i)
{
size_t offset = fbe_model.get(values[i]);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T, size_t N>
inline size_t FinalModelArray<T, N>::get(std::vector<T>& values) const noexcept
{
values.clear();
assert(((_buffer.offset() + fbe_offset()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset()) > _buffer.size())
return 0;
values.reserve(N);
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = N; i-- > 0;)
{
T value = T();
size_t offset = fbe_model.get(value);
values.emplace_back(value);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T, size_t N>
template <size_t S>
inline size_t FinalModelArray<T, N>::set(const T (&values)[S]) noexcept
{
assert(((_buffer.offset() + fbe_offset()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset()) > _buffer.size())
return 0;
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = 0; (i < S) && (i < N); ++i)
{
size_t offset = fbe_model.set(values[i]);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T, size_t N>
template <size_t S>
inline size_t FinalModelArray<T, N>::set(const std::array<T, S>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset()) > _buffer.size())
return 0;
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = 0; (i < S) && (i < N); ++i)
{
size_t offset = fbe_model.set(values[i]);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T, size_t N>
inline size_t FinalModelArray<T, N>::set(const std::vector<T>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset()) > _buffer.size())
return 0;
size_t size = 0;
FinalModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = 0; (i < values.size()) && (i < N); ++i)
{
size_t offset = fbe_model.set(values[i]);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::fbe_allocation_size(const std::vector<T>& values) const noexcept
{
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (const auto& value : values)
size += fbe_model.fbe_allocation_size(value);
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::fbe_allocation_size(const std::list<T>& values) const noexcept
{
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (const auto& value : values)
size += fbe_model.fbe_allocation_size(value);
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::fbe_allocation_size(const std::set<T>& values) const noexcept
{
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (const auto& value : values)
size += fbe_model.fbe_allocation_size(value);
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
uint32_t fbe_vector_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (size_t i = fbe_vector_size; i-- > 0;)
{
size_t offset = fbe_model.verify();
if (offset == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::get(std::vector<T>& values) const noexcept
{
values.clear();
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
size_t fbe_vector_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_vector_size == 0)
return 4;
values.reserve(fbe_vector_size);
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (size_t i = fbe_vector_size; i-- > 0;)
{
T value = T();
size_t offset = fbe_model.get(value);
values.emplace_back(value);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::get(std::list<T>& values) const noexcept
{
values.clear();
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
size_t fbe_vector_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_vector_size == 0)
return 4;
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (size_t i = fbe_vector_size; i-- > 0;)
{
T value = T();
size_t offset = fbe_model.get(value);
values.emplace_back(value);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::get(std::set<T>& values) const noexcept
{
values.clear();
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
size_t fbe_vector_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_vector_size == 0)
return 4;
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (size_t i = fbe_vector_size; i-- > 0;)
{
T value = T();
size_t offset = fbe_model.get(value);
values.emplace(value);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::set(const std::vector<T>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = (uint32_t)values.size();
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (const auto& value : values)
{
size_t offset = fbe_model.set(value);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::set(const std::list<T>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = (uint32_t)values.size();
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (const auto& value : values)
{
size_t offset = fbe_model.set(value);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename T>
inline size_t FinalModelVector<T>::set(const std::set<T>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = (uint32_t)values.size();
size_t size = 4;
FinalModel<T> fbe_model(_buffer, fbe_offset() + 4);
for (const auto& value : values)
{
size_t offset = fbe_model.set(value);
fbe_model.fbe_shift(offset);
size += offset;
}
return size;
}
template <typename TKey, typename TValue>
inline size_t FinalModelMap<TKey, TValue>::fbe_allocation_size(const std::map<TKey, TValue>& values) const noexcept
{
size_t size = 4;
FinalModel<TKey> fbe_model_key(_buffer, fbe_offset() + 4);
FinalModel<TValue> fbe_model_value(_buffer, fbe_offset() + 4);
for (const auto& value : values)
{
size += fbe_model_key.fbe_allocation_size(value.first);
size += fbe_model_value.fbe_allocation_size(value.second);
}
return size;
}
template <typename TKey, typename TValue>
inline size_t FinalModelMap<TKey, TValue>::fbe_allocation_size(const std::unordered_map<TKey, TValue>& values) const noexcept
{
size_t size = 4;
FinalModel<TKey> fbe_model_key(_buffer, fbe_offset() + 4);
FinalModel<TValue> fbe_model_value(_buffer, fbe_offset() + 4);
for (const auto& value : values)
{
size += fbe_model_key.fbe_allocation_size(value.first);
size += fbe_model_value.fbe_allocation_size(value.second);
}
return size;
}
template <typename TKey, typename TValue>
inline size_t FinalModelMap<TKey, TValue>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return std::numeric_limits<std::size_t>::max();
uint32_t fbe_map_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
size_t size = 4;
FinalModel<TKey> fbe_model_key(_buffer, fbe_offset() + 4);
FinalModel<TValue> fbe_model_value(_buffer, fbe_offset() + 4);
for (size_t i = fbe_map_size; i-- > 0;)
{
size_t offset_key = fbe_model_key.verify();
if (offset_key == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_model_key.fbe_shift(offset_key);
fbe_model_value.fbe_shift(offset_key);
size += offset_key;
size_t offset_value = fbe_model_value.verify();
if (offset_value == std::numeric_limits<std::size_t>::max())
return std::numeric_limits<std::size_t>::max();
fbe_model_key.fbe_shift(offset_value);
fbe_model_value.fbe_shift(offset_value);
size += offset_value;
}
return size;
}
template <typename TKey, typename TValue>
inline size_t FinalModelMap<TKey, TValue>::get(std::map<TKey, TValue>& values) const noexcept
{
values.clear();
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
size_t fbe_map_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_map_size == 0)
return 4;
size_t size = 4;
FinalModel<TKey> fbe_model_key(_buffer, fbe_offset() + 4);
FinalModel<TValue> fbe_model_value(_buffer, fbe_offset() + 4);
for (size_t i = fbe_map_size; i-- > 0;)
{
TKey key;
TValue value;
size_t offset_key = fbe_model_key.get(key);
fbe_model_key.fbe_shift(offset_key);
fbe_model_value.fbe_shift(offset_key);
size_t offset_value = fbe_model_value.get(value);
fbe_model_key.fbe_shift(offset_value);
fbe_model_value.fbe_shift(offset_value);
values.emplace(key, value);
size += offset_key + offset_value;
}
return size;
}
template <typename TKey, typename TValue>
inline size_t FinalModelMap<TKey, TValue>::get(std::unordered_map<TKey, TValue>& values) const noexcept
{
values.clear();
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
size_t fbe_map_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_map_size == 0)
return 4;
size_t size = 4;
FinalModel<TKey> fbe_model_key(_buffer, fbe_offset() + 4);
FinalModel<TValue> fbe_model_value(_buffer, fbe_offset() + 4);
for (size_t i = fbe_map_size; i-- > 0;)
{
TKey key;
TValue value;
size_t offset_key = fbe_model_key.get(key);
fbe_model_key.fbe_shift(offset_key);
fbe_model_value.fbe_shift(offset_key);
size_t offset_value = fbe_model_value.get(value);
fbe_model_key.fbe_shift(offset_value);
fbe_model_value.fbe_shift(offset_value);
values.emplace(key, value);
size += offset_key + offset_value;
}
return size;
}
template <typename TKey, typename TValue>
inline size_t FinalModelMap<TKey, TValue>::set(const std::map<TKey, TValue>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = (uint32_t)values.size();
size_t size = 4;
FinalModel<TKey> fbe_model_key(_buffer, fbe_offset() + 4);
FinalModel<TValue> fbe_model_value(_buffer, fbe_offset() + 4);
for (const auto& value : values)
{
size_t offset_key = fbe_model_key.set(value.first);
fbe_model_key.fbe_shift(offset_key);
fbe_model_value.fbe_shift(offset_key);
size_t offset_value = fbe_model_value.set(value.second);
fbe_model_key.fbe_shift(offset_value);
fbe_model_value.fbe_shift(offset_value);
size += offset_key + offset_value;
}
return size;
}
template <typename TKey, typename TValue>
inline size_t FinalModelMap<TKey, TValue>::set(const std::unordered_map<TKey, TValue>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + 4) > _buffer.size())
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = (uint32_t)values.size();
size_t size = 4;
FinalModel<TKey> fbe_model_key(_buffer, fbe_offset() + 4);
FinalModel<TValue> fbe_model_value(_buffer, fbe_offset() + 4);
for (const auto& value : values)
{
size_t offset_key = fbe_model_key.set(value.first);
fbe_model_key.fbe_shift(offset_key);
fbe_model_value.fbe_shift(offset_key);
size_t offset_value = fbe_model_value.set(value.second);
fbe_model_key.fbe_shift(offset_value);
fbe_model_value.fbe_shift(offset_value);
size += offset_key + offset_value;
}
return size;
}
} // namespace FBE

View file

@ -0,0 +1,514 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// Version: 1.7.0.0
#include "fbe_models.h"
namespace FBE {
uint64_t FieldModel<decimal_t>::extract(double a) noexcept
{
uint64_t result;
std::memcpy(&result, &a, sizeof(double));
return result;
}
uint64_t FieldModel<decimal_t>::uint32x32(uint32_t a, uint32_t b) noexcept
{
return (uint64_t)a * (uint64_t)b;
}
void FieldModel<decimal_t>::uint64x64(uint64_t a, uint64_t b, uint64_t& low64, uint32_t& high32) noexcept
{
uint64_t low = uint32x32((uint32_t)a, (uint32_t)b);
uint64_t mid = uint32x32((uint32_t)a, (uint32_t)(b >> 32));
uint64_t high = uint32x32((uint32_t)(a >> 32), (uint32_t)(b >> 32));
high += (mid >> 32);
low += (mid <<= 32);
// Test for carry
if (low < mid)
high++;
mid = uint32x32((uint32_t)(a >> 32), (uint32_t)b);
high += (mid >> 32);
low += (mid <<= 32);
// Test for carry
if (low < mid)
high++;
if (high > 0xFFFFFFFFu)
{
low64 = 0;
high32 = 0;
}
low64 = low;
high32 = (uint32_t)high;
}
void FieldModel<decimal_t>::get(decimal_t& value, decimal_t defaults) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
{
value = defaults;
return;
}
// Value taken via reverse engineering the double that corresponds to 2^64
const double ds2to64 = 1.8446744073709552e+019;
// Read decimal parts
uint64_t low = *((const uint64_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
uint32_t high = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 8));
uint32_t flags = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 12));
// Calculate decimal value
double dValue = ((double)low + (double)high * ds2to64) / pow(10.0, (uint8_t)(flags >> 16));
if (flags & 0x80000000)
dValue = -dValue;
value = dValue;
}
void FieldModel<decimal_t>::set(decimal_t value) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
// The most we can scale by is 10^28, which is just slightly more
// than 2^93. So a float with an exponent of -94 could just
// barely reach 0.5, but smaller exponents will always round to zero.
const uint32_t DBLBIAS = 1022;
// Get exponent value
double dValue = (double)value;
int32_t iExp = (int32_t)(((uint32_t)(extract(dValue) >> 52) & 0x7FFu) - DBLBIAS);
if ((iExp < -94) || (iExp > 96))
{
// Value too big for .NET Decimal (exponent is limited to [-94, 96])
memset((uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()), 0, 16);
return;
}
uint32_t flags = 0;
if (dValue < 0)
{
dValue = -dValue;
flags = 0x80000000;
}
// Round the input to a 15-digit integer. The R8 format has
// only 15 digits of precision, and we want to keep garbage digits
// out of the Decimal were making.
// Calculate max power of 10 input value could have by multiplying
// the exponent by log10(2). Using scaled integer multiplcation,
// log10(2) * 2 ^ 16 = .30103 * 65536 = 19728.3.
int32_t iPower = 14 - ((iExp * 19728) >> 16);
// iPower is between -14 and 43
if (iPower >= 0)
{
// We have less than 15 digits, scale input up.
if (iPower > 28)
iPower = 28;
dValue *= pow(10.0, iPower);
}
else
{
if ((iPower != -1) || (dValue >= 1E15))
dValue /= pow(10.0, -iPower);
else
iPower = 0; // didn't scale it
}
assert(dValue < 1E15);
if ((dValue < 1E14) && (iPower < 28))
{
dValue *= 10;
iPower++;
assert(dValue >= 1E14);
}
// Round to int64
uint64_t ulMant;
ulMant = (uint64_t)(int64_t)dValue;
dValue -= (int64_t)ulMant; // difference between input & integer
if ((dValue > 0.5) || ((dValue == 0.5) && ((ulMant & 1) != 0)))
ulMant++;
if (ulMant == 0)
{
// Mantissa is 0
memset((uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()), 0, 16);
return;
}
if (iPower < 0)
{
// Add -iPower factors of 10, -iPower <= (29 - 15) = 14
iPower = -iPower;
if (iPower < 10)
{
double pow10 = (double)powl(10.0, iPower);
uint64_t low64 = uint32x32((uint32_t)ulMant, (uint32_t)pow10);
uint64_t high64 = uint32x32((uint32_t)(ulMant >> 32), (uint32_t)pow10);
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = (uint32_t)low64;
high64 += low64 >> 32;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 4)) = (uint32_t)high64;
high64 >>= 32;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 8)) = (uint32_t)high64;
}
else
{
// Have a big power of 10.
assert(iPower <= 14);
uint64_t low64;
uint32_t high32;
uint64x64(ulMant, (uint64_t)pow(10.0, iPower), low64, high32);
*((uint64_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = low64;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 8)) = high32;
}
}
else
{
// Factor out powers of 10 to reduce the scale, if possible.
// The maximum number we could factor out would be 14. This
// comes from the fact we have a 15-digit number, and the
// MSD must be non-zero -- but the lower 14 digits could be
// zero. Note also the scale factor is never negative, so
// we can't scale by any more than the power we used to
// get the integer.
int lmax = iPower;
if (lmax > 14)
lmax = 14;
if ((((uint8_t)ulMant) == 0) && (lmax >= 8))
{
const uint32_t den = 100000000;
uint64_t div = ulMant / den;
if ((uint32_t)ulMant == (uint32_t)(div * den))
{
ulMant = div;
iPower -= 8;
lmax -= 8;
}
}
if ((((uint32_t)ulMant & 0xF) == 0) && (lmax >= 4))
{
const uint32_t den = 10000;
uint64_t div = ulMant / den;
if ((uint32_t)ulMant == (uint32_t)(div * den))
{
ulMant = div;
iPower -= 4;
lmax -= 4;
}
}
if ((((uint32_t)ulMant & 3) == 0) && (lmax >= 2))
{
const uint32_t den = 100;
uint64_t div = ulMant / den;
if ((uint32_t)ulMant == (uint32_t)(div * den))
{
ulMant = div;
iPower -= 2;
lmax -= 2;
}
}
if ((((uint32_t)ulMant & 1) == 0) && (lmax >= 1))
{
const uint32_t den = 10;
uint64_t div = ulMant / den;
if ((uint32_t)ulMant == (uint32_t)(div * den))
{
ulMant = div;
iPower--;
}
}
flags |= (uint32_t)iPower << 16;
*((uint64_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = ulMant;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 8)) = 0;
}
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 12)) = flags;
}
void FieldModel<uuid_t>::get(uuid_t& value, uuid_t defaults) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
{
value = defaults;
return;
}
std::memcpy(value.data().data(), (const uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()), fbe_size());
}
void FieldModel<uuid_t>::set(uuid_t value) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
std::memcpy((uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()), value.data().data(), fbe_size());
}
size_t FieldModel<buffer_t>::fbe_extra() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_bytes_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_bytes_offset == 0) || ((_buffer.offset() + fbe_bytes_offset + 4) > _buffer.size()))
return 0;
uint32_t fbe_bytes_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_bytes_offset));
return (size_t)(4 + fbe_bytes_size);
}
bool FieldModel<buffer_t>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return true;
uint32_t fbe_bytes_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_bytes_offset == 0)
return true;
if ((_buffer.offset() + fbe_bytes_offset + 4) > _buffer.size())
return false;
uint32_t fbe_bytes_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_bytes_offset));
if ((_buffer.offset() + fbe_bytes_offset + 4 + fbe_bytes_size) > _buffer.size())
return false;
return true;
}
size_t FieldModel<buffer_t>::get(void* data, size_t size) const noexcept
{
assert(((size == 0) || (data != nullptr)) && "Invalid buffer!");
if ((size > 0) && (data == nullptr))
return 0;
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_bytes_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_bytes_offset == 0)
return 0;
assert(((_buffer.offset() + fbe_bytes_offset + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_bytes_offset + 4) > _buffer.size())
return 0;
uint32_t fbe_bytes_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_bytes_offset));
assert(((_buffer.offset() + fbe_bytes_offset + 4 + fbe_bytes_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_bytes_offset + 4 + fbe_bytes_size) > _buffer.size())
return 0;
size_t result = std::min(size, (size_t)fbe_bytes_size);
memcpy(data, (const char*)(_buffer.data() + _buffer.offset() + fbe_bytes_offset + 4), result);
return result;
}
void FieldModel<buffer_t>::get(std::vector<uint8_t>& value) const noexcept
{
value.clear();
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
uint32_t fbe_bytes_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_bytes_offset == 0)
return;
assert(((_buffer.offset() + fbe_bytes_offset + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_bytes_offset + 4) > _buffer.size())
return;
uint32_t fbe_bytes_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_bytes_offset));
assert(((_buffer.offset() + fbe_bytes_offset + 4 + fbe_bytes_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_bytes_offset + 4 + fbe_bytes_size) > _buffer.size())
return;
const char* fbe_bytes = (const char*)(_buffer.data() + _buffer.offset() + fbe_bytes_offset + 4);
value.assign(fbe_bytes, fbe_bytes + fbe_bytes_size);
}
void FieldModel<buffer_t>::set(const void* data, size_t size)
{
assert(((size == 0) || (data != nullptr)) && "Invalid buffer!");
if ((size > 0) && (data == nullptr))
return;
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
uint32_t fbe_bytes_size = (uint32_t)size;
uint32_t fbe_bytes_offset = (uint32_t)(_buffer.allocate(4 + fbe_bytes_size) - _buffer.offset());
assert(((fbe_bytes_offset > 0) && ((_buffer.offset() + fbe_bytes_offset + 4 + fbe_bytes_size) <= _buffer.size())) && "Model is broken!");
if ((fbe_bytes_offset == 0) || ((_buffer.offset() + fbe_bytes_offset + 4 + fbe_bytes_size) > _buffer.size()))
return;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_bytes_offset;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_bytes_offset)) = fbe_bytes_size;
memcpy((char*)(_buffer.data() + _buffer.offset() + fbe_bytes_offset + 4), data, fbe_bytes_size);
}
size_t FieldModel<std::string>::fbe_extra() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_string_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_string_offset == 0) || ((_buffer.offset() + fbe_string_offset + 4) > _buffer.size()))
return 0;
uint32_t fbe_string_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_string_offset));
return (size_t)(4 + fbe_string_size);
}
bool FieldModel<std::string>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return true;
uint32_t fbe_string_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_string_offset == 0)
return true;
if ((_buffer.offset() + fbe_string_offset + 4) > _buffer.size())
return false;
uint32_t fbe_string_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_string_offset));
if ((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) > _buffer.size())
return false;
return true;
}
size_t FieldModel<std::string>::get(char* data, size_t size) const noexcept
{
assert(((size == 0) || (data != nullptr)) && "Invalid buffer!");
if ((size > 0) && (data == nullptr))
return 0;
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_string_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_string_offset == 0)
return 0;
assert(((_buffer.offset() + fbe_string_offset + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_string_offset + 4) > _buffer.size())
return 0;
uint32_t fbe_string_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_string_offset));
assert(((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) > _buffer.size())
return 0;
size_t result = std::min(size, (size_t)fbe_string_size);
memcpy(data, (const char*)(_buffer.data() + _buffer.offset() + fbe_string_offset + 4), result);
return result;
}
void FieldModel<std::string>::get(std::string& value) const noexcept
{
value.clear();
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
uint32_t fbe_string_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + _offset));
if (fbe_string_offset == 0)
return;
assert(((_buffer.offset() + fbe_string_offset + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_string_offset + 4) > _buffer.size())
return;
uint32_t fbe_string_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_string_offset));
assert(((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) > _buffer.size())
return;
value.assign((const char*)(_buffer.data() + _buffer.offset() + fbe_string_offset + 4), fbe_string_size);
}
void FieldModel<std::string>::get(std::string& value, const std::string& defaults) const noexcept
{
value = defaults;
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
uint32_t fbe_string_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + _offset));
if (fbe_string_offset == 0)
return;
assert(((_buffer.offset() + fbe_string_offset + 4) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_string_offset + 4) > _buffer.size())
return;
uint32_t fbe_string_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_string_offset));
assert(((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) > _buffer.size())
return;
value.assign((const char*)(_buffer.data() + _buffer.offset() + fbe_string_offset + 4), fbe_string_size);
}
void FieldModel<std::string>::set(const char* data, size_t size)
{
assert(((size == 0) || (data != nullptr)) && "Invalid buffer!");
if ((size > 0) && (data == nullptr))
return;
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
uint32_t fbe_string_size = (uint32_t)size;
uint32_t fbe_string_offset = (uint32_t)(_buffer.allocate(4 + fbe_string_size) - _buffer.offset());
assert(((fbe_string_offset > 0) && ((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) <= _buffer.size())) && "Model is broken!");
if ((fbe_string_offset == 0) || ((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) > _buffer.size()))
return;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_string_offset;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_string_offset)) = fbe_string_size;
memcpy((char*)(_buffer.data() + _buffer.offset() + fbe_string_offset + 4), data, fbe_string_size);
}
void FieldModel<std::string>::set(const std::string& value)
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
uint32_t fbe_string_size = (uint32_t)value.size();
uint32_t fbe_string_offset = (uint32_t)(_buffer.allocate(4 + fbe_string_size) - _buffer.offset());
assert(((fbe_string_offset > 0) && ((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) <= _buffer.size())) && "Model is broken!");
if ((fbe_string_offset == 0) || ((_buffer.offset() + fbe_string_offset + 4 + fbe_string_size) > _buffer.size()))
return;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_string_offset;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_string_offset)) = fbe_string_size;
memcpy((char*)(_buffer.data() + _buffer.offset() + fbe_string_offset + 4), value.data(), fbe_string_size);
}
} // namespace FBE

469
fbe/user_model/fbe_models.h Normal file
View file

@ -0,0 +1,469 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// Version: 1.7.0.0
#pragma once
#if defined(__clang__)
#pragma clang system_header
#elif defined(__GNUC__)
#pragma GCC system_header
#elif defined(_MSC_VER)
#pragma system_header
#endif
#include "fbe.h"
namespace FBE {
// Fast Binary Encoding base field model
template <typename T, typename TBase = T>
class FieldModelBase
{
public:
FieldModelBase(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return sizeof(TBase); }
// Get the field extra size
size_t fbe_extra() const noexcept { return 0; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the value is valid
bool verify() const noexcept { return true; }
// Get the field value
void get(T& value, T defaults = (T)0) const noexcept;
// Set the field value
void set(T value) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
};
// Fast Binary Encoding field model
template <typename T>
class FieldModel : public FieldModelBase<T>
{
public:
using FieldModelBase<T>::FieldModelBase;
};
// Fast Binary Encoding field model bool specialization
template <>
class FieldModel<bool> : public FieldModelBase<bool, uint8_t>
{
public:
using FieldModelBase<bool, uint8_t>::FieldModelBase;
};
// Fast Binary Encoding field model char specialization
template <>
class FieldModel<char> : public FieldModelBase<char, uint8_t>
{
public:
using FieldModelBase<char, uint8_t>::FieldModelBase;
};
// Fast Binary Encoding field model wchar specialization
template <>
class FieldModel<wchar_t> : public FieldModelBase<wchar_t, uint32_t>
{
public:
using FieldModelBase<wchar_t, uint32_t>::FieldModelBase;
};
// Fast Binary Encoding field model decimal specialization
template <>
class FieldModel<decimal_t>
{
public:
FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 16; }
// Get the field extra size
size_t fbe_extra() const noexcept { return 0; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the decimal value is valid
bool verify() const noexcept { return true; }
// Get the decimal value
void get(decimal_t& value, decimal_t defaults = decimal_t()) const noexcept;
// Set the decimal value
void set(decimal_t value) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
static uint64_t extract(double a) noexcept;
static uint64_t uint32x32(uint32_t a, uint32_t b) noexcept;
static void uint64x64(uint64_t a, uint64_t b, uint64_t& low64, uint32_t& high32) noexcept;
};
// Fast Binary Encoding field model UUID specialization
template <>
class FieldModel<uuid_t>
{
public:
FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 16; }
// Get the field extra size
size_t fbe_extra() const noexcept { return 0; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the UUID value is valid
bool verify() const noexcept { return true; }
// Get the UUID value
void get(uuid_t& value, uuid_t defaults = uuid_t::nil()) const noexcept;
// Set the UUID value
void set(uuid_t value) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
};
// Fast Binary Encoding field model bytes specialization
template <>
class FieldModel<buffer_t>
{
public:
FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 4; }
// Get the field extra size
size_t fbe_extra() const noexcept;
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the bytes value is valid
bool verify() const noexcept;
// Get the bytes value
size_t get(void* data, size_t size) const noexcept;
// Get the bytes value
template <size_t N>
size_t get(uint8_t (&data)[N]) const noexcept { return get(data, N); }
// Get the bytes value
template <size_t N>
size_t get(std::array<uint8_t, N>& data) const noexcept { return get(data.data(), data.size()); }
// Get the bytes value
void get(std::vector<uint8_t>& value) const noexcept;
// Get the bytes value
void get(buffer_t& value) const noexcept { get(value.buffer()); }
// Set the bytes value
void set(const void* data, size_t size);
// Set the bytes value
template <size_t N>
void set(const uint8_t (&data)[N]) { set(data, N); }
// Set the bytes value
template <size_t N>
void set(const std::array<uint8_t, N>& data) { set(data.data(), data.size()); }
// Set the bytes value
void set(const std::vector<uint8_t>& value) { set(value.data(), value.size()); }
// Set the bytes value
void set(const buffer_t& value) { set(value.buffer()); }
private:
FBEBuffer& _buffer;
size_t _offset;
};
// Fast Binary Encoding field model string specialization
template <>
class FieldModel<std::string>
{
public:
FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 4; }
// Get the field extra size
size_t fbe_extra() const noexcept;
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Check if the string value is valid
bool verify() const noexcept;
// Get the string value
size_t get(char* data, size_t size) const noexcept;
// Get the string value
template <size_t N>
size_t get(char (&data)[N]) const noexcept { return get(data, N); }
// Get the string value
template <size_t N>
size_t get(std::array<char, N>& data) const noexcept { return get(data.data(), data.size()); }
// Get the string value
void get(std::string& value) const noexcept;
// Get the string value
void get(std::string& value, const std::string& defaults) const noexcept;
// Set the string value
void set(const char* data, size_t size);
// Set the string value
template <size_t N>
void set(const char (&data)[N]) { set(data, N); }
// Set the string value
template <size_t N>
void set(const std::array<char, N>& data) { set(data.data(), data.size()); }
// Set the string value
void set(const std::string& value);
private:
FBEBuffer& _buffer;
size_t _offset;
};
// Fast Binary Encoding field model optional specialization
template <typename T>
class FieldModel<std::optional<T>>
{
public:
FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset), value(buffer, 0) {}
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 1 + 4; }
// Get the field extra size
size_t fbe_extra() const noexcept;
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
//! Is the value present?
explicit operator bool() const noexcept { return has_value(); }
// Checks if the object contains a value
bool has_value() const noexcept;
// Check if the optional value is valid
bool verify() const noexcept;
// Get the optional value (being phase)
size_t get_begin() const noexcept;
// Get the optional value (end phase)
void get_end(size_t fbe_begin) const noexcept;
// Get the optional value
void get(std::optional<T>& opt, const std::optional<T>& defaults = std::nullopt) const noexcept;
// Set the optional value (begin phase)
size_t set_begin(bool has_value);
// Set the optional value (end phase)
void set_end(size_t fbe_begin);
// Set the optional value
void set(const std::optional<T>& opt);
private:
FBEBuffer& _buffer;
size_t _offset;
public:
// Base field model value
FieldModel<T> value;
};
// Fast Binary Encoding field model array
template <typename T, size_t N>
class FieldModelArray
{
public:
FieldModelArray(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset), _model(buffer, offset) {}
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return N * _model.fbe_size(); }
// Get the field extra size
size_t fbe_extra() const noexcept { return 0; }
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Get the array
const uint8_t* data() const noexcept;
// Get the array
uint8_t* data() noexcept;
// Get the array offset
size_t offset() const noexcept { return 0; }
// Get the array size
size_t size() const noexcept { return N; }
// Array index operator
FieldModel<T> operator[](size_t index) const noexcept;
// Check if the array is valid
bool verify() const noexcept;
// Get the array as C-array
template <size_t S>
void get(T (&values)[S]) const noexcept;
// Get the array as std::array
template <size_t S>
void get(std::array<T, S>& values) const noexcept;
// Get the array as std::vector
void get(std::vector<T>& values) const noexcept;
// Set the array as C-array
template <size_t S>
void set(const T (&values)[S]) noexcept;
// Set the array as std::array
template <size_t S>
void set(const std::array<T, S>& values) noexcept;
// Set the array as std::vector
void set(const std::vector<T>& values) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
FieldModel<T> _model;
};
// Fast Binary Encoding field model vector
template <typename T>
class FieldModelVector
{
public:
FieldModelVector(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 4; }
// Get the field extra size
size_t fbe_extra() const noexcept;
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Get the vector offset
size_t offset() const noexcept;
// Get the vector size
size_t size() const noexcept;
// Vector index operator
FieldModel<T> operator[](size_t index) const noexcept;
// Resize the vector and get its first model
FieldModel<T> resize(size_t size);
// Check if the vector is valid
bool verify() const noexcept;
// Get the vector as std::vector
void get(std::vector<T>& values) const noexcept;
// Get the vector as std::list
void get(std::list<T>& values) const noexcept;
// Get the vector as std::set
void get(std::set<T>& values) const noexcept;
// Set the vector as std::vector
void set(const std::vector<T>& values) noexcept;
// Set the vector as std::list
void set(const std::list<T>& values) noexcept;
// Set the vector as std::set
void set(const std::set<T>& values) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
};
// Fast Binary Encoding field model map
template <typename TKey, typename TValue>
class FieldModelMap
{
public:
FieldModelMap(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset) {}
// Get the field offset
size_t fbe_offset() const noexcept { return _offset; }
// Get the field size
size_t fbe_size() const noexcept { return 4; }
// Get the field extra size
size_t fbe_extra() const noexcept;
// Shift the current field offset
void fbe_shift(size_t size) noexcept { _offset += size; }
// Unshift the current field offset
void fbe_unshift(size_t size) noexcept { _offset -= size; }
// Get the map offset
size_t offset() const noexcept;
// Get the map size
size_t size() const noexcept;
// Map index operator
std::pair<FieldModel<TKey>, FieldModel<TValue>> operator[](size_t index) const noexcept;
// Resize the map and get its first model
std::pair<FieldModel<TKey>, FieldModel<TValue>> resize(size_t size);
// Check if the map is valid
bool verify() const noexcept;
// Get the map as std::map
void get(std::map<TKey, TValue>& values) const noexcept;
// Get the map as std::unordered_map
void get(std::unordered_map<TKey, TValue>& values) const noexcept;
// Set the map as std::map
void set(const std::map<TKey, TValue>& values) noexcept;
// Set the map as std::unordered_map
void set(const std::unordered_map<TKey, TValue>& values) noexcept;
private:
FBEBuffer& _buffer;
size_t _offset;
};
} // namespace FBE
#include "fbe_models.inl"

View file

@ -0,0 +1,687 @@
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// Version: 1.7.0.0
namespace FBE {
template <typename T, typename TBase>
inline void FieldModelBase<T, TBase>::get(T& value, T defaults) const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
{
value = defaults;
return;
}
value = (T)(*((const TBase*)(_buffer.data() + _buffer.offset() + fbe_offset())));
}
template <typename T, typename TBase>
inline void FieldModelBase<T, TBase>::set(T value) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
*((TBase*)(_buffer.data() + _buffer.offset() + fbe_offset())) = (TBase)value;
}
template <typename T>
inline size_t FieldModel<std::optional<T>>::fbe_extra() const noexcept
{
if (!has_value())
return 0;
uint32_t fbe_optional_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 1));
if ((fbe_optional_offset == 0) || ((_buffer.offset() + fbe_optional_offset + 4) > _buffer.size()))
return 0;
_buffer.shift(fbe_optional_offset);
size_t fbe_result = value.fbe_size() + value.fbe_extra();
_buffer.unshift(fbe_optional_offset);
return fbe_result;
}
template <typename T>
inline bool FieldModel<std::optional<T>>::has_value() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return false;
uint8_t fbe_has_value = *((const uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
return (fbe_has_value != 0);
}
template <typename T>
inline bool FieldModel<std::optional<T>>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return true;
uint8_t fbe_has_value = *((const uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_has_value == 0)
return true;
uint32_t fbe_optional_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 1));
if (fbe_optional_offset == 0)
return false;
_buffer.shift(fbe_optional_offset);
bool fbe_result = value.verify();
_buffer.unshift(fbe_optional_offset);
return fbe_result;
}
template <typename T>
inline size_t FieldModel<std::optional<T>>::get_begin() const noexcept
{
if (!has_value())
return 0;
uint32_t fbe_optional_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 1));
assert((fbe_optional_offset > 0) && "Model is broken!");
if (fbe_optional_offset == 0)
return 0;
_buffer.shift(fbe_optional_offset);
return fbe_optional_offset;
}
template <typename T>
inline void FieldModel<std::optional<T>>::get_end(size_t fbe_begin) const noexcept
{
_buffer.unshift(fbe_begin);
}
template <typename T>
inline void FieldModel<std::optional<T>>::get(std::optional<T>& opt, const std::optional<T>& defaults) const noexcept
{
opt = defaults;
size_t fbe_begin = get_begin();
if (fbe_begin == 0)
return;
T temp = T();
value.get(temp);
opt.emplace(temp);
get_end(fbe_begin);
}
template <typename T>
inline size_t FieldModel<std::optional<T>>::set_begin(bool has_value)
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint8_t fbe_has_value = has_value ? 1 : 0;
*((uint8_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_has_value;
if (fbe_has_value == 0)
return 0;
uint32_t fbe_optional_size = (uint32_t)value.fbe_size();
uint32_t fbe_optional_offset = (uint32_t)(_buffer.allocate(fbe_optional_size) - _buffer.offset());
assert(((fbe_optional_offset > 0) && ((_buffer.offset() + fbe_optional_offset + fbe_optional_size) <= _buffer.size())) && "Model is broken!");
if ((fbe_optional_offset == 0) || ((_buffer.offset() + fbe_optional_offset + fbe_optional_size) > _buffer.size()))
return 0;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset() + 1)) = fbe_optional_offset;
_buffer.shift(fbe_optional_offset);
return fbe_optional_offset;
}
template <typename T>
inline void FieldModel<std::optional<T>>::set_end(size_t fbe_begin)
{
_buffer.unshift(fbe_begin);
}
template <typename T>
inline void FieldModel<std::optional<T>>::set(const std::optional<T>& opt)
{
size_t fbe_begin = set_begin(opt.has_value());
if (fbe_begin == 0)
return;
if (opt)
value.set(opt.value());
set_end(fbe_begin);
}
template <typename T, size_t N>
inline const uint8_t* FieldModelArray<T, N>::data() const noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
return _buffer.data() + _buffer.offset() + fbe_offset();
}
template <typename T, size_t N>
inline uint8_t* FieldModelArray<T, N>::data() noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
return _buffer.data() + _buffer.offset() + fbe_offset();
}
template <typename T, size_t N>
inline FieldModel<T> FieldModelArray<T, N>::operator[](size_t index) const noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
assert((index < N) && "Index is out of bounds!");
FieldModel<T> fbe_model(_buffer, fbe_offset());
fbe_model.fbe_shift(index * fbe_model.fbe_size());
return fbe_model;
}
template <typename T, size_t N>
inline bool FieldModelArray<T, N>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return false;
FieldModel<T> fbe_model(_buffer, fbe_offset());
for (size_t i = N; i-- > 0;)
{
if (!fbe_model.verify())
return false;
fbe_model.fbe_shift(fbe_model.fbe_size());
}
return true;
}
template <typename T, size_t N>
template <size_t S>
inline void FieldModelArray<T, N>::get(T (&values)[S]) const noexcept
{
auto fbe_model = (*this)[0];
for (size_t i = 0; (i < S) && (i < N); ++i)
{
fbe_model.get(values[i]);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T, size_t N>
template <size_t S>
inline void FieldModelArray<T, N>::get(std::array<T, S>& values) const noexcept
{
auto fbe_model = (*this)[0];
for (size_t i = 0; (i < S) && (i < N); ++i)
{
fbe_model.get(values[i]);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T, size_t N>
inline void FieldModelArray<T, N>::get(std::vector<T>& values) const noexcept
{
values.clear();
values.reserve(N);
auto fbe_model = (*this)[0];
for (size_t i = N; i-- > 0;)
{
T value = T();
fbe_model.get(value);
values.emplace_back(value);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T, size_t N>
template <size_t S>
inline void FieldModelArray<T, N>::set(const T (&values)[S]) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
auto fbe_model = (*this)[0];
for (size_t i = 0; (i < S) && (i < N); ++i)
{
fbe_model.set(values[i]);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T, size_t N>
template <size_t S>
inline void FieldModelArray<T, N>::set(const std::array<T, S>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
auto fbe_model = (*this)[0];
for (size_t i = 0; (i < S) && (i < N); ++i)
{
fbe_model.set(values[i]);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T, size_t N>
inline void FieldModelArray<T, N>::set(const std::vector<T>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
auto fbe_model = (*this)[0];
for (size_t i = 0; (i < values.size()) && (i < N); ++i)
{
fbe_model.set(values[i]);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T>
inline size_t FieldModelVector<T>::fbe_extra() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_vector_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_vector_offset == 0) || ((_buffer.offset() + fbe_vector_offset + 4) > _buffer.size()))
return 0;
uint32_t fbe_vector_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_vector_offset));
size_t fbe_result = 4;
FieldModel<T> fbe_model(_buffer, fbe_vector_offset + 4);
for (size_t i = fbe_vector_size; i-- > 0;)
{
fbe_result += fbe_model.fbe_size() + fbe_model.fbe_extra();
fbe_model.fbe_shift(fbe_model.fbe_size());
}
return fbe_result;
}
template <typename T>
inline size_t FieldModelVector<T>::offset() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_vector_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
return fbe_vector_offset;
}
template <typename T>
inline size_t FieldModelVector<T>::size() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_vector_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_vector_offset == 0) || ((_buffer.offset() + fbe_vector_offset + 4) > _buffer.size()))
return 0;
uint32_t fbe_vector_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_vector_offset));
return fbe_vector_size;
}
template <typename T>
inline FieldModel<T> FieldModelVector<T>::operator[](size_t index) const noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
uint32_t fbe_vector_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((fbe_vector_offset > 0) && ((_buffer.offset() + fbe_vector_offset + 4) <= _buffer.size())) && "Model is broken!");
[[maybe_unused]] uint32_t fbe_vector_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_vector_offset));
assert((index < fbe_vector_size) && "Index is out of bounds!");
FieldModel<T> fbe_model(_buffer, fbe_vector_offset + 4);
fbe_model.fbe_shift(index * fbe_model.fbe_size());
return fbe_model;
}
template <typename T>
inline FieldModel<T> FieldModelVector<T>::resize(size_t size)
{
FieldModel<T> fbe_model(_buffer, fbe_offset());
uint32_t fbe_vector_size = (uint32_t)(size * fbe_model.fbe_size());
uint32_t fbe_vector_offset = (uint32_t)(_buffer.allocate(4 + fbe_vector_size) - _buffer.offset());
assert(((fbe_vector_offset > 0) && ((_buffer.offset() + fbe_vector_offset + 4) <= _buffer.size())) && "Model is broken!");
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_vector_offset;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_vector_offset)) = (uint32_t)size;
memset((char*)(_buffer.data() + _buffer.offset() + fbe_vector_offset + 4), 0, fbe_vector_size);
return FieldModel<T>(_buffer, fbe_vector_offset + 4);
}
template <typename T>
inline bool FieldModelVector<T>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return true;
uint32_t fbe_vector_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_vector_offset == 0)
return true;
if ((_buffer.offset() + fbe_vector_offset + 4) > _buffer.size())
return false;
uint32_t fbe_vector_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_vector_offset));
FieldModel<T> fbe_model(_buffer, fbe_vector_offset + 4);
for (size_t i = fbe_vector_size; i-- > 0;)
{
if (!fbe_model.verify())
return false;
fbe_model.fbe_shift(fbe_model.fbe_size());
}
return true;
}
template <typename T>
inline void FieldModelVector<T>::get(std::vector<T>& values) const noexcept
{
values.clear();
size_t fbe_vector_size = size();
if (fbe_vector_size == 0)
return;
values.reserve(fbe_vector_size);
auto fbe_model = (*this)[0];
for (size_t i = fbe_vector_size; i-- > 0;)
{
T value = T();
fbe_model.get(value);
values.emplace_back(value);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T>
inline void FieldModelVector<T>::get(std::list<T>& values) const noexcept
{
values.clear();
size_t fbe_vector_size = size();
if (fbe_vector_size == 0)
return;
auto fbe_model = (*this)[0];
for (size_t i = fbe_vector_size; i-- > 0;)
{
T value = T();
fbe_model.get(value);
values.emplace_back(value);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T>
inline void FieldModelVector<T>::get(std::set<T>& values) const noexcept
{
values.clear();
size_t fbe_vector_size = size();
if (fbe_vector_size == 0)
return;
auto fbe_model = (*this)[0];
for (size_t i = fbe_vector_size; i-- > 0;)
{
T value = T();
fbe_model.get(value);
values.emplace(value);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T>
inline void FieldModelVector<T>::set(const std::vector<T>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
auto fbe_model = resize(values.size());
for (const auto& value : values)
{
fbe_model.set(value);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T>
inline void FieldModelVector<T>::set(const std::list<T>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
auto fbe_model = resize(values.size());
for (const auto& value : values)
{
fbe_model.set(value);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename T>
inline void FieldModelVector<T>::set(const std::set<T>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
auto fbe_model = resize(values.size());
for (const auto& value : values)
{
fbe_model.set(value);
fbe_model.fbe_shift(fbe_model.fbe_size());
}
}
template <typename TKey, typename TValue>
inline size_t FieldModelMap<TKey, TValue>::fbe_extra() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_map_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_map_offset == 0) || ((_buffer.offset() + fbe_map_offset + 4) > _buffer.size()))
return 0;
uint32_t fbe_map_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_map_offset));
size_t fbe_result = 4;
FieldModel<TKey> fbe_model_key(_buffer, fbe_map_offset + 4);
FieldModel<TValue> fbe_model_value(_buffer, fbe_map_offset + 4 + fbe_model_key.fbe_size());
for (size_t i = fbe_map_size; i-- > 0;)
{
fbe_result += fbe_model_key.fbe_size() + fbe_model_key.fbe_extra();
fbe_model_key.fbe_shift(fbe_model_key.fbe_size() + fbe_model_value.fbe_size());
fbe_result += fbe_model_value.fbe_size() + fbe_model_value.fbe_extra();
fbe_model_value.fbe_shift(fbe_model_key.fbe_size() + fbe_model_value.fbe_size());
}
return fbe_result;
}
template <typename TKey, typename TValue>
inline size_t FieldModelMap<TKey, TValue>::offset() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_map_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
return fbe_map_offset;
}
template <typename TKey, typename TValue>
inline size_t FieldModelMap<TKey, TValue>::size() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return 0;
uint32_t fbe_map_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if ((fbe_map_offset == 0) || ((_buffer.offset() + fbe_map_offset + 4) > _buffer.size()))
return 0;
uint32_t fbe_map_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_map_offset));
return fbe_map_size;
}
template <typename TKey, typename TValue>
inline std::pair<FieldModel<TKey>, FieldModel<TValue>> FieldModelMap<TKey, TValue>::operator[](size_t index) const noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
uint32_t fbe_map_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
assert(((fbe_map_offset > 0) && ((_buffer.offset() + fbe_map_offset + 4) <= _buffer.size())) && "Model is broken!");
[[maybe_unused]] uint32_t fbe_map_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_map_offset));
assert((index < fbe_map_size) && "Index is out of bounds!");
FieldModel<TKey> fbe_model_key(_buffer, fbe_map_offset + 4);
FieldModel<TValue> fbe_model_value(_buffer, fbe_map_offset + 4 + fbe_model_key.fbe_size());
fbe_model_key.fbe_shift(index * (fbe_model_key.fbe_size() + fbe_model_value.fbe_size()));
fbe_model_value.fbe_shift(index * (fbe_model_key.fbe_size() + fbe_model_value.fbe_size()));
return std::make_pair(fbe_model_key, fbe_model_value);
}
template <typename TKey, typename TValue>
inline std::pair<FieldModel<TKey>, FieldModel<TValue>> FieldModelMap<TKey, TValue>::resize(size_t size)
{
FieldModel<TKey> fbe_model_key(_buffer, fbe_offset());
FieldModel<TValue> fbe_model_value(_buffer, fbe_offset() + fbe_model_key.fbe_size());
uint32_t fbe_map_size = (uint32_t)(size * (fbe_model_key.fbe_size() + fbe_model_value.fbe_size()));
uint32_t fbe_map_offset = (uint32_t)(_buffer.allocate(4 + fbe_map_size) - _buffer.offset());
assert(((fbe_map_offset > 0) && ((_buffer.offset() + fbe_map_offset + 4 + fbe_map_size) <= _buffer.size())) && "Model is broken!");
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset())) = fbe_map_offset;
*((uint32_t*)(_buffer.data() + _buffer.offset() + fbe_map_offset)) = (uint32_t)size;
memset((char*)(_buffer.data() + _buffer.offset() + fbe_map_offset + 4), 0, fbe_map_size);
return std::make_pair(FieldModel<TKey>(_buffer, fbe_map_offset + 4), FieldModel<TValue>(_buffer, fbe_map_offset + 4 + fbe_model_key.fbe_size()));
}
template <typename TKey, typename TValue>
inline bool FieldModelMap<TKey, TValue>::verify() const noexcept
{
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return true;
uint32_t fbe_map_offset = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_offset()));
if (fbe_map_offset == 0)
return true;
if ((_buffer.offset() + fbe_map_offset + 4) > _buffer.size())
return false;
uint32_t fbe_map_size = *((const uint32_t*)(_buffer.data() + _buffer.offset() + fbe_map_offset));
FieldModel<TKey> fbe_model_key(_buffer, fbe_map_offset + 4);
FieldModel<TValue> fbe_model_value(_buffer, fbe_map_offset + 4 + fbe_model_key.fbe_size());
for (size_t i = fbe_map_size; i-- > 0;)
{
if (!fbe_model_key.verify())
return false;
fbe_model_key.fbe_shift(fbe_model_key.fbe_size() + fbe_model_value.fbe_size());
if (!fbe_model_value.verify())
return false;
fbe_model_value.fbe_shift(fbe_model_key.fbe_size() + fbe_model_value.fbe_size());
}
return true;
}
template <typename TKey, typename TValue>
inline void FieldModelMap<TKey, TValue>::get(std::map<TKey, TValue>& values) const noexcept
{
values.clear();
size_t fbe_map_size = size();
if (fbe_map_size == 0)
return;
auto fbe_model = (*this)[0];
for (size_t i = fbe_map_size; i-- > 0;)
{
TKey key;
TValue value;
fbe_model.first.get(key);
fbe_model.second.get(value);
values.emplace(key, value);
fbe_model.first.fbe_shift(fbe_model.first.fbe_size() + fbe_model.second.fbe_size());
fbe_model.second.fbe_shift(fbe_model.first.fbe_size() + fbe_model.second.fbe_size());
}
}
template <typename TKey, typename TValue>
inline void FieldModelMap<TKey, TValue>::get(std::unordered_map<TKey, TValue>& values) const noexcept
{
values.clear();
size_t fbe_map_size = size();
if (fbe_map_size == 0)
return;
auto fbe_model = (*this)[0];
for (size_t i = fbe_map_size; i-- > 0;)
{
TKey key;
TValue value;
fbe_model.first.get(key);
fbe_model.second.get(value);
values.emplace(key, value);
fbe_model.first.fbe_shift(fbe_model.first.fbe_size() + fbe_model.second.fbe_size());
fbe_model.second.fbe_shift(fbe_model.first.fbe_size() + fbe_model.second.fbe_size());
}
}
template <typename TKey, typename TValue>
inline void FieldModelMap<TKey, TValue>::set(const std::map<TKey, TValue>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
auto fbe_model = resize(values.size());
for (const auto& value : values)
{
fbe_model.first.set(value.first);
fbe_model.first.fbe_shift(fbe_model.first.fbe_size() + fbe_model.second.fbe_size());
fbe_model.second.set(value.second);
fbe_model.second.fbe_shift(fbe_model.first.fbe_size() + fbe_model.second.fbe_size());
}
}
template <typename TKey, typename TValue>
inline void FieldModelMap<TKey, TValue>::set(const std::unordered_map<TKey, TValue>& values) noexcept
{
assert(((_buffer.offset() + fbe_offset() + fbe_size()) <= _buffer.size()) && "Model is broken!");
if ((_buffer.offset() + fbe_offset() + fbe_size()) > _buffer.size())
return;
auto fbe_model = resize(values.size());
for (const auto& value : values)
{
fbe_model.first.set(value.first);
fbe_model.first.fbe_shift(fbe_model.first.fbe_size() + fbe_model.second.fbe_size());
fbe_model.second.set(value.second);
fbe_model.second.fbe_shift(fbe_model.first.fbe_size() + fbe_model.second.fbe_size());
}
}
} // namespace FBE

View file

@ -1,61 +1,65 @@
#pragma once
#include <iostream> //temporary
#include <fstream>
#include <iostream>
#include <shared_mutex>
#include "error_responses.hpp"
#include "parallel-hashmap/parallel_hashmap/phmap.h"
#include <parallel-hashmap/parallel_hashmap/phmap.h>
#include "bank_resp.h"
#include "user.h"
#if (CONSERVATIVE_DISK_SAVE && MAX_LOG_SIZE < 0) && !MULTI_THREADED
#include "change_flag.h"
#endif
class Bank
{
private:
#if MULTI_THREADED
phmap::parallel_flat_hash_map<
std::string, User,
phmap::priv::hash_default_hash<std::string>,
xxHashStringGen,
phmap::priv::hash_default_eq<std::string>,
phmap::priv::Allocator<phmap::priv::Pair<const std::string, User>>,
4UL,
std::mutex>
users;
#else
phmap::parallel_flat_hash_map<std::string, User, xxHashStringGen> users;
#endif
/**
* @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;
private:
#if CONSERVATIVE_DISK_SAVE
#if MULTI_THREADED
ChangeFlag<false> save_flag;
#else
bool save_flag = false;
#endif
#endif
/**
* @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;
std::shared_mutex save_lock;
public:
std::string admin_pass;
std::string admin_account;
int_fast8_t AddUser(const std::string &name, const std::string &init_pass) noexcept;
int_fast8_t AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept;
size_t NumOfUsers() const noexcept;
size_t NumOfLogs() const noexcept;
size_t SumBal() const noexcept;
int_fast8_t DelUser(const std::string &name, const std::string &attempt) noexcept;
int_fast8_t AdminDelUser(const std::string &name, const std::string &attempt) noexcept;
BankResponse GetBal(const std::string &name) const noexcept;
#if MAX_LOG_SIZE > 0
BankResponse GetLogs(const std::string &name) noexcept;
#endif
BankResponse SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount) noexcept;
bool VerifyPassword(const std::string &name, const std::string_view &attempt) const noexcept;
int_fast8_t SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount, const std::string &attempt) noexcept;
void ChangePassword(const std::string &name, const std::string &new_pass) noexcept;
BankResponse SetBal(const std::string &name, uint32_t amount) noexcept;
BankResponse ImpactBal(const std::string &name, int64_t amount) noexcept;
bool Contains(const std::string &name) const noexcept;
bool AdminVerifyAccount(const std::string &name) noexcept;
int_fast8_t Contains(const std::string &name) const noexcept;
int_fast8_t AdminVerifyPass(const std::string &attempt) noexcept;
BankResponse AddUser(const std::string &name, uint32_t init_bal, const std::string &init_pass) noexcept;
BankResponse DelUser(const std::string &name) noexcept;
void DelSelf(const std::string &name) noexcept;
int_fast8_t SetBal(const std::string &name, const std::string &attempt, uint32_t amount) noexcept;
int_fast64_t GetBal(const std::string &name) const noexcept;
int_fast8_t VerifyPassword(const std::string &name, const std::string &attempt) const noexcept;
int_fast8_t ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass) noexcept;
Json::Value GetLogs(const std::string &name, const std::string &attempt) noexcept;
void Save();
//NOT THREAD SAFE
const char *Save();
void Load();
};
//TODO make branchless
};

78
include/bank_api.h Normal file
View file

@ -0,0 +1,78 @@
#pragma once
#include <drogon/HttpController.h>
#include "str_intrusion.h"
#include "json_filter.h"
#include "user_filter.h"
using namespace drogon;
#define req_args const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback
class api : public HttpController<api, false>
{
Bank &bank;
public:
api(Bank &b) noexcept;
void JsonCpp(req_args) const;
void Json(req_args) const;
#if API_VERSION >= 1
void GetBal(req_args, const std::string &name) const;
void GetLogs(req_args);
void SendFunds(req_args) const;
void VerifyPassword(req_args) const;
void ChangePassword(req_args) const;
void AdminChangePassword(req_args) const;
void SetBal(req_args) const;
void ImpactBal(req_args) const;
void Help(req_args) const;
void Close(req_args) const;
void Contains(req_args, const std::string &name) const;
void AdminVerifyAccount(req_args) const;
void ApiProperties(req_args) const;
void AddUser(req_args) const;
void AdminAddUser(req_args) const;
void DelSelf(req_args) const;
void AdminDelUser(req_args) const;
#endif
METHOD_LIST_BEGIN
#if API_VERSION >= 1
//Usage
METHOD_ADD(api::GetBal, "/v1/user/balance?name={name}", Get, Options, "JsonFilter<false>");
#if MAX_LOG_SIZE > 0
METHOD_ADD(api::GetLogs, "/v1/user/log", Get, Options, "JsonFilter<false>", "UserFilter<true, false>");
#else
METHOD_ADD(api::GetLogs, "/v1/user/log", Get, Options, "JsonFilter<false>");
#endif
METHOD_ADD(api::SendFunds, "/v1/user/transfer", Post, Options, "JsonFilter<true>", "UserFilter<true, false>"); //expects ["name"](string) and ["amount"](32 bits)
METHOD_ADD(api::VerifyPassword, "/v1/user/verify_password", Post, Options, "UserFilter<false, false>", "JsonFilter<false>");
//Meta Usage
METHOD_ADD(api::ChangePassword, "/v1/user/change_password", Patch, Options, "JsonFilter<true>", "UserFilter<true, false>"); //expects ["pass"](string)
METHOD_ADD(api::AdminChangePassword, "/v1/admin/user/change_password", Patch, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) and ["pass"](string)
METHOD_ADD(api::SetBal, "/v1/admin/set_balance", Patch, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) and ["amount"](32 bits)
METHOD_ADD(api::ImpactBal, "/v1/admin/impact_balance", Post, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) and ["amount"](32 bits)
//System Usage
METHOD_ADD(api::Help, "/v1/help", Get, Options);
METHOD_ADD(api::Close, "/v1/admin/shutdown", Post, Options, "UserFilter<false, true>", "JsonFilter<false>");
METHOD_ADD(api::Contains, "/v1/user/exists?name={name}", Get, Options, "JsonFilter<false>");
METHOD_ADD(api::AdminVerifyAccount, "/v1/admin/verify_account", Post, Options, "UserFilter<false, true>", "JsonFilter<false>");
//User Managment
METHOD_ADD(api::AddUser, "/v1/user/register", Post, Options); //expects ["name"](string) ["pass"](string)
METHOD_ADD(api::AdminAddUser, "/v1/admin/user/register", Post, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) ["amount"](32 bits) ["pass"](string)
METHOD_ADD(api::DelSelf, "/v1/user/delete", Delete, Options, "UserFilter<true, false>", "JsonFilter<false>");
METHOD_ADD(api::AdminDelUser, "/v1/admin/user/delete", Delete, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string)
#endif
METHOD_ADD(api::ApiProperties, "/properties", Get, Options);
METHOD_LIST_END
};

View file

@ -1,57 +0,0 @@
#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 Ping(req_args) const;
void Close(req_args) const;
void AddUser(req_args, const 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::Ping, "/ping", 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
};

12
include/bank_resp.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <optional>
#include <drogon/HttpTypes.h>
#include <drogon/HttpResponse.h>
#include <../src/HttpResponseImpl.h>
#include <../src/HttpAppFrameworkImpl.h>
using BankResponse = std::pair<drogon::HttpStatusCode, std::optional<std::string>>;
template <>
drogon::HttpResponsePtr drogon::toResponse(BankResponse &&data);

17
include/change_flag.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include <atomic>
template <bool init>
class ChangeFlag
{
private:
std::atomic<bool> change_flag = init; //if true changes have been made
public:
ChangeFlag() noexcept;
ChangeFlag(ChangeFlag &&) noexcept;
void SetChangesOn() noexcept;
void SetChangesOff() noexcept;
bool GetChangeState() const noexcept;
};

View file

@ -1,14 +0,0 @@
#pragma once
// Setting both values to 0 does not compile logging
constexpr unsigned max_log_size = 100;
constexpr unsigned pre_log_size = 10;
constexpr unsigned max_name_size = 50;
constexpr const char *users_location = "../users.json";
constexpr const char *config_location = "../config.json";
//returns money to an account on deletion
constexpr bool return_on_del = false;
constexpr const char *return_account = "";

View file

@ -1,10 +0,0 @@
#pragma once
enum ErrorResponse
{
UserNotFound = -1,
WrongPassword = -2,
InvalidRequest = -3,
NameTooLong = -4,
UserAlreadyExists = -5,
InsufficientFunds = -6,
};

16
include/json_filter.h Normal file
View file

@ -0,0 +1,16 @@
#pragma once
#include <drogon/HttpFilter.h>
#include "bank_resp.h"
using namespace drogon;
template <bool check_content_type>
class JsonFilter : public HttpFilter<JsonFilter<check_content_type>, false>
{
public:
JsonFilter();
virtual void doFilter(const HttpRequestPtr &,
FilterCallback &&,
FilterChainCallback &&) override;
};

View file

@ -1,13 +1,23 @@
#pragma once
#include <json/json.h>
#include <array>
#include <algorithm>
#include "consts.hpp"
#include <json/json.h> // to be removed later
#include <deque>
#include "ccash_config.hpp"
#include "change_flag.h"
#include "transaction.h"
#include "simdjson.h"
using namespace simdjson;
struct Log
{
std::vector<Transaction> data;
void AddTrans(Transaction &&t);
Json::Value Serialize() const;
private:
ChangeFlag<true> log_flag;
std::string log_snapshot = "null";
public:
std::deque<Transaction> data;
std::string GetLogs() noexcept;
void AddTrans(const std::string &from, const std::string &to, uint32_t amount, time_t time) noexcept;
Json::Value Serialize() const; // to be removed later
};

23863
include/simdjson.h Normal file

File diff suppressed because it is too large Load diff

10
include/str_intrusion.h Normal file
View file

@ -0,0 +1,10 @@
#pragma once
#include <string>
struct StrFromSV_Wrapper
{
std::string str;
StrFromSV_Wrapper() noexcept;
StrFromSV_Wrapper(std::string_view sv) noexcept;
~StrFromSV_Wrapper() noexcept;
};

View file

@ -1,15 +1,14 @@
#pragma once
#include <string>
#include <chrono>
#include <cstdint>
#include <ctime>
struct Transaction
{
std::string from = "", to = "";
uint32_t amount = 0;
uint64_t time = 0;
time_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);
Transaction() noexcept;
Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount, time_t time) noexcept;
};

View file

@ -1,20 +1,24 @@
#pragma once
#include <json/json.h>
#include <string>
#include <algorithm>
#include <xxhash.h>
#include <json/json.h> //to be removed later
#include "xxhash_str.h"
#include "bank_dom_final_models.h"
#include "log.h"
struct User
{
uint32_t balance = 0;
uint64_t password;
uint32_t balance;
XXH64_hash_t password;
#if MAX_LOG_SIZE > 0
Log log;
#endif
User(const std::string &init_pass);
User(uint32_t init_bal, const 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);
User(uint32_t init_bal, const std::string &init_pass) noexcept;
User(uint32_t init_bal, XXH64_hash_t init_pass) noexcept;
#if MAX_LOG_SIZE > 0
User(uint32_t init_bal, XXH64_hash_t init_pass, const Json::Value &log_j) noexcept;
User(const bank_dom::User &u) noexcept;
bank_dom::User Encode() const noexcept;
#endif
Json::Value Serialize() const;
Json::Value Serialize() const; //to be removed later
};

21
include/user_filter.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include <drogon/HttpFilter.h>
#include <libbase64.h>
#include "str_intrusion.h"
#include "bank.h"
using namespace drogon;
template <bool set_body_flag, bool require_admin>
class UserFilter : public HttpFilter<UserFilter<set_body_flag, require_admin>, false>
{
private:
Bank &bank;
public:
UserFilter(Bank &b);
virtual void doFilter(const HttpRequestPtr &,
FilterCallback &&,
FilterChainCallback &&) override;
};

View file

@ -266,7 +266,7 @@ extern "C" {
***************************************/
#define XXH_VERSION_MAJOR 0
#define XXH_VERSION_MINOR 8
#define XXH_VERSION_RELEASE 0
#define XXH_VERSION_RELEASE 1
#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)
/*!
@ -275,7 +275,7 @@ extern "C" {
* This is only useful when xxHash is compiled as a shared library, as it is
* independent of the version defined in the header.
*
* @return `XXH_VERSION_NUMBER` as of when the function was compiled.
* @return `XXH_VERSION_NUMBER` as of when the libray was compiled.
*/
XXH_PUBLIC_API unsigned XXH_versionNumber (void);
@ -3341,7 +3341,7 @@ XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_
{
XXH_ASSERT(input != NULL);
XXH_ASSERT(secret != NULL);
XXH_ASSERT(8 <= len && len <= 16);
XXH_ASSERT(9 <= len && len <= 16);
{ xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed;
xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed;
xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1;

9
include/xxhash_str.h Normal file
View file

@ -0,0 +1,9 @@
#pragma once
#include <string>
#include "xxhash.h"
struct xxHashStringGen
{
XXH64_hash_t operator()(const std::string &str) const noexcept;
XXH64_hash_t operator()(const std::string_view &str) const noexcept;
};

150
main.cpp
View file

@ -3,13 +3,17 @@
#include <thread>
#include <sys/types.h>
#include <unistd.h>
#include "bank_f.h"
#include "bank_api.h"
//sig handling headers
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
//threads of cpu
#include <sys/sysinfo.h>
using namespace std::chrono;
using namespace drogon;
@ -17,63 +21,111 @@ static Bank bank;
void SaveSig(int s)
{
bank.Save();
std::cout << "\nSaving on close...\n";
std::cout << "\nSaving on close...\n"
<< bank.Save();
exit(1);
}
int main(int argc, char **argv)
{
static_assert(bool(max_log_size) == bool(pre_log_size), "You must either utilize both or neither logging variables.\n");
static_assert(max_log_size >= pre_log_size, "The maximum log size must be larger than or equal to the amount preallocated.\n");
static_assert(!max_log_size || !(max_log_size % pre_log_size), "The maximum log size must be divisible by the preallocation size.\n");
if (argc != 4)
{
std::cerr << "Usage: sudo ./bank <admin password> <saving frequency in minutes> <threads>\n";
return 0;
}
if (geteuid() != 0)
{
std::cerr << "ERROR: CCash MUST be ran as root\n";
return 0;
}
//Loading users from users.json
bank.Load();
//Sig handling
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = SaveSig;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
//Admin Password
bank.admin_pass = argv[1];
//Auto Saving
const unsigned long saving_freq = std::stoul(std::string(argv[2]));
if (saving_freq) //if saving frequency is 0 then auto saving is turned off
{
std::thread([saving_freq]() {
while (1)
if (argc == 1)
{
std::ofstream users_save(users_location, std::ios::out | std::ios::binary);
if (users_save.is_open())
{
std::this_thread::sleep_for(std::chrono::minutes(saving_freq));
bank.Save();
std::cout << "Saving " << duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count() << '\n';
uint8_t temp[16]{16, 0, 0, 0, 4};
users_save.write((char *)temp, 16);
users_save.close();
std::cout << "User save file generated\nUsage: sudo ./bank <admin account> <saving frequency in minutes>\n";
}
}).detach();
}
else
{
std::cerr << "File cannot be created\n";
}
return 0;
}
if (argc != 3)
{
std::cerr << "Usage: sudo ./bank <admin account> <saving frequency in minutes>\n";
return 0;
}
if (geteuid() != 0)
{
std::cerr << "ERROR: CCash MUST be ran as root\n";
return 0;
}
std::cout
<< "\nAPI Version : " << API_VERSION
<< "\n\nAVX : " << (__builtin_cpu_supports("avx") ? "enabled" : "disabled")
<< "\nAVX 2 : " << (__builtin_cpu_supports("avx2") ? "enabled" : "disabled")
<< "\nSSE 2 : " << (__builtin_cpu_supports("sse2") ? "enabled" : "disabled")
<< "\nSSE 3 : " << (__builtin_cpu_supports("sse3") ? "enabled" : "disabled")
<< "\nSSE 4.1 : " << (__builtin_cpu_supports("sse4.1") ? "enabled" : "disabled")
<< "\nSSE 4.2 : " << (__builtin_cpu_supports("sse4.2") ? "enabled" : "disabled")
#if MULTI_THREADED
<< "\n\nThreads : " << get_nprocs() + 1
<< "\nMulti threading : enabled";
#else
<< "\n\nThreads : " << 2
<< "\nMulti threading : disabled";
#endif
auto API = std::make_shared<BankF>(&bank);
app().registerPostHandlingAdvice(
[](const drogon::HttpRequestPtr &req, const drogon::HttpResponsePtr &resp) {
resp->addHeader("Access-Control-Allow-Origin", "*");
});
app().loadConfigFile(config_location).registerController(API).setThreadNum(std::stoul(std::string(argv[3]))).run();
//Loading users from users.json
bank.Load();
size_t num_of_logs = bank.NumOfLogs();
size_t num_of_users = bank.NumOfUsers();
std::cout << "\n\nLoaded " << num_of_users << " Users ~" << (float)(sizeof(User) * num_of_users) / 1048576 << "Mb"
<< "\nLoaded " << num_of_logs << " Logs ~" << (float)(num_of_logs * (90 + 80 + (max_name_size * 2))) / 1048576 << "Mb" //90:string representation(heap), sizeof(Transaction), max_name_size*2:filled to&from(heap)
<< "\nLoaded " << bank.SumBal() << " CSH"
<< std::endl; //flushing before EventLoop
//Sig handling
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = SaveSig;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
//Admin account
bank.admin_account = argv[1];
//Auto Saving
const unsigned long saving_freq = std::stoul(std::string(argv[2]));
if (saving_freq) //if saving frequency is 0 then auto saving is turned off
{
std::thread([saving_freq]() {
while (1)
{
std::this_thread::sleep_for(std::chrono::minutes(saving_freq));
std::cout << "Saving " << std::time(0) << "...\n"
<< bank.Save();
}
})
.detach();
}
} //destroying setup variables
static auto API = std::make_shared<api>(bank);
static auto user_filter_default = std::make_shared<UserFilter<true, false>>(bank);
static auto user_filter_sparse = std::make_shared<UserFilter<false, false>>(bank);
static auto admin_filter = std::make_shared<UserFilter<false, true>>(bank);
static auto json_resp_and_req_filter = std::make_shared<JsonFilter<true>>();
static auto json_resp_filter = std::make_shared<JsonFilter<false>>();
app()
.loadConfigFile(config_location)
.registerFilter(user_filter_default)
.registerFilter(user_filter_sparse)
.registerFilter(admin_filter)
.registerFilter(json_resp_and_req_filter)
.registerFilter(json_resp_filter)
.registerController(API)
#if MULTI_THREADED
.setThreadNum(get_nprocs())
#endif
.run();
return 0;
}

View file

@ -1,283 +1,377 @@
#include "bank.h"
int_fast8_t Bank::AddUser(const std::string &name, const std::string &init_pass) noexcept
using namespace drogon;
__attribute__((always_inline)) inline bool ValidUsername(const std::string &name) noexcept
{
if (name.size() > max_name_size)
if (name.size() < min_name_size || name.size() > max_name_size)
{
return ErrorResponse::NameTooLong;
return false;
}
for (char c : name)
{
if (c == ' ')
if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'))
{
return ErrorResponse::InvalidRequest;
return false;
}
}
{
std::shared_lock<std::shared_mutex> lock{size_l};
return (users.try_emplace_l(
name, [](User &) {}, init_pass))
? true
: ErrorResponse::UserAlreadyExists;
}
}
int_fast8_t Bank::AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass) noexcept
{
if (name.size() > max_name_size)
{
return ErrorResponse::NameTooLong;
}
if (admin_pass != attempt)
{
return ErrorResponse::WrongPassword;
}
std::shared_lock<std::shared_mutex> lock{size_l};
return (users.try_emplace_l(
name, [](User &) {}, init_bal, std::move(init_pass)))
? true
: ErrorResponse::UserAlreadyExists;
}
int_fast8_t Bank::DelUser(const std::string &name, const std::string &attempt) noexcept
{
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 (state) ? true : ErrorResponse::WrongPassword;
}
else
{
return ErrorResponse::UserNotFound;
}
}
int_fast8_t Bank::AdminDelUser(const std::string &name, const std::string &attempt) noexcept
{
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 (state) ? true : ErrorResponse::WrongPassword;
}
else
{
return ErrorResponse::UserNotFound;
}
return true;
}
int_fast8_t Bank::SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount, const std::string &attempt) noexcept
{
//cant send money to self, from self or amount is 0
if (a_name == b_name || !amount)
{
return ErrorResponse::InvalidRequest;
}
//as first modify_if checks a_name and grabs unique lock
if (Contains(b_name) != true)
{
return ErrorResponse::UserNotFound;
}
//NOT THREAD SAFE
size_t Bank::NumOfUsers() const noexcept { return users.size(); }
int_fast8_t state = false;
if constexpr (max_log_size > 0)
//NOT THREAD SAFE
size_t Bank::NumOfLogs() const noexcept
{
#if MAX_LOG_SIZE > 0
size_t res = 0;
for (const auto &u : users)
{
Transaction temp(a_name, b_name, amount);
std::shared_lock<std::shared_mutex> lock{send_funds_l};
users.modify_if(a_name, [&temp, &state, amount, &attempt](User &a) {
//if A can afford it and A's password matches attempt
if (a.balance < amount)
{
state = ErrorResponse::InsufficientFunds;
}
else if (a.password != XXH3_64bits(attempt.data(), attempt.size()))
{
state = ErrorResponse::WrongPassword;
}
else
{
a.balance -= amount;
a.log.AddTrans(Transaction(temp));
state = true;
}
});
if (state > 0)
{
users.modify_if(b_name, [&a_name, &b_name, &temp, amount](User &b) {
b.balance += amount;
b.log.AddTrans(std::move(temp));
});
}
return state;
res += u.second.log.data.size();
}
else
{
std::shared_lock<std::shared_mutex> lock{send_funds_l};
users.modify_if(a_name, [&state, amount, &attempt](User &a) {
//if A can afford it and A's password matches attempt
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;
}
});
if (state > 0)
{
users.modify_if(b_name, [&a_name, &b_name, amount](User &b) {
b.balance += amount;
});
}
return state;
}
}
int_fast8_t Bank::Contains(const std::string &name) const noexcept
{
return (users.contains(name)) ? true : ErrorResponse::UserNotFound;
}
int_fast8_t Bank::AdminVerifyPass(const std::string &attempt) noexcept
{
return (admin_pass == attempt) ? true : ErrorResponse::WrongPassword;
}
int_fast8_t Bank::SetBal(const std::string &name, const std::string &attempt, uint32_t amount) noexcept
{
if (admin_pass != attempt)
{
return ErrorResponse::WrongPassword;
}
return (users.modify_if(name, [amount](User &u) {
u.balance = amount;
}))
? true
: ErrorResponse::UserNotFound;
}
int_fast64_t Bank::GetBal(const std::string &name) const noexcept
{
int_fast64_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res](const User &u) {
res = u.balance;
});
return res;
#else
return 0;
#endif
}
int_fast8_t Bank::VerifyPassword(const std::string &name, const std::string &attempt) const noexcept
//NOT THREAD SAFE
size_t Bank::SumBal() const noexcept
{
int_fast8_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res, &attempt](const User &u) {
res = (u.password == XXH3_64bits(attempt.data(), attempt.size())) ? true : ErrorResponse::WrongPassword;
});
return res;
}
int_fast8_t Bank::ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass) noexcept
{
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
{
res = true;
u.password = XXH3_64bits(new_pass.data(), new_pass.size());
}
});
return res;
}
Json::Value Bank::GetLogs(const std::string &name, const std::string &attempt) noexcept
{
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
{
for (uint32_t i = u.log.data.size(); i > 0; --i)
{
res[i - 1]["to"] = u.log.data[u.log.data.size() - i].to;
res[i - 1]["from"] = u.log.data[u.log.data.size() - i].from;
res[i - 1]["amount"] = (Json::UInt)u.log.data[u.log.data.size() - i].amount;
res[i - 1]["time"] = (Json::UInt64)u.log.data[u.log.data.size() - i].time;
}
}
}))
size_t res = 0;
for (const auto &u : users)
{
return ErrorResponse::UserNotFound;
res += u.second.balance;
}
return res;
}
void Bank::Save()
BankResponse Bank::GetBal(const std::string &name) const noexcept
{
Json::Value temp;
//loading info into json temp
uint32_t res = 0;
if (!ValidUsername(name) || !users.if_contains(name, [&res](const User &u)
{ res = u.balance; }))
{
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())
{
throw std::invalid_argument("Saving Failed\n");
return {k404NotFound, "\"User not found\""};
}
else
{
std::ofstream user_save(users_location);
Json::StreamWriterBuilder builder;
const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
writer->write(temp, &user_save);
user_save.close();
return {k200OK, std::to_string(res)};
}
}
#if MAX_LOG_SIZE > 0
BankResponse Bank::GetLogs(const std::string &name) noexcept
{
BankResponse res;
if (!users.modify_if(name, [&res](User &u)
{ res = {k200OK, u.log.GetLogs()}; }))
{
return {k404NotFound, "\"User not found\""};
}
else
{
return res;
}
}
#endif
BankResponse Bank::SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount) noexcept
{
if (!amount)
{
return {k400BadRequest, "\"Amount cannot be 0\""};
}
if (a_name == b_name)
{
return {k400BadRequest, "\"Names cannot match\""};
}
if (!Contains(b_name))
{
return {k404NotFound, "\"Reciever does not exist\""};
}
BankResponse res;
std::shared_lock<std::shared_mutex> lock{save_lock};
#if MAX_LOG_SIZE > 0
time_t current_time = time(NULL);
#endif
if (!users.modify_if(a_name, [current_time, &a_name, &b_name, &res, amount](User &a)
{
//if A can afford it
if (a.balance < amount)
{
res = {k400BadRequest, "\"Insufficient funds\""};
}
else
{
a.balance -= amount;
#if MAX_LOG_SIZE > 0
a.log.AddTrans(a_name, b_name, amount, current_time);
#endif
res = {k200OK, std::to_string(a.balance)};
}
}))
{
return {k404NotFound, "\"Sender does not exist\""};
}
if (res.first == k200OK)
{
#if MAX_LOG_SIZE > 0
users.modify_if(b_name, [current_time, &a_name, &b_name, amount](User &b)
{
b.balance += amount;
b.log.AddTrans(a_name, b_name, amount, current_time);
});
#else
users.modify_if(b_name, [amount](User &b)
{ b.balance += amount; });
#endif
#if CONSERVATIVE_DISK_SAVE
#if MULTI_THREADED
save_flag.SetChangesOn();
#else
save_flag = true;
#endif
#endif
}
return res;
}
bool Bank::VerifyPassword(const std::string &name, const std::string_view &attempt) const noexcept
{
bool res = false;
users.if_contains(name, [&res, &attempt](const User &u)
{ res = (u.password == xxHashStringGen{}(attempt)); });
return res;
}
void Bank::ChangePassword(const std::string &name, const std::string &new_pass) noexcept
{
#if CONSERVATIVE_DISK_SAVE
#if MULTI_THREADED
save_flag.SetChangesOn();
#else
save_flag = true;
#endif
#endif
users.modify_if(name, [&new_pass](User &u)
{ u.password = xxHashStringGen{}(new_pass); });
}
BankResponse Bank::SetBal(const std::string &name, uint32_t amount) noexcept
{
if (ValidUsername(name) && users.modify_if(name, [amount](User &u)
{ u.balance = amount; }))
{
#if CONSERVATIVE_DISK_SAVE
#if MULTI_THREADED
save_flag.SetChangesOn();
#else
save_flag = true;
#endif
#endif
return {k204NoContent, std::nullopt}; //returns new balance
}
else
{
return {k404NotFound, "\"User not found\""};
}
}
BankResponse Bank::ImpactBal(const std::string &name, int64_t amount) noexcept
{
if (amount == 0)
{
return {k400BadRequest, "\"Amount cannot be 0\""};
}
uint32_t balance;
if (ValidUsername(name) && users.modify_if(name, [&balance, amount](User &u)
{ balance = (u.balance < (amount * -1) ? u.balance = 0 : u.balance += amount); }))
{
#if CONSERVATIVE_DISK_SAVE
#if MULTI_THREADED
save_flag.SetChangesOn();
#else
save_flag = true;
#endif
#endif
return {k200OK, std::to_string(balance)}; //may return new balance
}
else
{
return {k404NotFound, "\"User not found\""};
}
}
bool Bank::Contains(const std::string &name) const noexcept
{
return ValidUsername(name) && users.contains(name);
}
bool Bank::AdminVerifyAccount(const std::string &name) noexcept
{
return (name == admin_account);
}
BankResponse Bank::AddUser(const std::string &name, uint32_t init_bal, const std::string &init_pass) noexcept
{
if (!ValidUsername(name))
{
return {k400BadRequest, "\"Invalid Username\""};
}
std::shared_lock<std::shared_mutex> lock{save_lock};
if (users.try_emplace_l(
name, [](User &) {}, init_bal, init_pass))
{
#if CONSERVATIVE_DISK_SAVE
#if MULTI_THREADED
save_flag.SetChangesOn();
#else
save_flag = true;
#endif
#endif
return {k204NoContent, std::nullopt};
}
else
{
return {k409Conflict, "\"User already exists\""};
}
}
BankResponse Bank::DelUser(const std::string &name) noexcept
{
#if RETURN_ON_DEL
uint32_t bal;
if (users.if_contains(name, [&bal](const User &u)
{ bal = u.balance; }) &&
bal)
{
users.modify_if(return_account, [bal](User & u))
{
u.balance += bal;
}
}
#endif
std::shared_lock<std::shared_mutex> lock{save_lock};
if (ValidUsername(name) && users.erase(name))
{
#if CONSERVATIVE_DISK_SAVE
#if MULTI_THREADED
save_flag.SetChangesOn();
#else
save_flag = true;
#endif
#endif
return {k204NoContent, std::nullopt};
}
else
{
return {k404NotFound, "\"User not found\""};
}
}
//assumes we know name exists, unlike DelUser
void Bank::DelSelf(const std::string &name) noexcept
{
#if RETURN_ON_DEL
uint32_t bal;
if (users.if_contains(name, [&bal](const User &u)
{ bal = u.balance; }) &&
bal)
{
users.modify_if(return_account, [bal](User & u))
{
u.balance += bal;
}
}
#endif
#if CONSERVATIVE_DISK_SAVE
#if MULTI_THREADED
save_flag.SetChangesOn();
#else
save_flag = true;
#endif
#endif
std::shared_lock<std::shared_mutex> lock{save_lock};
users.erase(name);
}
//ONLY EVER BEING CALLED BY SAVE THREAD OR C-INTERUPT
const char *Bank::Save()
{
#if CONSERVATIVE_DISK_SAVE
if (
#if MULTI_THREADED
save_flag.GetChangeState()
#else
save_flag
#endif
)
{
#endif
std::ofstream users_save(users_location, std::ios::out | std::ios::binary);
if (!users_save.is_open())
{
throw std::invalid_argument("Cannot access saving file\n");
}
bank_dom::Global users_copy;
users_copy.users.reserve(users.size());
users_copy.keys.reserve(users.size());
{
std::unique_lock<std::shared_mutex> lock{save_lock};
for (const auto &u : users)
{
//we know it contains this key but we call this func to grab mutex
users.if_contains(u.first, [&users_copy, &u](const User &u_val)
{
users_copy.users.emplace_back(u_val.Encode());
users_copy.keys.emplace_back(u.first);
});
}
}
FBE::bank_dom::GlobalFinalModel writer;
writer.serialize(users_copy);
if (!writer.verify())
{
throw std::invalid_argument("Data is corrupted\n");
}
const FBE::FBEBuffer &write_buffer = writer.buffer();
users_save.write((char *)write_buffer.data(), write_buffer.size());
users_save.close();
if (!users_save.good())
{
throw std::invalid_argument("Error occurred at writing\n");
}
#if CONSERVATIVE_DISK_SAVE
#if MULTI_THREADED
save_flag.SetChangesOff();
#else
save_flag = true;
#endif
return " to disk...\n";
}
else
{
return " no changes...\n";
}
#endif
}
//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_location);
builder["collectComments"] = true;
JSONCPP_STRING errs;
if (!parseFromStream(builder, user_save, &temp, &errs))
std::ifstream users_load(users_location, std::ios::out | std::ios::binary);
if (!users_load.is_open())
{
std::cerr << errs << '\n';
user_save.close();
throw std::invalid_argument("Parsing Failed\n");
throw std::invalid_argument("Cannot find save file, to generate a new one run ./bank\n");
}
else
uint32_t buffer_size;
users_load.read((char *)&buffer_size, 4); //reading first 32 bits for size
FBE::bank_dom::GlobalFinalModel reader; //declaring model
reader.resize(buffer_size); //allocating new memory
users_load.read((char *)reader.buffer().data() + 4, buffer_size - 4); //reading rest of file
memcpy((char *)reader.buffer().data(), &buffer_size, 4); //copying first 32 bits back
if (!reader.verify())
{
user_save.close();
for (const auto &u : temp.getMemberNames())
{
if constexpr (max_log_size > 0)
{
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()));
}
}
throw std::invalid_argument("Data is corrupted\n");
}
bank_dom::Global users_global;
reader.deserialize(users_global);
for (size_t i = 0; i < users_global.users.size(); ++i)
{
users.try_emplace(users_global.keys[i], users_global.users[i]);
}
}

310
src/bank_api.cpp Normal file
View file

@ -0,0 +1,310 @@
#include "bank_api.h"
//all my homies hate jsoncpp
#define CACHE_FOREVER resp->setExpiredTime(0)
#define CORS resp->addHeader("Access-Control-Allow-Origin", "*")
static thread_local ondemand::parser parser;
#define SIMD_JSON_GEN \
static thread_local simdjson::padded_string input(req->getBody()); \
static thread_local auto doc = parser.iterate(input)
#define RESPONSE_PARSE(R) \
static thread_local auto resp = HttpResponse::newCustomHttpResponse(R); \
CORS; \
callback(resp)
#define RESPOND_TRUE \
static thread_local auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k204NoContent, std::nullopt}); \
CORS; \
CACHE_FOREVER; \
callback(resp)
#define NAME_PARAM req->getParameter("name")
api::api(Bank &b) noexcept : bank(b)
{
}
#if API_VERSION >= 1
//Usage
void api::GetBal(req_args, const std::string &name) const
{
RESPONSE_PARSE(bank.GetBal(name));
}
void api::GetLogs(req_args)
{
if constexpr (MAX_LOG_SIZE > 0)
{
RESPONSE_PARSE(bank.GetLogs(NAME_PARAM));
}
else
{
static thread_local auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k404NotFound, "\"Logs are Disabled\""});
CORS;
CACHE_FOREVER;
callback(resp);
}
}
void api::SendFunds(req_args) const
{
SIMD_JSON_GEN;
BankResponse res;
if (doc.error())
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
auto name = doc.find_field("name").get_string();
auto amount = doc.find_field("amount").get_uint64();
if (name.error() || amount.error())
{
res = BankResponse{k400BadRequest, "\"Missing JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper name_val(name.value());
res = bank.SendFunds(NAME_PARAM, name_val.str, amount.value());
}
}
RESPONSE_PARSE(std::move(res));
}
void api::VerifyPassword(req_args) const { RESPOND_TRUE; }
//Meta Usage
void api::ChangePassword(req_args) const
{
SIMD_JSON_GEN;
BankResponse res;
if (doc.error())
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
auto pass = doc.find_field("pass").get_string();
if (pass.error())
{
res = BankResponse{k400BadRequest, "\"Missing JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper pass_val(pass.value());
bank.ChangePassword(NAME_PARAM, pass_val.str);
res = BankResponse{k204NoContent, std::nullopt};
}
}
RESPONSE_PARSE(std::move(res));
}
void api::AdminChangePassword(req_args) const
{
SIMD_JSON_GEN;
BankResponse res;
if (doc.error())
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
auto name = doc.find_field("name").get_string();
auto pass = doc.find_field("pass").get_string();
if (name.error() || pass.error())
{
res = BankResponse{k400BadRequest, "\"Missing JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper name_val(name.value());
if (bank.Contains(name_val.str))
{
StrFromSV_Wrapper pass_val(pass.value());
bank.ChangePassword(name_val.str, pass_val.str);
res = BankResponse{k204NoContent, std::nullopt};
}
else
{
res = BankResponse{k404NotFound, "\"User not found\""};
}
}
}
RESPONSE_PARSE(std::move(res));
}
void api::SetBal(req_args) const
{
SIMD_JSON_GEN;
BankResponse res;
if (doc.error())
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
auto name = doc.find_field("name").get_string();
auto amount = doc.find_field("amount").get_uint64();
if (name.error() || amount.error())
{
res = BankResponse(k400BadRequest, "\"Missing JSON arg(s)\"");
}
else
{
StrFromSV_Wrapper name_val(name.value());
res = bank.SetBal(name_val.str, amount.value());
}
}
RESPONSE_PARSE(std::move(res));
}
void api::ImpactBal(req_args) const
{
SIMD_JSON_GEN;
BankResponse res;
if (doc.error())
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
auto name = doc.find_field("name").get_string();
auto amount = doc.find_field("amount").get_int64();
if (name.error() || amount.error())
{
res = BankResponse(k400BadRequest, "\"Missing JSON arg(s)\"");
}
else
{
StrFromSV_Wrapper name_val(name.value());
res = bank.ImpactBal(name_val.str, amount.value());
}
}
RESPONSE_PARSE(std::move(res));
}
//System Usage
void api::Help(req_args) const
{
static thread_local auto resp = HttpResponse::newRedirectionResponse("https://github.com/EntireTwix/CCash/blob/README.md", k301MovedPermanently);
CACHE_FOREVER;
callback(resp);
}
void api::Close(req_args) const
{
bank.Save();
RESPOND_TRUE; //filter handles admin creds
app().quit();
}
void api::Contains(req_args, const std::string &name) const
{
BankResponse res;
if (bank.Contains(name))
{
res = BankResponse(k204NoContent, std::nullopt);
}
else
{
res = BankResponse(k404NotFound, "\"User not found\"");
}
RESPONSE_PARSE(std::move(res));
}
void api::AdminVerifyAccount(req_args) const
{
RESPOND_TRUE; //filter handles admin creds
}
void api::ApiProperties(req_args) const
{
std::string info = "{\"version\":" + std::to_string(API_VERSION) + ",\"max_log\":" + std::to_string(MAX_LOG_SIZE) + ",\"return_on_del\":" + std::to_string((bool)RETURN_ON_DEL);
if constexpr (RETURN_ON_DEL)
{
info += ",\"" + std::string(return_account) + "\"}";
}
else
{
info += "}";
}
static thread_local auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k200OK, std::move(info)});
CORS;
CACHE_FOREVER;
callback(resp);
}
void api::AddUser(req_args) const
{
SIMD_JSON_GEN;
BankResponse res;
if (doc.error())
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
auto name = doc.find_field("name").get_string();
auto pass = doc.find_field("pass").get_string();
if (name.error() || pass.error())
{
res = BankResponse(k400BadRequest, "\"Missing JSON arg(s)\"");
}
else
{
StrFromSV_Wrapper name_val(name.value());
StrFromSV_Wrapper pass_val(pass.value());
res = bank.AddUser(name_val.str, 0, pass_val.str);
}
}
RESPONSE_PARSE(std::move(res));
}
void api::AdminAddUser(req_args) const
{
SIMD_JSON_GEN;
BankResponse res;
if (doc.error())
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
auto name = doc.find_field("name").get_string();
auto amount = doc.find_field("amount").get_uint64();
auto pass = doc.find_field("pass").get_string();
if (name.error() || amount.error() || pass.error())
{
res = BankResponse(k400BadRequest, "\"Missing JSON arg(s)\"");
}
else
{
StrFromSV_Wrapper name_val(name.value());
StrFromSV_Wrapper pass_val(pass.value());
res = bank.AddUser(name_val.str, amount.value(), pass_val.str);
}
}
RESPONSE_PARSE(std::move(res));
}
void api::DelSelf(req_args) const
{
bank.DelSelf(NAME_PARAM);
RESPOND_TRUE;
}
void api::AdminDelUser(req_args) const
{
SIMD_JSON_GEN;
BankResponse res;
if (doc.error())
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
auto name = doc.find_field("name").get_string();
if (name.error())
{
res = BankResponse(k400BadRequest, "\"Missing JSON arg(s)\"");
}
else
{
StrFromSV_Wrapper name_val(name.value());
res = bank.DelUser(name_val.str);
}
}
RESPONSE_PARSE(std::move(res));
}
#endif

File diff suppressed because one or more lines are too long

26
src/bank_resp.cpp Normal file
View file

@ -0,0 +1,26 @@
#include "bank_resp.h"
template <>
drogon::HttpResponsePtr drogon::toResponse(BankResponse &&data)
{
std::shared_ptr<HttpResponseImpl> res;
if (data.second)
{
res = std::make_shared<HttpResponseImpl>(data.first, CT_APPLICATION_JSON);
res->setBody(std::move(*data.second));
}
else
{
res = std::make_shared<HttpResponseImpl>();
res->setStatusCode(data.first);
}
const auto &advices = HttpAppFrameworkImpl::instance().getResponseCreationAdvices();
if (!advices.empty())
{
for (auto &advice : advices)
{
advice(res);
}
}
return res;
}

28
src/change_flag.cpp Normal file
View file

@ -0,0 +1,28 @@
#include "change_flag.h"
template <bool init>
ChangeFlag<init>::ChangeFlag() noexcept {}
template <bool init>
ChangeFlag<init>::ChangeFlag(ChangeFlag &&f) noexcept
{
change_flag.store(f.GetChangeState(), std::memory_order_release);
}
template <bool init>
void ChangeFlag<init>::SetChangesOn() noexcept
{
return change_flag.store(1, std::memory_order_release);
}
template <bool init>
void ChangeFlag<init>::SetChangesOff() noexcept
{
return change_flag.store(0, std::memory_order_release);
}
template <bool init>
bool ChangeFlag<init>::GetChangeState() const noexcept
{
return change_flag.load(std::memory_order_acquire);
}
template class ChangeFlag<true>;
template class ChangeFlag<false>;

41
src/json_filter.cpp Normal file
View file

@ -0,0 +1,41 @@
#include "json_filter.h"
template <bool check_content_type>
JsonFilter<check_content_type>::JsonFilter() {}
__attribute__((always_inline)) inline bool Contains(std::string_view str, const std::string &val)
{
return str.find(val) != std::string::npos;
}
template <bool check_content_type>
void JsonFilter<check_content_type>::doFilter(const HttpRequestPtr &req,
FilterCallback &&fcb,
FilterChainCallback &&fccb)
{
std::string_view accept_header = req->getHeader("Accept");
if constexpr (check_content_type)
{
std::string_view content_type = req->getHeader("content-type");
if (content_type == "application/json" && (Contains(accept_header, "*/*") || Contains(accept_header, "application/json")))
{
fccb();
return;
}
const auto &resp = HttpResponse::newCustomHttpResponse(BankResponse(k406NotAcceptable, "\"Client must Accept and have content-type of JSON\""));
fcb(resp);
}
else
{
if ((Contains(accept_header, "*/*") || Contains(accept_header, "application/json")))
{
fccb();
return;
}
const auto &resp = HttpResponse::newCustomHttpResponse(BankResponse(k406NotAcceptable, "\"Client must Accept JSON\""));
fcb(resp);
}
}
template class JsonFilter<true>;
template class JsonFilter<false>;

View file

@ -1,22 +1,45 @@
#include "log.h"
void Log::AddTrans(Transaction &&t)
void Log::AddTrans(const std::string &from, const std::string &to, uint32_t amount, time_t time) noexcept
{
if (data.size() == max_log_size) // If we hit the max size
log_flag.SetChangesOn();
if (data.size() == MAX_LOG_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;
data.pop_back();
}
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
data.emplace_back(from, to, amount, time);
}
std::string Log::GetLogs() noexcept
{
if (log_flag.GetChangeState() && data.size()) //if there are changes
{
//re-generate snapshot
//({\"amount\":1,\"from\":\"\",\"time\":1625943626,\"to\":\"\"}, + (2*max_name_size)+10+10) * # of logs) + 1
size_t predicted_size = ((58 + (2 * max_name_size)) * data.size()) + 1;
if (log_snapshot.capacity() < predicted_size)
{
log_snapshot.reserve(predicted_size);
}
log_snapshot = '[';
for (size_t i = 0; i < data.size(); ++i)
{
log_snapshot += "{\"to\":\"";
log_snapshot += data[i].to;
log_snapshot += "\",\"from\":\"";
log_snapshot += data[i].from;
log_snapshot += "\",\"amount\":";
log_snapshot += std::to_string(data[i].amount);
log_snapshot += ",\"time\":";
log_snapshot += std::to_string(data[i].time);
log_snapshot += "},";
}
log_snapshot.back() = ']';
log_flag.SetChangesOff();
}
return log_snapshot;
}
Json::Value Log::Serialize() const
{
Json::Value res;
@ -25,7 +48,11 @@ Json::Value Log::Serialize() const
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;
#ifdef _USE_32BIT_TIME_T
res[i]["time"] = (Json::Int)data[i].time;
#else
res[i]["time"] = (Json::Int64)data[i].time;
#endif
}
return res;
}

12032
src/simdjson.cpp Normal file

File diff suppressed because it is too large Load diff

45
src/str_intrusion.cpp Normal file
View file

@ -0,0 +1,45 @@
#include "str_intrusion.h"
//this function is horribly jank
template <typename Tag>
struct result
{
typedef typename Tag::type type;
static type ptr;
};
template <typename Tag>
typename result<Tag>::type result<Tag>::ptr;
template <typename Tag, typename Tag::type p>
struct rob : result<Tag>
{
struct filler
{
filler() { result<Tag>::ptr = p; }
};
static filler filler_obj;
};
template <typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;
struct string_length
{
typedef void (std::string::*type)(size_t);
};
template class rob<string_length, &std::string::_M_length>;
struct string_data
{
typedef void (std::string::*type)(char *);
};
template class rob<string_data, &std::string::_M_data>;
StrFromSV_Wrapper::StrFromSV_Wrapper() noexcept {}
StrFromSV_Wrapper::StrFromSV_Wrapper(std::string_view sv) noexcept
{
(str.*result<string_data>::ptr)((char *)sv.data());
(str.*result<string_length>::ptr)(sv.size());
}
StrFromSV_Wrapper::~StrFromSV_Wrapper() noexcept
{
(str.*result<string_data>::ptr)(nullptr);
(str.*result<string_length>::ptr)(0);
}

View file

@ -1,15 +1,4 @@
#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();
}
Transaction::Transaction() noexcept {};
Transaction::Transaction(const std::string &from_str, const std::string &to_str, uint32_t amount, time_t time_val) noexcept : from(from_str), to(to_str), amount(amount), time(time_val) {}

View file

@ -1,19 +1,12 @@
#include "user.h"
/**
* @brief User constructor
*
* @param init_pass initial password
*/
User::User(const 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, const std::string &init_pass) : balance(init_bal), password(XXH3_64bits(init_pass.data(), init_pass.size())) {}
User::User(uint32_t init_bal, const std::string &init_pass) noexcept : balance(init_bal), password(xxHashStringGen{}(init_pass)) {}
/**
* @brief User Constructor for loading
@ -21,31 +14,39 @@ User::User(uint32_t init_bal, const std::string &init_pass) : balance(init_bal),
* @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)
User::User(uint32_t init_bal, XXH64_hash_t init_pass) noexcept : balance(init_bal), password(init_pass) {}
#if MAX_LOG_SIZE > 0
User::User(const bank_dom::User &u) noexcept : balance(u.balance), password(u.password)
{
if (log_j.size())
if (u.logs)
{
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++)
for (uint32_t i = (u.logs.value().data.size() - MAX_LOG_SIZE); i < u.logs.value().data.size(); ++i)
{
log.data.push_back(Transaction(
log_j[i]["from"].asCString(),
log_j[i]["to"].asCString(),
log_j[i]["amount"].asUInt(),
log_j[i]["time"].asUInt64()));
const bank_dom::Transaction &temp = u.logs.value().data[i];
log.data.emplace_front(temp.from, temp.to, temp.amount, temp.time);
}
}
}
Json::Value User::Serialize() const
#endif
bank_dom::User User::Encode() const noexcept
{
Json::Value res;
res["balance"] = (Json::UInt)balance;
res["password"] = (Json::UInt64)password;
if constexpr (max_log_size > 0)
#if MAX_LOG_SIZE > 0
if (this->log.data.size())
{
res["log"] = log.Serialize();
bank_dom::Logs save_log;
save_log.data.reserve(this->log.data.size());
for (const Transaction &t : this->log.data)
{
save_log.data.emplace_back(t.from, t.to, t.amount, t.time);
}
return bank_dom::User(balance, password, save_log);
}
return res;
else
{
return bank_dom::User(balance, password, std::nullopt);
}
#else
return bank_dom::User(balance, password, std::nullopt);
#endif
}

78
src/user_filter.cpp Normal file
View file

@ -0,0 +1,78 @@
#include "user_filter.h"
__attribute__((always_inline)) inline bool ValidUsername(const std::string &name) noexcept
{
if (name.size() < min_name_size || name.size() > max_name_size)
{
return false;
}
for (char c : name)
{
if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'))
{
return false;
}
}
return true;
}
template <bool set_body_flag, bool require_admin>
UserFilter<set_body_flag, require_admin>::UserFilter(Bank &b) : bank(b) {}
template <bool set_body_flag, bool require_admin>
void UserFilter<set_body_flag, require_admin>::doFilter(const HttpRequestPtr &req,
FilterCallback &&fcb,
FilterChainCallback &&fccb)
{
std::string_view auth_header = req->getHeader("Authorization");
if (auth_header.size() > 6 && auth_header.size() <= ((max_name_size + 256) * 4) / 3) //"Basic " + (username + ':' + password) * 4/3
{
if (auth_header.substr(0, 6) == "Basic ")
{
std::string_view input = auth_header.substr(6);
char result_buffer[max_name_size + 256]; //(username + ':' + 255 password)
size_t new_sz;
base64_decode(input.data(), input.size(), result_buffer, &new_sz, 0);
std::string_view results_view(result_buffer, new_sz);
std::size_t middle = results_view.find(':');
if (middle != std::string::npos && ((new_sz - middle) <= 256))
{
StrFromSV_Wrapper username(results_view.substr(0, middle));
if (ValidUsername(username.str)) //check if username is a valid attempt to avoid hashing/grabbing shared lock
{
if constexpr (require_admin)
{
if (bank.AdminVerifyAccount(username.str))
{
StrFromSV_Wrapper password(results_view.substr(middle + 1));
if (bank.VerifyPassword(username.str, password.str))
{
fccb();
return;
}
}
}
else
{
StrFromSV_Wrapper password(results_view.substr(middle + 1));
if (bank.VerifyPassword(username.str, results_view.substr(middle + 1)))
{
if constexpr (set_body_flag)
{
req->setParameter("name", username.str);
}
fccb();
return;
}
}
}
}
}
}
fcb(HttpResponse::newCustomHttpResponse(BankResponse(k401Unauthorized, "\"Invalid Credentials\"")));
}
template class UserFilter<true, false>; //user default
template class UserFilter<false, false>; //user sparse
template class UserFilter<false, true>; //admin

10
src/xxhash_str.cpp Normal file
View file

@ -0,0 +1,10 @@
#include "xxhash_str.h"
XXH64_hash_t xxHashStringGen::operator()(const std::string &str) const noexcept
{
return XXH3_64bits(str.data(), str.size());
}
XXH64_hash_t xxHashStringGen::operator()(const std::string_view &str) const noexcept
{
return XXH3_64bits(str.data(), str.size());
}

1
third_party/base64 vendored Submodule

@ -0,0 +1 @@
Subproject commit eaebee8f666ea0451b0474e7be16006ad994004c

View file

@ -1,8 +0,0 @@
{
"" :
{
"balance" : 0,
"log" : null,
"password" : 0
},
}