mirror of
https://github.com/uroni/hs5.git
synced 2025-10-26 11:17:18 +00:00
Initial commit
This commit is contained in:
parent
895c2194b7
commit
6b300396c8
46
.clang-format
Normal file
46
.clang-format
Normal file
@ -0,0 +1,46 @@
|
||||
#
|
||||
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
#
|
||||
AccessModifierOffset: -4
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BinPackParameters: true
|
||||
ColumnLimit: 0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
DerivePointerBinding: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
IndentCaseLabels: true
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 60
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerBindsToType: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
Cpp11BracedListStyle: false
|
||||
Standard: Cpp03
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Allman
|
||||
IndentFunctionDeclarationAfterType: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInAngles: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpaceAfterControlStatementKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
ContinuationIndentWidth: 4
|
||||
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
build
|
||||
.cache
|
||||
data
|
||||
index.lmdb
|
||||
index.lmdb-lock
|
||||
test/requirements.txt
|
||||
test/.test_venv
|
||||
.vscode/settings.json
|
||||
__pycache__
|
||||
28
.vscode/launch.json
vendored
Normal file
28
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "(gdb) Attach",
|
||||
"type": "cppdbg",
|
||||
"request": "attach",
|
||||
"program": "/home/urpc/hs5/build/hs5",
|
||||
"MIMode": "gdb",
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"description": "Set Disassembly Flavor to Intel",
|
||||
"text": "-gdb-set disassembly-flavor intel",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
25
CMakeLists.txt
Normal file
25
CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(hs5 VERSION 0.1.0 LANGUAGES CXX C)
|
||||
|
||||
add_executable(hs5
|
||||
lmdb/midl.cpp
|
||||
lmdb/mdb.c
|
||||
lmdb/cppmidl.cpp
|
||||
main.cpp
|
||||
s3handler.cpp
|
||||
SingleFileStorage.cpp
|
||||
data.cpp
|
||||
os_functions.cpp
|
||||
utils.cpp )
|
||||
|
||||
#set(GCC_COVERAGE_COMPILE_FLAGS "-fcoroutines")
|
||||
|
||||
add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
|
||||
|
||||
find_package(folly CONFIG REQUIRED)
|
||||
find_package(proxygen CONFIG REQUIRED)
|
||||
find_package(zstd CONFIG REQUIRED)
|
||||
target_link_libraries(hs5 PRIVATE $<IF:$<TARGET_EXISTS:zstd::libzstd_shared>,zstd::libzstd_shared,zstd::libzstd_static> Folly::folly Folly::folly_deps Folly::follybenchmark Folly::folly_test_util proxygen::proxygen proxygen::proxygencurl proxygen::proxygenhttpserver)
|
||||
|
||||
|
||||
target_compile_features(hs5 PUBLIC cxx_std_20)
|
||||
661
COPYING
Normal file
661
COPYING
Normal file
@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
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 GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are 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.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"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.
|
||||
|
||||
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 "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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 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.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
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 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 work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero 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 that a certain numbered version of the GNU Affero 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 Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero 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.
|
||||
|
||||
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.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
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
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
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
|
||||
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
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
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,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero 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 your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
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 AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
661
LICENSE.txt
Normal file
661
LICENSE.txt
Normal file
@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
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 GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are 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.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"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.
|
||||
|
||||
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 "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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 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.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
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 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 work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero 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 that a certain numbered version of the GNU Affero 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 Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero 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.
|
||||
|
||||
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.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
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
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
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
|
||||
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
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
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,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero 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 your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
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 AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
12
README.md
12
README.md
@ -1 +1,11 @@
|
||||
# hs5
|
||||
High-performance scale-up self-hosted simple storage service (hs5)
|
||||
|
||||
* High performance: Designed to run with high performance
|
||||
|
||||
* Scale-up: Runs only on a single node. To scale it use it on a better machine. With machines with terabytes of RAM and hundreds of terabytes of storage available this might be enough for many use cases
|
||||
|
||||
* Self-hosted: You run it yourself, keeping ownership and responsibility of your data
|
||||
|
||||
* Simple: Simple to setup and run. API-compatible with AWS S3 API
|
||||
|
||||
* Storage Service: An object storage service like AWS S3
|
||||
8378
SingleFileStorage.cpp
Normal file
8378
SingleFileStorage.cpp
Normal file
File diff suppressed because it is too large
Load Diff
590
SingleFileStorage.h
Normal file
590
SingleFileStorage.h
Normal file
@ -0,0 +1,590 @@
|
||||
#pragma once
|
||||
#include <folly/io/IOBuf.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <memory.h>
|
||||
#include "lmdb/lmdb.h"
|
||||
#include <assert.h>
|
||||
#include "relaxed_atomic.h"
|
||||
#include <sys/types.h>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <shared_mutex>
|
||||
#include <folly/File.h>
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
#include <folly/io/IOBufQueue.h>
|
||||
|
||||
|
||||
using THREAD_ID = pid_t;
|
||||
|
||||
class SingleFileStorage
|
||||
{
|
||||
public:
|
||||
|
||||
struct SPunchItem
|
||||
{
|
||||
SPunchItem()
|
||||
: offset(-1), len(0) {}
|
||||
|
||||
SPunchItem(int64_t offset, int64_t len)
|
||||
: offset(offset), len(len) {}
|
||||
|
||||
bool operator<(const SPunchItem& other) const
|
||||
{
|
||||
return offset < other.offset;
|
||||
}
|
||||
|
||||
int64_t offset;
|
||||
int64_t len;
|
||||
};
|
||||
|
||||
struct Ext
|
||||
{
|
||||
Ext()
|
||||
: obj_offset(0), data_file_offset(-1), len(0) {}
|
||||
|
||||
Ext(int64_t obj_offset, int64_t data_file_offset, int64_t len)
|
||||
: obj_offset(obj_offset), data_file_offset(data_file_offset), len(len) {}
|
||||
|
||||
bool operator<(const Ext& other) const
|
||||
{
|
||||
return obj_offset < other.obj_offset;
|
||||
}
|
||||
|
||||
int64_t obj_offset;
|
||||
int64_t data_file_offset;
|
||||
int64_t len;
|
||||
};
|
||||
|
||||
enum class DelAction
|
||||
{
|
||||
Del = 0,
|
||||
DelOld = 1,
|
||||
DelWithQueued = 2,
|
||||
Queue = 3,
|
||||
Unqueue = 4,
|
||||
AssertQueueEmpty = 5
|
||||
};
|
||||
|
||||
struct SFSOptions
|
||||
{
|
||||
std::string data_path;
|
||||
std::string db_path;
|
||||
std::string freespace_cache_path;
|
||||
std::string dm_cache_path;
|
||||
int64_t dm_cache_size = 0;
|
||||
bool use_direct_io = false;
|
||||
int64_t data_file_size_limit_mb = 0;
|
||||
int64_t alloc_chunk_size = 512 * 1024 * 1024;
|
||||
std::string runtime_id;
|
||||
bool manual_commit = false;
|
||||
bool stop_on_error = false;
|
||||
bool punch_holes = true;
|
||||
};
|
||||
|
||||
SingleFileStorage(SFSOptions options);
|
||||
|
||||
//Start with dead SFS
|
||||
SingleFileStorage();
|
||||
|
||||
void operator=(const SingleFileStorage&) = delete;
|
||||
SingleFileStorage(SingleFileStorage&) = delete;
|
||||
|
||||
~SingleFileStorage();
|
||||
|
||||
static void init_mutex();
|
||||
|
||||
static void handle_mmap_read_error(void* addr);
|
||||
|
||||
struct WritePrepareResult
|
||||
{
|
||||
int err;
|
||||
std::vector<Ext> extents;
|
||||
};
|
||||
|
||||
WritePrepareResult write_prepare(const std::string& fn, size_t data_size, size_t max_data_fragments);
|
||||
|
||||
int write_ext(const Ext& ext, const void* data, size_t data_size);
|
||||
|
||||
int write_finalize(const std::string& fn, const std::vector<Ext>& extents, int64_t last_modified, const std::string& md5sum,
|
||||
bool no_del_old, bool is_fragment);
|
||||
|
||||
int write(const std::string& fn,
|
||||
const char* data, size_t data_size, int64_t last_modified, const std::string& md5sum,
|
||||
bool no_del_old, bool is_fragment, size_t max_data_fragments);
|
||||
|
||||
const static unsigned int ReadWithReadahead = 1;
|
||||
const static unsigned int ReadUnsynced = 2;
|
||||
const static unsigned int ReadMetaOnly = 4;
|
||||
|
||||
struct ReadPrepareResult
|
||||
{
|
||||
int err;
|
||||
std::vector<Ext> extents;
|
||||
int64_t total_len;
|
||||
};
|
||||
|
||||
ReadPrepareResult read_prepare(const std::string& fn, unsigned int flags);
|
||||
|
||||
struct ReadExtResult
|
||||
{
|
||||
int err;
|
||||
std::unique_ptr<folly::IOBuf> buf;
|
||||
};
|
||||
|
||||
ReadExtResult read_ext(const Ext& ext, const unsigned int flags, const size_t bufsize, folly::IOBufQueue& buf);
|
||||
|
||||
int read_finalize(const std::string& fn, const std::vector<Ext>& extents, unsigned int flags);
|
||||
|
||||
bool del(const std::string& fn, DelAction da,
|
||||
bool background_queue);
|
||||
|
||||
bool restore_old(const std::string& fn);
|
||||
|
||||
|
||||
bool commit(bool background_queue, int64_t transid) {
|
||||
return commit(background_queue, transid, 0);
|
||||
}
|
||||
|
||||
bool commit(bool background_queue, int64_t transid, int64_t disk_id);
|
||||
|
||||
bool empty_queue(bool background_queue);
|
||||
|
||||
struct IterData
|
||||
{
|
||||
MDB_txn* iter_txn;
|
||||
MDB_cursor* iter_cur;
|
||||
MDB_val iter_key;
|
||||
MDB_val iter_val;
|
||||
};
|
||||
|
||||
bool iter_start(int64_t disk_id, bool compressed, IterData& iter_data);
|
||||
|
||||
bool iter_start(bool compressed, IterData& iter_data);
|
||||
|
||||
bool iter_start(std::string fn, bool compressed, IterData& iter_data);
|
||||
|
||||
void start_debug();
|
||||
|
||||
void iter_stop(IterData& iter_data);
|
||||
|
||||
bool iter_next(IterData& iter_data);
|
||||
|
||||
bool iter_curr_val(std::string& fn, int64_t& offset, int64_t& size, std::vector<SPunchItem>& exta_exts, int64_t& last_modified, std::string& md5sum, IterData& iter_data);
|
||||
|
||||
bool iter_curr_val(std::string& fn, std::string& data, IterData& iter_data);
|
||||
|
||||
virtual void operator()();
|
||||
|
||||
int64_t get_free_space_in_data_file();
|
||||
|
||||
int64_t get_free_space_real();
|
||||
|
||||
int64_t get_total_space();
|
||||
|
||||
int64_t get_data_file_size();
|
||||
|
||||
int64_t max_free_extent(int64_t& len);
|
||||
|
||||
int64_t get_free_space_slow(bool verbose, int64_t& freespace_extents, std::vector<SPunchItem>* items);
|
||||
|
||||
bool check_len_idx();
|
||||
|
||||
using str_map = std::map<std::string, std::string>;
|
||||
|
||||
void defrag(str_map& params, relaxed_atomic<int64_t>& defrag_items);
|
||||
|
||||
std::string get_db_path() { return db_path; }
|
||||
|
||||
std::string get_cache_path() { return freespace_cache_path; }
|
||||
|
||||
bool is_write_offline() { return write_offline; }
|
||||
|
||||
bool start_thread(int64_t transid);
|
||||
|
||||
int64_t get_transid() {
|
||||
std::scoped_lock lock(mutex);
|
||||
return curr_transid;
|
||||
}
|
||||
|
||||
int64_t get_transid(int64_t disk_id);
|
||||
|
||||
std::string meminfo();
|
||||
|
||||
bool set_write_offline(bool b);
|
||||
|
||||
bool get_is_dead() { return is_dead; }
|
||||
|
||||
bool set_allow_defrag(bool b) {
|
||||
return set_allow_defrag(b, 0);
|
||||
}
|
||||
|
||||
bool set_allow_defrag(bool b, int64_t disk_id);
|
||||
|
||||
bool set_stop_defrag(bool b) { stop_defrag = b; return true; }
|
||||
|
||||
bool reset_del_log(int64_t disk_id, int64_t reset_transid);
|
||||
|
||||
bool reset_del_queue(int64_t disk_id, int64_t reset_transid);
|
||||
|
||||
int64_t get_disk_id(const std::string& uuid);
|
||||
|
||||
void migrate_thread();
|
||||
|
||||
bool start_migrate();
|
||||
|
||||
void reference();
|
||||
|
||||
void unreference();
|
||||
|
||||
static std::string decompress_filename(const std::string& fn);
|
||||
|
||||
static int64_t get_fn_disk_id(const std::string& fn);
|
||||
|
||||
static std::string remove_disk_id(const std::string& fn, size_t disk_id_size);
|
||||
|
||||
std::string freespace_stats();
|
||||
|
||||
virtual void wait_for_startup_finish();
|
||||
|
||||
std::string get_runtime_id() {
|
||||
return runtime_id;
|
||||
}
|
||||
|
||||
bool get_manual_commit() {
|
||||
return manual_commit;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
int write_int(const std::string& fn, const char* data, size_t data_size,
|
||||
int64_t last_modified, const std::string& md5sum, bool allow_defrag_lock, bool no_del_old,
|
||||
size_t max_data_fragments);
|
||||
|
||||
int64_t remove_fn(const std::string& fn,
|
||||
MDB_txn* txn, MDB_txn* freespace_txn, bool del_from_main, bool del_old, THREAD_ID tid);
|
||||
|
||||
int64_t restore_fn(const std::string& fn,
|
||||
MDB_txn* txn, MDB_txn* freespace_txn, THREAD_ID tid);
|
||||
|
||||
int64_t log_fn(const std::string& fn,
|
||||
MDB_txn* txn, THREAD_ID tid, int64_t transid);
|
||||
|
||||
int64_t add_tmp(int64_t idx, MDB_txn* txn, THREAD_ID tid, int64_t offset, int64_t len);
|
||||
|
||||
int64_t rm_tmp(int64_t idx, MDB_txn* txn, THREAD_ID tid);
|
||||
|
||||
void wait_queue(std::unique_lock<std::mutex>& lock, bool background_queue, bool defrag_check);
|
||||
|
||||
bool add_freemap_ext(MDB_txn* txn, int64_t offset, int64_t len, bool used_in_curr_trans, THREAD_ID tid);
|
||||
|
||||
bool add_freemap_ext_simple(MDB_txn* txn, int64_t offset, int64_t len, THREAD_ID tid);
|
||||
|
||||
bool find_freemap_ext(MDB_txn* txn, THREAD_ID tid, int64_t& start, int64_t& len);
|
||||
|
||||
void lock_defrag(const std::string& fn);
|
||||
|
||||
bool is_defrag_skip_item(const std::string& fn);
|
||||
|
||||
void unlock_defrag(const std::string& fn);
|
||||
|
||||
void wait_defrag(const std::string& fn, std::unique_lock<std::mutex>& lock);
|
||||
|
||||
void setup_mmap_read_error(THREAD_ID tid);
|
||||
|
||||
bool clear_mmap_read_error(THREAD_ID tid);
|
||||
|
||||
bool has_mmap_read_error_reset(THREAD_ID tid);
|
||||
|
||||
int64_t reset_del_log_fn(MDB_txn* txn, MDB_txn* freespace_txn, THREAD_ID tid, int64_t disk_id, int64_t transid);
|
||||
|
||||
int64_t reset_holes(MDB_txn* txn, MDB_txn* freespace_txn, THREAD_ID tid);
|
||||
|
||||
int64_t reset_del_queue(MDB_txn* txn, MDB_txn* freespace_txn, THREAD_ID tid, int64_t disk_id, int64_t transid);
|
||||
|
||||
void wait_startup_finished(std::unique_lock<std::mutex>& lock);
|
||||
|
||||
void free_extents(const std::vector<Ext>& extents);
|
||||
|
||||
int64_t get_really_min_space(int64_t& index_file_size);
|
||||
|
||||
int64_t get_burn_in_data_size();
|
||||
|
||||
bool do_free_minspace(MDB_txn* txn, MDB_txn* freespace_txn, THREAD_ID tid);
|
||||
|
||||
static std::mutex mmap_read_error_mutex;
|
||||
|
||||
static std::unordered_map<THREAD_ID, std::pair<bool, std::vector<uintptr_t> > > mmap_read_error_jmp;
|
||||
static std::vector<MDB_env*> mmap_dbs;
|
||||
|
||||
std::vector<uintptr_t> mmap_cleanup_addrs;
|
||||
|
||||
int64_t queue_del(const std::string& fn, MDB_txn* txn, THREAD_ID tid, int64_t transid);
|
||||
|
||||
int64_t unqueue_del(const std::string& fn, MDB_txn* txn, THREAD_ID tid);
|
||||
|
||||
void add_defrag_skip_items_queue();
|
||||
|
||||
bool open_cache_db(int64_t current_txn_id, int64_t mapsize, bool use_other, bool del_create, MDB_txn*& freespace_txn);
|
||||
|
||||
bool generate_freespace_cache(MDB_txn* source_txn, MDB_txn* dst_txncs, bool fast_gen);
|
||||
|
||||
bool freespace_check(MDB_txn* source_txn, MDB_txn* freespace_txn, bool fast_check);
|
||||
|
||||
bool clear_freespace_cache(MDB_txn* txn);
|
||||
|
||||
bool regen_datafile_free(MDB_txn* freespace_txn);
|
||||
|
||||
bool regen_free_len_idx(MDB_txn* freespace_txn);
|
||||
|
||||
class TmpMmapedPgIds
|
||||
{
|
||||
public:
|
||||
TmpMmapedPgIds();
|
||||
|
||||
~TmpMmapedPgIds();
|
||||
|
||||
|
||||
void add_pgid(size_t pgid)
|
||||
{
|
||||
if (n_pgids * sizeof(pgid) + sizeof(pgid) >= mmap_size)
|
||||
{
|
||||
std::cerr << "pgid mmap too small" << std::endl;
|
||||
abort();
|
||||
}
|
||||
memcpy(mmap_ptr + n_pgids * sizeof(pgid), &pgid, sizeof(pgid));
|
||||
++n_pgids;
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
return n_pgids;
|
||||
}
|
||||
|
||||
size_t* begin()
|
||||
{
|
||||
return reinterpret_cast<size_t*>(mmap_ptr);
|
||||
}
|
||||
|
||||
size_t* end()
|
||||
{
|
||||
return reinterpret_cast<size_t*>(mmap_ptr + n_pgids * sizeof(size_t));
|
||||
}
|
||||
|
||||
size_t& get(size_t idx)
|
||||
{
|
||||
return *(begin() + idx);
|
||||
}
|
||||
|
||||
private:
|
||||
folly::File backing_file;
|
||||
size_t n_pgids;
|
||||
size_t mmap_size;
|
||||
char* mmap_ptr;
|
||||
};
|
||||
|
||||
bool read_pgids(MDB_txn* txn, MDB_dbi dbi, THREAD_ID tid, TmpMmapedPgIds& mmap_pg_ids);
|
||||
|
||||
enum class FragAction
|
||||
{
|
||||
Add,
|
||||
Del,
|
||||
Commit,
|
||||
FindFree,
|
||||
AddNoDelOld,
|
||||
DelOld,
|
||||
RestoreOld,
|
||||
EmptyQueue,
|
||||
ReadFragInfo,
|
||||
FreeExtents,
|
||||
ResetDelLog,
|
||||
GetDiskId,
|
||||
QueueDel,
|
||||
UnqueueDel,
|
||||
DelWithQueued,
|
||||
ResetDelQueue,
|
||||
AssertDelQueueEmpty
|
||||
};
|
||||
|
||||
struct SFragInfo;
|
||||
|
||||
struct SCommitInfo
|
||||
{
|
||||
SCommitInfo()
|
||||
: commit_errors(0),
|
||||
frag_info(nullptr)
|
||||
{}
|
||||
int64_t commit_errors;
|
||||
std::condition_variable commit_done;
|
||||
int64_t new_datafile_offset;
|
||||
int64_t new_datafile_offset_end;
|
||||
SFragInfo* frag_info;
|
||||
};
|
||||
|
||||
struct SFragInfo
|
||||
{
|
||||
SFragInfo() : offset(-1), len(0),
|
||||
last_modified(0), commit_info(nullptr) {
|
||||
}
|
||||
SFragInfo(int64_t offset, int64_t len)
|
||||
: offset(offset), len(len),
|
||||
last_modified(0), commit_info(nullptr) {}
|
||||
|
||||
FragAction action;
|
||||
std::string fn;
|
||||
int64_t offset;
|
||||
int64_t len;
|
||||
int64_t last_modified;
|
||||
std::string md5sum;
|
||||
SCommitInfo* commit_info;
|
||||
std::vector<SPunchItem> extra_exts;
|
||||
};
|
||||
|
||||
std::string compress_filename(const std::string& fn);
|
||||
|
||||
SFragInfo get_frag_info(MDB_txn* txn, const std::string& fn);
|
||||
|
||||
bool generate_free_len_idx(MDB_txn* txn);
|
||||
|
||||
int64_t get_disk_id(MDB_txn * txn, THREAD_ID tid, const std::string& uuid);
|
||||
|
||||
int64_t get_disk_trans_id(MDB_txn * txn, THREAD_ID tid, int64_t disk_id);
|
||||
|
||||
bool set_disk_trans_id(MDB_txn * txn, THREAD_ID tid, int64_t disk_id, int64_t trans_id);
|
||||
|
||||
bool rewrite_npages(MDB_txn* txn, MDB_cursor* mc, THREAD_ID tid, size_t npages);
|
||||
|
||||
int put_with_rewrite(MDB_txn* txn, MDB_dbi dbi, MDB_val* tkey, MDB_val* tval, THREAD_ID tid, size_t npages);
|
||||
|
||||
void add_reading_item(const SFragInfo& fi);
|
||||
|
||||
void remove_reading_item(const std::vector<Ext>& extents);
|
||||
|
||||
void do_stop_on_error();
|
||||
|
||||
bool with_rewrite;
|
||||
|
||||
std::unordered_set<std::string> defrag_skip_items;
|
||||
bool is_defragging;
|
||||
int defrag_restart;
|
||||
std::atomic<bool> stop_defrag;
|
||||
bool allow_defrag;
|
||||
std::set<int64_t> disallow_defrag_disk_id;
|
||||
std::deque<SFragInfo> commit_queue;
|
||||
std::deque<SFragInfo> commit_background_queue;
|
||||
std::thread commit_thread_h;
|
||||
std::unordered_map<size_t, size_t> commit_items;
|
||||
|
||||
bool do_quit;
|
||||
|
||||
bool startup_finished;
|
||||
|
||||
int64_t data_file_max_size;
|
||||
int64_t data_file_offset;
|
||||
int64_t data_file_offset_end;
|
||||
int64_t data_file_free;
|
||||
std::map<int64_t, int64_t> reserved_extents;
|
||||
folly::File data_file;
|
||||
folly::File data_file_dio;
|
||||
folly::File new_data_file;
|
||||
folly::File new_data_file_dio;
|
||||
MDB_env* db_env;
|
||||
MDB_dbi dbi_main;
|
||||
MDB_dbi dbi_free;
|
||||
MDB_dbi dbi_free_len;
|
||||
MDB_dbi dbi_size;
|
||||
MDB_dbi dbi_old;
|
||||
MDB_dbi dbi_holes;
|
||||
MDB_dbi dbi_queue_del;
|
||||
MDB_env* cache_db_env;
|
||||
MDB_dbi dbi_cache_size;
|
||||
std::set<int64_t> curr_new_free_extents;
|
||||
std::set<int64_t> reading_free_skip_extents;
|
||||
std::unordered_set<std::string> defrag_items;
|
||||
std::vector<SPunchItem> curr_free_skip_extents;
|
||||
|
||||
struct ReadingItem
|
||||
{
|
||||
size_t refs = 0;
|
||||
bool free_skip = false;
|
||||
};
|
||||
|
||||
std::map<int64_t, ReadingItem> reading_items;
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable cond;
|
||||
|
||||
std::mutex datafileoffset_mutex;
|
||||
std::mutex freespace_mutex;
|
||||
|
||||
int64_t min_free_space;
|
||||
|
||||
std::string db_path;
|
||||
|
||||
std::string freespace_cache_path;
|
||||
|
||||
relaxed_atomic<bool> is_dead;
|
||||
relaxed_atomic<bool> write_offline;
|
||||
|
||||
int64_t curr_transid;
|
||||
|
||||
bool force_freespace_check;
|
||||
|
||||
bool regen_freespace_cache;
|
||||
bool sync_freespace_cache;
|
||||
|
||||
int64_t next_disk_id;
|
||||
|
||||
int64_t data_file_copy_done;
|
||||
int64_t data_file_copy_done_sync;
|
||||
int64_t data_file_copy_max;
|
||||
bool stop_data_file_copy;
|
||||
std::shared_mutex data_file_copy_mutex;
|
||||
std::thread migrate_thread_h;
|
||||
relaxed_atomic<int> references;
|
||||
|
||||
bool mdb_curr_sync;
|
||||
|
||||
std::filesystem::path data_file_path;
|
||||
|
||||
int64_t data_file_size_limit;
|
||||
int64_t alloc_chunk_size;
|
||||
|
||||
std::string runtime_id;
|
||||
bool manual_commit;
|
||||
bool stop_on_error;
|
||||
bool punch_holes;
|
||||
};
|
||||
|
||||
|
||||
class ScopedSFSRef
|
||||
{
|
||||
SingleFileStorage* sfs;
|
||||
public:
|
||||
ScopedSFSRef(SingleFileStorage* sfs)
|
||||
: sfs(sfs) {
|
||||
if(sfs!=nullptr)
|
||||
sfs->reference();
|
||||
}
|
||||
|
||||
void reset(SingleFileStorage* nsfs)
|
||||
{
|
||||
if (sfs != nullptr) sfs->unreference();
|
||||
sfs = nsfs;
|
||||
if (sfs != nullptr) sfs->reference();
|
||||
}
|
||||
|
||||
~ScopedSFSRef() {
|
||||
if (sfs != nullptr)
|
||||
sfs->unreference();
|
||||
}
|
||||
};
|
||||
933
data.cpp
Normal file
933
data.cpp
Normal file
@ -0,0 +1,933 @@
|
||||
/**
|
||||
* Copyright Martin Raiber. All Rights Reserved.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <memory.h>
|
||||
#include "data.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool is_big_endian(void)
|
||||
{
|
||||
union {
|
||||
unsigned int i;
|
||||
char c[4];
|
||||
} bint = {0x01020304};
|
||||
|
||||
return bint.c[0] == 1;
|
||||
}
|
||||
|
||||
unsigned int endian_swap(unsigned int x)
|
||||
{
|
||||
return (x>>24) |
|
||||
((x<<8) & 0x00FF0000) |
|
||||
((x>>8) & 0x0000FF00) |
|
||||
(x<<24);
|
||||
}
|
||||
|
||||
unsigned short endian_swap(unsigned short x)
|
||||
{
|
||||
return x = (x>>8) |
|
||||
(x<<8);
|
||||
}
|
||||
|
||||
std::string endian_swap_utf16(std::string str)
|
||||
{
|
||||
for(size_t i=0;i<str.size();i+=2)
|
||||
{
|
||||
unsigned short *t=(unsigned short*)&str[i];
|
||||
*t=endian_swap(*t);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
uint64_t endian_swap(uint64_t x)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (x>>56) |
|
||||
((x<<40) & 0x00FF000000000000) |
|
||||
((x<<24) & 0x0000FF0000000000) |
|
||||
((x<<8) & 0x000000FF00000000) |
|
||||
((x>>8) & 0x00000000FF000000) |
|
||||
((x>>24) & 0x0000000000FF0000) |
|
||||
((x>>40) & 0x000000000000FF00) |
|
||||
(x<<56);
|
||||
#else
|
||||
return (x>>56) |
|
||||
((x<<40) & 0x00FF000000000000LLU) |
|
||||
((x<<24) & 0x0000FF0000000000LLU) |
|
||||
((x<<8) & 0x000000FF00000000LLU) |
|
||||
((x>>8) & 0x00000000FF000000LLU) |
|
||||
((x>>24) & 0x0000000000FF0000LLU) |
|
||||
((x>>40) & 0x000000000000FF00LLU) |
|
||||
(x<<56);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int little_endian(unsigned int x)
|
||||
{
|
||||
if(is_big_endian())
|
||||
{
|
||||
return endian_swap(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short little_endian(unsigned short x)
|
||||
{
|
||||
if(is_big_endian())
|
||||
{
|
||||
return endian_swap(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
int little_endian(int x)
|
||||
{
|
||||
if(is_big_endian())
|
||||
{
|
||||
return static_cast<int>(endian_swap(static_cast<unsigned int>(x)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t little_endian(uint64_t x)
|
||||
{
|
||||
if(is_big_endian())
|
||||
{
|
||||
return endian_swap(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t little_endian(int64_t x)
|
||||
{
|
||||
if(is_big_endian())
|
||||
{
|
||||
return static_cast<int64_t>(endian_swap(static_cast<uint64_t>(x)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
float little_endian(float x)
|
||||
{
|
||||
if(is_big_endian())
|
||||
{
|
||||
unsigned int* ptr=reinterpret_cast<unsigned int*>(&x);
|
||||
unsigned int ret = endian_swap(*ptr);
|
||||
return *reinterpret_cast<float*>(&ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
double little_endian(double x)
|
||||
{
|
||||
if (is_big_endian())
|
||||
{
|
||||
uint64_t* ptr = reinterpret_cast<uint64_t*>(&x);
|
||||
uint64_t ret = endian_swap(*ptr);
|
||||
return *reinterpret_cast<double*>(&ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int big_endian(unsigned int x)
|
||||
{
|
||||
if(!is_big_endian())
|
||||
{
|
||||
return endian_swap(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short big_endian(unsigned short x)
|
||||
{
|
||||
if(!is_big_endian())
|
||||
{
|
||||
return endian_swap(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
int big_endian(int x)
|
||||
{
|
||||
if(!is_big_endian())
|
||||
{
|
||||
return static_cast<int>(endian_swap(static_cast<unsigned int>(x)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t big_endian(uint64_t x)
|
||||
{
|
||||
if(!is_big_endian())
|
||||
{
|
||||
return endian_swap(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t big_endian(int64_t x)
|
||||
{
|
||||
if(!is_big_endian())
|
||||
{
|
||||
return static_cast<int64_t>(endian_swap(static_cast<uint64_t>(x)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
float big_endian(float x)
|
||||
{
|
||||
if(!is_big_endian())
|
||||
{
|
||||
unsigned int* ptr=reinterpret_cast<unsigned int*>(&x);
|
||||
unsigned int ret = endian_swap(*ptr);
|
||||
return *reinterpret_cast<float*>(&ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
std::string big_endian_utf16(std::string str)
|
||||
{
|
||||
if(!is_big_endian())
|
||||
{
|
||||
return endian_swap_utf16(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
//FROM SQLITE
|
||||
|
||||
typedef uint64_t u64;
|
||||
typedef unsigned char u8;
|
||||
typedef uint32_t u32;
|
||||
|
||||
/*
|
||||
** Return the number of bytes that will be needed to store the given
|
||||
** 64-bit integer.
|
||||
*/
|
||||
int sqlite3VarintLen(u64 v){
|
||||
int i;
|
||||
for(i=1; (v >>= 7)!=0; i++)
|
||||
{
|
||||
assert( i<10 );
|
||||
}
|
||||
|
||||
if (i == 10)
|
||||
{
|
||||
return 9;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Bitmasks used by sqlite3GetVarint(). These precomputed constants
|
||||
** are defined here rather than simply putting the constant expressions
|
||||
** inline in order to work around bugs in the RVT compiler.
|
||||
**
|
||||
** SLOT_2_0 A mask for (0x7f<<14) | 0x7f
|
||||
**
|
||||
** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0
|
||||
*/
|
||||
#define SLOT_2_0 0x001fc07f
|
||||
#define SLOT_4_2_0 0xf01fc07f
|
||||
|
||||
|
||||
/*
|
||||
** Read a 64-bit variable-length integer from memory starting at p[0].
|
||||
** Return the number of bytes read. The value is stored in *v.
|
||||
*/
|
||||
u8 sqlite3GetVarint(const unsigned char *p, u64 *v, size_t max_length){
|
||||
u32 a,b,s;
|
||||
|
||||
if(max_length==0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
a = *p;
|
||||
/* a: p0 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
*v = a;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(max_length<=1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
b = *p;
|
||||
/* b: p1 (unmasked) */
|
||||
if (!(b&0x80))
|
||||
{
|
||||
a &= 0x7f;
|
||||
a = a<<7;
|
||||
a |= b;
|
||||
*v = a;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Verify that constants are precomputed correctly */
|
||||
assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) );
|
||||
assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) );
|
||||
|
||||
if(max_length<=2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
a = a<<14;
|
||||
a |= *p;
|
||||
/* a: p0<<14 | p2 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
a &= SLOT_2_0;
|
||||
b &= 0x7f;
|
||||
b = b<<7;
|
||||
a |= b;
|
||||
*v = a;
|
||||
return 3;
|
||||
}
|
||||
|
||||
/* CSE1 from below */
|
||||
a &= SLOT_2_0;
|
||||
|
||||
if(max_length<=3)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
b = b<<14;
|
||||
b |= *p;
|
||||
/* b: p1<<14 | p3 (unmasked) */
|
||||
if (!(b&0x80))
|
||||
{
|
||||
b &= SLOT_2_0;
|
||||
/* moved CSE1 up */
|
||||
/* a &= (0x7f<<14)|(0x7f); */
|
||||
a = a<<7;
|
||||
a |= b;
|
||||
*v = a;
|
||||
return 4;
|
||||
}
|
||||
|
||||
/* a: p0<<14 | p2 (masked) */
|
||||
/* b: p1<<14 | p3 (unmasked) */
|
||||
/* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
|
||||
/* moved CSE1 up */
|
||||
/* a &= (0x7f<<14)|(0x7f); */
|
||||
b &= SLOT_2_0;
|
||||
s = a;
|
||||
/* s: p0<<14 | p2 (masked) */
|
||||
|
||||
if(max_length<=4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
a = a<<14;
|
||||
a |= *p;
|
||||
/* a: p0<<28 | p2<<14 | p4 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
/* we can skip these cause they were (effectively) done above in calc'ing s */
|
||||
/* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
|
||||
/* b &= (0x7f<<14)|(0x7f); */
|
||||
b = b<<7;
|
||||
a |= b;
|
||||
s = s>>18;
|
||||
*v = ((u64)s)<<32 | a;
|
||||
return 5;
|
||||
}
|
||||
|
||||
/* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
|
||||
s = s<<7;
|
||||
s |= b;
|
||||
/* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
|
||||
|
||||
if(max_length<=5)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
b = b<<14;
|
||||
b |= *p;
|
||||
/* b: p1<<28 | p3<<14 | p5 (unmasked) */
|
||||
if (!(b&0x80))
|
||||
{
|
||||
/* we can skip this cause it was (effectively) done above in calc'ing s */
|
||||
/* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
|
||||
a &= SLOT_2_0;
|
||||
a = a<<7;
|
||||
a |= b;
|
||||
s = s>>18;
|
||||
*v = ((u64)s)<<32 | a;
|
||||
return 6;
|
||||
}
|
||||
|
||||
if(max_length<=6)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
a = a<<14;
|
||||
a |= *p;
|
||||
/* a: p2<<28 | p4<<14 | p6 (unmasked) */
|
||||
if (!(a&0x80))
|
||||
{
|
||||
a &= SLOT_4_2_0;
|
||||
b &= SLOT_2_0;
|
||||
b = b<<7;
|
||||
a |= b;
|
||||
s = s>>11;
|
||||
*v = ((u64)s)<<32 | a;
|
||||
return 7;
|
||||
}
|
||||
|
||||
/* CSE2 from below */
|
||||
a &= SLOT_2_0;
|
||||
|
||||
if(max_length<=7)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
b = b<<14;
|
||||
b |= *p;
|
||||
/* b: p3<<28 | p5<<14 | p7 (unmasked) */
|
||||
if (!(b&0x80))
|
||||
{
|
||||
b &= SLOT_4_2_0;
|
||||
/* moved CSE2 up */
|
||||
/* a &= (0x7f<<14)|(0x7f); */
|
||||
a = a<<7;
|
||||
a |= b;
|
||||
s = s>>4;
|
||||
*v = ((u64)s)<<32 | a;
|
||||
return 8;
|
||||
}
|
||||
|
||||
if(max_length<=8)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
a = a<<15;
|
||||
a |= *p;
|
||||
/* a: p4<<29 | p6<<15 | p8 (unmasked) */
|
||||
|
||||
/* moved CSE2 up */
|
||||
/* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */
|
||||
b &= SLOT_2_0;
|
||||
b = b<<8;
|
||||
a |= b;
|
||||
|
||||
s = s<<4;
|
||||
b = p[-4];
|
||||
b &= 0x7f;
|
||||
b = b>>3;
|
||||
s |= b;
|
||||
|
||||
*v = ((u64)s)<<32 | a;
|
||||
|
||||
return 9;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a 64-bit variable-length integer to memory starting at p[0].
|
||||
** The length of data write will be between 1 and 9 bytes. The number
|
||||
** of bytes written is returned.
|
||||
**
|
||||
** A variable-length integer consists of the lower 7 bits of each byte
|
||||
** for all bytes that have the 8th bit set and one byte with the 8th
|
||||
** bit clear. Except, if we get to the 9th byte, it stores the full
|
||||
** 8 bits and is the last byte.
|
||||
*/
|
||||
static int putVarint64(unsigned char *p, u64 v){
|
||||
int i, j, n;
|
||||
u8 buf[10];
|
||||
if( v & (((u64)0xff000000)<<32) ){
|
||||
p[8] = (u8)v;
|
||||
v >>= 8;
|
||||
for(i=7; i>=0; i--){
|
||||
p[i] = (u8)((v & 0x7f) | 0x80);
|
||||
v >>= 7;
|
||||
}
|
||||
return 9;
|
||||
}
|
||||
n = 0;
|
||||
do{
|
||||
buf[n++] = (u8)((v & 0x7f) | 0x80);
|
||||
v >>= 7;
|
||||
}while( v!=0 );
|
||||
buf[0] &= 0x7f;
|
||||
assert( n<=9 );
|
||||
for(i=0, j=n-1; j>=0; j--, i++){
|
||||
p[i] = buf[j];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int sqlite3PutVarint(unsigned char *p, u64 v){
|
||||
if( v<=0x7f ){
|
||||
p[0] = v&0x7f;
|
||||
return 1;
|
||||
}
|
||||
if( v<=0x3fff ){
|
||||
p[0] = ((v>>7)&0x7f)|0x80;
|
||||
p[1] = v&0x7f;
|
||||
return 2;
|
||||
}
|
||||
return putVarint64(p,v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char* CWData::getDataPtr(void)
|
||||
{
|
||||
if(data.size()>0)
|
||||
return &data[0];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned long CWData::getDataSize(void)
|
||||
{
|
||||
return (unsigned long)data.size();
|
||||
}
|
||||
|
||||
void CWData::addInt(int ta)
|
||||
{
|
||||
ta=little_endian(ta);
|
||||
data.insert(data.end(), reinterpret_cast<char*>(&ta), reinterpret_cast<char*>(&ta)+sizeof(int));
|
||||
}
|
||||
|
||||
void CWData::addUInt(unsigned int ta)
|
||||
{
|
||||
ta=little_endian(ta);
|
||||
data.insert(data.end(), reinterpret_cast<char*>(&ta), reinterpret_cast<char*>(&ta) + sizeof(unsigned int));
|
||||
}
|
||||
|
||||
void CWData::addInt64(int64_t ta)
|
||||
{
|
||||
ta=little_endian(ta);
|
||||
data.insert(data.end(), reinterpret_cast<char*>(&ta), reinterpret_cast<char*>(&ta) + sizeof(int64_t));
|
||||
}
|
||||
|
||||
void CWData::addUInt64(uint64_t ta)
|
||||
{
|
||||
ta=little_endian(ta);
|
||||
data.insert(data.end(), reinterpret_cast<char*>(&ta), reinterpret_cast<char*>(&ta) + sizeof(uint64_t));
|
||||
}
|
||||
|
||||
void CWData::addFloat(float ta)
|
||||
{
|
||||
ta=little_endian(ta);
|
||||
data.insert(data.end(), reinterpret_cast<char*>(&ta), reinterpret_cast<char*>(&ta) + sizeof(float));
|
||||
}
|
||||
|
||||
void CWData::addDouble(double ta)
|
||||
{
|
||||
ta = little_endian(ta);
|
||||
data.insert(data.end(), reinterpret_cast<char*>(&ta), reinterpret_cast<char*>(&ta) + sizeof(double));
|
||||
}
|
||||
|
||||
void CWData::addUShort(unsigned short ta)
|
||||
{
|
||||
ta=little_endian(ta);
|
||||
data.insert(data.end(), reinterpret_cast<char*>(&ta), reinterpret_cast<char*>(&ta) + sizeof(unsigned short));
|
||||
}
|
||||
|
||||
void CWData::addString(const std::string& ta)
|
||||
{
|
||||
addUInt(static_cast<unsigned int>(ta.size()));
|
||||
if(!ta.empty())
|
||||
{
|
||||
data.insert(data.end(), ta.begin(), ta.end());
|
||||
}
|
||||
}
|
||||
|
||||
void CWData::addString2(const std::string& ta)
|
||||
{
|
||||
addVarInt(ta.size());
|
||||
if (!ta.empty())
|
||||
{
|
||||
data.insert(data.end(), ta.begin(), ta.end());
|
||||
}
|
||||
}
|
||||
|
||||
void CWData::addChar(char ta)
|
||||
{
|
||||
data.insert(data.end(), ta);
|
||||
}
|
||||
|
||||
void CWData::addUChar(unsigned char ta)
|
||||
{
|
||||
data.insert(data.end(), static_cast<char>(ta));
|
||||
}
|
||||
|
||||
void CWData::addVoidPtr(void* ta)
|
||||
{
|
||||
data.insert(data.end(), reinterpret_cast<char*>(&ta), reinterpret_cast<char*>(&ta) + sizeof(void*));
|
||||
}
|
||||
|
||||
void CWData::addBuffer(const char* buffer, size_t bsize)
|
||||
{
|
||||
data.insert(data.end(), buffer, buffer + bsize);
|
||||
}
|
||||
|
||||
void CWData::clear()
|
||||
{
|
||||
data.clear();
|
||||
}
|
||||
|
||||
void CWData::reserve(size_t count)
|
||||
{
|
||||
data.reserve(count);
|
||||
}
|
||||
|
||||
void CWData::resize(size_t count)
|
||||
{
|
||||
data.resize(count);
|
||||
}
|
||||
|
||||
size_t CWData::capacity()
|
||||
{
|
||||
return data.capacity();
|
||||
}
|
||||
|
||||
void CWData::addVarInt( int64_t ta )
|
||||
{
|
||||
size_t cpos=data.size();
|
||||
int needed_bytes = sqlite3VarintLen(static_cast<u64>(ta));
|
||||
data.resize(cpos+needed_bytes);
|
||||
int p = sqlite3PutVarint(reinterpret_cast<unsigned char*>(&data[cpos]), ta);
|
||||
assert(p==needed_bytes);
|
||||
}
|
||||
|
||||
CRData::CRData(const char* c,size_t datalength, bool pCopy)
|
||||
{
|
||||
data=NULL;
|
||||
set(c,datalength, pCopy);
|
||||
}
|
||||
|
||||
CRData::CRData(void)
|
||||
{
|
||||
data=NULL;
|
||||
streampos=0;
|
||||
datalen=0;
|
||||
}
|
||||
|
||||
void CRData::set(const char* c,size_t datalength, bool pCopy)
|
||||
{
|
||||
datalen = (std::min)(size_t(100*1024*1024), datalength);
|
||||
|
||||
copy=pCopy;
|
||||
if( copy==false )
|
||||
{
|
||||
data=c;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( data!=NULL )
|
||||
delete [] data;
|
||||
data=new char[datalen];
|
||||
memcpy(const_cast<char*>(data), c, datalen);
|
||||
}
|
||||
streampos=0;
|
||||
}
|
||||
|
||||
CRData::CRData(const std::string *str)
|
||||
{
|
||||
set(str->c_str(), str->size(), false);
|
||||
}
|
||||
|
||||
CRData::~CRData()
|
||||
{
|
||||
if( copy )
|
||||
delete []data;
|
||||
}
|
||||
|
||||
bool CRData::getInt(int *ret)
|
||||
{
|
||||
if(streampos+sizeof(int)>datalen )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ret, &data[streampos], sizeof(int) );
|
||||
streampos+=sizeof(int);
|
||||
*ret=little_endian(*ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getInt64(int64_t *ret)
|
||||
{
|
||||
if(streampos+sizeof(int64_t)>datalen )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ret, &data[streampos], sizeof(int64_t) );
|
||||
streampos+=sizeof(int64_t);
|
||||
*ret=little_endian(*ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getUInt(unsigned int *ret)
|
||||
{
|
||||
if(streampos+sizeof(unsigned int )>datalen )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ret, &data[streampos], sizeof(unsigned int ) );
|
||||
streampos+=sizeof(unsigned int);
|
||||
*ret=little_endian(*ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getFloat(float *ret)
|
||||
{
|
||||
if(streampos+sizeof(float)>datalen )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ret, &data[streampos], sizeof(float) );
|
||||
streampos+=sizeof(float);
|
||||
*ret=little_endian(*ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getDouble(double * ret)
|
||||
{
|
||||
if (streampos + sizeof(double)>datalen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ret, &data[streampos], sizeof(double));
|
||||
streampos += sizeof(double);
|
||||
*ret = little_endian(*ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getUShort( unsigned short *ret)
|
||||
{
|
||||
if(streampos+sizeof(unsigned short)>datalen )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ret, &data[streampos], sizeof(unsigned short) );
|
||||
streampos+=sizeof(unsigned short);
|
||||
*ret=little_endian(*ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getStr(std::string *ret)
|
||||
{
|
||||
unsigned int strlen;
|
||||
if (!getUInt(&strlen))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(strlen>10*1024*1024)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(streampos+strlen>datalen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(strlen>0)
|
||||
{
|
||||
ret->assign(&data[streampos], strlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret->clear();
|
||||
}
|
||||
streampos+=strlen;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getStr2(std::string *ret)
|
||||
{
|
||||
int64_t strlen;
|
||||
if (!getVarInt(&strlen))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen>10 * 1024 * 1024
|
||||
|| strlen<0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (streampos + strlen>datalen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen>0)
|
||||
{
|
||||
ret->assign(&data[streampos], strlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret->clear();
|
||||
}
|
||||
streampos += strlen;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getChar(char *ret)
|
||||
{
|
||||
if(streampos+sizeof(char)>datalen )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
(*ret)=data[streampos];
|
||||
streampos+=sizeof(char);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getUChar(unsigned char *ret)
|
||||
{
|
||||
if(streampos+sizeof(unsigned char)>datalen )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
(*ret)=data[streampos];
|
||||
streampos+=sizeof(unsigned char);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getVoidPtr(void **ret)
|
||||
{
|
||||
if(streampos+sizeof(void*)>datalen )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(ret, &data[streampos], sizeof(void*) );
|
||||
streampos+=sizeof(void*);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRData::getVarInt( int64_t* ret )
|
||||
{
|
||||
u8 b = sqlite3GetVarint(reinterpret_cast<const unsigned char*>(&data[streampos]), reinterpret_cast<u64*>(ret), getLeft());
|
||||
streampos+=b;
|
||||
return b!=0;
|
||||
}
|
||||
|
||||
unsigned int CRData::getSize(void)
|
||||
{
|
||||
return static_cast<unsigned int>(datalen);
|
||||
}
|
||||
|
||||
unsigned int CRData::getLeft(void)
|
||||
{
|
||||
return static_cast<unsigned int>(datalen - streampos);
|
||||
}
|
||||
|
||||
unsigned int CRData::getStreampos(void)
|
||||
{
|
||||
return static_cast<unsigned int>(streampos);
|
||||
}
|
||||
|
||||
const char *CRData::getDataPtr(void)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
const char *CRData::getCurrDataPtr(void)
|
||||
{
|
||||
return data+streampos;
|
||||
}
|
||||
|
||||
void CRData::setStreampos(unsigned int spos)
|
||||
{
|
||||
if( spos <= datalen )
|
||||
{
|
||||
streampos=spos;
|
||||
}
|
||||
}
|
||||
|
||||
bool CRData::incrementPtr(unsigned int amount)
|
||||
{
|
||||
if(amount>getLeft())
|
||||
return false;
|
||||
|
||||
streampos+=amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
81
data.h
Normal file
81
data.h
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef DATA_H_
|
||||
#define DATA_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
class CWData
|
||||
{
|
||||
public:
|
||||
char* getDataPtr(void);
|
||||
unsigned long getDataSize(void);
|
||||
|
||||
void addInt(int ta);
|
||||
void addUInt(unsigned int ta);
|
||||
void addInt64(int64_t ta);
|
||||
void addUInt64(uint64_t ta);
|
||||
void addFloat(float ta);
|
||||
void addDouble(double ta);
|
||||
void addUShort(unsigned short ta);
|
||||
void addString(const std::string& ta);
|
||||
void addString2(const std::string& ta);
|
||||
void addChar(char ta);
|
||||
void addUChar(unsigned char ta);
|
||||
void addVoidPtr(void *ptr);
|
||||
void addBuffer(const char* buffer, size_t bsize);
|
||||
void addVarInt(int64_t ta);
|
||||
|
||||
void clear();
|
||||
|
||||
void reserve(size_t count);
|
||||
void resize(size_t count);
|
||||
|
||||
size_t capacity();
|
||||
protected:
|
||||
std::string data;
|
||||
};
|
||||
|
||||
class CRData
|
||||
{
|
||||
public:
|
||||
CRData(const char* c,size_t datalength, bool pCopy=false);
|
||||
CRData(const std::string *str);
|
||||
CRData(void);
|
||||
~CRData();
|
||||
|
||||
void set(const char* c,size_t datalength, bool pCopy=false);
|
||||
|
||||
bool getInt(int *ret);
|
||||
bool getInt64(int64_t *ret);
|
||||
bool getUInt(unsigned int *ret);
|
||||
bool getFloat(float *ret);
|
||||
bool getDouble(double *ret);
|
||||
bool getUShort( unsigned short *ret);
|
||||
bool getStr(std::string *ret);
|
||||
bool getStr2(std::string *ret);
|
||||
bool getChar(char *ret);
|
||||
bool getUChar(unsigned char *ret);
|
||||
bool getVoidPtr(void **ret);
|
||||
bool getVarInt(int64_t* ret);
|
||||
|
||||
unsigned int getSize(void);
|
||||
unsigned int getLeft(void);
|
||||
unsigned int getStreampos(void);
|
||||
void setStreampos(unsigned int spos);
|
||||
const char *getDataPtr(void);
|
||||
const char *getCurrDataPtr(void);
|
||||
bool incrementPtr(unsigned int amount);
|
||||
|
||||
private:
|
||||
|
||||
const char* data;
|
||||
size_t streampos;
|
||||
size_t datalen;
|
||||
|
||||
bool copy;
|
||||
};
|
||||
|
||||
|
||||
#endif //DATA_H_
|
||||
20
lmdb/COPYRIGHT
Normal file
20
lmdb/COPYRIGHT
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright 2011-2019 Howard Chu, Symas Corp.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted only as authorized by the OpenLDAP
|
||||
Public License.
|
||||
|
||||
A copy of this license is available in the file LICENSE in the
|
||||
top-level directory of the distribution or, alternatively, at
|
||||
<http://www.OpenLDAP.org/license.html>.
|
||||
|
||||
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
||||
|
||||
Individual files and/or contributed packages may be copyright by
|
||||
other parties and/or subject to additional restrictions.
|
||||
|
||||
This work also contains materials derived from public sources.
|
||||
|
||||
Additional information about OpenLDAP can be obtained at
|
||||
<http://www.openldap.org/>.
|
||||
47
lmdb/LICENSE
Normal file
47
lmdb/LICENSE
Normal file
@ -0,0 +1,47 @@
|
||||
The OpenLDAP Public License
|
||||
Version 2.8, 17 August 2003
|
||||
|
||||
Redistribution and use of this software and associated documentation
|
||||
("Software"), with or without modification, are permitted provided
|
||||
that the following conditions are met:
|
||||
|
||||
1. Redistributions in source form must retain copyright statements
|
||||
and notices,
|
||||
|
||||
2. Redistributions in binary form must reproduce applicable copyright
|
||||
statements and notices, this list of conditions, and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution, and
|
||||
|
||||
3. Redistributions must contain a verbatim copy of this document.
|
||||
|
||||
The OpenLDAP Foundation may revise this license from time to time.
|
||||
Each revision is distinguished by a version number. You may use
|
||||
this Software under terms of this license revision or under the
|
||||
terms of any subsequent revision of the license.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
|
||||
CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
|
||||
OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The names of the authors and copyright holders must not be used in
|
||||
advertising or otherwise to promote the sale, use or other dealing
|
||||
in this Software without specific, written prior permission. Title
|
||||
to copyright in this Software shall at all times remain with copyright
|
||||
holders.
|
||||
|
||||
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
||||
|
||||
Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
|
||||
California, USA. All Rights Reserved. Permission to copy and
|
||||
distribute verbatim copies of this document is granted.
|
||||
71
lmdb/cppmidl.cpp
Normal file
71
lmdb/cppmidl.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "cppmidl.h"
|
||||
#include <set>
|
||||
|
||||
struct _I_CPPMIDL
|
||||
{
|
||||
std::set<MDB_ID> ids;
|
||||
std::set<MDB_ID>::iterator it;
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
struct _CPPMIDL
|
||||
{
|
||||
struct _I_CPPMIDL i;
|
||||
};
|
||||
}
|
||||
|
||||
CPPMIDL cppmidl_alloc()
|
||||
{
|
||||
return new _CPPMIDL;
|
||||
}
|
||||
|
||||
const MDB_ID* cppmidl_search(CPPMIDL ids, MDB_ID id)
|
||||
{
|
||||
std::set<MDB_ID>::iterator it = ids->i.ids.lower_bound(id);
|
||||
if (it == ids->i.ids.end())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return &(*it);
|
||||
}
|
||||
|
||||
void cppmidl_erase(CPPMIDL ids, MDB_ID id)
|
||||
{
|
||||
ids->i.ids.erase(id);
|
||||
}
|
||||
|
||||
int cppmidl_empty(CPPMIDL ids)
|
||||
{
|
||||
return ids->i.ids.empty() ? 1 : 0;
|
||||
}
|
||||
|
||||
void cppmidl_begin(CPPMIDL ids)
|
||||
{
|
||||
ids->i.it = ids->i.ids.begin();
|
||||
}
|
||||
|
||||
const MDB_ID* cppmidl_next(CPPMIDL ids)
|
||||
{
|
||||
if (ids->i.it == ids->i.ids.end())
|
||||
return NULL;
|
||||
|
||||
const MDB_ID* ret = &(*ids->i.it);
|
||||
++ids->i.it;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cppmidl_free(CPPMIDL ids)
|
||||
{
|
||||
delete ids;
|
||||
}
|
||||
|
||||
void cppmidl_insert(CPPMIDL ids, MDB_ID id)
|
||||
{
|
||||
ids->i.ids.insert(id);
|
||||
}
|
||||
|
||||
void cppmidl_insert_list(CPPMIDL ids, CPPMIDL other)
|
||||
{
|
||||
ids->i.ids.insert(other->i.ids.begin(), other->i.ids.end());
|
||||
}
|
||||
36
lmdb/cppmidl.h
Normal file
36
lmdb/cppmidl.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef size_t MDB_ID;
|
||||
|
||||
struct _CPPMIDL;
|
||||
typedef struct _CPPMIDL* CPPMIDL;
|
||||
|
||||
struct _CPPMIDL_IT;
|
||||
typedef struct __CPPMIDL_IT* CPPMIDL_IT;
|
||||
|
||||
CPPMIDL cppmidl_alloc();
|
||||
|
||||
const MDB_ID* cppmidl_search(CPPMIDL ids, MDB_ID id);
|
||||
|
||||
void cppmidl_erase(CPPMIDL ids, MDB_ID id);
|
||||
|
||||
int cppmidl_empty(CPPMIDL ids);
|
||||
|
||||
void cppmidl_begin(CPPMIDL ids);
|
||||
|
||||
const MDB_ID* cppmidl_next(CPPMIDL ids);
|
||||
|
||||
void cppmidl_free(CPPMIDL ids);
|
||||
|
||||
void cppmidl_insert_list(CPPMIDL ids, CPPMIDL other);
|
||||
|
||||
void cppmidl_insert(CPPMIDL ids, MDB_ID id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
1639
lmdb/lmdb.h
Normal file
1639
lmdb/lmdb.h
Normal file
File diff suppressed because it is too large
Load Diff
10488
lmdb/mdb.c
Normal file
10488
lmdb/mdb.c
Normal file
File diff suppressed because it is too large
Load Diff
359
lmdb/midl.cpp
Normal file
359
lmdb/midl.cpp
Normal file
@ -0,0 +1,359 @@
|
||||
/** @file midl.c
|
||||
* @brief ldap bdb back-end ID List functions */
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2000-2019 The OpenLDAP Foundation.
|
||||
* Portions Copyright 2001-2018 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include "midl.h"
|
||||
|
||||
/** @defgroup internal LMDB Internals
|
||||
* @{
|
||||
*/
|
||||
/** @defgroup idls ID List Management
|
||||
* @{
|
||||
*/
|
||||
#define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) )
|
||||
|
||||
unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id )
|
||||
{
|
||||
/*
|
||||
* binary search of id in ids
|
||||
* if found, returns position of id
|
||||
* if not found, returns first position greater than id
|
||||
*/
|
||||
unsigned base = 0;
|
||||
unsigned cursor = 1;
|
||||
int val = 0;
|
||||
unsigned n = ids[0];
|
||||
|
||||
while( 0 < n ) {
|
||||
unsigned pivot = n >> 1;
|
||||
cursor = base + pivot + 1;
|
||||
val = CMP( ids[cursor], id );
|
||||
|
||||
if( val < 0 ) {
|
||||
n = pivot;
|
||||
|
||||
} else if ( val > 0 ) {
|
||||
base = cursor;
|
||||
n -= pivot + 1;
|
||||
|
||||
} else {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if( val > 0 ) {
|
||||
++cursor;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
#if 0 /* superseded by append/sort */
|
||||
int mdb_midl_insert( MDB_IDL ids, MDB_ID id )
|
||||
{
|
||||
unsigned x, i;
|
||||
|
||||
x = mdb_midl_search( ids, id );
|
||||
assert( x > 0 );
|
||||
|
||||
if( x < 1 ) {
|
||||
/* internal error */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( x <= ids[0] && ids[x] == id ) {
|
||||
/* duplicate */
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ++ids[0] >= MDB_IDL_DB_MAX ) {
|
||||
/* no room */
|
||||
--ids[0];
|
||||
return -2;
|
||||
|
||||
} else {
|
||||
/* insert id */
|
||||
for (i=ids[0]; i>x; i--)
|
||||
ids[i] = ids[i-1];
|
||||
ids[x] = id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
MDB_IDL mdb_midl_alloc(int num)
|
||||
{
|
||||
MDB_IDL ids = reinterpret_cast<MDB_IDL>(malloc((num+2) * sizeof(MDB_ID)));
|
||||
if (ids) {
|
||||
*ids++ = num;
|
||||
*ids = 0;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
void mdb_midl_free(MDB_IDL ids)
|
||||
{
|
||||
if (ids)
|
||||
free(ids-1);
|
||||
}
|
||||
|
||||
void mdb_midl_shrink( MDB_IDL *idp )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
if (*(--ids) > MDB_IDL_UM_MAX &&
|
||||
(ids = reinterpret_cast<MDB_IDL>(realloc(ids, (MDB_IDL_UM_MAX+2) * sizeof(MDB_ID)))))
|
||||
{
|
||||
*ids++ = MDB_IDL_UM_MAX;
|
||||
*idp = ids;
|
||||
}
|
||||
}
|
||||
|
||||
static int mdb_midl_grow( MDB_IDL *idp, int num )
|
||||
{
|
||||
MDB_IDL idn = *idp-1;
|
||||
/* grow it */
|
||||
idn = reinterpret_cast<MDB_IDL>(realloc(idn, (*idn + num + 2) * sizeof(MDB_ID)));
|
||||
if (!idn)
|
||||
return ENOMEM;
|
||||
*idn++ += num;
|
||||
*idp = idn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_need( MDB_IDL *idp, unsigned num )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
num += ids[0];
|
||||
if (num > ids[-1]) {
|
||||
num = (num + num/4 + (256 + 2)) & -256;
|
||||
if (!(ids = reinterpret_cast<MDB_IDL>(realloc(ids-1, num * sizeof(MDB_ID)))))
|
||||
return ENOMEM;
|
||||
*ids++ = num - 2;
|
||||
*idp = ids;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append( MDB_IDL *idp, MDB_ID id )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
/* Too big? */
|
||||
if (ids[0] >= ids[-1]) {
|
||||
if (mdb_midl_grow(idp, MDB_IDL_UM_MAX))
|
||||
return ENOMEM;
|
||||
ids = *idp;
|
||||
}
|
||||
ids[0]++;
|
||||
ids[ids[0]] = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
/* Too big? */
|
||||
if (ids[0] + app[0] >= ids[-1]) {
|
||||
if (mdb_midl_grow(idp, app[0]))
|
||||
return ENOMEM;
|
||||
ids = *idp;
|
||||
}
|
||||
memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID));
|
||||
ids[0] += app[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n )
|
||||
{
|
||||
MDB_ID *ids = *idp, len = ids[0];
|
||||
/* Too big? */
|
||||
if (len + n > ids[-1]) {
|
||||
if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX))
|
||||
return ENOMEM;
|
||||
ids = *idp;
|
||||
}
|
||||
ids[0] = len + n;
|
||||
ids += len;
|
||||
while (n)
|
||||
ids[n--] = id++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge )
|
||||
{
|
||||
MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k;
|
||||
idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */
|
||||
old_id = idl[j];
|
||||
while (i) {
|
||||
merge_id = merge[i--];
|
||||
for (; old_id < merge_id; old_id = idl[--j])
|
||||
idl[k--] = old_id;
|
||||
idl[k--] = merge_id;
|
||||
}
|
||||
idl[0] = total;
|
||||
}
|
||||
|
||||
/* Quicksort + Insertion sort for small arrays */
|
||||
|
||||
#define SMALL 8
|
||||
#define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; }
|
||||
|
||||
void
|
||||
mdb_midl_sort( MDB_IDL ids )
|
||||
{
|
||||
/* Max possible depth of int-indexed tree * 2 items/level */
|
||||
int istack[sizeof(int)*CHAR_BIT * 2];
|
||||
int i,j,k,l,ir,jstack;
|
||||
MDB_ID a, itmp;
|
||||
|
||||
ir = (int)ids[0];
|
||||
l = 1;
|
||||
jstack = 0;
|
||||
for(;;) {
|
||||
if (ir - l < SMALL) { /* Insertion sort */
|
||||
for (j=l+1;j<=ir;j++) {
|
||||
a = ids[j];
|
||||
for (i=j-1;i>=1;i--) {
|
||||
if (ids[i] >= a) break;
|
||||
ids[i+1] = ids[i];
|
||||
}
|
||||
ids[i+1] = a;
|
||||
}
|
||||
if (jstack == 0) break;
|
||||
ir = istack[jstack--];
|
||||
l = istack[jstack--];
|
||||
} else {
|
||||
k = (l + ir) >> 1; /* Choose median of left, center, right */
|
||||
MIDL_SWAP(ids[k], ids[l+1]);
|
||||
if (ids[l] < ids[ir]) {
|
||||
MIDL_SWAP(ids[l], ids[ir]);
|
||||
}
|
||||
if (ids[l+1] < ids[ir]) {
|
||||
MIDL_SWAP(ids[l+1], ids[ir]);
|
||||
}
|
||||
if (ids[l] < ids[l+1]) {
|
||||
MIDL_SWAP(ids[l], ids[l+1]);
|
||||
}
|
||||
i = l+1;
|
||||
j = ir;
|
||||
a = ids[l+1];
|
||||
for(;;) {
|
||||
do i++; while(ids[i] > a);
|
||||
do j--; while(ids[j] < a);
|
||||
if (j < i) break;
|
||||
MIDL_SWAP(ids[i],ids[j]);
|
||||
}
|
||||
ids[l+1] = ids[j];
|
||||
ids[j] = a;
|
||||
jstack += 2;
|
||||
if (ir-i+1 >= j-l) {
|
||||
istack[jstack] = ir;
|
||||
istack[jstack-1] = i;
|
||||
ir = j-1;
|
||||
} else {
|
||||
istack[jstack] = j-1;
|
||||
istack[jstack-1] = l;
|
||||
l = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id )
|
||||
{
|
||||
/*
|
||||
* binary search of id in ids
|
||||
* if found, returns position of id
|
||||
* if not found, returns first position greater than id
|
||||
*/
|
||||
unsigned base = 0;
|
||||
unsigned cursor = 1;
|
||||
int val = 0;
|
||||
unsigned n = (unsigned)ids[0].mid;
|
||||
|
||||
while( 0 < n ) {
|
||||
unsigned pivot = n >> 1;
|
||||
cursor = base + pivot + 1;
|
||||
val = CMP( id, ids[cursor].mid );
|
||||
|
||||
if( val < 0 ) {
|
||||
n = pivot;
|
||||
|
||||
} else if ( val > 0 ) {
|
||||
base = cursor;
|
||||
n -= pivot + 1;
|
||||
|
||||
} else {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if( val > 0 ) {
|
||||
++cursor;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id )
|
||||
{
|
||||
unsigned x, i;
|
||||
|
||||
x = mdb_mid2l_search( ids, id->mid );
|
||||
|
||||
if( x < 1 ) {
|
||||
/* internal error */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( x <= ids[0].mid && ids[x].mid == id->mid ) {
|
||||
/* duplicate */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ids[0].mid >= MDB_IDL_UM_MAX ) {
|
||||
/* too big */
|
||||
return -2;
|
||||
|
||||
} else {
|
||||
/* insert id */
|
||||
ids[0].mid++;
|
||||
for (i=(unsigned)ids[0].mid; i>x; i--)
|
||||
ids[i] = ids[i-1];
|
||||
ids[x] = *id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id )
|
||||
{
|
||||
/* Too big? */
|
||||
if (ids[0].mid >= MDB_IDL_UM_MAX) {
|
||||
return -2;
|
||||
}
|
||||
ids[0].mid++;
|
||||
ids[ids[0].mid] = *id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
187
lmdb/midl.h
Normal file
187
lmdb/midl.h
Normal file
@ -0,0 +1,187 @@
|
||||
/** @file midl.h
|
||||
* @brief LMDB ID List header file.
|
||||
*
|
||||
* This file was originally part of back-bdb but has been
|
||||
* modified for use in libmdb. Most of the macros defined
|
||||
* in this file are unused, just left over from the original.
|
||||
*
|
||||
* This file is only used internally in libmdb and its definitions
|
||||
* are not exposed publicly.
|
||||
*/
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2000-2019 The OpenLDAP Foundation.
|
||||
* Portions Copyright 2001-2018 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#ifndef _MDB_MIDL_H_
|
||||
#define _MDB_MIDL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup internal LMDB Internals
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup idls ID List Management
|
||||
* @{
|
||||
*/
|
||||
/** A generic unsigned ID number. These were entryIDs in back-bdb.
|
||||
* Preferably it should have the same size as a pointer.
|
||||
*/
|
||||
typedef size_t MDB_ID;
|
||||
|
||||
/** An IDL is an ID List, a sorted array of IDs. The first
|
||||
* element of the array is a counter for how many actual
|
||||
* IDs are in the list. In the original back-bdb code, IDLs are
|
||||
* sorted in ascending order. For libmdb IDLs are sorted in
|
||||
* descending order.
|
||||
*/
|
||||
typedef MDB_ID *MDB_IDL;
|
||||
|
||||
/* IDL sizes - likely should be even bigger
|
||||
* limiting factors: sizeof(ID), thread stack size
|
||||
*/
|
||||
#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
|
||||
#define MDB_IDL_DB_SIZE (1<<MDB_IDL_LOGN)
|
||||
#define MDB_IDL_UM_SIZE (1<<(MDB_IDL_LOGN+1))
|
||||
|
||||
#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE-1)
|
||||
#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE-1)
|
||||
#define MDB_IDL_DIRTY_MAX 256 /*1MiB of memory*/
|
||||
|
||||
#define MDB_IDL_SIZEOF(ids) (((ids)[0]+1) * sizeof(MDB_ID))
|
||||
#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
|
||||
#define MDB_IDL_CPY( dst, src ) (memcpy( dst, src, MDB_IDL_SIZEOF( src ) ))
|
||||
#define MDB_IDL_FIRST( ids ) ( (ids)[1] )
|
||||
#define MDB_IDL_LAST( ids ) ( (ids)[(ids)[0]] )
|
||||
|
||||
/** Current max length of an #mdb_midl_alloc()ed IDL */
|
||||
#define MDB_IDL_ALLOCLEN( ids ) ( (ids)[-1] )
|
||||
|
||||
/** Append ID to IDL. The IDL must be big enough. */
|
||||
#define mdb_midl_xappend(idl, id) do { \
|
||||
MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \
|
||||
xidl[xlen] = (id); \
|
||||
} while (0)
|
||||
|
||||
/** Search for an ID in an IDL.
|
||||
* @param[in] ids The IDL to search.
|
||||
* @param[in] id The ID to search for.
|
||||
* @return The index of the first ID greater than or equal to \b id.
|
||||
*/
|
||||
unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id );
|
||||
|
||||
/** Allocate an IDL.
|
||||
* Allocates memory for an IDL of the given size.
|
||||
* @return IDL on success, NULL on failure.
|
||||
*/
|
||||
MDB_IDL mdb_midl_alloc(int num);
|
||||
|
||||
/** Free an IDL.
|
||||
* @param[in] ids The IDL to free.
|
||||
*/
|
||||
void mdb_midl_free(MDB_IDL ids);
|
||||
|
||||
/** Shrink an IDL.
|
||||
* Return the IDL to the default size if it has grown larger.
|
||||
* @param[in,out] idp Address of the IDL to shrink.
|
||||
*/
|
||||
void mdb_midl_shrink(MDB_IDL *idp);
|
||||
|
||||
/** Make room for num additional elements in an IDL.
|
||||
* @param[in,out] idp Address of the IDL.
|
||||
* @param[in] num Number of elements to make room for.
|
||||
* @return 0 on success, ENOMEM on failure.
|
||||
*/
|
||||
int mdb_midl_need(MDB_IDL *idp, unsigned num);
|
||||
|
||||
/** Append an ID onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] id The ID to append.
|
||||
* @return 0 on success, ENOMEM if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append( MDB_IDL *idp, MDB_ID id );
|
||||
|
||||
/** Append an IDL onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] app The IDL to append.
|
||||
* @return 0 on success, ENOMEM if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app );
|
||||
|
||||
/** Append an ID range onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] id The lowest ID to append.
|
||||
* @param[in] n Number of IDs to append.
|
||||
* @return 0 on success, ENOMEM if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n );
|
||||
|
||||
/** Merge an IDL onto an IDL. The destination IDL must be big enough.
|
||||
* @param[in] idl The IDL to merge into.
|
||||
* @param[in] merge The IDL to merge.
|
||||
*/
|
||||
void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge );
|
||||
|
||||
/** Sort an IDL.
|
||||
* @param[in,out] ids The IDL to sort.
|
||||
*/
|
||||
void mdb_midl_sort( MDB_IDL ids );
|
||||
|
||||
/** An ID2 is an ID/pointer pair.
|
||||
*/
|
||||
typedef struct MDB_ID2 {
|
||||
MDB_ID mid; /**< The ID */
|
||||
void *mptr; /**< The pointer */
|
||||
} MDB_ID2;
|
||||
|
||||
/** An ID2L is an ID2 List, a sorted array of ID2s.
|
||||
* The first element's \b mid member is a count of how many actual
|
||||
* elements are in the array. The \b mptr member of the first element is unused.
|
||||
* The array is sorted in ascending order by \b mid.
|
||||
*/
|
||||
typedef MDB_ID2 *MDB_ID2L;
|
||||
|
||||
/** Search for an ID in an ID2L.
|
||||
* @param[in] ids The ID2L to search.
|
||||
* @param[in] id The ID to search for.
|
||||
* @return The index of the first ID2 whose \b mid member is greater than or equal to \b id.
|
||||
*/
|
||||
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id );
|
||||
|
||||
|
||||
/** Insert an ID2 into a ID2L.
|
||||
* @param[in,out] ids The ID2L to insert into.
|
||||
* @param[in] id The ID2 to insert.
|
||||
* @return 0 on success, -1 if the ID was already present in the ID2L.
|
||||
*/
|
||||
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id );
|
||||
|
||||
/** Append an ID2 into a ID2L.
|
||||
* @param[in,out] ids The ID2L to append into.
|
||||
* @param[in] id The ID2 to append.
|
||||
* @return 0 on success, -2 if the ID2L is too big.
|
||||
*/
|
||||
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id );
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _MDB_MIDL_H_ */
|
||||
110
main.cpp
Normal file
110
main.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright Martin Raiber. All Rights Reserved.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <folly/String.h>
|
||||
#include <gflags/gflags.h>
|
||||
#include <iostream>
|
||||
#include <folly/experimental/coro/Task.h>
|
||||
#include <folly/experimental/coro/BlockingWait.h>
|
||||
#include <folly/init/Init.h>
|
||||
#include <folly/Memory.h>
|
||||
#include <folly/executors/CPUThreadPoolExecutor.h>
|
||||
#include <folly/executors/GlobalExecutor.h>
|
||||
#include <folly/init/Init.h>
|
||||
#include <folly/io/async/EventBaseManager.h>
|
||||
#include <folly/portability/GFlags.h>
|
||||
#include <folly/portability/Unistd.h>
|
||||
#include <folly/Random.h>
|
||||
#include <proxygen/httpserver/HTTPServer.h>
|
||||
#include <proxygen/httpserver/RequestHandlerFactory.h>
|
||||
#include "s3handler.h"
|
||||
#include "SingleFileStorage.h"
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
DEFINE_int32(http_port, 11000, "Port to listen on with HTTP protocol");
|
||||
DEFINE_int32(h2_port, -1, "Port to listen on with HTTP/2 protocol (-1 if disabled)");
|
||||
DEFINE_string(ip, "localhost", "IP/Hostname to bind to");
|
||||
DEFINE_string(root_key, "", "Secret access key for 'root'");
|
||||
DEFINE_int64(data_file_size_limit_mb, 0, "Max data file size (0 for unlimited)");
|
||||
DEFINE_int64(data_file_alloc_chunk_size_mb, 512, "Data file chunk allocation size");
|
||||
DEFINE_int32(threads,
|
||||
0,
|
||||
"Number of threads to listen on. Numbers <= 0 "
|
||||
"will use the number of cores on this machine.");
|
||||
DEFINE_bool(manual_commit, false, "Manually commit by putting to a711e93e-93b4-4a9e-8a0b-688797470002");
|
||||
DEFINE_string(index_path, ".", "Path where to put the index file");
|
||||
DEFINE_string(data_path, ".", "Path where to put the data file");
|
||||
DEFINE_bool(stop_on_error, false, "Stop on write/read errors");
|
||||
DEFINE_bool(punch_holes, true, "Free up space if not enough free space is left by punching holes");
|
||||
|
||||
namespace {
|
||||
class S3HandlerFactory : public proxygen::RequestHandlerFactory {
|
||||
SingleFileStorage sfs;
|
||||
std::string root_key;
|
||||
public:
|
||||
S3HandlerFactory(SingleFileStorage::SFSOptions sfsoptions)
|
||||
: sfs(std::move(sfsoptions)),
|
||||
root_key(FLAGS_root_key)
|
||||
{
|
||||
sfs.start_thread(sfs.get_transid());
|
||||
}
|
||||
|
||||
void onServerStart(folly::EventBase* /*evb*/) noexcept override {
|
||||
}
|
||||
|
||||
void onServerStop() noexcept override {
|
||||
}
|
||||
|
||||
proxygen::RequestHandler* onRequest(proxygen::RequestHandler*, proxygen::HTTPMessage*) noexcept override {
|
||||
return new S3Handler(sfs, root_key);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
folly::init(&argc, &argv, true);
|
||||
SingleFileStorage::init_mutex();
|
||||
|
||||
std::vector<proxygen::HTTPServer::IPConfig> IPs = {
|
||||
{folly::SocketAddress(FLAGS_ip, FLAGS_http_port, true), proxygen::HTTPServer::Protocol::HTTP},
|
||||
};
|
||||
|
||||
if(FLAGS_h2_port!=-1)
|
||||
{
|
||||
IPs.push_back({folly::SocketAddress(FLAGS_ip, FLAGS_h2_port, true), proxygen::HTTPServer::Protocol::HTTP2});
|
||||
}
|
||||
|
||||
SingleFileStorage::SFSOptions sfsoptions;
|
||||
sfsoptions.data_path = FLAGS_data_path;
|
||||
sfsoptions.db_path = FLAGS_index_path;
|
||||
sfsoptions.data_file_size_limit_mb = FLAGS_data_file_size_limit_mb;
|
||||
sfsoptions.alloc_chunk_size = FLAGS_data_file_alloc_chunk_size_mb*1024*1024;
|
||||
std::vector<unsigned char> runtime_id(32);
|
||||
folly::Random::secureRandom(runtime_id.data(), runtime_id.size());
|
||||
sfsoptions.runtime_id = folly::hexlify<std::string>(folly::ByteRange(runtime_id.data(), runtime_id.size()));
|
||||
sfsoptions.manual_commit = FLAGS_manual_commit;
|
||||
sfsoptions.stop_on_error = FLAGS_stop_on_error;
|
||||
sfsoptions.punch_holes = FLAGS_punch_holes;
|
||||
|
||||
proxygen::HTTPServerOptions options;
|
||||
options.threads = static_cast<size_t>(FLAGS_threads);
|
||||
options.idleTimeout = 60s;
|
||||
options.shutdownOn = {SIGINT, SIGTERM};
|
||||
options.enableContentCompression = false;
|
||||
options.handlerFactories =
|
||||
proxygen::RequestHandlerChain().addThen<S3HandlerFactory>(sfsoptions).build();
|
||||
options.h2cEnabled = true;
|
||||
|
||||
proxygen::HTTPServer server(std::move(options));
|
||||
server.bind(IPs);
|
||||
|
||||
std::thread t([&]() { server.start(); });
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
|
||||
213
os_functions.cpp
Normal file
213
os_functions.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Copyright Martin Raiber. All Rights Reserved.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
#include "os_functions.h"
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <limits.h>
|
||||
|
||||
int64_t os_total_space(const std::string &path)
|
||||
{
|
||||
std::string cp=path;
|
||||
if(path.size()==0)
|
||||
return -1;
|
||||
if(cp[cp.size()-1]=='/')
|
||||
cp.erase(cp.size()-1, 1);
|
||||
if(cp[cp.size()-1]!='/')
|
||||
cp+='/';
|
||||
|
||||
struct statvfs64 buf;
|
||||
int rc=statvfs64((path).c_str(), &buf);
|
||||
if(rc==0)
|
||||
{
|
||||
fsblkcnt64_t used=buf.f_blocks-buf.f_bfree;
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
int64 total = (int64)(used+buf.f_bavail)*buf.f_frsize;
|
||||
#else
|
||||
fsblkcnt64_t total = (used+buf.f_bavail)*buf.f_bsize;
|
||||
#endif
|
||||
if(total>LLONG_MAX)
|
||||
{
|
||||
return LLONG_MAX;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t os_free_space(const std::string &path)
|
||||
{
|
||||
std::string cp=path;
|
||||
if(path.size()==0)
|
||||
return -1;
|
||||
if(cp[cp.size()-1]=='/')
|
||||
cp.erase(cp.size()-1, 1);
|
||||
if(cp[cp.size()-1]!='/')
|
||||
cp+='/';
|
||||
|
||||
struct statvfs64 buf = {};
|
||||
int rc=statvfs64((path).c_str(), &buf);
|
||||
if(rc==0)
|
||||
{
|
||||
#if defined(__FreeBSD__) || defined(__APPLE__)
|
||||
int64 free = (int64)buf.f_frsize*buf.f_bavail;
|
||||
#else
|
||||
fsblkcnt64_t blocksize = buf.f_frsize ? buf.f_frsize : buf.f_bsize;
|
||||
fsblkcnt64_t free = blocksize*buf.f_bavail;
|
||||
#endif
|
||||
if(free>LLONG_MAX)
|
||||
{
|
||||
return LLONG_MAX;
|
||||
}
|
||||
return free;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int os_get_file_type(const std::string &path)
|
||||
{
|
||||
int ret = 0;
|
||||
struct stat64 f_info;
|
||||
int rc1=stat64((path).c_str(), &f_info);
|
||||
if(rc1==0)
|
||||
{
|
||||
if ( S_ISDIR(f_info.st_mode) )
|
||||
{
|
||||
ret |= EFileType_Directory;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret |= EFileType_File;
|
||||
}
|
||||
}
|
||||
|
||||
int rc2 = lstat64((path).c_str(), &f_info);
|
||||
if(rc2==0)
|
||||
{
|
||||
if(S_ISLNK(f_info.st_mode))
|
||||
{
|
||||
ret |= EFileType_Symlink;
|
||||
}
|
||||
|
||||
if(!S_ISDIR(f_info.st_mode)
|
||||
&& !S_ISREG(f_info.st_mode) )
|
||||
{
|
||||
ret |= EFileType_Special;
|
||||
}
|
||||
|
||||
if(rc1!=0)
|
||||
{
|
||||
ret |= EFileType_File;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int os_popen(const std::string& cmd, std::string& ret)
|
||||
{
|
||||
ret.clear();
|
||||
|
||||
#ifdef __ANDROID__
|
||||
POFILE* pin = NULL;
|
||||
#endif
|
||||
|
||||
FILE* in = NULL;
|
||||
|
||||
#ifndef _WIN32
|
||||
#define _popen popen
|
||||
#define _pclose pclose
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
pin = and_popen(cmd.c_str(), "r");
|
||||
if(pin!=NULL) in=pin->fp;
|
||||
#elif __linux__
|
||||
in = _popen(cmd.c_str(), "re");
|
||||
if(!in) in = _popen(cmd.c_str(), "r");
|
||||
#else
|
||||
in = _popen(cmd.c_str(), "r");
|
||||
#endif
|
||||
|
||||
if(in==NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
size_t read;
|
||||
do
|
||||
{
|
||||
read=fread(buf, 1, sizeof(buf), in);
|
||||
if(read>0)
|
||||
{
|
||||
ret.append(buf, buf+read);
|
||||
}
|
||||
}
|
||||
while(read==sizeof(buf));
|
||||
|
||||
#ifdef __ANDROID__
|
||||
return and_pclose(pin);
|
||||
#else
|
||||
return _pclose(in);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define BTRFS_IOCTL_MAGIC 0x94
|
||||
#define BTRFS_IOC_SYNC _IO(BTRFS_IOCTL_MAGIC, 8)
|
||||
|
||||
bool os_sync(const std::string & path)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
int fd = open(path.c_str(), O_RDONLY|O_CLOEXEC);
|
||||
|
||||
if(fd!=-1)
|
||||
{
|
||||
if(ioctl(fd, BTRFS_IOC_SYNC, NULL)==-1)
|
||||
{
|
||||
if(errno!=ENOTTY && errno!=ENOSYS
|
||||
&& errno!=EINVAL)
|
||||
{
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(syncfs(fd)!=0)
|
||||
{
|
||||
if(errno==ENOSYS)
|
||||
{
|
||||
close(fd);
|
||||
sync();
|
||||
return true;
|
||||
}
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sync();
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
sync();
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
53
os_functions.h
Normal file
53
os_functions.h
Normal file
@ -0,0 +1,53 @@
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <folly/File.h>
|
||||
#include <folly/Exception.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static std::string os_file_sep() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
static pid_t getThreadID() {
|
||||
return syscall(__NR_gettid);
|
||||
}
|
||||
|
||||
static int64_t fileSize(const folly::File& file)
|
||||
{
|
||||
struct stat64 st;
|
||||
folly::checkUnixError(fstat64(file.fd(), &st), "fstat() failed");
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
static int64_t fileSize(int fd)
|
||||
{
|
||||
struct stat64 st;
|
||||
folly::checkUnixError(fstat64(fd, &st), "fstat() failed");
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
int64_t os_total_space(const std::string& path);
|
||||
|
||||
int64_t os_free_space(const std::string& path);
|
||||
|
||||
enum EFileType
|
||||
{
|
||||
EFileType_File = 1,
|
||||
EFileType_Directory = 2,
|
||||
EFileType_Symlink = 4,
|
||||
EFileType_Special = 8
|
||||
};
|
||||
|
||||
unsigned int os_get_file_type(const std::string& path);
|
||||
|
||||
int os_popen(const std::string& cmd, std::string& ret);
|
||||
|
||||
static bool punchHole(int fd, __off64_t spos, __off64_t size)
|
||||
{
|
||||
int rc = fallocate64(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, spos, size);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool os_sync(const std::string & path);
|
||||
72
relaxed_atomic.h
Normal file
72
relaxed_atomic.h
Normal file
@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
|
||||
template<typename T>
|
||||
struct relaxed_atomic : std::atomic<T>
|
||||
{
|
||||
relaxed_atomic()
|
||||
: std::atomic<T>() {}
|
||||
|
||||
relaxed_atomic(const relaxed_atomic&) = delete;
|
||||
relaxed_atomic& operator=(const relaxed_atomic&) = delete;
|
||||
|
||||
relaxed_atomic(const T val)
|
||||
: std::atomic<T>(val)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
T operator=(const T val) volatile noexcept {
|
||||
this->store(val, std::memory_order_relaxed);
|
||||
return val;
|
||||
}
|
||||
|
||||
T operator=(const T val) noexcept {
|
||||
this->store(val, std::memory_order_relaxed);
|
||||
return val;
|
||||
}
|
||||
|
||||
T operator++(int) noexcept {
|
||||
return this->fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
T operator++() noexcept {
|
||||
T tmp = this->fetch_add(1, std::memory_order_relaxed);
|
||||
++tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
T operator--(int) noexcept {
|
||||
return this->fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
T operator--() noexcept {
|
||||
T tmp = this->fetch_sub(1, std::memory_order_relaxed);
|
||||
--tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
T operator+=(const T val) noexcept {
|
||||
return this->fetch_add(val, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
T operator+=(const T val) volatile noexcept {
|
||||
return this->fetch_add(val, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
T operator-=(const T val) noexcept {
|
||||
return this->fetch_sub(val, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
T operator-=(const T val) volatile noexcept {
|
||||
return this->fetch_sub(val, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
operator T() const volatile noexcept {
|
||||
return this->load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
operator T() const noexcept {
|
||||
return this->load(std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
822
s3handler.cpp
Normal file
822
s3handler.cpp
Normal file
@ -0,0 +1,822 @@
|
||||
/**
|
||||
* Copyright Martin Raiber. All Rights Reserved.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "s3handler.h"
|
||||
#include "SingleFileStorage.h"
|
||||
#include <algorithm>
|
||||
#include <asm-generic/errno-base.h>
|
||||
#include <asm-generic/errno.h>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <folly/Format.h>
|
||||
#include <folly/Range.h>
|
||||
#include <folly/String.h>
|
||||
#include <folly/executors/GlobalExecutor.h>
|
||||
#include <folly/io/IOBuf.h>
|
||||
#include <folly/io/IOBufQueue.h>
|
||||
#include <folly/logging/LogLevel.h>
|
||||
#include <folly/logging/xlog.h>
|
||||
#include <limits>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <proxygen/lib/http/HTTPCommonHeaders.h>
|
||||
#include <proxygen/lib/http/HTTPMethod.h>
|
||||
|
||||
using namespace proxygen;
|
||||
|
||||
const char* c_commit_uuid = "a711e93e-93b4-4a9e-8a0b-688797470002";
|
||||
|
||||
std::string hashSha256Hex(const std::string &payload)
|
||||
{
|
||||
unsigned char md[SHA256_DIGEST_LENGTH];
|
||||
SHA256(reinterpret_cast<const unsigned char *>(payload.data()),
|
||||
payload.size(), md);
|
||||
return folly::hexlify<std::string>(
|
||||
folly::ByteRange(md, SHA256_DIGEST_LENGTH));
|
||||
}
|
||||
|
||||
std::string hmacSha256Binary(const std::string &key,
|
||||
const std::string &payload)
|
||||
{
|
||||
std::string ret;
|
||||
ret.resize(SHA256_DIGEST_LENGTH);
|
||||
unsigned int len = SHA256_DIGEST_LENGTH;
|
||||
HMAC(EVP_sha256(), key.data(), key.size(),
|
||||
reinterpret_cast<const unsigned char *>(payload.data()), payload.size(),
|
||||
reinterpret_cast<unsigned char *>(&ret[0]), &len);
|
||||
assert(len == SHA256_DIGEST_LENGTH);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string currDate()
|
||||
{
|
||||
time_t t = toTimeT(getCurrentTime<SteadyClock>());
|
||||
struct tm final_tm;
|
||||
localtime_r(&t, &final_tm);
|
||||
std::string ret;
|
||||
ret.resize(8);
|
||||
strftime(&ret[0], ret.size(), "%Y%m%d", &final_tm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool checkSig(HTTPMessage &headers, const std::string &secretKey,
|
||||
const folly::StringPiece &authorization,
|
||||
const std::string &payload)
|
||||
{
|
||||
const char alg_name[] = "AWS4-HMAC-SHA256";
|
||||
const char alg[] = "AWS4-HMAC-SHA256 ";
|
||||
if (authorization.find(alg) != 0)
|
||||
return false;
|
||||
|
||||
std::vector<folly::StringPiece> authorizationVec;
|
||||
folly::split(',', authorization.subpiece(sizeof(alg)), authorizationVec);
|
||||
|
||||
std::map<folly::StringPiece, folly::StringPiece> authorizationMap;
|
||||
|
||||
for (auto ave : authorizationVec)
|
||||
{
|
||||
size_t eq = ave.find_first_of('=');
|
||||
if (eq != std::string::npos)
|
||||
{
|
||||
authorizationMap.insert(
|
||||
std::make_pair(ave.subpiece(0, eq), ave.subpiece(eq + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
const char signedHeadersKey[] = "SignedHeaders";
|
||||
auto itSignedHeaders = authorizationMap.find(signedHeadersKey);
|
||||
if (itSignedHeaders == authorizationMap.end())
|
||||
return false;
|
||||
|
||||
const char credentialHeaderKey[] = "Credential";
|
||||
auto itCredential = authorizationMap.find(credentialHeaderKey);
|
||||
if (itCredential == authorizationMap.end())
|
||||
return false;
|
||||
|
||||
const char signatureHeaderKey[] = "Signature";
|
||||
auto itSignature = authorizationMap.find(signatureHeaderKey);
|
||||
if (itSignature == authorizationMap.end())
|
||||
return false;
|
||||
|
||||
std::vector<folly::StringPiece> credentialScopeToks;
|
||||
folly::split('/', itCredential->second, credentialScopeToks);
|
||||
|
||||
if (credentialScopeToks.size() != 5)
|
||||
return false;
|
||||
|
||||
std::vector<folly::StringPiece> signedHeadersVec;
|
||||
folly::split(';', itSignedHeaders->second, signedHeadersVec);
|
||||
|
||||
std::string canonicalHeaders;
|
||||
std::optional<folly::Range<const char *> > prevSignedHeader;
|
||||
bool hasHost = false;
|
||||
for (auto signedHeader : signedHeadersVec)
|
||||
{
|
||||
if (prevSignedHeader && prevSignedHeader >= signedHeader)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto fullVal = headers.getHeaders().getSingleOrEmpty(signedHeader);
|
||||
auto val = folly::trimWhitespace(fullVal);
|
||||
canonicalHeaders += folly::sformat("{}:{}\n", signedHeader, val);
|
||||
prevSignedHeader = signedHeader;
|
||||
if (signedHeader == "host" && !val.empty())
|
||||
hasHost = true;
|
||||
}
|
||||
|
||||
if (!hasHost)
|
||||
return false;
|
||||
|
||||
auto params = headers.getQueryParams();
|
||||
std::string canonicalParamStr;
|
||||
for (auto param : params)
|
||||
{
|
||||
if (!canonicalParamStr.empty())
|
||||
canonicalParamStr += "&";
|
||||
canonicalParamStr += param.first + "=" +
|
||||
folly::uriEscape<std::string>(
|
||||
param.second, folly::UriEscapeMode::QUERY);
|
||||
}
|
||||
std::string canonicalRequest = folly::sformat(
|
||||
"{}\n{}\n{}\n{}\n{}\n{}\n", headers.getMethodString(),
|
||||
headers.getPathAsStringPiece(), canonicalParamStr, canonicalHeaders,
|
||||
itSignedHeaders->second, hashSha256Hex(payload));
|
||||
|
||||
std::string hashedCanonicalRequest = hashSha256Hex(canonicalRequest);
|
||||
std::string requestDateTime =
|
||||
headers.getHeaders().getSingleOrEmpty("X-Amz-Date");
|
||||
|
||||
std::string stringToSign = folly::sformat(
|
||||
"{}\n{}\n{}{}{}{}\n{}\n", alg_name, requestDateTime,
|
||||
credentialScopeToks[1], credentialScopeToks[2], credentialScopeToks[3],
|
||||
credentialScopeToks[4], hashedCanonicalRequest);
|
||||
|
||||
std::string signingKey = hmacSha256Binary(
|
||||
hmacSha256Binary(
|
||||
hmacSha256Binary(hmacSha256Binary("AWS4" + secretKey, currDate()),
|
||||
credentialScopeToks[1].toString()),
|
||||
credentialScopeToks[2].toString()),
|
||||
"aws4_request");
|
||||
|
||||
std::string sig = folly::hexlify(hmacSha256Binary(signingKey, stringToSign));
|
||||
|
||||
return sig == itSignature->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles requests by serving the file named in path. Only supports GET.
|
||||
* reads happen in a CPU thread pool since read(2) is blocking.
|
||||
* If egress pauses, file reading is also paused.
|
||||
*/
|
||||
|
||||
void S3Handler::onRequest(std::unique_ptr<HTTPMessage> headers) noexcept
|
||||
{
|
||||
if (headers->getMethod() != HTTPMethod::PUT &&
|
||||
headers->getMethod() != HTTPMethod::GET &&
|
||||
headers->getMethod() != HTTPMethod::HEAD &&
|
||||
headers->getMethod() != HTTPMethod::DELETE)
|
||||
{
|
||||
ResponseBuilder(downstream_)
|
||||
.status(400, "Bad method")
|
||||
.body("Only GET/PUT is supported")
|
||||
.sendWithEOM();
|
||||
return;
|
||||
}
|
||||
|
||||
if (headers->getMethod() == HTTPMethod::GET || headers->getMethod() == HTTPMethod::HEAD)
|
||||
{
|
||||
request_type = headers->getMethod() == HTTPMethod::GET ? RequestType::GetObject : RequestType::HeadObject;
|
||||
|
||||
auto header_path = headers->getPathAsStringPiece();
|
||||
if(!header_path.empty())
|
||||
{
|
||||
fpath = std::string(header_path.subpiece(1));
|
||||
}
|
||||
|
||||
running = true;
|
||||
|
||||
if(fpath.find('/')==std::string::npos)
|
||||
{
|
||||
listObjects(*headers);
|
||||
return;
|
||||
}
|
||||
|
||||
if(fpath.find(c_commit_uuid)!=std::string::npos)
|
||||
{
|
||||
getCommitObject(*headers);
|
||||
return;
|
||||
}
|
||||
|
||||
getObject(*headers);
|
||||
return;
|
||||
}
|
||||
else if (headers->getMethod() == HTTPMethod::PUT)
|
||||
{
|
||||
request_type = RequestType::PutObject;
|
||||
fpath = std::string(headers->getPathAsStringPiece().subpiece(1));
|
||||
std::string cl = headers->getHeaders().getSingleOrEmpty(
|
||||
proxygen::HTTP_HEADER_CONTENT_LENGTH);
|
||||
if (cl.empty())
|
||||
{
|
||||
ResponseBuilder(downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body("Content-Length header not set")
|
||||
.sendWithEOM();
|
||||
return;
|
||||
}
|
||||
put_remaining = std::atoll(cl.c_str());
|
||||
|
||||
XLOGF(DBG0, "PutObject {} length {}", fpath, put_remaining);
|
||||
|
||||
if(fpath.find(c_commit_uuid)!=std::string::npos)
|
||||
{
|
||||
commit(*headers);
|
||||
return;
|
||||
}
|
||||
|
||||
putObject(*headers);
|
||||
return;
|
||||
}
|
||||
else if(headers->getMethod() == HTTPMethod::DELETE)
|
||||
{
|
||||
request_type = RequestType::DeleteObject;
|
||||
deleteObject(*headers);
|
||||
}
|
||||
}
|
||||
|
||||
void S3Handler::listObjects(proxygen::HTTPMessage& headers)
|
||||
{
|
||||
request_type = RequestType::ListObjects;
|
||||
auto marker = headers.getQueryParam("marker");
|
||||
auto max_keys = headers.getIntQueryParam("max-keys", 1000);
|
||||
auto prefix = headers.getQueryParam("prefix");
|
||||
auto delimiter = headers.getQueryParam("delimiter");
|
||||
|
||||
auto evb = folly::EventBaseManager::get()->getEventBase();
|
||||
|
||||
folly::getGlobalCPUExecutor()->add(
|
||||
[self = self, evb, marker, max_keys, prefix, delimiter]()
|
||||
{
|
||||
self->listObjects(evb, self, marker, std::max(0, std::min(10000, max_keys)), prefix, delimiter);
|
||||
});
|
||||
}
|
||||
|
||||
void S3Handler::getCommitObject(proxygen::HTTPMessage& headers)
|
||||
{
|
||||
if(request_type==RequestType::HeadObject)
|
||||
{
|
||||
ResponseBuilder(self->downstream_).status(200, "OK").header(proxygen::HTTP_HEADER_CONTENT_LENGTH, std::to_string(sfs.get_runtime_id().size())).sendWithEOM();
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(200, "OK")
|
||||
.body(fmt::format("{}", sfs.get_runtime_id()))
|
||||
.sendWithEOM();
|
||||
}
|
||||
|
||||
void S3Handler::commit(proxygen::HTTPMessage& headers)
|
||||
{
|
||||
if(put_remaining>0)
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body(fmt::format("Body length != 0"))
|
||||
.sendWithEOM();
|
||||
}
|
||||
|
||||
auto evb = folly::EventBaseManager::get()->getEventBase();
|
||||
folly::getGlobalCPUExecutor()->add(
|
||||
[self = this->self, evb]()
|
||||
{
|
||||
bool b = self->sfs.commit(false, -1);
|
||||
|
||||
evb->runInEventBaseThread([self = self, b]()
|
||||
{
|
||||
if(!b)
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body(fmt::format("Commit error"))
|
||||
.sendWithEOM();
|
||||
}
|
||||
else {
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(200, "OK")
|
||||
.sendWithEOM();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void S3Handler::getObject(proxygen::HTTPMessage& headers)
|
||||
{
|
||||
auto evb = folly::EventBaseManager::get()->getEventBase();
|
||||
folly::getGlobalCPUExecutor()->add(
|
||||
[self = self, evb]()
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
if(self->request_type == RequestType::HeadObject)
|
||||
flags |= SingleFileStorage::ReadMetaOnly;
|
||||
|
||||
auto res = self->sfs.read_prepare(self->fpath, flags);
|
||||
|
||||
if (res.err != 0)
|
||||
{
|
||||
evb->runInEventBaseThread([self = self, res]()
|
||||
{
|
||||
|
||||
if(res.err==ENOENT)
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(404, "Not found")
|
||||
.body(fmt::format("Object not found"))
|
||||
.sendWithEOM();
|
||||
}
|
||||
else if(res.err==ENOTRECOVERABLE)
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body(fmt::format("Storage is dead"))
|
||||
.sendWithEOM();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body(fmt::format("Error code: {}", res.err))
|
||||
.sendWithEOM();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
evb->runInEventBaseThread([self = self, total_len = res.total_len]()
|
||||
{
|
||||
auto resp = std::move(ResponseBuilder(self->downstream_).status(200, "OK").header(proxygen::HTTP_HEADER_CONTENT_LENGTH, std::to_string(total_len)));
|
||||
|
||||
if(self->request_type==RequestType::HeadObject)
|
||||
{
|
||||
XLOGF(DBG0, "Content length {} bytes for readObject HEAD of {}", total_len, self->fpath);
|
||||
resp.sendWithEOM();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
XLOGF(DBG0, "Content length {} bytes for readObject GET of {}", total_len, self->fpath);
|
||||
resp.send();
|
||||
}
|
||||
});
|
||||
|
||||
if (self->request_type == RequestType::HeadObject)
|
||||
return;
|
||||
|
||||
self->extents = std::move(res.extents);
|
||||
self->put_remaining.store(res.total_len, std::memory_order_relaxed);
|
||||
|
||||
self->readObject(evb, std::move(self), 0);
|
||||
});
|
||||
}
|
||||
|
||||
void S3Handler::putObject(proxygen::HTTPMessage& headers)
|
||||
{
|
||||
auto evb = folly::EventBaseManager::get()->getEventBase();
|
||||
folly::getGlobalCPUExecutor()->add(
|
||||
[self = this->self, evb]()
|
||||
{
|
||||
auto res = self->sfs.write_prepare(self->fpath, self->put_remaining, std::string::npos);
|
||||
if (res.err != 0)
|
||||
{
|
||||
evb->runInEventBaseThread([self = self, res]()
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body(fmt::format("Error preparing writing. Errno {}", res.err))
|
||||
.sendWithEOM();
|
||||
std::lock_guard lock(self->extents_mutex);
|
||||
self->finished_ = true;
|
||||
self->extents_cond.notify_all(); });
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock(self->extents_mutex);
|
||||
self->extents = std::move(res.extents);
|
||||
self->extents_cond.notify_all();
|
||||
});
|
||||
}
|
||||
|
||||
void S3Handler::deleteObject(proxygen::HTTPMessage& headers)
|
||||
{
|
||||
fpath = std::string(headers.getPathAsStringPiece().subpiece(1));
|
||||
auto evb = folly::EventBaseManager::get()->getEventBase();
|
||||
|
||||
folly::getGlobalCPUExecutor()->add(
|
||||
[self = this->self, evb]()
|
||||
{
|
||||
auto res = self->sfs.del(self->fpath, SingleFileStorage::DelAction::Del, false);
|
||||
|
||||
if(res && !self->sfs.get_manual_commit())
|
||||
{
|
||||
res = self->sfs.commit(false, -1);
|
||||
}
|
||||
|
||||
evb->runInEventBaseThread([self = self, res]()
|
||||
{
|
||||
if(!res && self->sfs.get_is_dead())
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body(fmt::format("Storage is dead"))
|
||||
.sendWithEOM();
|
||||
}
|
||||
else if(!res)
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(404, "Not found")
|
||||
.body(fmt::format("Object not found"))
|
||||
.sendWithEOM();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(200, "OK")
|
||||
.sendWithEOM();
|
||||
}
|
||||
self->finished_ = true;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
void S3Handler::readObject(folly::EventBase *evb, std::shared_ptr<S3Handler> self, int64_t offset)
|
||||
{
|
||||
const size_t bufsize = 32768;
|
||||
folly::IOBufQueue buf;
|
||||
|
||||
bool did_pause = false;
|
||||
bool has_error = false;
|
||||
while(offset < put_remaining.load(std::memory_order_relaxed))
|
||||
{
|
||||
if (self->paused_)
|
||||
{
|
||||
XLOGF(DBG0, "Sending of {} paused at {} done bytes. Finished={} Running={}", self->fpath, self->done_bytes, self->finished_, self->running);
|
||||
did_pause = true;
|
||||
break;
|
||||
}
|
||||
|
||||
auto it = std::upper_bound(extents.begin(), extents.end(), SingleFileStorage::Ext(offset, 0, 0));
|
||||
if(!extents.empty())
|
||||
--it;
|
||||
assert(it != extents.end());
|
||||
if(it==extents.end())
|
||||
break;
|
||||
|
||||
assert(it->obj_offset <= offset && it->obj_offset + it->len > offset);
|
||||
|
||||
int64_t ext_offset = offset - it->obj_offset;
|
||||
auto curr_ext = SingleFileStorage::Ext(it->obj_offset + ext_offset, it->data_file_offset + ext_offset, it->len - ext_offset);
|
||||
int64_t rlen = std::min(static_cast<int64_t>(bufsize), curr_ext.len);
|
||||
|
||||
auto res = sfs.read_ext(curr_ext, 0, bufsize, buf);
|
||||
|
||||
if(res.err!=0)
|
||||
{
|
||||
XLOGF(WARN, "Error reading extent code {}", res.err);
|
||||
evb->runInEventBaseThread([self = self]() mutable
|
||||
{
|
||||
self->downstream_->sendAbort();
|
||||
self->finished_ = true;
|
||||
self->running = false;
|
||||
} );
|
||||
has_error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
offset += res.buf->length();
|
||||
|
||||
XLOGF(DBG0, "Sending body len {} of fpath {} total_len {}", res.buf->length(), self->fpath, put_remaining.load(std::memory_order_relaxed));
|
||||
|
||||
evb->runInEventBaseThread([self = self, body = std::move(res.buf), total_len = put_remaining.load(std::memory_order_relaxed)]() mutable
|
||||
{
|
||||
if(self->finished_)
|
||||
return;
|
||||
|
||||
self->done_bytes += body->length();
|
||||
auto resp = std::move(ResponseBuilder(self->downstream_).body(std::move(body)));
|
||||
if(self->done_bytes == total_len)
|
||||
{
|
||||
resp.sendWithEOM();
|
||||
self->finished_ = true;
|
||||
self->running = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.send();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
if(!has_error && offset < put_remaining.load(std::memory_order_relaxed))
|
||||
{
|
||||
evb->runInEventBaseThread([self = self, did_pause]
|
||||
{
|
||||
if(self->finished_)
|
||||
{
|
||||
auto rc = self->sfs.read_finalize(self->fpath, self->extents, 0);
|
||||
assert(rc==0);
|
||||
return;
|
||||
}
|
||||
|
||||
XLOG(DBG0) << "Setting running=false";
|
||||
self->running = false;
|
||||
|
||||
if (did_pause)
|
||||
{
|
||||
XLOG(DBG0) << "Resuming deferred readObject";
|
||||
if(!self->paused_ && !self->running)
|
||||
{
|
||||
XLOG(DBG0) << "Was unpaused. Resuming.";
|
||||
self->onEgressResumed();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto rc = sfs.read_finalize(self->fpath, self->extents, 0);
|
||||
assert(rc==0);
|
||||
}
|
||||
}
|
||||
|
||||
void S3Handler::listObjects(folly::EventBase *evb, std::shared_ptr<S3Handler> self, const std::string& marker, int max_keys, const std::string& prefix, const std::string& delimiter)
|
||||
{
|
||||
SingleFileStorage::IterData iter_data = {};
|
||||
if(!sfs.iter_start(marker, false, iter_data))
|
||||
{
|
||||
evb->runInEventBaseThread([self = self]()
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body(fmt::format("Error listing"))
|
||||
.sendWithEOM(); });
|
||||
return;
|
||||
}
|
||||
|
||||
std::string val_data;
|
||||
|
||||
int i;
|
||||
bool truncated = true;
|
||||
for(i=0;i<max_keys;++i)
|
||||
{
|
||||
std::string key, md5sum;
|
||||
int64_t offset, size, last_modified;
|
||||
std::vector<SingleFileStorage::SPunchItem> extra_exts;
|
||||
if(!sfs.iter_curr_val(key, offset, size, extra_exts, last_modified, md5sum, iter_data))
|
||||
{
|
||||
truncated = false;
|
||||
break;
|
||||
}
|
||||
|
||||
for(const auto& ext: extra_exts)
|
||||
{
|
||||
size += ext.len;
|
||||
}
|
||||
|
||||
val_data += fmt::format("\t<Contents>\n"
|
||||
"\t\t<Key>{}</Key>\n"
|
||||
"\t\t<LastModified>2009-10-12T17:50:30.000Z</LastModified>\n"
|
||||
"\t\t<ETag>\"{}\"</ETag>\n"
|
||||
"\t\t<Size>{}</Size>\n"
|
||||
"\t\t<StorageClass>STANDARD</StorageClass>\n"
|
||||
"\t\t<Owner>\n"
|
||||
"\t\t\t<ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>\n"
|
||||
"\t\t\t<DisplayName>mtd@amazon.com</DisplayName>\n"
|
||||
"\t\t</Owner>\n"
|
||||
"\t</Contents>", key, folly::hexlify(md5sum), size);
|
||||
|
||||
if(!sfs.iter_next(iter_data))
|
||||
{
|
||||
evb->runInEventBaseThread([self = self]()
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body(fmt::format("Error listing (in iteration)"))
|
||||
.sendWithEOM(); });
|
||||
sfs.iter_stop(iter_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string next_maker;
|
||||
if(!truncated)
|
||||
{
|
||||
std::string data;
|
||||
sfs.iter_curr_val(next_maker, data, iter_data);
|
||||
}
|
||||
|
||||
sfs.iter_stop(iter_data);
|
||||
|
||||
std::string resp = fmt::format("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<ListBucketResult>\n"
|
||||
"\t<IsTruncated>{}</IsTruncated>\n"
|
||||
"\t<Marker>{}</Marker>\n"
|
||||
"\t<MaxKeys>{}</MaxKeys>\n"
|
||||
"\t<Delimiter>{}</Delimiter>\n"
|
||||
"\t<NextMarker>{}</NextMarker>\n"
|
||||
"{}"
|
||||
"</ListBucketResult>", truncated ? "true" : "false", marker, max_keys, delimiter, next_maker, val_data);
|
||||
|
||||
evb->runInEventBaseThread([self = self, resp = std::move(resp)]()
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(200, "OK")
|
||||
.body(resp)
|
||||
.sendWithEOM(); });
|
||||
}
|
||||
|
||||
void S3Handler::onEgressPaused() noexcept
|
||||
{
|
||||
// This will terminate readFile soon
|
||||
XLOG(DBG0) << "S3Handler paused";
|
||||
paused_ = true;
|
||||
}
|
||||
|
||||
void S3Handler::onEgressResumed() noexcept
|
||||
{
|
||||
XLOG(DBG0) << "S3Handler resumed";
|
||||
paused_ = false;
|
||||
// If readFileScheduled_, it will reschedule itself
|
||||
if (!running && !fpath.empty() && !finished_)
|
||||
{
|
||||
running = true;
|
||||
XLOGF(DBG0, "Starting readObject of {} offset {}", fpath, done_bytes);
|
||||
folly::getGlobalCPUExecutor()->add(
|
||||
[self = self, evb = folly::EventBaseManager::get()->getEventBase(), offset = done_bytes]()
|
||||
{
|
||||
std::string fpath = self->fpath;
|
||||
self->readObject(evb, std::move(self), offset);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
XLOGF(DBG0, "Deferred scheduling readFile finished={}", finished_);
|
||||
}
|
||||
}
|
||||
|
||||
void S3Handler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept
|
||||
{
|
||||
auto evb = folly::EventBaseManager::get()->getEventBase();
|
||||
|
||||
size_t body_bytes = body->length();
|
||||
|
||||
folly::getGlobalCPUExecutor()->add(
|
||||
[self = this->self, evb, offset = done_bytes, lbody = std::move(body)]() mutable
|
||||
{
|
||||
self->onBodyCPU(evb, offset, std::move(lbody));
|
||||
});
|
||||
|
||||
done_bytes += body_bytes;
|
||||
}
|
||||
|
||||
void S3Handler::onBodyCPU(folly::EventBase *evb, int64_t offset, std::unique_ptr<folly::IOBuf> body)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(extents_mutex);
|
||||
while (extents.empty() && !finished_)
|
||||
{
|
||||
extents_cond.wait(lock);
|
||||
}
|
||||
|
||||
if (finished_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(extents.size()>1)
|
||||
{
|
||||
assert(extents[0] < extents[1]);
|
||||
}
|
||||
|
||||
const uint8_t *data = body->data();
|
||||
size_t data_size = body->length();
|
||||
while(data_size > 0)
|
||||
{
|
||||
auto it = std::upper_bound(extents.begin(), extents.end(), SingleFileStorage::Ext(offset, 0, 0));
|
||||
if(!extents.empty())
|
||||
--it;
|
||||
assert(it != extents.end());
|
||||
if(it==extents.end())
|
||||
break;
|
||||
|
||||
if(!(it->obj_offset <= offset && it->obj_offset + it->len > offset))
|
||||
{
|
||||
XLOGF(DBG0, "Selected ext obj_offset={} len={} data_file_offset={} offset={} exts={}", it->obj_offset, it->len, it->data_file_offset, offset, extents.size());
|
||||
std::sort(extents.begin(), extents.end());
|
||||
auto it2= std::upper_bound(extents.begin(), extents.end(), SingleFileStorage::Ext(offset, 0, 0));
|
||||
XLOGF(DBG0, "Selected ext obj_offset={} len={} data_file_offset={} offset={} exts={}", it2->obj_offset, it2->len, it2->data_file_offset, offset, extents.size());
|
||||
break;
|
||||
}
|
||||
assert(it->obj_offset <= offset && it->obj_offset + it->len > offset);
|
||||
|
||||
int64_t ext_offset = offset - it->obj_offset;
|
||||
auto curr_ext = SingleFileStorage::Ext(it->obj_offset + ext_offset, it->data_file_offset + ext_offset, it->len - ext_offset);
|
||||
int64_t wlen = std::min(static_cast<int64_t>(data_size), curr_ext.len);
|
||||
|
||||
auto rc = sfs.write_ext(curr_ext, data, data_size);
|
||||
if (rc != 0)
|
||||
{
|
||||
evb->runInEventBaseThread([self = self]()
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body("Write ext error")
|
||||
.sendWithEOM();
|
||||
self->finished_ = true; });
|
||||
return;
|
||||
}
|
||||
|
||||
data += wlen;
|
||||
data_size -= wlen;
|
||||
offset += wlen;
|
||||
}
|
||||
|
||||
assert(data_size == 0);
|
||||
|
||||
if (put_remaining.fetch_sub(body->length(), std::memory_order_release) == body->length())
|
||||
{
|
||||
auto rc = sfs.write_finalize(fpath, extents, 0, std::string(), false, true);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
evb->runInEventBaseThread([self = self]()
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body("Write finalization error")
|
||||
.sendWithEOM();
|
||||
self->finished_ = true; });
|
||||
return;
|
||||
}
|
||||
|
||||
if(!sfs.get_manual_commit())
|
||||
{
|
||||
bool b = sfs.commit(false, -1);
|
||||
|
||||
if(!b)
|
||||
{
|
||||
evb->runInEventBaseThread([self = self]()
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(500, "Internal error")
|
||||
.body("Commit error")
|
||||
.sendWithEOM();
|
||||
self->finished_ = true; });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
evb->runInEventBaseThread([self = self]()
|
||||
{
|
||||
ResponseBuilder(self->downstream_)
|
||||
.status(200, "OK")
|
||||
.sendWithEOM();
|
||||
self->finished_ = true; });
|
||||
}
|
||||
}
|
||||
|
||||
void S3Handler::onEOM() noexcept {}
|
||||
|
||||
void S3Handler::onUpgrade(UpgradeProtocol /*protocol*/) noexcept
|
||||
{
|
||||
// handler doesn't support upgrades
|
||||
}
|
||||
|
||||
void S3Handler::requestComplete() noexcept
|
||||
{
|
||||
XLOG(DBG0, "Request complete");
|
||||
finished_ = true;
|
||||
paused_ = true;
|
||||
self.reset();
|
||||
}
|
||||
|
||||
void S3Handler::onError(ProxygenError /*err*/) noexcept
|
||||
{
|
||||
XLOG(DBG0, "onError");
|
||||
finished_ = true;
|
||||
paused_ = true;
|
||||
|
||||
if (request_type == RequestType::PutObject)
|
||||
{
|
||||
// TODO: Free extents
|
||||
}
|
||||
|
||||
self.reset();
|
||||
}
|
||||
76
s3handler.h
Normal file
76
s3handler.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "SingleFileStorage.h"
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <proxygen/httpserver/HTTPServer.h>
|
||||
#include <proxygen/httpserver/RequestHandler.h>
|
||||
#include <proxygen/httpserver/RequestHandlerFactory.h>
|
||||
#include <proxygen/httpserver/ResponseBuilder.h>
|
||||
#include <proxygen/lib/http/HTTPHeaders.h>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
class S3Handler : public proxygen::RequestHandler
|
||||
{
|
||||
SingleFileStorage &sfs;
|
||||
const std::string& root_key;
|
||||
|
||||
public:
|
||||
S3Handler(SingleFileStorage &sfs, const std::string& root_key) : sfs(sfs), self(this), root_key(root_key) {}
|
||||
|
||||
void
|
||||
onRequest(std::unique_ptr<proxygen::HTTPMessage> headers) noexcept override;
|
||||
|
||||
void onBody(std::unique_ptr<folly::IOBuf> body) noexcept override;
|
||||
|
||||
void onEOM() noexcept override;
|
||||
|
||||
void onUpgrade(proxygen::UpgradeProtocol proto) noexcept override;
|
||||
|
||||
void requestComplete() noexcept override;
|
||||
|
||||
void onError(proxygen::ProxygenError err) noexcept override;
|
||||
|
||||
void onEgressPaused() noexcept override;
|
||||
|
||||
void onEgressResumed() noexcept override;
|
||||
|
||||
private:
|
||||
void readFile(folly::EventBase *evb);
|
||||
void readObject(folly::EventBase *evb, std::shared_ptr<S3Handler> self, int64_t offset);
|
||||
void onBodyCPU(folly::EventBase *evb, int64_t offs, std::unique_ptr<folly::IOBuf> body);
|
||||
void listObjects(proxygen::HTTPMessage& headers);
|
||||
void getCommitObject(proxygen::HTTPMessage& headers);
|
||||
void getObject(proxygen::HTTPMessage& headers);
|
||||
void putObject(proxygen::HTTPMessage& headers);
|
||||
void commit(proxygen::HTTPMessage& headers);
|
||||
void deleteObject(proxygen::HTTPMessage& headers);
|
||||
|
||||
void listObjects(folly::EventBase *evb, std::shared_ptr<S3Handler> self, const std::string& marker, int max_keys, const std::string& prefix, const std::string& delimiter);
|
||||
|
||||
enum class RequestType
|
||||
{
|
||||
Unknown,
|
||||
GetObject,
|
||||
HeadObject,
|
||||
PutObject,
|
||||
DeleteObject,
|
||||
ListObjects
|
||||
};
|
||||
|
||||
std::shared_ptr<S3Handler> self;
|
||||
RequestType request_type = RequestType::Unknown;
|
||||
|
||||
std::string fpath;
|
||||
std::atomic<bool> paused_{ false };
|
||||
int64_t done_bytes = 0;
|
||||
bool running = false;
|
||||
bool finished_ = false;
|
||||
std::atomic<int64_t> put_remaining = -1;
|
||||
|
||||
std::mutex extents_mutex;
|
||||
std::condition_variable extents_cond;
|
||||
|
||||
std::vector<SingleFileStorage::Ext> extents;
|
||||
};
|
||||
67
test/hs5_fixture/__init__.py
Normal file
67
test/hs5_fixture/__init__.py
Normal file
@ -0,0 +1,67 @@
|
||||
# Copyright Martin Raiber. All Rights Reserved.
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
from pathlib import Path
|
||||
from shutil import rmtree
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
import botocore
|
||||
import pytest
|
||||
import os
|
||||
import boto3
|
||||
from mypy_boto3_s3.client import S3Client
|
||||
|
||||
curr_port = 11000
|
||||
|
||||
class Hs5Runner:
|
||||
|
||||
def __init__(self, workdir : Path) -> None:
|
||||
global curr_port
|
||||
|
||||
curr_port += 1
|
||||
|
||||
self._port = curr_port
|
||||
self._workdir = workdir
|
||||
self._root_key = uuid.uuid4().hex
|
||||
|
||||
self._process = subprocess.Popen(
|
||||
[f"{os.getcwd()}/build/hs5",
|
||||
"-http_port",
|
||||
str(curr_port),
|
||||
"-root_key",
|
||||
self._root_key,
|
||||
"-data_file_size_limit_mb",
|
||||
"100",
|
||||
"-data_file_alloc_chunk_size_mb",
|
||||
"10",
|
||||
"-logging", "DBG0"],
|
||||
stdout=sys.stdout,
|
||||
stderr=sys.stderr,
|
||||
cwd=workdir
|
||||
)
|
||||
pass
|
||||
|
||||
def stop(self) -> None:
|
||||
|
||||
with pytest.raises(subprocess.TimeoutExpired):
|
||||
self._process.wait(0.001)
|
||||
|
||||
self._process.kill()
|
||||
self._process.wait()
|
||||
|
||||
rmtree(self._workdir)
|
||||
|
||||
def get_url(self) -> str:
|
||||
return f"http://127.0.0.1:{self._port}"
|
||||
|
||||
def get_s3_client(self) -> S3Client:
|
||||
return boto3.client('s3', endpoint_url=self.get_url(), aws_access_key_id="root", aws_secret_access_key=self._root_key)
|
||||
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def hs5(tmpdir: Path):
|
||||
runner = Hs5Runner(tmpdir)
|
||||
yield runner
|
||||
runner.stop()
|
||||
168
test/test_upload.py
Normal file
168
test/test_upload.py
Normal file
@ -0,0 +1,168 @@
|
||||
# Copyright Martin Raiber. All Rights Reserved.
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
from concurrent.futures import thread
|
||||
from dataclasses import dataclass
|
||||
from distutils.command.upload import upload
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from re import T
|
||||
from uuid import uuid4
|
||||
import boto3
|
||||
from botocore.exceptions import ClientError
|
||||
import os
|
||||
from hs5_fixture import Hs5Runner, hs5
|
||||
import pytest
|
||||
import threading
|
||||
import binascii
|
||||
from boto3.s3.transfer import TransferConfig
|
||||
import time
|
||||
import filecmp
|
||||
|
||||
def create_random_file(fn: Path, size: int) -> int:
|
||||
with open(fn, "wb") as f:
|
||||
csize = 0
|
||||
crc = 0
|
||||
while csize < size:
|
||||
towrite = min(size-csize, 512*1024)
|
||||
data = os.urandom(towrite)
|
||||
#data = bytearray(towrite)
|
||||
f.write(data)
|
||||
crc = binascii.crc32(data, crc)
|
||||
csize += towrite
|
||||
|
||||
return crc
|
||||
|
||||
|
||||
|
||||
def test_put_get_del_list(tmp_path: Path, hs5: Hs5Runner):
|
||||
|
||||
with open(tmp_path / "upload.txt", "w") as upload_file:
|
||||
upload_file.write("abc")
|
||||
|
||||
s3_client = hs5.get_s3_client()
|
||||
s3_client.upload_file(upload_file.name, "testbucket", "upload.txt")
|
||||
dl_path = tmp_path / "download.txt"
|
||||
s3_client.download_file("testbucket", "upload.txt", str(dl_path))
|
||||
|
||||
with open(dl_path, "r") as f:
|
||||
assert f.read() == "abc"
|
||||
|
||||
list_resp = s3_client.list_objects(Bucket="testbucket")
|
||||
|
||||
assert not list_resp["IsTruncated"]
|
||||
objs = list_resp["Contents"]
|
||||
assert len(objs) == 1
|
||||
assert "Key" in objs[0] and objs[0]["Key"] == "testbucket/upload.txt"
|
||||
assert "Size" in objs[0] and objs[0]["Size"] == 3
|
||||
|
||||
s3_client.delete_object(Bucket="testbucket", Key="upload.txt")
|
||||
with pytest.raises(ClientError):
|
||||
s3_client.download_file("testbucket", "upload.txt", str(dl_path))
|
||||
|
||||
list_resp = s3_client.list_objects(Bucket="testbucket")
|
||||
assert not list_resp["IsTruncated"]
|
||||
assert "Contents" not in list_resp
|
||||
|
||||
def test_get_commit_obj(tmp_path: Path, hs5: Hs5Runner):
|
||||
s3_client = hs5.get_s3_client()
|
||||
fpath = tmp_path / "commit_uuid.txt"
|
||||
s3_client.download_file("testbucket", "a711e93e-93b4-4a9e-8a0b-688797470002", str(fpath))
|
||||
|
||||
with open(fpath, "r") as f:
|
||||
assert len(f.read())>30
|
||||
|
||||
def test_put_multipart(tmp_path: Path, hs5: Hs5Runner):
|
||||
|
||||
with open(tmp_path / "upload_multipart.dat", "wb") as upload_file:
|
||||
size = 50*1024*1024
|
||||
|
||||
while size > 0:
|
||||
buf = os.urandom(512*1024)
|
||||
upload_file.write(buf)
|
||||
size -= len(buf)
|
||||
|
||||
s3_client = hs5.get_s3_client()
|
||||
s3_client.upload_file(upload_file.name, "testbucket", "upload.txt")
|
||||
dl_path = tmp_path / "download.dat"
|
||||
s3_client.download_file("testbucket", "upload.txt", str(dl_path))
|
||||
|
||||
assert filecmp.cmp(upload_file.name, dl_path)
|
||||
|
||||
|
||||
def test_put_get_del_stress(tmp_path: Path, hs5: Hs5Runner):
|
||||
s3_client = hs5.get_s3_client()
|
||||
|
||||
@dataclass
|
||||
class DlInfo:
|
||||
allow_throttle = True
|
||||
running_downloads = 0
|
||||
|
||||
def put_get_del(n: int, obj_size: int, throttle: bool, dl_info: DlInfo):
|
||||
for i in range(0, n):
|
||||
fname = uuid4().hex + ".dat"
|
||||
fpath = tmp_path / fname
|
||||
ul_crc = create_random_file(fpath, obj_size)
|
||||
|
||||
config = TransferConfig(multipart_threshold=5*1024*1024*1024)
|
||||
|
||||
s3_client.upload_file(str(fpath), "testbucket", fname, Config=config)
|
||||
|
||||
def del_thread(fpath):
|
||||
s3_client.delete_object(Bucket="testbucket", Key=fname)
|
||||
|
||||
t = threading.Thread(target=del_thread, args=(fpath,))
|
||||
|
||||
resp = s3_client.get_object(Bucket="testbucket", Key=fname)
|
||||
crc = 0
|
||||
body = resp["Body"]
|
||||
cl = resp["ContentLength"]
|
||||
assert cl == obj_size
|
||||
cl -= 1
|
||||
b1 = body.read(1)
|
||||
crc = binascii.crc32(b1, crc)
|
||||
|
||||
dl_info.running_downloads += 1
|
||||
|
||||
t.start()
|
||||
|
||||
if throttle:
|
||||
while cl > 0:
|
||||
b2 = body.read(min(4096, cl))
|
||||
crc = binascii.crc32(b2, crc)
|
||||
if dl_info.allow_throttle:
|
||||
time.sleep(0.01)
|
||||
cl-=len(b2)
|
||||
else:
|
||||
b2 = body.read()
|
||||
crc = binascii.crc32(b2, crc)
|
||||
|
||||
assert crc == ul_crc
|
||||
|
||||
t.join()
|
||||
|
||||
dl_info.running_downloads -= 1
|
||||
|
||||
dl_info = DlInfo()
|
||||
|
||||
t0 = threading.Thread(target=put_get_del, args=(1, 90*1024*1024, True, dl_info))
|
||||
t0.start()
|
||||
|
||||
while dl_info.running_downloads==0:
|
||||
time.sleep(0.1)
|
||||
|
||||
threads : list[threading.Thread] = []
|
||||
for i in range(0, 1):
|
||||
t = threading.Thread(target=put_get_del, args=(100, 1*1024*1024, False, dl_info))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
dl_info.allow_throttle = False
|
||||
|
||||
t0.join()
|
||||
|
||||
|
||||
|
||||
24
utils.cpp
Normal file
24
utils.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright Martin Raiber. All Rights Reserved.
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include <folly/Random.h>
|
||||
#include <folly/String.h>
|
||||
|
||||
std::string random_uuid()
|
||||
{
|
||||
std::string rnd;
|
||||
rnd.resize(16);
|
||||
folly::Random::secureRandom(&rnd[0], 16);
|
||||
return folly::hexlify(rnd);
|
||||
}
|
||||
|
||||
std::string random_uuid_binary()
|
||||
{
|
||||
std::string rnd;
|
||||
rnd.resize(16);
|
||||
folly::Random::secureRandom(&rnd[0], 16);
|
||||
return rnd;
|
||||
}
|
||||
68
utils.h
Normal file
68
utils.h
Normal file
@ -0,0 +1,68 @@
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
static bool next(const std::string &pData, const size_t & doff, const std::string &pStr)
|
||||
{
|
||||
for(size_t i=0;i<pStr.size();++i)
|
||||
{
|
||||
if( i+doff>=pData.size() )
|
||||
return false;
|
||||
if( pData[doff+i]!=pStr[i] )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string getafter(const std::string &str,const std::string &data)
|
||||
{
|
||||
size_t pos=data.find(str);
|
||||
if(pos!=std::string::npos)
|
||||
{
|
||||
return data.substr(pos + str.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getuntil(const std::string& str, const std::string& data)
|
||||
{
|
||||
size_t off=data.find(str);
|
||||
if(off==std::string::npos)
|
||||
return std::string();
|
||||
return data.substr(0,off);
|
||||
}
|
||||
|
||||
static bool isHex(const std::string &str)
|
||||
{
|
||||
if (str.size() % 2 != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for(size_t i=0;i<str.size();++i)
|
||||
{
|
||||
char ch = str[i];
|
||||
if (ch >= '0'
|
||||
&& ch <= '9')
|
||||
{
|
||||
}
|
||||
else if (ch >= 'a' && ch <= 'f')
|
||||
{
|
||||
}
|
||||
else if (ch >= 'A' && ch <= 'F')
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string random_uuid();
|
||||
|
||||
std::string random_uuid_binary();
|
||||
Loading…
Reference in New Issue
Block a user