mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 06:10:58 +00:00
Merge branch 'develop' into Rust_integration
This commit is contained in:
17
.clang-tidy
17
.clang-tidy
@@ -56,32 +56,17 @@ Checks: "-*,
|
|||||||
|
|
||||||
readability-*,
|
readability-*,
|
||||||
-readability-avoid-const-params-in-decls,
|
-readability-avoid-const-params-in-decls,
|
||||||
-readability-avoid-unconditional-preprocessor-if,
|
|
||||||
-readability-container-data-pointer,
|
-readability-container-data-pointer,
|
||||||
-readability-delete-null-pointer,
|
|
||||||
-readability-function-cognitive-complexity,
|
-readability-function-cognitive-complexity,
|
||||||
-readability-function-size,
|
|
||||||
-readability-identifier-length,
|
-readability-identifier-length,
|
||||||
-readability-inconsistent-declaration-parameter-name,
|
-readability-inconsistent-declaration-parameter-name,
|
||||||
-readability-isolate-declaration,
|
-readability-isolate-declaration,
|
||||||
-readability-magic-numbers,
|
-readability-magic-numbers,
|
||||||
-readability-misplaced-array-index,
|
|
||||||
-readability-named-parameter,
|
-readability-named-parameter,
|
||||||
-readability-operators-representation,
|
|
||||||
-readability-qualified-auto,
|
-readability-qualified-auto,
|
||||||
-readability-redundant-access-specifiers,
|
-readability-redundant-access-specifiers,
|
||||||
-readability-redundant-control-flow,
|
|
||||||
-readability-redundant-function-ptr-dereference,
|
|
||||||
-readability-redundant-preprocessor,
|
|
||||||
-readability-redundant-smartptr-get,
|
|
||||||
-readability-redundant-string-cstr,
|
|
||||||
-readability-simplify-subscript-expr,
|
|
||||||
-readability-static-accessed-through-instance,
|
-readability-static-accessed-through-instance,
|
||||||
-readability-string-compare,
|
-readability-uppercase-literal-suffix
|
||||||
-readability-uniqueptr-delete-release,
|
|
||||||
-readability-uppercase-literal-suffix,
|
|
||||||
-readability-use-anyofallof,
|
|
||||||
-readability-use-concise-preprocessor-directives
|
|
||||||
"
|
"
|
||||||
# ---
|
# ---
|
||||||
# bugprone-narrowing-conversions, # This will break a lot of code but we should enable it in the future because it can eliminate a lot of bugs
|
# bugprone-narrowing-conversions, # This will break a lot of code but we should enable it in the future because it can eliminate a lot of bugs
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ words:
|
|||||||
- Btrfs
|
- Btrfs
|
||||||
- Buildx
|
- Buildx
|
||||||
- canonicality
|
- canonicality
|
||||||
|
- canonicalised
|
||||||
- changespq
|
- changespq
|
||||||
- checkme
|
- checkme
|
||||||
- choco
|
- choco
|
||||||
@@ -72,6 +73,7 @@ words:
|
|||||||
- citardauq
|
- citardauq
|
||||||
- clawback
|
- clawback
|
||||||
- clawbacks
|
- clawbacks
|
||||||
|
- clippy
|
||||||
- cmaketoolchain
|
- cmaketoolchain
|
||||||
- coeffs
|
- coeffs
|
||||||
- coldwallet
|
- coldwallet
|
||||||
@@ -259,6 +261,9 @@ words:
|
|||||||
- rocksdb
|
- rocksdb
|
||||||
- Rohrs
|
- Rohrs
|
||||||
- roundings
|
- roundings
|
||||||
|
- rustc
|
||||||
|
- rustfmt
|
||||||
|
- rustup
|
||||||
- sahyadri
|
- sahyadri
|
||||||
- Satoshi
|
- Satoshi
|
||||||
- scons
|
- scons
|
||||||
@@ -279,6 +284,8 @@ words:
|
|||||||
- sles
|
- sles
|
||||||
- soci
|
- soci
|
||||||
- socidb
|
- socidb
|
||||||
|
- sponsee
|
||||||
|
- sponsees
|
||||||
- SRPMS
|
- SRPMS
|
||||||
- sslws
|
- sslws
|
||||||
- statsd
|
- statsd
|
||||||
@@ -300,6 +307,8 @@ words:
|
|||||||
- takerpays
|
- takerpays
|
||||||
- ters
|
- ters
|
||||||
- TMEndpointv2
|
- TMEndpointv2
|
||||||
|
- toolchain
|
||||||
|
- tparam
|
||||||
- trixie
|
- trixie
|
||||||
- tx
|
- tx
|
||||||
- txid
|
- txid
|
||||||
@@ -327,6 +336,7 @@ words:
|
|||||||
- unserviced
|
- unserviced
|
||||||
- unshareable
|
- unshareable
|
||||||
- unshares
|
- unshares
|
||||||
|
- unsponsored
|
||||||
- unsquelch
|
- unsquelch
|
||||||
- unsquelched
|
- unsquelched
|
||||||
- unsquelching
|
- unsquelching
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ endfunction()
|
|||||||
function(create_symbolic_link target link)
|
function(create_symbolic_link target link)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
function(xrpl_add_benchmark name)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
macro(exclude_from_default target_)
|
macro(exclude_from_default target_)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
benchmarks.libxrpl > xrpl.basics
|
||||||
|
benchmarks.libxrpl > xrpl.config
|
||||||
|
benchmarks.libxrpl > xrpl.nodestore
|
||||||
libxrpl.basics > xrpl.basics
|
libxrpl.basics > xrpl.basics
|
||||||
libxrpl.conditions > xrpl.basics
|
libxrpl.conditions > xrpl.basics
|
||||||
libxrpl.conditions > xrpl.conditions
|
libxrpl.conditions > xrpl.conditions
|
||||||
@@ -159,6 +162,7 @@ test.peerfinder > xrpl.protocol
|
|||||||
test.protocol > test.jtx
|
test.protocol > test.jtx
|
||||||
test.protocol > test.unit_test
|
test.protocol > test.unit_test
|
||||||
test.protocol > xrpl.basics
|
test.protocol > xrpl.basics
|
||||||
|
test.protocol > xrpld.core
|
||||||
test.protocol > xrpl.json
|
test.protocol > xrpl.json
|
||||||
test.protocol > xrpl.protocol
|
test.protocol > xrpl.protocol
|
||||||
test.rpc > test.jtx
|
test.rpc > test.jtx
|
||||||
|
|||||||
62
.github/scripts/strategy-matrix/generate.py
vendored
62
.github/scripts/strategy-matrix/generate.py
vendored
@@ -25,24 +25,16 @@ def get_cmake_args(build_type: str, extra_args: str) -> str:
|
|||||||
return " ".join(args)
|
return " ".join(args)
|
||||||
|
|
||||||
|
|
||||||
def runs_on_event(exclude_event_types: list[str], event: str | None) -> bool:
|
|
||||||
"""Whether a config should run for the current event.
|
|
||||||
|
|
||||||
'exclude_event_types' is a list of GitHub event names (e.g.
|
|
||||||
["pull_request"]) on which the config should NOT run; an empty list means
|
|
||||||
the config runs on every event. When no event is given (event is None), no
|
|
||||||
filtering is applied.
|
|
||||||
"""
|
|
||||||
if event is None:
|
|
||||||
return True
|
|
||||||
return event not in exclude_event_types
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Input types — shapes of the JSON config files
|
# Input types — shapes of the JSON config files
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# Every config must declare 'minimal'. Minimal configs form the reduced matrix
|
||||||
|
# built for pull requests by default; the full matrix adds the rest. Packaging
|
||||||
|
# configs declare it too, but packaging is gated in the workflow, not by it.
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class LinuxConfig:
|
class LinuxConfig:
|
||||||
"""One entry in linux.json's 'configs' or 'package_configs' arrays."""
|
"""One entry in linux.json's 'configs' or 'package_configs' arrays."""
|
||||||
@@ -50,13 +42,11 @@ class LinuxConfig:
|
|||||||
compiler: list[str]
|
compiler: list[str]
|
||||||
build_type: list[str]
|
build_type: list[str]
|
||||||
arch: list[str]
|
arch: list[str]
|
||||||
|
minimal: bool
|
||||||
sanitizers: list[str] = dataclasses.field(default_factory=list)
|
sanitizers: list[str] = dataclasses.field(default_factory=list)
|
||||||
suffix: str = ""
|
suffix: str = ""
|
||||||
extra_cmake_args: str = ""
|
extra_cmake_args: str = ""
|
||||||
image: str = "" # only used by package_configs entries
|
image: str = "" # only used by package_configs entries
|
||||||
# List of GitHub event names (e.g. "pull_request") on which this config
|
|
||||||
# should NOT run. Empty means it runs on every event.
|
|
||||||
exclude_event_types: list[str] = dataclasses.field(default_factory=list)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
@@ -89,11 +79,9 @@ class PlatformConfig:
|
|||||||
"""One entry in macos.json's or windows.json's 'configs' array."""
|
"""One entry in macos.json's or windows.json's 'configs' array."""
|
||||||
|
|
||||||
build_type: list[str]
|
build_type: list[str]
|
||||||
|
minimal: bool
|
||||||
build_only: bool = False # if true, skip tests (e.g. macos/Windows Debug)
|
build_only: bool = False # if true, skip tests (e.g. macos/Windows Debug)
|
||||||
extra_cmake_args: str = ""
|
extra_cmake_args: str = ""
|
||||||
# List of GitHub event names (e.g. "pull_request") on which this config
|
|
||||||
# should NOT run. Empty means it runs on every event.
|
|
||||||
exclude_event_types: list[str] = dataclasses.field(default_factory=list)
|
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
if isinstance(self.build_type, str):
|
if isinstance(self.build_type, str):
|
||||||
@@ -168,20 +156,18 @@ _ARCHS: dict[str, Architecture] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def expand_linux_matrix(
|
def expand_linux_matrix(linux: LinuxFile, minimal: bool) -> list[MatrixEntry]:
|
||||||
linux: LinuxFile, event: str | None = None
|
|
||||||
) -> list[MatrixEntry]:
|
|
||||||
"""Expand a LinuxFile into a flat list of matrix entries.
|
"""Expand a LinuxFile into a flat list of matrix entries.
|
||||||
|
|
||||||
Each config entry is expanded over the cross-product of its
|
Each config entry is expanded over the cross-product of its
|
||||||
compiler, build_type, sanitizers, and architecture lists. Configs that
|
compiler, build_type, sanitizers, and architecture lists. When 'minimal' is
|
||||||
exclude the current event are skipped.
|
true, only configs flagged as minimal are included.
|
||||||
"""
|
"""
|
||||||
entries: list[MatrixEntry] = []
|
entries: list[MatrixEntry] = []
|
||||||
|
|
||||||
for distro, configs in linux.configs.items():
|
for distro, configs in linux.configs.items():
|
||||||
for cfg in configs:
|
for cfg in configs:
|
||||||
if not runs_on_event(cfg.exclude_event_types, event):
|
if minimal and not cfg.minimal:
|
||||||
continue
|
continue
|
||||||
# An empty sanitizers list means "one entry with no sanitizer".
|
# An empty sanitizers list means "one entry with no sanitizer".
|
||||||
effective_sanitizers = cfg.sanitizers or [""]
|
effective_sanitizers = cfg.sanitizers or [""]
|
||||||
@@ -240,19 +226,17 @@ def expand_linux_packaging(linux: LinuxFile) -> list[PackagingEntry]:
|
|||||||
return entries
|
return entries
|
||||||
|
|
||||||
|
|
||||||
def expand_platform_matrix(
|
def expand_platform_matrix(pf: PlatformFile, minimal: bool) -> list[MatrixEntry]:
|
||||||
pf: PlatformFile, event: str | None = None
|
|
||||||
) -> list[MatrixEntry]:
|
|
||||||
"""Expand a PlatformFile (macOS or Windows) into matrix entries.
|
"""Expand a PlatformFile (macOS or Windows) into matrix entries.
|
||||||
|
|
||||||
Configs that exclude the current event are skipped.
|
When 'minimal' is true, only configs flagged as minimal are included.
|
||||||
"""
|
"""
|
||||||
platform_name, arch = pf.platform.split("/")
|
platform_name, arch = pf.platform.split("/")
|
||||||
is_windows = platform_name == "windows"
|
is_windows = platform_name == "windows"
|
||||||
|
|
||||||
entries: list[MatrixEntry] = []
|
entries: list[MatrixEntry] = []
|
||||||
for cfg in pf.configs:
|
for cfg in pf.configs:
|
||||||
if not runs_on_event(cfg.exclude_event_types, event):
|
if minimal and not cfg.minimal:
|
||||||
continue
|
continue
|
||||||
for build_type in cfg.build_type:
|
for build_type in cfg.build_type:
|
||||||
entries.append(
|
entries.append(
|
||||||
@@ -292,12 +276,12 @@ if __name__ == "__main__":
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-e",
|
"-m",
|
||||||
"--event",
|
"--minimal",
|
||||||
help="The GitHub event name that triggered the workflow (e.g. 'push', "
|
help="Emit only the minimal matrix (the configs flagged 'minimal'), "
|
||||||
"'pull_request'). Configs are filtered by their 'event_type'. If "
|
"used for pull requests by default. If omitted, the full matrix is "
|
||||||
"omitted, no filtering is applied.",
|
"emitted.",
|
||||||
default=None,
|
action="store_true",
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -308,15 +292,15 @@ if __name__ == "__main__":
|
|||||||
else:
|
else:
|
||||||
if args.config in ("linux", None):
|
if args.config in ("linux", None):
|
||||||
matrix += expand_linux_matrix(
|
matrix += expand_linux_matrix(
|
||||||
LinuxFile.load(THIS_DIR / "linux.json"), args.event
|
LinuxFile.load(THIS_DIR / "linux.json"), args.minimal
|
||||||
)
|
)
|
||||||
if args.config in ("macos", None):
|
if args.config in ("macos", None):
|
||||||
matrix += expand_platform_matrix(
|
matrix += expand_platform_matrix(
|
||||||
PlatformFile.load(THIS_DIR / "macos.json"), args.event
|
PlatformFile.load(THIS_DIR / "macos.json"), args.minimal
|
||||||
)
|
)
|
||||||
if args.config in ("windows", None):
|
if args.config in ("windows", None):
|
||||||
matrix += expand_platform_matrix(
|
matrix += expand_platform_matrix(
|
||||||
PlatformFile.load(THIS_DIR / "windows.json"), args.event
|
PlatformFile.load(THIS_DIR / "windows.json"), args.minimal
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"matrix={json.dumps({'include': [dataclasses.asdict(e) for e in matrix]})}")
|
print(f"matrix={json.dumps({'include': [dataclasses.asdict(e) for e in matrix]})}")
|
||||||
|
|||||||
33
.github/scripts/strategy-matrix/linux.json
vendored
33
.github/scripts/strategy-matrix/linux.json
vendored
@@ -1,17 +1,31 @@
|
|||||||
{
|
{
|
||||||
"image_tag": "sha-e29b523",
|
"image_tag": "sha-2e25435",
|
||||||
"configs": {
|
"configs": {
|
||||||
"ubuntu": [
|
"ubuntu": [
|
||||||
|
{
|
||||||
|
"compiler": ["clang"],
|
||||||
|
"build_type": ["Release"],
|
||||||
|
"arch": ["amd64"],
|
||||||
|
"minimal": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"compiler": ["gcc"],
|
||||||
|
"build_type": ["Release"],
|
||||||
|
"arch": ["amd64"],
|
||||||
|
"minimal": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"compiler": ["gcc", "clang"],
|
"compiler": ["gcc", "clang"],
|
||||||
"build_type": ["Debug", "Release"],
|
"build_type": ["Debug", "Release"],
|
||||||
"arch": ["amd64", "arm64"]
|
"arch": ["arm64"],
|
||||||
|
"minimal": false
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"compiler": ["gcc", "clang"],
|
"compiler": ["gcc", "clang"],
|
||||||
"build_type": ["Debug", "Release"],
|
"build_type": ["Debug", "Release"],
|
||||||
"arch": ["amd64"],
|
"arch": ["amd64"],
|
||||||
|
"minimal": false,
|
||||||
"sanitizers": ["address", "undefinedbehavior"]
|
"sanitizers": ["address", "undefinedbehavior"]
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -19,6 +33,7 @@
|
|||||||
"compiler": ["gcc"],
|
"compiler": ["gcc"],
|
||||||
"build_type": ["Debug"],
|
"build_type": ["Debug"],
|
||||||
"arch": ["amd64"],
|
"arch": ["amd64"],
|
||||||
|
"minimal": true,
|
||||||
"suffix": "coverage",
|
"suffix": "coverage",
|
||||||
"extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=500 -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0"
|
"extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=500 -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0"
|
||||||
},
|
},
|
||||||
@@ -26,6 +41,7 @@
|
|||||||
"compiler": ["clang"],
|
"compiler": ["clang"],
|
||||||
"build_type": ["Debug"],
|
"build_type": ["Debug"],
|
||||||
"arch": ["amd64"],
|
"arch": ["amd64"],
|
||||||
|
"minimal": false,
|
||||||
"suffix": "voidstar",
|
"suffix": "voidstar",
|
||||||
"extra_cmake_args": "-Dvoidstar=ON"
|
"extra_cmake_args": "-Dvoidstar=ON"
|
||||||
},
|
},
|
||||||
@@ -33,6 +49,7 @@
|
|||||||
"compiler": ["clang"],
|
"compiler": ["clang"],
|
||||||
"build_type": ["Release"],
|
"build_type": ["Release"],
|
||||||
"arch": ["amd64"],
|
"arch": ["amd64"],
|
||||||
|
"minimal": false,
|
||||||
"suffix": "reffee",
|
"suffix": "reffee",
|
||||||
"extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=1000"
|
"extra_cmake_args": "-DUNIT_TEST_REFERENCE_FEE=1000"
|
||||||
},
|
},
|
||||||
@@ -40,9 +57,9 @@
|
|||||||
"compiler": ["gcc"],
|
"compiler": ["gcc"],
|
||||||
"build_type": ["Debug"],
|
"build_type": ["Debug"],
|
||||||
"arch": ["amd64"],
|
"arch": ["amd64"],
|
||||||
|
"minimal": false,
|
||||||
"suffix": "unity",
|
"suffix": "unity",
|
||||||
"extra_cmake_args": "-Dunity=ON",
|
"extra_cmake_args": "-Dunity=ON"
|
||||||
"exclude_event_types": ["pull_request"]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -50,7 +67,8 @@
|
|||||||
{
|
{
|
||||||
"compiler": ["gcc"],
|
"compiler": ["gcc"],
|
||||||
"build_type": ["Release"],
|
"build_type": ["Release"],
|
||||||
"arch": ["amd64"]
|
"arch": ["amd64"],
|
||||||
|
"minimal": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -58,7 +76,8 @@
|
|||||||
{
|
{
|
||||||
"compiler": ["gcc"],
|
"compiler": ["gcc"],
|
||||||
"build_type": ["Release"],
|
"build_type": ["Release"],
|
||||||
"arch": ["amd64"]
|
"arch": ["amd64"],
|
||||||
|
"minimal": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -68,6 +87,7 @@
|
|||||||
"compiler": ["gcc"],
|
"compiler": ["gcc"],
|
||||||
"build_type": ["Release"],
|
"build_type": ["Release"],
|
||||||
"arch": ["amd64"],
|
"arch": ["amd64"],
|
||||||
|
"minimal": false,
|
||||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-577d745"
|
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-577d745"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -77,6 +97,7 @@
|
|||||||
"compiler": ["gcc"],
|
"compiler": ["gcc"],
|
||||||
"build_type": ["Release"],
|
"build_type": ["Release"],
|
||||||
"arch": ["amd64"],
|
"arch": ["amd64"],
|
||||||
|
"minimal": false,
|
||||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-577d745"
|
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-577d745"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
5
.github/scripts/strategy-matrix/macos.json
vendored
5
.github/scripts/strategy-matrix/macos.json
vendored
@@ -4,13 +4,14 @@
|
|||||||
"configs": [
|
"configs": [
|
||||||
{
|
{
|
||||||
"build_type": "Release",
|
"build_type": "Release",
|
||||||
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
|
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||||
|
"minimal": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"build_type": "Debug",
|
"build_type": "Debug",
|
||||||
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
"extra_cmake_args": "-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||||
"build_only": true,
|
"build_only": true,
|
||||||
"exclude_event_types": ["pull_request"]
|
"minimal": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
4
.github/scripts/strategy-matrix/windows.json
vendored
4
.github/scripts/strategy-matrix/windows.json
vendored
@@ -2,11 +2,11 @@
|
|||||||
"platform": "windows/amd64",
|
"platform": "windows/amd64",
|
||||||
"runner": ["self-hosted", "Windows", "dev-box-windows-2026"],
|
"runner": ["self-hosted", "Windows", "dev-box-windows-2026"],
|
||||||
"configs": [
|
"configs": [
|
||||||
{ "build_type": "Release" },
|
{ "build_type": "Release", "minimal": true },
|
||||||
{
|
{
|
||||||
"build_type": "Debug",
|
"build_type": "Debug",
|
||||||
"build_only": true,
|
"build_only": true,
|
||||||
"exclude_event_types": ["pull_request"]
|
"minimal": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
1
.github/workflows/conflicting-pr.yml
vendored
1
.github/workflows/conflicting-pr.yml
vendored
@@ -14,6 +14,7 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
main:
|
main:
|
||||||
|
if: ${{ !contains(github.event.pull_request.labels.*.name, 'IgnoreConflicts') }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check if PRs are dirty
|
- name: Check if PRs are dirty
|
||||||
|
|||||||
44
.github/workflows/on-pr.yml
vendored
44
.github/workflows/on-pr.yml
vendored
@@ -1,7 +1,11 @@
|
|||||||
# This workflow runs all workflows to check, build and test the project on
|
# This workflow runs workflows to check, build and test the project
|
||||||
# various Linux flavors, as well as on MacOS and Windows, on every push to a
|
# on every meaningful change on pull_request.
|
||||||
# user branch. However, it will not run if the pull request is a draft unless it
|
# However, it will not run if the PR is a draft
|
||||||
# has the 'DraftRunCI' label. For commits to PRs that target a release branch,
|
# unless it has the 'DraftRunCI' or 'Full CI build' label.
|
||||||
|
#
|
||||||
|
# By default a PR builds only a minimal matrix.
|
||||||
|
# The full matrix runs once the PR is labeled "Ready to merge" or "Full CI build".
|
||||||
|
# For commits to PRs that target a release branch,
|
||||||
# it also uploads the libxrpl recipe to the Conan remote.
|
# it also uploads the libxrpl recipe to the Conan remote.
|
||||||
name: PR
|
name: PR
|
||||||
|
|
||||||
@@ -15,8 +19,16 @@ on:
|
|||||||
- reopened
|
- reopened
|
||||||
- synchronize
|
- synchronize
|
||||||
- ready_for_review
|
- ready_for_review
|
||||||
|
# Trigger on label changes so toggling "Ready to merge" or "Full CI build"
|
||||||
|
# switches between the minimal and full matrix without needing a new push.
|
||||||
|
- labeled
|
||||||
|
- unlabeled
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
|
# A single per-ref group with cancel-in-progress means any newer run (a push
|
||||||
|
# or a label change) supersedes the in-progress one for that ref. Keeping
|
||||||
|
# exactly one authoritative run per ref ensures a fast do-nothing run can never
|
||||||
|
# mask a real build's checks.
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
@@ -25,11 +37,18 @@ defaults:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# This job determines whether the rest of the workflow should run. It runs
|
# This job determines whether the rest of the workflow should run at all,
|
||||||
# when the PR is not a draft (which should also cover merge-group) or
|
# based on the current set of labels: it runs when the PR is not a draft
|
||||||
# has the 'DraftRunCI' label.
|
# (which should also cover merge-group) or has the 'DraftRunCI' or
|
||||||
|
# 'Full CI build' label. Whether a build then happens, and whether it is the
|
||||||
|
# minimal or full matrix, is decided further below and in the strategy matrix.
|
||||||
should-run:
|
should-run:
|
||||||
if: ${{ !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }}
|
if: >-
|
||||||
|
${{
|
||||||
|
!github.event.pull_request.draft
|
||||||
|
|| contains(github.event.pull_request.labels.*.name, 'DraftRunCI')
|
||||||
|
|| contains(github.event.pull_request.labels.*.name, 'Full CI build')
|
||||||
|
}}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -93,15 +112,17 @@ jobs:
|
|||||||
# least one of:
|
# least one of:
|
||||||
# * Any of the files checked in the `changes` step were modified
|
# * Any of the files checked in the `changes` step were modified
|
||||||
# * The PR is NOT a draft and is labeled "Ready to merge"
|
# * The PR is NOT a draft and is labeled "Ready to merge"
|
||||||
|
# * The PR is labeled "Full CI build" (draft or not)
|
||||||
# * The workflow is running from the merge queue
|
# * The workflow is running from the merge queue
|
||||||
id: go
|
id: go
|
||||||
env:
|
env:
|
||||||
FILES: ${{ steps.changes.outputs.any_changed }}
|
FILES: ${{ steps.changes.outputs.any_changed }}
|
||||||
DRAFT: ${{ github.event.pull_request.draft }}
|
DRAFT: ${{ github.event.pull_request.draft }}
|
||||||
READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }}
|
READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }}
|
||||||
|
FULL: ${{ contains(github.event.pull_request.labels.*.name, 'Full CI build') }}
|
||||||
MERGE: ${{ github.event_name == 'merge_group' }}
|
MERGE: ${{ github.event_name == 'merge_group' }}
|
||||||
run: |
|
run: |
|
||||||
echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >>"${GITHUB_OUTPUT}"
|
echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FULL == 'true' || env.FILES == 'true' || env.MERGE == 'true' }}" >>"${GITHUB_OUTPUT}"
|
||||||
cat "${GITHUB_OUTPUT}"
|
cat "${GITHUB_OUTPUT}"
|
||||||
outputs:
|
outputs:
|
||||||
go: ${{ steps.go.outputs.go == 'true' }}
|
go: ${{ steps.go.outputs.go == 'true' }}
|
||||||
@@ -151,7 +172,10 @@ jobs:
|
|||||||
|
|
||||||
package:
|
package:
|
||||||
needs: [should-run, build-test]
|
needs: [should-run, build-test]
|
||||||
if: ${{ needs.should-run.outputs.go == 'true' }}
|
# Packaging consumes the debian/rhel release binaries, which are only built
|
||||||
|
# by the full matrix. Skip it for pull requests that ran only the minimal
|
||||||
|
# matrix (i.e. not yet labeled "Ready to merge" or "Full CI build").
|
||||||
|
if: ${{ needs.should-run.outputs.go == 'true' && (github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'Ready to merge') || contains(github.event.pull_request.labels.*.name, 'Full CI build')) }}
|
||||||
uses: ./.github/workflows/reusable-package.yml
|
uses: ./.github/workflows/reusable-package.yml
|
||||||
|
|
||||||
upload-recipe:
|
upload-recipe:
|
||||||
|
|||||||
4
.github/workflows/publish-docs.yml
vendored
4
.github/workflows/publish-docs.yml
vendored
@@ -41,13 +41,13 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523
|
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-2e25435
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: Prepare runner
|
- name: Prepare runner
|
||||||
uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c
|
uses: XRPLF/actions/prepare-runner@ad188deb3dae79dc39816e16ddfdad1e06c6fab2
|
||||||
with:
|
with:
|
||||||
enable_ccache: false
|
enable_ccache: false
|
||||||
|
|
||||||
|
|||||||
21
.github/workflows/reusable-build-test-config.yml
vendored
21
.github/workflows/reusable-build-test-config.yml
vendored
@@ -113,7 +113,7 @@ jobs:
|
|||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: Prepare runner
|
- name: Prepare runner
|
||||||
uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c
|
uses: XRPLF/actions/prepare-runner@ad188deb3dae79dc39816e16ddfdad1e06c6fab2
|
||||||
with:
|
with:
|
||||||
enable_ccache: ${{ inputs.ccache_enabled }}
|
enable_ccache: ${{ inputs.ccache_enabled }}
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ jobs:
|
|||||||
- name: Check tools
|
- name: Check tools
|
||||||
env:
|
env:
|
||||||
CHECK_TOOLS_SKIP_CLONE: "1"
|
CHECK_TOOLS_SKIP_CLONE: "1"
|
||||||
run: ./bin/check-tools.sh
|
run: ./bin/check-tools.sh || true
|
||||||
|
|
||||||
- name: Print build environment
|
- name: Print build environment
|
||||||
uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574
|
uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574
|
||||||
@@ -329,6 +329,23 @@ jobs:
|
|||||||
|
|
||||||
LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log
|
LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log
|
||||||
|
|
||||||
|
# Smoke-run every benchmark module with a single repetition to confirm the
|
||||||
|
# benchmarks still build and execute. This is a correctness check, not a
|
||||||
|
# performance measurement, so it is skipped for instrumented builds
|
||||||
|
# (sanitizers/coverage/voidstar), where it would be slow and meaningless,
|
||||||
|
# and on Windows, where the `install` target does not build them.
|
||||||
|
- name: Run the benchmarks
|
||||||
|
if: ${{ !inputs.build_only && runner.os != 'Windows' && env.SANITIZERS_ENABLED == 'false' && env.COVERAGE_ENABLED != 'true' && env.VOIDSTAR_ENABLED != 'true' }}
|
||||||
|
working-directory: ${{ env.BUILD_DIR }}
|
||||||
|
run: |
|
||||||
|
rc=0
|
||||||
|
while IFS= read -r bench; do
|
||||||
|
echo "::group::${bench}"
|
||||||
|
"./${bench}" --benchmark_repetitions=1 || rc=1
|
||||||
|
echo "::endgroup::"
|
||||||
|
done < <(find src/benchmarks -type f -perm -u+x -name 'xrpl.bench.*')
|
||||||
|
exit "${rc}"
|
||||||
|
|
||||||
- name: Show test failure summary
|
- name: Show test failure summary
|
||||||
if: ${{ failure() && !inputs.build_only }}
|
if: ${{ failure() && !inputs.build_only }}
|
||||||
env:
|
env:
|
||||||
|
|||||||
4
.github/workflows/reusable-clang-tidy.yml
vendored
4
.github/workflows/reusable-clang-tidy.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
|||||||
needs: [determine-files]
|
needs: [determine-files]
|
||||||
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
|
if: ${{ needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.need_full_run == 'true' }}
|
||||||
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
runs-on: ["self-hosted", "Linux", "X64", "heavy"]
|
||||||
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-e29b523"
|
container: "ghcr.io/xrplf/xrpld/nix-debian:sha-2e25435"
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
issues: write
|
issues: write
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: Prepare runner
|
- name: Prepare runner
|
||||||
uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c
|
uses: XRPLF/actions/prepare-runner@ad188deb3dae79dc39816e16ddfdad1e06c6fab2
|
||||||
with:
|
with:
|
||||||
enable_ccache: false
|
enable_ccache: false
|
||||||
|
|
||||||
|
|||||||
@@ -35,5 +35,8 @@ jobs:
|
|||||||
id: generate
|
id: generate
|
||||||
env:
|
env:
|
||||||
GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}', inputs.os) || '' }}
|
GENERATE_CONFIG: ${{ inputs.os != '' && format('--config={0}', inputs.os) || '' }}
|
||||||
GENERATE_EVENT: ${{ github.event_name }}
|
# Run only the minimal matrix for pull requests that are not yet
|
||||||
run: ./generate.py ${GENERATE_CONFIG} --event="${GENERATE_EVENT}" >>"${GITHUB_OUTPUT}"
|
# labeled "Ready to merge" or "Full CI build". Any other event (merge
|
||||||
|
# queue, push, schedule, manual dispatch) runs the full matrix.
|
||||||
|
GENERATE_MINIMAL: ${{ (github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'Ready to merge') && !contains(github.event.pull_request.labels.*.name, 'Full CI build')) && '--minimal' || '' }}
|
||||||
|
run: ./generate.py ${GENERATE_CONFIG} ${GENERATE_MINIMAL} >>"${GITHUB_OUTPUT}"
|
||||||
|
|||||||
2
.github/workflows/reusable-upload-recipe.yml
vendored
2
.github/workflows/reusable-upload-recipe.yml
vendored
@@ -40,7 +40,7 @@ defaults:
|
|||||||
jobs:
|
jobs:
|
||||||
upload:
|
upload:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-e29b523
|
container: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-2e25435
|
||||||
env:
|
env:
|
||||||
REMOTE_NAME: ${{ inputs.remote_name }}
|
REMOTE_NAME: ${{ inputs.remote_name }}
|
||||||
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
|
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.remote_username }}
|
||||||
|
|||||||
2
.github/workflows/upload-conan-deps.yml
vendored
2
.github/workflows/upload-conan-deps.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
|||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: Prepare runner
|
- name: Prepare runner
|
||||||
uses: XRPLF/actions/prepare-runner@64ec3cf3b152b4444638f470bbd6df7a7a30c81c
|
uses: XRPLF/actions/prepare-runner@ad188deb3dae79dc39816e16ddfdad1e06c6fab2
|
||||||
with:
|
with:
|
||||||
enable_ccache: false
|
enable_ccache: false
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ repos:
|
|||||||
# as standalone translation units, so they have no compile_commands.json
|
# as standalone translation units, so they have no compile_commands.json
|
||||||
# entry to lint (verify_headers checks them transitively).
|
# entry to lint (verify_headers checks them transitively).
|
||||||
exclude: '^include/xrpl/protocol_autogen|\.ipp$'
|
exclude: '^include/xrpl/protocol_autogen|\.ipp$'
|
||||||
|
# run-clang-tidy --fix may edit headers included by files it is not run on,
|
||||||
|
# so pre-commit must not split the files across parallel hook invocations.
|
||||||
|
# The script determines the staged files itself and lets run-clang-tidy
|
||||||
|
# handle parallelism internally.
|
||||||
|
pass_filenames: false
|
||||||
- id: fix-include-style
|
- id: fix-include-style
|
||||||
name: fix include style
|
name: fix include style
|
||||||
entry: ./bin/pre-commit/fix_include_style.py
|
entry: ./bin/pre-commit/fix_include_style.py
|
||||||
@@ -43,6 +48,11 @@ repos:
|
|||||||
language: python
|
language: python
|
||||||
entry: ./bin/pre-commit/fix_pragma_once.py
|
entry: ./bin/pre-commit/fix_pragma_once.py
|
||||||
files: \.(h|hpp)$
|
files: \.(h|hpp)$
|
||||||
|
- id: check-doxygen-style
|
||||||
|
name: check Doxygen comment style
|
||||||
|
entry: ./bin/pre-commit/check_doxygen_style.py
|
||||||
|
language: python
|
||||||
|
types_or: [c++, c]
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5
|
rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ This section contains changes targeting a future version.
|
|||||||
|
|
||||||
### Additions
|
### Additions
|
||||||
|
|
||||||
|
- `account_tx`: Added an optional `delegate` request object to filter delegated transactions. The object requires `delegate_filter`, which must be either `actor` for transactions owned by the requested account but signed by another account, or `authorizer` for transactions signed by the requested account on behalf of another account. The optional `counter_party` account narrows the results to a specific signer/delegate for `actor` or a specific owner/delegator for `authorizer`. Malformed `delegate`, `delegate_filter`, and `counter_party` values return standard invalid field errors, and invalid account IDs return `actMalformed`.
|
||||||
|
When paginating delegate-filtered queries, a marker from a delegate-filtered query includes a `delegate` flag and is only valid for follow-up requests that also supply `delegate` (mixing marker conventions returns `invalidParams`). Because filtering is applied after the ledger scan, a page may contain fewer results than `limit` (possibly zero) while still returning a marker, so callers must continue until no marker is present.
|
||||||
|
|
||||||
- `ledger_entry`, `account_objects`: The `Delegate` ledger entry now includes an optional `DestinationNode` field, which stores the index into the authorized account's owner directory. This field is present on entries created after bidirectional directory tracking was introduced and may appear in RPC responses for those entries. ([#6681](https://github.com/XRPLF/rippled/pull/6681))
|
- `ledger_entry`, `account_objects`: The `Delegate` ledger entry now includes an optional `DestinationNode` field, which stores the index into the authorized account's owner directory. This field is present on entries created after bidirectional directory tracking was introduced and may appear in RPC responses for those entries. ([#6681](https://github.com/XRPLF/rippled/pull/6681))
|
||||||
|
|
||||||
- `server_definitions`: Added the following new sections to the response ([#6321](https://github.com/XRPLF/rippled/pull/6321)):
|
- `server_definitions`: Added the following new sections to the response ([#6321](https://github.com/XRPLF/rippled/pull/6321)):
|
||||||
|
|||||||
@@ -131,6 +131,10 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
target_link_libraries(xrpl_libs INTERFACE ${nudb})
|
target_link_libraries(xrpl_libs INTERFACE ${nudb})
|
||||||
|
|
||||||
|
if(benchmark)
|
||||||
|
find_package(benchmark REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(coverage)
|
if(coverage)
|
||||||
include(XrplCov)
|
include(XrplCov)
|
||||||
endif()
|
endif()
|
||||||
@@ -146,3 +150,7 @@ if(tests)
|
|||||||
include(CTest)
|
include(CTest)
|
||||||
add_subdirectory(src/tests/libxrpl)
|
add_subdirectory(src/tests/libxrpl)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(benchmark)
|
||||||
|
add_subdirectory(src/benchmarks/libxrpl)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -83,8 +83,11 @@ If you create new source files, they must be organized as follows:
|
|||||||
`src/libxrpl`.
|
`src/libxrpl`.
|
||||||
- All other non-test files must go under `src/xrpld`.
|
- All other non-test files must go under `src/xrpld`.
|
||||||
- All test source files must go under `src/test`.
|
- All test source files must go under `src/test`.
|
||||||
|
- All benchmark source files must go under `src/benchmarks`.
|
||||||
|
|
||||||
The source must be formatted according to the style guide below.
|
The source must be formatted according to the style guide below. The easiest
|
||||||
|
way to satisfy this is to install the [`pre-commit`](#pre-commit-hooks) hooks,
|
||||||
|
which format and lint your changes automatically on every commit.
|
||||||
|
|
||||||
Header includes must be [levelized](.github/scripts/levelization).
|
Header includes must be [levelized](.github/scripts/levelization).
|
||||||
|
|
||||||
@@ -212,13 +215,61 @@ This is a non-exhaustive list of recommended style guidelines. These are
|
|||||||
not always strictly enforced and serve as a way to keep the codebase
|
not always strictly enforced and serve as a way to keep the codebase
|
||||||
coherent rather than a set of _thou shalt not_ commandments.
|
coherent rather than a set of _thou shalt not_ commandments.
|
||||||
|
|
||||||
|
## Pre-commit hooks
|
||||||
|
|
||||||
|
We use the [`pre-commit`](https://pre-commit.com/) framework to run the
|
||||||
|
formatting and linting tools that keep the codebase consistent. `pre-commit`
|
||||||
|
runs each tool configured in
|
||||||
|
[`.pre-commit-config.yaml`](./.pre-commit-config.yaml) in its own isolated
|
||||||
|
environment, so you don't need to install most of the individual tools
|
||||||
|
yourself. The version of each hook sourced from an external repository
|
||||||
|
(`clang-format`, `gersemi`, etc.) is pinned in that file, so running the hooks
|
||||||
|
locally uses exactly the same versions as CI. A few `local` hooks — most notably
|
||||||
|
`clang-tidy` — run tools from your own environment; see
|
||||||
|
[Installing clang-tidy](#installing-clang-tidy) for how to get those.
|
||||||
|
|
||||||
|
To get started, install `pre-commit` and enable the git hook scripts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install pre-commit
|
||||||
|
pre-commit install
|
||||||
|
```
|
||||||
|
|
||||||
|
Once installed, the hooks run automatically on your staged files every time you
|
||||||
|
`git commit`. You can also run them on demand:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all hooks against only the staged files
|
||||||
|
pre-commit run
|
||||||
|
|
||||||
|
# Run all hooks against every file in the repository
|
||||||
|
pre-commit run --all-files
|
||||||
|
|
||||||
|
# Run a single hook (e.g. clang-format) against all files
|
||||||
|
pre-commit run clang-format --all-files
|
||||||
|
```
|
||||||
|
|
||||||
|
The hooks configured in this repository include, among others:
|
||||||
|
|
||||||
|
- `clang-format` — C++/proto formatting (see [Formatting](#formatting))
|
||||||
|
- `clang-tidy` — C++ static analysis (see [Clang-tidy](#clang-tidy)); opt in with `TIDY=1`
|
||||||
|
- `fix-include-style`, `fix-pragma-once`, `check-doxygen-style` — C++ hygiene
|
||||||
|
- `gersemi` — CMake formatting
|
||||||
|
- `prettier`, `black`, `shfmt` — formatting for JavaScript/JSON/Markdown, Python, and shell
|
||||||
|
- `cspell` — spell checking
|
||||||
|
|
||||||
|
The same hooks run in CI on every pull request, so running them locally before
|
||||||
|
you push helps you avoid CI failures.
|
||||||
|
|
||||||
## Formatting
|
## Formatting
|
||||||
|
|
||||||
All code must conform to `clang-format` version 22,
|
All code must conform to `clang-format`, according to the settings in
|
||||||
according to the settings in [`.clang-format`](./.clang-format),
|
[`.clang-format`](./.clang-format), unless the result would be unreasonably
|
||||||
unless the result would be unreasonably difficult to read or maintain.
|
difficult to read or maintain. The `clang-format` version is pinned in
|
||||||
To demarcate lines that should be left as-is, surround them with comments like
|
[`.pre-commit-config.yaml`](./.pre-commit-config.yaml), so the
|
||||||
this:
|
[`pre-commit`](#pre-commit-hooks) hook always formats with the same version as
|
||||||
|
CI. To demarcate lines that should be left as-is, surround them with comments
|
||||||
|
like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -226,9 +277,21 @@ this:
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
```
|
```
|
||||||
|
|
||||||
You can format individual files in place by running `clang-format -i <file>...`
|
The easiest way to format your changes is to let the `pre-commit` hook run
|
||||||
|
automatically on commit, or to run it manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pre-commit run clang-format --all-files
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also format individual files in place by running `clang-format -i <file>...`
|
||||||
from any directory within this project.
|
from any directory within this project.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This uses whatever `clang-format` version is installed locally, which may
|
||||||
|
> differ from the pinned version used by `pre-commit` and CI, so the results
|
||||||
|
> can vary.
|
||||||
|
|
||||||
There is a Continuous Integration job that runs clang-format on pull requests. If the code doesn't comply, a patch file that corrects auto-fixable formatting issues is generated.
|
There is a Continuous Integration job that runs clang-format on pull requests. If the code doesn't comply, a patch file that corrects auto-fixable formatting issues is generated.
|
||||||
|
|
||||||
To download the patch file:
|
To download the patch file:
|
||||||
@@ -239,13 +302,6 @@ To download the patch file:
|
|||||||
4. Download the zip file and extract it to your local git repository. Run `git apply [patch-file-name]`.
|
4. Download the zip file and extract it to your local git repository. Run `git apply [patch-file-name]`.
|
||||||
5. Commit and push.
|
5. Commit and push.
|
||||||
|
|
||||||
You can install a pre-commit hook to automatically run `clang-format` before every commit:
|
|
||||||
|
|
||||||
```
|
|
||||||
pip3 install pre-commit
|
|
||||||
pre-commit install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Clang-tidy
|
## Clang-tidy
|
||||||
|
|
||||||
All code must pass `clang-tidy` checks according to the settings in [`.clang-tidy`](./.clang-tidy).
|
All code must pass `clang-tidy` checks according to the settings in [`.clang-tidy`](./.clang-tidy).
|
||||||
@@ -267,7 +323,7 @@ Before running clang-tidy, you must build the project to generate required files
|
|||||||
|
|
||||||
#### Via pre-commit (recommended)
|
#### Via pre-commit (recommended)
|
||||||
|
|
||||||
If you have already installed the pre-commit hooks (see above), you can run clang-tidy on your staged files using:
|
If you have already installed the [`pre-commit`](#pre-commit-hooks) hooks, you can run clang-tidy on your staged files using:
|
||||||
|
|
||||||
```
|
```
|
||||||
TIDY=1 pre-commit run clang-tidy
|
TIDY=1 pre-commit run clang-tidy
|
||||||
|
|||||||
@@ -110,6 +110,23 @@ if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Rust toolchain. Part of the Nix commonPackages, so available on both Linux
|
||||||
|
# and macOS. The cargo plugins are invoked through cargo (`cargo <sub>`), which
|
||||||
|
# resolves the matching `cargo-<sub>` binary on PATH; `--version` is offline and
|
||||||
|
# does not need a Cargo project.
|
||||||
|
if [ "${os}" = "linux" ] || [ "${os}" = "macos" ]; then
|
||||||
|
echo
|
||||||
|
echo "Rust toolchain:"
|
||||||
|
check cargo
|
||||||
|
check cargo-audit cargo audit --version
|
||||||
|
check cargo-llvm-cov cargo llvm-cov --version
|
||||||
|
check cargo-nextest cargo nextest --version
|
||||||
|
check clippy clippy-driver --version
|
||||||
|
check rust-analyzer
|
||||||
|
check rustc
|
||||||
|
check rustfmt
|
||||||
|
fi
|
||||||
|
|
||||||
# GCC is the default compiler on Linux. macOS uses the system Apple Clang
|
# GCC is the default compiler on Linux. macOS uses the system Apple Clang
|
||||||
# instead, so GCC/g++/gcov are not expected there.
|
# instead, so GCC/g++/gcov are not expected there.
|
||||||
if [ "${os}" = "linux" ]; then
|
if [ "${os}" = "linux" ]; then
|
||||||
|
|||||||
440
bin/pre-commit/check_doxygen_style.py
Executable file
440
bin/pre-commit/check_doxygen_style.py
Executable file
@@ -0,0 +1,440 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Check C++ Doxygen comment style.
|
||||||
|
|
||||||
|
Enforces the house convention for documentation comments:
|
||||||
|
|
||||||
|
* Use ``/** ... */`` blocks, not ``///``, ``//!`` or ``/*! ... */``; a plain
|
||||||
|
``/* ... */`` that contains Doxygen commands is a doc comment missing its
|
||||||
|
second star. Trailing member-after comments use ``///<`` (not ``//!<``,
|
||||||
|
``/*!< ... */`` or ``/**< ... */`` -- the block forms get reflowed and
|
||||||
|
mis-attached by clang-format on packed enum values, the line form does not).
|
||||||
|
* ``/**`` sits alone on its line; the closing ``*/`` sits alone on its line.
|
||||||
|
* Every content line is prefixed with `` * `` (no bare-indented continuation).
|
||||||
|
* The first content line is flush (not over-indented).
|
||||||
|
* Doxygen commands use the ``@cmd`` form, not ``\\cmd``.
|
||||||
|
* Use ``@return`` / ``@throws`` rather than prose ``Returns:`` / ``Throws:``.
|
||||||
|
* A plain ``//`` comment carrying a block-level ``@command`` (``@param``,
|
||||||
|
``@return``, ``@see``, ...) is documentation and must be a ``/** ... */``
|
||||||
|
block (Doxygen ignores ``//``).
|
||||||
|
* Use canonical command spellings: ``@return`` (not ``@returns``),
|
||||||
|
``@throws`` (not ``@throw``), ``@see`` (not ``@sa``).
|
||||||
|
* Order block tags ``@tparam`` -> ``@param`` -> ``@return``. (Whether
|
||||||
|
``@param`` order matches the signature is not checked here -- too fragile to
|
||||||
|
parse; Doxygen's WARN_IF_DOC_ERROR covers name mismatches.)
|
||||||
|
* One-liners are expanded to three lines, EXCEPT bare markers ``@{`` / ``@}``
|
||||||
|
/ ``@cond [label]`` / ``@endcond`` / ``@file [name]`` which stay on one line.
|
||||||
|
|
||||||
|
Left intentionally alone (recognized, valid Doxygen that is not this style's
|
||||||
|
concern):
|
||||||
|
|
||||||
|
* ``///<`` trailing "member-after" comments (the house form).
|
||||||
|
* Divider lines made only of slashes (``//////////``).
|
||||||
|
* Plain ``/* ... */`` (non-Doxygen) comments.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
check_doxygen_style.py [FILE ...] # explicit files
|
||||||
|
check_doxygen_style.py # default: src/ and include/ trees
|
||||||
|
|
||||||
|
Exit status is non-zero if any violation is found.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from collections.abc import Iterable, Iterator
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class Category(Enum):
|
||||||
|
"""A kind of style violation: a printed ``label`` and its ``description``.
|
||||||
|
|
||||||
|
The description is the default message; a few categories whose wording
|
||||||
|
depends on the offending text (see ``Finding.detail``) override it.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, label: str, description: str) -> None:
|
||||||
|
self.label = label
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
BACKSLASH_COMMAND = ("backslash-command", "use the @cmd form, not \\cmd")
|
||||||
|
WRONG_COMMAND = ("wrong-command", "use the canonical command spelling")
|
||||||
|
TRIPLE_SLASH = ("triple-slash", "use a /** ... */ block instead of ///")
|
||||||
|
QT_MEMBER = ("qt-member", "use ///< instead of //!<")
|
||||||
|
QT_LINE = ("qt-line", "use a /** ... */ block instead of //!")
|
||||||
|
BLOCK_MEMBER = ("block-member", "use ///< instead of /**<")
|
||||||
|
QT_BLOCK_MEMBER = ("qt-block-member", "use ///< instead of /*!<")
|
||||||
|
DOC_IN_LINE_COMMENT = (
|
||||||
|
"doc-in-line-comment",
|
||||||
|
"use a /** ... */ block for documentation, not //",
|
||||||
|
)
|
||||||
|
QT_COMMENT = ("qt-comment", "use /** instead of /*!")
|
||||||
|
SINGLE_LINE_BLOCK = (
|
||||||
|
"single-line-block",
|
||||||
|
"expand one-line /** ... */ to a multi-line block "
|
||||||
|
"(markers @{ @} @cond @endcond @file may stay)",
|
||||||
|
)
|
||||||
|
TEXT_ON_OPENER = ("text-on-opener", "move text off the /** opener line")
|
||||||
|
BARE_CONTINUATION = ("bare-continuation", 'prefix continuation lines with " * "')
|
||||||
|
OVER_INDENTED = ("over-indented", "first content line is over-indented")
|
||||||
|
OVER_INDENTED_TAG = (
|
||||||
|
"over-indented-tag",
|
||||||
|
'Doxygen tag over-indented; use a single space after "*"',
|
||||||
|
)
|
||||||
|
COMBINED_MARKER = (
|
||||||
|
"combined-marker",
|
||||||
|
"scope marker @{ / @} should be its own single-line /** @{ */ block",
|
||||||
|
)
|
||||||
|
PROSE_LABEL = ("prose-label", "use a Doxygen tag instead of a prose label")
|
||||||
|
CONTENT_ON_CLOSER = ("content-on-closer", "move content off the closing */ line")
|
||||||
|
PLAIN_BLOCK_DOC = (
|
||||||
|
"plain-block-doc",
|
||||||
|
"documentation comment must open with /** not /*",
|
||||||
|
)
|
||||||
|
TAG_ORDER = (
|
||||||
|
"tag-order",
|
||||||
|
"block tags out of order; expected @tparam, then @param, then @return",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Finding:
|
||||||
|
"""A single style violation at a 1-based line number.
|
||||||
|
|
||||||
|
``detail`` overrides the category's default description when the message
|
||||||
|
depends on the offending text (e.g. which command was misspelled).
|
||||||
|
"""
|
||||||
|
|
||||||
|
line: int
|
||||||
|
category: Category
|
||||||
|
detail: str | None = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message(self) -> str:
|
||||||
|
return self.detail if self.detail is not None else self.category.description
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_ROOTS = ("src", "include")
|
||||||
|
EXTS = {".h", ".hpp", ".cpp", ".ipp", ".cxx", ".cc"}
|
||||||
|
|
||||||
|
# Every Doxygen command we recognize when written with a backslash (\cmd).
|
||||||
|
_ALL_COMMANDS = (
|
||||||
|
"brief|param|tparam|return|returns|retval|note|warning|pre|post|see|sa|ref|"
|
||||||
|
"throw|throws|exception|deprecated|details|code|endcode|verbatim|endverbatim|"
|
||||||
|
"li|arg|c|internal|since|todo|attention|remark|remarks|ingroup|defgroup"
|
||||||
|
)
|
||||||
|
# Block-level tags whose over-indentation we flag inside a block body.
|
||||||
|
_BLOCK_TAGS = (
|
||||||
|
"param|tparam|returns?|retval|brief|throws?|note|warning|"
|
||||||
|
"pre|post|see|sa|details|deprecated"
|
||||||
|
)
|
||||||
|
# Tags that, appearing anywhere in a comment, mark it as documentation.
|
||||||
|
_ANY_DOC_TAGS = (
|
||||||
|
"param|tparam|returns?|retval|brief|throws?|note|warning|pre|post|see|sa"
|
||||||
|
)
|
||||||
|
# Tags that make a plain // comment a mis-styled doc comment.
|
||||||
|
_LINE_DOC_TAGS = "brief|param|tparam|returns?|retval|throws?|note|see|pre|post"
|
||||||
|
|
||||||
|
# \cmd that should be @cmd.
|
||||||
|
RE_BACKSLASH_CMD = re.compile(r"\\(" + _ALL_COMMANDS + r")\b")
|
||||||
|
# Bare markers that may legitimately stay on a single line.
|
||||||
|
RE_MARKER = re.compile(r"^@(\{|\}|cond(\s.*)?|endcond|file(\s.*)?)$")
|
||||||
|
# Prose section labels that should be Doxygen tags.
|
||||||
|
RE_PROSE_LABEL = re.compile(r"^\*\s(Returns|Throws|Exceptions):\s*$")
|
||||||
|
# An over-indented block tag: "*" followed by 2+ spaces then the tag.
|
||||||
|
RE_OVERINDENTED_TAG = re.compile(r"^\*\s{2,}@(" + _BLOCK_TAGS + r")\b")
|
||||||
|
# Any documentation tag (used to spot a doc comment hiding in a plain /* */).
|
||||||
|
RE_ANY_DOC_TAG = re.compile(r"@(" + _ANY_DOC_TAGS + r")\b")
|
||||||
|
# A documentation tag inside a // comment.
|
||||||
|
RE_LINE_DOC_TAG = re.compile(r"@(" + _LINE_DOC_TAGS + r")\b")
|
||||||
|
# Order-relevant tags, for the @tparam -> @param -> @return ordering check.
|
||||||
|
RE_ORDER_TAG = re.compile(r"^\*\s*@(param|tparam|returns?|retval)\b")
|
||||||
|
# First content line indented by 2+ spaces after the "*".
|
||||||
|
RE_FIRST_OVERINDENT = re.compile(r"^\s*\*\s{2,}\S")
|
||||||
|
# A scope marker @{ / @} sharing a comment with other text.
|
||||||
|
RE_COMBINED_MARKER = re.compile(r"^\*\s*@[{}]\s*$")
|
||||||
|
|
||||||
|
# Non-canonical command spellings -> the house spelling (bare command names).
|
||||||
|
# Used both to flag a wrong @form and to suggest the right @form for a \wrong.
|
||||||
|
CANONICAL_COMMAND = {"returns": "return", "throw": "throws", "sa": "see"}
|
||||||
|
WRONG_SPELLINGS = [
|
||||||
|
(re.compile(rf"@{wrong}\b"), f"@{right}")
|
||||||
|
for wrong, right in CANONICAL_COMMAND.items()
|
||||||
|
]
|
||||||
|
|
||||||
|
# Order block tags should appear in; a body out of this order is a violation.
|
||||||
|
EXPECTED_TAG_ORDER = ("tparam", "param", "return")
|
||||||
|
|
||||||
|
|
||||||
|
def is_doxy_open(stripped: str) -> bool:
|
||||||
|
"""True for a line-start Doxygen block opener we should normalize."""
|
||||||
|
if stripped.startswith("/*!"): # Qt-style Doxygen
|
||||||
|
return not stripped.startswith("/*!<") # member-after, leave inline
|
||||||
|
return (
|
||||||
|
stripped.startswith("/**")
|
||||||
|
and not stripped.startswith("/***")
|
||||||
|
and not stripped.startswith("/**/")
|
||||||
|
and not stripped.startswith("/**<")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _flag_commands(raw_line: str, stripped: str, index: int) -> list[Finding]:
|
||||||
|
"""Flag \\cmd and misspelled @cmd on a comment line (opener, body, or closer)."""
|
||||||
|
if not stripped.startswith(("*", "//", "/*")):
|
||||||
|
return []
|
||||||
|
findings: list[Finding] = []
|
||||||
|
backslash = RE_BACKSLASH_CMD.search(raw_line)
|
||||||
|
if backslash:
|
||||||
|
command = backslash.group(1)
|
||||||
|
canonical = CANONICAL_COMMAND.get(command, command)
|
||||||
|
findings.append(
|
||||||
|
Finding(
|
||||||
|
index + 1,
|
||||||
|
Category.BACKSLASH_COMMAND,
|
||||||
|
f"use @{canonical} instead of \\{command}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for pattern, replacement in WRONG_SPELLINGS:
|
||||||
|
wrong = pattern.search(raw_line)
|
||||||
|
if wrong:
|
||||||
|
findings.append(
|
||||||
|
Finding(
|
||||||
|
index + 1,
|
||||||
|
Category.WRONG_COMMAND,
|
||||||
|
f"use {replacement} instead of {wrong.group(0)}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return findings
|
||||||
|
|
||||||
|
|
||||||
|
def _flag_line_comment(raw_line: str, stripped: str, index: int) -> Finding | None:
|
||||||
|
"""Return the finding for a single-line comment form (///, //!, /**<, ...), else None."""
|
||||||
|
if stripped.startswith("///") and not stripped.startswith(("////", "///<")):
|
||||||
|
return Finding(index + 1, Category.TRIPLE_SLASH)
|
||||||
|
if "//!<" in raw_line:
|
||||||
|
return Finding(index + 1, Category.QT_MEMBER)
|
||||||
|
if stripped.startswith("//!"):
|
||||||
|
return Finding(index + 1, Category.QT_LINE)
|
||||||
|
if "/**<" in raw_line:
|
||||||
|
return Finding(index + 1, Category.BLOCK_MEMBER)
|
||||||
|
if "/*!<" in raw_line:
|
||||||
|
return Finding(index + 1, Category.QT_BLOCK_MEMBER)
|
||||||
|
if stripped.startswith("//") and RE_LINE_DOC_TAG.search(stripped):
|
||||||
|
return Finding(index + 1, Category.DOC_IN_LINE_COMMENT)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _flag_single_line_block(stripped: str, line_no: int, is_qt: bool) -> list[Finding]:
|
||||||
|
"""Findings for a whole /** ... */ or /*! ... */ block on one line."""
|
||||||
|
inner = re.sub(r"^/\*[*!]", "", stripped)
|
||||||
|
inner = re.sub(r"\*/\s*$", "", inner).strip()
|
||||||
|
findings: list[Finding] = []
|
||||||
|
if is_qt:
|
||||||
|
findings.append(Finding(line_no, Category.QT_COMMENT))
|
||||||
|
if inner and not RE_MARKER.match(inner):
|
||||||
|
findings.append(Finding(line_no, Category.SINGLE_LINE_BLOCK))
|
||||||
|
return findings
|
||||||
|
|
||||||
|
|
||||||
|
def _canonical_order_tag(body: str) -> str | None:
|
||||||
|
"""The order-relevant tag (tparam/param/return) a body line opens with, if any."""
|
||||||
|
match = RE_ORDER_TAG.match(body)
|
||||||
|
if match is None:
|
||||||
|
return None
|
||||||
|
command = match.group(1)
|
||||||
|
return "return" if command in ("return", "returns", "retval") else command
|
||||||
|
|
||||||
|
|
||||||
|
def _flag_body_line(
|
||||||
|
body_line: str, line_no: int, is_first_content: bool
|
||||||
|
) -> list[Finding]:
|
||||||
|
"""Findings for one interior line of a multi-line block."""
|
||||||
|
body = body_line.strip()
|
||||||
|
findings: list[Finding] = []
|
||||||
|
if body and not body.startswith("*"):
|
||||||
|
findings.append(Finding(line_no, Category.BARE_CONTINUATION))
|
||||||
|
if body.startswith("*"):
|
||||||
|
if is_first_content and RE_FIRST_OVERINDENT.match(body_line):
|
||||||
|
findings.append(Finding(line_no, Category.OVER_INDENTED))
|
||||||
|
if RE_OVERINDENTED_TAG.match(body):
|
||||||
|
findings.append(Finding(line_no, Category.OVER_INDENTED_TAG))
|
||||||
|
if RE_COMBINED_MARKER.match(body):
|
||||||
|
findings.append(Finding(line_no, Category.COMBINED_MARKER))
|
||||||
|
label = RE_PROSE_LABEL.match(body)
|
||||||
|
if label:
|
||||||
|
suggested_tag = "@return" if label.group(1) == "Returns" else "@throws"
|
||||||
|
findings.append(
|
||||||
|
Finding(
|
||||||
|
line_no,
|
||||||
|
Category.PROSE_LABEL,
|
||||||
|
f'use {suggested_tag} instead of prose "{label.group(1)}:"',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return findings
|
||||||
|
|
||||||
|
|
||||||
|
def _flag_closer(closer_line: str, line_no: int) -> list[Finding]:
|
||||||
|
"""Findings for content sharing the closing */ line."""
|
||||||
|
before = closer_line[: closer_line.index("*/")].strip()
|
||||||
|
if before and before != "*":
|
||||||
|
return [Finding(line_no, Category.CONTENT_ON_CLOSER)]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _flag_tag_order(first_tag_line: dict[str, int]) -> list[Finding]:
|
||||||
|
"""One finding if the present block tags are not in EXPECTED_TAG_ORDER."""
|
||||||
|
tag_lines = [
|
||||||
|
first_tag_line[tag] for tag in EXPECTED_TAG_ORDER if tag in first_tag_line
|
||||||
|
]
|
||||||
|
if tag_lines != sorted(tag_lines):
|
||||||
|
return [Finding(min(tag_lines), Category.TAG_ORDER)]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _flag_doxy_block(lines: list[str], start: int) -> tuple[int, list[Finding]]:
|
||||||
|
"""Handle a /** or /*! block opening at ``start``; return (next index, findings)."""
|
||||||
|
raw_line = lines[start]
|
||||||
|
stripped = raw_line.lstrip()
|
||||||
|
open_pos = raw_line.index("/*")
|
||||||
|
is_qt = stripped.startswith("/*!")
|
||||||
|
|
||||||
|
# A whole block on one line: /** ... */.
|
||||||
|
if "*/" in raw_line[open_pos + 2 :]:
|
||||||
|
return start + 1, _flag_single_line_block(stripped, start + 1, is_qt)
|
||||||
|
|
||||||
|
# Multi-line block: opener, then scan the body to the closer.
|
||||||
|
findings: list[Finding] = []
|
||||||
|
if is_qt:
|
||||||
|
findings.append(Finding(start + 1, Category.QT_COMMENT))
|
||||||
|
if raw_line[open_pos + 3 :].strip():
|
||||||
|
findings.append(Finding(start + 1, Category.TEXT_ON_OPENER))
|
||||||
|
|
||||||
|
line_count = len(lines)
|
||||||
|
cursor = start + 1
|
||||||
|
is_first_content = True
|
||||||
|
first_tag_line: dict[str, int] = {} # canonical tag -> 1-based first line
|
||||||
|
while cursor < line_count and "*/" not in lines[cursor]:
|
||||||
|
body_line = lines[cursor]
|
||||||
|
body = body_line.strip()
|
||||||
|
findings.extend(_flag_commands(body_line, body, cursor))
|
||||||
|
tag = _canonical_order_tag(body)
|
||||||
|
if tag is not None:
|
||||||
|
first_tag_line.setdefault(tag, cursor + 1)
|
||||||
|
findings.extend(_flag_body_line(body_line, cursor + 1, is_first_content))
|
||||||
|
if body.startswith("*"):
|
||||||
|
is_first_content = False
|
||||||
|
cursor += 1
|
||||||
|
|
||||||
|
if cursor < line_count:
|
||||||
|
closer_line = lines[cursor]
|
||||||
|
findings.extend(_flag_commands(closer_line, closer_line.strip(), cursor))
|
||||||
|
findings.extend(_flag_closer(closer_line, cursor + 1))
|
||||||
|
findings.extend(_flag_tag_order(first_tag_line))
|
||||||
|
|
||||||
|
return cursor + 1, findings
|
||||||
|
|
||||||
|
|
||||||
|
def _flag_plain_block(lines: list[str], start: int) -> tuple[int, list[Finding]]:
|
||||||
|
"""Handle a line-start plain /* ... */ block; return (next index, findings).
|
||||||
|
|
||||||
|
Only flagged when it hides a documentation command (a missing second star).
|
||||||
|
"""
|
||||||
|
line_count = len(lines)
|
||||||
|
cursor = start
|
||||||
|
while cursor < line_count and "*/" not in lines[cursor]:
|
||||||
|
cursor += 1
|
||||||
|
findings: list[Finding] = []
|
||||||
|
# The opener (start) is command-checked by check_file; check the rest here.
|
||||||
|
for i in range(start + 1, min(cursor + 1, line_count)):
|
||||||
|
findings.extend(_flag_commands(lines[i], lines[i].strip(), i))
|
||||||
|
block_text = "\n".join(
|
||||||
|
lines[start : cursor + 1] if cursor < line_count else lines[start:]
|
||||||
|
)
|
||||||
|
if RE_ANY_DOC_TAG.search(block_text):
|
||||||
|
findings.append(Finding(start + 1, Category.PLAIN_BLOCK_DOC))
|
||||||
|
next_index = cursor + 1 if cursor < line_count else line_count
|
||||||
|
return next_index, findings
|
||||||
|
|
||||||
|
|
||||||
|
def check_source(text: str) -> list[Finding]:
|
||||||
|
"""Return all style violations found in the given source text."""
|
||||||
|
lines = text.split("\n")
|
||||||
|
findings: list[Finding] = []
|
||||||
|
line_count = len(lines)
|
||||||
|
index = 0
|
||||||
|
in_plain_block = False # inside a mid-line, non-Doxygen /* ... */
|
||||||
|
while index < line_count:
|
||||||
|
raw_line = lines[index]
|
||||||
|
stripped = raw_line.lstrip()
|
||||||
|
|
||||||
|
# Skip the interior of a plain block opened on an earlier line.
|
||||||
|
if in_plain_block:
|
||||||
|
in_plain_block = "*/" not in raw_line
|
||||||
|
index += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
findings.extend(_flag_commands(raw_line, stripped, index))
|
||||||
|
|
||||||
|
line_finding = _flag_line_comment(raw_line, stripped, index)
|
||||||
|
if line_finding is not None:
|
||||||
|
findings.append(line_finding)
|
||||||
|
index += 1
|
||||||
|
elif is_doxy_open(stripped):
|
||||||
|
index, block_findings = _flag_doxy_block(lines, index)
|
||||||
|
findings.extend(block_findings)
|
||||||
|
elif stripped.startswith("/*"):
|
||||||
|
index, block_findings = _flag_plain_block(lines, index)
|
||||||
|
findings.extend(block_findings)
|
||||||
|
else:
|
||||||
|
# A /* that opens mid-line without closing starts a plain block.
|
||||||
|
if "/*" in raw_line and not stripped.startswith("//"):
|
||||||
|
if "*/" not in raw_line[raw_line.index("/*") + 2 :]:
|
||||||
|
in_plain_block = True
|
||||||
|
index += 1
|
||||||
|
return findings
|
||||||
|
|
||||||
|
|
||||||
|
def check_file(path: Path) -> list[Finding]:
|
||||||
|
"""Return all style violations found in one file."""
|
||||||
|
return check_source(path.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def iter_files(paths: Iterable[str]) -> Iterator[Path]:
|
||||||
|
"""Yield every C++ source file among the given files and directories."""
|
||||||
|
for raw_path in paths:
|
||||||
|
path = Path(raw_path)
|
||||||
|
if path.is_dir():
|
||||||
|
for candidate in path.rglob("*"):
|
||||||
|
if candidate.is_file() and candidate.suffix in EXTS:
|
||||||
|
yield candidate
|
||||||
|
elif path.suffix in EXTS:
|
||||||
|
yield path
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="Check Doxygen comment style.")
|
||||||
|
parser.add_argument(
|
||||||
|
"files", nargs="*", help="files or directories (default: src/ include/)"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-q", "--quiet", action="store_true", help="only print the summary count"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
roots = args.files or [root for root in DEFAULT_ROOTS if Path(root).is_dir()]
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
for path in sorted(set(iter_files(roots)), key=str):
|
||||||
|
for finding in check_file(path):
|
||||||
|
total += 1
|
||||||
|
if not args.quiet:
|
||||||
|
print(
|
||||||
|
f"{path}:{finding.line}: {finding.category.label}: {finding.message}"
|
||||||
|
)
|
||||||
|
print(f"\n{total} doxygen-style violation(s)", file=sys.stderr)
|
||||||
|
return 1 if total else 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -1,27 +1,46 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Pre-commit hook that runs clang-tidy on changed files using run-clang-tidy.
|
"""Pre-commit hook that runs clang-tidy on staged files using run-clang-tidy.
|
||||||
|
|
||||||
The set of files is chosen by pre-commit (see .pre-commit-config.yaml), which
|
The script determines the staged files itself (see `pass_filenames: false` in
|
||||||
filters to C/C++ sources and excludes `.ipp` fragments. Headers are linted
|
.pre-commit-config.yaml) so run-clang-tidy is run once and handles parallelism
|
||||||
directly: the `verify_headers` build option (ON by default) compiles every
|
internally: pre-commit would otherwise split the files across parallel hook
|
||||||
`.h`/`.hpp` on its own, so each header is the main file of its own
|
invocations that race when fixes edit a shared header.
|
||||||
compile_commands.json entry and run-clang-tidy can analyse it just like a
|
|
||||||
`.cpp`.
|
Fixes are collected with `-export-fixes` and applied by clang-apply-replacements
|
||||||
|
in a separate step rather than with run-clang-tidy's `-fix`. The `add_module`
|
||||||
|
build isolates each module's headers behind a per-module symlink directory
|
||||||
|
(build/modules/<module>/...), so a header reachable from several translation
|
||||||
|
units is referenced through different paths that all resolve to the same source
|
||||||
|
file. clang-apply-replacements deduplicates identical replacements by their
|
||||||
|
literal path, so those paths must be canonicalised to the real source path
|
||||||
|
first; otherwise the same fix is applied once per path and corrupts the header.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
CLANG_TIDY_VERSION = 22
|
CLANG_TIDY_VERSION = 22
|
||||||
|
|
||||||
|
# Extensions run-clang-tidy can analyse: `.cpp` translation units and, thanks to
|
||||||
|
# the `verify_headers` build option, `.h`/`.hpp` headers (each has its own
|
||||||
|
# compile_commands.json entry). `.ipp` fragments have no entry and are skipped.
|
||||||
|
TIDY_EXTENSIONS = {".cpp", ".h", ".hpp"}
|
||||||
|
|
||||||
def find_run_clang_tidy() -> str | None:
|
# A single-quoted `FilePath:` entry in an -export-fixes YAML file, allowing the
|
||||||
for candidate in (f"run-clang-tidy-{CLANG_TIDY_VERSION}", "run-clang-tidy"):
|
# `- ` marker that precedes it inside a `Replacements:` sequence. clang-tidy
|
||||||
|
# emits paths single-quoted and doubles any embedded quote per YAML rules.
|
||||||
|
FILEPATH_RE = re.compile(r"^(\s*(?:-\s+)?FilePath:\s*)'((?:[^']|'')*)'\s*$")
|
||||||
|
|
||||||
|
|
||||||
|
def find_tool(name: str) -> str | None:
|
||||||
|
for candidate in (f"{name}-{CLANG_TIDY_VERSION}", name):
|
||||||
if path := shutil.which(candidate):
|
if path := shutil.which(candidate):
|
||||||
return path
|
return path
|
||||||
return None
|
return None
|
||||||
@@ -35,23 +54,43 @@ def find_build_dir(repo_root: Path) -> Path | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def staged_files(repo_root: Path) -> list[Path]:
|
||||||
|
"""Return absolute paths of staged, lint-able C/C++ files.
|
||||||
|
|
||||||
|
`--diff-filter=d` excludes deletions so we never lint a removed file.
|
||||||
|
"""
|
||||||
|
output = subprocess.check_output(
|
||||||
|
["git", "diff", "--staged", "--name-only", "--diff-filter=d", "--"]
|
||||||
|
+ [f"*{ext}" for ext in TIDY_EXTENSIONS],
|
||||||
|
text=True,
|
||||||
|
cwd=repo_root,
|
||||||
|
)
|
||||||
|
return [repo_root / rel for rel in output.splitlines() if rel]
|
||||||
|
|
||||||
|
|
||||||
|
def canonicalize_fix_paths(fixes_dir: Path) -> None:
|
||||||
|
"""Rewrite every `FilePath` in the exported fixes to its real source path.
|
||||||
|
|
||||||
|
A header included through a module's isolation symlink is recorded under that
|
||||||
|
symlink's path; collapsing all paths to the same real file lets
|
||||||
|
clang-apply-replacements recognise the per-translation-unit duplicates and
|
||||||
|
apply each fix once.
|
||||||
|
"""
|
||||||
|
for yaml in fixes_dir.glob("*.yaml"):
|
||||||
|
lines = []
|
||||||
|
for line in yaml.read_text().splitlines():
|
||||||
|
if m := FILEPATH_RE.match(line):
|
||||||
|
path = m.group(2).replace("''", "'")
|
||||||
|
real = os.path.realpath(path).replace("'", "''")
|
||||||
|
line = f"{m.group(1)}'{real}'"
|
||||||
|
lines.append(line)
|
||||||
|
yaml.write_text("\n".join(lines) + "\n")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if not os.environ.get("TIDY"):
|
if not os.environ.get("TIDY"):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
files = sys.argv[1:]
|
|
||||||
if not files:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
run_clang_tidy = find_run_clang_tidy()
|
|
||||||
if not run_clang_tidy:
|
|
||||||
print(
|
|
||||||
f"clang-tidy check failed: TIDY is enabled but neither "
|
|
||||||
f"'run-clang-tidy-{CLANG_TIDY_VERSION}' nor 'run-clang-tidy' was found in PATH.",
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
repo_root = Path(
|
repo_root = Path(
|
||||||
subprocess.check_output(
|
subprocess.check_output(
|
||||||
["git", "rev-parse", "--show-toplevel"],
|
["git", "rev-parse", "--show-toplevel"],
|
||||||
@@ -59,6 +98,29 @@ def main():
|
|||||||
text=True,
|
text=True,
|
||||||
).strip()
|
).strip()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
files = staged_files(repo_root)
|
||||||
|
if not files:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
run_clang_tidy = find_tool("run-clang-tidy")
|
||||||
|
clang_apply_replacements = find_tool("clang-apply-replacements")
|
||||||
|
missing = [
|
||||||
|
name
|
||||||
|
for name, path in (
|
||||||
|
("run-clang-tidy", run_clang_tidy),
|
||||||
|
("clang-apply-replacements", clang_apply_replacements),
|
||||||
|
)
|
||||||
|
if not path
|
||||||
|
]
|
||||||
|
if missing:
|
||||||
|
print(
|
||||||
|
f"clang-tidy check failed: TIDY is enabled but {' and '.join(missing)} "
|
||||||
|
f"was not found in PATH (tried the '-{CLANG_TIDY_VERSION}' suffix too).",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
build_dir = find_build_dir(repo_root)
|
build_dir = find_build_dir(repo_root)
|
||||||
if not build_dir:
|
if not build_dir:
|
||||||
print(
|
print(
|
||||||
@@ -68,11 +130,23 @@ def main():
|
|||||||
)
|
)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
result = subprocess.run(
|
with tempfile.TemporaryDirectory() as fixes_dir:
|
||||||
[run_clang_tidy, "-quiet", "-p", str(build_dir), "-fix", "-allow-no-checks"]
|
result = subprocess.run(
|
||||||
+ files
|
[
|
||||||
)
|
run_clang_tidy,
|
||||||
return result.returncode
|
"-quiet",
|
||||||
|
"-p",
|
||||||
|
build_dir,
|
||||||
|
"-export-fixes",
|
||||||
|
fixes_dir,
|
||||||
|
"-allow-no-checks",
|
||||||
|
]
|
||||||
|
+ files
|
||||||
|
)
|
||||||
|
canonicalize_fix_paths(Path(fixes_dir))
|
||||||
|
applied = subprocess.run([clang_apply_replacements, fixes_dir])
|
||||||
|
|
||||||
|
return result.returncode or applied.returncode
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
406
bin/pre-commit/test_check_doxygen_style.py
Executable file
406
bin/pre-commit/test_check_doxygen_style.py
Executable file
@@ -0,0 +1,406 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Tests for check_doxygen_style.py.
|
||||||
|
|
||||||
|
Run directly (no test framework needed):
|
||||||
|
./bin/pre-commit/test_check_doxygen_style.py
|
||||||
|
or under pytest:
|
||||||
|
pytest bin/pre-commit/test_check_doxygen_style.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from check_doxygen_style import Finding, check_source
|
||||||
|
|
||||||
|
|
||||||
|
def findings_for(text: str) -> list[Finding]:
|
||||||
|
"""Return the style violations for the given source text.
|
||||||
|
|
||||||
|
The text is dedented and its leading newline stripped, so fixtures can be
|
||||||
|
written as indented triple-quoted here-docs while keeping honest 1-based
|
||||||
|
line numbers.
|
||||||
|
"""
|
||||||
|
text = textwrap.dedent(text).lstrip("\n")
|
||||||
|
return check_source(text)
|
||||||
|
|
||||||
|
|
||||||
|
def labels_for(text: str) -> list[str]:
|
||||||
|
return [f.category.label for f in findings_for(text)]
|
||||||
|
|
||||||
|
|
||||||
|
def messages_for(text: str) -> list[str]:
|
||||||
|
return [f.message for f in findings_for(text)]
|
||||||
|
|
||||||
|
|
||||||
|
# --- well-formed input produces nothing -------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_clean_block_ok() -> None:
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* Brief.
|
||||||
|
*
|
||||||
|
* @tparam T a type
|
||||||
|
* @param x the x
|
||||||
|
* @return the result
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert findings_for(code) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_blank_lines_inside_block_ok() -> None:
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* a
|
||||||
|
*
|
||||||
|
* b
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert findings_for(code) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_member_and_divider_allowed() -> None:
|
||||||
|
assert findings_for("int x; ///< ok member\n") == []
|
||||||
|
assert findings_for("//////////\n") == []
|
||||||
|
assert findings_for("//// text\n") == []
|
||||||
|
|
||||||
|
|
||||||
|
# --- line-comment forms ------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_triple_slash() -> None:
|
||||||
|
code = "/// doc\n"
|
||||||
|
assert labels_for(code) == ["triple-slash"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_qt_line() -> None:
|
||||||
|
code = "//! doc\n"
|
||||||
|
assert labels_for(code) == ["qt-line"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_qt_member() -> None:
|
||||||
|
code = "int x; //!< doc\n"
|
||||||
|
assert labels_for(code) == ["qt-member"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_block_member() -> None:
|
||||||
|
code = "int x; /**< doc */\n"
|
||||||
|
assert labels_for(code) == ["block-member"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_qt_block_member() -> None:
|
||||||
|
code = "int x; /*!< doc */\n"
|
||||||
|
assert labels_for(code) == ["qt-block-member"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_doc_in_line_comment() -> None:
|
||||||
|
code = "// @param x\n"
|
||||||
|
assert labels_for(code) == ["doc-in-line-comment"]
|
||||||
|
|
||||||
|
|
||||||
|
# --- block forms -------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_qt_comment() -> None:
|
||||||
|
code = """
|
||||||
|
/*!
|
||||||
|
* brief
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["qt-comment"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_qt_comment_single_line() -> None:
|
||||||
|
# /*! ... */ on one line -> qt-comment (plus single-line-block)
|
||||||
|
code = "/*! brief */\n"
|
||||||
|
assert labels_for(code) == ["qt-comment", "single-line-block"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_single_line_block() -> None:
|
||||||
|
code = "/** brief */\n"
|
||||||
|
assert labels_for(code) == ["single-line-block"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_single_line_markers_allowed() -> None:
|
||||||
|
for marker in ("@{", "@}", "@cond LABEL", "@endcond", "@file foo.h"):
|
||||||
|
code = f"/** {marker} */\n"
|
||||||
|
assert findings_for(code) == [], marker
|
||||||
|
|
||||||
|
|
||||||
|
def test_text_on_opener() -> None:
|
||||||
|
code = """
|
||||||
|
/** text here
|
||||||
|
* more
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["text-on-opener"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_bare_continuation() -> None:
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* a
|
||||||
|
bare line
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["bare-continuation"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_over_indented_first_line() -> None:
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* over
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["over-indented"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_over_indented_tag() -> None:
|
||||||
|
# a flush first line consumes "first content", isolating the tag check
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* brief
|
||||||
|
* @param x
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["over-indented-tag"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_combined_marker() -> None:
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["combined-marker"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_prose_label() -> None:
|
||||||
|
for word in ("Returns", "Throws", "Exceptions"):
|
||||||
|
code = f"""
|
||||||
|
/**
|
||||||
|
* {word}:
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["prose-label"], word
|
||||||
|
|
||||||
|
|
||||||
|
def test_content_on_closer() -> None:
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* a
|
||||||
|
* b */
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["content-on-closer"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_plain_block_doc() -> None:
|
||||||
|
assert labels_for("/* @param x */\n") == ["plain-block-doc"]
|
||||||
|
assert findings_for("/* just an ordinary note */\n") == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_tag_order() -> None:
|
||||||
|
out_of_order = """
|
||||||
|
/**
|
||||||
|
* @param x
|
||||||
|
* @tparam T
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(out_of_order) == ["tag-order"]
|
||||||
|
|
||||||
|
correct = """
|
||||||
|
/**
|
||||||
|
* @tparam T
|
||||||
|
* @param x
|
||||||
|
* @return r
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert findings_for(correct) == []
|
||||||
|
|
||||||
|
single = """
|
||||||
|
/**
|
||||||
|
* @param x
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert findings_for(single) == [] # single tag: never out of order
|
||||||
|
|
||||||
|
|
||||||
|
# --- command spelling (must work on body/closer lines, not just the opener) --
|
||||||
|
|
||||||
|
|
||||||
|
def test_backslash_command_on_body_line() -> None:
|
||||||
|
code = r"""
|
||||||
|
/**
|
||||||
|
* \brief x
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["backslash-command"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_backslash_command_suggests_canonical_spelling() -> None:
|
||||||
|
# a backslash + non-canonical spelling is fixed in one pass, not two:
|
||||||
|
# \sa -> @see (not @sa), \returns -> @return (not @returns)
|
||||||
|
sa = r"""
|
||||||
|
/**
|
||||||
|
* \sa other
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert messages_for(sa) == [r"use @see instead of \sa"]
|
||||||
|
|
||||||
|
returns = r"""
|
||||||
|
/**
|
||||||
|
* \returns x
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert messages_for(returns) == [r"use @return instead of \returns"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_wrong_command_on_body_line() -> None:
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* @returns x
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["wrong-command"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_body_line_commands_regression() -> None:
|
||||||
|
# regression: these live on body lines of a multi-line block
|
||||||
|
code = r"""
|
||||||
|
/**
|
||||||
|
* @returns bad
|
||||||
|
* @throw ex
|
||||||
|
* @sa other
|
||||||
|
* \param y
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == [
|
||||||
|
"wrong-command",
|
||||||
|
"wrong-command",
|
||||||
|
"wrong-command",
|
||||||
|
"backslash-command",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_command_on_closer_line() -> None:
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* a
|
||||||
|
* @sa b */
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["wrong-command", "content-on-closer"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_double_count_across_opener_body_closer() -> None:
|
||||||
|
code = """
|
||||||
|
/** @returns opener
|
||||||
|
* @throw body
|
||||||
|
* @sa closer */
|
||||||
|
"""
|
||||||
|
assert labels_for(code).count("wrong-command") == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_code_with_word_allowed() -> None:
|
||||||
|
# @code{.cpp} is valid Doxygen and must not be flagged
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* @code{.cpp}
|
||||||
|
* int x;
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert findings_for(code) == []
|
||||||
|
|
||||||
|
|
||||||
|
# --- rendered message text ---------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_message_uses_category_description() -> None:
|
||||||
|
# a static category renders its default description
|
||||||
|
code = "/// doc\n"
|
||||||
|
assert messages_for(code) == ["use a /** ... */ block instead of ///"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_message_detail_overrides() -> None:
|
||||||
|
# dynamic categories render the offending text via Finding.detail
|
||||||
|
backslash = r"""
|
||||||
|
/**
|
||||||
|
* \param y
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert messages_for(backslash) == [r"use @param instead of \param"]
|
||||||
|
|
||||||
|
wrong = """
|
||||||
|
/**
|
||||||
|
* @returns x
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert messages_for(wrong) == ["use @return instead of @returns"]
|
||||||
|
|
||||||
|
prose = """
|
||||||
|
/**
|
||||||
|
* Throws:
|
||||||
|
*/
|
||||||
|
"""
|
||||||
|
assert messages_for(prose) == ['use @throws instead of prose "Throws:"']
|
||||||
|
|
||||||
|
|
||||||
|
# --- robustness --------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_file_no_crash() -> None:
|
||||||
|
assert findings_for("") == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_mid_line_plain_block_skipped() -> None:
|
||||||
|
# a /* opened mid-line (after code) and spanning lines is skipped, so its
|
||||||
|
# comment-like contents are not analyzed
|
||||||
|
code = """
|
||||||
|
int x = 0; /* note: @returns is not a real tag here
|
||||||
|
* @param also not real
|
||||||
|
*/
|
||||||
|
int y = 0;
|
||||||
|
"""
|
||||||
|
assert findings_for(code) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_unclosed_block_scanned_to_eof() -> None:
|
||||||
|
# an unterminated /** block is still scanned to EOF (no crash, body checked)
|
||||||
|
code = """
|
||||||
|
/**
|
||||||
|
* @returns x
|
||||||
|
"""
|
||||||
|
assert labels_for(code) == ["wrong-command"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_banner_and_empty_comment_not_flagged() -> None:
|
||||||
|
code = """
|
||||||
|
/***
|
||||||
|
* banner
|
||||||
|
***/
|
||||||
|
"""
|
||||||
|
assert findings_for(code) == []
|
||||||
|
assert findings_for("/**/\n") == []
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
tests = sorted(
|
||||||
|
(name, fn)
|
||||||
|
for name, fn in globals().items()
|
||||||
|
if name.startswith("test_") and callable(fn)
|
||||||
|
)
|
||||||
|
failed = 0
|
||||||
|
for name, fn in tests:
|
||||||
|
try:
|
||||||
|
fn()
|
||||||
|
print(f"PASS {name}")
|
||||||
|
except AssertionError as exc:
|
||||||
|
failed += 1
|
||||||
|
print(f"FAIL {name}: {exc!r}")
|
||||||
|
print(f"\n{len(tests) - failed}/{len(tests)} passed")
|
||||||
|
return 1 if failed else 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
36
cmake/XrplAddBenchmark.cmake
Normal file
36
cmake/XrplAddBenchmark.cmake
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
include(isolate_headers)
|
||||||
|
|
||||||
|
# Define a benchmark executable for the module `name`.
|
||||||
|
#
|
||||||
|
# This follows the same general pattern as other build helpers in this repo
|
||||||
|
# (e.g. `add_module`): create a target and isolate headers, but here the target
|
||||||
|
# is a benchmark executable and no `add_test(...)` is registered.
|
||||||
|
#
|
||||||
|
# `isolate_headers` exposes only `${CMAKE_CURRENT_SOURCE_DIR}/${name}` on the
|
||||||
|
# include path, rooted at `src`, so a benchmark's own headers are reached as
|
||||||
|
# `<benchmarks/.../${name}/...>` and nothing else in the tree leaks in.
|
||||||
|
function(xrpl_add_benchmark name)
|
||||||
|
set(target ${PROJECT_NAME}.bench.${name})
|
||||||
|
|
||||||
|
file(
|
||||||
|
GLOB_RECURSE sources
|
||||||
|
CONFIGURE_DEPENDS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp"
|
||||||
|
)
|
||||||
|
add_executable(${target} ${ARGN} ${sources})
|
||||||
|
|
||||||
|
# Benchmark sources register cases through Google Benchmark's static
|
||||||
|
# registrars (anonymous-namespace lambdas). Merging several such files into
|
||||||
|
# one unity translation unit collides those internal-linkage entities, so
|
||||||
|
# keep benchmarks out of the unity build - mirroring xrpl.libpb in
|
||||||
|
# XrplCore.cmake. Each file compiles fine on its own.
|
||||||
|
set_target_properties(${target} PROPERTIES UNITY_BUILD OFF)
|
||||||
|
|
||||||
|
isolate_headers(
|
||||||
|
${target}
|
||||||
|
"${CMAKE_SOURCE_DIR}/src"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${name}"
|
||||||
|
PRIVATE
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
@@ -30,6 +30,8 @@ if(tests)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
option(benchmark "Build benchmarks" ON)
|
||||||
|
|
||||||
# Enabled by default so every header is compiled on its own as the main file of
|
# Enabled by default so every header is compiled on its own as the main file of
|
||||||
# its own compile_commands.json entry - this is what lets clang-tidy (and clangd
|
# its own compile_commands.json entry - this is what lets clang-tidy (and clangd
|
||||||
# and IDEs) analyse a header's own includes directly. The per-header objects are
|
# and IDEs) analyse a header's own includes directly. The per-header objects are
|
||||||
|
|||||||
@@ -177,7 +177,9 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir
|
|||||||
object_ = *sle;
|
object_ = *sle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Ledger entry-specific field setters */
|
/**
|
||||||
|
* @brief Ledger entry-specific field setters
|
||||||
|
*/
|
||||||
% for field in fields:
|
% for field in fields:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -185,7 +185,9 @@ public:
|
|||||||
object_ = *tx;
|
object_ = *tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Transaction-specific field setters */
|
/**
|
||||||
|
* @brief Transaction-specific field setters
|
||||||
|
*/
|
||||||
% for field in fields:
|
% for field in fields:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
11
conan.lock
11
conan.lock
@@ -10,22 +10,23 @@
|
|||||||
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1782392413.075713",
|
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1782392413.075713",
|
||||||
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897",
|
"re2/20251105#8579cfd0bda4daf0683f9e3898f964b4%1782392402.431897",
|
||||||
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
|
"protobuf/6.33.5#ff253ead763bd8d9904a52979cd21e81%1782392410.233933",
|
||||||
"openssl/3.6.3#1163d4ddc603907084d08a6a0c6e580f%1782307150.583886",
|
"openssl/3.6.3#f806de8933e3bf6f01016c6a888cee2e%1783945160.863288",
|
||||||
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1782392402.297166",
|
"nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1782392402.297166",
|
||||||
"mpt-crypto/0.4.0-rc2#a580f2f9ad0e795de696aa62d54fb9af%1782425834.488828",
|
"mpt-crypto/0.4.0-rc4#ffdba12f2332357f0d8b0ae944cfff52%1784138702.932355",
|
||||||
"lz4/1.10.0#982d9b673900f665a1da109e09c17cab%1782392402.164188",
|
"lz4/1.10.0#982d9b673900f665a1da109e09c17cab%1782392402.164188",
|
||||||
"libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1782392792.775744",
|
"libiconv/1.17#9923bc6dc6f106646d6967e0039a5ada%1782392792.775744",
|
||||||
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1782392402.420732",
|
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1782392402.420732",
|
||||||
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892",
|
"libarchive/3.8.7#c446109bd1f1d8ba7936c94189bc50e6%1782392403.066892",
|
||||||
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
|
"jemalloc/5.3.1#1fc58d55316041f10fbc1e8a2eae632a%1776700028.228",
|
||||||
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
|
"gtest/1.17.0#5224b3b3ff3b4ce1133cbdd27d53ee7d%1782392402.791979",
|
||||||
"grpc/1.81.1#5217e6ef0544c42b46f4af35d5e7f649%1782307148.845616",
|
"grpc/1.81.1#f729f6d75992d20f9c72828e9142d62f%1783945160.094135",
|
||||||
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
|
"ed25519/2015.03#ae761bdc52730a843f0809bdf6c1b1f6%1782307148.15562",
|
||||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
|
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1782392402.538492",
|
||||||
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
|
"c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1782392402.681654",
|
||||||
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1782392402.296732",
|
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1782392402.296732",
|
||||||
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1782392419.475605",
|
"boost/1.91.0#ea540ca2133d831b560036aa24dece3c%1782392419.475605",
|
||||||
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
|
"benchmark/1.9.5#b885dc73ad67b40a55d45684d1c88ad1%1782736613.864841",
|
||||||
|
"abseil/20250127.0#9ef01c1451a8340f9022e46238c0fbb6%1783945159.651047"
|
||||||
],
|
],
|
||||||
"build_requires": [
|
"build_requires": [
|
||||||
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
|
"zlib/1.3.2#1cb806da49011867778ffb6ac7190fcb%1782392402.122708",
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
|
"b2/5.4.2#ffd6084a119587e70f11cd45d1a386e2%1782392402.624226",
|
||||||
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
|
"automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56",
|
||||||
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
|
"autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86",
|
||||||
"abseil/20250127.0#bb0baf1f362bc4a725a24eddd419b8f7%1782307147.395833"
|
"abseil/20250127.0#9ef01c1451a8340f9022e46238c0fbb6%1783945159.651047"
|
||||||
],
|
],
|
||||||
"python_requires": [],
|
"python_requires": [],
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|||||||
@@ -20,6 +20,22 @@ compiler.libcxx={{ detect_api.detect_libcxx(compiler, version, compiler_exe) }}
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
[conf]
|
[conf]
|
||||||
|
{# The Boost recipe builds with b2, which doesn't use Conan's toolchain files. #}
|
||||||
|
{# Instead it hand-rolls the compiler for user-config.jam, #}
|
||||||
|
{# and its fallback probes a version-suffixed binary (e.g. `g++-15`) before plain `g++`. #}
|
||||||
|
{# Inside the Nix shell the wrapper only provides `g++`/`gcc` (no `-15` suffix), #}
|
||||||
|
{# so on a host that also has a system `g++-15` the probe escapes Nix #}
|
||||||
|
{# and picks the system compiler, which is mismatched with the Nix libraries #}
|
||||||
|
{# and breaks the build (e.g. Boost.Stacktrace link checks fail). #}
|
||||||
|
{# Pinning the executables here short-circuits that probe so Boost (and the rest of the toolchain) #}
|
||||||
|
{# resolve the same compiler. #}
|
||||||
|
{# Not part of the package ID, so binaries stay shareable. #}
|
||||||
|
{% if os != "Windows" %}
|
||||||
|
{% set cc_exe = {"gcc": "gcc", "clang": "clang", "apple-clang": "clang"}.get(compiler) %}
|
||||||
|
{% set cxx_exe = {"gcc": "g++", "clang": "clang++", "apple-clang": "clang++"}.get(compiler) %}
|
||||||
|
tools.build:compiler_executables={'c':'{{ cc_exe }}','cpp':'{{ cxx_exe }}'}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{# By default, Conan tries to reuse binaries built with different cppstd versions. #}
|
{# By default, Conan tries to reuse binaries built with different cppstd versions. #}
|
||||||
{# We want to avoid that to improve reproduceability, so we add the cppstd version to the package ID. #}
|
{# We want to avoid that to improve reproduceability, so we add the cppstd version to the package ID. #}
|
||||||
{# More info: https://docs.conan.io/2/reference/extensions/binary_compatibility.html #}
|
{# More info: https://docs.conan.io/2/reference/extensions/binary_compatibility.html #}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class Xrpl(ConanFile):
|
|||||||
settings = "os", "compiler", "build_type", "arch"
|
settings = "os", "compiler", "build_type", "arch"
|
||||||
options = {
|
options = {
|
||||||
"assertions": [True, False],
|
"assertions": [True, False],
|
||||||
|
"benchmark": [True, False],
|
||||||
"coverage": [True, False],
|
"coverage": [True, False],
|
||||||
"fPIC": [True, False],
|
"fPIC": [True, False],
|
||||||
"jemalloc": [True, False],
|
"jemalloc": [True, False],
|
||||||
@@ -46,6 +47,7 @@ class Xrpl(ConanFile):
|
|||||||
|
|
||||||
default_options = {
|
default_options = {
|
||||||
"assertions": False,
|
"assertions": False,
|
||||||
|
"benchmark": True,
|
||||||
"coverage": False,
|
"coverage": False,
|
||||||
"fPIC": True,
|
"fPIC": True,
|
||||||
"jemalloc": False,
|
"jemalloc": False,
|
||||||
@@ -129,12 +131,14 @@ class Xrpl(ConanFile):
|
|||||||
self.options["boost"].without_cobalt = True
|
self.options["boost"].without_cobalt = True
|
||||||
|
|
||||||
def requirements(self):
|
def requirements(self):
|
||||||
|
if self.options.benchmark:
|
||||||
|
self.requires("benchmark/1.9.5")
|
||||||
self.requires("boost/1.91.0", force=True, transitive_headers=True)
|
self.requires("boost/1.91.0", force=True, transitive_headers=True)
|
||||||
self.requires("date/3.0.4", transitive_headers=True)
|
self.requires("date/3.0.4", transitive_headers=True)
|
||||||
if self.options.jemalloc:
|
if self.options.jemalloc:
|
||||||
self.requires("jemalloc/5.3.1")
|
self.requires("jemalloc/5.3.1")
|
||||||
self.requires("lz4/1.10.0", force=True)
|
self.requires("lz4/1.10.0", force=True)
|
||||||
self.requires("mpt-crypto/0.4.0-rc2", transitive_headers=True)
|
self.requires("mpt-crypto/0.4.0-rc4", transitive_headers=True)
|
||||||
self.requires("protobuf/6.33.5", force=True)
|
self.requires("protobuf/6.33.5", force=True)
|
||||||
if self.options.rocksdb:
|
if self.options.rocksdb:
|
||||||
self.requires("rocksdb/10.5.1")
|
self.requires("rocksdb/10.5.1")
|
||||||
@@ -162,6 +166,7 @@ class Xrpl(ConanFile):
|
|||||||
def generate(self):
|
def generate(self):
|
||||||
tc = CMakeToolchain(self)
|
tc = CMakeToolchain(self)
|
||||||
tc.variables["tests"] = self.options.tests
|
tc.variables["tests"] = self.options.tests
|
||||||
|
tc.variables["benchmark"] = self.options.benchmark
|
||||||
tc.variables["assert"] = self.options.assertions
|
tc.variables["assert"] = self.options.assertions
|
||||||
tc.variables["coverage"] = self.options.coverage
|
tc.variables["coverage"] = self.options.coverage
|
||||||
tc.variables["jemalloc"] = self.options.jemalloc
|
tc.variables["jemalloc"] = self.options.jemalloc
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Extract a tar archive compressed with lz4
|
/**
|
||||||
|
* Extract a tar archive compressed with lz4
|
||||||
@param src the path of the archive to be extracted
|
*
|
||||||
@param dst the directory to extract to
|
* @param src the path of the archive to be extracted
|
||||||
|
* @param dst the directory to extract to
|
||||||
@throws runtime_error
|
*
|
||||||
*/
|
* @throws runtime_error
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& dst);
|
extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& dst);
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Storage for linear binary data.
|
/**
|
||||||
Blocks of binary data appear often in various idioms and structures.
|
* Storage for linear binary data.
|
||||||
*/
|
* Blocks of binary data appear often in various idioms and structures.
|
||||||
|
*/
|
||||||
using Blob = std::vector<unsigned char>;
|
using Blob = std::vector<unsigned char>;
|
||||||
|
|
||||||
} // namespace xrpl
|
} // namespace xrpl
|
||||||
|
|||||||
@@ -10,9 +10,10 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Like std::vector<char> but better.
|
/**
|
||||||
Meets the requirements of BufferFactory.
|
* Like std::vector<char> but better.
|
||||||
*/
|
* Meets the requirements of BufferFactory.
|
||||||
|
*/
|
||||||
class Buffer
|
class Buffer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -24,30 +25,37 @@ public:
|
|||||||
|
|
||||||
Buffer() = default;
|
Buffer() = default;
|
||||||
|
|
||||||
/** Create an uninitialized buffer with the given size. */
|
/**
|
||||||
|
* Create an uninitialized buffer with the given size.
|
||||||
|
*/
|
||||||
explicit Buffer(std::size_t size)
|
explicit Buffer(std::size_t size)
|
||||||
: p_((size != 0u) ? new std::uint8_t[size] : nullptr), size_(size)
|
: p_((size != 0u) ? new std::uint8_t[size] : nullptr), size_(size)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a buffer as a copy of existing memory.
|
/**
|
||||||
|
* Create a buffer as a copy of existing memory.
|
||||||
@param data a pointer to the existing memory. If
|
*
|
||||||
size is non-zero, it must not be null.
|
* @param data a pointer to the existing memory. If
|
||||||
@param size size of the existing memory block.
|
* size is non-zero, it must not be null.
|
||||||
*/
|
* @param size size of the existing memory block.
|
||||||
|
*/
|
||||||
Buffer(void const* data, std::size_t size) : Buffer(size)
|
Buffer(void const* data, std::size_t size) : Buffer(size)
|
||||||
{
|
{
|
||||||
if (size != 0u)
|
if (size != 0u)
|
||||||
std::memcpy(p_.get(), data, size);
|
std::memcpy(p_.get(), data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Copy-construct */
|
/**
|
||||||
|
* Copy-construct
|
||||||
|
*/
|
||||||
Buffer(Buffer const& other) : Buffer(other.p_.get(), other.size_)
|
Buffer(Buffer const& other) : Buffer(other.p_.get(), other.size_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Copy assign */
|
/**
|
||||||
|
* Copy assign
|
||||||
|
*/
|
||||||
Buffer&
|
Buffer&
|
||||||
operator=(Buffer const& other)
|
operator=(Buffer const& other)
|
||||||
{
|
{
|
||||||
@@ -59,17 +67,19 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Move-construct.
|
/**
|
||||||
The other buffer is reset.
|
* Move-construct.
|
||||||
*/
|
* The other buffer is reset.
|
||||||
|
*/
|
||||||
Buffer(Buffer&& other) noexcept : p_(std::move(other.p_)), size_(other.size_)
|
Buffer(Buffer&& other) noexcept : p_(std::move(other.p_)), size_(other.size_)
|
||||||
{
|
{
|
||||||
other.size_ = 0;
|
other.size_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Move-assign.
|
/**
|
||||||
The other buffer is reset.
|
* Move-assign.
|
||||||
*/
|
* The other buffer is reset.
|
||||||
|
*/
|
||||||
Buffer&
|
Buffer&
|
||||||
operator=(Buffer&& other) noexcept
|
operator=(Buffer&& other) noexcept
|
||||||
{
|
{
|
||||||
@@ -82,12 +92,16 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Construct from a slice */
|
/**
|
||||||
|
* Construct from a slice
|
||||||
|
*/
|
||||||
explicit Buffer(Slice s) : Buffer(s.data(), s.size())
|
explicit Buffer(Slice s) : Buffer(s.data(), s.size())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Assign from slice */
|
/**
|
||||||
|
* Assign from slice
|
||||||
|
*/
|
||||||
Buffer&
|
Buffer&
|
||||||
operator=(Slice s)
|
operator=(Slice s)
|
||||||
{
|
{
|
||||||
@@ -101,7 +115,9 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the number of bytes in the buffer. */
|
/**
|
||||||
|
* Returns the number of bytes in the buffer.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
size() const noexcept
|
size() const noexcept
|
||||||
{
|
{
|
||||||
@@ -121,10 +137,11 @@ public:
|
|||||||
return Slice{p_.get(), size_};
|
return Slice{p_.get(), size_};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a pointer to beginning of the storage.
|
/**
|
||||||
@note The return type is guaranteed to be a pointer
|
* Return a pointer to beginning of the storage.
|
||||||
to a single byte, to facilitate pointer arithmetic.
|
* @note The return type is guaranteed to be a pointer
|
||||||
*/
|
* to a single byte, to facilitate pointer arithmetic.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
[[nodiscard]] std::uint8_t const*
|
[[nodiscard]] std::uint8_t const*
|
||||||
data() const noexcept
|
data() const noexcept
|
||||||
@@ -139,9 +156,10 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Reset the buffer.
|
/**
|
||||||
All memory is deallocated. The resulting size is 0.
|
* Reset the buffer.
|
||||||
*/
|
* All memory is deallocated. The resulting size is 0.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
clear() noexcept
|
clear() noexcept
|
||||||
{
|
{
|
||||||
@@ -149,9 +167,10 @@ public:
|
|||||||
size_ = 0;
|
size_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Reallocate the storage.
|
/**
|
||||||
Existing data, if any, is discarded.
|
* Reallocate the storage.
|
||||||
*/
|
* Existing data, if any, is discarded.
|
||||||
|
*/
|
||||||
std::uint8_t*
|
std::uint8_t*
|
||||||
alloc(std::size_t n)
|
alloc(std::size_t n)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
|
|
||||||
namespace xrpl::compression_algorithms {
|
namespace xrpl::compression_algorithms {
|
||||||
|
|
||||||
/** LZ4 block compression.
|
/**
|
||||||
|
* LZ4 block compression.
|
||||||
* @tparam BufferFactory Callable object or lambda.
|
* @tparam BufferFactory Callable object or lambda.
|
||||||
* Takes the requested buffer size and returns allocated buffer pointer.
|
* Takes the requested buffer size and returns allocated buffer pointer.
|
||||||
* @param in Data to compress
|
* @param in Data to compress
|
||||||
@@ -80,7 +81,8 @@ lz4Decompress(
|
|||||||
return decompressedSize;
|
return decompressedSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** LZ4 block decompression.
|
/**
|
||||||
|
* LZ4 block decompression.
|
||||||
* @tparam InputStream ZeroCopyInputStream
|
* @tparam InputStream ZeroCopyInputStream
|
||||||
* @param in Input source stream
|
* @param in Input source stream
|
||||||
* @param inSize Size of compressed data
|
* @param inSize Size of compressed data
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Manages all counted object types. */
|
/**
|
||||||
|
* Manages all counted object types.
|
||||||
|
*/
|
||||||
class CountedObjects
|
class CountedObjects
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -23,10 +25,11 @@ public:
|
|||||||
getCounts(int minimumThreshold) const;
|
getCounts(int minimumThreshold) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Implementation for @ref CountedObject.
|
/**
|
||||||
|
* Implementation for @ref CountedObject.
|
||||||
@internal
|
*
|
||||||
*/
|
* @internal
|
||||||
|
*/
|
||||||
class Counter
|
class Counter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -94,13 +97,14 @@ private:
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Tracks the number of instances of an object.
|
/**
|
||||||
|
* Tracks the number of instances of an object.
|
||||||
Derived classes have their instances counted automatically. This is used
|
*
|
||||||
for reporting purposes.
|
* Derived classes have their instances counted automatically. This is used
|
||||||
|
* for reporting purposes.
|
||||||
@ingroup basics
|
*
|
||||||
*/
|
* @ingroup basics
|
||||||
|
*/
|
||||||
template <class Object>
|
template <class Object>
|
||||||
class CountedObject
|
class CountedObject
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,9 +6,10 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Sampling function using exponential decay to provide a continuous value.
|
/**
|
||||||
@tparam The number of seconds in the decay window.
|
* Sampling function using exponential decay to provide a continuous value.
|
||||||
*/
|
* @tparam The number of seconds in the decay window.
|
||||||
|
*/
|
||||||
template <int Window, typename Clock>
|
template <int Window, typename Clock>
|
||||||
class DecayingSample
|
class DecayingSample
|
||||||
{
|
{
|
||||||
@@ -19,15 +20,16 @@ public:
|
|||||||
DecayingSample() = delete;
|
DecayingSample() = delete;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@param now Start time of DecayingSample.
|
* @param now Start time of DecayingSample.
|
||||||
*/
|
*/
|
||||||
explicit DecayingSample(time_point now) : value_(value_type()), when_(now)
|
explicit DecayingSample(time_point now) : value_(value_type()), when_(now)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add a new sample.
|
/**
|
||||||
The value is first aged according to the specified time.
|
* Add a new sample.
|
||||||
*/
|
* The value is first aged according to the specified time.
|
||||||
|
*/
|
||||||
value_type
|
value_type
|
||||||
add(value_type value, time_point now)
|
add(value_type value, time_point now)
|
||||||
{
|
{
|
||||||
@@ -36,9 +38,10 @@ public:
|
|||||||
return value_ / Window;
|
return value_ / Window;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Retrieve the current value in normalized units.
|
/**
|
||||||
The samples are first aged according to the specified time.
|
* Retrieve the current value in normalized units.
|
||||||
*/
|
* The samples are first aged according to the specified time.
|
||||||
|
*/
|
||||||
value_type
|
value_type
|
||||||
value(time_point now)
|
value(time_point now)
|
||||||
{
|
{
|
||||||
@@ -87,9 +90,10 @@ private:
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Sampling function using exponential decay to provide a continuous value.
|
/**
|
||||||
@tparam HalfLife The half life of a sample, in seconds.
|
* Sampling function using exponential decay to provide a continuous value.
|
||||||
*/
|
* @tparam HalfLife The half life of a sample, in seconds.
|
||||||
|
*/
|
||||||
template <int HalfLife, class Clock>
|
template <int HalfLife, class Clock>
|
||||||
class DecayWindow
|
class DecayWindow
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,33 +10,37 @@ namespace xrpl {
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Tag to create an intrusive pointer from another intrusive pointer by using a
|
/**
|
||||||
static cast. This is useful to create an intrusive pointer to a derived
|
* Tag to create an intrusive pointer from another intrusive pointer by using a
|
||||||
class from an intrusive pointer to a base class.
|
* static cast. This is useful to create an intrusive pointer to a derived
|
||||||
*/
|
* class from an intrusive pointer to a base class.
|
||||||
|
*/
|
||||||
struct StaticCastTagSharedIntrusive
|
struct StaticCastTagSharedIntrusive
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Tag to create an intrusive pointer from another intrusive pointer by using a
|
/**
|
||||||
dynamic cast. This is useful to create an intrusive pointer to a derived
|
* Tag to create an intrusive pointer from another intrusive pointer by using a
|
||||||
class from an intrusive pointer to a base class. If the cast fails an empty
|
* dynamic cast. This is useful to create an intrusive pointer to a derived
|
||||||
(null) intrusive pointer is created.
|
* class from an intrusive pointer to a base class. If the cast fails an empty
|
||||||
*/
|
* (null) intrusive pointer is created.
|
||||||
|
*/
|
||||||
struct DynamicCastTagSharedIntrusive
|
struct DynamicCastTagSharedIntrusive
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
/** When creating or adopting a raw pointer, controls whether the strong count
|
/**
|
||||||
is incremented or not. Use this tag to increment the strong count.
|
* When creating or adopting a raw pointer, controls whether the strong count
|
||||||
*/
|
* is incremented or not. Use this tag to increment the strong count.
|
||||||
|
*/
|
||||||
struct SharedIntrusiveAdoptIncrementStrongTag
|
struct SharedIntrusiveAdoptIncrementStrongTag
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
/** When creating or adopting a raw pointer, controls whether the strong count
|
/**
|
||||||
is incremented or not. Use this tag to leave the strong count unchanged.
|
* When creating or adopting a raw pointer, controls whether the strong count
|
||||||
*/
|
* is incremented or not. Use this tag to leave the strong count unchanged.
|
||||||
|
*/
|
||||||
struct SharedIntrusiveAdoptNoIncrementTag
|
struct SharedIntrusiveAdoptNoIncrementTag
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
@@ -50,20 +54,21 @@ concept CAdoptTag = std::is_same_v<T, SharedIntrusiveAdoptIncrementStrongTag> ||
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** A shared intrusive pointer class that supports weak pointers.
|
/**
|
||||||
|
* A shared intrusive pointer class that supports weak pointers.
|
||||||
This is meant to be used for SHAMapInnerNodes, but may be useful for other
|
*
|
||||||
cases. Since the reference counts are stored on the pointee, the pointee is
|
* This is meant to be used for SHAMapInnerNodes, but may be useful for other
|
||||||
not destroyed until both the strong _and_ weak pointer counts go to zero.
|
* cases. Since the reference counts are stored on the pointee, the pointee is
|
||||||
When the strong pointer count goes to zero, the "partialDestructor" is
|
* not destroyed until both the strong _and_ weak pointer counts go to zero.
|
||||||
called. This can be used to destroy as much of the object as possible while
|
* When the strong pointer count goes to zero, the "partialDestructor" is
|
||||||
still retaining the reference counts. For example, for SHAMapInnerNodes the
|
* called. This can be used to destroy as much of the object as possible while
|
||||||
children may be reset in that function. Note that std::shared_pointer WILL
|
* still retaining the reference counts. For example, for SHAMapInnerNodes the
|
||||||
run the destructor when the strong count reaches zero, but may not free the
|
* children may be reset in that function. Note that std::shared_pointer WILL
|
||||||
memory used by the object until the weak count reaches zero. In xrpld, we
|
* run the destructor when the strong count reaches zero, but may not free the
|
||||||
typically allocate shared pointers with the `make_shared` function. When
|
* memory used by the object until the weak count reaches zero. In xrpld, we
|
||||||
that is used, the memory is not reclaimed until the weak count reaches zero.
|
* typically allocate shared pointers with the `make_shared` function. When
|
||||||
*/
|
* that is used, the memory is not reclaimed until the weak count reaches zero.
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
class SharedIntrusive
|
class SharedIntrusive
|
||||||
{
|
{
|
||||||
@@ -111,8 +116,9 @@ public:
|
|||||||
operator=(
|
operator=(
|
||||||
SharedIntrusive<TT>&& rhs); // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved)
|
SharedIntrusive<TT>&& rhs); // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||||
|
|
||||||
/** Adopt the raw pointer. The strong reference may or may not be
|
/**
|
||||||
incremented, depending on the TAdoptTag
|
* Adopt the raw pointer. The strong reference may or may not be
|
||||||
|
* incremented, depending on the TAdoptTag
|
||||||
*/
|
*/
|
||||||
template <CAdoptTag TAdoptTag = SharedIntrusiveAdoptIncrementStrongTag>
|
template <CAdoptTag TAdoptTag = SharedIntrusiveAdoptIncrementStrongTag>
|
||||||
void
|
void
|
||||||
@@ -120,27 +126,31 @@ public:
|
|||||||
|
|
||||||
~SharedIntrusive();
|
~SharedIntrusive();
|
||||||
|
|
||||||
/** Create a new SharedIntrusive by statically casting the pointer
|
/**
|
||||||
controlled by the rhs param.
|
* Create a new SharedIntrusive by statically casting the pointer
|
||||||
*/
|
* controlled by the rhs param.
|
||||||
|
*/
|
||||||
template <class TT>
|
template <class TT>
|
||||||
SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive<TT> const& rhs);
|
SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive<TT> const& rhs);
|
||||||
|
|
||||||
/** Create a new SharedIntrusive by statically casting the pointer
|
/**
|
||||||
controlled by the rhs param.
|
* Create a new SharedIntrusive by statically casting the pointer
|
||||||
*/
|
* controlled by the rhs param.
|
||||||
|
*/
|
||||||
template <class TT>
|
template <class TT>
|
||||||
SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive<TT>&& rhs);
|
SharedIntrusive(StaticCastTagSharedIntrusive, SharedIntrusive<TT>&& rhs);
|
||||||
|
|
||||||
/** Create a new SharedIntrusive by dynamically casting the pointer
|
/**
|
||||||
controlled by the rhs param.
|
* Create a new SharedIntrusive by dynamically casting the pointer
|
||||||
*/
|
* controlled by the rhs param.
|
||||||
|
*/
|
||||||
template <class TT>
|
template <class TT>
|
||||||
SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive<TT> const& rhs);
|
SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive<TT> const& rhs);
|
||||||
|
|
||||||
/** Create a new SharedIntrusive by dynamically casting the pointer
|
/**
|
||||||
controlled by the rhs param.
|
* Create a new SharedIntrusive by dynamically casting the pointer
|
||||||
*/
|
* controlled by the rhs param.
|
||||||
|
*/
|
||||||
template <class TT>
|
template <class TT>
|
||||||
SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive<TT>&& rhs);
|
SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive<TT>&& rhs);
|
||||||
|
|
||||||
@@ -153,17 +163,22 @@ public:
|
|||||||
explicit
|
explicit
|
||||||
operator bool() const noexcept;
|
operator bool() const noexcept;
|
||||||
|
|
||||||
/** Set the pointer to null, decrement the strong count, and run the
|
/**
|
||||||
appropriate release action.
|
* Set the pointer to null, decrement the strong count, and run the
|
||||||
*/
|
* appropriate release action.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
/** Get the raw pointer */
|
/**
|
||||||
|
* Get the raw pointer
|
||||||
|
*/
|
||||||
[[nodiscard]] T*
|
[[nodiscard]] T*
|
||||||
get() const;
|
get() const;
|
||||||
|
|
||||||
/** Return the strong count */
|
/**
|
||||||
|
* Return the strong count
|
||||||
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
useCount() const;
|
useCount() const;
|
||||||
|
|
||||||
@@ -181,43 +196,51 @@ public:
|
|||||||
friend class WeakIntrusive;
|
friend class WeakIntrusive;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Return the raw pointer held by this object. */
|
/**
|
||||||
|
* Return the raw pointer held by this object.
|
||||||
|
*/
|
||||||
[[nodiscard]] T*
|
[[nodiscard]] T*
|
||||||
unsafeGetRawPtr() const;
|
unsafeGetRawPtr() const;
|
||||||
|
|
||||||
/** Exchange the current raw pointer held by this object with the given
|
/**
|
||||||
pointer. Decrement the strong count of the raw pointer previously held
|
* Exchange the current raw pointer held by this object with the given
|
||||||
by this object and run the appropriate release action.
|
* pointer. Decrement the strong count of the raw pointer previously held
|
||||||
|
* by this object and run the appropriate release action.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
unsafeReleaseAndStore(T* next);
|
unsafeReleaseAndStore(T* next);
|
||||||
|
|
||||||
/** Set the raw pointer directly. This is wrapped in a function so the class
|
/**
|
||||||
can support both atomic and non-atomic pointers in a future patch.
|
* Set the raw pointer directly. This is wrapped in a function so the class
|
||||||
|
* can support both atomic and non-atomic pointers in a future patch.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
unsafeSetRawPtr(T* p);
|
unsafeSetRawPtr(T* p);
|
||||||
|
|
||||||
/** Exchange the raw pointer directly.
|
/**
|
||||||
This sets the raw pointer to the given value and returns the previous
|
* Exchange the raw pointer directly.
|
||||||
value. This is wrapped in a function so the class can support both
|
* This sets the raw pointer to the given value and returns the previous
|
||||||
atomic and non-atomic pointers in a future patch.
|
* value. This is wrapped in a function so the class can support both
|
||||||
|
* atomic and non-atomic pointers in a future patch.
|
||||||
*/
|
*/
|
||||||
T*
|
T*
|
||||||
unsafeExchange(T* p);
|
unsafeExchange(T* p);
|
||||||
|
|
||||||
/** pointer to the type with an intrusive count */
|
/**
|
||||||
|
* pointer to the type with an intrusive count
|
||||||
|
*/
|
||||||
T* ptr_{nullptr};
|
T* ptr_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** A weak intrusive pointer class for the SharedIntrusive pointer class.
|
/**
|
||||||
|
* A weak intrusive pointer class for the SharedIntrusive pointer class.
|
||||||
Note that this weak pointer class asks differently from normal weak pointer
|
*
|
||||||
classes. When the strong pointer count goes to zero, the "partialDestructor"
|
* Note that this weak pointer class asks differently from normal weak pointer
|
||||||
is called. See the comment on SharedIntrusive for a fuller explanation.
|
* classes. When the strong pointer count goes to zero, the "partialDestructor"
|
||||||
*/
|
* is called. See the comment on SharedIntrusive for a fuller explanation.
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
class WeakIntrusive
|
class WeakIntrusive
|
||||||
{
|
{
|
||||||
@@ -247,54 +270,62 @@ public:
|
|||||||
WeakIntrusive&
|
WeakIntrusive&
|
||||||
operator=(SharedIntrusive<TT> const& rhs);
|
operator=(SharedIntrusive<TT> const& rhs);
|
||||||
|
|
||||||
/** Adopt the raw pointer and increment the weak count. */
|
/**
|
||||||
|
* Adopt the raw pointer and increment the weak count.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
adopt(T* ptr);
|
adopt(T* ptr);
|
||||||
|
|
||||||
~WeakIntrusive();
|
~WeakIntrusive();
|
||||||
|
|
||||||
/** Get a strong pointer from the weak pointer, if possible. This will
|
/**
|
||||||
only return a seated pointer if the strong count on the raw pointer
|
* Get a strong pointer from the weak pointer, if possible. This will
|
||||||
is non-zero before locking.
|
* only return a seated pointer if the strong count on the raw pointer
|
||||||
|
* is non-zero before locking.
|
||||||
*/
|
*/
|
||||||
SharedIntrusive<T>
|
SharedIntrusive<T>
|
||||||
lock() const;
|
lock() const;
|
||||||
|
|
||||||
/** Return true if the strong count is zero. */
|
/**
|
||||||
|
* Return true if the strong count is zero.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
expired() const;
|
expired() const;
|
||||||
|
|
||||||
/** Set the pointer to null and decrement the weak count.
|
/**
|
||||||
|
* Set the pointer to null and decrement the weak count.
|
||||||
Note: This may run the destructor if the strong count is zero.
|
*
|
||||||
*/
|
* Note: This may run the destructor if the strong count is zero.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T* ptr_ = nullptr;
|
T* ptr_ = nullptr;
|
||||||
|
|
||||||
/** Decrement the weak count. This does _not_ set the raw pointer to
|
/**
|
||||||
null.
|
* Decrement the weak count. This does _not_ set the raw pointer to
|
||||||
|
* null.
|
||||||
Note: This may run the destructor if the strong count is zero.
|
*
|
||||||
*/
|
* Note: This may run the destructor if the strong count is zero.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
unsafeReleaseNoStore();
|
unsafeReleaseNoStore();
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** A combination of a strong and a weak intrusive pointer stored in the
|
/**
|
||||||
space of a single pointer.
|
* A combination of a strong and a weak intrusive pointer stored in the
|
||||||
|
* space of a single pointer.
|
||||||
This class is similar to a `std::variant<SharedIntrusive,WeakIntrusive>`
|
*
|
||||||
with some optimizations. In particular, it uses a low-order bit to
|
* This class is similar to a `std::variant<SharedIntrusive,WeakIntrusive>`
|
||||||
determine if the raw pointer represents a strong pointer or a weak
|
* with some optimizations. In particular, it uses a low-order bit to
|
||||||
pointer. It can also be quickly switched between its strong pointer and
|
* determine if the raw pointer represents a strong pointer or a weak
|
||||||
weak pointer representations. This class is useful for storing intrusive
|
* pointer. It can also be quickly switched between its strong pointer and
|
||||||
pointers in tagged caches.
|
* weak pointer representations. This class is useful for storing intrusive
|
||||||
*/
|
* pointers in tagged caches.
|
||||||
|
*/
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class SharedWeakUnion
|
class SharedWeakUnion
|
||||||
@@ -336,69 +367,83 @@ public:
|
|||||||
|
|
||||||
~SharedWeakUnion();
|
~SharedWeakUnion();
|
||||||
|
|
||||||
/** Return a strong pointer if this is already a strong pointer (i.e.
|
/**
|
||||||
don't lock the weak pointer. Use the `lock` method if that's what's
|
* Return a strong pointer if this is already a strong pointer (i.e.
|
||||||
needed)
|
* don't lock the weak pointer. Use the `lock` method if that's what's
|
||||||
|
* needed)
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] SharedIntrusive<T>
|
[[nodiscard]] SharedIntrusive<T>
|
||||||
getStrong() const;
|
getStrong() const;
|
||||||
|
|
||||||
/** Return true if this is a strong pointer and the strong pointer is
|
/**
|
||||||
seated.
|
* Return true if this is a strong pointer and the strong pointer is
|
||||||
|
* seated.
|
||||||
*/
|
*/
|
||||||
explicit
|
explicit
|
||||||
operator bool() const noexcept;
|
operator bool() const noexcept;
|
||||||
|
|
||||||
/** Set the pointer to null, decrement the appropriate ref count, and
|
/**
|
||||||
run the appropriate release action.
|
* Set the pointer to null, decrement the appropriate ref count, and
|
||||||
|
* run the appropriate release action.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
/** If this is a strong pointer, return the raw pointer. Otherwise
|
/**
|
||||||
return null.
|
* If this is a strong pointer, return the raw pointer. Otherwise
|
||||||
|
* return null.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] T*
|
[[nodiscard]] T*
|
||||||
get() const;
|
get() const;
|
||||||
|
|
||||||
/** If this is a strong pointer, return the strong count. Otherwise
|
/**
|
||||||
|
* If this is a strong pointer, return the strong count. Otherwise
|
||||||
* return 0
|
* return 0
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
useCount() const;
|
useCount() const;
|
||||||
|
|
||||||
/** Return true if there is a non-zero strong count. */
|
/**
|
||||||
|
* Return true if there is a non-zero strong count.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
expired() const;
|
expired() const;
|
||||||
|
|
||||||
/** If this is a strong pointer, return the strong pointer. Otherwise
|
/**
|
||||||
attempt to lock the weak pointer.
|
* If this is a strong pointer, return the strong pointer. Otherwise
|
||||||
|
* attempt to lock the weak pointer.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] SharedIntrusive<T>
|
[[nodiscard]] SharedIntrusive<T>
|
||||||
lock() const;
|
lock() const;
|
||||||
|
|
||||||
/** Return true is this represents a strong pointer. */
|
/**
|
||||||
|
* Return true is this represents a strong pointer.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isStrong() const;
|
isStrong() const;
|
||||||
|
|
||||||
/** Return true is this represents a weak pointer. */
|
/**
|
||||||
|
* Return true is this represents a weak pointer.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isWeak() const;
|
isWeak() const;
|
||||||
|
|
||||||
/** If this is a weak pointer, attempt to convert it to a strong
|
/**
|
||||||
pointer.
|
* If this is a weak pointer, attempt to convert it to a strong
|
||||||
|
* pointer.
|
||||||
@return true if successfully converted to a strong pointer (or was
|
*
|
||||||
already a strong pointer). Otherwise false.
|
* @return true if successfully converted to a strong pointer (or was
|
||||||
*/
|
* already a strong pointer). Otherwise false.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
convertToStrong();
|
convertToStrong();
|
||||||
|
|
||||||
/** If this is a strong pointer, attempt to convert it to a weak
|
/**
|
||||||
pointer.
|
* If this is a strong pointer, attempt to convert it to a weak
|
||||||
|
* pointer.
|
||||||
@return false if the pointer is null. Otherwise return true.
|
*
|
||||||
*/
|
* @return false if the pointer is null. Otherwise return true.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
convertToWeak();
|
convertToWeak();
|
||||||
|
|
||||||
@@ -411,23 +456,27 @@ private:
|
|||||||
static constexpr std::uintptr_t kPtrMask = ~kTagMask;
|
static constexpr std::uintptr_t kPtrMask = ~kTagMask;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Return the raw pointer held by this object.
|
/**
|
||||||
|
* Return the raw pointer held by this object.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] T*
|
[[nodiscard]] T*
|
||||||
unsafeGetRawPtr() const;
|
unsafeGetRawPtr() const;
|
||||||
|
|
||||||
enum class RefStrength { Strong, Weak };
|
enum class RefStrength { Strong, Weak };
|
||||||
/** Set the raw pointer and tag bit directly.
|
/**
|
||||||
|
* Set the raw pointer and tag bit directly.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
unsafeSetRawPtr(T* p, RefStrength rs);
|
unsafeSetRawPtr(T* p, RefStrength rs);
|
||||||
|
|
||||||
/** Set the raw pointer and tag bit to all zeros (strong null pointer).
|
/**
|
||||||
|
* Set the raw pointer and tag bit to all zeros (strong null pointer).
|
||||||
*/
|
*/
|
||||||
void unsafeSetRawPtr(std::nullptr_t);
|
void unsafeSetRawPtr(std::nullptr_t);
|
||||||
|
|
||||||
/** Decrement the appropriate ref count, and run the appropriate release
|
/**
|
||||||
action. Note: this does _not_ set the raw pointer to null.
|
* Decrement the appropriate ref count, and run the appropriate release
|
||||||
|
* action. Note: this does _not_ set the raw pointer to null.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
unsafeReleaseNoStore();
|
unsafeReleaseNoStore();
|
||||||
@@ -435,12 +484,13 @@ private:
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Create a shared intrusive pointer.
|
/**
|
||||||
|
* Create a shared intrusive pointer.
|
||||||
Note: unlike std::shared_ptr, where there is an advantage of allocating
|
*
|
||||||
the pointer and control block together, there is no benefit for intrusive
|
* Note: unlike std::shared_ptr, where there is an advantage of allocating
|
||||||
pointers.
|
* the pointer and control block together, there is no benefit for intrusive
|
||||||
*/
|
* pointers.
|
||||||
|
*/
|
||||||
template <class TT, class... Args>
|
template <class TT, class... Args>
|
||||||
SharedIntrusive<TT>
|
SharedIntrusive<TT>
|
||||||
makeSharedIntrusive(Args&&... args)
|
makeSharedIntrusive(Args&&... args)
|
||||||
|
|||||||
@@ -8,35 +8,38 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Action to perform when releasing a strong pointer.
|
/**
|
||||||
|
* Action to perform when releasing a strong pointer.
|
||||||
noop: Do nothing. For example, a `noop` action will occur when a count is
|
*
|
||||||
decremented to a non-zero value.
|
* noop: Do nothing. For example, a `noop` action will occur when a count is
|
||||||
|
* decremented to a non-zero value.
|
||||||
partialDestroy: Run the `partialDestructor`. This action will happen when a
|
*
|
||||||
strong count is decremented to zero and the weak count is non-zero.
|
* partialDestroy: Run the `partialDestructor`. This action will happen when a
|
||||||
|
* strong count is decremented to zero and the weak count is non-zero.
|
||||||
destroy: Run the destructor. This action will occur when either the strong
|
*
|
||||||
count or weak count is decremented and the other count is also zero.
|
* destroy: Run the destructor. This action will occur when either the strong
|
||||||
|
* count or weak count is decremented and the other count is also zero.
|
||||||
*/
|
*/
|
||||||
enum class ReleaseStrongRefAction { NoOp, PartialDestroy, Destroy };
|
enum class ReleaseStrongRefAction { NoOp, PartialDestroy, Destroy };
|
||||||
|
|
||||||
/** Action to perform when releasing a weak pointer.
|
/**
|
||||||
|
* Action to perform when releasing a weak pointer.
|
||||||
noop: Do nothing. For example, a `noop` action will occur when a count is
|
*
|
||||||
decremented to a non-zero value.
|
* noop: Do nothing. For example, a `noop` action will occur when a count is
|
||||||
|
* decremented to a non-zero value.
|
||||||
destroy: Run the destructor. This action will occur when either the strong
|
*
|
||||||
count or weak count is decremented and the other count is also zero.
|
* destroy: Run the destructor. This action will occur when either the strong
|
||||||
|
* count or weak count is decremented and the other count is also zero.
|
||||||
*/
|
*/
|
||||||
enum class ReleaseWeakRefAction { NoOp, Destroy };
|
enum class ReleaseWeakRefAction { NoOp, Destroy };
|
||||||
|
|
||||||
/** Implement the strong count, weak count, and bit flags for an intrusive
|
/**
|
||||||
pointer.
|
* Implement the strong count, weak count, and bit flags for an intrusive
|
||||||
|
* pointer.
|
||||||
A class can satisfy the requirements of an xrpl::IntrusivePointer by
|
*
|
||||||
inheriting from this class.
|
* A class can satisfy the requirements of an xrpl::IntrusivePointer by
|
||||||
*/
|
* inheriting from this class.
|
||||||
|
*/
|
||||||
struct IntrusiveRefCounts
|
struct IntrusiveRefCounts
|
||||||
{
|
{
|
||||||
virtual ~IntrusiveRefCounts() noexcept;
|
virtual ~IntrusiveRefCounts() noexcept;
|
||||||
@@ -105,109 +108,123 @@ private:
|
|||||||
static constexpr size_t kFieldTypeBits = sizeof(FieldType) * 8;
|
static constexpr size_t kFieldTypeBits = sizeof(FieldType) * 8;
|
||||||
static constexpr FieldType kOne = 1;
|
static constexpr FieldType kOne = 1;
|
||||||
|
|
||||||
/** `refCounts` consists of four fields that are treated atomically:
|
/**
|
||||||
|
* `refCounts` consists of four fields that are treated atomically:
|
||||||
1. Strong count. This is a count of the number of shared pointers that
|
*
|
||||||
hold a reference to this object. When the strong counts goes to zero,
|
* 1. Strong count. This is a count of the number of shared pointers that
|
||||||
if the weak count is zero, the destructor is run. If the weak count is
|
* hold a reference to this object. When the strong counts goes to zero,
|
||||||
non-zero when the strong count goes to zero then the partialDestructor
|
* if the weak count is zero, the destructor is run. If the weak count is
|
||||||
is run.
|
* non-zero when the strong count goes to zero then the partialDestructor
|
||||||
|
* is run.
|
||||||
2. Weak count. This is a count of the number of weak pointer that hold
|
*
|
||||||
a reference to this object. When the weak count goes to zero and the
|
* 2. Weak count. This is a count of the number of weak pointer that hold
|
||||||
strong count is also zero, then the destructor is run.
|
* a reference to this object. When the weak count goes to zero and the
|
||||||
|
* strong count is also zero, then the destructor is run.
|
||||||
3. Partial destroy started bit. This bit is set if the
|
*
|
||||||
`partialDestructor` function has been started (or is about to be
|
* 3. Partial destroy started bit. This bit is set if the
|
||||||
started). This is used to prevent the destructor from running
|
* `partialDestructor` function has been started (or is about to be
|
||||||
concurrently with the partial destructor. This can easily happen when
|
* started). This is used to prevent the destructor from running
|
||||||
the last strong pointer release its reference in one thread and starts
|
* concurrently with the partial destructor. This can easily happen when
|
||||||
the partialDestructor, while in another thread the last weak pointer
|
* the last strong pointer release its reference in one thread and starts
|
||||||
goes out of scope and starts the destructor while the partialDestructor
|
* the partialDestructor, while in another thread the last weak pointer
|
||||||
is still running. Both a start and finished bit is needed to handle a
|
* goes out of scope and starts the destructor while the partialDestructor
|
||||||
corner-case where the last strong pointer goes out of scope, then then
|
* is still running. Both a start and finished bit is needed to handle a
|
||||||
last `weakPointer` goes out of scope, but this happens before the
|
* corner-case where the last strong pointer goes out of scope, then then
|
||||||
`partialDestructor` bit is set. It would be possible to use a single
|
* last `weakPointer` goes out of scope, but this happens before the
|
||||||
bit if it could also be set atomically when the strong count goes to
|
* `partialDestructor` bit is set. It would be possible to use a single
|
||||||
zero and the weak count is non-zero, but that would add complexity (and
|
* bit if it could also be set atomically when the strong count goes to
|
||||||
likely slow down common cases as well).
|
* zero and the weak count is non-zero, but that would add complexity (and
|
||||||
|
* likely slow down common cases as well).
|
||||||
4. Partial destroy finished bit. This bit is set when the
|
*
|
||||||
`partialDestructor` has finished running. See (3) above for more
|
* 4. Partial destroy finished bit. This bit is set when the
|
||||||
information.
|
* `partialDestructor` has finished running. See (3) above for more
|
||||||
|
* information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mutable std::atomic<FieldType> refCounts_{kStrongDelta};
|
mutable std::atomic<FieldType> refCounts_{kStrongDelta};
|
||||||
|
|
||||||
/** Amount to change the strong count when adding or releasing a reference
|
/**
|
||||||
|
* Amount to change the strong count when adding or releasing a reference
|
||||||
Note: The strong count is stored in the low `StrongCountNumBits` bits
|
*
|
||||||
of refCounts
|
* Note: The strong count is stored in the low `StrongCountNumBits` bits
|
||||||
*/
|
* of refCounts
|
||||||
|
*/
|
||||||
static constexpr FieldType kStrongDelta = 1;
|
static constexpr FieldType kStrongDelta = 1;
|
||||||
|
|
||||||
/** Amount to change the weak count when adding or releasing a reference
|
/**
|
||||||
|
* Amount to change the weak count when adding or releasing a reference
|
||||||
Note: The weak count is stored in the high `WeakCountNumBits` bits of
|
*
|
||||||
refCounts
|
* Note: The weak count is stored in the high `WeakCountNumBits` bits of
|
||||||
*/
|
* refCounts
|
||||||
|
*/
|
||||||
static constexpr FieldType kWeakDelta = (kOne << kStrongCountNumBits);
|
static constexpr FieldType kWeakDelta = (kOne << kStrongCountNumBits);
|
||||||
|
|
||||||
/** Flag that is set when the partialDestroy function has started running
|
/**
|
||||||
(or is about to start running).
|
* Flag that is set when the partialDestroy function has started running
|
||||||
|
* (or is about to start running).
|
||||||
See description of the `refCounts` field for a fuller description of
|
*
|
||||||
this field.
|
* See description of the `refCounts` field for a fuller description of
|
||||||
*/
|
* this field.
|
||||||
|
*/
|
||||||
static constexpr FieldType kPartialDestroyStartedMask = (kOne << (kFieldTypeBits - 1));
|
static constexpr FieldType kPartialDestroyStartedMask = (kOne << (kFieldTypeBits - 1));
|
||||||
|
|
||||||
/** Flag that is set when the partialDestroy function has finished running
|
/**
|
||||||
|
* Flag that is set when the partialDestroy function has finished running
|
||||||
See description of the `refCounts` field for a fuller description of
|
*
|
||||||
this field.
|
* See description of the `refCounts` field for a fuller description of
|
||||||
*/
|
* this field.
|
||||||
|
*/
|
||||||
static constexpr FieldType kPartialDestroyFinishedMask = (kOne << (kFieldTypeBits - 2));
|
static constexpr FieldType kPartialDestroyFinishedMask = (kOne << (kFieldTypeBits - 2));
|
||||||
|
|
||||||
/** Mask that will zero out all the `count` bits and leave the tag bits
|
/**
|
||||||
unchanged.
|
* Mask that will zero out all the `count` bits and leave the tag bits
|
||||||
*/
|
* unchanged.
|
||||||
|
*/
|
||||||
static constexpr FieldType kTagMask = kPartialDestroyStartedMask | kPartialDestroyFinishedMask;
|
static constexpr FieldType kTagMask = kPartialDestroyStartedMask | kPartialDestroyFinishedMask;
|
||||||
|
|
||||||
/** Mask that will zero out the `tag` bits and leave the count bits
|
/**
|
||||||
unchanged.
|
* Mask that will zero out the `tag` bits and leave the count bits
|
||||||
*/
|
* unchanged.
|
||||||
|
*/
|
||||||
static constexpr FieldType kValueMask = ~kTagMask;
|
static constexpr FieldType kValueMask = ~kTagMask;
|
||||||
|
|
||||||
/** Mask that will zero out everything except the strong count.
|
/**
|
||||||
|
* Mask that will zero out everything except the strong count.
|
||||||
*/
|
*/
|
||||||
static constexpr FieldType kStrongMask = ((kOne << kStrongCountNumBits) - 1) & kValueMask;
|
static constexpr FieldType kStrongMask = ((kOne << kStrongCountNumBits) - 1) & kValueMask;
|
||||||
|
|
||||||
/** Mask that will zero out everything except the weak count.
|
/**
|
||||||
|
* Mask that will zero out everything except the weak count.
|
||||||
*/
|
*/
|
||||||
static constexpr FieldType kWeakMask =
|
static constexpr FieldType kWeakMask =
|
||||||
(((kOne << kWeakCountNumBits) - 1) << kStrongCountNumBits) & kValueMask;
|
(((kOne << kWeakCountNumBits) - 1) << kStrongCountNumBits) & kValueMask;
|
||||||
|
|
||||||
/** Unpack the count and tag fields from the packed atomic integer form. */
|
/**
|
||||||
|
* Unpack the count and tag fields from the packed atomic integer form.
|
||||||
|
*/
|
||||||
struct RefCountPair
|
struct RefCountPair
|
||||||
{
|
{
|
||||||
CountType strong;
|
CountType strong;
|
||||||
CountType weak;
|
CountType weak;
|
||||||
/** The `partialDestroyStartedBit` is set to on when the partial
|
/**
|
||||||
destroy function is started. It is not a boolean; it is a uint32
|
* The `partialDestroyStartedBit` is set to on when the partial
|
||||||
with all bits zero with the possible exception of the
|
* destroy function is started. It is not a boolean; it is a uint32
|
||||||
`partialDestroyStartedMask` bit. This is done so it can be directly
|
* with all bits zero with the possible exception of the
|
||||||
masked into the `combinedValue`.
|
* `partialDestroyStartedMask` bit. This is done so it can be directly
|
||||||
|
* masked into the `combinedValue`.
|
||||||
*/
|
*/
|
||||||
FieldType partialDestroyStartedBit{0};
|
FieldType partialDestroyStartedBit{0};
|
||||||
/** The `partialDestroyFinishedBit` is set to on when the partial
|
/**
|
||||||
destroy function has finished.
|
* The `partialDestroyFinishedBit` is set to on when the partial
|
||||||
|
* destroy function has finished.
|
||||||
*/
|
*/
|
||||||
FieldType partialDestroyFinishedBit{0};
|
FieldType partialDestroyFinishedBit{0};
|
||||||
RefCountPair(FieldType v) noexcept;
|
RefCountPair(FieldType v) noexcept;
|
||||||
RefCountPair(CountType s, CountType w) noexcept;
|
RefCountPair(CountType s, CountType w) noexcept;
|
||||||
|
|
||||||
/** Convert back to the packed integer form. */
|
/**
|
||||||
|
* Convert back to the packed integer form.
|
||||||
|
*/
|
||||||
[[nodiscard]] FieldType
|
[[nodiscard]] FieldType
|
||||||
combinedValue() const noexcept;
|
combinedValue() const noexcept;
|
||||||
|
|
||||||
@@ -215,9 +232,10 @@ private:
|
|||||||
static_cast<CountType>((kOne << kStrongCountNumBits) - 1);
|
static_cast<CountType>((kOne << kStrongCountNumBits) - 1);
|
||||||
static constexpr CountType kMaxWeakValue =
|
static constexpr CountType kMaxWeakValue =
|
||||||
static_cast<CountType>((kOne << kWeakCountNumBits) - 1);
|
static_cast<CountType>((kOne << kWeakCountNumBits) - 1);
|
||||||
/** Put an extra margin to detect when running up against limits.
|
/**
|
||||||
This is only used in debug code, and is useful if we reduce the
|
* Put an extra margin to detect when running up against limits.
|
||||||
number of bits in the strong and weak counts (to 16 and 14 bits).
|
* This is only used in debug code, and is useful if we reduce the
|
||||||
|
* number of bits in the strong and weak counts (to 16 and 14 bits).
|
||||||
*/
|
*/
|
||||||
static constexpr CountType kCheckStrongMaxValue = kMaxStrongValue - 32;
|
static constexpr CountType kCheckStrongMaxValue = kMaxStrongValue - 32;
|
||||||
static constexpr CountType kCheckWeakMaxValue = kMaxWeakValue - 32;
|
static constexpr CountType kCheckWeakMaxValue = kMaxWeakValue - 32;
|
||||||
|
|||||||
@@ -70,11 +70,15 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stores instance of T specific to the calling coroutine or thread. */
|
/**
|
||||||
|
* Stores instance of T specific to the calling coroutine or thread.
|
||||||
|
*/
|
||||||
T&
|
T&
|
||||||
operator*();
|
operator*();
|
||||||
|
|
||||||
/** Stores instance of T specific to the calling coroutine or thread. */
|
/**
|
||||||
|
* Stores instance of T specific to the calling coroutine or thread.
|
||||||
|
*/
|
||||||
T*
|
T*
|
||||||
operator->()
|
operator->()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Manages partitions for logging. */
|
/**
|
||||||
|
* Manages partitions for logging.
|
||||||
|
*/
|
||||||
class Logs
|
class Logs
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -40,69 +42,81 @@ private:
|
|||||||
writeAlways(beast::Severity level, std::string const& text) override;
|
writeAlways(beast::Severity level, std::string const& text) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Manages a system file containing logged output.
|
/**
|
||||||
The system file remains open during program execution. Interfaces
|
* Manages a system file containing logged output.
|
||||||
are provided for interoperating with standard log management
|
* The system file remains open during program execution. Interfaces
|
||||||
tools like logrotate(8):
|
* are provided for interoperating with standard log management
|
||||||
http://linuxcommand.org/man_pages/logrotate8.html
|
* tools like logrotate(8):
|
||||||
@note None of the listed interfaces are thread-safe.
|
* http://linuxcommand.org/man_pages/logrotate8.html
|
||||||
*/
|
* @note None of the listed interfaces are thread-safe.
|
||||||
|
*/
|
||||||
class File
|
class File
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Construct with no associated system file.
|
/**
|
||||||
A system file may be associated later with @ref open.
|
* Construct with no associated system file.
|
||||||
@see open
|
* A system file may be associated later with @ref open.
|
||||||
*/
|
* @see open
|
||||||
|
*/
|
||||||
File();
|
File();
|
||||||
|
|
||||||
/** Destroy the object.
|
/**
|
||||||
If a system file is associated, it will be flushed and closed.
|
* Destroy the object.
|
||||||
*/
|
* If a system file is associated, it will be flushed and closed.
|
||||||
|
*/
|
||||||
~File() = default;
|
~File() = default;
|
||||||
|
|
||||||
/** Determine if a system file is associated with the log.
|
/**
|
||||||
@return `true` if a system file is associated and opened for
|
* Determine if a system file is associated with the log.
|
||||||
writing.
|
* @return `true` if a system file is associated and opened for
|
||||||
*/
|
* writing.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isOpen() const noexcept;
|
isOpen() const noexcept;
|
||||||
|
|
||||||
/** Associate a system file with the log.
|
/**
|
||||||
If the file does not exist an attempt is made to create it
|
* Associate a system file with the log.
|
||||||
and open it for writing. If the file already exists an attempt is
|
* If the file does not exist an attempt is made to create it
|
||||||
made to open it for appending.
|
* and open it for writing. If the file already exists an attempt is
|
||||||
If a system file is already associated with the log, it is closed
|
* made to open it for appending.
|
||||||
first.
|
* If a system file is already associated with the log, it is closed
|
||||||
@return `true` if the file was opened.
|
* first.
|
||||||
*/
|
* @return `true` if the file was opened.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
open(boost::filesystem::path const& path);
|
open(boost::filesystem::path const& path);
|
||||||
|
|
||||||
/** Close and re-open the system file associated with the log
|
/**
|
||||||
This assists in interoperating with external log management tools.
|
* Close and re-open the system file associated with the log
|
||||||
@return `true` if the file was opened.
|
* This assists in interoperating with external log management tools.
|
||||||
*/
|
* @return `true` if the file was opened.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
closeAndReopen();
|
closeAndReopen();
|
||||||
|
|
||||||
/** Close the system file if it is open. */
|
/**
|
||||||
|
* Close the system file if it is open.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
close();
|
close();
|
||||||
|
|
||||||
/** write to the log file.
|
/**
|
||||||
Does nothing if there is no associated system file.
|
* write to the log file.
|
||||||
*/
|
* Does nothing if there is no associated system file.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
write(char const* text);
|
write(char const* text);
|
||||||
|
|
||||||
/** write to the log file and append an end of line marker.
|
/**
|
||||||
Does nothing if there is no associated system file.
|
* write to the log file and append an end of line marker.
|
||||||
*/
|
* Does nothing if there is no associated system file.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
writeln(char const* text);
|
writeln(char const* text);
|
||||||
|
|
||||||
/** Write to the log file using std::string. */
|
/**
|
||||||
|
* Write to the log file using std::string.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
void
|
void
|
||||||
write(std::string const& str)
|
write(std::string const& str)
|
||||||
@@ -223,19 +237,21 @@ private:
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Debug logging:
|
// Debug logging:
|
||||||
|
|
||||||
/** Set the sink for the debug journal.
|
/**
|
||||||
|
* Set the sink for the debug journal.
|
||||||
@param sink unique_ptr to new debug Sink.
|
*
|
||||||
@return unique_ptr to the previous Sink. nullptr if there was no Sink.
|
* @param sink unique_ptr to new debug Sink.
|
||||||
*/
|
* @return unique_ptr to the previous Sink. nullptr if there was no Sink.
|
||||||
|
*/
|
||||||
std::unique_ptr<beast::Journal::Sink>
|
std::unique_ptr<beast::Journal::Sink>
|
||||||
setDebugLogSink(std::unique_ptr<beast::Journal::Sink> sink);
|
setDebugLogSink(std::unique_ptr<beast::Journal::Sink> sink);
|
||||||
|
|
||||||
/** Returns a debug journal.
|
/**
|
||||||
The journal may drain to a null sink, so its output
|
* Returns a debug journal.
|
||||||
may never be seen. Never use it for critical
|
* The journal may drain to a null sink, so its output
|
||||||
information.
|
* may never be seen. Never use it for critical
|
||||||
*/
|
* information.
|
||||||
|
*/
|
||||||
beast::Journal
|
beast::Journal
|
||||||
debugLog();
|
debugLog();
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Calculate one number divided by another number in percentage.
|
/**
|
||||||
|
* Calculate one number divided by another number in percentage.
|
||||||
* The result is rounded up to the next integer, and capped in the range [0,100]
|
* The result is rounded up to the next integer, and capped in the range [0,100]
|
||||||
* E.g. calculatePercent(1, 100) = 1 because 1/100 = 0.010000
|
* E.g. calculatePercent(1, 100) = 1 because 1/100 = 0.010000
|
||||||
* calculatePercent(1, 99) = 2 because 1/99 = 0.010101
|
* calculatePercent(1, 99) = 2 because 1/99 = 0.010101
|
||||||
@@ -19,7 +20,7 @@ namespace xrpl {
|
|||||||
* @return the percentage, in [0, 100]
|
* @return the percentage, in [0, 100]
|
||||||
*
|
*
|
||||||
* @note total cannot be zero.
|
* @note total cannot be zero.
|
||||||
* */
|
*/
|
||||||
constexpr std::size_t
|
constexpr std::size_t
|
||||||
calculatePercent(std::size_t count, std::size_t total)
|
calculatePercent(std::size_t count, std::size_t total)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
@@ -48,46 +47,54 @@ isPowerOfTen(T value)
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
/** Builds a table of the powers of 10
|
/**
|
||||||
|
* Builds a table of the powers of 10
|
||||||
*
|
*
|
||||||
* This function is marked consteval, so it can only be run in
|
* This function is marked consteval, so it can only be run in
|
||||||
* a constexpr context. This assures that it is and can only be run at
|
* a constexpr context. This assures that it is and can only be run at
|
||||||
* compile time. Doing it at runtime would be pretty wasteful and
|
* compile time. Doing it at runtime would be pretty wasteful and
|
||||||
* inefficient.
|
* inefficient.
|
||||||
*/
|
*/
|
||||||
constexpr std::size_t kInt64Digits = 20;
|
constexpr std::size_t kUint64Digits = 20;
|
||||||
consteval std::array<std::uint64_t, kInt64Digits>
|
[[maybe_unused]] constexpr std::size_t kUint128Digits = 39;
|
||||||
|
|
||||||
|
template <typename T, std::size_t Digits>
|
||||||
|
consteval std::array<T, Digits>
|
||||||
buildPowersOfTen()
|
buildPowersOfTen()
|
||||||
{
|
{
|
||||||
std::array<std::uint64_t, kInt64Digits> result{};
|
std::array<T, Digits> result{};
|
||||||
|
|
||||||
std::uint64_t power = 1;
|
T power = 1;
|
||||||
std::size_t exponent = 0;
|
std::size_t exponent = 0;
|
||||||
// end the loop early so it doesn't overflow;
|
// end the loop early so it doesn't overflow;
|
||||||
for (; exponent < result.size() - 1; ++exponent, power *= 10)
|
for (; exponent < result.size() - 1; ++exponent, power *= 10)
|
||||||
{
|
{
|
||||||
result[exponent] = power;
|
result[exponent] = power;
|
||||||
if (power > std::numeric_limits<std::uint64_t>::max() / 10)
|
if (power > std::numeric_limits<T>::max() / 10)
|
||||||
throw std::logic_error("Power of 10 table is too big");
|
throw std::logic_error("Power of 10 table is too big");
|
||||||
}
|
}
|
||||||
result[exponent] = power;
|
result[exponent] = power;
|
||||||
if (power < std::numeric_limits<std::uint64_t>::max() / 10)
|
if (power < std::numeric_limits<T>::max() / 10)
|
||||||
throw std::logic_error("Power of 10 table is not big enough for the uint64_t type");
|
throw std::logic_error("Power of 10 table is not big enough for the given type");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
constexpr std::array<std::uint64_t, detail::kInt64Digits> kPowerOfTen = detail::buildPowersOfTen();
|
template <typename T = std::uint64_t, std::size_t Digits = detail::kUint64Digits>
|
||||||
|
constexpr std::array<T, Digits> kPowerOfTenImpl = detail::buildPowersOfTen<T, Digits>();
|
||||||
|
|
||||||
|
constexpr auto kPowerOfTen = kPowerOfTenImpl<std::uint64_t, detail::kUint64Digits>;
|
||||||
|
|
||||||
static_assert(kPowerOfTen[0] == 1);
|
static_assert(kPowerOfTen[0] == 1);
|
||||||
static_assert(kPowerOfTen[1] == 10);
|
static_assert(kPowerOfTen[1] == 10);
|
||||||
static_assert(kPowerOfTen[10] == 10'000'000'000);
|
static_assert(kPowerOfTen[10] == 10'000'000'000);
|
||||||
static_assert(
|
static_assert(
|
||||||
isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kInt64Digits - 1);
|
isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kUint64Digits - 1);
|
||||||
|
|
||||||
/** MantissaRange defines a range for the mantissa of a normalized Number.
|
/**
|
||||||
|
* MantissaRange defines a range for the mantissa of a normalized Number.
|
||||||
*
|
*
|
||||||
* The mantissa is in the range [min, max], where
|
* The mantissa is in the range [min, max], where
|
||||||
* * min is a power of 10, and
|
* * min is a power of 10, and
|
||||||
@@ -124,17 +131,37 @@ struct MantissaRange final
|
|||||||
{
|
{
|
||||||
using rep = std::uint64_t;
|
using rep = std::uint64_t;
|
||||||
|
|
||||||
|
// NOLINTBEGIN(readability-enum-initial-value)
|
||||||
|
// The values don't matter, except for Large
|
||||||
enum class MantissaScale {
|
enum class MantissaScale {
|
||||||
|
// Small can be removed when either featureSingleAssetVault or featureLendingProtocol are
|
||||||
|
// retired
|
||||||
Small,
|
Small,
|
||||||
// LargeLegacy can be removed when fixCleanup3_2_0 is retired
|
// LargeLegacy can be removed when fixCleanup3_2_0 is retired
|
||||||
LargeLegacy,
|
LargeLegacy,
|
||||||
Large,
|
// Large320 can be removed when fixCleanup3_3_0 is retired
|
||||||
|
Large320,
|
||||||
|
// If Large330 is ever the only remaining "Large*" entry, it can be renamed to just "Large".
|
||||||
|
Large330,
|
||||||
|
// Large is a de-facto alias for "the latest", and is only here for backward compatibility
|
||||||
|
// in the extremely unlikely case that a downstream project made use of it. Note that
|
||||||
|
// because the behavior changed, this may still be a breaking change.
|
||||||
|
Large = Large330,
|
||||||
};
|
};
|
||||||
|
// NOLINTEND(readability-enum-initial-value)
|
||||||
|
|
||||||
// This entire enum can be removed when fixCleanup3_2_0 is retired
|
// This entire enum can be removed when the last relevant amendment is retired
|
||||||
enum class CuspRoundingFix : bool {
|
enum class CuspRoundingFix : std::uint8_t {
|
||||||
Disabled = false,
|
// Disabled can be removed when fixCleanup3_2_0 is retired
|
||||||
Enabled = true,
|
Disabled = 0,
|
||||||
|
// Enabled320 can be removed when fixCleanup3_3_0 is retired
|
||||||
|
Enabled320 = 1,
|
||||||
|
// If we ever get to the point that there's only one entry, remove the entire enum
|
||||||
|
Enabled330 = 2,
|
||||||
|
// Enabled is a de-facto alias for "the latest", and is only here for backward compatibility
|
||||||
|
// in the extremely unlikely case that a downstream project made use of it. Note that
|
||||||
|
// because the behavior changed, this may still be a breaking change.
|
||||||
|
Enabled = Enabled330,
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit constexpr MantissaRange(MantissaScale sc) : scale(sc)
|
explicit constexpr MantissaRange(MantissaScale sc) : scale(sc)
|
||||||
@@ -145,13 +172,27 @@ struct MantissaRange final
|
|||||||
int const log{getExponent(scale)};
|
int const log{getExponent(scale)};
|
||||||
rep const min{getMin(scale, log)};
|
rep const min{getMin(scale, log)};
|
||||||
rep const max{(min * 10) - 1};
|
rep const max{(min * 10) - 1};
|
||||||
CuspRoundingFix const cuspRoundingFixEnabled{isCuspFixEnabled(scale)};
|
CuspRoundingFix const cuspRoundingFix{isCuspFixEnabled(scale)};
|
||||||
|
|
||||||
static MantissaRange const&
|
|
||||||
getMantissaRange(MantissaScale scale);
|
|
||||||
|
|
||||||
static std::set<MantissaScale> const&
|
static std::set<MantissaScale> const&
|
||||||
getAllScales();
|
getAllScales()
|
||||||
|
{
|
||||||
|
static std::set<MantissaRange::MantissaScale> const kScales = {
|
||||||
|
MantissaRange::MantissaScale::Small,
|
||||||
|
MantissaRange::MantissaScale::LargeLegacy,
|
||||||
|
MantissaRange::MantissaScale::Large320,
|
||||||
|
MantissaRange::MantissaScale::Large330,
|
||||||
|
};
|
||||||
|
return kScales;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Access
|
||||||
|
{
|
||||||
|
static constexpr MantissaRange const&
|
||||||
|
mantissaRange(MantissaScale scale);
|
||||||
|
|
||||||
|
friend Number;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr int
|
static constexpr int
|
||||||
@@ -162,7 +203,8 @@ private:
|
|||||||
case MantissaScale::Small:
|
case MantissaScale::Small:
|
||||||
return 15;
|
return 15;
|
||||||
case MantissaScale::LargeLegacy:
|
case MantissaScale::LargeLegacy:
|
||||||
case MantissaScale::Large:
|
case MantissaScale::Large320:
|
||||||
|
case MantissaScale::Large330:
|
||||||
return 18;
|
return 18;
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
default:
|
default:
|
||||||
@@ -191,24 +233,24 @@ private:
|
|||||||
case MantissaScale::Small:
|
case MantissaScale::Small:
|
||||||
case MantissaScale::LargeLegacy:
|
case MantissaScale::LargeLegacy:
|
||||||
return CuspRoundingFix::Disabled;
|
return CuspRoundingFix::Disabled;
|
||||||
case MantissaScale::Large:
|
case MantissaScale::Large320:
|
||||||
return CuspRoundingFix::Enabled;
|
return CuspRoundingFix::Enabled320;
|
||||||
|
case MantissaScale::Large330:
|
||||||
|
return CuspRoundingFix::Enabled330;
|
||||||
default:
|
default:
|
||||||
// If called in a constexpr context, this throw assures that the build fails if an
|
// If called in a constexpr context, this throw assures that the build fails if an
|
||||||
// invalid scale is used.
|
// invalid scale is used.
|
||||||
throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE
|
throw std::runtime_error("Unknown mantissa scale"); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unordered_map<MantissaScale, MantissaRange> const&
|
|
||||||
getRanges();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Like std::integral, but only 64-bit integral types.
|
// Like std::integral, but only 64-bit integral types.
|
||||||
template <class T>
|
template <class T>
|
||||||
concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::uint64_t>;
|
concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::uint64_t>;
|
||||||
|
|
||||||
/** Number is a floating point type that can represent a wide range of values.
|
/**
|
||||||
|
* Number is a floating point type that can represent a wide range of values.
|
||||||
*
|
*
|
||||||
* It can represent all values that can be represented by an STAmount -
|
* It can represent all values that can be represented by an STAmount -
|
||||||
* regardless of asset type - XRPAmount, MPTAmount, and IOUAmount, with at least
|
* regardless of asset type - XRPAmount, MPTAmount, and IOUAmount, with at least
|
||||||
@@ -304,7 +346,6 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
|||||||
* disable the amendments that control the mantissa range choice
|
* disable the amendments that control the mantissa range choice
|
||||||
* (SingleAssetVault and LendingProtocol), and/or check if either of those
|
* (SingleAssetVault and LendingProtocol), and/or check if either of those
|
||||||
* amendments are enabled to determine which result to expect.
|
* amendments are enabled to determine which result to expect.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
class Number final
|
class Number final
|
||||||
{
|
{
|
||||||
@@ -323,6 +364,8 @@ public:
|
|||||||
static constexpr internalrep kMaxRep = std::numeric_limits<rep>::max();
|
static constexpr internalrep kMaxRep = std::numeric_limits<rep>::max();
|
||||||
static_assert(kMaxRep == 9'223'372'036'854'775'807);
|
static_assert(kMaxRep == 9'223'372'036'854'775'807);
|
||||||
static_assert(-kMaxRep == std::numeric_limits<rep>::min() + 1);
|
static_assert(-kMaxRep == std::numeric_limits<rep>::min() + 1);
|
||||||
|
static constexpr internalrep kMaxRepUp = ((kMaxRep / 10) + 1) * 10;
|
||||||
|
static_assert(kMaxRepUp == 9'223'372'036'854'775'810ULL);
|
||||||
|
|
||||||
// May need to make unchecked private
|
// May need to make unchecked private
|
||||||
struct Unchecked
|
struct Unchecked
|
||||||
@@ -390,10 +433,11 @@ public:
|
|||||||
static Number
|
static Number
|
||||||
lowest() noexcept;
|
lowest() noexcept;
|
||||||
|
|
||||||
/** Conversions to Number are implicit and conversions away from Number
|
/**
|
||||||
* are explicit. This design encourages and facilitates the use of Number
|
* Conversions to Number are implicit and conversions away from Number
|
||||||
* as the preferred type for floating point arithmetic as it makes
|
* are explicit. This design encourages and facilitates the use of Number
|
||||||
* "mixed mode" more convenient, e.g. MPTAmount + Number.
|
* as the preferred type for floating point arithmetic as it makes
|
||||||
|
* "mixed mode" more convenient, e.g. MPTAmount + Number.
|
||||||
*/
|
*/
|
||||||
explicit
|
explicit
|
||||||
operator rep() const; // round to nearest, even on tie
|
operator rep() const; // round to nearest, even on tie
|
||||||
@@ -448,7 +492,9 @@ public:
|
|||||||
return l.mantissa_ < r.mantissa_;
|
return l.mantissa_ < r.mantissa_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the sign of the amount */
|
/**
|
||||||
|
* Return the sign of the amount
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr int
|
[[nodiscard]] constexpr int
|
||||||
signum() const noexcept
|
signum() const noexcept
|
||||||
{
|
{
|
||||||
@@ -502,14 +548,16 @@ public:
|
|||||||
static RoundingMode
|
static RoundingMode
|
||||||
setround(RoundingMode inMode);
|
setround(RoundingMode inMode);
|
||||||
|
|
||||||
/** Returns which mantissa scale is currently in use for normalization.
|
/**
|
||||||
|
* Returns which mantissa scale is currently in use for normalization.
|
||||||
*
|
*
|
||||||
* If you think you need to call this outside of unit tests, no you don't.
|
* If you think you need to call this outside of unit tests, no you don't.
|
||||||
*/
|
*/
|
||||||
static MantissaRange::MantissaScale
|
static MantissaRange::MantissaScale
|
||||||
getMantissaScale();
|
getMantissaScale();
|
||||||
|
|
||||||
/** Changes which mantissa scale is used for normalization.
|
/**
|
||||||
|
* Changes which mantissa scale is used for normalization.
|
||||||
*
|
*
|
||||||
* If you think you need to call this outside of unit tests, no you don't.
|
* If you think you need to call this outside of unit tests, no you don't.
|
||||||
*/
|
*/
|
||||||
@@ -545,6 +593,13 @@ public:
|
|||||||
std::pair<T, int>
|
std::pair<T, int>
|
||||||
normalizeToRange() const;
|
normalizeToRange() const;
|
||||||
|
|
||||||
|
// Safely convert rep (int64) mantissa to internalrep (uint64). If the rep
|
||||||
|
// is negative, returns the positive value. This takes a little extra work
|
||||||
|
// because converting std::numeric_limits<std::int64_t>::min() flirts with
|
||||||
|
// UB, and can vary across compilers.
|
||||||
|
static internalrep
|
||||||
|
externalToInternal(rep mantissa);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static thread_local RoundingMode mode;
|
static thread_local RoundingMode mode;
|
||||||
// The available ranges for mantissa
|
// The available ranges for mantissa
|
||||||
@@ -554,10 +609,17 @@ private:
|
|||||||
// changing the values inside the range.
|
// changing the values inside the range.
|
||||||
static thread_local std::reference_wrapper<MantissaRange const> kRange;
|
static thread_local std::reference_wrapper<MantissaRange const> kRange;
|
||||||
|
|
||||||
|
class Guard;
|
||||||
|
|
||||||
void
|
void
|
||||||
normalize(MantissaRange const& range);
|
normalize(MantissaRange const& range);
|
||||||
|
|
||||||
/** Normalize Number components to an arbitrary range.
|
// Guard has the fields that we need, as well as MantissaRange, so if we have a guard, use that
|
||||||
|
void
|
||||||
|
normalize(Guard const& guard);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize Number components to an arbitrary range.
|
||||||
*
|
*
|
||||||
* min/maxMantissa are parameters because this function is used by both
|
* min/maxMantissa are parameters because this function is used by both
|
||||||
* normalize(), which reads from kRange, and by normalizeToRange,
|
* normalize(), which reads from kRange, and by normalizeToRange,
|
||||||
@@ -571,7 +633,7 @@ private:
|
|||||||
int& exponent,
|
int& exponent,
|
||||||
internalrep const& minMantissa,
|
internalrep const& minMantissa,
|
||||||
internalrep const& maxMantissa,
|
internalrep const& maxMantissa,
|
||||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled);
|
MantissaRange::CuspRoundingFix cuspRoundingFix);
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
friend void
|
friend void
|
||||||
@@ -581,7 +643,7 @@ private:
|
|||||||
int& exponent,
|
int& exponent,
|
||||||
MantissaRange::rep const& minMantissa,
|
MantissaRange::rep const& minMantissa,
|
||||||
MantissaRange::rep const& maxMantissa,
|
MantissaRange::rep const& maxMantissa,
|
||||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled,
|
MantissaRange::CuspRoundingFix cuspRoundingFix,
|
||||||
bool dropped);
|
bool dropped);
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
@@ -592,15 +654,6 @@ private:
|
|||||||
// exponent could go out of range, so it will be checked.
|
// exponent could go out of range, so it will be checked.
|
||||||
[[nodiscard]] Number
|
[[nodiscard]] Number
|
||||||
shiftExponent(int exponentDelta) const;
|
shiftExponent(int exponentDelta) const;
|
||||||
|
|
||||||
// Safely convert rep (int64) mantissa to internalrep (uint64). If the rep
|
|
||||||
// is negative, returns the positive value. This takes a little extra work
|
|
||||||
// because converting std::numeric_limits<std::int64_t>::min() flirts with
|
|
||||||
// UB, and can vary across compilers.
|
|
||||||
static internalrep
|
|
||||||
externalToInternal(rep mantissa);
|
|
||||||
|
|
||||||
class Guard;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept
|
constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept
|
||||||
@@ -635,7 +688,8 @@ inline Number::Number(rep mantissa) : Number{mantissa, 0}
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the mantissa of the external view of the Number.
|
/**
|
||||||
|
* Returns the mantissa of the external view of the Number.
|
||||||
*
|
*
|
||||||
* Please see the "---- External Interface ----" section of the class
|
* Please see the "---- External Interface ----" section of the class
|
||||||
* documentation for an explanation of why the internal value may be modified.
|
* documentation for an explanation of why the internal value may be modified.
|
||||||
@@ -656,7 +710,8 @@ Number::mantissa() const noexcept
|
|||||||
return sign * static_cast<Number::rep>(m);
|
return sign * static_cast<Number::rep>(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the exponent of the external view of the Number.
|
/**
|
||||||
|
* Returns the exponent of the external view of the Number.
|
||||||
*
|
*
|
||||||
* Please see the "---- External Interface ----" section of the class
|
* Please see the "---- External Interface ----" section of the class
|
||||||
* documentation for an explanation of why the internal value may be modified.
|
* documentation for an explanation of why the internal value may be modified.
|
||||||
@@ -862,21 +917,11 @@ squelch(Number const& x, Number const& limit) noexcept
|
|||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string
|
std::string
|
||||||
to_string(MantissaRange::MantissaScale const& scale)
|
to_string(MantissaRange::MantissaScale const& scale);
|
||||||
{
|
|
||||||
switch (scale)
|
std::string
|
||||||
{
|
to_string(Number::RoundingMode const& round);
|
||||||
case MantissaRange::MantissaScale::Small:
|
|
||||||
return "small";
|
|
||||||
case MantissaRange::MantissaScale::LargeLegacy:
|
|
||||||
return "largeLegacy";
|
|
||||||
case MantissaRange::MantissaScale::Large:
|
|
||||||
return "large";
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("Bad scale");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SaveNumberRoundMode
|
class SaveNumberRoundMode
|
||||||
{
|
{
|
||||||
@@ -915,10 +960,10 @@ public:
|
|||||||
operator=(NumberRoundModeGuard const&) = delete;
|
operator=(NumberRoundModeGuard const&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Sets the new scale and restores the old scale when it leaves scope.
|
/**
|
||||||
|
* Sets the new scale and restores the old scale when it leaves scope.
|
||||||
*
|
*
|
||||||
* If you think you need to use this class outside of unit tests, no you don't.
|
* If you think you need to use this class outside of unit tests, no you don't.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
class NumberMantissaScaleGuard
|
class NumberMantissaScaleGuard
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,23 +13,25 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** A closed interval over the domain T.
|
/**
|
||||||
|
* A closed interval over the domain T.
|
||||||
For an instance ClosedInterval c, this represents the closed interval
|
*
|
||||||
(c.first(), c.last()). A single element interval has c.first() == c.last().
|
* For an instance ClosedInterval c, this represents the closed interval
|
||||||
|
* (c.first(), c.last()). A single element interval has c.first() == c.last().
|
||||||
This is simply a type-alias for boost interval container library interval
|
*
|
||||||
set, so users should consult that documentation for available supporting
|
* This is simply a type-alias for boost interval container library interval
|
||||||
member and free functions.
|
* set, so users should consult that documentation for available supporting
|
||||||
*/
|
* member and free functions.
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
using ClosedInterval = boost::icl::closed_interval<T>;
|
using ClosedInterval = boost::icl::closed_interval<T>;
|
||||||
|
|
||||||
/** Create a closed range interval
|
/**
|
||||||
|
* Create a closed range interval
|
||||||
Helper function to create a closed range interval without having to qualify
|
*
|
||||||
the template argument.
|
* Helper function to create a closed range interval without having to qualify
|
||||||
*/
|
* the template argument.
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
ClosedInterval<T>
|
ClosedInterval<T>
|
||||||
range(T low, T high)
|
range(T low, T high)
|
||||||
@@ -37,28 +39,30 @@ range(T low, T high)
|
|||||||
return ClosedInterval<T>(low, high);
|
return ClosedInterval<T>(low, high);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A set of closed intervals over the domain T.
|
/**
|
||||||
|
* A set of closed intervals over the domain T.
|
||||||
Represents a set of values of the domain T using the minimum number
|
*
|
||||||
of disjoint ClosedInterval<T>. This is useful to represent ranges of
|
* Represents a set of values of the domain T using the minimum number
|
||||||
T where a few instances are missing, e.g. the set 1-5,8-9,11-14.
|
* of disjoint ClosedInterval<T>. This is useful to represent ranges of
|
||||||
|
* T where a few instances are missing, e.g. the set 1-5,8-9,11-14.
|
||||||
This is simply a type-alias for boost interval container library interval
|
*
|
||||||
set, so users should consult that documentation for available supporting
|
* This is simply a type-alias for boost interval container library interval
|
||||||
member and free functions.
|
* set, so users should consult that documentation for available supporting
|
||||||
*/
|
* member and free functions.
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
using RangeSet = boost::icl::interval_set<T, std::less, ClosedInterval<T>>;
|
using RangeSet = boost::icl::interval_set<T, std::less, ClosedInterval<T>>;
|
||||||
|
|
||||||
/** Convert a ClosedInterval to a styled string
|
/**
|
||||||
|
* Convert a ClosedInterval to a styled string
|
||||||
The styled string is
|
*
|
||||||
"c.first()-c.last()" if c.first() != c.last()
|
* The styled string is
|
||||||
"c.first()" if c.first() == c.last()
|
* "c.first()-c.last()" if c.first() != c.last()
|
||||||
|
* "c.first()" if c.first() == c.last()
|
||||||
@param ci The closed interval to convert
|
*
|
||||||
@return The style string
|
* @param ci The closed interval to convert
|
||||||
*/
|
* @return The style string
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
std::string
|
std::string
|
||||||
to_string(ClosedInterval<T> const& ci)
|
to_string(ClosedInterval<T> const& ci)
|
||||||
@@ -68,14 +72,15 @@ to_string(ClosedInterval<T> const& ci)
|
|||||||
return std::to_string(ci.first()) + "-" + std::to_string(ci.last());
|
return std::to_string(ci.first()) + "-" + std::to_string(ci.last());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert the given RangeSet to a styled string.
|
/**
|
||||||
|
* Convert the given RangeSet to a styled string.
|
||||||
The styled string representation is the set of disjoint intervals joined
|
*
|
||||||
by commas. The string "empty" is returned if the set is empty.
|
* The styled string representation is the set of disjoint intervals joined
|
||||||
|
* by commas. The string "empty" is returned if the set is empty.
|
||||||
@param rs The rangeset to convert
|
*
|
||||||
@return The styled string
|
* @param rs The rangeset to convert
|
||||||
*/
|
* @return The styled string
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
std::string
|
std::string
|
||||||
to_string(RangeSet<T> const& rs)
|
to_string(RangeSet<T> const& rs)
|
||||||
@@ -91,15 +96,16 @@ to_string(RangeSet<T> const& rs)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert the given styled string to a RangeSet.
|
/**
|
||||||
|
* Convert the given styled string to a RangeSet.
|
||||||
The styled string representation is the set
|
*
|
||||||
of disjoint intervals joined by commas.
|
* The styled string representation is the set
|
||||||
|
* of disjoint intervals joined by commas.
|
||||||
@param rs The set to be populated
|
*
|
||||||
@param s The styled string to convert
|
* @param rs The set to be populated
|
||||||
@return True on successfully converting styled string
|
* @param s The styled string to convert
|
||||||
*/
|
* @return True on successfully converting styled string
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
fromString(RangeSet<T>& rs, std::string const& s)
|
fromString(RangeSet<T>& rs, std::string const& s)
|
||||||
@@ -161,14 +167,15 @@ fromString(RangeSet<T>& rs, std::string const& s)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Find the largest value not in the set that is less than a given value.
|
/**
|
||||||
|
* Find the largest value not in the set that is less than a given value.
|
||||||
@param rs The set of interest
|
*
|
||||||
@param t The value that must be larger than the result
|
* @param rs The set of interest
|
||||||
@param minVal (Default is 0) The smallest allowed value
|
* @param t The value that must be larger than the result
|
||||||
@return The largest v such that minV <= v < t and !contains(rs, v) or
|
* @param minVal (Default is 0) The smallest allowed value
|
||||||
std::nullopt if no such v exists.
|
* @return The largest v such that minV <= v < t and !contains(rs, v) or
|
||||||
*/
|
* std::nullopt if no such v exists.
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
std::optional<T>
|
std::optional<T>
|
||||||
prevMissing(RangeSet<T> const& rs, T t, T minVal = 0)
|
prevMissing(RangeSet<T> const& rs, T t, T minVal = 0)
|
||||||
|
|||||||
@@ -15,22 +15,29 @@ public:
|
|||||||
|
|
||||||
virtual ~Resolver() = 0;
|
virtual ~Resolver() = 0;
|
||||||
|
|
||||||
/** Issue an asynchronous stop request. */
|
/**
|
||||||
|
* Issue an asynchronous stop request.
|
||||||
|
*/
|
||||||
virtual void
|
virtual void
|
||||||
stopAsync() = 0;
|
stopAsync() = 0;
|
||||||
|
|
||||||
/** Issue a synchronous stop request. */
|
/**
|
||||||
|
* Issue a synchronous stop request.
|
||||||
|
*/
|
||||||
virtual void
|
virtual void
|
||||||
stop() = 0;
|
stop() = 0;
|
||||||
|
|
||||||
/** Issue a synchronous start request. */
|
/**
|
||||||
|
* Issue a synchronous start request.
|
||||||
|
*/
|
||||||
virtual void
|
virtual void
|
||||||
start() = 0;
|
start() = 0;
|
||||||
|
|
||||||
/** resolve all hostnames on the list
|
/**
|
||||||
@param names the names to be resolved
|
* resolve all hostnames on the list
|
||||||
@param handler the handler to call
|
* @param names the names to be resolved
|
||||||
*/
|
* @param handler the handler to call
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
template <class Handler>
|
template <class Handler>
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -7,13 +7,14 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** A combination of a std::shared_ptr and a std::weak_pointer.
|
/**
|
||||||
|
* A combination of a std::shared_ptr and a std::weak_pointer.
|
||||||
|
*
|
||||||
This class is a wrapper to a `std::variant<std::shared_ptr,std::weak_ptr>`
|
*
|
||||||
This class is useful for storing intrusive pointers in tagged caches using less
|
* This class is a wrapper to a `std::variant<std::shared_ptr,std::weak_ptr>`
|
||||||
memory than storing both pointers directly.
|
* This class is useful for storing intrusive pointers in tagged caches using less
|
||||||
*/
|
* memory than storing both pointers directly.
|
||||||
|
*/
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class SharedWeakCachePointer
|
class SharedWeakCachePointer
|
||||||
@@ -48,65 +49,79 @@ public:
|
|||||||
|
|
||||||
~SharedWeakCachePointer();
|
~SharedWeakCachePointer();
|
||||||
|
|
||||||
/** Return a strong pointer if this is already a strong pointer (i.e. don't
|
/**
|
||||||
lock the weak pointer. Use the `lock` method if that's what's needed)
|
* Return a strong pointer if this is already a strong pointer (i.e. don't
|
||||||
|
* lock the weak pointer. Use the `lock` method if that's what's needed)
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] std::shared_ptr<T> const&
|
[[nodiscard]] std::shared_ptr<T> const&
|
||||||
getStrong() const;
|
getStrong() const;
|
||||||
|
|
||||||
/** Return true if this is a strong pointer and the strong pointer is
|
/**
|
||||||
seated.
|
* Return true if this is a strong pointer and the strong pointer is
|
||||||
|
* seated.
|
||||||
*/
|
*/
|
||||||
explicit
|
explicit
|
||||||
operator bool() const noexcept;
|
operator bool() const noexcept;
|
||||||
|
|
||||||
/** Set the pointer to null, decrement the appropriate ref count, and run
|
/**
|
||||||
the appropriate release action.
|
* Set the pointer to null, decrement the appropriate ref count, and run
|
||||||
|
* the appropriate release action.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
/** If this is a strong pointer, return the raw pointer. Otherwise return
|
/**
|
||||||
null.
|
* If this is a strong pointer, return the raw pointer. Otherwise return
|
||||||
|
* null.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] T*
|
[[nodiscard]] T*
|
||||||
get() const;
|
get() const;
|
||||||
|
|
||||||
/** If this is a strong pointer, return the strong count. Otherwise return 0
|
/**
|
||||||
|
* If this is a strong pointer, return the strong count. Otherwise return 0
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
useCount() const;
|
useCount() const;
|
||||||
|
|
||||||
/** Return true if there is a non-zero strong count. */
|
/**
|
||||||
|
* Return true if there is a non-zero strong count.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
expired() const;
|
expired() const;
|
||||||
|
|
||||||
/** If this is a strong pointer, return the strong pointer. Otherwise
|
/**
|
||||||
attempt to lock the weak pointer.
|
* If this is a strong pointer, return the strong pointer. Otherwise
|
||||||
|
* attempt to lock the weak pointer.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] std::shared_ptr<T>
|
[[nodiscard]] std::shared_ptr<T>
|
||||||
lock() const;
|
lock() const;
|
||||||
|
|
||||||
/** Return true is this represents a strong pointer. */
|
/**
|
||||||
|
* Return true is this represents a strong pointer.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isStrong() const;
|
isStrong() const;
|
||||||
|
|
||||||
/** Return true is this represents a weak pointer. */
|
/**
|
||||||
|
* Return true is this represents a weak pointer.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isWeak() const;
|
isWeak() const;
|
||||||
|
|
||||||
/** If this is a weak pointer, attempt to convert it to a strong pointer.
|
/**
|
||||||
|
* If this is a weak pointer, attempt to convert it to a strong pointer.
|
||||||
@return true if successfully converted to a strong pointer (or was
|
*
|
||||||
already a strong pointer). Otherwise false.
|
* @return true if successfully converted to a strong pointer (or was
|
||||||
*/
|
* already a strong pointer). Otherwise false.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
convertToStrong();
|
convertToStrong();
|
||||||
|
|
||||||
/** If this is a strong pointer, attempt to convert it to a weak pointer.
|
/**
|
||||||
|
* If this is a strong pointer, attempt to convert it to a weak pointer.
|
||||||
@return false if the pointer is null. Otherwise return true.
|
*
|
||||||
*/
|
* @return false if the pointer is null. Otherwise return true.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
convertToWeak();
|
convertToWeak();
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ class SlabAllocator
|
|||||||
|
|
||||||
static_assert(alignof(Type) == 8 || alignof(Type) == 4);
|
static_assert(alignof(Type) == 8 || alignof(Type) == 4);
|
||||||
|
|
||||||
/** A block of memory that is owned by a slab allocator */
|
/**
|
||||||
|
* A block of memory that is owned by a slab allocator
|
||||||
|
*/
|
||||||
struct SlabBlock
|
struct SlabBlock
|
||||||
{
|
{
|
||||||
// A mutex to protect the freelist for this block:
|
// A mutex to protect the freelist for this block:
|
||||||
@@ -80,7 +82,9 @@ class SlabAllocator
|
|||||||
SlabBlock&
|
SlabBlock&
|
||||||
operator=(SlabBlock&& other) = delete;
|
operator=(SlabBlock&& other) = delete;
|
||||||
|
|
||||||
/** Determines whether the given pointer belongs to this allocator */
|
/**
|
||||||
|
* Determines whether the given pointer belongs to this allocator
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
own(std::uint8_t const* pIn) const noexcept
|
own(std::uint8_t const* pIn) const noexcept
|
||||||
{
|
{
|
||||||
@@ -107,14 +111,15 @@ class SlabAllocator
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return an item to this allocator's freelist.
|
/**
|
||||||
|
* Return an item to this allocator's freelist.
|
||||||
@param ptr The pointer to the chunk of memory being deallocated.
|
*
|
||||||
|
* @param ptr The pointer to the chunk of memory being deallocated.
|
||||||
@note This is a dangerous, private interface; the item being
|
*
|
||||||
returned should belong to this allocator. Debug builds
|
* @note This is a dangerous, private interface; the item being
|
||||||
will check and assert if this is not the case. Release
|
* returned should belong to this allocator. Debug builds
|
||||||
builds will not.
|
* will check and assert if this is not the case. Release
|
||||||
|
* builds will not.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
deallocate(std::uint8_t* ptr) noexcept
|
deallocate(std::uint8_t* ptr) noexcept
|
||||||
@@ -145,13 +150,14 @@ private:
|
|||||||
std::size_t const slabSize_;
|
std::size_t const slabSize_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Constructs a slab allocator able to allocate objects of a fixed size
|
/**
|
||||||
|
* Constructs a slab allocator able to allocate objects of a fixed size
|
||||||
@param count the number of items the slab allocator can allocate; note
|
*
|
||||||
that a count of 0 is valid and means that the allocator
|
* @param count the number of items the slab allocator can allocate; note
|
||||||
is, effectively, disabled. This can be very useful in some
|
* that a count of 0 is valid and means that the allocator
|
||||||
contexts (e.g. when minimal memory usage is needed) and
|
* is, effectively, disabled. This can be very useful in some
|
||||||
allows for graceful failure.
|
* contexts (e.g. when minimal memory usage is needed) and
|
||||||
|
* allows for graceful failure.
|
||||||
*/
|
*/
|
||||||
constexpr explicit SlabAllocator(
|
constexpr explicit SlabAllocator(
|
||||||
std::size_t extra,
|
std::size_t extra,
|
||||||
@@ -179,17 +185,20 @@ public:
|
|||||||
// shutdown process up could make this possible.
|
// shutdown process up could make this possible.
|
||||||
~SlabAllocator() = default;
|
~SlabAllocator() = default;
|
||||||
|
|
||||||
/** Returns the size of the memory block this allocator returns. */
|
/**
|
||||||
|
* Returns the size of the memory block this allocator returns.
|
||||||
|
*/
|
||||||
[[nodiscard]] constexpr std::size_t
|
[[nodiscard]] constexpr std::size_t
|
||||||
size() const noexcept
|
size() const noexcept
|
||||||
{
|
{
|
||||||
return itemSize_;
|
return itemSize_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a suitably aligned pointer, if one is available.
|
/**
|
||||||
|
* Returns a suitably aligned pointer, if one is available.
|
||||||
@return a pointer to a block of memory from the allocator, or
|
*
|
||||||
nullptr if the allocator can't satisfy this request.
|
* @return a pointer to a block of memory from the allocator, or
|
||||||
|
* nullptr if the allocator can't satisfy this request.
|
||||||
*/
|
*/
|
||||||
std::uint8_t*
|
std::uint8_t*
|
||||||
allocate() noexcept
|
allocate() noexcept
|
||||||
@@ -250,12 +259,13 @@ public:
|
|||||||
return slab->allocate();
|
return slab->allocate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the memory block to the allocator.
|
/**
|
||||||
|
* Returns the memory block to the allocator.
|
||||||
@param ptr A pointer to a memory block.
|
*
|
||||||
@param size If non-zero, a hint as to the size of the block.
|
* @param ptr A pointer to a memory block.
|
||||||
@return true if this memory block belonged to the allocator and has
|
* @param size If non-zero, a hint as to the size of the block.
|
||||||
been released; false otherwise.
|
* @return true if this memory block belonged to the allocator and has
|
||||||
|
* been released; false otherwise.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
deallocate(std::uint8_t* ptr) noexcept
|
deallocate(std::uint8_t* ptr) noexcept
|
||||||
@@ -278,7 +288,9 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A collection of slab allocators of various sizes for a given type. */
|
/**
|
||||||
|
* A collection of slab allocators of various sizes for a given type.
|
||||||
|
*/
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
class SlabAllocatorSet
|
class SlabAllocatorSet
|
||||||
{
|
{
|
||||||
@@ -345,13 +357,14 @@ public:
|
|||||||
|
|
||||||
~SlabAllocatorSet() = default;
|
~SlabAllocatorSet() = default;
|
||||||
|
|
||||||
/** Returns a suitably aligned pointer, if one is available.
|
/**
|
||||||
|
* Returns a suitably aligned pointer, if one is available.
|
||||||
@param extra The number of extra bytes, above and beyond the size of
|
*
|
||||||
the object, that should be returned by the allocator.
|
* @param extra The number of extra bytes, above and beyond the size of
|
||||||
|
* the object, that should be returned by the allocator.
|
||||||
@return a pointer to a block of memory, or nullptr if the allocator
|
*
|
||||||
can't satisfy this request.
|
* @return a pointer to a block of memory, or nullptr if the allocator
|
||||||
|
* can't satisfy this request.
|
||||||
*/
|
*/
|
||||||
std::uint8_t*
|
std::uint8_t*
|
||||||
allocate(std::size_t extra) noexcept
|
allocate(std::size_t extra) noexcept
|
||||||
@@ -368,12 +381,13 @@ public:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the memory block to the allocator.
|
/**
|
||||||
|
* Returns the memory block to the allocator.
|
||||||
@param ptr A pointer to a memory block.
|
*
|
||||||
|
* @param ptr A pointer to a memory block.
|
||||||
@return true if this memory block belonged to one of the allocators
|
*
|
||||||
in this set and has been released; false otherwise.
|
* @return true if this memory block belonged to one of the allocators
|
||||||
|
* in this set and has been released; false otherwise.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
deallocate(std::uint8_t* ptr) noexcept
|
deallocate(std::uint8_t* ptr) noexcept
|
||||||
|
|||||||
@@ -16,12 +16,13 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** An immutable linear range of bytes.
|
/**
|
||||||
|
* An immutable linear range of bytes.
|
||||||
A fully constructed Slice is guaranteed to be in a valid state.
|
*
|
||||||
A Slice is lightweight and copyable, it retains no ownership
|
* A fully constructed Slice is guaranteed to be in a valid state.
|
||||||
of the underlying memory.
|
* A Slice is lightweight and copyable, it retains no ownership
|
||||||
*/
|
* of the underlying memory.
|
||||||
|
*/
|
||||||
class Slice
|
class Slice
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -32,30 +33,37 @@ public:
|
|||||||
using value_type = std::uint8_t;
|
using value_type = std::uint8_t;
|
||||||
using const_iterator = value_type const*;
|
using const_iterator = value_type const*;
|
||||||
|
|
||||||
/** Default constructed Slice has length 0. */
|
/**
|
||||||
|
* Default constructed Slice has length 0.
|
||||||
|
*/
|
||||||
Slice() noexcept = default;
|
Slice() noexcept = default;
|
||||||
|
|
||||||
Slice(Slice const&) noexcept = default;
|
Slice(Slice const&) noexcept = default;
|
||||||
Slice&
|
Slice&
|
||||||
operator=(Slice const&) noexcept = default;
|
operator=(Slice const&) noexcept = default;
|
||||||
|
|
||||||
/** Create a slice pointing to existing memory. */
|
/**
|
||||||
|
* Create a slice pointing to existing memory.
|
||||||
|
*/
|
||||||
Slice(void const* data, std::size_t size) noexcept
|
Slice(void const* data, std::size_t size) noexcept
|
||||||
: data_(reinterpret_cast<std::uint8_t const*>(data)), size_(size)
|
: data_(reinterpret_cast<std::uint8_t const*>(data)), size_(size)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return `true` if the byte range is empty. */
|
/**
|
||||||
|
* Return `true` if the byte range is empty.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
empty() const noexcept
|
empty() const noexcept
|
||||||
{
|
{
|
||||||
return size_ == 0;
|
return size_ == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the number of bytes in the storage.
|
/**
|
||||||
|
* Returns the number of bytes in the storage.
|
||||||
This may be zero for an empty range.
|
*
|
||||||
*/
|
* This may be zero for an empty range.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
size() const noexcept
|
size() const noexcept
|
||||||
@@ -70,17 +78,20 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Return a pointer to beginning of the storage.
|
/**
|
||||||
@note The return type is guaranteed to be a pointer
|
* Return a pointer to beginning of the storage.
|
||||||
to a single byte, to facilitate pointer arithmetic.
|
* @note The return type is guaranteed to be a pointer
|
||||||
*/
|
* to a single byte, to facilitate pointer arithmetic.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::uint8_t const*
|
[[nodiscard]] std::uint8_t const*
|
||||||
data() const noexcept
|
data() const noexcept
|
||||||
{
|
{
|
||||||
return data_;
|
return data_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Access raw bytes. */
|
/**
|
||||||
|
* Access raw bytes.
|
||||||
|
*/
|
||||||
std::uint8_t
|
std::uint8_t
|
||||||
operator[](std::size_t i) const noexcept
|
operator[](std::size_t i) const noexcept
|
||||||
{
|
{
|
||||||
@@ -88,7 +99,9 @@ public:
|
|||||||
return data_[i];
|
return data_[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Advance the buffer. */
|
/**
|
||||||
|
* Advance the buffer.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
Slice&
|
Slice&
|
||||||
operator+=(std::size_t n)
|
operator+=(std::size_t n)
|
||||||
@@ -108,7 +121,9 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Shrinks the slice by moving its start forward by n characters. */
|
/**
|
||||||
|
* Shrinks the slice by moving its start forward by n characters.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
removePrefix(std::size_t n)
|
removePrefix(std::size_t n)
|
||||||
{
|
{
|
||||||
@@ -116,7 +131,9 @@ public:
|
|||||||
size_ -= n;
|
size_ -= n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shrinks the slice by moving its end backward by n characters. */
|
/**
|
||||||
|
* Shrinks the slice by moving its end backward by n characters.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
removeSuffix(std::size_t n)
|
removeSuffix(std::size_t n)
|
||||||
{
|
{
|
||||||
@@ -147,16 +164,17 @@ public:
|
|||||||
return data_ + size_;
|
return data_ + size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a "sub slice" of given length starting at the given position
|
/**
|
||||||
|
* Return a "sub slice" of given length starting at the given position
|
||||||
Note that the subslice encompasses the range [pos, pos + rCount),
|
*
|
||||||
where rCount is the smaller of count and size() - pos.
|
* Note that the subslice encompasses the range [pos, pos + rCount),
|
||||||
|
* where rCount is the smaller of count and size() - pos.
|
||||||
@param pos position of the first character
|
*
|
||||||
@count requested length
|
* @param pos position of the first character
|
||||||
|
* @count requested length
|
||||||
@returns The requested subslice, if the request is valid.
|
*
|
||||||
@throws std::out_of_range if pos > size()
|
* @return The requested subslice, if the request is valid.
|
||||||
|
* @throws std::out_of_range if pos > size()
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] Slice
|
[[nodiscard]] Slice
|
||||||
substr(std::size_t pos, std::size_t count = std::numeric_limits<std::size_t>::max()) const
|
substr(std::size_t pos, std::size_t count = std::numeric_limits<std::size_t>::max()) const
|
||||||
|
|||||||
@@ -17,15 +17,16 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Format arbitrary binary data as an SQLite "blob literal".
|
/**
|
||||||
|
* Format arbitrary binary data as an SQLite "blob literal".
|
||||||
In SQLite, blob literals must be encoded when used in a query. Per
|
*
|
||||||
https://sqlite.org/lang_expr.html#literal_values_constants_ they are
|
* In SQLite, blob literals must be encoded when used in a query. Per
|
||||||
encoded as string literals containing hexadecimal data and preceded
|
* https://sqlite.org/lang_expr.html#literal_values_constants_ they are
|
||||||
by a single 'X' character.
|
* encoded as string literals containing hexadecimal data and preceded
|
||||||
|
* by a single 'X' character.
|
||||||
@param blob An arbitrary blob of binary data
|
*
|
||||||
@return The input, encoded as a blob literal.
|
* @param blob An arbitrary blob of binary data
|
||||||
|
* @return The input, encoded as a blob literal.
|
||||||
*/
|
*/
|
||||||
std::string
|
std::string
|
||||||
sqlBlobLiteral(Blob const& blob);
|
sqlBlobLiteral(Blob const& blob);
|
||||||
@@ -130,11 +131,12 @@ trimWhitespace(std::string str);
|
|||||||
std::optional<std::uint64_t>
|
std::optional<std::uint64_t>
|
||||||
toUInt64(std::string const& s);
|
toUInt64(std::string const& s);
|
||||||
|
|
||||||
/** Determines if the given string looks like a TOML-file hosting domain.
|
/**
|
||||||
|
* Determines if the given string looks like a TOML-file hosting domain.
|
||||||
Do not use this function to determine if a particular string is a valid
|
*
|
||||||
domain, as this function may reject domains that are otherwise valid and
|
* Do not use this function to determine if a particular string is a valid
|
||||||
doesn't check whether the TLD is valid.
|
* domain, as this function may reject domains that are otherwise valid and
|
||||||
|
* doesn't check whether the TLD is valid.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
isProperlyFormedTomlDomain(std::string_view domain);
|
isProperlyFormedTomlDomain(std::string_view domain);
|
||||||
|
|||||||
@@ -41,18 +41,19 @@ struct ReplaceDynamically;
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/** Map/cache combination.
|
/**
|
||||||
This class implements a cache and a map. The cache keeps objects alive
|
* Map/cache combination.
|
||||||
in the map. The map allows multiple code paths that reference objects
|
* This class implements a cache and a map. The cache keeps objects alive
|
||||||
with the same tag to get the same actual object.
|
* in the map. The map allows multiple code paths that reference objects
|
||||||
|
* with the same tag to get the same actual object.
|
||||||
So long as data is in the cache, it will stay in memory.
|
*
|
||||||
If it stays in memory even after it is ejected from the cache,
|
* So long as data is in the cache, it will stay in memory.
|
||||||
the map will track it.
|
* If it stays in memory even after it is ejected from the cache,
|
||||||
|
* the map will track it.
|
||||||
@note Callers must not modify data objects that are stored in the cache
|
*
|
||||||
unless they hold their own lock over all cache operations.
|
* @note Callers must not modify data objects that are stored in the cache
|
||||||
*/
|
* unless they hold their own lock over all cache operations.
|
||||||
|
*/
|
||||||
template <
|
template <
|
||||||
class Key,
|
class Key,
|
||||||
class T,
|
class T,
|
||||||
@@ -82,11 +83,15 @@ public:
|
|||||||
beast::insight::Collector::ptr const& collector = beast::insight::NullCollector::make());
|
beast::insight::Collector::ptr const& collector = beast::insight::NullCollector::make());
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Return the clock associated with the cache. */
|
/**
|
||||||
|
* Return the clock associated with the cache.
|
||||||
|
*/
|
||||||
clock_type&
|
clock_type&
|
||||||
clock();
|
clock();
|
||||||
|
|
||||||
/** Returns the number of items in the container. */
|
/**
|
||||||
|
* Returns the number of items in the container.
|
||||||
|
*/
|
||||||
std::size_t
|
std::size_t
|
||||||
size() const;
|
size() const;
|
||||||
|
|
||||||
@@ -105,9 +110,10 @@ public:
|
|||||||
void
|
void
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
/** Refresh the last access time on a key if present.
|
/**
|
||||||
@return `true` If the key was found.
|
* Refresh the last access time on a key if present.
|
||||||
*/
|
* @return `true` If the key was found.
|
||||||
|
*/
|
||||||
template <class KeyComparable>
|
template <class KeyComparable>
|
||||||
bool
|
bool
|
||||||
touchIfExists(KeyComparable const& key);
|
touchIfExists(KeyComparable const& key);
|
||||||
@@ -130,14 +136,15 @@ private:
|
|||||||
SharedPointerType const&,
|
SharedPointerType const&,
|
||||||
SharedPointerType&>;
|
SharedPointerType&>;
|
||||||
|
|
||||||
/** Shared implementation of the canonicalize family.
|
/**
|
||||||
|
* Shared implementation of the canonicalize family.
|
||||||
`policy` selects how a collision is resolved when `key` already exists:
|
*
|
||||||
detail::ReplaceCached, detail::ReplaceClient or
|
* `policy` selects how a collision is resolved when `key` already exists:
|
||||||
detail::ReplaceDynamically. For ReplaceDynamically `replaceCallback` is
|
* detail::ReplaceCached, detail::ReplaceClient or
|
||||||
invoked with the existing strong pointer and returns whether to replace
|
* detail::ReplaceDynamically. For ReplaceDynamically `replaceCallback` is
|
||||||
the cached value with `data`; for the tag policies it is unused.
|
* invoked with the existing strong pointer and returns whether to replace
|
||||||
*/
|
* the cached value with `data`; for the tag policies it is unused.
|
||||||
|
*/
|
||||||
template <class Policy, class Callback = std::nullptr_t>
|
template <class Policy, class Callback = std::nullptr_t>
|
||||||
bool
|
bool
|
||||||
canonicalizeImpl(
|
canonicalizeImpl(
|
||||||
@@ -147,69 +154,73 @@ private:
|
|||||||
Callback&& replaceCallback = nullptr);
|
Callback&& replaceCallback = nullptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Replace aliased objects with originals.
|
/**
|
||||||
|
* Replace aliased objects with originals.
|
||||||
Due to concurrency it is possible for two separate objects with
|
*
|
||||||
the same content and referring to the same unique "thing" to exist.
|
* Due to concurrency it is possible for two separate objects with
|
||||||
This routine eliminates the duplicate and performs a replacement
|
* the same content and referring to the same unique "thing" to exist.
|
||||||
on the callers shared pointer if needed.
|
* This routine eliminates the duplicate and performs a replacement
|
||||||
|
* on the callers shared pointer if needed.
|
||||||
`replaceCallback` is a callable taking the existing strong pointer and
|
*
|
||||||
returning whether to replace the cached value with `data` (true) or to
|
* `replaceCallback` is a callable taking the existing strong pointer and
|
||||||
keep the cached value and write it back into `data` (false). Because the
|
* returning whether to replace the cached value with `data` (true) or to
|
||||||
write-back case mutates `data`, `data` must be writable.
|
* keep the cached value and write it back into `data` (false). Because the
|
||||||
|
* write-back case mutates `data`, `data` must be writable.
|
||||||
@param key The key corresponding to the object
|
*
|
||||||
@param data A shared pointer to the data corresponding to the object.
|
* @param key The key corresponding to the object
|
||||||
@param replaceCallback A callable (existing strong pointer -> bool).
|
* @param data A shared pointer to the data corresponding to the object.
|
||||||
|
* @param replaceCallback A callable (existing strong pointer -> bool).
|
||||||
@return `true` if an existing live entry was found and used; `false` if a new entry was
|
*
|
||||||
inserted or an expired tracked entry was re-cached.
|
* @return `true` if an existing live entry was found and used; `false` if a new entry was
|
||||||
**/
|
* inserted or an expired tracked entry was re-cached.
|
||||||
|
*/
|
||||||
template <class Callback>
|
template <class Callback>
|
||||||
bool
|
bool
|
||||||
canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback);
|
canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback);
|
||||||
|
|
||||||
/** Insert/update the canonical entry for `key`, always replacing the
|
/**
|
||||||
cached value with `data`.
|
* Insert/update the canonical entry for `key`, always replacing the
|
||||||
|
* cached value with `data`.
|
||||||
If an entry already exists for `key`, the cached value is unconditionally
|
*
|
||||||
replaced with `data`; otherwise `data` is inserted. `data` is never
|
* If an entry already exists for `key`, the cached value is unconditionally
|
||||||
written back, so it may be const.
|
* replaced with `data`; otherwise `data` is inserted. `data` is never
|
||||||
|
* written back, so it may be const.
|
||||||
@param key The key corresponding to the object.
|
*
|
||||||
@param data A shared pointer to the data corresponding to the object.
|
* @param key The key corresponding to the object.
|
||||||
|
* @param data A shared pointer to the data corresponding to the object.
|
||||||
@return `true` if an existing live entry was found and used; `false` if a new entry was
|
*
|
||||||
inserted or an expired tracked entry was re-cached.
|
* @return `true` if an existing live entry was found and used; `false` if a new entry was
|
||||||
**/
|
* inserted or an expired tracked entry was re-cached.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data);
|
canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data);
|
||||||
|
|
||||||
/** Insert the canonical entry for `key`, keeping any existing cached value.
|
/**
|
||||||
|
* Insert the canonical entry for `key`, keeping any existing cached value.
|
||||||
If an entry already exists for `key`, the cached value is kept and
|
*
|
||||||
written back into `data` so the caller ends up with the canonical
|
* If an entry already exists for `key`, the cached value is kept and
|
||||||
object; otherwise `data` is inserted. Because `data` may be overwritten
|
* written back into `data` so the caller ends up with the canonical
|
||||||
it must be writable.
|
* object; otherwise `data` is inserted. Because `data` may be overwritten
|
||||||
|
* it must be writable.
|
||||||
@param key The key corresponding to the object.
|
*
|
||||||
@param data A shared pointer to the data corresponding to the object;
|
* @param key The key corresponding to the object.
|
||||||
updated to the canonical value when one already exists.
|
* @param data A shared pointer to the data corresponding to the object;
|
||||||
|
* updated to the canonical value when one already exists.
|
||||||
@return `true` if an existing live entry was found and used; `false` if a new entry was
|
*
|
||||||
inserted or an expired tracked entry was re-cached.
|
* @return `true` if an existing live entry was found and used; `false` if a new entry was
|
||||||
**/
|
* inserted or an expired tracked entry was re-cached.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
canonicalizeReplaceClient(key_type const& key, SharedPointerType& data);
|
canonicalizeReplaceClient(key_type const& key, SharedPointerType& data);
|
||||||
|
|
||||||
SharedPointerType
|
SharedPointerType
|
||||||
fetch(key_type const& key);
|
fetch(key_type const& key);
|
||||||
|
|
||||||
/** Insert the element into the container.
|
/**
|
||||||
If the key already exists, nothing happens.
|
* Insert the element into the container.
|
||||||
@return `true` If the element was inserted
|
* If the key already exists, nothing happens.
|
||||||
*/
|
* @return `true` If the element was inserted
|
||||||
|
*/
|
||||||
template <class ReturnType = bool>
|
template <class ReturnType = bool>
|
||||||
auto
|
auto
|
||||||
insert(key_type const& key, T const& value) -> ReturnType
|
insert(key_type const& key, T const& value) -> ReturnType
|
||||||
@@ -235,15 +246,18 @@ public:
|
|||||||
getKeys() const;
|
getKeys() const;
|
||||||
|
|
||||||
// CachedSLEs functions.
|
// CachedSLEs functions.
|
||||||
/** Returns the fraction of cache hits. */
|
/**
|
||||||
|
* Returns the fraction of cache hits.
|
||||||
|
*/
|
||||||
double
|
double
|
||||||
rate() const;
|
rate() const;
|
||||||
|
|
||||||
/** Fetch an item from the cache.
|
/**
|
||||||
If the digest was not found, Handler
|
* Fetch an item from the cache.
|
||||||
will be called with this signature:
|
* If the digest was not found, Handler
|
||||||
SLE::const_pointer(void)
|
* will be called with this signature:
|
||||||
*/
|
* SLE::const_pointer(void)
|
||||||
|
*/
|
||||||
template <class Handler>
|
template <class Handler>
|
||||||
SharedPointerType
|
SharedPointerType
|
||||||
fetch(key_type const& digest, Handler const& h);
|
fetch(key_type const& digest, Handler const& h);
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
#include <xrpl/basics/IntrusivePointer.ipp>
|
#include <xrpl/basics/IntrusivePointer.ipp>
|
||||||
#include <xrpl/basics/Log.h> // IWYU pragma: keep
|
#include <xrpl/basics/Log.h> // IWYU pragma: keep
|
||||||
#include <xrpl/basics/TaggedCache.h>
|
#include <xrpl/basics/TaggedCache.h>
|
||||||
|
#include <xrpl/basics/scope.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
@@ -601,8 +604,42 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
|||||||
std::vector<key_type> v;
|
std::vector<key_type> v;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::scoped_lock const lock(mutex_);
|
// Keep track of how many iterations are needed. Exit the loop if the number of retries gets
|
||||||
v.reserve(cache_.size());
|
// absurd. (Note that if this somehow ever happens, one more allocation will be done under
|
||||||
|
// lock, which is undesirable, but really should be almost impossible.)
|
||||||
|
std::size_t allocationIterations = 0;
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
for (auto size = cache_.size(); v.capacity() < size && allocationIterations < 20;
|
||||||
|
size = cache_.size())
|
||||||
|
{
|
||||||
|
ScopeUnlock const unlock(lock);
|
||||||
|
if (allocationIterations > 0)
|
||||||
|
{
|
||||||
|
JLOG(journal_.info())
|
||||||
|
<< "getKeys(): Cache grew beyond allocated capacity after "
|
||||||
|
<< allocationIterations << " prior attempt(s). Have " << v.capacity()
|
||||||
|
<< ", need " << size << ". Retrying allocation";
|
||||||
|
}
|
||||||
|
// Allocate the current size plus a little extra, in case the cache grows while
|
||||||
|
// allocating. Each time another allocation is needed, the extra also gets bigger until
|
||||||
|
// it ultimately doubles the size + 1.
|
||||||
|
constexpr std::size_t baseShift = 5;
|
||||||
|
auto const bufferOffset = std::min(allocationIterations, std::size_t{baseShift});
|
||||||
|
auto const bufferShift = baseShift - bufferOffset;
|
||||||
|
size += (size >> bufferShift) + 1;
|
||||||
|
v.reserve(size);
|
||||||
|
++allocationIterations;
|
||||||
|
}
|
||||||
|
if (v.capacity() < cache_.size())
|
||||||
|
{
|
||||||
|
// LCOV_EXCL_START
|
||||||
|
UNREACHABLE("xrpl::TaggedCache::getKeys(): failed to allocate sufficient capacity");
|
||||||
|
v.reserve(cache_.size());
|
||||||
|
// LCOV_EXCL_STOP
|
||||||
|
}
|
||||||
|
XRPL_ASSERT(lock.owns_lock(), "xrpl::TaggedCache::getKeys(): owns lock");
|
||||||
|
XRPL_ASSERT(
|
||||||
|
v.capacity() >= cache_.size(), "xrpl::TaggedCache::getKeys(): sufficient capacity");
|
||||||
for (auto const& _ : cache_)
|
for (auto const& _ : cache_)
|
||||||
v.push_back(_.first);
|
v.push_back(_.first);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** to_string() generalizes std::to_string to handle bools, chars, and strings.
|
/**
|
||||||
|
* to_string() generalizes std::to_string to handle bools, chars, and strings.
|
||||||
It's also possible to provide implementation of to_string for a class
|
*
|
||||||
which needs a string implementation.
|
* It's also possible to provide implementation of to_string for a class
|
||||||
|
* which needs a string implementation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
|||||||
@@ -7,12 +7,13 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Tracks program uptime to seconds precision.
|
/**
|
||||||
|
* Tracks program uptime to seconds precision.
|
||||||
The timer caches the current time as a performance optimization.
|
*
|
||||||
This allows clients to query the current time thousands of times
|
* The timer caches the current time as a performance optimization.
|
||||||
per second.
|
* This allows clients to query the current time thousands of times
|
||||||
*/
|
* per second.
|
||||||
|
*/
|
||||||
|
|
||||||
class UptimeClock
|
class UptimeClock
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,18 +63,19 @@ struct AlwaysFalseT : std::bool_constant<false>
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/** Integers of any length that is a multiple of 32-bits
|
/**
|
||||||
|
* Integers of any length that is a multiple of 32-bits
|
||||||
@note This class stores its values internally in big-endian
|
*
|
||||||
form and that internal representation is part of the
|
* @note This class stores its values internally in big-endian
|
||||||
binary protocol of the XRP Ledger and cannot be changed
|
* form and that internal representation is part of the
|
||||||
arbitrarily without causing breakage.
|
* binary protocol of the XRP Ledger and cannot be changed
|
||||||
|
* arbitrarily without causing breakage.
|
||||||
@tparam Bits The number of bits this integer should have; must
|
*
|
||||||
be at least 64 and a multiple of 32.
|
* @tparam Bits The number of bits this integer should have; must
|
||||||
@tparam Tag An arbitrary type that functions as a tag and allows
|
* be at least 64 and a multiple of 32.
|
||||||
the instantiation of "distinct" types that the same
|
* @tparam Tag An arbitrary type that functions as a tag and allows
|
||||||
number of bits.
|
* the instantiation of "distinct" types that the same
|
||||||
|
* number of bits.
|
||||||
*/
|
*/
|
||||||
template <std::size_t Bits, class Tag = void>
|
template <std::size_t Bits, class Tag = void>
|
||||||
class BaseUInt
|
class BaseUInt
|
||||||
@@ -154,21 +155,23 @@ public:
|
|||||||
return data() + kBytes;
|
return data() + kBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Value hashing function.
|
/**
|
||||||
The seed prevents crafted inputs from causing degenerate parent
|
* Value hashing function.
|
||||||
containers.
|
* The seed prevents crafted inputs from causing degenerate parent
|
||||||
*/
|
* containers.
|
||||||
|
*/
|
||||||
using hasher = HardenedHash<>;
|
using hasher = HardenedHash<>;
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Construct from a raw pointer.
|
/**
|
||||||
The buffer pointed to by `data` must be at least Bits/8 bytes.
|
* Construct from a raw pointer.
|
||||||
|
* The buffer pointed to by `data` must be at least Bits/8 bytes.
|
||||||
@note the structure is used to disambiguate this from the std::uint64_t
|
*
|
||||||
constructor: something like base_uint(0) is ambiguous.
|
* @note the structure is used to disambiguate this from the std::uint64_t
|
||||||
*/
|
* constructor: something like base_uint(0) is ambiguous.
|
||||||
|
*/
|
||||||
// NIKB TODO Remove the need for this constructor.
|
// NIKB TODO Remove the need for this constructor.
|
||||||
struct VoidHelper
|
struct VoidHelper
|
||||||
{
|
{
|
||||||
@@ -305,7 +308,9 @@ public:
|
|||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
c.size() * sizeof(typename Container::value_type) == size(),
|
c.size() * sizeof(typename Container::value_type) == size(),
|
||||||
"xrpl::BaseUInt::fromRaw(Container auto) : input size match");
|
"xrpl::BaseUInt::fromRaw(Container auto) : input size match");
|
||||||
std::memcpy(result.data_.data(), c.data(), size());
|
std::size_t const canCopy =
|
||||||
|
std::min(size(), c.size() * sizeof(typename Container::value_type));
|
||||||
|
std::memcpy(result.data_.data(), c.data(), canCopy);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +324,11 @@ public:
|
|||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
c.size() * sizeof(typename Container::value_type) == size(),
|
c.size() * sizeof(typename Container::value_type) == size(),
|
||||||
"xrpl::BaseUInt::operator=(Container auto) : input size match");
|
"xrpl::BaseUInt::operator=(Container auto) : input size match");
|
||||||
std::memcpy(data_.data(), c.data(), size());
|
std::size_t const canCopy =
|
||||||
|
std::min(size(), c.size() * sizeof(typename Container::value_type));
|
||||||
|
if (canCopy < size())
|
||||||
|
*this = beast::kZero;
|
||||||
|
std::memcpy(data_.data(), c.data(), canCopy);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,13 +512,14 @@ public:
|
|||||||
h(a.data_.data(), sizeof(a.data_));
|
h(a.data_.data(), sizeof(a.data_));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parse a hex string into a base_uint
|
/**
|
||||||
|
* Parse a hex string into a base_uint
|
||||||
The input must be precisely `2 * bytes` hexadecimal characters
|
*
|
||||||
long, with one exception: the value '0'.
|
* The input must be precisely `2 * bytes` hexadecimal characters
|
||||||
|
* long, with one exception: the value '0'.
|
||||||
@param sv A null-terminated string of hexadecimal characters
|
*
|
||||||
@return true if the input was parsed properly; false otherwise.
|
* @param sv A null-terminated string of hexadecimal characters
|
||||||
|
* @return true if the input was parsed properly; false otherwise.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] constexpr bool
|
[[nodiscard]] constexpr bool
|
||||||
parseHex(std::string_view sv)
|
parseHex(std::string_view sv)
|
||||||
@@ -595,7 +605,7 @@ template <std::size_t Bits, typename Tag>
|
|||||||
[[nodiscard]] constexpr bool
|
[[nodiscard]] constexpr bool
|
||||||
operator==(BaseUInt<Bits, Tag> const& lhs, BaseUInt<Bits, Tag> const& rhs)
|
operator==(BaseUInt<Bits, Tag> const& lhs, BaseUInt<Bits, Tag> const& rhs)
|
||||||
{
|
{
|
||||||
return (lhs <=> rhs) == 0;
|
return (lhs <=> rhs) == 0; // NOLINT(modernize-use-nullptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -21,15 +21,16 @@ using days =
|
|||||||
|
|
||||||
using weeks = std::chrono::duration<int, std::ratio_multiply<days::period, std::ratio<7>>>;
|
using weeks = std::chrono::duration<int, std::ratio_multiply<days::period, std::ratio<7>>>;
|
||||||
|
|
||||||
/** Clock for measuring the network time.
|
/**
|
||||||
|
* Clock for measuring the network time.
|
||||||
The epoch is January 1, 2000
|
*
|
||||||
|
* The epoch is January 1, 2000
|
||||||
epoch_offset
|
*
|
||||||
= date(2000-01-01) - date(1970-0-01)
|
* epoch_offset
|
||||||
= days(10957)
|
* = date(2000-01-01) - date(1970-0-01)
|
||||||
= seconds(946684800)
|
* = days(10957)
|
||||||
*/
|
* = seconds(946684800)
|
||||||
|
*/
|
||||||
|
|
||||||
static constexpr std::chrono::seconds kEpochOffset =
|
static constexpr std::chrono::seconds kEpochOffset =
|
||||||
date::sys_days{date::year{2000} / 1 / 1} - date::sys_days{date::year{1970} / 1 / 1};
|
date::sys_days{date::year{2000} / 1 / 1} - date::sys_days{date::year{1970} / 1 / 1};
|
||||||
@@ -81,16 +82,21 @@ toStringIso(NetClock::time_point tp)
|
|||||||
return toStringIso(date::sys_time<NetClock::duration>{tp.time_since_epoch() + kEpochOffset});
|
return toStringIso(date::sys_time<NetClock::duration>{tp.time_since_epoch() + kEpochOffset});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A clock for measuring elapsed time.
|
/**
|
||||||
|
* A clock for measuring elapsed time.
|
||||||
The epoch is unspecified.
|
*
|
||||||
*/
|
* The epoch is unspecified.
|
||||||
|
*/
|
||||||
using Stopwatch = beast::AbstractClock<std::chrono::steady_clock>;
|
using Stopwatch = beast::AbstractClock<std::chrono::steady_clock>;
|
||||||
|
|
||||||
/** A manual Stopwatch for unit tests. */
|
/**
|
||||||
|
* A manual Stopwatch for unit tests.
|
||||||
|
*/
|
||||||
using TestStopwatch = beast::ManualClock<std::chrono::steady_clock>;
|
using TestStopwatch = beast::ManualClock<std::chrono::steady_clock>;
|
||||||
|
|
||||||
/** Returns an instance of a wall clock. */
|
/**
|
||||||
|
* Returns an instance of a wall clock.
|
||||||
|
*/
|
||||||
inline Stopwatch&
|
inline Stopwatch&
|
||||||
stopwatch()
|
stopwatch()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,20 +15,23 @@ namespace xrpl {
|
|||||||
preconditions, postconditions, and invariants.
|
preconditions, postconditions, and invariants.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Generates and logs a call stack */
|
/**
|
||||||
|
* Generates and logs a call stack
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
logThrow(std::string const& title);
|
logThrow(std::string const& title);
|
||||||
|
|
||||||
/** Rethrow the exception currently being handled.
|
/**
|
||||||
|
* Rethrow the exception currently being handled.
|
||||||
When called from within a catch block, it will pass
|
*
|
||||||
control to the next matching exception handler, if any.
|
* When called from within a catch block, it will pass
|
||||||
Otherwise, std::terminate will be called.
|
* control to the next matching exception handler, if any.
|
||||||
|
* Otherwise, std::terminate will be called.
|
||||||
ASAN can't handle sudden jumps in control flow very well. This
|
*
|
||||||
function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from
|
* ASAN can't handle sudden jumps in control flow very well. This
|
||||||
triggering false positives, since it throws.
|
* function is marked as XRPL_NO_SANITIZE_ADDRESS to prevent it from
|
||||||
*/
|
* triggering false positives, since it throws.
|
||||||
|
*/
|
||||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||||
rethrow()
|
rethrow()
|
||||||
{
|
{
|
||||||
@@ -56,7 +59,9 @@ Throw(Args&&... args)
|
|||||||
throw std::move(e);
|
throw std::move(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when faulty logic causes a broken invariant. */
|
/**
|
||||||
|
* Called when faulty logic causes a broken invariant.
|
||||||
|
*/
|
||||||
[[noreturn]] void
|
[[noreturn]] void
|
||||||
logicError(std::string const& how) noexcept;
|
logicError(std::string const& how) noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -39,33 +39,33 @@ makeSeedPair() noexcept
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Seed functor once per construction
|
* Seed functor once per construction
|
||||||
|
*
|
||||||
A std compatible hash adapter that resists adversarial inputs.
|
* A std compatible hash adapter that resists adversarial inputs.
|
||||||
For this to work, T must implement in its own namespace:
|
* For this to work, T must implement in its own namespace:
|
||||||
|
*
|
||||||
@code
|
* @code
|
||||||
|
*
|
||||||
template <class Hasher>
|
* template <class Hasher>
|
||||||
void
|
* void
|
||||||
hash_append (Hasher& h, T const& t) noexcept
|
* hash_append (Hasher& h, T const& t) noexcept
|
||||||
{
|
* {
|
||||||
// hash_append each base and member that should
|
* // hash_append each base and member that should
|
||||||
// participate in forming the hash
|
* // participate in forming the hash
|
||||||
using beast::hash_append;
|
* using beast::hash_append;
|
||||||
hash_append (h, static_cast<T::base1 const&>(t));
|
* hash_append (h, static_cast<T::base1 const&>(t));
|
||||||
hash_append (h, static_cast<T::base2 const&>(t));
|
* hash_append (h, static_cast<T::base2 const&>(t));
|
||||||
// ...
|
* // ...
|
||||||
hash_append (h, t.member1);
|
* hash_append (h, t.member1);
|
||||||
hash_append (h, t.member2);
|
* hash_append (h, t.member2);
|
||||||
// ...
|
* // ...
|
||||||
}
|
* }
|
||||||
|
*
|
||||||
@endcode
|
* @endcode
|
||||||
|
*
|
||||||
Do not use any version of Murmur or CityHash for the Hasher
|
* Do not use any version of Murmur or CityHash for the Hasher
|
||||||
template parameter (the hashing algorithm). For details
|
* template parameter (the hashing algorithm). For details
|
||||||
see https://131002.net/siphash/#at
|
* see https://131002.net/siphash/#at
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template <class HashAlgorithm = beast::Xxhasher>
|
template <class HashAlgorithm = beast::Xxhasher>
|
||||||
class HardenedHash
|
class HardenedHash
|
||||||
|
|||||||
@@ -7,11 +7,15 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** Create a self-signed SSL context that allows anonymous Diffie Hellman. */
|
/**
|
||||||
|
* Create a self-signed SSL context that allows anonymous Diffie Hellman.
|
||||||
|
*/
|
||||||
std::shared_ptr<boost::asio::ssl::context>
|
std::shared_ptr<boost::asio::ssl::context>
|
||||||
makeSslContext(std::string const& cipherList);
|
makeSslContext(std::string const& cipherList);
|
||||||
|
|
||||||
/** Create an authenticated SSL context using the specified files. */
|
/**
|
||||||
|
* Create an authenticated SSL context using the specified files.
|
||||||
|
*/
|
||||||
std::shared_ptr<boost::asio::ssl::context>
|
std::shared_ptr<boost::asio::ssl::context>
|
||||||
makeSslContextAuthed(
|
makeSslContextAuthed(
|
||||||
std::string const& keyFile,
|
std::string const& keyFile,
|
||||||
|
|||||||
@@ -7,16 +7,16 @@
|
|||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
constexpr auto kMuldivMax = std::numeric_limits<std::uint64_t>::max();
|
constexpr auto kMuldivMax = std::numeric_limits<std::uint64_t>::max();
|
||||||
|
|
||||||
/** Return value*mul/div accurately.
|
/**
|
||||||
Computes the result of the multiplication and division in
|
* Return value*mul/div accurately.
|
||||||
a single step, avoiding overflow and retaining precision.
|
*
|
||||||
Throws:
|
* Computes the result of the multiplication and division in
|
||||||
None
|
* a single step, avoiding overflow and retaining precision.
|
||||||
Returns:
|
*
|
||||||
`std::optional`:
|
* @throws None
|
||||||
`std::nullopt` if the calculation overflows. Otherwise, `value * mul
|
* @return `std::nullopt` if the calculation overflows. Otherwise,
|
||||||
/ div`.
|
* `value * mul / div`.
|
||||||
*/
|
*/
|
||||||
std::optional<std::uint64_t>
|
std::optional<std::uint64_t>
|
||||||
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
|
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
|
||||||
|
|
||||||
|
|||||||
@@ -33,16 +33,17 @@ template <class Engine, class Result = typename Engine::result_type>
|
|||||||
using is_engine = std::is_invocable_r<Result, Engine>;
|
using is_engine = std::is_invocable_r<Result, Engine>;
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/** Return the default random engine.
|
/**
|
||||||
|
* Return the default random engine.
|
||||||
This engine is guaranteed to be deterministic, but by
|
*
|
||||||
default will be randomly seeded. It is NOT cryptographically
|
* This engine is guaranteed to be deterministic, but by
|
||||||
secure and MUST NOT be used to generate randomness that
|
* default will be randomly seeded. It is NOT cryptographically
|
||||||
will be used for keys, secure cookies, IVs, padding, etc.
|
* secure and MUST NOT be used to generate randomness that
|
||||||
|
* will be used for keys, secure cookies, IVs, padding, etc.
|
||||||
Each thread gets its own instance of the engine which
|
*
|
||||||
will be randomly seeded.
|
* Each thread gets its own instance of the engine which
|
||||||
*/
|
* will be randomly seeded.
|
||||||
|
*/
|
||||||
inline beast::xor_shift_engine&
|
inline beast::xor_shift_engine&
|
||||||
defaultPrng()
|
defaultPrng()
|
||||||
{
|
{
|
||||||
@@ -70,25 +71,26 @@ defaultPrng()
|
|||||||
return kEngine;
|
return kEngine;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a uniformly distributed random integer.
|
/**
|
||||||
|
* Return a uniformly distributed random integer.
|
||||||
@param min The smallest value to return. If not specified
|
*
|
||||||
the value defaults to 0.
|
* @param min The smallest value to return. If not specified
|
||||||
@param max The largest value to return. If not specified
|
* the value defaults to 0.
|
||||||
the value defaults to the largest value that
|
* @param max The largest value to return. If not specified
|
||||||
can be represented.
|
* the value defaults to the largest value that
|
||||||
|
* can be represented.
|
||||||
The randomness is generated by the specified engine (or
|
*
|
||||||
the default engine if one is not specified). The result
|
* The randomness is generated by the specified engine (or
|
||||||
is cryptographically secure only when the engine passed
|
* the default engine if one is not specified). The result
|
||||||
into the function is cryptographically secure.
|
* is cryptographically secure only when the engine passed
|
||||||
|
* into the function is cryptographically secure.
|
||||||
@note The range is always a closed interval, so calling
|
*
|
||||||
rand_int(-5, 15) can return any integer in the
|
* @note The range is always a closed interval, so calling
|
||||||
closed interval [-5, 15]; similarly, calling
|
* rand_int(-5, 15) can return any integer in the
|
||||||
rand_int(7) can return any integer in the closed
|
* closed interval [-5, 15]; similarly, calling
|
||||||
interval [0, 7].
|
* rand_int(7) can return any integer in the closed
|
||||||
*/
|
* interval [0, 7].
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
template <class Engine, class Integral>
|
template <class Engine, class Integral>
|
||||||
Integral
|
Integral
|
||||||
@@ -144,7 +146,9 @@ randInt()
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Return a random byte */
|
/**
|
||||||
|
* Return a random byte
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
template <class Byte, class Engine>
|
template <class Byte, class Engine>
|
||||||
Byte
|
Byte
|
||||||
@@ -166,7 +170,9 @@ randByte()
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Return a random boolean value */
|
/**
|
||||||
|
* Return a random boolean value
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
template <class Engine>
|
template <class Engine>
|
||||||
inline bool
|
inline bool
|
||||||
|
|||||||
@@ -156,41 +156,41 @@ template <class EF>
|
|||||||
ScopeSuccess(EF) -> ScopeSuccess<EF>;
|
ScopeSuccess(EF) -> ScopeSuccess<EF>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Automatically unlocks and re-locks a unique_lock object.
|
* Automatically unlocks and re-locks a unique_lock object.
|
||||||
|
*
|
||||||
This is the reverse of a std::unique_lock object - instead of locking the
|
* This is the reverse of a std::unique_lock object - instead of locking the
|
||||||
mutex for the lifetime of this object, it unlocks it.
|
* mutex for the lifetime of this object, it unlocks it.
|
||||||
|
*
|
||||||
Make sure you don't try to unlock mutexes that aren't actually locked!
|
* Make sure you don't try to unlock mutexes that aren't actually locked!
|
||||||
|
*
|
||||||
This is essentially a less-versatile boost::reverse_lock.
|
* This is essentially a less-versatile boost::reverse_lock.
|
||||||
|
*
|
||||||
e.g. @code
|
* e.g. @code
|
||||||
|
*
|
||||||
std::mutex mut;
|
* std::mutex mut;
|
||||||
|
*
|
||||||
for (;;)
|
* for (;;)
|
||||||
{
|
* {
|
||||||
std::unique_lock myScopedLock{mut};
|
* std::unique_lock myScopedLock{mut};
|
||||||
// mut is now locked
|
* // mut is now locked
|
||||||
|
*
|
||||||
... do some stuff with it locked ..
|
* ... do some stuff with it locked ..
|
||||||
|
*
|
||||||
while (xyz)
|
* while (xyz)
|
||||||
{
|
* {
|
||||||
... do some stuff with it locked ..
|
* ... do some stuff with it locked ..
|
||||||
|
*
|
||||||
scope_unlock unlocker{myScopedLock};
|
* scope_unlock unlocker{myScopedLock};
|
||||||
|
*
|
||||||
// mut is now unlocked for the remainder of this block,
|
* // mut is now unlocked for the remainder of this block,
|
||||||
// and re-locked at the end.
|
* // and re-locked at the end.
|
||||||
|
*
|
||||||
...do some stuff with it unlocked ...
|
* ...do some stuff with it unlocked ...
|
||||||
} // mut gets locked here.
|
* } // mut gets locked here.
|
||||||
|
*
|
||||||
} // mut gets unlocked here
|
* } // mut gets unlocked here
|
||||||
@endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template <class Mutex>
|
template <class Mutex>
|
||||||
class ScopeUnlock
|
class ScopeUnlock
|
||||||
|
|||||||
@@ -15,15 +15,16 @@
|
|||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
/** Inform the processor that we are in a tight spin-wait loop.
|
/**
|
||||||
|
* Inform the processor that we are in a tight spin-wait loop.
|
||||||
Spinlocks caught in tight loops can result in the processor's pipeline
|
*
|
||||||
filling up with comparison operations, resulting in a misprediction at
|
* Spinlocks caught in tight loops can result in the processor's pipeline
|
||||||
the time the lock is finally acquired, necessitating pipeline flushing
|
* filling up with comparison operations, resulting in a misprediction at
|
||||||
which is ridiculously expensive and results in very high latency.
|
* the time the lock is finally acquired, necessitating pipeline flushing
|
||||||
|
* which is ridiculously expensive and results in very high latency.
|
||||||
This function instructs the processor to "pause" for some architecture
|
*
|
||||||
specific amount of time, to prevent this.
|
* This function instructs the processor to "pause" for some architecture
|
||||||
|
* specific amount of time, to prevent this.
|
||||||
*/
|
*/
|
||||||
inline void
|
inline void
|
||||||
spinPause() noexcept
|
spinPause() noexcept
|
||||||
@@ -38,37 +39,39 @@ spinPause() noexcept
|
|||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/** @{ */
|
/** @{ */
|
||||||
/** Classes to handle arrays of spinlocks packed into a single atomic integer:
|
/**
|
||||||
|
* Classes to handle arrays of spinlocks packed into a single atomic integer:
|
||||||
Packed spinlocks allow for tremendously space-efficient lock-sharding
|
*
|
||||||
but they come at a cost.
|
* Packed spinlocks allow for tremendously space-efficient lock-sharding
|
||||||
|
* but they come at a cost.
|
||||||
First, the implementation is necessarily low-level and uses advanced
|
*
|
||||||
features like memory ordering and highly platform-specific tricks to
|
* First, the implementation is necessarily low-level and uses advanced
|
||||||
maximize performance. This imposes a significant and ongoing cost to
|
* features like memory ordering and highly platform-specific tricks to
|
||||||
developers.
|
* maximize performance. This imposes a significant and ongoing cost to
|
||||||
|
* developers.
|
||||||
Second, and perhaps most important, is that the packing of multiple
|
*
|
||||||
locks into a single integer which, albeit space-efficient, also has
|
* Second, and perhaps most important, is that the packing of multiple
|
||||||
performance implications stemming from data dependencies, increased
|
* locks into a single integer which, albeit space-efficient, also has
|
||||||
cache-coherency traffic between processors and heavier loads on the
|
* performance implications stemming from data dependencies, increased
|
||||||
processor's load/store units.
|
* cache-coherency traffic between processors and heavier loads on the
|
||||||
|
* processor's load/store units.
|
||||||
To be sure, these locks can have advantages but they are definitely
|
*
|
||||||
not general purpose locks and should not be thought of or used that
|
* To be sure, these locks can have advantages but they are definitely
|
||||||
way. The use cases for them are likely few and far between; without
|
* not general purpose locks and should not be thought of or used that
|
||||||
a compelling reason to use them, backed by profiling data, it might
|
* way. The use cases for them are likely few and far between; without
|
||||||
be best to use one of the standard locking primitives instead. Note
|
* a compelling reason to use them, backed by profiling data, it might
|
||||||
that in most common platforms, `std::mutex` is so heavily optimized
|
* be best to use one of the standard locking primitives instead. Note
|
||||||
that it can, usually, outperform spinlocks.
|
* that in most common platforms, `std::mutex` is so heavily optimized
|
||||||
|
* that it can, usually, outperform spinlocks.
|
||||||
@tparam T An unsigned integral type (e.g. std::uint16_t)
|
*
|
||||||
|
* @tparam T An unsigned integral type (e.g. std::uint16_t)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** A class that grabs a single packed spinlock from an atomic integer.
|
/**
|
||||||
|
* A class that grabs a single packed spinlock from an atomic integer.
|
||||||
This class meets the requirements of Lockable:
|
*
|
||||||
https://en.cppreference.com/w/cpp/named_req/Lockable
|
* This class meets the requirements of Lockable:
|
||||||
|
* https://en.cppreference.com/w/cpp/named_req/Lockable
|
||||||
*/
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
class PackedSpinlock
|
class PackedSpinlock
|
||||||
@@ -91,13 +94,14 @@ public:
|
|||||||
PackedSpinlock&
|
PackedSpinlock&
|
||||||
operator=(PackedSpinlock const&) = delete;
|
operator=(PackedSpinlock const&) = delete;
|
||||||
|
|
||||||
/** A single spinlock packed inside the specified atomic
|
/**
|
||||||
|
* A single spinlock packed inside the specified atomic
|
||||||
@param lock The atomic integer inside which the spinlock is packed.
|
*
|
||||||
@param index The index of the spinlock this object acquires.
|
* @param lock The atomic integer inside which the spinlock is packed.
|
||||||
|
* @param index The index of the spinlock this object acquires.
|
||||||
@note For performance reasons, you should strive to have `lock` be
|
*
|
||||||
on a cacheline by itself.
|
* @note For performance reasons, you should strive to have `lock` be
|
||||||
|
* on a cacheline by itself.
|
||||||
*/
|
*/
|
||||||
PackedSpinlock(std::atomic<T>& lock, int index) : bits_(lock), mask_(static_cast<T>(1) << index)
|
PackedSpinlock(std::atomic<T>& lock, int index) : bits_(lock), mask_(static_cast<T>(1) << index)
|
||||||
{
|
{
|
||||||
@@ -133,17 +137,18 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A spinlock implemented on top of an atomic integer.
|
/**
|
||||||
|
* A spinlock implemented on top of an atomic integer.
|
||||||
@note Using `packed_spinlock` and `spinlock` against the same underlying
|
*
|
||||||
atomic integer can result in `spinlock` not being able to actually
|
* @note Using `packed_spinlock` and `spinlock` against the same underlying
|
||||||
acquire the lock during periods of high contention, because of how
|
* atomic integer can result in `spinlock` not being able to actually
|
||||||
the two locks operate: `spinlock` will spin trying to grab all the
|
* acquire the lock during periods of high contention, because of how
|
||||||
bits at once, whereas any given `packed_spinlock` will only try to
|
* the two locks operate: `spinlock` will spin trying to grab all the
|
||||||
grab one bit at a time. Caveat emptor.
|
* bits at once, whereas any given `packed_spinlock` will only try to
|
||||||
|
* grab one bit at a time. Caveat emptor.
|
||||||
This class meets the requirements of Lockable:
|
*
|
||||||
https://en.cppreference.com/w/cpp/named_req/Lockable
|
* This class meets the requirements of Lockable:
|
||||||
|
* https://en.cppreference.com/w/cpp/named_req/Lockable
|
||||||
*/
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
class Spinlock
|
class Spinlock
|
||||||
@@ -159,12 +164,13 @@ public:
|
|||||||
Spinlock&
|
Spinlock&
|
||||||
operator=(Spinlock const&) = delete;
|
operator=(Spinlock const&) = delete;
|
||||||
|
|
||||||
/** Grabs the
|
/**
|
||||||
|
* Grabs the
|
||||||
@param lock The atomic integer to spin against.
|
*
|
||||||
|
* @param lock The atomic integer to spin against.
|
||||||
@note For performance reasons, you should strive to have `lock` be
|
*
|
||||||
on a cacheline by itself.
|
* @note For performance reasons, you should strive to have `lock` be
|
||||||
|
* on a cacheline by itself.
|
||||||
*/
|
*/
|
||||||
Spinlock(std::atomic<T>& lock) : lock_(lock)
|
Spinlock(std::atomic<T>& lock) : lock_(lock)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,17 +12,18 @@
|
|||||||
|
|
||||||
namespace xrpl {
|
namespace xrpl {
|
||||||
|
|
||||||
/** A type-safe wrap around standard integral types
|
/**
|
||||||
|
* A type-safe wrap around standard integral types
|
||||||
The tag is used to implement type safety, catching mismatched types at
|
*
|
||||||
compile time. Multiple instantiations wrapping the same underlying integral
|
* The tag is used to implement type safety, catching mismatched types at
|
||||||
type are distinct types (distinguished by tag) and will not interoperate. A
|
* compile time. Multiple instantiations wrapping the same underlying integral
|
||||||
tagged_integer supports all the usual assignment, arithmetic, comparison and
|
* type are distinct types (distinguished by tag) and will not interoperate. A
|
||||||
shifting operations defined for the underlying type
|
* tagged_integer supports all the usual assignment, arithmetic, comparison and
|
||||||
|
* shifting operations defined for the underlying type
|
||||||
The tag is not meant as a unit, which would require restricting the set of
|
*
|
||||||
allowed arithmetic operations.
|
* The tag is not meant as a unit, which would require restricting the set of
|
||||||
*/
|
* allowed arithmetic operations.
|
||||||
|
*/
|
||||||
template <class Int, class Tag>
|
template <class Int, class Tag>
|
||||||
class TaggedInteger : boost::totally_ordered<
|
class TaggedInteger : boost::totally_ordered<
|
||||||
TaggedInteger<Int, Tag>,
|
TaggedInteger<Int, Tag>,
|
||||||
|
|||||||
@@ -14,7 +14,9 @@
|
|||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/** Measures handler latency on an io_context queue. */
|
/**
|
||||||
|
* Measures handler latency on an io_context queue.
|
||||||
|
*/
|
||||||
template <class Clock>
|
template <class Clock>
|
||||||
class IOLatencyProbe
|
class IOLatencyProbe
|
||||||
{
|
{
|
||||||
@@ -42,7 +44,9 @@ public:
|
|||||||
cancel(lock, true);
|
cancel(lock, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the io_context associated with the latency probe. */
|
/**
|
||||||
|
* Return the io_context associated with the latency probe.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
boost::asio::io_context&
|
boost::asio::io_context&
|
||||||
getIoContext()
|
getIoContext()
|
||||||
@@ -57,9 +61,10 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Cancel all pending i/o.
|
/**
|
||||||
Any handlers which have already been queued will still be called.
|
* Cancel all pending i/o.
|
||||||
*/
|
* Any handlers which have already been queued will still be called.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
void
|
void
|
||||||
cancel()
|
cancel()
|
||||||
@@ -76,10 +81,11 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Measure one sample of i/o latency.
|
/**
|
||||||
Handler will be called with this signature:
|
* Measure one sample of i/o latency.
|
||||||
void Handler (Duration d);
|
* Handler will be called with this signature:
|
||||||
*/
|
* void Handler (Duration d);
|
||||||
|
*/
|
||||||
template <class Handler>
|
template <class Handler>
|
||||||
void
|
void
|
||||||
sampleOne(Handler&& handler)
|
sampleOne(Handler&& handler)
|
||||||
@@ -91,10 +97,11 @@ public:
|
|||||||
ios_, SampleOp<Handler>(std::forward<Handler>(handler), Clock::now(), false, this));
|
ios_, SampleOp<Handler>(std::forward<Handler>(handler), Clock::now(), false, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initiate continuous i/o latency sampling.
|
/**
|
||||||
Handler will be called with this signature:
|
* Initiate continuous i/o latency sampling.
|
||||||
void Handler (std::chrono::milliseconds);
|
* Handler will be called with this signature:
|
||||||
*/
|
* void Handler (std::chrono::milliseconds);
|
||||||
|
*/
|
||||||
template <class Handler>
|
template <class Handler>
|
||||||
void
|
void
|
||||||
sample(Handler&& handler)
|
sample(Handler&& handler)
|
||||||
|
|||||||
@@ -2,34 +2,35 @@
|
|||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/** Abstract interface to a clock.
|
/**
|
||||||
|
* Abstract interface to a clock.
|
||||||
This makes now() a member function instead of a static member, so
|
*
|
||||||
an instance of the class can be dependency injected, facilitating
|
* This makes now() a member function instead of a static member, so
|
||||||
unit tests where time may be controlled.
|
* an instance of the class can be dependency injected, facilitating
|
||||||
|
* unit tests where time may be controlled.
|
||||||
An abstract_clock inherits all the nested types of the Clock
|
*
|
||||||
template parameter.
|
* An abstract_clock inherits all the nested types of the Clock
|
||||||
|
* template parameter.
|
||||||
Example:
|
*
|
||||||
|
* Example:
|
||||||
@code
|
*
|
||||||
|
* @code
|
||||||
struct Implementation
|
*
|
||||||
{
|
* struct Implementation
|
||||||
using clock_type = abstract_clock <std::chrono::steady_clock>;
|
* {
|
||||||
clock_type& clock_;
|
* using clock_type = abstract_clock <std::chrono::steady_clock>;
|
||||||
explicit Implementation (clock_type& clock)
|
* clock_type& clock_;
|
||||||
: clock_(clock)
|
* explicit Implementation (clock_type& clock)
|
||||||
{
|
* : clock_(clock)
|
||||||
}
|
* {
|
||||||
};
|
* }
|
||||||
|
* };
|
||||||
@endcode
|
*
|
||||||
|
* @endcode
|
||||||
@tparam Clock A type meeting these requirements:
|
*
|
||||||
http://en.cppreference.com/w/cpp/concept/Clock
|
* @tparam Clock A type meeting these requirements:
|
||||||
*/
|
* http://en.cppreference.com/w/cpp/concept/Clock
|
||||||
|
*/
|
||||||
template <class Clock>
|
template <class Clock>
|
||||||
class AbstractClock
|
class AbstractClock
|
||||||
{
|
{
|
||||||
@@ -46,7 +47,9 @@ public:
|
|||||||
AbstractClock() = default;
|
AbstractClock() = default;
|
||||||
AbstractClock(AbstractClock const&) = default;
|
AbstractClock(AbstractClock const&) = default;
|
||||||
|
|
||||||
/** Returns the current time. */
|
/**
|
||||||
|
* Returns the current time.
|
||||||
|
*/
|
||||||
[[nodiscard]] virtual time_point
|
[[nodiscard]] virtual time_point
|
||||||
now() const = 0;
|
now() const = 0;
|
||||||
};
|
};
|
||||||
@@ -74,11 +77,12 @@ struct AbstractClockWrapper : public AbstractClock<Facade>
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Returns a global instance of an abstract clock.
|
/**
|
||||||
@tparam Facade A type meeting these requirements:
|
* Returns a global instance of an abstract clock.
|
||||||
http://en.cppreference.com/w/cpp/concept/Clock
|
* @tparam Facade A type meeting these requirements:
|
||||||
@tparam Clock The actual concrete clock to use.
|
* http://en.cppreference.com/w/cpp/concept/Clock
|
||||||
*/
|
* @tparam Clock The actual concrete clock to use.
|
||||||
|
*/
|
||||||
template <class Facade, class Clock = Facade>
|
template <class Facade, class Clock = Facade>
|
||||||
AbstractClock<Facade>&
|
AbstractClock<Facade>&
|
||||||
getAbstractClock()
|
getAbstractClock()
|
||||||
|
|||||||
@@ -4,15 +4,16 @@
|
|||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/** A clock whose minimum resolution is one second.
|
/**
|
||||||
|
* A clock whose minimum resolution is one second.
|
||||||
The purpose of this class is to optimize the performance of the now()
|
*
|
||||||
member function call. It uses a dedicated thread that wakes up at least
|
* The purpose of this class is to optimize the performance of the now()
|
||||||
once per second to sample the requested trivial clock.
|
* member function call. It uses a dedicated thread that wakes up at least
|
||||||
|
* once per second to sample the requested trivial clock.
|
||||||
@tparam Clock A type meeting these requirements:
|
*
|
||||||
http://en.cppreference.com/w/cpp/concept/Clock
|
* @tparam Clock A type meeting these requirements:
|
||||||
*/
|
* http://en.cppreference.com/w/cpp/concept/Clock
|
||||||
|
*/
|
||||||
class BasicSecondsClock
|
class BasicSecondsClock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -7,15 +7,16 @@
|
|||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/** Manual clock implementation.
|
/**
|
||||||
|
* Manual clock implementation.
|
||||||
This concrete class implements the @ref abstract_clock interface and
|
*
|
||||||
allows the time to be advanced manually, mainly for the purpose of
|
* This concrete class implements the @ref abstract_clock interface and
|
||||||
providing a clock in unit tests.
|
* allows the time to be advanced manually, mainly for the purpose of
|
||||||
|
* providing a clock in unit tests.
|
||||||
@tparam Clock A type meeting these requirements:
|
*
|
||||||
http://en.cppreference.com/w/cpp/concept/Clock
|
* @tparam Clock A type meeting these requirements:
|
||||||
*/
|
* http://en.cppreference.com/w/cpp/concept/Clock
|
||||||
|
*/
|
||||||
template <class Clock>
|
template <class Clock>
|
||||||
class ManualClock : public AbstractClock<Clock>
|
class ManualClock : public AbstractClock<Clock>
|
||||||
{
|
{
|
||||||
@@ -38,7 +39,9 @@ public:
|
|||||||
return now_;
|
return now_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the current time of the manual clock. */
|
/**
|
||||||
|
* Set the current time of the manual clock.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
set(time_point const& when)
|
set(time_point const& when)
|
||||||
{
|
{
|
||||||
@@ -48,7 +51,9 @@ public:
|
|||||||
now_ = when;
|
now_ = when;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convenience for setting the time in seconds from epoch. */
|
/**
|
||||||
|
* Convenience for setting the time in seconds from epoch.
|
||||||
|
*/
|
||||||
template <class Integer>
|
template <class Integer>
|
||||||
void
|
void
|
||||||
set(Integer secondsFromEpoch)
|
set(Integer secondsFromEpoch)
|
||||||
@@ -56,7 +61,9 @@ public:
|
|||||||
set(time_point(duration(std::chrono::seconds(secondsFromEpoch))));
|
set(time_point(duration(std::chrono::seconds(secondsFromEpoch))));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Advance the clock by a duration. */
|
/**
|
||||||
|
* Advance the clock by a duration.
|
||||||
|
*/
|
||||||
template <class Rep, class Period>
|
template <class Rep, class Period>
|
||||||
void
|
void
|
||||||
advance(std::chrono::duration<Rep, Period> const& elapsed)
|
advance(std::chrono::duration<Rep, Period> const& elapsed)
|
||||||
@@ -67,7 +74,9 @@ public:
|
|||||||
now_ += elapsed;
|
now_ += elapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convenience for advancing the clock by one second. */
|
/**
|
||||||
|
* Convenience for advancing the clock by one second.
|
||||||
|
*/
|
||||||
ManualClock&
|
ManualClock&
|
||||||
operator++()
|
operator++()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/** Expire aged container items past the specified age. */
|
/**
|
||||||
|
* Expire aged container items past the specified age.
|
||||||
|
*/
|
||||||
template <class AgedContainer, class Rep, class Period>
|
template <class AgedContainer, class Rep, class Period>
|
||||||
std::size_t
|
std::size_t
|
||||||
expire(AgedContainer& c, std::chrono::duration<Rep, Period> const& age)
|
expire(AgedContainer& c, std::chrono::duration<Rep, Period> const& age)
|
||||||
|
|||||||
@@ -39,22 +39,23 @@ struct IsBoostReverseIterator<boost::intrusive::reverse_iterator<It>> : std::tru
|
|||||||
explicit IsBoostReverseIterator() = default;
|
explicit IsBoostReverseIterator() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Associative container where each element is also indexed by time.
|
/**
|
||||||
|
* Associative container where each element is also indexed by time.
|
||||||
This container mirrors the interface of the standard library ordered
|
*
|
||||||
associative containers, with the addition that each element is associated
|
* This container mirrors the interface of the standard library ordered
|
||||||
with a `when` `time_point` which is obtained from the value of the clock's
|
* associative containers, with the addition that each element is associated
|
||||||
`now`. The function `touch` updates the time for an element to the current
|
* with a `when` `time_point` which is obtained from the value of the clock's
|
||||||
time as reported by the clock.
|
* `now`. The function `touch` updates the time for an element to the current
|
||||||
|
* time as reported by the clock.
|
||||||
An extra set of iterator types and member functions are provided in the
|
*
|
||||||
`chronological` memberspace that allow traversal in temporal or reverse
|
* An extra set of iterator types and member functions are provided in the
|
||||||
temporal order. This container is useful as a building block for caches
|
* `chronological` memberspace that allow traversal in temporal or reverse
|
||||||
whose items expire after a certain amount of time. The chronological
|
* temporal order. This container is useful as a building block for caches
|
||||||
iterators allow for fully customizable expiration strategies.
|
* whose items expire after a certain amount of time. The chronological
|
||||||
|
* iterators allow for fully customizable expiration strategies.
|
||||||
@see aged_set, aged_multiset, aged_map, aged_multimap
|
*
|
||||||
*/
|
* @see aged_set, aged_multiset, aged_map, aged_multimap
|
||||||
|
*/
|
||||||
template <
|
template <
|
||||||
bool IsMulti,
|
bool IsMulti,
|
||||||
bool IsMap,
|
bool IsMap,
|
||||||
@@ -1795,7 +1796,9 @@ swap(
|
|||||||
lhs.swap(rhs);
|
lhs.swap(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Expire aged container items past the specified age. */
|
/**
|
||||||
|
* Expire aged container items past the specified age.
|
||||||
|
*/
|
||||||
template <
|
template <
|
||||||
bool IsMulti,
|
bool IsMulti,
|
||||||
bool IsMap,
|
bool IsMap,
|
||||||
|
|||||||
@@ -43,23 +43,24 @@ TODO
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
/** Associative container where each element is also indexed by time.
|
/**
|
||||||
|
* Associative container where each element is also indexed by time.
|
||||||
This container mirrors the interface of the standard library unordered
|
*
|
||||||
associative containers, with the addition that each element is associated
|
* This container mirrors the interface of the standard library unordered
|
||||||
with a `when` `time_point` which is obtained from the value of the clock's
|
* associative containers, with the addition that each element is associated
|
||||||
`now`. The function `touch` updates the time for an element to the current
|
* with a `when` `time_point` which is obtained from the value of the clock's
|
||||||
time as reported by the clock.
|
* `now`. The function `touch` updates the time for an element to the current
|
||||||
|
* time as reported by the clock.
|
||||||
An extra set of iterator types and member functions are provided in the
|
*
|
||||||
`chronological` memberspace that allow traversal in temporal or reverse
|
* An extra set of iterator types and member functions are provided in the
|
||||||
temporal order. This container is useful as a building block for caches
|
* `chronological` memberspace that allow traversal in temporal or reverse
|
||||||
whose items expire after a certain amount of time. The chronological
|
* temporal order. This container is useful as a building block for caches
|
||||||
iterators allow for fully customizable expiration strategies.
|
* whose items expire after a certain amount of time. The chronological
|
||||||
|
* iterators allow for fully customizable expiration strategies.
|
||||||
@see aged_unordered_set, aged_unordered_multiset
|
*
|
||||||
@see aged_unordered_map, aged_unordered_multimap
|
* @see aged_unordered_set, aged_unordered_multiset
|
||||||
*/
|
* @see aged_unordered_map, aged_unordered_multimap
|
||||||
|
*/
|
||||||
template <
|
template <
|
||||||
bool IsMulti,
|
bool IsMulti,
|
||||||
bool IsMap,
|
bool IsMap,
|
||||||
@@ -2709,7 +2710,9 @@ swap(
|
|||||||
lhs.swap(rhs);
|
lhs.swap(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Expire aged container items past the specified age. */
|
/**
|
||||||
|
* Expire aged container items past the specified age.
|
||||||
|
*/
|
||||||
template <
|
template <
|
||||||
bool IsMulti,
|
bool IsMulti,
|
||||||
bool IsMap,
|
bool IsMap,
|
||||||
|
|||||||
@@ -12,9 +12,10 @@
|
|||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/** Changes the name of the caller thread.
|
/**
|
||||||
Different OSes may place different length or content limits on this name.
|
* Changes the name of the caller thread.
|
||||||
*/
|
* Different OSes may place different length or content limits on this name.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
setCurrentThreadName(std::string_view newThreadName);
|
setCurrentThreadName(std::string_view newThreadName);
|
||||||
|
|
||||||
@@ -24,13 +25,14 @@ setCurrentThreadName(std::string_view newThreadName);
|
|||||||
// Maximum number of characters is therefore 15.
|
// Maximum number of characters is therefore 15.
|
||||||
constexpr std::size_t kMaxThreadNameLength = 15;
|
constexpr std::size_t kMaxThreadNameLength = 15;
|
||||||
|
|
||||||
/** Sets the name of the caller thread with compile-time size checking.
|
/**
|
||||||
@tparam N The size of the string literal including null terminator
|
* Sets the name of the caller thread with compile-time size checking.
|
||||||
@param newThreadName A string literal to set as the thread name
|
* @tparam N The size of the string literal including null terminator
|
||||||
|
* @param newThreadName A string literal to set as the thread name
|
||||||
This template overload enforces that thread names are at most 16 characters
|
*
|
||||||
(including null terminator) at compile time, matching Linux's limit.
|
* This template overload enforces that thread names are at most 16 characters
|
||||||
*/
|
* (including null terminator) at compile time, matching Linux's limit.
|
||||||
|
*/
|
||||||
template <std::size_t N>
|
template <std::size_t N>
|
||||||
void
|
void
|
||||||
setCurrentThreadName(char const (&newThreadName)[N])
|
setCurrentThreadName(char const (&newThreadName)[N])
|
||||||
@@ -41,14 +43,15 @@ setCurrentThreadName(char const (&newThreadName)[N])
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Returns the name of the caller thread.
|
/**
|
||||||
|
* Returns the name of the caller thread.
|
||||||
The name returned is the name as set by a call to setCurrentThreadName().
|
*
|
||||||
If the thread name is set by an external force, then that name change
|
* The name returned is the name as set by a call to setCurrentThreadName().
|
||||||
will not be reported.
|
* If the thread name is set by an external force, then that name change
|
||||||
|
* will not be reported.
|
||||||
If no name has ever been set, then the empty string is returned.
|
*
|
||||||
*/
|
* If no name has ever been set, then the empty string is returned.
|
||||||
|
*/
|
||||||
std::string
|
std::string
|
||||||
getCurrentThreadName();
|
getCurrentThreadName();
|
||||||
|
|
||||||
|
|||||||
@@ -163,17 +163,19 @@ struct LexicalCast<Out, char*>
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Thrown when a conversion is not possible with LexicalCast.
|
/**
|
||||||
Only used in the throw variants of lexicalCast.
|
* Thrown when a conversion is not possible with LexicalCast.
|
||||||
*/
|
* Only used in the throw variants of lexicalCast.
|
||||||
|
*/
|
||||||
struct BadLexicalCast : public std::bad_cast
|
struct BadLexicalCast : public std::bad_cast
|
||||||
{
|
{
|
||||||
explicit BadLexicalCast() = default;
|
explicit BadLexicalCast() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Intelligently convert from one type to another.
|
/**
|
||||||
@return `false` if there was a parsing or range error
|
* Intelligently convert from one type to another.
|
||||||
*/
|
* @return `false` if there was a parsing or range error
|
||||||
|
*/
|
||||||
template <class Out, class In>
|
template <class Out, class In>
|
||||||
bool
|
bool
|
||||||
lexicalCastChecked(Out& out, In in)
|
lexicalCastChecked(Out& out, In in)
|
||||||
@@ -181,12 +183,13 @@ lexicalCastChecked(Out& out, In in)
|
|||||||
return detail::LexicalCast<Out, In>()(out, in);
|
return detail::LexicalCast<Out, In>()(out, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert from one type to another, throw on error
|
/**
|
||||||
|
* Convert from one type to another, throw on error
|
||||||
An exception of type BadLexicalCast is thrown if the conversion fails.
|
*
|
||||||
|
* An exception of type BadLexicalCast is thrown if the conversion fails.
|
||||||
@return The new type.
|
*
|
||||||
*/
|
* @return The new type.
|
||||||
|
*/
|
||||||
template <class Out, class In>
|
template <class Out, class In>
|
||||||
Out
|
Out
|
||||||
lexicalCastThrow(In in)
|
lexicalCastThrow(In in)
|
||||||
@@ -197,11 +200,12 @@ lexicalCastThrow(In in)
|
|||||||
throw BadLexicalCast();
|
throw BadLexicalCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert from one type to another.
|
/**
|
||||||
|
* Convert from one type to another.
|
||||||
@param defaultValue The value returned if parsing fails
|
*
|
||||||
@return The new type.
|
* @param defaultValue The value returned if parsing fails
|
||||||
*/
|
* @return The new type.
|
||||||
|
*/
|
||||||
template <class Out, class In>
|
template <class Out, class In>
|
||||||
Out
|
Out
|
||||||
lexicalCast(In in, Out defaultValue = Out())
|
lexicalCast(In in, Out defaultValue = Out())
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ class List;
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
/** Copy `const` attribute from T to U if present. */
|
/**
|
||||||
|
* Copy `const` attribute from T to U if present.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
struct CopyConst
|
struct CopyConst
|
||||||
@@ -153,110 +155,111 @@ private:
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/** Intrusive doubly linked list.
|
/**
|
||||||
|
* Intrusive doubly linked list.
|
||||||
This intrusive List is a container similar in operation to std::list in the
|
*
|
||||||
Standard Template Library (STL). Like all @ref intrusive containers, List
|
* This intrusive List is a container similar in operation to std::list in the
|
||||||
requires you to first derive your class from List<>::Node:
|
* Standard Template Library (STL). Like all @ref intrusive containers, List
|
||||||
|
* requires you to first derive your class from List<>::Node:
|
||||||
@code
|
*
|
||||||
|
* @code
|
||||||
struct Object : List <Object>::Node
|
*
|
||||||
{
|
* struct Object : List <Object>::Node
|
||||||
explicit Object (int value) : value_ (value)
|
* {
|
||||||
{
|
* explicit Object (int value) : value_ (value)
|
||||||
}
|
* {
|
||||||
|
* }
|
||||||
int value_;
|
*
|
||||||
};
|
* int value_;
|
||||||
|
* };
|
||||||
@endcode
|
*
|
||||||
|
* @endcode
|
||||||
Now we define the list, and add a couple of items.
|
*
|
||||||
|
* Now we define the list, and add a couple of items.
|
||||||
@code
|
*
|
||||||
|
* @code
|
||||||
List <Object> list;
|
*
|
||||||
|
* List <Object> list;
|
||||||
list.push_back (* (new Object (1)));
|
*
|
||||||
list.push_back (* (new Object (2)));
|
* list.push_back (* (new Object (1)));
|
||||||
|
* list.push_back (* (new Object (2)));
|
||||||
@endcode
|
*
|
||||||
|
* @endcode
|
||||||
For compatibility with the standard containers, push_back() expects a
|
*
|
||||||
reference to the object. Unlike the standard container, however, push_back()
|
* For compatibility with the standard containers, push_back() expects a
|
||||||
places the actual object in the list and not a copy-constructed duplicate.
|
* reference to the object. Unlike the standard container, however, push_back()
|
||||||
|
* places the actual object in the list and not a copy-constructed duplicate.
|
||||||
Iterating over the list follows the same idiom as the STL:
|
*
|
||||||
|
* Iterating over the list follows the same idiom as the STL:
|
||||||
@code
|
*
|
||||||
|
* @code
|
||||||
for (List <Object>::iterator iter = list.begin(); iter != list.end; ++iter)
|
*
|
||||||
std::cout << iter->value_;
|
* for (List <Object>::iterator iter = list.begin(); iter != list.end; ++iter)
|
||||||
|
* std::cout << iter->value_;
|
||||||
@endcode
|
*
|
||||||
|
* @endcode
|
||||||
You can even use BOOST_FOREACH, or range based for loops:
|
*
|
||||||
|
* You can even use BOOST_FOREACH, or range based for loops:
|
||||||
@code
|
*
|
||||||
|
* @code
|
||||||
BOOST_FOREACH (Object& object, list) // boost only
|
*
|
||||||
std::cout << object.value_;
|
* BOOST_FOREACH (Object& object, list) // boost only
|
||||||
|
* std::cout << object.value_;
|
||||||
for (Object& object : list) // C++11 only
|
*
|
||||||
std::cout << object.value_;
|
* for (Object& object : list) // C++11 only
|
||||||
|
* std::cout << object.value_;
|
||||||
@endcode
|
*
|
||||||
|
* @endcode
|
||||||
Because List is mostly STL compliant, it can be passed into STL algorithms:
|
*
|
||||||
e.g. `std::for_each()` or `std::find_first_of()`.
|
* Because List is mostly STL compliant, it can be passed into STL algorithms:
|
||||||
|
* e.g. `std::for_each()` or `std::find_first_of()`.
|
||||||
In general, objects placed into a List should be dynamically allocated
|
*
|
||||||
although this cannot be enforced at compile time. Since the caller provides
|
* In general, objects placed into a List should be dynamically allocated
|
||||||
the storage for the object, the caller is also responsible for deleting the
|
* although this cannot be enforced at compile time. Since the caller provides
|
||||||
object. An object still exists after being removed from a List, until the
|
* the storage for the object, the caller is also responsible for deleting the
|
||||||
caller deletes it. This means an element can be moved from one List to
|
* object. An object still exists after being removed from a List, until the
|
||||||
another with practically no overhead.
|
* caller deletes it. This means an element can be moved from one List to
|
||||||
|
* another with practically no overhead.
|
||||||
Unlike the standard containers, an object may only exist in one list at a
|
*
|
||||||
time, unless special preparations are made. The Tag template parameter is
|
* Unlike the standard containers, an object may only exist in one list at a
|
||||||
used to distinguish between different list types for the same object,
|
* time, unless special preparations are made. The Tag template parameter is
|
||||||
allowing the object to exist in more than one list simultaneously.
|
* used to distinguish between different list types for the same object,
|
||||||
|
* allowing the object to exist in more than one list simultaneously.
|
||||||
For example, consider an actor system where a global list of actors is
|
*
|
||||||
maintained, so that they can each be periodically receive processing
|
* For example, consider an actor system where a global list of actors is
|
||||||
time. We wish to also maintain a list of the subset of actors that require
|
* maintained, so that they can each be periodically receive processing
|
||||||
a domain-dependent update. To achieve this, we declare two tags, the
|
* time. We wish to also maintain a list of the subset of actors that require
|
||||||
associated list types, and the list element thusly:
|
* a domain-dependent update. To achieve this, we declare two tags, the
|
||||||
|
* associated list types, and the list element thusly:
|
||||||
@code
|
*
|
||||||
|
* @code
|
||||||
struct Actor; // Forward declaration required
|
*
|
||||||
|
* struct Actor; // Forward declaration required
|
||||||
struct ProcessTag { };
|
*
|
||||||
struct UpdateTag { };
|
* struct ProcessTag { };
|
||||||
|
* struct UpdateTag { };
|
||||||
using ProcessList = List <Actor, ProcessTag>;
|
*
|
||||||
using UpdateList = List <Actor, UpdateTag>;
|
* using ProcessList = List <Actor, ProcessTag>;
|
||||||
|
* using UpdateList = List <Actor, UpdateTag>;
|
||||||
// Derive from both node types so we can be in each list at once.
|
*
|
||||||
//
|
* // Derive from both node types so we can be in each list at once.
|
||||||
struct Actor : ProcessList::Node, UpdateList::Node
|
* //
|
||||||
{
|
* struct Actor : ProcessList::Node, UpdateList::Node
|
||||||
bool process (); // returns true if we need an update
|
* {
|
||||||
void update ();
|
* bool process (); // returns true if we need an update
|
||||||
};
|
* void update ();
|
||||||
|
* };
|
||||||
@endcode
|
*
|
||||||
|
* @endcode
|
||||||
@tparam T The base type of element which the list will store
|
*
|
||||||
pointers to.
|
* @tparam T The base type of element which the list will store
|
||||||
|
* pointers to.
|
||||||
@tparam Tag An optional unique type name used to distinguish lists and
|
*
|
||||||
nodes, when the object can exist in multiple lists simultaneously.
|
* @tparam Tag An optional unique type name used to distinguish lists and
|
||||||
|
* nodes, when the object can exist in multiple lists simultaneously.
|
||||||
@ingroup beast_core intrusive
|
*
|
||||||
*/
|
* @ingroup beast_core intrusive
|
||||||
|
*/
|
||||||
template <typename T, typename Tag = void>
|
template <typename T, typename Tag = void>
|
||||||
class List
|
class List
|
||||||
{
|
{
|
||||||
@@ -274,7 +277,9 @@ public:
|
|||||||
using iterator = detail::ListIterator<Node>;
|
using iterator = detail::ListIterator<Node>;
|
||||||
using const_iterator = detail::ListIterator<Node const>;
|
using const_iterator = detail::ListIterator<Node const>;
|
||||||
|
|
||||||
/** Create an empty list. */
|
/**
|
||||||
|
* Create an empty list.
|
||||||
|
*/
|
||||||
List()
|
List()
|
||||||
{
|
{
|
||||||
head_.prev_ = nullptr; // identifies the head
|
head_.prev_ = nullptr; // identifies the head
|
||||||
@@ -286,119 +291,133 @@ public:
|
|||||||
List&
|
List&
|
||||||
operator=(List const&) = delete;
|
operator=(List const&) = delete;
|
||||||
|
|
||||||
/** Determine if the list is empty.
|
/**
|
||||||
@return `true` if the list is empty.
|
* Determine if the list is empty.
|
||||||
*/
|
* @return `true` if the list is empty.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
empty() const noexcept
|
empty() const noexcept
|
||||||
{
|
{
|
||||||
return size() == 0;
|
return size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the number of elements in the list. */
|
/**
|
||||||
|
* Returns the number of elements in the list.
|
||||||
|
*/
|
||||||
[[nodiscard]] size_type
|
[[nodiscard]] size_type
|
||||||
size() const noexcept
|
size() const noexcept
|
||||||
{
|
{
|
||||||
return size_;
|
return size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a reference to the first element.
|
/**
|
||||||
@invariant The list may not be empty.
|
* Obtain a reference to the first element.
|
||||||
@return A reference to the first element.
|
* @invariant The list may not be empty.
|
||||||
*/
|
* @return A reference to the first element.
|
||||||
|
*/
|
||||||
reference
|
reference
|
||||||
front() noexcept
|
front() noexcept
|
||||||
{
|
{
|
||||||
return element_from(head_.next_);
|
return element_from(head_.next_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a const reference to the first element.
|
/**
|
||||||
@invariant The list may not be empty.
|
* Obtain a const reference to the first element.
|
||||||
@return A const reference to the first element.
|
* @invariant The list may not be empty.
|
||||||
*/
|
* @return A const reference to the first element.
|
||||||
|
*/
|
||||||
[[nodiscard]] const_reference
|
[[nodiscard]] const_reference
|
||||||
front() const noexcept
|
front() const noexcept
|
||||||
{
|
{
|
||||||
return element_from(head_.next_);
|
return element_from(head_.next_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a reference to the last element.
|
/**
|
||||||
@invariant The list may not be empty.
|
* Obtain a reference to the last element.
|
||||||
@return A reference to the last element.
|
* @invariant The list may not be empty.
|
||||||
*/
|
* @return A reference to the last element.
|
||||||
|
*/
|
||||||
reference
|
reference
|
||||||
back() noexcept
|
back() noexcept
|
||||||
{
|
{
|
||||||
return element_from(tail_.prev_);
|
return element_from(tail_.prev_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a const reference to the last element.
|
/**
|
||||||
@invariant The list may not be empty.
|
* Obtain a const reference to the last element.
|
||||||
@return A const reference to the last element.
|
* @invariant The list may not be empty.
|
||||||
*/
|
* @return A const reference to the last element.
|
||||||
|
*/
|
||||||
[[nodiscard]] const_reference
|
[[nodiscard]] const_reference
|
||||||
back() const noexcept
|
back() const noexcept
|
||||||
{
|
{
|
||||||
return element_from(tail_.prev_);
|
return element_from(tail_.prev_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain an iterator to the beginning of the list.
|
/**
|
||||||
@return An iterator pointing to the beginning of the list.
|
* Obtain an iterator to the beginning of the list.
|
||||||
*/
|
* @return An iterator pointing to the beginning of the list.
|
||||||
|
*/
|
||||||
iterator
|
iterator
|
||||||
begin() noexcept
|
begin() noexcept
|
||||||
{
|
{
|
||||||
return iterator(head_.next_);
|
return iterator(head_.next_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a const iterator to the beginning of the list.
|
/**
|
||||||
@return A const iterator pointing to the beginning of the list.
|
* Obtain a const iterator to the beginning of the list.
|
||||||
*/
|
* @return A const iterator pointing to the beginning of the list.
|
||||||
|
*/
|
||||||
[[nodiscard]] const_iterator
|
[[nodiscard]] const_iterator
|
||||||
begin() const noexcept
|
begin() const noexcept
|
||||||
{
|
{
|
||||||
return const_iterator(head_.next_);
|
return const_iterator(head_.next_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a const iterator to the beginning of the list.
|
/**
|
||||||
@return A const iterator pointing to the beginning of the list.
|
* Obtain a const iterator to the beginning of the list.
|
||||||
*/
|
* @return A const iterator pointing to the beginning of the list.
|
||||||
|
*/
|
||||||
[[nodiscard]] const_iterator
|
[[nodiscard]] const_iterator
|
||||||
cbegin() const noexcept
|
cbegin() const noexcept
|
||||||
{
|
{
|
||||||
return const_iterator(head_.next_);
|
return const_iterator(head_.next_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a iterator to the end of the list.
|
/**
|
||||||
@return An iterator pointing to the end of the list.
|
* Obtain a iterator to the end of the list.
|
||||||
*/
|
* @return An iterator pointing to the end of the list.
|
||||||
|
*/
|
||||||
iterator
|
iterator
|
||||||
end() noexcept
|
end() noexcept
|
||||||
{
|
{
|
||||||
return iterator(&tail_);
|
return iterator(&tail_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a const iterator to the end of the list.
|
/**
|
||||||
@return A constiterator pointing to the end of the list.
|
* Obtain a const iterator to the end of the list.
|
||||||
*/
|
* @return A constiterator pointing to the end of the list.
|
||||||
|
*/
|
||||||
[[nodiscard]] const_iterator
|
[[nodiscard]] const_iterator
|
||||||
end() const noexcept
|
end() const noexcept
|
||||||
{
|
{
|
||||||
return const_iterator(&tail_);
|
return const_iterator(&tail_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a const iterator to the end of the list
|
/**
|
||||||
@return A constiterator pointing to the end of the list.
|
* Obtain a const iterator to the end of the list
|
||||||
*/
|
* @return A constiterator pointing to the end of the list.
|
||||||
|
*/
|
||||||
[[nodiscard]] const_iterator
|
[[nodiscard]] const_iterator
|
||||||
cend() const noexcept
|
cend() const noexcept
|
||||||
{
|
{
|
||||||
return const_iterator(&tail_);
|
return const_iterator(&tail_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clear the list.
|
/**
|
||||||
@note This does not free the elements.
|
* Clear the list.
|
||||||
*/
|
* @note This does not free the elements.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
clear() noexcept
|
clear() noexcept
|
||||||
{
|
{
|
||||||
@@ -407,12 +426,13 @@ public:
|
|||||||
size_ = 0;
|
size_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Insert an element.
|
/**
|
||||||
@invariant The element must not already be in the list.
|
* Insert an element.
|
||||||
@param pos The location to insert after.
|
* @invariant The element must not already be in the list.
|
||||||
@param element The element to insert.
|
* @param pos The location to insert after.
|
||||||
@return An iterator pointing to the newly inserted element.
|
* @param element The element to insert.
|
||||||
*/
|
* @return An iterator pointing to the newly inserted element.
|
||||||
|
*/
|
||||||
iterator
|
iterator
|
||||||
insert(iterator pos, T& element) noexcept
|
insert(iterator pos, T& element) noexcept
|
||||||
{
|
{
|
||||||
@@ -425,11 +445,12 @@ public:
|
|||||||
return iterator(node);
|
return iterator(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Insert another list into this one.
|
/**
|
||||||
The other list is cleared.
|
* Insert another list into this one.
|
||||||
@param pos The location to insert after.
|
* The other list is cleared.
|
||||||
@param other The list to insert.
|
* @param pos The location to insert after.
|
||||||
*/
|
* @param other The list to insert.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
insert(iterator pos, List& other) noexcept
|
insert(iterator pos, List& other) noexcept
|
||||||
{
|
{
|
||||||
@@ -445,11 +466,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove an element.
|
/**
|
||||||
@invariant The element must exist in the list.
|
* Remove an element.
|
||||||
@param pos An iterator pointing to the element to remove.
|
* @invariant The element must exist in the list.
|
||||||
@return An iterator pointing to the next element after the one removed.
|
* @param pos An iterator pointing to the element to remove.
|
||||||
*/
|
* @return An iterator pointing to the next element after the one removed.
|
||||||
|
*/
|
||||||
iterator
|
iterator
|
||||||
erase(iterator pos) noexcept
|
erase(iterator pos) noexcept
|
||||||
{
|
{
|
||||||
@@ -461,20 +483,22 @@ public:
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Insert an element at the beginning of the list.
|
/**
|
||||||
@invariant The element must not exist in the list.
|
* Insert an element at the beginning of the list.
|
||||||
@param element The element to insert.
|
* @invariant The element must not exist in the list.
|
||||||
*/
|
* @param element The element to insert.
|
||||||
|
*/
|
||||||
iterator
|
iterator
|
||||||
pushFront(T& element) noexcept
|
pushFront(T& element) noexcept
|
||||||
{
|
{
|
||||||
return insert(begin(), element);
|
return insert(begin(), element);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove the element at the beginning of the list.
|
/**
|
||||||
@invariant The list must not be empty.
|
* Remove the element at the beginning of the list.
|
||||||
@return A reference to the popped element.
|
* @invariant The list must not be empty.
|
||||||
*/
|
* @return A reference to the popped element.
|
||||||
|
*/
|
||||||
T&
|
T&
|
||||||
popFront() noexcept
|
popFront() noexcept
|
||||||
{
|
{
|
||||||
@@ -483,20 +507,22 @@ public:
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Append an element at the end of the list.
|
/**
|
||||||
@invariant The element must not exist in the list.
|
* Append an element at the end of the list.
|
||||||
@param element The element to append.
|
* @invariant The element must not exist in the list.
|
||||||
*/
|
* @param element The element to append.
|
||||||
|
*/
|
||||||
iterator
|
iterator
|
||||||
pushBack(T& element) noexcept
|
pushBack(T& element) noexcept
|
||||||
{
|
{
|
||||||
return insert(end(), element);
|
return insert(end(), element);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Remove the element at the end of the list.
|
/**
|
||||||
@invariant The list must not be empty.
|
* Remove the element at the end of the list.
|
||||||
@return A reference to the popped element.
|
* @invariant The list must not be empty.
|
||||||
*/
|
* @return A reference to the popped element.
|
||||||
|
*/
|
||||||
T&
|
T&
|
||||||
popBack() noexcept
|
popBack() noexcept
|
||||||
{
|
{
|
||||||
@@ -505,7 +531,9 @@ public:
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Swap contents with another list. */
|
/**
|
||||||
|
* Swap contents with another list.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
swap(List& other) noexcept
|
swap(List& other) noexcept
|
||||||
{
|
{
|
||||||
@@ -515,42 +543,46 @@ public:
|
|||||||
append(temp);
|
append(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Insert another list at the beginning of this list.
|
/**
|
||||||
The other list is cleared.
|
* Insert another list at the beginning of this list.
|
||||||
@param list The other list to insert.
|
* The other list is cleared.
|
||||||
*/
|
* @param list The other list to insert.
|
||||||
|
*/
|
||||||
iterator
|
iterator
|
||||||
prepend(List& list) noexcept
|
prepend(List& list) noexcept
|
||||||
{
|
{
|
||||||
return insert(begin(), list);
|
return insert(begin(), list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Append another list at the end of this list.
|
/**
|
||||||
The other list is cleared.
|
* Append another list at the end of this list.
|
||||||
@param list the other list to append.
|
* The other list is cleared.
|
||||||
*/
|
* @param list the other list to append.
|
||||||
|
*/
|
||||||
iterator
|
iterator
|
||||||
append(List& list) noexcept
|
append(List& list) noexcept
|
||||||
{
|
{
|
||||||
return insert(end(), list);
|
return insert(end(), list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain an iterator from an element.
|
/**
|
||||||
@invariant The element must exist in the list.
|
* Obtain an iterator from an element.
|
||||||
@param element The element to obtain an iterator for.
|
* @invariant The element must exist in the list.
|
||||||
@return An iterator to the element.
|
* @param element The element to obtain an iterator for.
|
||||||
*/
|
* @return An iterator to the element.
|
||||||
|
*/
|
||||||
iterator
|
iterator
|
||||||
iteratorTo(T& element) const noexcept
|
iteratorTo(T& element) const noexcept
|
||||||
{
|
{
|
||||||
return iterator(static_cast<Node*>(&element));
|
return iterator(static_cast<Node*>(&element));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Obtain a const iterator from an element.
|
/**
|
||||||
@invariant The element must exist in the list.
|
* Obtain a const iterator from an element.
|
||||||
@param element The element to obtain an iterator for.
|
* @invariant The element must exist in the list.
|
||||||
@return A const iterator to the element.
|
* @param element The element to obtain an iterator for.
|
||||||
*/
|
* @return A const iterator to the element.
|
||||||
|
*/
|
||||||
[[nodiscard]] const_iterator
|
[[nodiscard]] const_iterator
|
||||||
constIteratorTo(T const& element) const noexcept
|
constIteratorTo(T const& element) const noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -103,18 +103,19 @@ operator!=(
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Multiple Producer, Multiple Consumer (MPMC) intrusive stack.
|
/**
|
||||||
|
* Multiple Producer, Multiple Consumer (MPMC) intrusive stack.
|
||||||
This stack is implemented using the same intrusive interface as List.
|
*
|
||||||
All mutations are lock-free.
|
* This stack is implemented using the same intrusive interface as List.
|
||||||
|
* All mutations are lock-free.
|
||||||
The caller is responsible for preventing the "ABA" problem:
|
*
|
||||||
http://en.wikipedia.org/wiki/ABA_problem
|
* The caller is responsible for preventing the "ABA" problem:
|
||||||
|
* http://en.wikipedia.org/wiki/ABA_problem
|
||||||
@param Tag A type name used to distinguish lists and nodes, for
|
*
|
||||||
putting objects in multiple lists. If this parameter is
|
* @param Tag A type name used to distinguish lists and nodes, for
|
||||||
omitted, the default tag is used.
|
* putting objects in multiple lists. If this parameter is
|
||||||
*/
|
* omitted, the default tag is used.
|
||||||
|
*/
|
||||||
template <class Element, class Tag = void>
|
template <class Element, class Tag = void>
|
||||||
class LockFreeStack
|
class LockFreeStack
|
||||||
{
|
{
|
||||||
@@ -162,24 +163,27 @@ public:
|
|||||||
LockFreeStack&
|
LockFreeStack&
|
||||||
operator=(LockFreeStack const&) = delete;
|
operator=(LockFreeStack const&) = delete;
|
||||||
|
|
||||||
/** Returns true if the stack is empty. */
|
/**
|
||||||
|
* Returns true if the stack is empty.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
empty() const
|
empty() const
|
||||||
{
|
{
|
||||||
return head_.load() == &end_;
|
return head_.load() == &end_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Push a node onto the stack.
|
/**
|
||||||
The caller is responsible for preventing the ABA problem.
|
* Push a node onto the stack.
|
||||||
This operation is lock-free.
|
* The caller is responsible for preventing the ABA problem.
|
||||||
Thread safety:
|
* This operation is lock-free.
|
||||||
Safe to call from any thread.
|
* Thread safety:
|
||||||
|
* Safe to call from any thread.
|
||||||
@param node The node to push.
|
*
|
||||||
|
* @param node The node to push.
|
||||||
@return `true` if the stack was previously empty. If multiple threads
|
*
|
||||||
are attempting to push, only one will receive `true`.
|
* @return `true` if the stack was previously empty. If multiple threads
|
||||||
*/
|
* are attempting to push, only one will receive `true`.
|
||||||
|
*/
|
||||||
// VFALCO NOTE Fix this, shouldn't it be a reference like intrusive list?
|
// VFALCO NOTE Fix this, shouldn't it be a reference like intrusive list?
|
||||||
bool
|
bool
|
||||||
pushFront(Node* node)
|
pushFront(Node* node)
|
||||||
@@ -195,15 +199,16 @@ public:
|
|||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Pop an element off the stack.
|
/**
|
||||||
The caller is responsible for preventing the ABA problem.
|
* Pop an element off the stack.
|
||||||
This operation is lock-free.
|
* The caller is responsible for preventing the ABA problem.
|
||||||
Thread safety:
|
* This operation is lock-free.
|
||||||
Safe to call from any thread.
|
* Thread safety:
|
||||||
|
* Safe to call from any thread.
|
||||||
@return The element that was popped, or `nullptr` if the stack
|
*
|
||||||
was empty.
|
* @return The element that was popped, or `nullptr` if the stack
|
||||||
*/
|
* was empty.
|
||||||
|
*/
|
||||||
Element*
|
Element*
|
||||||
popFront()
|
popFront()
|
||||||
{
|
{
|
||||||
@@ -219,12 +224,13 @@ public:
|
|||||||
return static_cast<Element*>(node);
|
return static_cast<Element*>(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a forward iterator to the beginning or end of the stack.
|
/**
|
||||||
Undefined behavior results if push_front or pop_front is called
|
* Return a forward iterator to the beginning or end of the stack.
|
||||||
while an iteration is in progress.
|
* Undefined behavior results if push_front or pop_front is called
|
||||||
Thread safety:
|
* while an iteration is in progress.
|
||||||
Caller is responsible for synchronization.
|
* Thread safety:
|
||||||
*/
|
* Caller is responsible for synchronization.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
iterator
|
iterator
|
||||||
begin()
|
begin()
|
||||||
|
|||||||
@@ -6,13 +6,14 @@
|
|||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
/** A Semantic Version number.
|
/**
|
||||||
|
* A Semantic Version number.
|
||||||
Identifies the build of a particular version of software using
|
*
|
||||||
the Semantic Versioning Specification described here:
|
* Identifies the build of a particular version of software using
|
||||||
|
* the Semantic Versioning Specification described here:
|
||||||
http://semver.org/
|
*
|
||||||
*/
|
* http://semver.org/
|
||||||
|
*/
|
||||||
class SemanticVersion
|
class SemanticVersion
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -29,14 +30,17 @@ public:
|
|||||||
|
|
||||||
SemanticVersion(std::string_view version);
|
SemanticVersion(std::string_view version);
|
||||||
|
|
||||||
/** Parse a semantic version string.
|
/**
|
||||||
The parsing is as strict as possible.
|
* Parse a semantic version string.
|
||||||
@return `true` if the string was parsed.
|
* The parsing is as strict as possible.
|
||||||
*/
|
* @return `true` if the string was parsed.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
parse(std::string_view input);
|
parse(std::string_view input);
|
||||||
|
|
||||||
/** Produce a string from semantic version components. */
|
/**
|
||||||
|
* Produce a string from semantic version components.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::string
|
[[nodiscard]] std::string
|
||||||
print() const;
|
print() const;
|
||||||
|
|
||||||
@@ -52,9 +56,10 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Compare two SemanticVersions against each other.
|
/**
|
||||||
The comparison follows the rules as per the specification.
|
* Compare two SemanticVersions against each other.
|
||||||
*/
|
* The comparison follows the rules as per the specification.
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
compare(SemanticVersion const& lhs, SemanticVersion const& rhs);
|
compare(SemanticVersion const& lhs, SemanticVersion const& rhs);
|
||||||
|
|
||||||
|
|||||||
@@ -135,19 +135,20 @@ struct IsUniquelyRepresented<std::array<T, N>>
|
|||||||
explicit IsUniquelyRepresented() = default;
|
explicit IsUniquelyRepresented() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Metafunction returning `true` if the type can be hashed in one call.
|
/**
|
||||||
|
* Metafunction returning `true` if the type can be hashed in one call.
|
||||||
For `IsContiguouslyHashable<T>::value` to be true, then for every
|
*
|
||||||
combination of possible values of `T` held in `x` and `y`,
|
* For `IsContiguouslyHashable<T>::value` to be true, then for every
|
||||||
if `x == y`, then it must be true that `memcmp(&x, &y, sizeof(T))`
|
* combination of possible values of `T` held in `x` and `y`,
|
||||||
return 0; i.e. that `x` and `y` are represented by the same bit pattern.
|
* if `x == y`, then it must be true that `memcmp(&x, &y, sizeof(T))`
|
||||||
|
* return 0; i.e. that `x` and `y` are represented by the same bit pattern.
|
||||||
For example: A two's complement `int` should be contiguously hashable.
|
*
|
||||||
Every bit pattern produces a unique value that does not compare equal to
|
* For example: A two's complement `int` should be contiguously hashable.
|
||||||
any other bit pattern's value. A IEEE floating point should not be
|
* Every bit pattern produces a unique value that does not compare equal to
|
||||||
contiguously hashable because -0. and 0. have different bit patterns,
|
* any other bit pattern's value. A IEEE floating point should not be
|
||||||
though they compare equal.
|
* contiguously hashable because -0. and 0. have different bit patterns,
|
||||||
*/
|
* though they compare equal.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
template <class T, class HashAlgorithm>
|
template <class T, class HashAlgorithm>
|
||||||
struct IsContiguouslyHashable
|
struct IsContiguouslyHashable
|
||||||
@@ -172,29 +173,30 @@ struct IsContiguouslyHashable<T[N], HashAlgorithm>
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Logically concatenate input data to a `Hasher`.
|
/**
|
||||||
|
* Logically concatenate input data to a `Hasher`.
|
||||||
Hasher requirements:
|
*
|
||||||
|
* Hasher requirements:
|
||||||
`X` is the type `Hasher`
|
*
|
||||||
`h` is a value of type `x`
|
* `X` is the type `Hasher`
|
||||||
`p` is a value convertible to `void const*`
|
* `h` is a value of type `x`
|
||||||
`n` is a value of type `std::size_t`, greater than zero
|
* `p` is a value convertible to `void const*`
|
||||||
|
* `n` is a value of type `std::size_t`, greater than zero
|
||||||
Expression:
|
*
|
||||||
`h.append (p, n);`
|
* Expression:
|
||||||
Throws:
|
* `h.append (p, n);`
|
||||||
Never
|
* Throws:
|
||||||
Effect:
|
* Never
|
||||||
Adds the input data to the hasher state.
|
* Effect:
|
||||||
|
* Adds the input data to the hasher state.
|
||||||
Expression:
|
*
|
||||||
`static_cast<std::size_t>(j)`
|
* Expression:
|
||||||
Throws:
|
* `static_cast<std::size_t>(j)`
|
||||||
Never
|
* Throws:
|
||||||
Effect:
|
* Never
|
||||||
Returns the resulting hash of all the input data.
|
* Effect:
|
||||||
*/
|
* Returns the resulting hash of all the input data.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
|
|
||||||
// scalars
|
// scalars
|
||||||
|
|||||||
@@ -12,16 +12,17 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** Interface for a manager that allows collection of metrics.
|
/**
|
||||||
|
* Interface for a manager that allows collection of metrics.
|
||||||
To export metrics from a class, pass and save a shared_ptr to this
|
*
|
||||||
interface in the class constructor. Create the metric objects
|
* To export metrics from a class, pass and save a shared_ptr to this
|
||||||
as desired (counters, events, gauges, meters, and an optional hook)
|
* interface in the class constructor. Create the metric objects
|
||||||
using the interface.
|
* as desired (counters, events, gauges, meters, and an optional hook)
|
||||||
|
* using the interface.
|
||||||
@see Counter, Event, Gauge, Hook, Meter
|
*
|
||||||
@see NullCollector, StatsDCollector
|
* @see Counter, Event, Gauge, Hook, Meter
|
||||||
*/
|
* @see NullCollector, StatsDCollector
|
||||||
|
*/
|
||||||
class Collector
|
class Collector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -29,18 +30,19 @@ public:
|
|||||||
|
|
||||||
virtual ~Collector() = 0;
|
virtual ~Collector() = 0;
|
||||||
|
|
||||||
/** Create a hook.
|
/**
|
||||||
|
* Create a hook.
|
||||||
A hook is called at each collection interval, on an implementation
|
*
|
||||||
defined thread. This is a convenience facility for gathering metrics
|
* A hook is called at each collection interval, on an implementation
|
||||||
in the polling style. The typical usage is to update all the metrics
|
* defined thread. This is a convenience facility for gathering metrics
|
||||||
of interest in the handler.
|
* in the polling style. The typical usage is to update all the metrics
|
||||||
|
* of interest in the handler.
|
||||||
Handler will be called with this signature:
|
*
|
||||||
void handler (void)
|
* Handler will be called with this signature:
|
||||||
|
* void handler (void)
|
||||||
@see Hook
|
*
|
||||||
*/
|
* @see Hook
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
template <class Handler>
|
template <class Handler>
|
||||||
Hook
|
Hook
|
||||||
@@ -53,9 +55,10 @@ public:
|
|||||||
makeHook(HookImpl::HandlerType const& handler) = 0;
|
makeHook(HookImpl::HandlerType const& handler) = 0;
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Create a counter with the specified name.
|
/**
|
||||||
@see Counter
|
* Create a counter with the specified name.
|
||||||
*/
|
* @see Counter
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
virtual Counter
|
virtual Counter
|
||||||
makeCounter(std::string const& name) = 0;
|
makeCounter(std::string const& name) = 0;
|
||||||
@@ -69,9 +72,10 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Create an event with the specified name.
|
/**
|
||||||
@see Event
|
* Create an event with the specified name.
|
||||||
*/
|
* @see Event
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
virtual Event
|
virtual Event
|
||||||
makeEvent(std::string const& name) = 0;
|
makeEvent(std::string const& name) = 0;
|
||||||
@@ -85,9 +89,10 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Create a gauge with the specified name.
|
/**
|
||||||
@see Gauge
|
* Create a gauge with the specified name.
|
||||||
*/
|
* @see Gauge
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
virtual Gauge
|
virtual Gauge
|
||||||
makeGauge(std::string const& name) = 0;
|
makeGauge(std::string const& name) = 0;
|
||||||
@@ -101,9 +106,10 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Create a meter with the specified name.
|
/**
|
||||||
@see Meter
|
* Create a meter with the specified name.
|
||||||
*/
|
* @see Meter
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
virtual Meter
|
virtual Meter
|
||||||
makeMeter(std::string const& name) = 0;
|
makeMeter(std::string const& name) = 0;
|
||||||
|
|||||||
@@ -7,34 +7,39 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** A metric for measuring an integral value.
|
/**
|
||||||
|
* A metric for measuring an integral value.
|
||||||
A counter is a gauge calculated at the server. The owner of the counter
|
*
|
||||||
may increment and decrement the value by an amount.
|
* A counter is a gauge calculated at the server. The owner of the counter
|
||||||
|
* may increment and decrement the value by an amount.
|
||||||
This is a lightweight reference wrapper which is cheap to copy and assign.
|
*
|
||||||
When the last reference goes away, the metric is no longer collected.
|
* This is a lightweight reference wrapper which is cheap to copy and assign.
|
||||||
*/
|
* When the last reference goes away, the metric is no longer collected.
|
||||||
|
*/
|
||||||
class Counter final
|
class Counter final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = CounterImpl::value_type;
|
using value_type = CounterImpl::value_type;
|
||||||
|
|
||||||
/** Create a null metric.
|
/**
|
||||||
A null metric reports no information.
|
* Create a null metric.
|
||||||
*/
|
* A null metric reports no information.
|
||||||
|
*/
|
||||||
Counter() = default;
|
Counter() = default;
|
||||||
|
|
||||||
/** Create the metric reference the specified implementation.
|
/**
|
||||||
Normally this won't be called directly. Instead, call the appropriate
|
* Create the metric reference the specified implementation.
|
||||||
factory function in the Collector interface.
|
* Normally this won't be called directly. Instead, call the appropriate
|
||||||
@see Collector.
|
* factory function in the Collector interface.
|
||||||
*/
|
* @see Collector.
|
||||||
|
*/
|
||||||
explicit Counter(std::shared_ptr<CounterImpl> impl) : impl_(std::move(impl))
|
explicit Counter(std::shared_ptr<CounterImpl> impl) : impl_(std::move(impl))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Increment the counter. */
|
/**
|
||||||
|
* Increment the counter.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
void
|
void
|
||||||
increment(value_type amount) const
|
increment(value_type amount) const
|
||||||
|
|||||||
@@ -8,35 +8,40 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** A metric for reporting event timing.
|
/**
|
||||||
|
* A metric for reporting event timing.
|
||||||
An event is an operation that has an associated millisecond time, or
|
*
|
||||||
other integral value. Because events happen at a specific moment, the
|
* An event is an operation that has an associated millisecond time, or
|
||||||
metric only supports a push-style interface.
|
* other integral value. Because events happen at a specific moment, the
|
||||||
|
* metric only supports a push-style interface.
|
||||||
This is a lightweight reference wrapper which is cheap to copy and assign.
|
*
|
||||||
When the last reference goes away, the metric is no longer collected.
|
* This is a lightweight reference wrapper which is cheap to copy and assign.
|
||||||
*/
|
* When the last reference goes away, the metric is no longer collected.
|
||||||
|
*/
|
||||||
class Event final
|
class Event final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = EventImpl::value_type;
|
using value_type = EventImpl::value_type;
|
||||||
|
|
||||||
/** Create a null metric.
|
/**
|
||||||
A null metric reports no information.
|
* Create a null metric.
|
||||||
*/
|
* A null metric reports no information.
|
||||||
|
*/
|
||||||
Event() = default;
|
Event() = default;
|
||||||
|
|
||||||
/** Create the metric reference the specified implementation.
|
/**
|
||||||
Normally this won't be called directly. Instead, call the appropriate
|
* Create the metric reference the specified implementation.
|
||||||
factory function in the Collector interface.
|
* Normally this won't be called directly. Instead, call the appropriate
|
||||||
@see Collector.
|
* factory function in the Collector interface.
|
||||||
*/
|
* @see Collector.
|
||||||
|
*/
|
||||||
explicit Event(std::shared_ptr<EventImpl> impl) : impl_(std::move(impl))
|
explicit Event(std::shared_ptr<EventImpl> impl) : impl_(std::move(impl))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Push an event notification. */
|
/**
|
||||||
|
* Push an event notification.
|
||||||
|
*/
|
||||||
template <class Rep, class Period>
|
template <class Rep, class Period>
|
||||||
void
|
void
|
||||||
notify(std::chrono::duration<Rep, Period> const& value) const
|
notify(std::chrono::duration<Rep, Period> const& value) const
|
||||||
|
|||||||
@@ -7,40 +7,44 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** A metric for measuring an integral value.
|
/**
|
||||||
|
* A metric for measuring an integral value.
|
||||||
A gauge is an instantaneous measurement of a value, like the gas gauge
|
*
|
||||||
in a car. The caller directly sets the value, or adjusts it by a
|
* A gauge is an instantaneous measurement of a value, like the gas gauge
|
||||||
specified amount. The value is kept in the client rather than the collector.
|
* in a car. The caller directly sets the value, or adjusts it by a
|
||||||
|
* specified amount. The value is kept in the client rather than the collector.
|
||||||
This is a lightweight reference wrapper which is cheap to copy and assign.
|
*
|
||||||
When the last reference goes away, the metric is no longer collected.
|
* This is a lightweight reference wrapper which is cheap to copy and assign.
|
||||||
*/
|
* When the last reference goes away, the metric is no longer collected.
|
||||||
|
*/
|
||||||
class Gauge final
|
class Gauge final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = GaugeImpl::value_type;
|
using value_type = GaugeImpl::value_type;
|
||||||
using difference_type = GaugeImpl::difference_type;
|
using difference_type = GaugeImpl::difference_type;
|
||||||
|
|
||||||
/** Create a null metric.
|
/**
|
||||||
A null metric reports no information.
|
* Create a null metric.
|
||||||
*/
|
* A null metric reports no information.
|
||||||
|
*/
|
||||||
Gauge() = default;
|
Gauge() = default;
|
||||||
|
|
||||||
/** Create the metric reference the specified implementation.
|
/**
|
||||||
Normally this won't be called directly. Instead, call the appropriate
|
* Create the metric reference the specified implementation.
|
||||||
factory function in the Collector interface.
|
* Normally this won't be called directly. Instead, call the appropriate
|
||||||
@see Collector.
|
* factory function in the Collector interface.
|
||||||
*/
|
* @see Collector.
|
||||||
|
*/
|
||||||
explicit Gauge(std::shared_ptr<GaugeImpl> impl) : impl_(std::move(impl))
|
explicit Gauge(std::shared_ptr<GaugeImpl> impl) : impl_(std::move(impl))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the value on the gauge.
|
/**
|
||||||
A Collector implementation should combine multiple calls to value
|
* Set the value on the gauge.
|
||||||
changes into a single change if the calls occur within a single
|
* A Collector implementation should combine multiple calls to value
|
||||||
collection interval.
|
* changes into a single change if the calls occur within a single
|
||||||
*/
|
* collection interval.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
void
|
void
|
||||||
set(value_type value) const
|
set(value_type value) const
|
||||||
@@ -62,7 +66,9 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Adjust the value of the gauge. */
|
/**
|
||||||
|
* Adjust the value of the gauge.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
void
|
void
|
||||||
increment(difference_type amount) const
|
increment(difference_type amount) const
|
||||||
|
|||||||
@@ -7,13 +7,17 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** A collector front-end that manages a group of metrics. */
|
/**
|
||||||
|
* A collector front-end that manages a group of metrics.
|
||||||
|
*/
|
||||||
class Group : public Collector
|
class Group : public Collector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ptr = std::shared_ptr<Group>;
|
using ptr = std::shared_ptr<Group>;
|
||||||
|
|
||||||
/** Returns the name of this group, for diagnostics. */
|
/**
|
||||||
|
* Returns the name of this group, for diagnostics.
|
||||||
|
*/
|
||||||
[[nodiscard]] virtual std::string const&
|
[[nodiscard]] virtual std::string const&
|
||||||
name() const = 0;
|
name() const = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,13 +8,17 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** A container for managing a set of metric groups. */
|
/**
|
||||||
|
* A container for managing a set of metric groups.
|
||||||
|
*/
|
||||||
class Groups
|
class Groups
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~Groups() = 0;
|
virtual ~Groups() = 0;
|
||||||
|
|
||||||
/** Find or create a new collector with a given name. */
|
/**
|
||||||
|
* Find or create a new collector with a given name.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
virtual Group::ptr const&
|
virtual Group::ptr const&
|
||||||
get(std::string const& name) = 0;
|
get(std::string const& name) = 0;
|
||||||
@@ -27,7 +31,9 @@ public:
|
|||||||
/** @} */
|
/** @} */
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Create a group container that uses the specified collector. */
|
/**
|
||||||
|
* Create a group container that uses the specified collector.
|
||||||
|
*/
|
||||||
std::unique_ptr<Groups>
|
std::unique_ptr<Groups>
|
||||||
makeGroups(Collector::ptr const& collector);
|
makeGroups(Collector::ptr const& collector);
|
||||||
|
|
||||||
|
|||||||
@@ -7,20 +7,24 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** A reference to a handler for performing polled collection. */
|
/**
|
||||||
|
* A reference to a handler for performing polled collection.
|
||||||
|
*/
|
||||||
class Hook final
|
class Hook final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Create a null hook.
|
/**
|
||||||
A null hook has no associated handler.
|
* Create a null hook.
|
||||||
*/
|
* A null hook has no associated handler.
|
||||||
|
*/
|
||||||
Hook() = default;
|
Hook() = default;
|
||||||
|
|
||||||
/** Create a hook referencing the specified implementation.
|
/**
|
||||||
Normally this won't be called directly. Instead, call the appropriate
|
* Create a hook referencing the specified implementation.
|
||||||
factory function in the Collector interface.
|
* Normally this won't be called directly. Instead, call the appropriate
|
||||||
@see Collector.
|
* factory function in the Collector interface.
|
||||||
*/
|
* @see Collector.
|
||||||
|
*/
|
||||||
explicit Hook(std::shared_ptr<HookImpl> impl) : impl_(std::move(impl))
|
explicit Hook(std::shared_ptr<HookImpl> impl) : impl_(std::move(impl))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,33 +7,38 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** A metric for measuring an integral value.
|
/**
|
||||||
|
* A metric for measuring an integral value.
|
||||||
A meter may be thought of as an increment-only counter.
|
*
|
||||||
|
* A meter may be thought of as an increment-only counter.
|
||||||
This is a lightweight reference wrapper which is cheap to copy and assign.
|
*
|
||||||
When the last reference goes away, the metric is no longer collected.
|
* This is a lightweight reference wrapper which is cheap to copy and assign.
|
||||||
*/
|
* When the last reference goes away, the metric is no longer collected.
|
||||||
|
*/
|
||||||
class Meter final
|
class Meter final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = MeterImpl::value_type;
|
using value_type = MeterImpl::value_type;
|
||||||
|
|
||||||
/** Create a null metric.
|
/**
|
||||||
A null metric reports no information.
|
* Create a null metric.
|
||||||
*/
|
* A null metric reports no information.
|
||||||
|
*/
|
||||||
Meter() = default;
|
Meter() = default;
|
||||||
|
|
||||||
/** Create the metric reference the specified implementation.
|
/**
|
||||||
Normally this won't be called directly. Instead, call the appropriate
|
* Create the metric reference the specified implementation.
|
||||||
factory function in the Collector interface.
|
* Normally this won't be called directly. Instead, call the appropriate
|
||||||
@see Collector.
|
* factory function in the Collector interface.
|
||||||
*/
|
* @see Collector.
|
||||||
|
*/
|
||||||
explicit Meter(std::shared_ptr<MeterImpl> impl) : impl_(std::move(impl))
|
explicit Meter(std::shared_ptr<MeterImpl> impl) : impl_(std::move(impl))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Increment the meter. */
|
/**
|
||||||
|
* Increment the meter.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
void
|
void
|
||||||
increment(value_type amount) const
|
increment(value_type amount) const
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** A Collector which does not collect metrics. */
|
/**
|
||||||
|
* A Collector which does not collect metrics.
|
||||||
|
*/
|
||||||
class NullCollector : public Collector
|
class NullCollector : public Collector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -9,20 +9,22 @@
|
|||||||
|
|
||||||
namespace beast::insight {
|
namespace beast::insight {
|
||||||
|
|
||||||
/** A Collector that reports metrics to a StatsD server.
|
/**
|
||||||
Reference:
|
* A Collector that reports metrics to a StatsD server.
|
||||||
https://github.com/b/statsd_spec
|
* Reference:
|
||||||
*/
|
* https://github.com/b/statsd_spec
|
||||||
|
*/
|
||||||
class StatsDCollector : public Collector
|
class StatsDCollector : public Collector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit StatsDCollector() = default;
|
explicit StatsDCollector() = default;
|
||||||
|
|
||||||
/** Create a StatsD collector.
|
/**
|
||||||
@param address The IP address and port of the StatsD server.
|
* Create a StatsD collector.
|
||||||
@param prefix A string pre-pended before each metric name.
|
* @param address The IP address and port of the StatsD server.
|
||||||
@param journal Destination for logging output.
|
* @param prefix A string pre-pended before each metric name.
|
||||||
*/
|
* @param journal Destination for logging output.
|
||||||
|
*/
|
||||||
static std::shared_ptr<StatsDCollector>
|
static std::shared_ptr<StatsDCollector>
|
||||||
make(IP::Endpoint const& address, std::string const& prefix, Journal journal);
|
make(IP::Endpoint const& address, std::string const& prefix, Journal journal);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,42 +19,54 @@ namespace IP {
|
|||||||
|
|
||||||
using Address = boost::asio::ip::address;
|
using Address = boost::asio::ip::address;
|
||||||
|
|
||||||
/** Returns the address represented as a string. */
|
/**
|
||||||
|
* Returns the address represented as a string.
|
||||||
|
*/
|
||||||
inline std::string
|
inline std::string
|
||||||
to_string(Address const& addr)
|
to_string(Address const& addr)
|
||||||
{
|
{
|
||||||
return addr.to_string();
|
return addr.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if this is a loopback address. */
|
/**
|
||||||
|
* Returns `true` if this is a loopback address.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isLoopback(Address const& addr)
|
isLoopback(Address const& addr)
|
||||||
{
|
{
|
||||||
return addr.is_loopback();
|
return addr.is_loopback();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if the address is unspecified. */
|
/**
|
||||||
|
* Returns `true` if the address is unspecified.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isUnspecified(Address const& addr)
|
isUnspecified(Address const& addr)
|
||||||
{
|
{
|
||||||
return addr.is_unspecified();
|
return addr.is_unspecified();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if the address is a multicast address. */
|
/**
|
||||||
|
* Returns `true` if the address is a multicast address.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isMulticast(Address const& addr)
|
isMulticast(Address const& addr)
|
||||||
{
|
{
|
||||||
return addr.is_multicast();
|
return addr.is_multicast();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if the address is a private unroutable address. */
|
/**
|
||||||
|
* Returns `true` if the address is a private unroutable address.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isPrivate(Address const& addr)
|
isPrivate(Address const& addr)
|
||||||
{
|
{
|
||||||
return (addr.is_v4()) ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6());
|
return (addr.is_v4()) ? isPrivate(addr.to_v4()) : isPrivate(addr.to_v6());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if the address is a public routable address. */
|
/**
|
||||||
|
* Returns `true` if the address is a public routable address.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isPublic(Address const& addr)
|
isPublic(Address const& addr)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,23 +6,29 @@
|
|||||||
|
|
||||||
namespace beast::IP {
|
namespace beast::IP {
|
||||||
|
|
||||||
/** Convert to Endpoint.
|
/**
|
||||||
The port is set to zero.
|
* Convert to Endpoint.
|
||||||
*/
|
* The port is set to zero.
|
||||||
|
*/
|
||||||
Endpoint
|
Endpoint
|
||||||
fromAsio(boost::asio::ip::address const& address);
|
fromAsio(boost::asio::ip::address const& address);
|
||||||
|
|
||||||
/** Convert to Endpoint. */
|
/**
|
||||||
|
* Convert to Endpoint.
|
||||||
|
*/
|
||||||
Endpoint
|
Endpoint
|
||||||
fromAsio(boost::asio::ip::tcp::endpoint const& endpoint);
|
fromAsio(boost::asio::ip::tcp::endpoint const& endpoint);
|
||||||
|
|
||||||
/** Convert to asio::ip::address.
|
/**
|
||||||
The port is ignored.
|
* Convert to asio::ip::address.
|
||||||
*/
|
* The port is ignored.
|
||||||
|
*/
|
||||||
boost::asio::ip::address
|
boost::asio::ip::address
|
||||||
toAsioAddress(Endpoint const& endpoint);
|
toAsioAddress(Endpoint const& endpoint);
|
||||||
|
|
||||||
/** Convert to asio::ip::tcp::endpoint. */
|
/**
|
||||||
|
* Convert to asio::ip::tcp::endpoint.
|
||||||
|
*/
|
||||||
boost::asio::ip::tcp::endpoint
|
boost::asio::ip::tcp::endpoint
|
||||||
toAsioEndpoint(Endpoint const& endpoint);
|
toAsioEndpoint(Endpoint const& endpoint);
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,22 @@ namespace beast::IP {
|
|||||||
|
|
||||||
using AddressV4 = boost::asio::ip::address_v4;
|
using AddressV4 = boost::asio::ip::address_v4;
|
||||||
|
|
||||||
/** Returns `true` if the address is a private unroutable address. */
|
/**
|
||||||
|
* Returns `true` if the address is a private unroutable address.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
isPrivate(AddressV4 const& addr);
|
isPrivate(AddressV4 const& addr);
|
||||||
|
|
||||||
/** Returns `true` if the address is a public routable address. */
|
/**
|
||||||
|
* Returns `true` if the address is a public routable address.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
isPublic(AddressV4 const& addr);
|
isPublic(AddressV4 const& addr);
|
||||||
|
|
||||||
/** Returns the address class for the given address.
|
/**
|
||||||
@note Class 'D' represents multicast addresses (224.*.*.*).
|
* Returns the address class for the given address.
|
||||||
*/
|
* @note Class 'D' represents multicast addresses (224.*.*.*).
|
||||||
|
*/
|
||||||
char
|
char
|
||||||
getClass(AddressV4 const& address);
|
getClass(AddressV4 const& address);
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,15 @@ namespace beast::IP {
|
|||||||
|
|
||||||
using AddressV6 = boost::asio::ip::address_v6;
|
using AddressV6 = boost::asio::ip::address_v6;
|
||||||
|
|
||||||
/** Returns `true` if the address is a private unroutable address. */
|
/**
|
||||||
|
* Returns `true` if the address is a private unroutable address.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
isPrivate(AddressV6 const& addr);
|
isPrivate(AddressV6 const& addr);
|
||||||
|
|
||||||
/** Returns `true` if the address is a public routable address. */
|
/**
|
||||||
|
* Returns `true` if the address is a public routable address.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
isPublic(AddressV6 const& addr);
|
isPublic(AddressV6 const& addr);
|
||||||
|
|
||||||
|
|||||||
@@ -17,51 +17,68 @@ namespace beast::IP {
|
|||||||
|
|
||||||
using Port = std::uint16_t;
|
using Port = std::uint16_t;
|
||||||
|
|
||||||
/** A version-independent IP address and port combination. */
|
/**
|
||||||
|
* A version-independent IP address and port combination.
|
||||||
|
*/
|
||||||
class Endpoint
|
class Endpoint
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Create an unspecified endpoint. */
|
/**
|
||||||
|
* Create an unspecified endpoint.
|
||||||
|
*/
|
||||||
Endpoint();
|
Endpoint();
|
||||||
|
|
||||||
/** Create an endpoint from the address and optional port. */
|
/**
|
||||||
|
* Create an endpoint from the address and optional port.
|
||||||
|
*/
|
||||||
explicit Endpoint(Address addr, Port port = 0);
|
explicit Endpoint(Address addr, Port port = 0);
|
||||||
|
|
||||||
/** Create an Endpoint from a string.
|
/**
|
||||||
If the port is omitted, the endpoint will have a zero port.
|
* Create an Endpoint from a string.
|
||||||
@return An optional endpoint; will be `std::nullopt` on failure
|
* If the port is omitted, the endpoint will have a zero port.
|
||||||
*/
|
* @return An optional endpoint; will be `std::nullopt` on failure
|
||||||
|
*/
|
||||||
static std::optional<Endpoint>
|
static std::optional<Endpoint>
|
||||||
fromStringChecked(std::string const& s);
|
fromStringChecked(std::string const& s);
|
||||||
static Endpoint
|
static Endpoint
|
||||||
fromString(std::string const& s);
|
fromString(std::string const& s);
|
||||||
|
|
||||||
/** Returns a string representing the endpoint. */
|
/**
|
||||||
|
* Returns a string representing the endpoint.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::string
|
[[nodiscard]] std::string
|
||||||
toString() const;
|
toString() const;
|
||||||
|
|
||||||
/** Returns the port number on the endpoint. */
|
/**
|
||||||
|
* Returns the port number on the endpoint.
|
||||||
|
*/
|
||||||
[[nodiscard]] Port
|
[[nodiscard]] Port
|
||||||
port() const
|
port() const
|
||||||
{
|
{
|
||||||
return port_;
|
return port_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a new Endpoint with a different port. */
|
/**
|
||||||
|
* Returns a new Endpoint with a different port.
|
||||||
|
*/
|
||||||
[[nodiscard]] Endpoint
|
[[nodiscard]] Endpoint
|
||||||
atPort(Port port) const
|
atPort(Port port) const
|
||||||
{
|
{
|
||||||
return Endpoint(addr_, port);
|
return Endpoint(addr_, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the address portion of this endpoint. */
|
/**
|
||||||
|
* Returns the address portion of this endpoint.
|
||||||
|
*/
|
||||||
[[nodiscard]] Address const&
|
[[nodiscard]] Address const&
|
||||||
address() const
|
address() const
|
||||||
{
|
{
|
||||||
return addr_;
|
return addr_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convenience accessors for the address part. */
|
/**
|
||||||
|
* Convenience accessors for the address part.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
isV4() const
|
isV4() const
|
||||||
@@ -85,7 +102,9 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** Arithmetic comparison. */
|
/**
|
||||||
|
* Arithmetic comparison.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
friend bool
|
friend bool
|
||||||
operator==(Endpoint const& lhs, Endpoint const& rhs);
|
operator==(Endpoint const& lhs, Endpoint const& rhs);
|
||||||
@@ -131,35 +150,45 @@ private:
|
|||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
/** Returns `true` if the endpoint is a loopback address. */
|
/**
|
||||||
|
* Returns `true` if the endpoint is a loopback address.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isLoopback(Endpoint const& endpoint)
|
isLoopback(Endpoint const& endpoint)
|
||||||
{
|
{
|
||||||
return isLoopback(endpoint.address());
|
return isLoopback(endpoint.address());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if the endpoint is unspecified. */
|
/**
|
||||||
|
* Returns `true` if the endpoint is unspecified.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isUnspecified(Endpoint const& endpoint)
|
isUnspecified(Endpoint const& endpoint)
|
||||||
{
|
{
|
||||||
return isUnspecified(endpoint.address());
|
return isUnspecified(endpoint.address());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if the endpoint is a multicast address. */
|
/**
|
||||||
|
* Returns `true` if the endpoint is a multicast address.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isMulticast(Endpoint const& endpoint)
|
isMulticast(Endpoint const& endpoint)
|
||||||
{
|
{
|
||||||
return isMulticast(endpoint.address());
|
return isMulticast(endpoint.address());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if the endpoint is a private unroutable address. */
|
/**
|
||||||
|
* Returns `true` if the endpoint is a private unroutable address.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isPrivate(Endpoint const& endpoint)
|
isPrivate(Endpoint const& endpoint)
|
||||||
{
|
{
|
||||||
return isPrivate(endpoint.address());
|
return isPrivate(endpoint.address());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if the endpoint is a public routable address. */
|
/**
|
||||||
|
* Returns `true` if the endpoint is a public routable address.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isPublic(Endpoint const& endpoint)
|
isPublic(Endpoint const& endpoint)
|
||||||
{
|
{
|
||||||
@@ -168,14 +197,18 @@ isPublic(Endpoint const& endpoint)
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Returns the endpoint represented as a string. */
|
/**
|
||||||
|
* Returns the endpoint represented as a string.
|
||||||
|
*/
|
||||||
inline std::string
|
inline std::string
|
||||||
to_string(Endpoint const& endpoint)
|
to_string(Endpoint const& endpoint)
|
||||||
{
|
{
|
||||||
return endpoint.toString();
|
return endpoint.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Output stream conversion. */
|
/**
|
||||||
|
* Output stream conversion.
|
||||||
|
*/
|
||||||
template <typename OutputStream>
|
template <typename OutputStream>
|
||||||
OutputStream&
|
OutputStream&
|
||||||
operator<<(OutputStream& os, Endpoint const& endpoint)
|
operator<<(OutputStream& os, Endpoint const& endpoint)
|
||||||
@@ -184,7 +217,9 @@ operator<<(OutputStream& os, Endpoint const& endpoint)
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Input stream conversion. */
|
/**
|
||||||
|
* Input stream conversion.
|
||||||
|
*/
|
||||||
std::istream&
|
std::istream&
|
||||||
operator>>(std::istream& is, Endpoint& endpoint);
|
operator>>(std::istream& is, Endpoint& endpoint);
|
||||||
|
|
||||||
@@ -193,7 +228,9 @@ operator>>(std::istream& is, Endpoint& endpoint);
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
/** std::hash support. */
|
/**
|
||||||
|
* std::hash support.
|
||||||
|
*/
|
||||||
template <>
|
template <>
|
||||||
struct hash<::beast::IP::Endpoint>
|
struct hash<::beast::IP::Endpoint>
|
||||||
{
|
{
|
||||||
@@ -208,7 +245,9 @@ struct hash<::beast::IP::Endpoint>
|
|||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
namespace boost {
|
namespace boost {
|
||||||
/** boost::hash support. */
|
/**
|
||||||
|
* boost::hash support.
|
||||||
|
*/
|
||||||
template <>
|
template <>
|
||||||
struct hash<::beast::IP::Endpoint>
|
struct hash<::beast::IP::Endpoint>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,17 +30,20 @@ struct CiEqualPred
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Returns `true` if `c` is linear white space.
|
/**
|
||||||
|
* Returns `true` if `c` is linear white space.
|
||||||
This excludes the CRLF sequence allowed for line continuations.
|
*
|
||||||
*/
|
* This excludes the CRLF sequence allowed for line continuations.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isLws(char c)
|
isLws(char c)
|
||||||
{
|
{
|
||||||
return c == ' ' || c == '\t';
|
return c == ' ' || c == '\t';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if `c` is any whitespace character. */
|
/**
|
||||||
|
* Returns `true` if `c` is any whitespace character.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
isWhite(char c)
|
isWhite(char c)
|
||||||
{
|
{
|
||||||
@@ -87,14 +90,15 @@ trimRight(String const& s)
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/** Parse a character sequence of values separated by commas.
|
/**
|
||||||
Double quotes and escape sequences will be converted. Excess white
|
* Parse a character sequence of values separated by commas.
|
||||||
space, commas, double quotes, and empty elements are not copied.
|
* Double quotes and escape sequences will be converted. Excess white
|
||||||
Format:
|
* space, commas, double quotes, and empty elements are not copied.
|
||||||
#(token|quoted-string)
|
* Format:
|
||||||
Reference:
|
* #(token|quoted-string)
|
||||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2
|
* Reference:
|
||||||
*/
|
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2
|
||||||
|
*/
|
||||||
template <
|
template <
|
||||||
class FwdIt,
|
class FwdIt,
|
||||||
class Result = std::vector<std::basic_string<typename std::iterator_traits<FwdIt>::value_type>>,
|
class Result = std::vector<std::basic_string<typename std::iterator_traits<FwdIt>::value_type>>,
|
||||||
@@ -189,14 +193,15 @@ splitCommas(boost::beast::string_view const& s)
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Iterates through a comma separated list.
|
/**
|
||||||
|
* Iterates through a comma separated list.
|
||||||
Meets the requirements of ForwardIterator.
|
*
|
||||||
|
* Meets the requirements of ForwardIterator.
|
||||||
List defined in rfc2616 2.1.
|
*
|
||||||
|
* List defined in rfc2616 2.1.
|
||||||
@note Values returned may contain backslash escapes.
|
*
|
||||||
*/
|
* @note Values returned may contain backslash escapes.
|
||||||
|
*/
|
||||||
class ListIterator
|
class ListIterator
|
||||||
{
|
{
|
||||||
using iter_type = boost::string_ref::const_iterator;
|
using iter_type = boost::string_ref::const_iterator;
|
||||||
@@ -323,17 +328,20 @@ ListIterator::increment()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** Returns true if two strings are equal.
|
/**
|
||||||
|
* Returns true if two strings are equal.
|
||||||
A case-insensitive comparison is used.
|
*
|
||||||
*/
|
* A case-insensitive comparison is used.
|
||||||
|
*/
|
||||||
inline bool
|
inline bool
|
||||||
ciEqual(boost::string_ref s1, boost::string_ref s2)
|
ciEqual(boost::string_ref s1, boost::string_ref s2)
|
||||||
{
|
{
|
||||||
return boost::range::equal(s1, s2, detail::CiEqualPred{});
|
return boost::range::equal(s1, s2, detail::CiEqualPred{});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a range representing the list. */
|
/**
|
||||||
|
* Returns a range representing the list.
|
||||||
|
*/
|
||||||
inline boost::iterator_range<ListIterator>
|
inline boost::iterator_range<ListIterator>
|
||||||
makeList(boost::string_ref const& field)
|
makeList(boost::string_ref const& field)
|
||||||
{
|
{
|
||||||
@@ -341,20 +349,22 @@ makeList(boost::string_ref const& field)
|
|||||||
ListIterator{field.begin(), field.end()}, ListIterator{field.end(), field.end()}};
|
ListIterator{field.begin(), field.end()}, ListIterator{field.end(), field.end()}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if the specified token exists in the list.
|
/**
|
||||||
|
* Returns true if the specified token exists in the list.
|
||||||
A case-insensitive comparison is used.
|
*
|
||||||
*/
|
* A case-insensitive comparison is used.
|
||||||
|
*/
|
||||||
template <class = void>
|
template <class = void>
|
||||||
bool
|
bool
|
||||||
tokenInList(boost::string_ref const& value, boost::string_ref const& token)
|
tokenInList(boost::string_ref const& value, boost::string_ref const& token)
|
||||||
{
|
{
|
||||||
for (auto const& item : makeList(value))
|
auto const list = makeList(value);
|
||||||
{
|
// ListIterator is not default-constructible, so it does not model a std::ranges
|
||||||
if (ciEqual(item, token))
|
// sentinel/range; the classic std::any_of (which only needs an input iterator)
|
||||||
return true;
|
// is used instead.
|
||||||
}
|
// NOLINTNEXTLINE(modernize-use-ranges)
|
||||||
return false;
|
return std::any_of(
|
||||||
|
list.begin(), list.end(), [&token](auto const& item) { return ciEqual(item, token); });
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool IsRequest, class Body, class Fields>
|
template <bool IsRequest, class Body, class Fields>
|
||||||
|
|||||||
@@ -19,12 +19,13 @@
|
|||||||
|
|
||||||
namespace beast::test {
|
namespace beast::test {
|
||||||
|
|
||||||
/** Mix-in to support tests using asio coroutines.
|
/**
|
||||||
|
* Mix-in to support tests using asio coroutines.
|
||||||
Derive from this class and use yield_to to launch test
|
*
|
||||||
functions inside coroutines. This is handy for testing
|
* Derive from this class and use yield_to to launch test
|
||||||
asynchronous asio code.
|
* functions inside coroutines. This is handy for testing
|
||||||
*/
|
* asynchronous asio code.
|
||||||
|
*/
|
||||||
class EnableYieldTo
|
class EnableYieldTo
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@@ -38,7 +39,9 @@ private:
|
|||||||
std::size_t running_ = 0;
|
std::size_t running_ = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// The type of yield context passed to functions.
|
/**
|
||||||
|
* The type of yield context passed to functions.
|
||||||
|
*/
|
||||||
using yield_context = boost::asio::yield_context;
|
using yield_context = boost::asio::yield_context;
|
||||||
|
|
||||||
explicit EnableYieldTo(std::size_t concurrency = 1) : work_(boost::asio::make_work_guard(ios_))
|
explicit EnableYieldTo(std::size_t concurrency = 1) : work_(boost::asio::make_work_guard(ios_))
|
||||||
@@ -57,24 +60,27 @@ public:
|
|||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the `io_context` associated with the object
|
/**
|
||||||
|
* Return the `io_context` associated with the object
|
||||||
|
*/
|
||||||
boost::asio::io_context&
|
boost::asio::io_context&
|
||||||
getIoContext()
|
getIoContext()
|
||||||
{
|
{
|
||||||
return ios_;
|
return ios_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Run one or more functions, each in a coroutine.
|
/**
|
||||||
|
* Run one or more functions, each in a coroutine.
|
||||||
This call will block until all coroutines terminate.
|
*
|
||||||
|
* This call will block until all coroutines terminate.
|
||||||
Each functions should have this signature:
|
*
|
||||||
@code
|
* Each functions should have this signature:
|
||||||
void f(yield_context);
|
* @code
|
||||||
@endcode
|
* void f(yield_context);
|
||||||
|
* @endcode
|
||||||
@param fn... One or more functions to invoke.
|
*
|
||||||
*/
|
* @param fn... One or more functions to invoke.
|
||||||
|
*/
|
||||||
#if BEAST_DOXYGEN
|
#if BEAST_DOXYGEN
|
||||||
template <class... FN>
|
template <class... FN>
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
|
|
||||||
namespace beast::unit_test {
|
namespace beast::unit_test {
|
||||||
|
|
||||||
/** Utility for producing nicely composed output of amounts with units. */
|
/**
|
||||||
|
* Utility for producing nicely composed output of amounts with units.
|
||||||
|
*/
|
||||||
class Amount
|
class Amount
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -6,10 +6,11 @@
|
|||||||
|
|
||||||
namespace beast::unit_test::detail {
|
namespace beast::unit_test::detail {
|
||||||
|
|
||||||
/** Adapter to constrain a container interface.
|
/**
|
||||||
The interface allows for limited read only operations. Derived classes
|
* Adapter to constrain a container interface.
|
||||||
provide additional behavior.
|
* The interface allows for limited read only operations. Derived classes
|
||||||
*/
|
* provide additional behavior.
|
||||||
|
*/
|
||||||
template <class Container>
|
template <class Container>
|
||||||
class ConstContainer
|
class ConstContainer
|
||||||
{
|
{
|
||||||
@@ -38,21 +39,27 @@ public:
|
|||||||
using iterator = cont_type::const_iterator;
|
using iterator = cont_type::const_iterator;
|
||||||
using const_iterator = cont_type::const_iterator;
|
using const_iterator = cont_type::const_iterator;
|
||||||
|
|
||||||
/** Returns `true` if the container is empty. */
|
/**
|
||||||
|
* Returns `true` if the container is empty.
|
||||||
|
*/
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
empty() const
|
empty() const
|
||||||
{
|
{
|
||||||
return cont_.empty();
|
return cont_.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the number of items in the container. */
|
/**
|
||||||
|
* Returns the number of items in the container.
|
||||||
|
*/
|
||||||
[[nodiscard]] size_type
|
[[nodiscard]] size_type
|
||||||
size() const
|
size() const
|
||||||
{
|
{
|
||||||
return cont_.size();
|
return cont_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns forward iterators for traversal. */
|
/**
|
||||||
|
* Returns forward iterators for traversal.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
[[nodiscard]] const_iterator
|
[[nodiscard]] const_iterator
|
||||||
begin() const
|
begin() const
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ namespace beast::unit_test {
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
/// Holds test suites registered during static initialization.
|
/**
|
||||||
|
* Holds test suites registered during static initialization.
|
||||||
|
*/
|
||||||
inline SuiteList&
|
inline SuiteList&
|
||||||
globalSuites()
|
globalSuites()
|
||||||
{
|
{
|
||||||
@@ -34,7 +36,9 @@ struct InsertSuite
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/// Holds test suites registered during static initialization.
|
/**
|
||||||
|
* Holds test suites registered during static initialization.
|
||||||
|
*/
|
||||||
inline SuiteList const&
|
inline SuiteList const&
|
||||||
globalSuites()
|
globalSuites()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -121,42 +121,48 @@ Selector::operator()(SuiteInfo const& s)
|
|||||||
|
|
||||||
// Utility functions for producing predicates to select suites.
|
// Utility functions for producing predicates to select suites.
|
||||||
|
|
||||||
/** Returns a predicate that implements a smart matching rule.
|
/**
|
||||||
The predicate checks the suite, module, and library fields of the
|
* Returns a predicate that implements a smart matching rule.
|
||||||
SuiteInfo in that order. When it finds a match, it changes modes
|
* The predicate checks the suite, module, and library fields of the
|
||||||
depending on what was found:
|
* SuiteInfo in that order. When it finds a match, it changes modes
|
||||||
|
* depending on what was found:
|
||||||
If a suite is matched first, then only the suite is selected. The
|
*
|
||||||
suite may be marked manual.
|
* If a suite is matched first, then only the suite is selected. The
|
||||||
|
* suite may be marked manual.
|
||||||
If a module is matched first, then only suites from that module
|
*
|
||||||
and library not marked manual are selected from then on.
|
* If a module is matched first, then only suites from that module
|
||||||
|
* and library not marked manual are selected from then on.
|
||||||
If a library is matched first, then only suites from that library
|
*
|
||||||
not marked manual are selected from then on.
|
* If a library is matched first, then only suites from that library
|
||||||
|
* not marked manual are selected from then on.
|
||||||
*/
|
*/
|
||||||
inline Selector
|
inline Selector
|
||||||
matchAuto(std::string const& name)
|
matchAuto(std::string const& name)
|
||||||
{
|
{
|
||||||
return Selector(Selector::ModeT::Automatch, name);
|
return Selector(Selector::ModeT::Automatch, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a predicate that matches all suites not marked manual. */
|
/**
|
||||||
|
* Return a predicate that matches all suites not marked manual.
|
||||||
|
*/
|
||||||
inline Selector
|
inline Selector
|
||||||
matchAll()
|
matchAll()
|
||||||
{
|
{
|
||||||
return Selector(Selector::ModeT::All);
|
return Selector(Selector::ModeT::All);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a predicate that matches a specific suite. */
|
/**
|
||||||
|
* Returns a predicate that matches a specific suite.
|
||||||
|
*/
|
||||||
inline Selector
|
inline Selector
|
||||||
matchSuite(std::string const& name)
|
matchSuite(std::string const& name)
|
||||||
{
|
{
|
||||||
return Selector(Selector::ModeT::Suite, name);
|
return Selector(Selector::ModeT::Suite, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a predicate that matches all suites in a library. */
|
/**
|
||||||
|
* Returns a predicate that matches all suites in a library.
|
||||||
|
*/
|
||||||
inline Selector
|
inline Selector
|
||||||
matchLibrary(std::string const& name)
|
matchLibrary(std::string const& name)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
|
|
||||||
namespace beast::unit_test {
|
namespace beast::unit_test {
|
||||||
|
|
||||||
/** A test runner that stores the results. */
|
/**
|
||||||
|
* A test runner that stores the results.
|
||||||
|
*/
|
||||||
class Recorder : public Runner
|
class Recorder : public Runner
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -24,7 +26,9 @@ private:
|
|||||||
public:
|
public:
|
||||||
Recorder() = default;
|
Recorder() = default;
|
||||||
|
|
||||||
/** Returns a report with the results of all completed suites. */
|
/**
|
||||||
|
* Returns a report with the results of all completed suites.
|
||||||
|
*/
|
||||||
[[nodiscard]] Results const&
|
[[nodiscard]] Results const&
|
||||||
report() const
|
report() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ namespace beast::unit_test {
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
/** A simple test runner that writes everything to a stream in real time.
|
/**
|
||||||
The totals are output when the object is destroyed.
|
* A simple test runner that writes everything to a stream in real time.
|
||||||
*/
|
* The totals are output when the object is destroyed.
|
||||||
|
*/
|
||||||
template <class = void>
|
template <class = void>
|
||||||
class Reporter : public Runner
|
class Reporter : public Runner
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,11 +13,15 @@
|
|||||||
|
|
||||||
namespace beast::unit_test {
|
namespace beast::unit_test {
|
||||||
|
|
||||||
/** Holds a set of test condition outcomes in a testcase. */
|
/**
|
||||||
|
* Holds a set of test condition outcomes in a testcase.
|
||||||
|
*/
|
||||||
class CaseResults
|
class CaseResults
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Holds the result of evaluating one test condition. */
|
/**
|
||||||
|
* Holds the result of evaluating one test condition.
|
||||||
|
*/
|
||||||
struct Test
|
struct Test
|
||||||
{
|
{
|
||||||
explicit Test(bool pass) : pass(pass)
|
explicit Test(bool pass) : pass(pass)
|
||||||
@@ -41,28 +45,36 @@ private:
|
|||||||
public:
|
public:
|
||||||
TestsT() = default;
|
TestsT() = default;
|
||||||
|
|
||||||
/** Returns the total number of test conditions. */
|
/**
|
||||||
|
* Returns the total number of test conditions.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
total() const
|
total() const
|
||||||
{
|
{
|
||||||
return cont().size();
|
return cont().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the number of failed test conditions. */
|
/**
|
||||||
|
* Returns the number of failed test conditions.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
failed() const
|
failed() const
|
||||||
{
|
{
|
||||||
return failed_;
|
return failed_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Register a successful test condition. */
|
/**
|
||||||
|
* Register a successful test condition.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
pass()
|
pass()
|
||||||
{
|
{
|
||||||
cont().emplace_back(true);
|
cont().emplace_back(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Register a failed test condition. */
|
/**
|
||||||
|
* Register a failed test condition.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
fail(std::string const& reason = "")
|
fail(std::string const& reason = "")
|
||||||
{
|
{
|
||||||
@@ -74,7 +86,9 @@ private:
|
|||||||
class LogT : public detail::ConstContainer<std::vector<std::string>>
|
class LogT : public detail::ConstContainer<std::vector<std::string>>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Insert a string into the log. */
|
/**
|
||||||
|
* Insert a string into the log.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
insert(std::string const& s)
|
insert(std::string const& s)
|
||||||
{
|
{
|
||||||
@@ -89,23 +103,31 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the name of this testcase. */
|
/**
|
||||||
|
* Returns the name of this testcase.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::string const&
|
[[nodiscard]] std::string const&
|
||||||
name() const
|
name() const
|
||||||
{
|
{
|
||||||
return name_;
|
return name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Memberspace for a container of test condition outcomes. */
|
/**
|
||||||
|
* Memberspace for a container of test condition outcomes.
|
||||||
|
*/
|
||||||
TestsT tests;
|
TestsT tests;
|
||||||
|
|
||||||
/** Memberspace for a container of testcase log messages. */
|
/**
|
||||||
|
* Memberspace for a container of testcase log messages.
|
||||||
|
*/
|
||||||
LogT log;
|
LogT log;
|
||||||
};
|
};
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
/** Holds the set of testcase results in a suite. */
|
/**
|
||||||
|
* Holds the set of testcase results in a suite.
|
||||||
|
*/
|
||||||
class SuiteResults : public detail::ConstContainer<std::vector<CaseResults>>
|
class SuiteResults : public detail::ConstContainer<std::vector<CaseResults>>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -118,28 +140,36 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the name of this suite. */
|
/**
|
||||||
|
* Returns the name of this suite.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::string const&
|
[[nodiscard]] std::string const&
|
||||||
name() const
|
name() const
|
||||||
{
|
{
|
||||||
return name_;
|
return name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the total number of test conditions. */
|
/**
|
||||||
|
* Returns the total number of test conditions.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
total() const
|
total() const
|
||||||
{
|
{
|
||||||
return total_;
|
return total_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the number of failures. */
|
/**
|
||||||
|
* Returns the number of failures.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
failed() const
|
failed() const
|
||||||
{
|
{
|
||||||
return failed_;
|
return failed_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Insert a set of testcase results. */
|
/**
|
||||||
|
* Insert a set of testcase results.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
void
|
void
|
||||||
insert(CaseResults&& r)
|
insert(CaseResults&& r)
|
||||||
@@ -162,7 +192,9 @@ public:
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// VFALCO TODO Make this a template class using scoped allocators
|
// VFALCO TODO Make this a template class using scoped allocators
|
||||||
/** Holds the results of running a set of testsuites. */
|
/**
|
||||||
|
* Holds the results of running a set of testsuites.
|
||||||
|
*/
|
||||||
class Results : public detail::ConstContainer<std::vector<SuiteResults>>
|
class Results : public detail::ConstContainer<std::vector<SuiteResults>>
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -173,28 +205,36 @@ private:
|
|||||||
public:
|
public:
|
||||||
Results() = default;
|
Results() = default;
|
||||||
|
|
||||||
/** Returns the total number of test cases. */
|
/**
|
||||||
|
* Returns the total number of test cases.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
cases() const
|
cases() const
|
||||||
{
|
{
|
||||||
return cases_;
|
return cases_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the total number of test conditions. */
|
/**
|
||||||
|
* Returns the total number of test conditions.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
total() const
|
total() const
|
||||||
{
|
{
|
||||||
return total_;
|
return total_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the number of failures. */
|
/**
|
||||||
|
* Returns the number of failures.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::size_t
|
[[nodiscard]] std::size_t
|
||||||
failed() const
|
failed() const
|
||||||
{
|
{
|
||||||
return failed_;
|
return failed_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Insert a set of suite results. */
|
/**
|
||||||
|
* Insert a set of suite results.
|
||||||
|
*/
|
||||||
/** @{ */
|
/** @{ */
|
||||||
void
|
void
|
||||||
insert(SuiteResults&& r)
|
insert(SuiteResults&& r)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user