Compare commits

..

No commits in common. "main" and "v1.2.0" have entirely different histories.
main ... v1.2.0

83 changed files with 1398 additions and 45647 deletions

View file

@ -1,7 +0,0 @@
/build
/help.md
/services.md
/APIs.md
/README.md
/benchmarking.cpp
/docs

View file

@ -1,15 +0,0 @@
---
name: Bug report
about: an errors scope must be of CCash and not one of its connected services or
API, if so the issue should be submitted on those repos
title: "[BUG]"
labels: bug
assignees: ''
---
**Description**
* A description of the bug's effects.
**Reproducing Instructions**
* Please attempt to find a way to reliably reproduce the bug, this will speed up fixing it. (optional)

View file

@ -1,17 +0,0 @@
---
name: Feature request
about: suggestions for how to improve CCash
title: "[IDEA]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Usage**
How this would ideally be used / benefit

View file

@ -1,41 +0,0 @@
name: Build
on:
push:
branches:
- main
jobs:
release:
name: Push Docker image to GitHub Packages
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Checkout the repo
uses: actions/checkout@v2
- name: Login to GitHub Docker Registry
uses: docker/login-action@v1
with:
registry: docker.pkg.github.com
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Format repository
run: |
echo IMAGE_REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
- name: Build container image
uses: docker/build-push-action@v2
with:
push: true
tags: |
docker.pkg.github.com/${{ env.IMAGE_REPOSITORY }}/ccash:${{ github.sha }}
docker.pkg.github.com/${{ env.IMAGE_REPOSITORY }}/ccash:latest
trigger-deploy:
needs: release
runs-on: ubuntu-latest
steps:
- run: |
curl -X POST \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Authorization: Bearer ${{ secrets.CCASH_DEPLOY_TOKEN }}' \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/deploy.yaml/dispatches \
-d '{"ref":"main"}'

5
.gitignore vendored
View file

@ -1,5 +1,4 @@
.vscode
build
ccash_config.hpp
deployment/.yamllint
config
config.json
users.json

3
.gitmodules vendored
View file

@ -4,6 +4,3 @@
[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

10
APIs.md Normal file
View file

@ -0,0 +1,10 @@
# Language Specific APIs
## Complete
* [JS API](https://github.com/LukeeeeBennett/ccash-client-js)
* [ComputerCraft (Lua) API](https://github.com/Reactified/rpm/blob/main/packages/ccash-api/api.lua)
## In Dev
* [Python API](https://github.com/fearlessdoggo21/ccashpythonclient)
* [CS API](https://github.com/Soverclysm/CCash-dotnet-api)
* [Rust API](https://git.stboyden.com/STBoyden/ccash-rs)

View file

@ -6,98 +6,25 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_FLAGS "-Wall -Wextra -march=native")
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
find_package(Threads REQUIRED)
add_executable(${PROJECT_NAME} main.cpp)
add_executable(${PROJECT_NAME} main.cpp )
target_sources(${PROJECT_NAME} PRIVATE
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
src/bank_f.cpp
src/bank.cpp
src/log.cpp
src/transaction.cpp
src/user.cpp
)
if(DEFINED USER_SAVE_LOC)
set(USER_SAVE "\"${USER_SAVE_LOC}\"")
else()
set(USER_SAVE "\"../config/users.dat\"")
endif()
if(DEFINED DROGON_CONFIG_LOC)
set(DROGON_CONFIG "\"${DROGON_CONFIG_LOC}\"")
else()
set(DROGON_CONFIG "\"../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 ADD_USER_OPEN)
set(ADD_USER_OPEN_VAL ${ADD_USER_OPEN})
else()
set(ADD_USER_OPEN_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()
if(DEFINED USE_DEPRECATED_ENDPOINTS)
set(USE_DEPRECATED_ENDPOINTS_VAL ${USE_DEPRECATED_ENDPOINTS})
else()
set(USE_DEPRECATED_ENDPOINTS_VAL true)
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,21 +0,0 @@
FROM alpine:latest
WORKDIR /
RUN apk update && apk add bash git cmake g++ make protobuf jsoncpp-dev openssl libressl-dev zlib-dev util-linux-dev libtool autoconf automake python3
RUN git clone --recurse-submodules https://github.com/EntireTwix/CCash.git
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.dat" ..
RUN make -j$(nproc)
ARG ADMIN_A=admin
ARG SAVE_FREQ=2
WORKDIR /
RUN sed -i 's/\r$//' /CCash/config/ssl.sh && \
chmod +x /CCash/config/ssl.sh
RUN /CCash/build/bank
CMD /CCash/config/ssl.sh && /CCash/build/bank ${ADMIN_A} ${SAVE_FREQ}

833
LICENSE
View file

@ -1,281 +1,622 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Version 3, 29 June 2007
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
TERMS AND CONDITIONS
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
0. Definitions.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
"This License" refers to version 3 of the GNU General Public License.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
A "covered work" means either the unmodified Program or a work based
on the Program.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
1. Source Code.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
The Corresponding Source for a work in source code form is that
same work.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
2. Basic Permissions.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
13. Use with the GNU Affero General Public License.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
14. Revised Versions of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
NO WARRANTY
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
15. Disclaimer of Warranty.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
@ -287,15 +628,15 @@ free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
@ -303,37 +644,31 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

111
README.md
View file

@ -1,17 +1,100 @@
![image](https://user-images.githubusercontent.com/31377881/139971016-9ca428d1-1a17-4c87-85e3-9b7ead0780f5.png)
# 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.
* [problem/solution](docs/idea.md)
* connected services
* how to create
* [explanation](docs/connected_services/how_to/explanation.md)
* [API implementations](docs/connected_services/how_to/APIs.md)
* [the API](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)
the currency model most Minecraft Servers adopt if any, is resource based, usually diamonds, this model is fraught with issues however:
* [Versions](docs/versioning.md)
- 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](services.md)
Go to [here](help.md) to see the API's endpoints.
Language specific APIs can be found [here](APIs.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 | Work |
| :------------------------------------------ | ---------------------------------------------------------- |
| [Expand](https://github.com/Expand-sys) | Frontend |
| [React](https://github.com/Reactified) | CC {API, Shops, and ATM, Logo} |
| [Doggo](https://github.com/FearlessDoggo21) | Logs loading/adding Optimized, HTTP convention suggestions |
| [Luke](https://github.com/LukeeeeBennett) | JS API, Slight Doc edits |
| [Jolly](https://github.com/STBoyden) | Slight Doc edits |
## 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, [graph](https://user-images.githubusercontent.com/750081/61976089-aedeab00-af9f-11e9-9239-e5375d6c080f.png)
- **Lightweight**, anecodotally I experienced 0.0% idle, <1% CPU usage on average, 7% at peak, 1000 requests in 0.85s
### Safety
- **Tamper Proof** relative to an in-game implementation
- **Auto-Saving** and Saves on close
- **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)
## 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)

View file

@ -1,105 +0,0 @@
#include <iostream>
#include <chrono>
#include <thread>
#include <sys/types.h>
#include <unistd.h>
#include <random>
#include "xxhash_str.h"
#include "bank.h"
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
using namespace std::chrono;
#include <ctime>
#include <ratio>
#include <chrono>
#define time_func_a(f, a, x) \
{ \
using namespace std::chrono; \
uint32_t timer = 0; \
for (int i = 0; i < x; ++i) \
{ \
auto t1 = high_resolution_clock::now().time_since_epoch(); \
f; \
auto t2 = high_resolution_clock::now().time_since_epoch(); \
a; \
timer += std::chrono::duration_cast<std::chrono::nanoseconds>((t2 - t1)).count(); \
} \
std::cout << timer / x << '\n'; \
}
#define time_func(f, x) \
{ \
using namespace std::chrono; \
uint32_t timer = 0; \
for (int i = 0; i < x; ++i) \
{ \
auto t1 = high_resolution_clock::now().time_since_epoch(); \
f; \
auto t2 = high_resolution_clock::now().time_since_epoch(); \
timer += std::chrono::duration_cast<std::chrono::nanoseconds>((t2 - t1)).count(); \
} \
std::cout << timer / x << '\n'; \
}
#define Op_a(v, name, x, a) \
{ \
std::cout << name; \
time_func_a(v, a, x); \
}
#define Op(v, name, x) \
{ \
std::cout << name; \
time_func(v, x); \
}
int main(int argc, char **argv)
{
for (size_t i = 100; i < 10100; ++i)
{
Bank::AddUser(std::to_string(i), 0, "root");
}
std::cout << "added " << Bank::NumOfUsers() << " users\n";
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::GetBal("twix"), "get bal: ", 1000000);
Op(Bank::VerifyPassword("twix", "root"), "verify pass: ", 1000000);
Op(Bank::ChangePassword("twix", "root"), "change pass: ", 1000000);
#if MAX_LOG_SIZE > 0
#if USE_DEPRECATED_ENDPOINTS
Op(Bank::GetLogs("twix"), "get logs init: ", 1);
Op(Bank::GetLogs("twix"), "get logs cached: ", 1000000);
#endif
Op(Bank::GetLogsV2("twix"), "get logs init (v2): ", 1);
Op(Bank::GetLogsV2("twix"), "get logs cached (v2): ", 1000000);
Op(Bank::GetLogsRange("twix", 0, 100), "get logs range: ", 1000000);
#endif
Op(Bank::PruneUsers(0, 0), "prune users: ", 1);
Op(Bank::Save(), "saving: ", 1);
return 0;
}

View file

@ -1,35 +0,0 @@
#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@
#define MULTI_THREADED @MULTI_THREADED_VAL@
#define ADD_USER_OPEN @ADD_USER_OPEN_VAL@
#define USE_DEPRECATED_ENDPOINTS @USE_DEPRECATED_ENDPOINTS_VAL@

16
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": "",
"key": ""
}
]
}

View file

@ -1,16 +0,0 @@
{
"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"
}
]
}

View file

@ -1,9 +0,0 @@
#!/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

Binary file not shown.

View file

@ -1,20 +0,0 @@
[PREVIOUS PAGE](building.md)
### How do I setup a CCash instance
You can with [build docs](https://github.com/EntireTwix/CCash/blob/main/docs/building.md) you can build from source or use the Docker package.
### Why is my username invalid
Usernames are restricted by minecraft's requirements
* letters
* numbers
* _
* length must be atleast 3 and at most 16 characters.
### Is this crypto like krist?
CCash isn't a crypto, simply a ledger keeping track of who owns what.
### Why isnt this on a database?
Because this usecase requires none of the features a database would offer.
### People are making too many accounts maliciously to fill up space on my server!
Consider disabling `ADD_USER_OPEN` in the [build proccess](https://github.com/EntireTwix/CCash/blob/main/docs/building.md).
### My instance is taking up too much storage or RAM
Reduce log size and/or use the prune users endpoint to remove dead accounts.
### Why not use an economy mod
Speed of operations, CCash being external to MC (and so compatible with any version/configuration), and the API are the main advantages to an economy mod.

View file

@ -1,159 +0,0 @@
# 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, such that each time save frequency is met, changes having been made 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 139 bytes in memory and 43 bytes in disk at default settings on the current version, so 7543 logs per MB of RAM. Setting to 0 will disable logs
* with no users memory usage is ~8.47 MB
* saving frequency being set to 0 will disable frequency saving and only save on close
* make backups of your save files!
## Docker & Ansible
If you want to use the docker package, deploy information can be found [here](deploy.md)
## Drogon Depedencies
### Linux
#### Debian
```
sudo apt install libjsoncpp-dev uuid-dev openssl libssl-dev zlib1g-dev make cmake
```
#### 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
```
#### Other
anything that can download the appropriate dependencies
```
make & cmake
jsoncpp
libuuid
openssl
zlib
```
### MacOS
```
brew install jsoncpp ossp-uuid openssl zlib
```
## 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
```
### CMake Variables
there are multiple flags responsible configuring CCash:
| name | default | description | pros | cons |
| :----------------------- | :------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | -------------------------------------------------------- |
| USER_SAVE_LOC | "config/users.dat" | where the users are saved | `N/A` | `N/A` |
| DROGON_CONFIG_LOC | "config/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 |
| ADD_USER_OPEN | `true` | anybody can make a new account, if set to false only admins can add accounts via `AdminAddUser()` | `N/A` | spamming new users |
| USE_DEPRECATED_ENDPOINTS | `true` | some endpoints have newer versions making them obsolete but old programs might still call these endpoints so they are simply marked deprecated. | supports old programs | old endpoints can be ineffecient |
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>
sudo ./bank
```
the last command generates a blank save file in your defined location.
## 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
}
]
}
```
## Usage
You can now run the program from the build location. For example
```
sudo ./bank admin 5
```
in this example CCash will be launched with the admin account named `"admin"`, and a saving frequency of every `5` minutes; without daemon being given its default is `false`.
Another example
```
sudo ./bank Kevin 0 true
```
in this example CCash will be launched with the admin account named `"Kevin"`, and a saving frequency of `0` meaning the server will only save when closed; daemon is set to `true` and so will be launched in the background.

View file

@ -1,32 +0,0 @@
[PREVIOUS PAGE](how_to/endpoints.md) | [NEXT PAGE](../features/user_side.md)
## Key
| description | symbol |
| :-----------------------: | :----------------- |
| supported | :heavy_check_mark: |
| uses deprecated endpoints | ⚠ |
| uses defunt endpoints | :no_entry: |
| in development | :hammer: |
## General
| author | name | support | image |
| :-------------------------------------- | ----------------------------------------------------------- | :----------------: | :-------------------------------------------------------------------------------------------------------------: |
| [Expand](https://github.com/Expand-sys) | [Web Frontend](https://github.com/Expand-sys/ccashfrontend) | :heavy_check_mark: | ![image](https://user-images.githubusercontent.com/31377881/121337724-afe9fe80-c8d1-11eb-8851-23ec5e74cd26.png) | |
| [ArcNyxx](https://github.com/ArcNyxx) | [CCash CLI](https://github.com/ArcNyxx/ccash_cmd) | ⚠ | |
## Minecraft
| author | name | support | image |
| :------------------------------------------ | ---------------------------------------------------------------------------------------- | :----------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [Reactified](https://github.com/Reactified) | [Shop](https://github.com/Reactified/rpm/tree/main/packages/ccash-shop) | :heavy_check_mark: | ![image](https://user-images.githubusercontent.com/31377881/120050327-de163700-bfd1-11eb-9d5a-f75c003e867c.png) |
| [Reactified](https://github.com/Reactified) | [Wallet](https://github.com/Reactified/rpm/tree/main/packages/ccash-wallet) | ⚠ | ![image](https://user-images.githubusercontent.com/31377881/121338034-fb041180-c8d1-11eb-8640-b18c141eb980.png) |
| [Reactified](https://github.com/Reactified) | [ATM](https://github.com/Reactified/rpm/tree/main/packages/ccash-bank) | :heavy_check_mark: | ![image](https://user-images.githubusercontent.com/31377881/121277361-4d6b1100-c885-11eb-87c8-cfebcf58da4f.png) |
| [Reactified](https://github.com/Reactified) | [Chunk Loader Shop](https://github.com/Reactified/rpm/tree/main/packages/forceload-shop) | :heavy_check_mark: | ![image](https://user-images.githubusercontent.com/31377881/209894520-f7183f45-bbac-40f3-9f95-043bda3c0097.png) ![image](https://user-images.githubusercontent.com/31377881/209894553-16ef7e04-52e7-4198-8a39-9ad2ada6eaf7.png) |
| [STBoyden](https://github.com/STBoyden) | [Commodities Exchange](https://github.com/STBoyden/ccash-market) | :hammer: | |
## Desired
| idea | description |
| :-----------: | :------------------------------------------------------------- |
| Gambling | physical or digital casino. |
| Shipping | the infastructure to quickly send items across long distances. |
| Mod or Plugin | a server-side mod |

View file

@ -1,41 +0,0 @@
[PREVIOUS PAGE](explanation.md) | [NEXT PAGE](endpoints.md)
CCash is backwards compatible, so even if a language API does not support the newester version it can still call the old endpoints. Only when the major version increments are deprecated features made defunct (e.g `v1.0.0` -> `v2.0.0`), check the [endpoint docs](endpoints.md) to avoid using deprecated endpoints. For more information about versioning check out [versioning docs](../../versioning.md).
| author | language | | newest CCash supported version |
| :-------------------------------------------------------- | :--------: | -------------------------------------------------------------------- | :----------------------------: |
| [SpaceCat](https://github.com/SpaceCat-Chan) | CCLua | [CatsCCashLuaApi](https://github.com/SpaceCat-Chan/CatsCCashLuaApi) | `v2.5.1` |
| [Sam](https://github.com/STBoyden) | Rust | [ccash rs](https://github.com/STBoyden/ccash-rs) | `v2.5.1` |
| [Doggo](https://github.com/ArcNyxx) | Python | [CCashPythonClient](https://github.com/ArcNyxx/ccash_python_client) | `v2.5.1` |
| [Luke](https://github.com/LukeeeeBennett/ccash-client-js) | TypeScript | [ccash client js](https://github.com/LukeeeeBennett/ccash-client-js) | `v1.3.0` |
here is a demo program for the lua API by SpaceCat
```lua
local ccash = require("ccash.api")
ccash.meta.set_server_address("https://ccashinstance.net/")
local cred_name = "my_account"
local cred_pass = "my_pass"
math.randomseed(os.time())
local temp_name = tostring(math.random(100, 2147483647))
print("temp account "..temp_name)
print("enter target name ")
local target_name = io.read()
print("enter target amount ")
local target_amount = tonumber(io.read())
print(ccash.register(temp_name, "root123"))
print(ccash.send_funds(cred_name, cred_pass, temp_name, target_amount))
print(ccash.send_funds(temp_name, "root123", target_name, target_amount))
print(ccash.delete_self(temp_name, "root123"))
```
this particular program obfuscates transactions so that the person receiving it has no way of knowing who it came from
```
my_account -> temp_account -> target_account
+100 +100
```
then temp_account is deleted

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -1,98 +0,0 @@
# 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
⚠ - Deprecated endpoint
:no_entry: - Defunct endpoint
## all error responses have JSON string along with them to describe
### Usage endpoints
| name | last change | purpose | json input | path | HTTP Method | correct status | return type | return value | Jresp | Jreq | A | U |
| :------------- | :---------: | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ------------------------------- | :---------: | :------------: | :--------------: | :-----------------------------------------------------------: | :----------------: | :----------------: | :---: | :----------------: |
| GetBal | `v2.3.0` | 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: | :x: | :x: | :x: |
| GetLogs | `v2.3.0` | retrieves the logs of a given user, length varies by server configuration (oldest to newest transactions) | `N/A` | ⚠ api/v1/user/log | `GET` | 200 | array of objects | [{"to":string, "from":string, "amount":uint32, "time":int64}] | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: |
| GetLogsV2 | `v2.6.1` | retrieves the logs of a given user, length varies by server configuration (newest to oldest transactions) | `N/A` | api/v2/user/log | `GET` | 200 | array of objects | [{"counterparty":string, "amount":int64, "time":int64}] | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: |
| GetLogsRange | `v2.6.1` | retrieves the logs of a given user, where `{start}` is the 0-indexed first log and `{length}` is the number of logs after that index. | `N/A` | api/v1/user/log_range | `GET` | 200 | array of objects | [{"counterparty":string, "amount":int64, "time":int64}] | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: |
| SendFunds | `v2.3.0` | 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: | :x: | :heavy_check_mark: |
| ChangePassword | `v2.3.0` | 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: | :x: | :heavy_check_mark: |
| VerifyPassword | `v2.3.0` | 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: | :x: | :x: | :heavy_check_mark: |
### Usage enpoint errors
| name | 400 | 401 | 404 | 406 |
| :------------- | :----------------: | :----------------: | :----------------: | :----------------: |
| GetBal | :x: | :x: | :heavy_check_mark: | :heavy_check_mark: |
| GetLogs | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| GetLogsV2 | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| GetLogsRange | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| SendFunds | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| VerifyPassword | :x: | :heavy_check_mark: | :x: | :heavy_check_mark: |
| ChangePassword | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: |
### Meta Usage endpoints
| name | last change | purpose | json input | path | HTTP Method | correct status | return type | return value | Jresp | Jreq | A | U |
| :------------------ | :---------: | -------------------------------------------------------------------------------------------------- | ------------------------------- | --------------------------------- | :---------: | :------------: | :--------------: | :-----------------------------------------------------: | :----------------: | :----------------: | :----------------: | :---: |
| AdminGetLogs | `v2.6.1` | retreives the logs of a given user `{name}`, length varies by server configuration | `N/A` | api/v1/admin/user/log?name={name} | `GET` | 200 | array of objects | [{"counterparty":string, "amount":int64, "time":int64}] | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: |
| AdminChangePassword | `v2.3.0` | 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: | :x: |
| AdminVerifyAccount | `v2.3.0` | checks wether a user is the admin | `N/A` | api/v1/admin/verify_account | `POST` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: |
| SetBal | `v2.3.0` | 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: | :x: |
| ImpactBal | `v2.3.0` | modifies the user `"name"`'s balance by `"amount"` if positive itll add, if negative itll subtract | {"name":string,"amount":int64} | api/v1/admin/impact_balance | `POST` | 200 | uint32 | new balance after modification | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
### Meta Usage endpoint errors
| name | 400 | 401 | 404 | 406 |
| :------------------ | :----------------: | :----------------: | :----------------: | :----------------: |
| AdminGetLogs | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| AdminChangePassword | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| AdminVerifyAccount | :x: | :heavy_check_mark: | :x: | :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: |
### Sytem Usage endpoints
| name | last change | purpose | json input | path | HTTP Method | correct status | return type | return value | Jresp | Jreq | A | U |
| :------------ | :---------: | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------ | :---------: | :------------: | :---------: | :-----------------------------------------------------------------: | :----------------: | :----------------: | :----------------: | :---: |
| Help | `v2.3.0` | redirects to GitHub projects Docs | `N/A` | api/help | `GET` | 301 | `N/A` | `N/A` | :x: | :x: | :x: | :x: |
| Close | `v2.3.0` | saves & closes the CCash webserver | `N/A` | api/v1/admin/shutdown | `POST` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: |
| Contains | `v2.3.0` | checks wether a given user `{name}` exists | `N/A` | api/v1/user/exists?name={name} | `GET` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :x: | :x: | :x: |
| PruneUsers | `v2.3.0` | deletes users with most recent transactions older then `"time"` (if logs are enabled) and have less money then `"amount"` | {"time":int64,"amount":uint32} or just {"amount":uint32} | api/v1/admin/prune_users | `POST` | 200 | uint64 | number of users deleted | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| ApiProperties | `v2.5.1` | properties of the given instance | `N/A` | api/properties | `GET` | 200 | json object | {"max_log":uint64, "add_user_open":boolean, "return_on_del":string} | :heavy_check_mark: | :x: | :x: | :x: |
### System Usage endpoin errors
| name | 401 | 404 | 406 |
| :------------ | :----------------: | :----------------: | :----------------: |
| Help | :x: | :x: | :x: |
| Close | :heavy_check_mark: | :x: | :heavy_check_mark: |
| Contains | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| PruneUsers | :heavy_check_mark: | :x: | :heavy_check_mark: |
| ApiProperties | :x: | :x: | :x: |
### Username Requirements
Valid
* letters
* numbers
* _
* Length must be atleast 3 and at most 16 characters.
### User Management endpoints
| name | last change | purpose | json input | path | HTTP Method | correct status | return type | return value | Jresp | Jreq | A | U |
| :----------- | :---------: | --------------------------------------- | --------------------------------------------- | -------------------------- | :---------: | :------------: | :---------: | :----------: | :----------------: | :----------------: | :----------------: | :----------------: |
| AddUser | `v2.3.0` | adding a user with a balance of 0 | {"name":string,"pass":string} | api/v1/user/register | `POST` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: |
| AdminAddUser | `v2.3.0` | 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: | :x: |
| DelSelf | `v2.3.0` | deletes a user | `N/A` | api/v1/user/delete | `DELETE` | 204 | `N/A` | `N/A` | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: |
| AdminDelUser | `v2.3.0` | 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: | :x: |
### User Management endpoint errors
| name | 400 | 401 | 404 | 406 | 409 |
| :----------- | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: |
| AddUser | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| AdminAddUser | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: |
| DelSelf | :x: | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: |
| AdminDelUser | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |

View file

@ -1,21 +0,0 @@
[PREVIOUS PAGE](../../idea.md) | [NEXT PAGE](APIs.md)
Using the ledger's API allows (you/others) to (make/use) connected services which 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 is to provide functionality which relies on the ground truth of ownership of currency. This information is centrally secured in CCash.
To browse the currently available connected 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). What they do is provide a simplified 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. 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).

View file

@ -1,10 +0,0 @@
# Contributors
| name | work |
| :------------------------------------------- | ---------------------------------- |
| [Luke](https://github.com/LukeeeeBennett) | Github Actions, and JS API |
| [React](https://github.com/Reactified) | Logo, CC Wallet/Shop/ATM |
| [Doggo](https://github.com/FearlessDoggo21) | HTTP advice, Python API, and C CLI |
| [SpaceCat](https://github.com/SpaceCat-Chan) | CCLua API |
| [Expand](https://github.com/Expand-sys) | Docker, and Ansible |
| [Sam](https://github.com/STBoyden) | Rust API |
| Caesay | Restful API advice |

View file

@ -1,47 +0,0 @@
# Deploying CCash
CCash can deployed to a remote machine pretty simply.
A pre-built docker image is supplied in the repos [GitHub Packages](https://github.com/features/packages) container registry [EntireTwix/CCash](https://github.com/EntireTwix/CCash/packages/851105).
It can be run with docker like so:
```
docker run -itp 443:443 -v ccashconfig:/ccash/config -e ADMIN_A=<admin-username> -e SAVE_FREQ=<in minutes> ghcr.io/entiretwix/ccash/ccash:latest
```
## Ansible
Additionally CCash can be deployed to any infrastructure able to run Rocky/Alma Linux 8/9 x86_64 virtual or not, we will be eventually updating it to allow it to run on other OS's but for now RHEL is what works.
As CCash is intended to be run as root, the playbook is run also as root. The playbook also builds CCash from the latest github push, so there may be bugs.
In order to use the ansible playbook, clone the playbook to any pc with the ability to access the server through SSH and with Ansible installed, edit the inventory file to contain the IP address of the target server and run the following commands:
```ansible-galaxy install -r deployment/requirements.yml
```ansible-playbook -i deployment/inventory deployment/main.yml -k```
When this is complete the server will have ccash installed to the user dir, this is customizable in the vars/default.yml file along with the admin username and save frequency.
To start CCash run:
```systemctl start ccash```
To run ccash at start up run:
```systemctl enable ccash```
## Build
Previously this used GitHub Workflows, I(Expand) dont know how to do those but its not that hard to deploy stuff manually. To run the pre configured docker image run the above command and you are off to the races it will deploy a self signed certificate and use that for deployment. As this is not a user facing deployment the certificate is self signed and thus will throw an error on chrome, though this will still work if you ignore it. For production you should deploy with a reverse proxy and a correct certificate for your domain.
To build this manually you may download the dockerfile only, it will pull the latest repository the commands for building are
```
docker build --build-arg ADMIN_A=<admin-username> --build-arg SAVE_FREQ=<in minutes> -t ccash . --no-cache
docker run -itp 443:443 -v ccashconfig:/ccash/config -e ADMIN_A=<admin-username> -e SAVE_FREQ=<in minutes> ccash
```
if you have the know how you may edit the docker file and add CMAKE commands as listed in the build section of the CCash documentation.
## Deploy
You can deploy this docker image to be run on a remote machine in a few steps or you can deploy manually in this case we are using [Debian OS](https://www.debian.org/) running on the [Linode](https://www.linode.com/) cloud provider, but most OS and cloud providers will work, assuming the machine can run an SSH server.
Additionally, there is a dockerfile where you can build it yourself or a repository available on [dockerhub](https://hub.docker.com/r/expandsys/ccash) for you to just pull and run on any machine quickly and easily.
### Configure the machine
For docker deployment there is one supported config and that is the command listed above,
ADMIN_A = Admin account name, must be all lowercase, and the account must be created before use. To do this either use the [CCashDeploy](https://hub.docker.com/r/expandsys/ccashdeploy) docker image and use the CCashFrontend to register the account or use curl to send the raw command to the endpoint.
SAVE_FREQ = Saving frequency in minutes, pretty simple

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,47 +0,0 @@
[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) |
| --------------------- | ----- | ---------------- |
| __XXH3__ (SSE2) | 64 | 31.5 GB/s |
| _RAM sequential read_ | N/A | 28.0 GB/s |
## [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`.
## Backwards Compatible API
versioning is implemented by the endpoints path, for example `api/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

@ -1,33 +0,0 @@
[PREVIOUS PAGE](../connected_services/existing_services.md) | [NEXT PAGE](implementation.md)
# Features
## Performance
#### Speed
<!-- graphs -->
Capable of processing thousands of requests per second, with little slow down as user size grows to the millions.
#### Lightweight
<!-- specs -->
* Low memory usage at 8 MB baseline, and 139 bytes per new log.
* Extremely low CPU usage in the single digits of %.
* Small save files at typically a couple kb, easily shareable.
## Accessibility
#### Connected Services
as explained in earlier docs a ecosystem of connected services allows you many ways to utilize CCash.
#### APIs
for devs who wanna make more connected services, existing APIs exist in multiple lanaguages enabling faster development/maintenance.
#### External
its game indepedent meaning you dont have to be in-game to use it. With support for Docker and Ansible Playbook.
## Security
#### HTTPS
OpenSSL is used to secure the HTTP server.
#### Hashed Passwords
if the server's save file is compromised the user passwords will not be derivable.
## Other
#### Logs
each transaction is logged and the last `n` logs are stored, if set to 0 logs will be disabled.
#### Return On Delete
by default this feature is off, but when enabled deleted account's funds will be redirected to a specified account rather then "burned"
#### Configurable
as you can read in [building.md](../building.md) CCash is highly configurable.
#### Game Independent
As CCash does not require an addition to the game in the form of a mod or plugin, it can be ran on any server.

View file

@ -1,30 +0,0 @@
[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 usecase.
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.
* Some resources are passively reapable (e.g iron or gold), making the generation of currency a larger focus than that of creating value.
* Locality is required for transactions.
* Theft is possible; ownership is physical possession based.
CCash solves these issues and adds a level of abstraction. The main philosophy of CCash is to have fast core operations which other services build on, CCash can proccess thousands of requests per second.
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 connected services and CCash is very lightweight and so will be a tiny proportion of the server's total computation.
**DISCLAIMER: ComputerCraft requires you add `127.0.0.1` to its config section `allowed_domains` if you're interacting with CCash locally**
As CCash is just a means of keeping track of who has what, the economic system you use is entirely up to whomever hosts the instance.
I suggest an admin should manage the instance to lower incentive to manipulate balances.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,12 +0,0 @@
# Versioning
## Endpoint Version
When changing an existing endpoint in a breaking way the version will increment in the path e.g `api/v1/user/log` -> `api/v2/user/log`. If the change is non-breaking then extra parameters will simply be added to the endpoint. If the older version is marked deprecated it will be documented in [endpoints.md](connected_services/how_to/endpoints.md)
## Release Version
Major changes (e.g `v1.0.0` -> `v2.0.0`) denote a breaking change as all previously deprecated endpoints will now be made defunct and wont be built with that release.
Minor changes (e.g `v1.0.0` -> `v1.1.0`) denote a non-breaking change that adds or changes something about CCash.
Patches (e.g `v1.0.0` -> `v1.0.1`) denote bug fixes or docs changes.

View file

@ -1,27 +0,0 @@
package bank_dom
struct Transaction
{
string counterparty = "";
bool receiving = false;
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;
}

View file

@ -1,199 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// FBE version: 1.14.1.0
//------------------------------------------------------------------------------
#include "bank_dom.h"
namespace bank_dom {
Transaction::Transaction()
: counterparty("")
, receiving(false)
, amount((uint32_t)0ull)
, time((uint64_t)0ull)
{}
Transaction::Transaction(const std::string& arg_counterparty, bool arg_receiving, uint32_t arg_amount, uint64_t arg_time)
: counterparty(arg_counterparty)
, receiving(arg_receiving)
, 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(counterparty, other.counterparty);
swap(receiving, other.receiving);
swap(amount, other.amount);
swap(time, other.time);
}
std::ostream& operator<<(std::ostream& stream, const Transaction& value)
{
stream << "Transaction(";
stream << "counterparty="; stream << "\"" << value.counterparty << "\"";
stream << ",receiving="; stream << (value.receiving ? "true" : "false");
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

View file

@ -1,240 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// FBE version: 1.14.1.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 counterparty;
bool receiving;
uint32_t amount;
uint64_t time;
size_t fbe_type() const noexcept { return 1; }
Transaction();
Transaction(const std::string& arg_counterparty, bool arg_receiving, 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
#if defined(FMT_VERSION) && (FMT_VERSION >= 90000)
template <> struct fmt::formatter<bank_dom::Transaction> : ostream_formatter {};
#endif
template<>
struct std::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 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
#if defined(FMT_VERSION) && (FMT_VERSION >= 90000)
template <> struct fmt::formatter<bank_dom::Logs> : ostream_formatter {};
#endif
template<>
struct std::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 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
#if defined(FMT_VERSION) && (FMT_VERSION >= 90000)
template <> struct fmt::formatter<bank_dom::User> : ostream_formatter {};
#endif
template<>
struct std::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 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
#if defined(FMT_VERSION) && (FMT_VERSION >= 90000)
template <> struct fmt::formatter<bank_dom::Global> : ostream_formatter {};
#endif
template<>
struct std::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 bank_dom {
} // namespace bank_dom

View file

@ -1,640 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// FBE version: 1.14.1.0
//------------------------------------------------------------------------------
#include "bank_dom_final_models.h"
namespace FBE {
FinalModel<::bank_dom::Transaction>::FinalModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, counterparty(buffer, 0)
, receiving(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
+ counterparty.fbe_allocation_size(fbe_value.counterparty)
+ receiving.fbe_allocation_size(fbe_value.receiving)
+ 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;
counterparty.fbe_offset(fbe_current_offset);
fbe_field_size = counterparty.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;
receiving.fbe_offset(fbe_current_offset);
fbe_field_size = receiving.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;
counterparty.fbe_offset(fbe_current_offset);
fbe_field_size = counterparty.get(fbe_value.counterparty);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
receiving.fbe_offset(fbe_current_offset);
fbe_field_size = receiving.get(fbe_value.receiving);
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;
counterparty.fbe_offset(fbe_current_offset);
fbe_field_size = counterparty.set(fbe_value.counterparty);
fbe_current_offset += fbe_field_size;
fbe_current_size += fbe_field_size;
receiving.fbe_offset(fbe_current_offset);
fbe_field_size = receiving.set(fbe_value.receiving);
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

@ -1,322 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// FBE version: 1.14.1.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> counterparty;
FinalModel<bool> receiving;
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

@ -1,932 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// FBE version: 1.14.1.0
//------------------------------------------------------------------------------
#include "bank_dom_models.h"
namespace FBE {
FieldModel<::bank_dom::Transaction>::FieldModel(FBEBuffer& buffer, size_t offset) noexcept : _buffer(buffer), _offset(offset)
, counterparty(buffer, 4 + 4)
, receiving(buffer, counterparty.fbe_offset() + counterparty.fbe_size())
, amount(buffer, receiving.fbe_offset() + receiving.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
+ counterparty.fbe_size()
+ receiving.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()
+ counterparty.fbe_extra()
+ receiving.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 + counterparty.fbe_size()) > fbe_struct_size)
return true;
if (!counterparty.verify())
return false;
fbe_current_size += counterparty.fbe_size();
if ((fbe_current_size + receiving.fbe_size()) > fbe_struct_size)
return true;
if (!receiving.verify())
return false;
fbe_current_size += receiving.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 + counterparty.fbe_size()) <= fbe_struct_size)
counterparty.get(fbe_value.counterparty, "");
else
fbe_value.counterparty = "";
fbe_current_size += counterparty.fbe_size();
if ((fbe_current_size + receiving.fbe_size()) <= fbe_struct_size)
receiving.get(fbe_value.receiving, false);
else
fbe_value.receiving = false;
fbe_current_size += receiving.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
{
counterparty.set(fbe_value.counterparty);
receiving.set(fbe_value.receiving);
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

@ -1,398 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: user_model.fbe
// FBE version: 1.14.1.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> counterparty;
FieldModel<bool> receiving;
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

View file

@ -1,415 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// FBE version: 1.14.1.0
//------------------------------------------------------------------------------
#include "fbe.h"
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <rpc.h>
#undef DELETE
#undef ERROR
#undef HOST_NOT_FOUND
#undef Yield
#undef min
#undef max
#undef uuid_t
#endif
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

View file

@ -1,667 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// FBE version: 1.14.1.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)
#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
#if defined(FMT_VERSION) && (FMT_VERSION >= 90000)
template <>
struct fmt::formatter<FBE::decimal_t> : formatter<std::string_view>
{
template <typename FormatContext>
auto format(const FBE::decimal_t& value, FormatContext& ctx) const
{
return formatter<string_view>::format((double)value, ctx);
}
};
#endif
template <>
struct std::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 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
#if defined(FMT_VERSION) && (FMT_VERSION >= 90000)
template <>
struct fmt::formatter<FBE::uuid_t> : formatter<std::string_view>
{
template <typename FormatContext>
auto format(const FBE::uuid_t& value, FormatContext& ctx) const
{
return formatter<string_view>::format(value.string(), ctx);
}
};
#endif
template <>
struct std::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 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

@ -1,438 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// FBE version: 1.14.1.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

@ -1,459 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// FBE version: 1.14.1.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

@ -1,637 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// FBE version: 1.14.1.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 = TKey();
TValue value = TValue();
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 = TKey();
TValue value = TValue();
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

@ -1,516 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// FBE version: 1.14.1.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

View file

@ -1,471 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// FBE version: 1.14.1.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

@ -1,689 +0,0 @@
//------------------------------------------------------------------------------
// Automatically generated by the Fast Binary Encoding compiler, do not modify!
// https://github.com/chronoxor/FastBinaryEncoding
// Source: FBE
// FBE version: 1.14.1.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 = TKey();
TValue value = TValue();
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 = TKey();
TValue value = TValue();
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

45
help.md Normal file
View file

@ -0,0 +1,45 @@
# Error Responses
| # | meaning |
| --- | ----------------- |
| -1 | UserNotFound |
| -2 | WrongPassword |
| -3 | InvalidRequest |
| -4 | NameTooLong |
| -5 | UserAlreadyExists |
| -6 | InsufficientFunds |
# Things of Note
* all endpoints respond with **JSON** file type
* "**A**" denotes requiring Authentication in the form of a header titled "**Password**"
# Usage
| Name | Path | Method | A | Description |
| :------------: | :------------------------------------- | :----: | :---: | -------------------------------------------------------------------------------------------------- |
| GetBal | BankF/{name}/bal | GET | false | returns the balance of a given user `{name}` |
| GetLog | BankF/{name}/log | GET | true | returns a list of last `n` number of transactions (a configurable amount) of a given user `{name}` |
| SendFunds | BankF/{name}/send/{to}?amount={amount} | POST | true | sends `{amount}` from user `{name}` to user `{to}` |
| VerifyPassword | BankF/{name}/pass/verify | GET | true | returns `1` if the supplied user `{name}`'s password matches the password supplied in the header |
# Meta Usage
| Name | Path | Method | A | Description |
| :------------: | :------------------------------------- | :----: | :---: | ---------------------------------------------------------------------------------------------------------------------------------------- |
| ChangePassword | BankF/{name}/pass/change | PATCH | true | if the password supplied in the header matches the user `{name}`'s password, the user's password is changed to the one given in the body |
| SetBal | BankF/admin/{name}/bal?amount={amount} | PATCH | true | sets the balance of a give user `{name}` if the supplied password matches the admin password |
# System Usage
| Name | Path | Method | A | Description |
| :-------------: | :-------------------- | :----: | :---: | ------------------------------------------------------------------------------------- |
| Help | BankF/help | GET | false | the page you're looking at right now! |
| Ping | BankF/ping | GET | false | for pinging the server to see if its online |
| Close | BankF/admin/close | POST | true | saves and then closes the program if the supplied password matches the admin password |
| Contains | BankF/contains/{name} | GET | false | returns `1` if the supplied user `{name}` exists |
| AdminVerifyPass | BankF/admin/verify | GET | true | returns `1` if the password supplied in the header matches the admin password |
# User Management
| Name | Path | Method | A | Description |
| :----------: | :------------------------------------------ | :----: | :---: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AddUser | BankF/user/{name} | POST | true | registers a user with the name `{name}`, balance of 0 and a password of the password supplied in the header |
| AdminAddUser | BankF/admin/user/{name}?init_bal={init_bal} | POST | true | if the password supplied in the header matches the admin password, then it registers a user with the name `{name}`, balance of `init_bal` and a password supplied by the body of the request |
| DelUser | BankF/user/{name} | DELETE | true | if the password supplied in the header matches the user `{name}`'s password, then the user is deleted |
| AdminDelUser | BankF/admin/user/{name} | DELETE | true | if the password supplied in the header matches the admin password, then the user is deleted |

View file

@ -1,74 +1,61 @@
#pragma once
#include <cassert>
#include <fstream>
#include <iostream>
#include <shared_mutex>
#include <parallel-hashmap/parallel_hashmap/phmap.h>
#include "bank_resp.h"
#include "error_responses.hpp"
#include "parallel-hashmap/parallel_hashmap/phmap.h"
#include "user.h"
#if (CONSERVATIVE_DISK_SAVE && MAX_LOG_SIZE < 0) && !MULTI_THREADED
#include "change_flag.h"
#endif
class Bank
{
#if MULTI_THREADED
static phmap::parallel_flat_hash_map<
private:
phmap::parallel_flat_hash_map<
std::string, User,
xxHashStringGen,
phmap::priv::hash_default_hash<std::string>,
phmap::priv::hash_default_eq<std::string>,
phmap::priv::Allocator<phmap::priv::Pair<const std::string, User>>,
4UL,
std::mutex>
users;
#else
static phmap::parallel_flat_hash_map<std::string, User, xxHashStringGen> users;
#endif
private:
#if CONSERVATIVE_DISK_SAVE
static ChangeFlag<false> save_flag;
#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;
// must grab as shared if the operation is gonna modify "users"'s size or can be caught in a intermediary state such as SendFunds()
// must grab as unique if the operation is gonna use user iterators
static std::shared_mutex iter_lock;
/**
* @brief send_funds_l should be grabbed if balances are being MODIFIED (shared) or if an operation needs to READ without the intermediary states that sendfunds has (unique)
*
*/
std::shared_mutex send_funds_l;
public:
static std::string admin_account;
std::string admin_pass;
static size_t NumOfUsers() noexcept;
static size_t NumOfLogs() noexcept;
static size_t SumBal() noexcept;
int_fast8_t AddUser(const std::string &name, const std::string &init_pass);
int_fast8_t AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass);
static BankResponse GetBal(const std::string &name) noexcept;
int_fast8_t DelUser(const std::string &name, const std::string &attempt);
int_fast8_t AdminDelUser(const std::string &name, const std::string &attempt);
#if MAX_LOG_SIZE > 0
#if USE_DEPRECATED_ENDPOINTS
static BankResponse GetLogs(const std::string &name) noexcept;
#endif
static BankResponse GetLogsV2(const std::string &name) noexcept;
static BankResponse GetLogsRange(const std::string &name, size_t n, size_t m) noexcept;
#endif
int_fast8_t SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount, const std::string &attempt);
static BankResponse SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount) noexcept;
static bool VerifyPassword(const std::string &name, const std::string_view &attempt) noexcept;
static void ChangePassword(const std::string &name, const std::string &new_pass) noexcept;
static BankResponse SetBal(const std::string &name, int64_t amount) noexcept;
static BankResponse ImpactBal(const std::string &name, int64_t amount) noexcept;
static bool Contains(const std::string &name) noexcept;
int_fast8_t Contains(const std::string &name) const;
int_fast8_t AdminVerifyPass(const std::string &attempt);
#if MAX_LOG_SIZE > 0
static BankResponse PruneUsers(time_t threshold_time, uint32_t threshold_bal) noexcept;
#else
static BankResponse PruneUsers(uint32_t threshold_bal) noexcept;
#endif
int_fast8_t SetBal(const std::string &name, const std::string &attempt, uint32_t amount);
int_fast64_t GetBal(const std::string &name) const;
static BankResponse AddUser(const std::string &name, uint32_t init_bal, const std::string &init_pass) noexcept;
static BankResponse DelUser(const std::string &name) noexcept;
static void DelSelf(const std::string &name) noexcept;
int_fast8_t VerifyPassword(const std::string &name, const std::string &attempt) const;
int_fast8_t ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass);
static const char *Save();
static void Load();
};
Json::Value GetLogs(const std::string &name, const std::string &attempt);
void Save();
//NOT THREAD SAFE, BY NO MEANS SHOULD THIS BE CALLED WHILE RECEIEVING REQUESTS
void Load();
};
//TODO make branchless

View file

@ -1,91 +0,0 @@
#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>
{
public:
static void GetBal(req_args, const std::string &name);
#if USE_DEPRECATED_ENDPOINTS
static void GetLogs(req_args);
#endif
static void GetLogsV2(req_args);
static void GetLogsRange(req_args, size_t start, size_t length);
static void SendFunds(req_args);
static void ChangePassword(req_args);
static void VerifyPassword(req_args);
static void AdminGetLogs(req_args, const std::string& name);
static void AdminChangePassword(req_args);
static void AdminVerifyAccount(req_args);
static void SetBal(req_args);
static void ImpactBal(req_args);
static void Help(req_args);
static void Close(req_args);
static void Contains(req_args, const std::string &name);
static void PruneUsers(req_args);
static void ApiProperties(req_args);
static void AddUser(req_args);
static void AdminAddUser(req_args);
static void DelSelf(req_args);
static void AdminDelUser(req_args);
METHOD_LIST_BEGIN
//Usage
METHOD_ADD(api::GetBal, "/v1/user/balance?name={name}", Get, Options, "JsonFilter<false>");
#if MAX_LOG_SIZE > 0
#if USE_DEPRECATED_ENDPOINTS
METHOD_ADD(api::GetLogs, "/v1/user/log", Get, Options, "JsonFilter<false>", "UserFilter<true, false>");
#endif
METHOD_ADD(api::GetLogsV2, "/v2/user/log", Get, Options, "JsonFilter<false>", "UserFilter<true, false>");
METHOD_ADD(api::GetLogsRange, "/v1/user/log_range?start={start}&length={length}", Get, Options, "JsonFilter<false>", "UserFilter<true, false>");
METHOD_ADD(api::AdminGetLogs, "/v1/admin/user/log?name={name}", Get, Options, "JsonFilter<false>", "UserFilter<false, true>");
#else
#if USE_DEPRECATED_ENDPOINTS
METHOD_ADD(api::GetLogs, "/v1/user/log", Get, Options, "JsonFilter<false>");
#endif
METHOD_ADD(api::GetLogsV2, "/v2/user/log", Get, Options, "JsonFilter<false>");
METHOD_ADD(api::GetLogsRange, "/v1/user/log_range?start={start}&length={length}", Get, Options, "JsonFilter<false>");
METHOD_ADD(api::AdminGetLogs, "/v1/admin/user/log?name={name}", Get, Options, "JsonFilter<false>");
#endif
METHOD_ADD(api::SendFunds, "/v1/user/transfer", Post, Options, "JsonFilter<true>", "UserFilter<true, false>"); //expects ["name"](string) and ["amount"](uint32)
METHOD_ADD(api::ChangePassword, "/v1/user/change_password", Patch, Options, "JsonFilter<true>", "UserFilter<true, false>"); //expects ["pass"](string)
METHOD_ADD(api::VerifyPassword, "/v1/user/verify_password", Post, Options, "UserFilter<false, false>", "JsonFilter<false>");
//Meta Usage
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::AdminVerifyAccount, "/v1/admin/verify_account", Post, Options, "UserFilter<false, true>", "JsonFilter<false>");
METHOD_ADD(api::SetBal, "/v1/admin/set_balance", Patch, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) and ["amount"](uint32)
METHOD_ADD(api::ImpactBal, "/v1/admin/impact_balance", Post, Options, "JsonFilter<true>", "UserFilter<false, true>"); //expects ["name"](string) and ["amount"](uint32)
//System Usage
METHOD_ADD(api::Help, "/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::PruneUsers, "/v1/admin/prune_users", Post, "UserFilter<false, true>", "JsonFilter<true>"); //expects ["time"](int64) and ["amount"](uint32)
METHOD_ADD(api::ApiProperties, "/properties", Get, Options);
//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"](uint32) ["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)
METHOD_LIST_END
};

57
include/bank_f.h Normal file
View file

@ -0,0 +1,57 @@
#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
};

View file

@ -1,12 +0,0 @@
#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);

View file

@ -1,17 +0,0 @@
#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;
};

6
include/consts.hpp Normal file
View file

@ -0,0 +1,6 @@
#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;

View file

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

View file

@ -1,14 +0,0 @@
#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>>
{
public:
virtual void doFilter(const HttpRequestPtr &,
FilterCallback &&,
FilterChainCallback &&) override;
};

View file

@ -1,31 +1,13 @@
#pragma once
#include <deque>
#include "ccash_config.hpp"
#include "change_flag.h"
#include <json/json.h>
#include <array>
#include <algorithm>
#include "consts.hpp"
#include "transaction.h"
#include "simdjson.h"
using namespace simdjson;
struct Log
{
private:
#if USE_DEPRECATED_ENDPOINTS
ChangeFlag<true> log_flag;
std::string log_snapshot = "null";
#endif
ChangeFlag<true> log_flag_v2;
std::string log_snapshot_v2 = "null";
public:
std::deque<Transaction> data;
#if USE_DEPRECATED_ENDPOINTS
std::string GetLogs(const std::string& name) noexcept;
#endif
std::string GetLogsV2() noexcept;
std::string GetLogsRange(size_t n, size_t m) noexcept;
void AddTrans(const std::string &counterparty_str, bool receiving, uint32_t amount, time_t time) noexcept;
std::vector<Transaction> data;
void AddTrans(Transaction &&t);
Json::Value Serialize() const;
};

File diff suppressed because it is too large Load diff

View file

@ -1,15 +0,0 @@
#pragma once
#include <string>
/*
The intended use of this cursed class is in violating the encapsulation of std::string this class acts like std::string_view even though its stored in a std::string.
The reason this was needed is because sometimes we have a std::string_view instance and another library requires a const std::string& argument, forcing us to copy to a string before passing it, this copying is unnecessary.
*/
struct StrFromSV_Wrapper
{
std::string str;
StrFromSV_Wrapper() noexcept;
StrFromSV_Wrapper(std::string_view sv) noexcept;
~StrFromSV_Wrapper() noexcept;
};

View file

@ -1,15 +1,15 @@
#pragma once
#include <string>
#include <chrono>
#include <cstdint>
#include <ctime>
struct Transaction
{
std::string counterparty = "";
bool receiving = false;
std::string from = "", to = "";
uint32_t amount = 0;
time_t time = 0;
uint64_t time = 0;
Transaction() noexcept;
Transaction(const std::string &counterparty_str, bool receiving, uint32_t amount, time_t time) noexcept;
Transaction();
Transaction(std::string from_str, std::string to_str, uint32_t amount, uint64_t time);
Transaction(std::string from_str, std::string to_str, uint32_t amount);
};

View file

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

View file

@ -1,16 +0,0 @@
#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>>
{
public:
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 1
#define XXH_VERSION_RELEASE 0
#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 libray was compiled.
* @return `XXH_VERSION_NUMBER` as of when the function 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(9 <= len && len <= 16);
XXH_ASSERT(8 <= 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;

View file

@ -1,9 +0,0 @@
#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;
};

151
main.cpp
View file

@ -1,124 +1,79 @@
#include <iostream>
#include <chrono>
#include <thread>
#include <sys/types.h>
#include <unistd.h>
#include "bank_api.h"
#include "bank_f.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;
static Bank bank;
void SaveSig(int s)
{
std::cout << "\nSaving on close...\n"
<< Bank::Save();
bank.Save();
std::cout << "\nSaving on close...\n";
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)
{
static_assert(MAX_LOG_SIZE >= 0);
if (argc == 1)
{
std::ofstream users_save(users_location, std::ios::out | std::ios::binary);
if (users_save.is_open())
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(argv[2]);
if (saving_freq) //if saving frequency is 0 then auto saving is turned off
{
std::thread([saving_freq]() {
while (1)
{
uint8_t temp[16]{16, 0, 0, 0, 4};
users_save.write((char *)temp, 16);
users_save.close();
std::cout << "User save file generated\n" << "Usage: sudo ./bank <admin account> <saving frequency in minutes> [daemon flag {default: false}]\n";
return 0;
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';
}
else
{
std::cerr << "File cannot be created (may already exist)\n";
return -1;
}
}
if (argc < 3)
{
std::cerr << "Usage: sudo ./bank <admin account> <saving frequency in minutes> [daemon flag {default: false}]\n";
return -1;
}
if (geteuid() != 0)
{
std::cerr << "ERROR: CCash MUST be ran as root\n";
return -1;
}
const unsigned long saving_freq = std::stoul(std::string(argv[2]));
std::cout
<< "\nAPI : v2.6.1\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() + saving_freq
<< "\nMulti threading : enabled";
#else
<< "\n\nThreads : " << 1 + saving_freq
<< "\nMulti threading : disabled";
#endif
}).detach();
}
//Loading users from users.dat
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 * (75 + sizeof(Transaction) + max_name_size)) / 1048576 << "MB" //75:cached response per log(heap), sizeof(Transaction), max_name_size:counterparty(heap)
<< "\nLoaded " << Bank::SumBal() << " C$H"
<< 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
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();
}
if (argc == 4 && !strcmp(argv[3], "true")) { app().enableRunAsDaemon(); }
else if (argc == 4 && strcmp(argv[3], "false"))
{
std::cerr << "daemon flag must be \"true\" or \"false\"\n";
return -1;
}
} //destroying setup variables
app()
.loadConfigFile(config_location)
#if MULTI_THREADED
.setThreadNum(get_nprocs())
#endif
.run();
SaveSig(0);
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.json").registerController(API).setThreadNum(std::stoul(argv[3])).run();
return 0;
}

29
services.md Normal file
View file

@ -0,0 +1,29 @@
# Connected Services
### Implemented:
- [Web Frontend](https://github.com/Expand-sys/ccashfrontend)
![image](https://user-images.githubusercontent.com/31377881/121337724-afe9fe80-c8d1-11eb-8851-23ec5e74cd26.png)
- [CC Frontend](https://github.com/Reactified/rpm/blob/main/packages/ccash-wallet)
![image](https://user-images.githubusercontent.com/31377881/121338034-fb041180-c8d1-11eb-8640-b18c141eb980.png)
- [CC Shop](https://github.com/Reactified/rpm/tree/main/packages/ccash-shop)
![image](https://user-images.githubusercontent.com/31377881/120050327-de163700-bfd1-11eb-9d5a-f75c003e867c.png)
![image](https://user-images.githubusercontent.com/31377881/120050367-09992180-bfd2-11eb-9a22-449d73c196cf.png)
- [CC Reverse ATM](https://github.com/Reactified/misc/tree/main/lua/ccash-bank) an ATM for economies allowing for an initial exchange to start up
![image](https://user-images.githubusercontent.com/31377881/121277361-4d6b1100-c885-11eb-87c8-cfebcf58da4f.png)
### In-Dev:
- [a Market](https://github.com/STBoyden/market-api-2.0)
### Ideas:
- an API in your preferred language (if not found in [APIs.md](APIs.md))
- Gambling
- Shipping
- High-level bank operations such as loans
- Some trust based system for transactions similiar to Paypal
- a better version of one of these existing ideas
- something completely different

View file

@ -1,433 +1,279 @@
#include "bank.h"
#if MULTI_THREADED
phmap::parallel_flat_hash_map<
std::string, User,
xxHashStringGen,
phmap::priv::hash_default_eq<std::string>,
phmap::priv::Allocator<phmap::priv::Pair<const std::string, User>>,
4UL,
std::mutex>
Bank::users;
#else
phmap::parallel_flat_hash_map<std::string, User, xxHashStringGen> Bank::users;
#endif
#if CONSERVATIVE_DISK_SAVE
ChangeFlag<false> Bank::save_flag;
#endif
std::shared_mutex Bank::iter_lock;
std::string Bank::admin_account;
using namespace drogon;
#if CONSERVATIVE_DISK_SAVE
#define SET_CHANGES_ON save_flag.SetChangesOn()
#else
#define SET_CHANGES_ON
#endif
inline bool ValidUsername(const std::string &name) noexcept
int_fast8_t Bank::AddUser(const std::string &name, const std::string &init_pass)
{
if (name.size() < min_name_size || name.size() > max_name_size)
if (name.size() > max_name_size)
{
return false;
return ErrorResponse::NameTooLong;
}
for (char c : name)
{
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'))
{
return false;
}
std::shared_lock<std::shared_mutex> lock{size_l};
return (users.try_emplace_l(
name, [](User &) {}, init_pass))
? true
: ErrorResponse::UserAlreadyExists;
}
return true;
}
//NOT THREAD SAFE
size_t Bank::NumOfUsers() noexcept { return Bank::users.size(); }
//NOT THREAD SAFE
size_t Bank::NumOfLogs() noexcept
int_fast8_t Bank::AdminAddUser(const std::string &attempt, std::string &&name, uint32_t init_bal, std::string &&init_pass)
{
#if MAX_LOG_SIZE > 0
size_t res = 0;
for (const auto &u : users)
if (name.size() > max_name_size)
{
res += u.second.log.data.size();
return ErrorResponse::NameTooLong;
}
return res;
#else
return 0;
#endif
}
//NOT THREAD SAFE
size_t Bank::SumBal() noexcept
{
size_t res = 0;
for (const auto &u : users)
if (admin_pass != attempt)
{
res += u.second.balance;
return ErrorResponse::WrongPassword;
}
return res;
}
BankResponse Bank::GetBal(const std::string &name) noexcept
{
uint32_t res = 0;
if (!ValidUsername(name) || !Bank::users.if_contains(name, [&res](const User &u) { res = u.balance; }))
{
return {k404NotFound, "\"User not found\""};
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)
{
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 {k200OK, std::to_string(res)};
return ErrorResponse::UserNotFound;
}
}
#if MAX_LOG_SIZE > 0
#if USE_DEPRECATED_ENDPOINTS
BankResponse Bank::GetLogs(const std::string &name) noexcept
int_fast8_t Bank::AdminDelUser(const std::string &name, const std::string &attempt)
{
BankResponse res;
if (!Bank::users.modify_if(name, [&name, &res](User &u) { res = {k200OK, u.log.GetLogs(name)}; }))
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 {k404NotFound, "\"User not found\""};
return (state) ? true : ErrorResponse::WrongPassword;
}
else
{
return res;
}
}
#endif
BankResponse Bank::GetLogsV2(const std::string &name) noexcept
{
BankResponse res;
if (!Bank::users.modify_if(name, [&name, &res](User &u) { res = {k200OK, u.log.GetLogsV2()}; }))
{
return {k404NotFound, "\"User not found\""};
}
else
{
return res;
return ErrorResponse::UserNotFound;
}
}
BankResponse Bank::GetLogsRange(const std::string &name, size_t start, size_t length) noexcept
int_fast8_t Bank::SendFunds(const std::string &a_name, const std::string &b_name, uint32_t amount, const std::string &attempt)
{
BankResponse res;
if (start >= MAX_LOG_SIZE)
//cant send money to self, from self or amount is 0
if (a_name == b_name || !amount)
{
return {k400BadRequest, "\"Invalid {start} index\""};
}
if (!length)
{
return {k400BadRequest, "\"Invalid {length}\""};
}
if (!Bank::users.modify_if(name, [&name, &res, start, length](User &u) { res = {k200OK, u.log.GetLogsRange(start, length)}; }))
{
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\""};
return ErrorResponse::InvalidRequest;
}
//as first modify_if checks a_name and grabs unique lock
if (!Contains(b_name))
{
return {k404NotFound, "\"Reciever does not exist\""};
return ErrorResponse::UserNotFound;
}
BankResponse res;
std::shared_lock<std::shared_mutex> lock{iter_lock};
#if MAX_LOG_SIZE > 0
time_t current_time = time(NULL);
if (!Bank::users.modify_if(a_name, [current_time, &a_name, &b_name, &res, amount](User &a)
#else
if (!Bank::users.modify_if(a_name, [&a_name, &b_name, &res, amount](User &a)
#endif
{
//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(b_name, false, amount, current_time);
#endif
res = {k200OK, std::to_string(a.balance)};
}
}))
int_fast8_t state = false;
{
return {k404NotFound, "\"Sender does not exist\""};
}
if (res.first == k200OK)
{
#if MAX_LOG_SIZE > 0
Bank::users.modify_if(b_name, [current_time, &a_name, &b_name, amount](User &b) {
b.balance += amount;
b.log.AddTrans(a_name, true, amount, current_time);
});
#else
Bank::users.modify_if(b_name, [amount](User &b) { b.balance += amount; });
#endif
SET_CHANGES_ON;
}
return res;
}
bool Bank::VerifyPassword(const std::string &name, const std::string_view &attempt) noexcept
{
bool res = false;
Bank::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
{
SET_CHANGES_ON;
Bank::users.modify_if(name, [&new_pass](User &u) { u.password = xxHashStringGen{}(new_pass); });
}
BankResponse Bank::SetBal(const std::string &name, int64_t amount) noexcept
{
if (ValidUsername(name) && Bank::users.modify_if(name, [&amount](User &u) {
amount -= u.balance;
u.balance += amount;
#if MAX_LOG_SIZE > 0
u.log.AddTrans("Ω", (amount > 0), std::abs(amount), time(NULL));
#endif
}))
{
SET_CHANGES_ON;
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 bal;
if (ValidUsername(name) && Bank::users.modify_if(name, [&bal, &amount](User &u) {
if (u.balance < (amount * -1)) { amount = -int64_t(u.balance); };
bal = u.balance += amount;
#if MAX_LOG_SIZE > 0
u.log.AddTrans("Ω", (amount > 0), std::abs(amount), time(NULL));
#endif
}))
{
SET_CHANGES_ON;
return {k200OK, std::to_string(bal)}; //may return new balance
}
else
{
return {k404NotFound, "\"User not found\""};
}
}
bool Bank::Contains(const std::string &name) noexcept
{
return ValidUsername(name) && Bank::users.contains(name);
}
#if MAX_LOG_SIZE > 0
BankResponse Bank::PruneUsers(time_t threshold_time, uint32_t threshold_bal) noexcept
#else
BankResponse Bank::PruneUsers(uint32_t threshold_bal) noexcept
#endif
{
std::unique_lock<std::shared_mutex> lock{iter_lock};
size_t deleted_count = 0;
#if RETURN_ON_DEL
uint32_t bal = 0;
#endif
for (const auto &u : users)
{
#if MAX_LOG_SIZE > 0
#if RETURN_ON_DEL
if (Bank::users.erase_if(u.first, [threshold_time, threshold_bal, &bal, &deleted_count](User &u) {
bal += u.balance;
#else
if (Bank::users.erase_if(u.first, [threshold_time, threshold_bal, &deleted_count](User &u) {
#endif
return ((!u.log.data.size() || u.log.data.back().time < threshold_time) && u.balance < threshold_bal);
#else
#if RETURN_ON_DEL
if (Bank::users.erase_if(u.first, [threshold_bal, &bal, &deleted_count](User &u) {
bal += u.balance;
#else
if (Bank::users.erase_if(u.first, [threshold_bal, &deleted_count](User &u) {
#endif
return (u.balance < threshold_bal);
#endif
std::shared_lock<std::shared_mutex> lock{send_funds_l}; //because SendFunds requires 3 locking operations
if (users.modify_if(a_name, [&state, amount, &attempt](User &a) {
//if A exists, A can afford it, and A's password matches
if (a.balance < amount)
{
state = ErrorResponse::InsufficientFunds;
}
else
{
if (a.password != XXH3_64bits(attempt.data(), attempt.size()))
{
state = ErrorResponse::WrongPassword;
}
else
{
a.balance -= amount;
state = true;
}
}
}))
{
SET_CHANGES_ON;
++deleted_count;
}
}
#if RETURN_ON_DEL
if (bal)
{
Bank::users.modify_if(return_account, [bal](User &u) { u.balance += bal; });
}
#endif
return {k200OK, std::to_string(deleted_count)};
}
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{iter_lock};
if (Bank::users.try_emplace_l(
name, [](User &) {}, init_bal, init_pass))
{
SET_CHANGES_ON;
return {k204NoContent, std::nullopt};
}
else
{
return {k409Conflict, "\"User already exists\""};
}
}
BankResponse Bank::DelUser(const std::string &name) noexcept
{
std::shared_lock<std::shared_mutex> lock{iter_lock};
#if RETURN_ON_DEL
uint32_t bal;
if (Bank::users.if_contains(name, [&bal](const User &u) { bal = u.balance; }) &&
bal)
{
Bank::users.modify_if(return_account, [bal](User &u) { u.balance += bal; });
}
#endif
if (ValidUsername(name) && Bank::users.erase(name))
{
SET_CHANGES_ON;
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
{
std::shared_lock<std::shared_mutex> lock{iter_lock};
#if RETURN_ON_DEL
uint32_t bal;
if (Bank::users.if_contains(name, [&bal](const User &u) { bal = u.balance; }) &&
bal)
{
Bank::users.modify_if(return_account, [bal](User &u) { u.balance += bal; });
}
#endif
SET_CHANGES_ON;
Bank::users.erase(name);
}
//ONLY EVER BEING CALLED BY SAVE THREAD OR C-INTERUPT
const char *Bank::Save()
{
#if CONSERVATIVE_DISK_SAVE
if (save_flag.GetChangeState())
{
#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(Bank::users.size());
users_copy.keys.reserve(Bank::users.size());
{
std::unique_lock<std::shared_mutex> lock{iter_lock};
for (const auto &u : users)
if (state > 0)
{
Bank::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);
});
//if B does exist
if (users.modify_if(b_name, [amount](User &b) {
b.balance += amount;
}))
{
if constexpr (max_log_size > 0)
{
Transaction temp(a_name, b_name, amount);
Transaction temp2 = temp;
users.modify_if(a_name, [&temp](User &a) {
a.log.AddTrans(std::move(temp));
});
users.modify_if(b_name, [&temp2](User &b) {
b.log.AddTrans(std::move(temp2));
});
}
return true;
}
else
{
//attempt to refund if A exist
users.modify_if(a_name, [amount](User &a) {
a.balance += amount;
});
return ErrorResponse::UserNotFound; //because had to refund transaction
}
}
else
{
return state;
}
}
FBE::bank_dom::GlobalFinalModel writer;
writer.serialize(users_copy);
assert(writer.verify() && "Data is corrupted!");
const FBE::FBEBuffer &write_buffer = writer.buffer();
users_save.write((char *)write_buffer.data(), write_buffer.size());
users_save.close();
if (!users_save.good())
else
{
throw std::invalid_argument("Error occurred at writing to save file\n");
return ErrorResponse::UserNotFound;
}
#if CONSERVATIVE_DISK_SAVE
save_flag.SetChangesOff();
return " to disk...\n";
}
}
int_fast8_t Bank::Contains(const std::string &name) const
{
return (users.contains(name)) ? true : ErrorResponse::UserNotFound;
}
int_fast8_t Bank::AdminVerifyPass(const std::string &attempt)
{
return (admin_pass == attempt) ? true : ErrorResponse::WrongPassword;
}
int_fast8_t Bank::SetBal(const std::string &name, const std::string &attempt, uint32_t amount)
{
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
{
int_fast64_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res](const User &u) {
res = u.balance;
});
return res;
}
int_fast8_t Bank::VerifyPassword(const std::string &name, const std::string &attempt) const
{
int_fast8_t res = ErrorResponse::UserNotFound;
users.if_contains(name, [&res, &attempt](const User &u) {
res = (u.password == XXH3_64bits(attempt.data(), attempt.size())) ? true : ErrorResponse::WrongPassword;
});
return res;
}
int_fast8_t Bank::ChangePassword(const std::string &name, const std::string &attempt, std::string &&new_pass)
{
int_fast8_t res = ErrorResponse::UserNotFound;
users.modify_if(name, [&res, &attempt, &new_pass](User &u) {
if (u.password != XXH3_64bits(attempt.data(), attempt.size()))
{
res = ErrorResponse::WrongPassword;
}
else
{
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)
{
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;
}
}
}))
{
return ErrorResponse::UserNotFound;
}
return res;
}
void Bank::Save()
{
Json::Value temp;
//loading info into json temp
{
std::scoped_lock<std::shared_mutex, std::shared_mutex> lock{size_l, send_funds_l};
for (const auto &u : users)
{
//we know it contains this key but we call this func to grab mutex
users.if_contains(u.first, [&temp, &u](const User &u_val) {
temp[u.first] = u_val.Serialize();
});
}
}
if (temp.isNull())
{
throw std::invalid_argument("Saving Failed\n");
}
else
{
return " no changes...\n";
std::ofstream user_save("../users.json");
Json::StreamWriterBuilder builder;
const std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
writer->write(temp, &user_save);
user_save.close();
}
#else
return " to disk...\n";
#endif
}
//NOT THREAD SAFE, BY NO MEANS SHOULD THIS BE CALLED WHILE RECEIEVING REQUESTS
void Bank::Load()
{
std::ifstream users_load(users_location, std::ios::out | std::ios::binary);
if (!users_load.is_open())
Json::CharReaderBuilder builder;
Json::Value temp;
std::ifstream user_save("../users.json");
builder["collectComments"] = true;
JSONCPP_STRING errs;
if (!parseFromStream(builder, user_save, &temp, &errs))
{
throw std::invalid_argument("Cannot find save file, to generate a new one run \"sudo ./bank\" (warning: will override file if it already exists)\n");
std::cerr << errs << '\n';
user_save.close();
throw std::invalid_argument("Parsing Failed\n");
}
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())
else
{
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)
{
Bank::users.try_emplace(users_global.keys[i], users_global.users[i]);
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()));
}
}
}
}

View file

@ -1,373 +0,0 @@
#include "bank_api.h"
#define CACHE_FOREVER resp->setExpiredTime(0)
#define CORS resp->addHeader("Access-Control-Allow-Origin", "*")
thread_local ondemand::parser parser;
#define SIMD_JSON_GEN \
simdjson::padded_string input(req->getBody()); \
ondemand::document doc;
#define RESPONSE_PARSE(R) \
auto resp = HttpResponse::newCustomHttpResponse(R); \
CORS; \
callback(resp)
#define RESPOND_TRUE \
auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k204NoContent, std::nullopt}); \
CORS; \
CACHE_FOREVER; \
callback(resp)
#define NAME_PARAM req->getParameter("name")
//Usage
void api::GetBal(req_args, const std::string &name)
{
RESPONSE_PARSE(Bank::GetBal(name));
}
#if USE_DEPRECATED_ENDPOINTS
void api::GetLogs(req_args)
{
#if MAX_LOG_SIZE > 0
RESPONSE_PARSE(Bank::GetLogs(NAME_PARAM));
#else
auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k404NotFound, "\"Logs are Disabled\""});
CORS;
CACHE_FOREVER;
callback(resp);
#endif
}
#endif
void api::GetLogsV2(req_args)
{
#if MAX_LOG_SIZE > 0
RESPONSE_PARSE(Bank::GetLogsV2(NAME_PARAM));
#else
auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k404NotFound, "\"Logs are Disabled\""});
CORS;
CACHE_FOREVER;
callback(resp);
#endif
}
void api::GetLogsRange(req_args, size_t start, size_t length)
{
#if MAX_LOG_SIZE > 0
RESPONSE_PARSE(Bank::GetLogsRange(NAME_PARAM, start, length));
#else
auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k404NotFound, "\"Logs are Disabled\""});
CORS;
CACHE_FOREVER;
callback(resp);
#endif
}
void api::AdminGetLogs(req_args, const std::string& name)
{
#if MAX_LOG_SIZE > 0
RESPONSE_PARSE(Bank::GetLogsV2(name));
#else
auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k404NotFound, "\"Logs are Disabled\""});
CORS;
CACHE_FOREVER;
callback(resp);
#endif
}
void api::SendFunds(req_args)
{
SIMD_JSON_GEN;
BankResponse res;
if (parser.iterate(input).get(doc))
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
std::string_view name;
uint64_t amount; // as simdjson lacks .get(uint32_t support)
if (doc["name"].get(name) || doc["amount"].get(amount) || (amount > std::numeric_limits<uint32_t>::max()))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper name_val(name);
res = Bank::SendFunds(NAME_PARAM, name_val.str, amount);
}
}
RESPONSE_PARSE(std::move(res));
}
void api::VerifyPassword(req_args) { RESPOND_TRUE; }
//Meta Usage
void api::ChangePassword(req_args)
{
SIMD_JSON_GEN;
BankResponse res;
if (parser.iterate(input).get(doc))
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
std::string_view pass;
if (doc["pass"].get(pass))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper pass_val(pass);
Bank::ChangePassword(NAME_PARAM, pass_val.str);
res = BankResponse{k204NoContent, std::nullopt};
}
}
RESPONSE_PARSE(std::move(res));
}
void api::AdminChangePassword(req_args)
{
SIMD_JSON_GEN;
BankResponse res;
if (parser.iterate(input).get(doc))
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
std::string_view name, pass;
if (doc["name"].get(name) || doc["pass"].get(pass))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper name_val(name);
if (Bank::Contains(name_val.str))
{
StrFromSV_Wrapper pass_val(pass);
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)
{
SIMD_JSON_GEN;
BankResponse res;
if (parser.iterate(input).get(doc))
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
std::string_view name;
uint64_t amount;
if (doc["name"].get(name) || doc["amount"].get(amount) || (amount > std::numeric_limits<uint32_t>::max()))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper name_val(name);
res = Bank::SetBal(name_val.str, amount);
}
}
RESPONSE_PARSE(std::move(res));
}
void api::ImpactBal(req_args)
{
SIMD_JSON_GEN;
BankResponse res;
if (parser.iterate(input).get(doc))
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
std::string_view name;
int64_t amount;
if (doc["name"].get(name) || doc["amount"].get(amount))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper name_val(name);
res = Bank::ImpactBal(name_val.str, amount);
}
}
RESPONSE_PARSE(std::move(res));
}
//System Usage
void api::Help(req_args)
{
auto resp = HttpResponse::newRedirectionResponse("https://github.com/EntireTwix/CCash/blob/main/README.md", k301MovedPermanently);
CACHE_FOREVER;
callback(resp);
}
void api::Close(req_args)
{
Bank::Save();
RESPOND_TRUE; //filter handles admin creds
app().quit();
}
void api::Contains(req_args, const std::string &name)
{
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)
{
RESPOND_TRUE; //filter handles admin creds
}
void api::ApiProperties(req_args)
{
std::string info = "{\"max_log\":" + std::to_string(MAX_LOG_SIZE) + ",\"add_user_open\":" + (ADD_USER_OPEN?"true":"false") + ",\"return_on_del\":" + (RETURN_ON_DEL?'\"' + std::string(return_account) + "\"":"null") + '}';
auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k200OK, std::move(info)});
CORS;
CACHE_FOREVER;
callback(resp);
}
void api::PruneUsers(req_args)
{
SIMD_JSON_GEN;
BankResponse res;
if (parser.iterate(input).get(doc))
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
uint64_t amount;
#if MAX_LOG_SIZE > 0
int64_t time;
if (doc["time"].get(time) || doc["amount"].get(amount) || (amount > std::numeric_limits<uint32_t>::max()))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
res = Bank::PruneUsers(time, amount);
}
#else
if (doc["amount"].get(amount) || (amount > std::numeric_limits<uint32_t>::max()))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
res = Bank::PruneUsers(amount);
}
#endif
}
RESPONSE_PARSE(std::move(res));
}
void api::AddUser(req_args)
{
if constexpr (ADD_USER_OPEN)
{
SIMD_JSON_GEN;
BankResponse res;
if (parser.iterate(input).get(doc))
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
std::string_view name, pass;
if (doc["name"].get(name) || doc["pass"].get(pass))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper name_val(name);
StrFromSV_Wrapper pass_val(pass);
res = Bank::AddUser(name_val.str, 0, pass_val.str);
}
}
RESPONSE_PARSE(std::move(res));
}
else
{
auto resp = HttpResponse::newCustomHttpResponse(BankResponse{k404NotFound, "\"AddUser is Disabled\""});
CORS;
CACHE_FOREVER;
callback(resp);
}
}
void api::AdminAddUser(req_args)
{
SIMD_JSON_GEN;
BankResponse res;
if (parser.iterate(input).get(doc))
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
std::string_view name;
uint64_t amount;
std::string_view pass;
if (doc["name"].get(name) || doc["amount"].get(amount) || doc["pass"].get(pass) || (amount > std::numeric_limits<uint32_t>::max()))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper name_val(name);
StrFromSV_Wrapper pass_val(pass);
res = Bank::AddUser(name_val.str, amount, pass_val.str);
}
}
RESPONSE_PARSE(std::move(res));
}
void api::DelSelf(req_args)
{
Bank::DelSelf(NAME_PARAM);
RESPOND_TRUE;
}
void api::AdminDelUser(req_args)
{
SIMD_JSON_GEN;
BankResponse res;
if (parser.iterate(input).get(doc))
{
res = BankResponse{k400BadRequest, "\"Invalid JSON\""};
}
else
{
std::string_view name;
if (doc["name"].get(name))
{
res = BankResponse{k400BadRequest, "\"Missing/Invalid JSON arg(s)\""};
}
else
{
StrFromSV_Wrapper name_val(name);
res = Bank::DelUser(name_val.str);
}
}
RESPONSE_PARSE(std::move(res));
}

121
src/bank_f.cpp Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,26 +0,0 @@
#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;
}

View file

@ -1,28 +0,0 @@
#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>;

View file

@ -1,38 +0,0 @@
#include "json_filter.h"
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,168 +1,31 @@
#include "log.h"
void Log::AddTrans(const std::string &counterparty_str, bool receiving, uint32_t amount, time_t time) noexcept
void Log::AddTrans(Transaction &&t)
{
#if USE_DEPRECATED_ENDPOINTS
log_flag.SetChangesOn();
#endif
log_flag_v2.SetChangesOn();
if (data.size() == MAX_LOG_SIZE)
if (data.size() == max_log_size) // If we hit the max size
{
data.pop_front();
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.emplace_back(counterparty_str, receiving, amount, time);
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
}
#if USE_DEPRECATED_ENDPOINTS
std::string Log::GetLogs(const std::string& name) noexcept
Json::Value Log::Serialize() const
{
if (log_flag.GetChangeState() && data.size()) // if there are changes
Json::Value res;
for (uint32_t i = 0; i < data.size(); ++i)
{
log_snapshot.resize(0);
//re-generate snapshot
size_t predicted_size = ((57 + (2 * max_name_size)) * data.size()) + 1;
if (log_snapshot.capacity() < predicted_size)
{
log_snapshot.reserve(predicted_size);
}
log_snapshot = '['; //1
for (size_t i = 0; i < data.size(); ++i)
{
log_snapshot += "{\"to\":\""; //7
log_snapshot += data[i].receiving? name : data[i].counterparty; //max_name_size?
log_snapshot += "\",\"from\":\""; //10
log_snapshot += data[i].receiving? data[i].counterparty : name; //max_name_size?
log_snapshot += "\",\"amount\":"; //11
log_snapshot += std::to_string(data[i].amount); //10?
log_snapshot += ",\"time\":"; //8
log_snapshot += std::to_string(data[i].time); //10?
log_snapshot += "},"; //2
}
log_snapshot.back() = ']';
log_flag.SetChangesOff();
res[i]["to"] = data[i].to;
res[i]["from"] = data[i].from;
res[i]["amount"] = (Json::UInt)data[i].amount;
res[i]["time"] = (Json::UInt64)data[i].time;
}
return log_snapshot;
}
#endif
std::string Log::GetLogsV2() noexcept
{
if (log_flag_v2.GetChangeState() && data.size()) // if there are changes
{
log_snapshot_v2.resize(0);
//re-generate snapshot
size_t predicted_size = ((59 + max_name_size) * data.size()) + 1;
if (log_snapshot_v2.capacity() < predicted_size)
{
log_snapshot_v2.reserve(predicted_size);
}
log_snapshot_v2 = '['; //1
for (size_t i = data.size(); i --> 0;)
{
log_snapshot_v2 += "{\"counterparty\":\""; //17
log_snapshot_v2 += data[i].counterparty; //max_name_size?
log_snapshot_v2 += "\",\"amount\":"; //11
if (!data[i].receiving) { log_snapshot_v2 += '-'; } //1
log_snapshot_v2 += std::to_string(data[i].amount); //10?
log_snapshot_v2 += ",\"time\":"; //8
log_snapshot_v2 += std::to_string(data[i].time); //10?
log_snapshot_v2 += "},"; //2
}
log_snapshot_v2.back() = ']';
log_flag_v2.SetChangesOff();
}
return log_snapshot_v2;
}
std::string Log::GetLogsRange(size_t start, size_t length) noexcept
{
if (start >= data.size()) { return "[]"; }
if (start == 0 && length == MAX_LOG_SIZE) { return log_snapshot_v2; }
if (log_flag_v2.GetChangeState() && data.size()) { GetLogsV2(); }
size_t log_index_n, i;
if (start < (0.5 * MAX_LOG_SIZE))
{
// std::cout << "a\n";
i = 0;
log_index_n = 0;
while(i < log_snapshot_v2.size())
{
if (log_index_n == start)
{
log_index_n = i;
break;
}
i += (41 + min_name_size);
while (log_snapshot_v2[i] != ',') { ++i; }
++log_index_n;
}
}
else
{
// std::cout << "b\n";
i = log_snapshot_v2.size();
log_index_n = data.size();
while(i --> 0)
{
if (log_index_n == start)
{
log_index_n = i + 1;
break;
}
i -= (41 + min_name_size);
while (log_snapshot_v2[i] != ',') { --i; }
--log_index_n;
}
}
size_t log_index_m = std::string::npos;
if ((start + length) < data.size())
{
if (length < (0.5 * MAX_LOG_SIZE))
{
// std::cout << "c\n";
log_index_m = 0;
while(i < log_snapshot_v2.size())
{
if (log_index_m == length)
{
log_index_m = i + 1;
break;
}
i += (41 + min_name_size);
while (log_snapshot_v2[i] != ',') { ++i; }
++log_index_m;
}
}
else
{
// std::cout << "d\n";
i = log_snapshot_v2.size();
log_index_m = data.size();
while(i --> 0)
{
if (log_index_m == length)
{
log_index_m = i + 1;
break;
}
i -= (41 + min_name_size);
while (log_snapshot_v2[i] != ',') { --i; }
--log_index_m;
}
}
log_index_m -= log_index_n;
}
std::string res(log_snapshot_v2.substr(log_index_n, log_index_m));
res[0] = '[';
res[res.size() - 1] = ']';
// std::cout << res << '\n';
return res;
}

File diff suppressed because it is too large Load diff

View file

@ -1,45 +0,0 @@
#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,4 +1,15 @@
#include "transaction.h"
Transaction::Transaction() noexcept {};
Transaction::Transaction(const std::string &counterparty_str, bool receiving, uint32_t amount, time_t time) noexcept : counterparty(counterparty_str), receiving(receiving), amount(amount), time(time) {}
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();
}

View file

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

View file

@ -1,75 +0,0 @@
#include "user_filter.h"
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 >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'))
{
return false;
}
}
return true;
}
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::admin_account == 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

View file

@ -1,10 +0,0 @@
#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

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

8
users.json Normal file
View file

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