mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-13 18:20:22 +00:00
Compare commits
2 Commits
mvadari/te
...
vvysokikh/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
791924370b | ||
|
|
2738d883a8 |
53
.clang-tidy
53
.clang-tidy
@@ -9,48 +9,101 @@ Checks: "-*,
|
||||
|
||||
cppcoreguidelines-*,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-avoid-capturing-lambda-coroutines,
|
||||
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||
-cppcoreguidelines-avoid-do-while,
|
||||
-cppcoreguidelines-avoid-goto,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-avoid-reference-coroutine-parameters,
|
||||
-cppcoreguidelines-c-copy-assignment-signature,
|
||||
-cppcoreguidelines-explicit-virtual-functions,
|
||||
-cppcoreguidelines-interfaces-global-init,
|
||||
-cppcoreguidelines-macro-to-enum,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-missing-std-forward,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
-cppcoreguidelines-no-malloc,
|
||||
-cppcoreguidelines-noexcept-destructor,
|
||||
-cppcoreguidelines-noexcept-move-operations,
|
||||
-cppcoreguidelines-noexcept-swap,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-prefer-member-initializer,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-cppcoreguidelines-pro-type-const-cast,
|
||||
-cppcoreguidelines-pro-type-cstyle-cast,
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-cppcoreguidelines-slicing,
|
||||
-cppcoreguidelines-special-member-functions,
|
||||
|
||||
llvm-namespace-comment,
|
||||
|
||||
misc-*,
|
||||
-misc-anonymous-namespace-in-header,
|
||||
-misc-confusable-identifiers,
|
||||
-misc-coroutine-hostile-raii,
|
||||
-misc-misleading-bidirectional,
|
||||
-misc-misleading-identifier,
|
||||
-misc-multiple-inheritance,
|
||||
-misc-new-delete-overloads,
|
||||
-misc-no-recursion,
|
||||
-misc-non-copyable-objects,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-override-with-different-visibility,
|
||||
-misc-predictable-rand,
|
||||
-misc-unconventional-assign-operator,
|
||||
-misc-uniqueptr-reset-release,
|
||||
-misc-unused-parameters,
|
||||
-misc-use-anonymous-namespace,
|
||||
-misc-use-internal-linkage,
|
||||
|
||||
modernize-*,
|
||||
-modernize-avoid-bind,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-avoid-c-style-cast,
|
||||
-modernize-avoid-setjmp-longjmp,
|
||||
-modernize-avoid-variadic-functions,
|
||||
-modernize-deprecated-ios-base-aliases,
|
||||
-modernize-loop-convert,
|
||||
-modernize-macro-to-enum,
|
||||
-modernize-min-max-use-initializer-list,
|
||||
-modernize-raw-string-literal,
|
||||
-modernize-redundant-void-arg,
|
||||
-modernize-replace-auto-ptr,
|
||||
-modernize-replace-disallow-copy-and-assign-macro,
|
||||
-modernize-replace-random-shuffle,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-shrink-to-fit,
|
||||
-modernize-unary-static-assert,
|
||||
-modernize-use-auto,
|
||||
-modernize-use-bool-literals,
|
||||
-modernize-use-constraints,
|
||||
-modernize-use-default-member-init,
|
||||
-modernize-use-integer-sign-comparison,
|
||||
-modernize-use-noexcept,
|
||||
-modernize-use-nullptr,
|
||||
-modernize-use-std-format,
|
||||
-modernize-use-std-print,
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-use-transparent-functors,
|
||||
-modernize-use-uncaught-exceptions,
|
||||
|
||||
performance-*,
|
||||
-performance-avoid-endl,
|
||||
-performance-enum-size,
|
||||
-performance-inefficient-algorithm,
|
||||
-performance-inefficient-string-concatenation,
|
||||
-performance-no-int-to-ptr,
|
||||
-performance-noexcept-destructor,
|
||||
-performance-noexcept-move-constructor,
|
||||
-performance-noexcept-swap,
|
||||
-performance-type-promotion-in-math-fn,
|
||||
-performance-unnecessary-copy-initialization,
|
||||
-performance-unnecessary-value-param,
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ test.app > xrpl.server
|
||||
test.app > xrpl.shamap
|
||||
test.app > xrpl.tx
|
||||
test.basics > test.jtx
|
||||
test.basics > test.unit_test
|
||||
test.basics > xrpl.basics
|
||||
test.basics > xrpl.core
|
||||
test.basics > xrpld.rpc
|
||||
@@ -161,6 +162,9 @@ test.protocol > test.unit_test
|
||||
test.protocol > xrpl.basics
|
||||
test.protocol > xrpl.json
|
||||
test.protocol > xrpl.protocol
|
||||
test.resource > test.unit_test
|
||||
test.resource > xrpl.basics
|
||||
test.resource > xrpl.resource
|
||||
test.rpc > test.jtx
|
||||
test.rpc > xrpl.basics
|
||||
test.rpc > xrpl.config
|
||||
@@ -184,6 +188,12 @@ test.server > xrpld.core
|
||||
test.server > xrpl.json
|
||||
test.server > xrpl.protocol
|
||||
test.server > xrpl.server
|
||||
test.shamap > test.unit_test
|
||||
test.shamap > xrpl.basics
|
||||
test.shamap > xrpl.config
|
||||
test.shamap > xrpl.nodestore
|
||||
test.shamap > xrpl.protocol
|
||||
test.shamap > xrpl.shamap
|
||||
test.unit_test > xrpl.basics
|
||||
test.unit_test > xrpl.protocol
|
||||
tests.libxrpl > xrpl.basics
|
||||
@@ -195,7 +205,6 @@ tests.libxrpl > xrpl.net
|
||||
tests.libxrpl > xrpl.nodestore
|
||||
tests.libxrpl > xrpl.protocol
|
||||
tests.libxrpl > xrpl.protocol_autogen
|
||||
tests.libxrpl > xrpl.resource
|
||||
tests.libxrpl > xrpl.server
|
||||
tests.libxrpl > xrpl.shamap
|
||||
tests.libxrpl > xrpl.tx
|
||||
|
||||
22
.github/workflows/reusable-build-test-config.yml
vendored
22
.github/workflows/reusable-build-test-config.yml
vendored
@@ -227,8 +227,7 @@ jobs:
|
||||
--build . \
|
||||
--config "${BUILD_TYPE}" \
|
||||
--parallel "${BUILD_NPROC}" \
|
||||
--target "${CMAKE_TARGET}" \
|
||||
2>&1 | tee build.log
|
||||
--target "${CMAKE_TARGET}"
|
||||
|
||||
- name: Show ccache statistics
|
||||
if: ${{ inputs.ccache_enabled }}
|
||||
@@ -326,7 +325,7 @@ jobs:
|
||||
LD_PRELOAD="$PRELOAD" ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log
|
||||
|
||||
- name: Show test failure summary
|
||||
if: ${{ failure() }}
|
||||
if: ${{ failure() && !inputs.build_only }}
|
||||
env:
|
||||
WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
|
||||
run: |
|
||||
@@ -337,16 +336,13 @@ jobs:
|
||||
|
||||
cd "${WORKING_DIR}"
|
||||
|
||||
if [ -f unittest.log ]; then
|
||||
if ! grep -E "failed" unittest.log | grep -vE "^I[0-9]|^[0-9]+> (ERR:|FTL:)"; then
|
||||
echo "unittest.log present but no failure lines found."
|
||||
fi
|
||||
elif [ -f build.log ]; then
|
||||
if ! grep -E "error:" build.log; then
|
||||
echo "build.log present but no compile errors found."
|
||||
fi
|
||||
else
|
||||
echo "unittest.log/build.log not found; something went wrong."
|
||||
if [ ! -f unittest.log ]; then
|
||||
echo "unittest.log not found; embedded tests may not have run."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! grep -E "failed" unittest.log; then
|
||||
echo "Log present but no failure lines found in unittest.log."
|
||||
fi
|
||||
- name: Debug failure (Linux)
|
||||
if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }}
|
||||
|
||||
@@ -43,11 +43,6 @@ repos:
|
||||
language: python
|
||||
entry: ./bin/pre-commit/fix_pragma_once.py
|
||||
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
|
||||
rev: dd18dad857d6133e90bbe478f4f2f22ec0030269 # frozen: v22.1.5
|
||||
@@ -95,19 +90,20 @@ repos:
|
||||
- repo: https://github.com/streetsidesoftware/cspell-cli
|
||||
rev: ea11f9efc0bec520073405bc30552da887ba71bc # frozen: v10.0.1
|
||||
hooks:
|
||||
- id: cspell
|
||||
name: check changed files spelling
|
||||
- id: cspell # Spell check changed files
|
||||
exclude: |
|
||||
(?x)^(
|
||||
\.cspell\.config\.yaml|
|
||||
.config/cspell.config.yaml|
|
||||
include/xrpl/protocol_autogen/(transactions|ledger_entries)/.*
|
||||
)$
|
||||
- id: cspell
|
||||
- id: cspell # Spell check the commit message
|
||||
name: check commit message spelling
|
||||
args:
|
||||
- --no-must-find-files
|
||||
- --no-progress
|
||||
- --no-summary
|
||||
- --files
|
||||
- .git/COMMIT_EDITMSG
|
||||
stages: [commit-msg]
|
||||
|
||||
- repo: local
|
||||
|
||||
2
BUILD.md
2
BUILD.md
@@ -25,7 +25,7 @@ You can verify that the required tools are installed and runnable with:
|
||||
| ----------- | --------------- |
|
||||
| GCC | 15.2 |
|
||||
| Clang | 22 |
|
||||
| Apple Clang | 21 |
|
||||
| Apple Clang | 17 |
|
||||
| MSVC | 19.44[^windows] |
|
||||
|
||||
## Operating Systems
|
||||
|
||||
@@ -1,440 +0,0 @@
|
||||
#!/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())
|
||||
@@ -17,11 +17,9 @@ import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
CLANG_TIDY_VERSION = 22
|
||||
|
||||
|
||||
def find_run_clang_tidy() -> str | None:
|
||||
for candidate in (f"run-clang-tidy-{CLANG_TIDY_VERSION}", "run-clang-tidy"):
|
||||
for candidate in ("run-clang-tidy-21", "run-clang-tidy"):
|
||||
if path := shutil.which(candidate):
|
||||
return path
|
||||
return None
|
||||
@@ -46,8 +44,8 @@ def main():
|
||||
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.",
|
||||
"clang-tidy check failed: TIDY is enabled but neither "
|
||||
"'run-clang-tidy-21' nor 'run-clang-tidy' was found in PATH.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
@@ -1,406 +0,0 @@
|
||||
#!/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())
|
||||
@@ -177,9 +177,7 @@ ${field['typeData']['setter_type']} ${field['paramName']}${',' if i < len(requir
|
||||
object_ = *sle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ledger entry-specific field setters
|
||||
*/
|
||||
/** @brief Ledger entry-specific field setters */
|
||||
% for field in fields:
|
||||
|
||||
/**
|
||||
|
||||
@@ -185,9 +185,7 @@ public:
|
||||
object_ = *tx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transaction-specific field setters
|
||||
*/
|
||||
/** @brief Transaction-specific field setters */
|
||||
% for field in fields:
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,9 @@ overrides:
|
||||
- /'[^']*'/g # single-quoted strings
|
||||
- /`[^`]*`/g # backtick strings
|
||||
suggestWords:
|
||||
- unsynched->unsynced
|
||||
- xprl->xrpl
|
||||
- xprld->xrpld # cspell: disable-line not sure what this problem is....
|
||||
- unsynched->unsynced # cspell: disable-line not sure what this problem is....
|
||||
- synched->synced
|
||||
- synch->sync
|
||||
words:
|
||||
@@ -279,8 +281,6 @@ words:
|
||||
- sles
|
||||
- soci
|
||||
- socidb
|
||||
- sponsee
|
||||
- sponsees
|
||||
- SRPMS
|
||||
- sslws
|
||||
- statsd
|
||||
@@ -302,7 +302,6 @@ words:
|
||||
- takerpays
|
||||
- ters
|
||||
- TMEndpointv2
|
||||
- tparam
|
||||
- trixie
|
||||
- tx
|
||||
- txid
|
||||
@@ -330,7 +329,6 @@ words:
|
||||
- unserviced
|
||||
- unshareable
|
||||
- unshares
|
||||
- unsponsored
|
||||
- unsquelch
|
||||
- unsquelched
|
||||
- unsquelching
|
||||
15
docs/build/environment.md
vendored
15
docs/build/environment.md
vendored
@@ -33,10 +33,9 @@ with a single command and without installing anything system-wide:
|
||||
nix --experimental-features 'nix-command flakes' develop
|
||||
```
|
||||
|
||||
On **Linux**, Nix also provides the compiler (GCC); on **macOS**, it provides
|
||||
Clang. If you instead opt to use your system-wide Apple Clang (via
|
||||
`nix develop .#apple-clang`), you need to manage its version yourself (see
|
||||
below).
|
||||
On **Linux**, Nix also provides the compiler (GCC). On **macOS**, the shell uses
|
||||
your **system-wide Apple Clang** as the compiler, so you still need to manage
|
||||
its version (see below).
|
||||
|
||||
See [Using the Nix development shell](./nix.md) for installation and usage
|
||||
details, including how to select a different compiler.
|
||||
@@ -49,10 +48,10 @@ details, including how to select a different compiler.
|
||||
|
||||
### macOS: managing the Apple Clang version
|
||||
|
||||
If you use your system-wide Apple Clang on macOS (via `nix develop .#apple-clang`),
|
||||
the compiler version is whatever your installed Xcode (or Command Line Tools)
|
||||
provides. The following command should return a version greater than or equal to
|
||||
the [minimum required](#tested-compiler-versions):
|
||||
Because the Nix shell uses the system-wide Apple Clang on macOS, the compiler
|
||||
version is whatever your installed Xcode (or Command Line Tools) provides. The
|
||||
following command should return a version greater than or equal to the
|
||||
[minimum required](#tested-compiler-versions):
|
||||
|
||||
```bash
|
||||
clang --version
|
||||
|
||||
40
docs/build/nix.md
vendored
40
docs/build/nix.md
vendored
@@ -9,7 +9,7 @@ This guide explains how to use Nix to set up a reproducible development environm
|
||||
- **Reproducible environment**: Everyone gets the same versions of tools and compilers
|
||||
- **Matches CI**: The Linux CI runs in Docker images built from this exact Nix environment
|
||||
- **No system pollution**: Dependencies are isolated and don't affect your system packages
|
||||
- **Consistent compilers**: The GCC and Clang shells use the same versions as CI
|
||||
- **Multiple compiler versions**: Easily switch between different GCC and Clang versions
|
||||
- **Quick setup**: Get started with a single command
|
||||
- **Works on Linux and macOS**: Consistent experience across platforms
|
||||
|
||||
@@ -31,8 +31,8 @@ This will:
|
||||
|
||||
- Download and set up all required development tools (CMake, Ninja, Conan, etc.)
|
||||
- Configure the appropriate compiler for your platform:
|
||||
- **Linux**: GCC (provided by Nix)
|
||||
- **macOS**: Clang (provided by Nix)
|
||||
- **Linux**: GCC 15.2 (provided by Nix)
|
||||
- **macOS**: Apple Clang (your system compiler)
|
||||
|
||||
The first time you run this command, it will take a few minutes to download and build the environment. Subsequent runs will be much faster.
|
||||
|
||||
@@ -40,12 +40,12 @@ The first time you run this command, it will take a few minutes to download and
|
||||
|
||||
- **Linux**: `nix develop` gives you a shell with all the tooling necessary to
|
||||
develop xrpld and with GCC 15.2 (also provided by Nix). There are no caveats.
|
||||
- **macOS**: `nix develop` gives you a full environment too, with Clang (and
|
||||
every other tool, including Conan) provided by Nix. To use your system-wide
|
||||
Apple Clang instead, enter `nix develop .#apple-clang`. Conan has no binary in
|
||||
the Nix cache for macOS, so it is built from source the first time you enter
|
||||
the shell, which makes the initial setup slower (this is handled
|
||||
automatically; see [`nix/devshell.nix`](../../nix/devshell.nix)).
|
||||
- **macOS**: `nix develop` gives you a full environment too. The compiler is
|
||||
your system-wide Apple Clang, while every other tool — including Conan — is
|
||||
provided by Nix. Conan has no binary in the Nix cache for macOS, so it is
|
||||
built from source the first time you enter the shell, which makes the initial
|
||||
setup slower (this is handled automatically; see
|
||||
[`nix/devshell.nix`](../../nix/devshell.nix)).
|
||||
|
||||
> [!TIP]
|
||||
> To avoid typing `--experimental-features 'nix-command flakes'` every time, you can permanently enable flakes by creating `~/.config/nix/nix.conf`:
|
||||
@@ -62,9 +62,7 @@ The first time you run this command, it will take a few minutes to download and
|
||||
|
||||
### Choosing a different compiler
|
||||
|
||||
A compiler can be chosen by providing its name with the `.#` prefix, e.g. `nix develop .#clang`.
|
||||
The `.#gcc` and `.#clang` shells provide the same GCC and Clang versions used in CI
|
||||
(pinned in [`nix/packages.nix`](../../nix/packages.nix)).
|
||||
A compiler can be chosen by providing its name with the `.#` prefix, e.g. `nix develop .#gcc15`.
|
||||
Use `nix flake show` to see all the available development shells.
|
||||
|
||||
Use `nix develop .#no-compiler` to use the compiler from your system.
|
||||
@@ -72,11 +70,11 @@ Use `nix develop .#no-compiler` to use the compiler from your system.
|
||||
### Example Usage
|
||||
|
||||
```bash
|
||||
# Use GCC (same version as CI)
|
||||
nix develop .#gcc
|
||||
# Use GCC 14
|
||||
nix develop .#gcc14
|
||||
|
||||
# Use Clang (same version as CI)
|
||||
nix develop .#clang
|
||||
# Use Clang 19
|
||||
nix develop .#clang19
|
||||
|
||||
# Use default for your platform
|
||||
nix develop
|
||||
@@ -114,15 +112,7 @@ Once inside the Nix development shell, follow the standard [build instructions](
|
||||
|
||||
[direnv](https://direnv.net/) or [nix-direnv](https://github.com/nix-community/nix-direnv) can automatically activate the Nix development shell when you enter the repository directory.
|
||||
|
||||
This is also the most robust way to use the environment from **any shell** (bash, zsh, fish, …): direnv stays in your current shell and loads the environment _after_ your shell's startup files have run, so the Nix-provided tools take precedence over anything your shell configuration adds to `$PATH`.
|
||||
|
||||
The repository already ships an `.envrc` at its root that activates the Nix flake development shell, so you don't need to create one. To use it:
|
||||
|
||||
1. [Install direnv](https://direnv.net/docs/installation.html) and [hook it into your shell](https://direnv.net/docs/hook.html) (bash, zsh, fish, …). Installing [nix-direnv](https://github.com/nix-community/nix-direnv) as well is recommended: it caches the shell so that activation is near-instant after the first run.
|
||||
2. Run `direnv allow` once in the repository root. direnv will then load (and reload) the Nix development shell automatically whenever you enter the directory.
|
||||
|
||||
> [!NOTE]
|
||||
> direnv only caches the `.direnv` directory (already listed in `.gitignore`); no other repository files are affected.
|
||||
This is also the most robust way to use the environment from **any shell** (bash, zsh, fish, …): direnv stays in your current shell and loads the environment _after_ your shell's startup files have run, so the Nix-provided tools take precedence over anything your shell configuration adds to `$PATH`. To use it, install direnv for your shell, then add an `.envrc` containing `use flake` at the repository root and run `direnv allow`.
|
||||
|
||||
## Conan and Prebuilt Packages
|
||||
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Extract a tar archive compressed with lz4
|
||||
*
|
||||
* @param src the path of the archive to be extracted
|
||||
* @param dst the directory to extract to
|
||||
*
|
||||
* @throws runtime_error
|
||||
*/
|
||||
/** Extract a tar archive compressed with lz4
|
||||
|
||||
@param src the path of the archive to be extracted
|
||||
@param dst the directory to extract to
|
||||
|
||||
@throws runtime_error
|
||||
*/
|
||||
void
|
||||
extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& dst);
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
|
||||
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>;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
|
||||
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
|
||||
{
|
||||
private:
|
||||
@@ -25,37 +24,30 @@ public:
|
||||
|
||||
Buffer() = default;
|
||||
|
||||
/**
|
||||
* Create an uninitialized buffer with the given size.
|
||||
*/
|
||||
/** Create an uninitialized buffer with the given size. */
|
||||
explicit Buffer(std::size_t size)
|
||||
: p_((size != 0u) ? new std::uint8_t[size] : nullptr), size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 size size of the existing memory block.
|
||||
*/
|
||||
/** 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 size size of the existing memory block.
|
||||
*/
|
||||
Buffer(void const* data, std::size_t size) : Buffer(size)
|
||||
{
|
||||
if (size != 0u)
|
||||
std::memcpy(p_.get(), data, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy-construct
|
||||
*/
|
||||
/** Copy-construct */
|
||||
Buffer(Buffer const& other) : Buffer(other.p_.get(), other.size_)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy assign
|
||||
*/
|
||||
/** Copy assign */
|
||||
Buffer&
|
||||
operator=(Buffer const& other)
|
||||
{
|
||||
@@ -67,19 +59,17 @@ public:
|
||||
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_)
|
||||
{
|
||||
other.size_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move-assign.
|
||||
* The other buffer is reset.
|
||||
*/
|
||||
/** Move-assign.
|
||||
The other buffer is reset.
|
||||
*/
|
||||
Buffer&
|
||||
operator=(Buffer&& other) noexcept
|
||||
{
|
||||
@@ -92,16 +82,12 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct from a slice
|
||||
*/
|
||||
/** Construct from a slice */
|
||||
explicit Buffer(Slice s) : Buffer(s.data(), s.size())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign from slice
|
||||
*/
|
||||
/** Assign from slice */
|
||||
Buffer&
|
||||
operator=(Slice s)
|
||||
{
|
||||
@@ -115,9 +101,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in the buffer.
|
||||
*/
|
||||
/** Returns the number of bytes in the buffer. */
|
||||
[[nodiscard]] std::size_t
|
||||
size() const noexcept
|
||||
{
|
||||
@@ -137,11 +121,10 @@ public:
|
||||
return Slice{p_.get(), size_};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pointer to beginning of the storage.
|
||||
* @note The return type is guaranteed to be a pointer
|
||||
* to a single byte, to facilitate pointer arithmetic.
|
||||
*/
|
||||
/** Return a pointer to beginning of the storage.
|
||||
@note The return type is guaranteed to be a pointer
|
||||
to a single byte, to facilitate pointer arithmetic.
|
||||
*/
|
||||
/** @{ */
|
||||
[[nodiscard]] std::uint8_t const*
|
||||
data() const noexcept
|
||||
@@ -156,10 +139,9 @@ 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
|
||||
clear() noexcept
|
||||
{
|
||||
@@ -167,10 +149,9 @@ public:
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reallocate the storage.
|
||||
* Existing data, if any, is discarded.
|
||||
*/
|
||||
/** Reallocate the storage.
|
||||
Existing data, if any, is discarded.
|
||||
*/
|
||||
std::uint8_t*
|
||||
alloc(std::size_t n)
|
||||
{
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
|
||||
namespace xrpl::compression_algorithms {
|
||||
|
||||
/**
|
||||
* LZ4 block compression.
|
||||
/** LZ4 block compression.
|
||||
* @tparam BufferFactory Callable object or lambda.
|
||||
* Takes the requested buffer size and returns allocated buffer pointer.
|
||||
* @param in Data to compress
|
||||
@@ -81,8 +80,7 @@ lz4Decompress(
|
||||
return decompressedSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* LZ4 block decompression.
|
||||
/** LZ4 block decompression.
|
||||
* @tparam InputStream ZeroCopyInputStream
|
||||
* @param in Input source stream
|
||||
* @param inSize Size of compressed data
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Manages all counted object types.
|
||||
*/
|
||||
/** Manages all counted object types. */
|
||||
class CountedObjects
|
||||
{
|
||||
public:
|
||||
@@ -25,11 +23,10 @@ public:
|
||||
getCounts(int minimumThreshold) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Implementation for @ref CountedObject.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
/** Implementation for @ref CountedObject.
|
||||
|
||||
@internal
|
||||
*/
|
||||
class Counter
|
||||
{
|
||||
public:
|
||||
@@ -97,14 +94,13 @@ private:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Tracks the number of instances of an object.
|
||||
*
|
||||
* Derived classes have their instances counted automatically. This is used
|
||||
* for reporting purposes.
|
||||
*
|
||||
* @ingroup basics
|
||||
*/
|
||||
/** Tracks the number of instances of an object.
|
||||
|
||||
Derived classes have their instances counted automatically. This is used
|
||||
for reporting purposes.
|
||||
|
||||
@ingroup basics
|
||||
*/
|
||||
template <class Object>
|
||||
class CountedObject
|
||||
{
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
|
||||
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>
|
||||
class DecayingSample
|
||||
{
|
||||
@@ -20,16 +19,15 @@ public:
|
||||
DecayingSample() = delete;
|
||||
|
||||
/**
|
||||
* @param now Start time of DecayingSample.
|
||||
*/
|
||||
@param now Start time of DecayingSample.
|
||||
*/
|
||||
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
|
||||
add(value_type value, time_point now)
|
||||
{
|
||||
@@ -38,10 +36,9 @@ public:
|
||||
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(time_point now)
|
||||
{
|
||||
@@ -90,10 +87,9 @@ 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>
|
||||
class DecayWindow
|
||||
{
|
||||
|
||||
@@ -10,37 +10,33 @@ 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
|
||||
* class from an intrusive pointer to a base class.
|
||||
*/
|
||||
/** 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
|
||||
class from an intrusive pointer to a base class.
|
||||
*/
|
||||
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
|
||||
* class from an intrusive pointer to a base class. If the cast fails an empty
|
||||
* (null) intrusive pointer is created.
|
||||
*/
|
||||
/** 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
|
||||
class from an intrusive pointer to a base class. If the cast fails an empty
|
||||
(null) intrusive pointer is created.
|
||||
*/
|
||||
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
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
};
|
||||
@@ -54,21 +50,20 @@ concept CAdoptTag = std::is_same_v<T, SharedIntrusiveAdoptIncrementStrongTag> ||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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
|
||||
* not destroyed until both the strong _and_ weak pointer counts go to zero.
|
||||
* When the strong pointer count goes to zero, the "partialDestructor" is
|
||||
* called. This can be used to destroy as much of the object as possible while
|
||||
* still retaining the reference counts. For example, for SHAMapInnerNodes the
|
||||
* children may be reset in that function. Note that std::shared_pointer WILL
|
||||
* run the destructor when the strong count reaches zero, but may not free the
|
||||
* memory used by the object until the weak count reaches zero. In xrpld, we
|
||||
* typically allocate shared pointers with the `make_shared` function. When
|
||||
* that is used, the memory is not reclaimed until the weak count reaches zero.
|
||||
*/
|
||||
/** 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
|
||||
not destroyed until both the strong _and_ weak pointer counts go to zero.
|
||||
When the strong pointer count goes to zero, the "partialDestructor" is
|
||||
called. This can be used to destroy as much of the object as possible while
|
||||
still retaining the reference counts. For example, for SHAMapInnerNodes the
|
||||
children may be reset in that function. Note that std::shared_pointer WILL
|
||||
run the destructor when the strong count reaches zero, but may not free the
|
||||
memory used by the object until the weak count reaches zero. In xrpld, we
|
||||
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>
|
||||
class SharedIntrusive
|
||||
{
|
||||
@@ -116,9 +111,8 @@ public:
|
||||
operator=(
|
||||
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>
|
||||
void
|
||||
@@ -126,31 +120,27 @@ public:
|
||||
|
||||
~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>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
SharedIntrusive(DynamicCastTagSharedIntrusive, SharedIntrusive<TT>&& rhs);
|
||||
|
||||
@@ -163,22 +153,17 @@ public:
|
||||
explicit
|
||||
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
|
||||
reset();
|
||||
|
||||
/**
|
||||
* Get the raw pointer
|
||||
*/
|
||||
/** Get the raw pointer */
|
||||
[[nodiscard]] T*
|
||||
get() const;
|
||||
|
||||
/**
|
||||
* Return the strong count
|
||||
*/
|
||||
/** Return the strong count */
|
||||
[[nodiscard]] std::size_t
|
||||
useCount() const;
|
||||
|
||||
@@ -196,51 +181,43 @@ public:
|
||||
friend class WeakIntrusive;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Return the raw pointer held by this object.
|
||||
*/
|
||||
/** Return the raw pointer held by this object. */
|
||||
[[nodiscard]] T*
|
||||
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
|
||||
* by this object and run the appropriate release action.
|
||||
/** Exchange the current raw pointer held by this object with the given
|
||||
pointer. Decrement the strong count of the raw pointer previously held
|
||||
by this object and run the appropriate release action.
|
||||
*/
|
||||
void
|
||||
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
|
||||
unsafeSetRawPtr(T* p);
|
||||
|
||||
/**
|
||||
* Exchange the raw pointer directly.
|
||||
* This sets the raw pointer to the given value and returns the previous
|
||||
* value. This is wrapped in a function so the class can support both
|
||||
* atomic and non-atomic pointers in a future patch.
|
||||
/** Exchange the raw pointer directly.
|
||||
This sets the raw pointer to the given value and returns the previous
|
||||
value. This is wrapped in a function so the class can support both
|
||||
atomic and non-atomic pointers in a future patch.
|
||||
*/
|
||||
T*
|
||||
unsafeExchange(T* p);
|
||||
|
||||
/**
|
||||
* pointer to the type with an intrusive count
|
||||
*/
|
||||
/** pointer to the type with an intrusive count */
|
||||
T* ptr_{nullptr};
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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"
|
||||
* is called. See the comment on SharedIntrusive for a fuller explanation.
|
||||
*/
|
||||
/** 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"
|
||||
is called. See the comment on SharedIntrusive for a fuller explanation.
|
||||
*/
|
||||
template <class T>
|
||||
class WeakIntrusive
|
||||
{
|
||||
@@ -270,62 +247,54 @@ public:
|
||||
WeakIntrusive&
|
||||
operator=(SharedIntrusive<TT> const& rhs);
|
||||
|
||||
/**
|
||||
* Adopt the raw pointer and increment the weak count.
|
||||
*/
|
||||
/** Adopt the raw pointer and increment the weak count. */
|
||||
void
|
||||
adopt(T* ptr);
|
||||
|
||||
~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
|
||||
* is non-zero before locking.
|
||||
/** 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
|
||||
is non-zero before locking.
|
||||
*/
|
||||
SharedIntrusive<T>
|
||||
lock() const;
|
||||
|
||||
/**
|
||||
* Return true if the strong count is zero.
|
||||
*/
|
||||
/** Return true if the strong count is zero. */
|
||||
[[nodiscard]] bool
|
||||
expired() const;
|
||||
|
||||
/**
|
||||
* Set the pointer to null and decrement the weak count.
|
||||
*
|
||||
* Note: This may run the destructor if the strong count is zero.
|
||||
*/
|
||||
/** Set the pointer to null and decrement the weak count.
|
||||
|
||||
Note: This may run the destructor if the strong count is zero.
|
||||
*/
|
||||
void
|
||||
reset();
|
||||
|
||||
private:
|
||||
T* ptr_ = nullptr;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** 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.
|
||||
*/
|
||||
void
|
||||
unsafeReleaseNoStore();
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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
|
||||
* determine if the raw pointer represents a strong pointer or a weak
|
||||
* pointer. It can also be quickly switched between its strong pointer and
|
||||
* weak pointer representations. This class is useful for storing intrusive
|
||||
* pointers in tagged caches.
|
||||
*/
|
||||
/** 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
|
||||
determine if the raw pointer represents a strong pointer or a weak
|
||||
pointer. It can also be quickly switched between its strong pointer and
|
||||
weak pointer representations. This class is useful for storing intrusive
|
||||
pointers in tagged caches.
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
class SharedWeakUnion
|
||||
@@ -367,83 +336,69 @@ public:
|
||||
|
||||
~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
|
||||
* 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]] SharedIntrusive<T>
|
||||
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
|
||||
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
|
||||
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*
|
||||
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
|
||||
*/
|
||||
[[nodiscard]] std::size_t
|
||||
useCount() const;
|
||||
|
||||
/**
|
||||
* Return true if there is a non-zero strong count.
|
||||
*/
|
||||
/** Return true if there is a non-zero strong count. */
|
||||
[[nodiscard]] bool
|
||||
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>
|
||||
lock() const;
|
||||
|
||||
/**
|
||||
* Return true is this represents a strong pointer.
|
||||
*/
|
||||
/** Return true is this represents a strong pointer. */
|
||||
[[nodiscard]] bool
|
||||
isStrong() const;
|
||||
|
||||
/**
|
||||
* Return true is this represents a weak pointer.
|
||||
*/
|
||||
/** Return true is this represents a weak pointer. */
|
||||
[[nodiscard]] bool
|
||||
isWeak() const;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** 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.
|
||||
*/
|
||||
bool
|
||||
convertToStrong();
|
||||
|
||||
/**
|
||||
* If this is a strong pointer, attempt to convert it to a weak
|
||||
* pointer.
|
||||
*
|
||||
* @return false if the pointer is null. Otherwise return true.
|
||||
*/
|
||||
/** If this is a strong pointer, attempt to convert it to a weak
|
||||
pointer.
|
||||
|
||||
@return false if the pointer is null. Otherwise return true.
|
||||
*/
|
||||
bool
|
||||
convertToWeak();
|
||||
|
||||
@@ -456,27 +411,23 @@ private:
|
||||
static constexpr std::uintptr_t kPtrMask = ~kTagMask;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Return the raw pointer held by this object.
|
||||
/** Return the raw pointer held by this object.
|
||||
*/
|
||||
[[nodiscard]] T*
|
||||
unsafeGetRawPtr() const;
|
||||
|
||||
enum class RefStrength { Strong, Weak };
|
||||
/**
|
||||
* Set the raw pointer and tag bit directly.
|
||||
/** Set the raw pointer and tag bit directly.
|
||||
*/
|
||||
void
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
unsafeReleaseNoStore();
|
||||
@@ -484,13 +435,12 @@ private:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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
|
||||
* pointers.
|
||||
*/
|
||||
/** 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
|
||||
pointers.
|
||||
*/
|
||||
template <class TT, class... Args>
|
||||
SharedIntrusive<TT>
|
||||
makeSharedIntrusive(Args&&... args)
|
||||
|
||||
@@ -641,9 +641,6 @@ template <class T>
|
||||
T*
|
||||
SharedWeakUnion<T>::unsafeGetRawPtr() const
|
||||
{
|
||||
// tp_ packs a raw pointer together with a strength bit; recovering the
|
||||
// pointer inherently requires an integer-to-pointer cast.
|
||||
// NOLINTNEXTLINE(performance-no-int-to-ptr)
|
||||
return reinterpret_cast<T*>(tp_ & kPtrMask);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,38 +8,35 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
/** 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.
|
||||
|
||||
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.
|
||||
*/
|
||||
enum class ReleaseStrongRefAction { NoOp, PartialDestroy, Destroy };
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
/** 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.
|
||||
|
||||
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 };
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** 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.
|
||||
*/
|
||||
struct IntrusiveRefCounts
|
||||
{
|
||||
virtual ~IntrusiveRefCounts() noexcept;
|
||||
@@ -108,123 +105,109 @@ private:
|
||||
static constexpr size_t kFieldTypeBits = sizeof(FieldType) * 8;
|
||||
static constexpr FieldType kOne = 1;
|
||||
|
||||
/**
|
||||
* `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,
|
||||
* if the weak count is zero, the destructor is run. If the weak count is
|
||||
* 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
|
||||
* 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
|
||||
* started). This is used to prevent the destructor from running
|
||||
* concurrently with the partial destructor. This can easily happen when
|
||||
* the last strong pointer release its reference in one thread and starts
|
||||
* the partialDestructor, while in another thread the last weak pointer
|
||||
* goes out of scope and starts the destructor while the partialDestructor
|
||||
* is still running. Both a start and finished bit is needed to handle a
|
||||
* corner-case where the last strong pointer goes out of scope, then then
|
||||
* last `weakPointer` goes out of scope, but this happens before the
|
||||
* `partialDestructor` bit is set. It would be possible to use a single
|
||||
* bit if it could also be set atomically when the strong count goes to
|
||||
* 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
|
||||
* information.
|
||||
*/
|
||||
/** `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,
|
||||
if the weak count is zero, the destructor is run. If the weak count is
|
||||
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
|
||||
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
|
||||
started). This is used to prevent the destructor from running
|
||||
concurrently with the partial destructor. This can easily happen when
|
||||
the last strong pointer release its reference in one thread and starts
|
||||
the partialDestructor, while in another thread the last weak pointer
|
||||
goes out of scope and starts the destructor while the partialDestructor
|
||||
is still running. Both a start and finished bit is needed to handle a
|
||||
corner-case where the last strong pointer goes out of scope, then then
|
||||
last `weakPointer` goes out of scope, but this happens before the
|
||||
`partialDestructor` bit is set. It would be possible to use a single
|
||||
bit if it could also be set atomically when the strong count goes to
|
||||
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
|
||||
information.
|
||||
|
||||
*/
|
||||
|
||||
mutable std::atomic<FieldType> refCounts_{kStrongDelta};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
/** 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
|
||||
*/
|
||||
static constexpr FieldType kStrongDelta = 1;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
/** 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
|
||||
*/
|
||||
static constexpr FieldType kWeakDelta = (kOne << kStrongCountNumBits);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** 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.
|
||||
*/
|
||||
static constexpr FieldType kPartialDestroyStartedMask = (kOne << (kFieldTypeBits - 1));
|
||||
|
||||
/**
|
||||
* Flag that is set when the partialDestroy function has finished running
|
||||
*
|
||||
* See description of the `refCounts` field for a fuller description of
|
||||
* this field.
|
||||
*/
|
||||
/** Flag that is set when the partialDestroy function has finished running
|
||||
|
||||
See description of the `refCounts` field for a fuller description of
|
||||
this field.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Mask that will zero out everything except the weak count.
|
||||
/** Mask that will zero out everything except the weak count.
|
||||
*/
|
||||
static constexpr FieldType kWeakMask =
|
||||
(((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
|
||||
{
|
||||
CountType strong;
|
||||
CountType weak;
|
||||
/**
|
||||
* The `partialDestroyStartedBit` is set to on when the partial
|
||||
* destroy function is started. It is not a boolean; it is a uint32
|
||||
* with all bits zero with the possible exception of the
|
||||
* `partialDestroyStartedMask` bit. This is done so it can be directly
|
||||
* masked into the `combinedValue`.
|
||||
/** The `partialDestroyStartedBit` is set to on when the partial
|
||||
destroy function is started. It is not a boolean; it is a uint32
|
||||
with all bits zero with the possible exception of the
|
||||
`partialDestroyStartedMask` bit. This is done so it can be directly
|
||||
masked into the `combinedValue`.
|
||||
*/
|
||||
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};
|
||||
RefCountPair(FieldType v) noexcept;
|
||||
RefCountPair(CountType s, CountType w) noexcept;
|
||||
|
||||
/**
|
||||
* Convert back to the packed integer form.
|
||||
*/
|
||||
/** Convert back to the packed integer form. */
|
||||
[[nodiscard]] FieldType
|
||||
combinedValue() const noexcept;
|
||||
|
||||
@@ -232,10 +215,9 @@ private:
|
||||
static_cast<CountType>((kOne << kStrongCountNumBits) - 1);
|
||||
static constexpr CountType kMaxWeakValue =
|
||||
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
|
||||
* number of bits in the strong and weak counts (to 16 and 14 bits).
|
||||
/** 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
|
||||
number of bits in the strong and weak counts (to 16 and 14 bits).
|
||||
*/
|
||||
static constexpr CountType kCheckStrongMaxValue = kMaxStrongValue - 32;
|
||||
static constexpr CountType kCheckWeakMaxValue = kMaxWeakValue - 32;
|
||||
|
||||
@@ -70,15 +70,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores instance of T specific to the calling coroutine or thread.
|
||||
*/
|
||||
/** Stores instance of T specific to the calling coroutine or thread. */
|
||||
T&
|
||||
operator*();
|
||||
|
||||
/**
|
||||
* Stores instance of T specific to the calling coroutine or thread.
|
||||
*/
|
||||
/** Stores instance of T specific to the calling coroutine or thread. */
|
||||
T*
|
||||
operator->()
|
||||
{
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* Manages partitions for logging.
|
||||
*/
|
||||
/** Manages partitions for logging. */
|
||||
class Logs
|
||||
{
|
||||
private:
|
||||
@@ -42,81 +40,69 @@ private:
|
||||
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
|
||||
* are provided for interoperating with standard log management
|
||||
* tools like logrotate(8):
|
||||
* http://linuxcommand.org/man_pages/logrotate8.html
|
||||
* @note None of the listed interfaces are thread-safe.
|
||||
*/
|
||||
/** Manages a system file containing logged output.
|
||||
The system file remains open during program execution. Interfaces
|
||||
are provided for interoperating with standard log management
|
||||
tools like logrotate(8):
|
||||
http://linuxcommand.org/man_pages/logrotate8.html
|
||||
@note None of the listed interfaces are thread-safe.
|
||||
*/
|
||||
class File
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct with no associated system file.
|
||||
* A system file may be associated later with @ref open.
|
||||
* @see open
|
||||
*/
|
||||
/** Construct with no associated system file.
|
||||
A system file may be associated later with @ref open.
|
||||
@see open
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Determine if a system file is associated with the log.
|
||||
* @return `true` if a system file is associated and opened for
|
||||
* writing.
|
||||
*/
|
||||
/** Determine if a system file is associated with the log.
|
||||
@return `true` if a system file is associated and opened for
|
||||
writing.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isOpen() const noexcept;
|
||||
|
||||
/**
|
||||
* Associate a system file with the log.
|
||||
* If the file does not exist an attempt is made to create it
|
||||
* and open it for writing. If the file already exists an attempt is
|
||||
* made to open it for appending.
|
||||
* If a system file is already associated with the log, it is closed
|
||||
* first.
|
||||
* @return `true` if the file was opened.
|
||||
*/
|
||||
/** Associate a system file with the log.
|
||||
If the file does not exist an attempt is made to create it
|
||||
and open it for writing. If the file already exists an attempt is
|
||||
made to open it for appending.
|
||||
If a system file is already associated with the log, it is closed
|
||||
first.
|
||||
@return `true` if the file was opened.
|
||||
*/
|
||||
bool
|
||||
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.
|
||||
* @return `true` if the file was opened.
|
||||
*/
|
||||
/** Close and re-open the system file associated with the log
|
||||
This assists in interoperating with external log management tools.
|
||||
@return `true` if the file was opened.
|
||||
*/
|
||||
bool
|
||||
closeAndReopen();
|
||||
|
||||
/**
|
||||
* Close the system file if it is open.
|
||||
*/
|
||||
/** Close the system file if it is open. */
|
||||
void
|
||||
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
|
||||
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
|
||||
writeln(char const* text);
|
||||
|
||||
/**
|
||||
* Write to the log file using std::string.
|
||||
*/
|
||||
/** Write to the log file using std::string. */
|
||||
/** @{ */
|
||||
void
|
||||
write(std::string const& str)
|
||||
@@ -237,21 +223,19 @@ private:
|
||||
//------------------------------------------------------------------------------
|
||||
// Debug logging:
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** 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.
|
||||
*/
|
||||
std::unique_ptr<beast::Journal::Sink>
|
||||
setDebugLogSink(std::unique_ptr<beast::Journal::Sink> sink);
|
||||
|
||||
/**
|
||||
* Returns a debug journal.
|
||||
* The journal may drain to a null sink, so its output
|
||||
* may never be seen. Never use it for critical
|
||||
* information.
|
||||
*/
|
||||
/** Returns a debug journal.
|
||||
The journal may drain to a null sink, so its output
|
||||
may never be seen. Never use it for critical
|
||||
information.
|
||||
*/
|
||||
beast::Journal
|
||||
debugLog();
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
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]
|
||||
* E.g. calculatePercent(1, 100) = 1 because 1/100 = 0.010000
|
||||
* calculatePercent(1, 99) = 2 because 1/99 = 0.010101
|
||||
@@ -20,7 +19,7 @@ namespace xrpl {
|
||||
* @return the percentage, in [0, 100]
|
||||
*
|
||||
* @note total cannot be zero.
|
||||
*/
|
||||
* */
|
||||
constexpr std::size_t
|
||||
calculatePercent(std::size_t count, std::size_t total)
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -47,54 +48,46 @@ isPowerOfTen(T value)
|
||||
|
||||
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
|
||||
* 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
|
||||
* inefficient.
|
||||
*/
|
||||
constexpr std::size_t kUint64Digits = 20;
|
||||
[[maybe_unused]] constexpr std::size_t kUint128Digits = 39;
|
||||
|
||||
template <typename T, std::size_t Digits>
|
||||
consteval std::array<T, Digits>
|
||||
constexpr std::size_t kInt64Digits = 20;
|
||||
consteval std::array<std::uint64_t, kInt64Digits>
|
||||
buildPowersOfTen()
|
||||
{
|
||||
std::array<T, Digits> result{};
|
||||
std::array<std::uint64_t, kInt64Digits> result{};
|
||||
|
||||
T power = 1;
|
||||
std::uint64_t power = 1;
|
||||
std::size_t exponent = 0;
|
||||
// end the loop early so it doesn't overflow;
|
||||
for (; exponent < result.size() - 1; ++exponent, power *= 10)
|
||||
{
|
||||
result[exponent] = power;
|
||||
if (power > std::numeric_limits<T>::max() / 10)
|
||||
if (power > std::numeric_limits<std::uint64_t>::max() / 10)
|
||||
throw std::logic_error("Power of 10 table is too big");
|
||||
}
|
||||
result[exponent] = power;
|
||||
if (power < std::numeric_limits<T>::max() / 10)
|
||||
throw std::logic_error("Power of 10 table is not big enough for the given type");
|
||||
if (power < std::numeric_limits<std::uint64_t>::max() / 10)
|
||||
throw std::logic_error("Power of 10 table is not big enough for the uint64_t type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
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>;
|
||||
constexpr std::array<std::uint64_t, detail::kInt64Digits> kPowerOfTen = detail::buildPowersOfTen();
|
||||
|
||||
static_assert(kPowerOfTen[0] == 1);
|
||||
static_assert(kPowerOfTen[1] == 10);
|
||||
static_assert(kPowerOfTen[10] == 10'000'000'000);
|
||||
static_assert(
|
||||
isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kUint64Digits - 1);
|
||||
isPowerOfTen(kPowerOfTen.back()) && *logTen(kPowerOfTen.back()) == detail::kInt64Digits - 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
|
||||
* * min is a power of 10, and
|
||||
@@ -131,37 +124,17 @@ struct MantissaRange final
|
||||
{
|
||||
using rep = std::uint64_t;
|
||||
|
||||
// NOLINTBEGIN(readability-enum-initial-value)
|
||||
// The values don't matter, except for Large
|
||||
enum class MantissaScale {
|
||||
// Small can be removed when either featureSingleAssetVault or featureLendingProtocol are
|
||||
// retired
|
||||
Small,
|
||||
// LargeLegacy can be removed when fixCleanup3_2_0 is retired
|
||||
LargeLegacy,
|
||||
// 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,
|
||||
Large,
|
||||
};
|
||||
// NOLINTEND(readability-enum-initial-value)
|
||||
|
||||
// This entire enum can be removed when the last relevant amendment is retired
|
||||
enum class CuspRoundingFix : std::uint8_t {
|
||||
// Disabled can be removed when fixCleanup3_2_0 is retired
|
||||
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,
|
||||
// This entire enum can be removed when fixCleanup3_2_0 is retired
|
||||
enum class CuspRoundingFix : bool {
|
||||
Disabled = false,
|
||||
Enabled = true,
|
||||
};
|
||||
|
||||
explicit constexpr MantissaRange(MantissaScale sc) : scale(sc)
|
||||
@@ -172,27 +145,13 @@ struct MantissaRange final
|
||||
int const log{getExponent(scale)};
|
||||
rep const min{getMin(scale, log)};
|
||||
rep const max{(min * 10) - 1};
|
||||
CuspRoundingFix const cuspRoundingFix{isCuspFixEnabled(scale)};
|
||||
CuspRoundingFix const cuspRoundingFixEnabled{isCuspFixEnabled(scale)};
|
||||
|
||||
static MantissaRange const&
|
||||
getMantissaRange(MantissaScale scale);
|
||||
|
||||
static std::set<MantissaScale> const&
|
||||
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;
|
||||
};
|
||||
getAllScales();
|
||||
|
||||
private:
|
||||
static constexpr int
|
||||
@@ -203,8 +162,7 @@ private:
|
||||
case MantissaScale::Small:
|
||||
return 15;
|
||||
case MantissaScale::LargeLegacy:
|
||||
case MantissaScale::Large320:
|
||||
case MantissaScale::Large330:
|
||||
case MantissaScale::Large:
|
||||
return 18;
|
||||
// LCOV_EXCL_START
|
||||
default:
|
||||
@@ -233,24 +191,24 @@ private:
|
||||
case MantissaScale::Small:
|
||||
case MantissaScale::LargeLegacy:
|
||||
return CuspRoundingFix::Disabled;
|
||||
case MantissaScale::Large320:
|
||||
return CuspRoundingFix::Enabled320;
|
||||
case MantissaScale::Large330:
|
||||
return CuspRoundingFix::Enabled330;
|
||||
case MantissaScale::Large:
|
||||
return CuspRoundingFix::Enabled;
|
||||
default:
|
||||
// If called in a constexpr context, this throw assures that the build fails if an
|
||||
// invalid scale is used.
|
||||
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.
|
||||
template <class 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 -
|
||||
* regardless of asset type - XRPAmount, MPTAmount, and IOUAmount, with at least
|
||||
@@ -346,6 +304,7 @@ 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
|
||||
* (SingleAssetVault and LendingProtocol), and/or check if either of those
|
||||
* amendments are enabled to determine which result to expect.
|
||||
*
|
||||
*/
|
||||
class Number final
|
||||
{
|
||||
@@ -431,11 +390,10 @@ public:
|
||||
static Number
|
||||
lowest() noexcept;
|
||||
|
||||
/**
|
||||
* Conversions to Number are implicit and conversions away from Number
|
||||
* are explicit. This design encourages and facilitates the use of Number
|
||||
* as the preferred type for floating point arithmetic as it makes
|
||||
* "mixed mode" more convenient, e.g. MPTAmount + Number.
|
||||
/** Conversions to Number are implicit and conversions away from Number
|
||||
* are explicit. This design encourages and facilitates the use of Number
|
||||
* as the preferred type for floating point arithmetic as it makes
|
||||
* "mixed mode" more convenient, e.g. MPTAmount + Number.
|
||||
*/
|
||||
explicit
|
||||
operator rep() const; // round to nearest, even on tie
|
||||
@@ -490,9 +448,7 @@ public:
|
||||
return l.mantissa_ < r.mantissa_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sign of the amount
|
||||
*/
|
||||
/** Return the sign of the amount */
|
||||
[[nodiscard]] constexpr int
|
||||
signum() const noexcept
|
||||
{
|
||||
@@ -546,16 +502,14 @@ public:
|
||||
static RoundingMode
|
||||
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.
|
||||
*/
|
||||
static MantissaRange::MantissaScale
|
||||
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.
|
||||
*/
|
||||
@@ -600,17 +554,10 @@ private:
|
||||
// changing the values inside the range.
|
||||
static thread_local std::reference_wrapper<MantissaRange const> kRange;
|
||||
|
||||
class Guard;
|
||||
|
||||
void
|
||||
normalize(MantissaRange const& 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.
|
||||
/** Normalize Number components to an arbitrary range.
|
||||
*
|
||||
* min/maxMantissa are parameters because this function is used by both
|
||||
* normalize(), which reads from kRange, and by normalizeToRange,
|
||||
@@ -624,7 +571,7 @@ private:
|
||||
int& exponent,
|
||||
internalrep const& minMantissa,
|
||||
internalrep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix);
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled);
|
||||
|
||||
template <class T>
|
||||
friend void
|
||||
@@ -634,7 +581,7 @@ private:
|
||||
int& exponent,
|
||||
MantissaRange::rep const& minMantissa,
|
||||
MantissaRange::rep const& maxMantissa,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFix,
|
||||
MantissaRange::CuspRoundingFix cuspRoundingFixEnabled,
|
||||
bool dropped);
|
||||
|
||||
[[nodiscard]] bool
|
||||
@@ -652,6 +599,8 @@ private:
|
||||
// UB, and can vary across compilers.
|
||||
static internalrep
|
||||
externalToInternal(rep mantissa);
|
||||
|
||||
class Guard;
|
||||
};
|
||||
|
||||
constexpr Number::Number(bool negative, internalrep mantissa, int exponent, Unchecked) noexcept
|
||||
@@ -686,8 +635,7 @@ 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
|
||||
* documentation for an explanation of why the internal value may be modified.
|
||||
@@ -708,8 +656,7 @@ Number::mantissa() const noexcept
|
||||
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
|
||||
* documentation for an explanation of why the internal value may be modified.
|
||||
@@ -915,11 +862,21 @@ squelch(Number const& x, Number const& limit) noexcept
|
||||
return x;
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(MantissaRange::MantissaScale const& scale);
|
||||
|
||||
std::string
|
||||
to_string(Number::RoundingMode const& round);
|
||||
inline std::string
|
||||
to_string(MantissaRange::MantissaScale const& scale)
|
||||
{
|
||||
switch (scale)
|
||||
{
|
||||
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
|
||||
{
|
||||
@@ -958,10 +915,10 @@ public:
|
||||
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.
|
||||
*
|
||||
*/
|
||||
class NumberMantissaScaleGuard
|
||||
{
|
||||
|
||||
@@ -13,25 +13,23 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* 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().
|
||||
*
|
||||
* This is simply a type-alias for boost interval container library interval
|
||||
* set, so users should consult that documentation for available supporting
|
||||
* member and free functions.
|
||||
*/
|
||||
/** 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().
|
||||
|
||||
This is simply a type-alias for boost interval container library interval
|
||||
set, so users should consult that documentation for available supporting
|
||||
member and free functions.
|
||||
*/
|
||||
template <class T>
|
||||
using ClosedInterval = boost::icl::closed_interval<T>;
|
||||
|
||||
/**
|
||||
* Create a closed range interval
|
||||
*
|
||||
* Helper function to create a closed range interval without having to qualify
|
||||
* the template argument.
|
||||
*/
|
||||
/** Create a closed range interval
|
||||
|
||||
Helper function to create a closed range interval without having to qualify
|
||||
the template argument.
|
||||
*/
|
||||
template <class T>
|
||||
ClosedInterval<T>
|
||||
range(T low, T high)
|
||||
@@ -39,30 +37,28 @@ range(T low, T high)
|
||||
return ClosedInterval<T>(low, high);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* member and free functions.
|
||||
*/
|
||||
/** 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
|
||||
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
|
||||
member and free functions.
|
||||
*/
|
||||
template <class T>
|
||||
using RangeSet = boost::icl::interval_set<T, std::less, ClosedInterval<T>>;
|
||||
|
||||
/**
|
||||
* Convert a ClosedInterval to a styled string
|
||||
*
|
||||
* The styled string is
|
||||
* "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
|
||||
*/
|
||||
/** Convert a ClosedInterval to a styled string
|
||||
|
||||
The styled string is
|
||||
"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
|
||||
*/
|
||||
template <class T>
|
||||
std::string
|
||||
to_string(ClosedInterval<T> const& ci)
|
||||
@@ -72,15 +68,14 @@ to_string(ClosedInterval<T> const& ci)
|
||||
return std::to_string(ci.first()) + "-" + std::to_string(ci.last());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param rs The rangeset to convert
|
||||
* @return The 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.
|
||||
|
||||
@param rs The rangeset to convert
|
||||
@return The styled string
|
||||
*/
|
||||
template <class T>
|
||||
std::string
|
||||
to_string(RangeSet<T> const& rs)
|
||||
@@ -96,16 +91,15 @@ to_string(RangeSet<T> const& rs)
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given styled string to a RangeSet.
|
||||
*
|
||||
* 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
|
||||
* @return True on successfully converting styled string
|
||||
*/
|
||||
/** Convert the given styled string to a RangeSet.
|
||||
|
||||
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
|
||||
@return True on successfully converting styled string
|
||||
*/
|
||||
template <class T>
|
||||
[[nodiscard]] bool
|
||||
fromString(RangeSet<T>& rs, std::string const& s)
|
||||
@@ -167,15 +161,14 @@ fromString(RangeSet<T>& rs, std::string const& s)
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 minVal (Default is 0) The smallest allowed value
|
||||
* @return The largest v such that minV <= v < t and !contains(rs, v) or
|
||||
* std::nullopt if no such v exists.
|
||||
*/
|
||||
/** 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 minVal (Default is 0) The smallest allowed value
|
||||
@return The largest v such that minV <= v < t and !contains(rs, v) or
|
||||
std::nullopt if no such v exists.
|
||||
*/
|
||||
template <class T>
|
||||
std::optional<T>
|
||||
prevMissing(RangeSet<T> const& rs, T t, T minVal = 0)
|
||||
|
||||
@@ -15,29 +15,22 @@ public:
|
||||
|
||||
virtual ~Resolver() = 0;
|
||||
|
||||
/**
|
||||
* Issue an asynchronous stop request.
|
||||
*/
|
||||
/** Issue an asynchronous stop request. */
|
||||
virtual void
|
||||
stopAsync() = 0;
|
||||
|
||||
/**
|
||||
* Issue a synchronous stop request.
|
||||
*/
|
||||
/** Issue a synchronous stop request. */
|
||||
virtual void
|
||||
stop() = 0;
|
||||
|
||||
/**
|
||||
* Issue a synchronous start request.
|
||||
*/
|
||||
/** Issue a synchronous start request. */
|
||||
virtual void
|
||||
start() = 0;
|
||||
|
||||
/**
|
||||
* resolve all hostnames on the list
|
||||
* @param names the names to be resolved
|
||||
* @param handler the handler to call
|
||||
*/
|
||||
/** resolve all hostnames on the list
|
||||
@param names the names to be resolved
|
||||
@param handler the handler to call
|
||||
*/
|
||||
/** @{ */
|
||||
template <class Handler>
|
||||
void
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* memory than storing both pointers directly.
|
||||
*/
|
||||
/** 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
|
||||
memory than storing both pointers directly.
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
class SharedWeakCachePointer
|
||||
@@ -49,79 +48,65 @@ public:
|
||||
|
||||
~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&
|
||||
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
|
||||
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
|
||||
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*
|
||||
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
|
||||
useCount() const;
|
||||
|
||||
/**
|
||||
* Return true if there is a non-zero strong count.
|
||||
*/
|
||||
/** Return true if there is a non-zero strong count. */
|
||||
[[nodiscard]] bool
|
||||
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>
|
||||
lock() const;
|
||||
|
||||
/**
|
||||
* Return true is this represents a strong pointer.
|
||||
*/
|
||||
/** Return true is this represents a strong pointer. */
|
||||
[[nodiscard]] bool
|
||||
isStrong() const;
|
||||
|
||||
/**
|
||||
* Return true is this represents a weak pointer.
|
||||
*/
|
||||
/** Return true is this represents a weak pointer. */
|
||||
[[nodiscard]] bool
|
||||
isWeak() const;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** 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.
|
||||
*/
|
||||
bool
|
||||
convertToStrong();
|
||||
|
||||
/**
|
||||
* If this is a strong pointer, attempt to convert it to a weak pointer.
|
||||
*
|
||||
* @return false if the pointer is null. Otherwise return true.
|
||||
*/
|
||||
/** If this is a strong pointer, attempt to convert it to a weak pointer.
|
||||
|
||||
@return false if the pointer is null. Otherwise return true.
|
||||
*/
|
||||
bool
|
||||
convertToWeak();
|
||||
|
||||
|
||||
@@ -33,9 +33,7 @@ class SlabAllocator
|
||||
|
||||
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
|
||||
{
|
||||
// A mutex to protect the freelist for this block:
|
||||
@@ -82,9 +80,7 @@ class SlabAllocator
|
||||
SlabBlock&
|
||||
operator=(SlabBlock&& other) = delete;
|
||||
|
||||
/**
|
||||
* Determines whether the given pointer belongs to this allocator
|
||||
*/
|
||||
/** Determines whether the given pointer belongs to this allocator */
|
||||
bool
|
||||
own(std::uint8_t const* pIn) const noexcept
|
||||
{
|
||||
@@ -111,15 +107,14 @@ class SlabAllocator
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an item to this allocator's freelist.
|
||||
*
|
||||
* @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
|
||||
* will check and assert if this is not the case. Release
|
||||
* builds will not.
|
||||
/** Return an item to this allocator's freelist.
|
||||
|
||||
@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
|
||||
will check and assert if this is not the case. Release
|
||||
builds will not.
|
||||
*/
|
||||
void
|
||||
deallocate(std::uint8_t* ptr) noexcept
|
||||
@@ -150,14 +145,13 @@ private:
|
||||
std::size_t const slabSize_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* 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
|
||||
* is, effectively, disabled. This can be very useful in some
|
||||
* contexts (e.g. when minimal memory usage is needed) and
|
||||
* allows for graceful failure.
|
||||
/** 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
|
||||
is, effectively, disabled. This can be very useful in some
|
||||
contexts (e.g. when minimal memory usage is needed) and
|
||||
allows for graceful failure.
|
||||
*/
|
||||
constexpr explicit SlabAllocator(
|
||||
std::size_t extra,
|
||||
@@ -185,20 +179,17 @@ public:
|
||||
// shutdown process up could make this possible.
|
||||
~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
|
||||
size() const noexcept
|
||||
{
|
||||
return itemSize_;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
/** 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.
|
||||
*/
|
||||
std::uint8_t*
|
||||
allocate() noexcept
|
||||
@@ -259,13 +250,12 @@ public:
|
||||
return slab->allocate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return true if this memory block belonged to the allocator and has
|
||||
* been released; false otherwise.
|
||||
/** 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.
|
||||
@return true if this memory block belonged to the allocator and has
|
||||
been released; false otherwise.
|
||||
*/
|
||||
bool
|
||||
deallocate(std::uint8_t* ptr) noexcept
|
||||
@@ -288,9 +278,7 @@ 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>
|
||||
class SlabAllocatorSet
|
||||
{
|
||||
@@ -357,14 +345,13 @@ public:
|
||||
|
||||
~SlabAllocatorSet() = default;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return a pointer to a block of memory, or nullptr if the allocator
|
||||
* can't satisfy this request.
|
||||
/** 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.
|
||||
|
||||
@return a pointer to a block of memory, or nullptr if the allocator
|
||||
can't satisfy this request.
|
||||
*/
|
||||
std::uint8_t*
|
||||
allocate(std::size_t extra) noexcept
|
||||
@@ -381,13 +368,12 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the memory block to the allocator.
|
||||
*
|
||||
* @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.
|
||||
/** Returns the memory block to the allocator.
|
||||
|
||||
@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.
|
||||
*/
|
||||
bool
|
||||
deallocate(std::uint8_t* ptr) noexcept
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* of the underlying memory.
|
||||
*/
|
||||
/** 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
|
||||
of the underlying memory.
|
||||
*/
|
||||
class Slice
|
||||
{
|
||||
private:
|
||||
@@ -33,37 +32,30 @@ public:
|
||||
using value_type = std::uint8_t;
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
/**
|
||||
* Default constructed Slice has length 0.
|
||||
*/
|
||||
/** Default constructed Slice has length 0. */
|
||||
Slice() noexcept = default;
|
||||
|
||||
Slice(Slice const&) noexcept = default;
|
||||
Slice&
|
||||
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
|
||||
: 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
|
||||
empty() const noexcept
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in the storage.
|
||||
*
|
||||
* This may be zero for an empty range.
|
||||
*/
|
||||
/** Returns the number of bytes in the storage.
|
||||
|
||||
This may be zero for an empty range.
|
||||
*/
|
||||
/** @{ */
|
||||
[[nodiscard]] std::size_t
|
||||
size() const noexcept
|
||||
@@ -78,20 +70,17 @@ public:
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Return a pointer to beginning of the storage.
|
||||
* @note The return type is guaranteed to be a pointer
|
||||
* to a single byte, to facilitate pointer arithmetic.
|
||||
*/
|
||||
/** Return a pointer to beginning of the storage.
|
||||
@note The return type is guaranteed to be a pointer
|
||||
to a single byte, to facilitate pointer arithmetic.
|
||||
*/
|
||||
[[nodiscard]] std::uint8_t const*
|
||||
data() const noexcept
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access raw bytes.
|
||||
*/
|
||||
/** Access raw bytes. */
|
||||
std::uint8_t
|
||||
operator[](std::size_t i) const noexcept
|
||||
{
|
||||
@@ -99,9 +88,7 @@ public:
|
||||
return data_[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance the buffer.
|
||||
*/
|
||||
/** Advance the buffer. */
|
||||
/** @{ */
|
||||
Slice&
|
||||
operator+=(std::size_t n)
|
||||
@@ -121,9 +108,7 @@ public:
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Shrinks the slice by moving its start forward by n characters.
|
||||
*/
|
||||
/** Shrinks the slice by moving its start forward by n characters. */
|
||||
void
|
||||
removePrefix(std::size_t n)
|
||||
{
|
||||
@@ -131,9 +116,7 @@ public:
|
||||
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
|
||||
removeSuffix(std::size_t n)
|
||||
{
|
||||
@@ -164,17 +147,16 @@ public:
|
||||
return data_ + size_;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param pos position of the first character
|
||||
* @count requested length
|
||||
*
|
||||
* @return The requested subslice, if the request is valid.
|
||||
* @throws std::out_of_range if pos > size()
|
||||
/** 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.
|
||||
|
||||
@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()
|
||||
*/
|
||||
[[nodiscard]] Slice
|
||||
substr(std::size_t pos, std::size_t count = std::numeric_limits<std::size_t>::max()) const
|
||||
@@ -229,17 +211,15 @@ operator<<(Stream& s, Slice const& v)
|
||||
}
|
||||
|
||||
template <class T, std::size_t N>
|
||||
Slice
|
||||
std::enable_if_t<std::is_same_v<T, char> || std::is_same_v<T, unsigned char>, Slice>
|
||||
makeSlice(std::array<T, N> const& a)
|
||||
requires(std::is_same_v<T, char> || std::is_same_v<T, unsigned char>)
|
||||
{
|
||||
return Slice(a.data(), a.size());
|
||||
}
|
||||
|
||||
template <class T, class Alloc>
|
||||
Slice
|
||||
std::enable_if_t<std::is_same_v<T, char> || std::is_same_v<T, unsigned char>, Slice>
|
||||
makeSlice(std::vector<T, Alloc> const& v)
|
||||
requires(std::is_same_v<T, char> || std::is_same_v<T, unsigned char>)
|
||||
{
|
||||
return Slice(v.data(), v.size());
|
||||
}
|
||||
|
||||
@@ -17,16 +17,15 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
/** 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
|
||||
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.
|
||||
*/
|
||||
std::string
|
||||
sqlBlobLiteral(Blob const& blob);
|
||||
@@ -131,12 +130,11 @@ trimWhitespace(std::string str);
|
||||
std::optional<std::uint64_t>
|
||||
toUInt64(std::string const& s);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* doesn't check whether the TLD is valid.
|
||||
/** 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
|
||||
doesn't check whether the TLD is valid.
|
||||
*/
|
||||
bool
|
||||
isProperlyFormedTomlDomain(std::string_view domain);
|
||||
|
||||
@@ -41,19 +41,18 @@ struct ReplaceDynamically;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Map/cache combination.
|
||||
* This class implements a cache and a map. The cache keeps objects alive
|
||||
* 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,
|
||||
* 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.
|
||||
*/
|
||||
/** Map/cache combination.
|
||||
This class implements a cache and a map. The cache keeps objects alive
|
||||
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,
|
||||
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.
|
||||
*/
|
||||
template <
|
||||
class Key,
|
||||
class T,
|
||||
@@ -83,15 +82,11 @@ public:
|
||||
beast::insight::Collector::ptr const& collector = beast::insight::NullCollector::make());
|
||||
|
||||
public:
|
||||
/**
|
||||
* Return the clock associated with the cache.
|
||||
*/
|
||||
/** Return the clock associated with the cache. */
|
||||
clock_type&
|
||||
clock();
|
||||
|
||||
/**
|
||||
* Returns the number of items in the container.
|
||||
*/
|
||||
/** Returns the number of items in the container. */
|
||||
std::size_t
|
||||
size() const;
|
||||
|
||||
@@ -110,10 +105,9 @@ public:
|
||||
void
|
||||
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>
|
||||
bool
|
||||
touchIfExists(KeyComparable const& key);
|
||||
@@ -136,15 +130,14 @@ private:
|
||||
SharedPointerType const&,
|
||||
SharedPointerType&>;
|
||||
|
||||
/**
|
||||
* Shared implementation of the canonicalize family.
|
||||
*
|
||||
* `policy` selects how a collision is resolved when `key` already exists:
|
||||
* detail::ReplaceCached, detail::ReplaceClient or
|
||||
* detail::ReplaceDynamically. For ReplaceDynamically `replaceCallback` is
|
||||
* invoked with the existing strong pointer and returns whether to replace
|
||||
* the cached value with `data`; for the tag policies it is unused.
|
||||
*/
|
||||
/** Shared implementation of the canonicalize family.
|
||||
|
||||
`policy` selects how a collision is resolved when `key` already exists:
|
||||
detail::ReplaceCached, detail::ReplaceClient or
|
||||
detail::ReplaceDynamically. For ReplaceDynamically `replaceCallback` is
|
||||
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>
|
||||
bool
|
||||
canonicalizeImpl(
|
||||
@@ -154,82 +147,76 @@ private:
|
||||
Callback&& replaceCallback = nullptr);
|
||||
|
||||
public:
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
* 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 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.
|
||||
*/
|
||||
/** 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.
|
||||
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
|
||||
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 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.
|
||||
**/
|
||||
template <class Callback>
|
||||
bool
|
||||
canonicalize(key_type const& key, SharedPointerType& data, Callback&& replaceCallback);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
/** 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
|
||||
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.
|
||||
|
||||
@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
|
||||
canonicalizeReplaceCache(key_type const& key, SharedPointerType const& data);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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;
|
||||
* 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.
|
||||
*/
|
||||
/** 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
|
||||
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;
|
||||
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.
|
||||
**/
|
||||
bool
|
||||
canonicalizeReplaceClient(key_type const& key, SharedPointerType& data);
|
||||
|
||||
SharedPointerType
|
||||
fetch(key_type const& key);
|
||||
|
||||
/**
|
||||
* Insert the element into the container.
|
||||
* If the key already exists, nothing happens.
|
||||
* @return `true` If the element was inserted
|
||||
*/
|
||||
/** Insert the element into the container.
|
||||
If the key already exists, nothing happens.
|
||||
@return `true` If the element was inserted
|
||||
*/
|
||||
template <class ReturnType = bool>
|
||||
auto
|
||||
insert(key_type const& key, T const& value) -> ReturnType
|
||||
requires(!IsKeyCache);
|
||||
insert(key_type const& key, T const& value) -> std::enable_if_t<!IsKeyCache, ReturnType>;
|
||||
|
||||
template <class ReturnType = bool>
|
||||
auto
|
||||
insert(key_type const& key) -> ReturnType
|
||||
requires IsKeyCache;
|
||||
insert(key_type const& key) -> std::enable_if_t<IsKeyCache, ReturnType>;
|
||||
|
||||
// VFALCO NOTE It looks like this returns a copy of the data in
|
||||
// the output parameter 'data'. This could be expensive.
|
||||
@@ -246,18 +233,15 @@ public:
|
||||
getKeys() const;
|
||||
|
||||
// CachedSLEs functions.
|
||||
/**
|
||||
* Returns the fraction of cache hits.
|
||||
*/
|
||||
/** Returns the fraction of cache hits. */
|
||||
double
|
||||
rate() const;
|
||||
|
||||
/**
|
||||
* Fetch an item from the cache.
|
||||
* If the digest was not found, Handler
|
||||
* will be called with this signature:
|
||||
* SLE::const_pointer(void)
|
||||
*/
|
||||
/** Fetch an item from the cache.
|
||||
If the digest was not found, Handler
|
||||
will be called with this signature:
|
||||
SLE::const_pointer(void)
|
||||
*/
|
||||
template <class Handler>
|
||||
SharedPointerType
|
||||
fetch(key_type const& digest, Handler const& h);
|
||||
|
||||
@@ -57,10 +57,7 @@ inline TaggedCache<
|
||||
beast::insight::Collector::ptr const& collector)
|
||||
: journal_(journal)
|
||||
, clock_(clock)
|
||||
, stats_(
|
||||
name,
|
||||
[this] { collectMetrics(); },
|
||||
collector)
|
||||
, stats_(name, std::bind(&TaggedCache::collectMetrics, this), collector)
|
||||
, name_(name)
|
||||
, targetSize_(size)
|
||||
, targetAge_(expiration)
|
||||
@@ -503,8 +500,7 @@ template <
|
||||
template <class ReturnType>
|
||||
inline auto
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
insert(key_type const& key, T const& value) -> ReturnType
|
||||
requires(!IsKeyCache)
|
||||
insert(key_type const& key, T const& value) -> std::enable_if_t<!IsKeyCache, ReturnType>
|
||||
{
|
||||
static_assert(
|
||||
std::is_same_v<std::shared_ptr<T>, SharedPointerType> ||
|
||||
@@ -534,8 +530,7 @@ template <
|
||||
template <class ReturnType>
|
||||
inline auto
|
||||
TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash, KeyEqual, Mutex>::
|
||||
insert(key_type const& key) -> ReturnType
|
||||
requires IsKeyCache
|
||||
insert(key_type const& key) -> std::enable_if_t<IsKeyCache, ReturnType>
|
||||
{
|
||||
std::scoped_lock const lock(mutex_);
|
||||
clock_type::time_point const now(clock_.now());
|
||||
|
||||
@@ -5,17 +5,15 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
/** 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.
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
std::string
|
||||
std::enable_if_t<std::is_arithmetic_v<T>, std::string>
|
||||
to_string(T t) // NOLINT(readability-identifier-naming)
|
||||
requires(std::is_arithmetic_v<T>)
|
||||
{
|
||||
return std::to_string(t);
|
||||
}
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* per second.
|
||||
*/
|
||||
/** 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
|
||||
per second.
|
||||
*/
|
||||
|
||||
class UptimeClock
|
||||
{
|
||||
|
||||
@@ -63,19 +63,18 @@ struct AlwaysFalseT : std::bool_constant<false>
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 Tag An arbitrary type that functions as a tag and allows
|
||||
* the instantiation of "distinct" types that the same
|
||||
* number of 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
|
||||
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 Tag An arbitrary type that functions as a tag and allows
|
||||
the instantiation of "distinct" types that the same
|
||||
number of bits.
|
||||
*/
|
||||
template <std::size_t Bits, class Tag = void>
|
||||
class BaseUInt
|
||||
@@ -98,7 +97,7 @@ public:
|
||||
//
|
||||
|
||||
static constexpr std::size_t kBytes = Bits / 8;
|
||||
static_assert(sizeof(data_) == kBytes);
|
||||
static_assert(sizeof(data_) == kBytes, "");
|
||||
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
@@ -155,23 +154,21 @@ public:
|
||||
return data() + kBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value hashing function.
|
||||
* The seed prevents crafted inputs from causing degenerate parent
|
||||
* containers.
|
||||
*/
|
||||
/** Value hashing function.
|
||||
The seed prevents crafted inputs from causing degenerate parent
|
||||
containers.
|
||||
*/
|
||||
using hasher = HardenedHash<>;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/** 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.
|
||||
*/
|
||||
// NIKB TODO Remove the need for this constructor.
|
||||
struct VoidHelper
|
||||
{
|
||||
@@ -283,11 +280,12 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
template <class Container>
|
||||
explicit BaseUInt(Container const& c)
|
||||
requires(
|
||||
template <
|
||||
class Container,
|
||||
class = std::enable_if_t<
|
||||
detail::IsContiguousContainer<Container>::value &&
|
||||
std::is_trivially_copyable_v<typename Container::value_type>)
|
||||
std::is_trivially_copyable_v<typename Container::value_type>>>
|
||||
explicit BaseUInt(Container const& c)
|
||||
{
|
||||
// Use AlwaysFalseT so the static_assert condition is dependent
|
||||
// and only triggers when this constructor template is instantiated.
|
||||
@@ -297,12 +295,13 @@ public:
|
||||
"Use base_uint::fromRaw instead.");
|
||||
}
|
||||
|
||||
template <class Container>
|
||||
template <
|
||||
class Container,
|
||||
class = std::enable_if_t<
|
||||
detail::IsContiguousContainer<Container>::value &&
|
||||
std::is_trivially_copyable_v<typename Container::value_type>>>
|
||||
static BaseUInt
|
||||
fromRaw(Container const& c)
|
||||
requires(
|
||||
detail::IsContiguousContainer<Container>::value &&
|
||||
std::is_trivially_copyable_v<typename Container::value_type>)
|
||||
{
|
||||
BaseUInt result;
|
||||
XRPL_ASSERT(
|
||||
@@ -313,11 +312,11 @@ public:
|
||||
}
|
||||
|
||||
template <class Container>
|
||||
BaseUInt&
|
||||
std::enable_if_t<
|
||||
detail::IsContiguousContainer<Container>::value &&
|
||||
std::is_trivially_copyable_v<typename Container::value_type>,
|
||||
BaseUInt&>
|
||||
operator=(Container const& c)
|
||||
requires(
|
||||
detail::IsContiguousContainer<Container>::value &&
|
||||
std::is_trivially_copyable_v<typename Container::value_type>)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
c.size() * sizeof(typename Container::value_type) == size(),
|
||||
@@ -506,14 +505,13 @@ public:
|
||||
h(a.data_.data(), sizeof(a.data_));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a hex string into a base_uint
|
||||
*
|
||||
* 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.
|
||||
/** Parse a hex string into a base_uint
|
||||
|
||||
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.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool
|
||||
parseHex(std::string_view sv)
|
||||
@@ -599,7 +597,7 @@ template <std::size_t Bits, typename Tag>
|
||||
[[nodiscard]] constexpr bool
|
||||
operator==(BaseUInt<Bits, Tag> const& lhs, BaseUInt<Bits, Tag> const& rhs)
|
||||
{
|
||||
return (lhs <=> rhs) == 0; // NOLINT(modernize-use-nullptr)
|
||||
return (lhs <=> rhs) == 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -21,16 +21,15 @@ using days =
|
||||
|
||||
using weeks = std::chrono::duration<int, std::ratio_multiply<days::period, std::ratio<7>>>;
|
||||
|
||||
/**
|
||||
* Clock for measuring the network time.
|
||||
*
|
||||
* The epoch is January 1, 2000
|
||||
*
|
||||
* epoch_offset
|
||||
* = date(2000-01-01) - date(1970-0-01)
|
||||
* = days(10957)
|
||||
* = seconds(946684800)
|
||||
*/
|
||||
/** Clock for measuring the network time.
|
||||
|
||||
The epoch is January 1, 2000
|
||||
|
||||
epoch_offset
|
||||
= date(2000-01-01) - date(1970-0-01)
|
||||
= days(10957)
|
||||
= seconds(946684800)
|
||||
*/
|
||||
|
||||
static constexpr std::chrono::seconds kEpochOffset =
|
||||
date::sys_days{date::year{2000} / 1 / 1} - date::sys_days{date::year{1970} / 1 / 1};
|
||||
@@ -82,21 +81,16 @@ toStringIso(NetClock::time_point tp)
|
||||
return toStringIso(date::sys_time<NetClock::duration>{tp.time_since_epoch() + kEpochOffset});
|
||||
}
|
||||
|
||||
/**
|
||||
* A clock for measuring elapsed time.
|
||||
*
|
||||
* The epoch is unspecified.
|
||||
*/
|
||||
/** A clock for measuring elapsed time.
|
||||
|
||||
The epoch is unspecified.
|
||||
*/
|
||||
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>;
|
||||
|
||||
/**
|
||||
* Returns an instance of a wall clock.
|
||||
*/
|
||||
/** Returns an instance of a wall clock. */
|
||||
inline Stopwatch&
|
||||
stopwatch()
|
||||
{
|
||||
|
||||
54
include/xrpl/basics/comparators.h
Normal file
54
include/xrpl/basics/comparators.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
/*
|
||||
* MSVC 2019 version 16.9.0 added [[nodiscard]] to the std comparison
|
||||
* operator() functions. boost::bimap checks that the comparator is a
|
||||
* BinaryFunction, in part by calling the function and ignoring the value.
|
||||
* These two things don't play well together. These wrapper classes simply
|
||||
* strip [[nodiscard]] from operator() for use in boost::bimap.
|
||||
*
|
||||
* See also:
|
||||
* https://www.boost.org/doc/libs/1_75_0/libs/bimap/doc/html/boost_bimap/the_tutorial/controlling_collection_types.html
|
||||
*/
|
||||
|
||||
template <class T = void>
|
||||
struct less
|
||||
{
|
||||
using result_type = bool;
|
||||
|
||||
constexpr bool
|
||||
operator()(T const& left, T const& right) const
|
||||
{
|
||||
return std::less<T>()(left, right);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T = void>
|
||||
struct equal_to
|
||||
{
|
||||
using result_type = bool;
|
||||
|
||||
constexpr bool
|
||||
operator()(T const& left, T const& right) const
|
||||
{
|
||||
return std::equal_to<T>()(left, right);
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template <class T = void>
|
||||
using less = std::less<T>;
|
||||
|
||||
template <class T = void>
|
||||
using equal_to = std::equal_to<T>;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace xrpl
|
||||
@@ -15,23 +15,20 @@ namespace xrpl {
|
||||
preconditions, postconditions, and invariants.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generates and logs a call stack
|
||||
*/
|
||||
/** Generates and logs a call stack */
|
||||
void
|
||||
logThrow(std::string const& title);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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
|
||||
* triggering false positives, since it throws.
|
||||
*/
|
||||
/** 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.
|
||||
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
|
||||
triggering false positives, since it throws.
|
||||
*/
|
||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||
rethrow()
|
||||
{
|
||||
@@ -59,9 +56,7 @@ Throw(Args&&... args)
|
||||
throw std::move(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when faulty logic causes a broken invariant.
|
||||
*/
|
||||
/** Called when faulty logic causes a broken invariant. */
|
||||
[[noreturn]] void
|
||||
logicError(std::string const& how) noexcept;
|
||||
|
||||
|
||||
@@ -39,33 +39,33 @@ makeSeedPair() noexcept
|
||||
|
||||
/**
|
||||
* Seed functor once per construction
|
||||
*
|
||||
* A std compatible hash adapter that resists adversarial inputs.
|
||||
* For this to work, T must implement in its own namespace:
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* template <class Hasher>
|
||||
* void
|
||||
* hash_append (Hasher& h, T const& t) noexcept
|
||||
* {
|
||||
* // hash_append each base and member that should
|
||||
* // participate in forming the hash
|
||||
* using beast::hash_append;
|
||||
* hash_append (h, static_cast<T::base1 const&>(t));
|
||||
* hash_append (h, static_cast<T::base2 const&>(t));
|
||||
* // ...
|
||||
* hash_append (h, t.member1);
|
||||
* hash_append (h, t.member2);
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* Do not use any version of Murmur or CityHash for the Hasher
|
||||
* template parameter (the hashing algorithm). For details
|
||||
* see https://131002.net/siphash/#at
|
||||
*/
|
||||
|
||||
A std compatible hash adapter that resists adversarial inputs.
|
||||
For this to work, T must implement in its own namespace:
|
||||
|
||||
@code
|
||||
|
||||
template <class Hasher>
|
||||
void
|
||||
hash_append (Hasher& h, T const& t) noexcept
|
||||
{
|
||||
// hash_append each base and member that should
|
||||
// participate in forming the hash
|
||||
using beast::hash_append;
|
||||
hash_append (h, static_cast<T::base1 const&>(t));
|
||||
hash_append (h, static_cast<T::base2 const&>(t));
|
||||
// ...
|
||||
hash_append (h, t.member1);
|
||||
hash_append (h, t.member2);
|
||||
// ...
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
Do not use any version of Murmur or CityHash for the Hasher
|
||||
template parameter (the hashing algorithm). For details
|
||||
see https://131002.net/siphash/#at
|
||||
*/
|
||||
|
||||
template <class HashAlgorithm = beast::Xxhasher>
|
||||
class HardenedHash
|
||||
|
||||
@@ -7,15 +7,11 @@
|
||||
|
||||
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>
|
||||
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>
|
||||
makeSslContextAuthed(
|
||||
std::string const& keyFile,
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
namespace xrpl {
|
||||
constexpr auto kMuldivMax = std::numeric_limits<std::uint64_t>::max();
|
||||
|
||||
/**
|
||||
* Return value*mul/div accurately.
|
||||
*
|
||||
* Computes the result of the multiplication and division in
|
||||
* a single step, avoiding overflow and retaining precision.
|
||||
*
|
||||
* @throws None
|
||||
* @return `std::nullopt` if the calculation overflows. Otherwise,
|
||||
* `value * mul / div`.
|
||||
*/
|
||||
/** Return value*mul/div accurately.
|
||||
Computes the result of the multiplication and division in
|
||||
a single step, avoiding overflow and retaining precision.
|
||||
Throws:
|
||||
None
|
||||
Returns:
|
||||
`std::optional`:
|
||||
`std::nullopt` if the calculation overflows. Otherwise, `value * mul
|
||||
/ div`.
|
||||
*/
|
||||
std::optional<std::uint64_t>
|
||||
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
|
||||
|
||||
|
||||
@@ -138,8 +138,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
ConstIterator(Iterator const& orig) : map(orig.map), ait(orig.ait), mit(orig.mit)
|
||||
ConstIterator(Iterator const& orig)
|
||||
{
|
||||
map = orig.map;
|
||||
ait = orig.ait;
|
||||
mit = orig.mit;
|
||||
}
|
||||
|
||||
const_reference
|
||||
@@ -228,11 +231,11 @@ private:
|
||||
|
||||
public:
|
||||
PartitionedUnorderedMap(std::optional<std::size_t> partitions = std::nullopt)
|
||||
{
|
||||
// Set partitions to the number of hardware threads if the parameter
|
||||
// is either empty or set to 0.
|
||||
: partitions_(
|
||||
partitions && (*partitions != 0u) ? *partitions : std::thread::hardware_concurrency())
|
||||
{
|
||||
partitions_ =
|
||||
partitions && (*partitions != 0u) ? *partitions : std::thread::hardware_concurrency();
|
||||
map_.resize(partitions_);
|
||||
XRPL_ASSERT(
|
||||
partitions_,
|
||||
|
||||
@@ -33,17 +33,16 @@ template <class Engine, class Result = typename Engine::result_type>
|
||||
using is_engine = std::is_invocable_r<Result, Engine>;
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Return the default random engine.
|
||||
*
|
||||
* This engine is guaranteed to be deterministic, but by
|
||||
* default will be randomly seeded. It is NOT cryptographically
|
||||
* 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.
|
||||
*/
|
||||
/** Return the default random engine.
|
||||
|
||||
This engine is guaranteed to be deterministic, but by
|
||||
default will be randomly seeded. It is NOT cryptographically
|
||||
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.
|
||||
*/
|
||||
inline beast::xor_shift_engine&
|
||||
defaultPrng()
|
||||
{
|
||||
@@ -71,31 +70,29 @@ defaultPrng()
|
||||
return kEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a uniformly distributed random integer.
|
||||
*
|
||||
* @param min The smallest value to return. If not specified
|
||||
* the value defaults to 0.
|
||||
* @param max The largest value to return. If not specified
|
||||
* 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
|
||||
* 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
|
||||
* closed interval [-5, 15]; similarly, calling
|
||||
* rand_int(7) can return any integer in the closed
|
||||
* interval [0, 7].
|
||||
*/
|
||||
/** Return a uniformly distributed random integer.
|
||||
|
||||
@param min The smallest value to return. If not specified
|
||||
the value defaults to 0.
|
||||
@param max The largest value to return. If not specified
|
||||
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
|
||||
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
|
||||
closed interval [-5, 15]; similarly, calling
|
||||
rand_int(7) can return any integer in the closed
|
||||
interval [0, 7].
|
||||
*/
|
||||
/** @{ */
|
||||
template <class Engine, class Integral>
|
||||
Integral
|
||||
std::enable_if_t<std::is_integral_v<Integral> && detail::is_engine<Engine>::value, Integral>
|
||||
randInt(Engine& engine, Integral min, Integral max)
|
||||
requires(std::is_integral_v<Integral> && detail::is_engine<Engine>::value)
|
||||
{
|
||||
XRPL_ASSERT(max > min, "xrpl::randInt : max over min inputs");
|
||||
|
||||
@@ -106,73 +103,63 @@ randInt(Engine& engine, Integral min, Integral max)
|
||||
}
|
||||
|
||||
template <class Integral>
|
||||
Integral
|
||||
std::enable_if_t<std::is_integral_v<Integral>, Integral>
|
||||
randInt(Integral min, Integral max)
|
||||
requires(std::is_integral_v<Integral>)
|
||||
{
|
||||
return randInt(defaultPrng(), min, max);
|
||||
}
|
||||
|
||||
template <class Engine, class Integral>
|
||||
Integral
|
||||
std::enable_if_t<std::is_integral_v<Integral> && detail::is_engine<Engine>::value, Integral>
|
||||
randInt(Engine& engine, Integral max)
|
||||
requires(std::is_integral_v<Integral> && detail::is_engine<Engine>::value)
|
||||
{
|
||||
return randInt(engine, Integral(0), max);
|
||||
}
|
||||
|
||||
template <class Integral>
|
||||
Integral
|
||||
std::enable_if_t<std::is_integral_v<Integral>, Integral>
|
||||
randInt(Integral max)
|
||||
requires(std::is_integral_v<Integral>)
|
||||
{
|
||||
return randInt(defaultPrng(), max);
|
||||
}
|
||||
|
||||
template <class Integral, class Engine>
|
||||
Integral
|
||||
std::enable_if_t<std::is_integral_v<Integral> && detail::is_engine<Engine>::value, Integral>
|
||||
randInt(Engine& engine)
|
||||
requires(std::is_integral_v<Integral> && detail::is_engine<Engine>::value)
|
||||
{
|
||||
return randInt(engine, std::numeric_limits<Integral>::max());
|
||||
}
|
||||
|
||||
template <class Integral = int>
|
||||
Integral
|
||||
std::enable_if_t<std::is_integral_v<Integral>, Integral>
|
||||
randInt()
|
||||
requires(std::is_integral_v<Integral>)
|
||||
{
|
||||
return randInt(defaultPrng(), std::numeric_limits<Integral>::max());
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Return a random byte
|
||||
*/
|
||||
/** Return a random byte */
|
||||
/** @{ */
|
||||
template <class Byte, class Engine>
|
||||
Byte
|
||||
std::enable_if_t<
|
||||
(std::is_same_v<Byte, unsigned char> || std::is_same_v<Byte, std::uint8_t>) &&
|
||||
detail::is_engine<Engine>::value,
|
||||
Byte>
|
||||
randByte(Engine& engine)
|
||||
requires(
|
||||
(std::is_same_v<Byte, unsigned char> || std::is_same_v<Byte, std::uint8_t>) &&
|
||||
detail::is_engine<Engine>::value)
|
||||
{
|
||||
return static_cast<Byte>(randInt<Engine, std::uint32_t>(
|
||||
engine, std::numeric_limits<Byte>::min(), std::numeric_limits<Byte>::max()));
|
||||
}
|
||||
|
||||
template <class Byte = std::uint8_t>
|
||||
Byte
|
||||
std::enable_if_t<(std::is_same_v<Byte, unsigned char> || std::is_same_v<Byte, std::uint8_t>), Byte>
|
||||
randByte()
|
||||
requires(std::is_same_v<Byte, unsigned char> || std::is_same_v<Byte, std::uint8_t>)
|
||||
{
|
||||
return randByte<Byte>(defaultPrng());
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Return a random boolean value
|
||||
*/
|
||||
/** Return a random boolean value */
|
||||
/** @{ */
|
||||
template <class Engine>
|
||||
inline bool
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h> // IWYU pragma: keep
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -17,9 +15,8 @@ concept SafeToCast = (std::is_integral_v<Src> && std::is_integral_v<Dest>) &&
|
||||
: sizeof(Dest) >= sizeof(Src));
|
||||
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
constexpr std::enable_if_t<std::is_integral_v<Dest> && std::is_integral_v<Src>, Dest>
|
||||
safeCast(Src s) noexcept
|
||||
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||
{
|
||||
static_assert(
|
||||
std::is_signed_v<Dest> || std::is_unsigned_v<Src>, "Cannot cast signed to unsigned");
|
||||
@@ -31,17 +28,15 @@ safeCast(Src s) noexcept
|
||||
}
|
||||
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
constexpr std::enable_if_t<std::is_enum_v<Dest> && std::is_integral_v<Src>, Dest>
|
||||
safeCast(Src s) noexcept
|
||||
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||
{
|
||||
return static_cast<Dest>(safeCast<std::underlying_type_t<Dest>>(s));
|
||||
}
|
||||
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
constexpr std::enable_if_t<std::is_integral_v<Dest> && std::is_enum_v<Src>, Dest>
|
||||
safeCast(Src s) noexcept
|
||||
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
||||
{
|
||||
return safeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
|
||||
}
|
||||
@@ -51,9 +46,8 @@ safeCast(Src s) noexcept
|
||||
// underlying types become safe, it can be converted to a safe_cast.
|
||||
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
constexpr std::enable_if_t<std::is_integral_v<Dest> && std::is_integral_v<Src>, Dest>
|
||||
unsafeCast(Src s) noexcept
|
||||
requires(std::is_integral_v<Dest> && std::is_integral_v<Src>)
|
||||
{
|
||||
static_assert(
|
||||
!SafeToCast<Src, Dest>,
|
||||
@@ -63,17 +57,15 @@ unsafeCast(Src s) noexcept
|
||||
}
|
||||
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
constexpr std::enable_if_t<std::is_enum_v<Dest> && std::is_integral_v<Src>, Dest>
|
||||
unsafeCast(Src s) noexcept
|
||||
requires(std::is_enum_v<Dest> && std::is_integral_v<Src>)
|
||||
{
|
||||
return static_cast<Dest>(unsafeCast<std::underlying_type_t<Dest>>(s));
|
||||
}
|
||||
|
||||
template <class Dest, class Src>
|
||||
constexpr Dest
|
||||
constexpr std::enable_if_t<std::is_integral_v<Dest> && std::is_enum_v<Src>, Dest>
|
||||
unsafeCast(Src s) noexcept
|
||||
requires(std::is_integral_v<Dest> && std::is_enum_v<Src>)
|
||||
{
|
||||
return unsafeCast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
|
||||
}
|
||||
|
||||
@@ -46,9 +46,11 @@ public:
|
||||
operator=(ScopeExit&&) = delete;
|
||||
|
||||
template <class EFP>
|
||||
explicit ScopeExit(EFP&& f) noexcept
|
||||
requires(
|
||||
!std::is_same_v<std::remove_cv_t<EFP>, ScopeExit> && std::is_constructible_v<EF, EFP>)
|
||||
explicit ScopeExit(
|
||||
EFP&& f,
|
||||
std::enable_if_t<
|
||||
!std::is_same_v<std::remove_cv_t<EFP>, ScopeExit> &&
|
||||
std::is_constructible_v<EF, EFP>>* = 0) noexcept
|
||||
: exitFunction_{std::forward<EFP>(f)}
|
||||
{
|
||||
static_assert(std::is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>);
|
||||
@@ -91,9 +93,11 @@ public:
|
||||
operator=(ScopeFail&&) = delete;
|
||||
|
||||
template <class EFP>
|
||||
explicit ScopeFail(EFP&& f) noexcept
|
||||
requires(
|
||||
!std::is_same_v<std::remove_cv_t<EFP>, ScopeFail> && std::is_constructible_v<EF, EFP>)
|
||||
explicit ScopeFail(
|
||||
EFP&& f,
|
||||
std::enable_if_t<
|
||||
!std::is_same_v<std::remove_cv_t<EFP>, ScopeFail> &&
|
||||
std::is_constructible_v<EF, EFP>>* = 0) noexcept
|
||||
: exitFunction_{std::forward<EFP>(f)}
|
||||
{
|
||||
static_assert(std::is_nothrow_constructible_v<EF, decltype(std::forward<EFP>(f))>);
|
||||
@@ -136,11 +140,12 @@ public:
|
||||
operator=(ScopeSuccess&&) = delete;
|
||||
|
||||
template <class EFP>
|
||||
explicit ScopeSuccess(EFP&& f) noexcept(
|
||||
std::is_nothrow_constructible_v<EF, EFP> || std::is_nothrow_constructible_v<EF, EFP&>)
|
||||
requires(
|
||||
explicit ScopeSuccess(
|
||||
EFP&& f,
|
||||
std::enable_if_t<
|
||||
!std::is_same_v<std::remove_cv_t<EFP>, ScopeSuccess> &&
|
||||
std::is_constructible_v<EF, EFP>)
|
||||
std::is_constructible_v<EF, EFP>>* =
|
||||
0) noexcept(std::is_nothrow_constructible_v<EF, EFP> || std::is_nothrow_constructible_v<EF, EFP&>)
|
||||
: exitFunction_{std::forward<EFP>(f)}
|
||||
{
|
||||
}
|
||||
@@ -156,41 +161,41 @@ template <class EF>
|
||||
ScopeSuccess(EF) -> ScopeSuccess<EF>;
|
||||
|
||||
/**
|
||||
* Automatically unlocks and re-locks a unique_lock object.
|
||||
*
|
||||
* This is the reverse of a std::unique_lock object - instead of locking the
|
||||
* mutex for the lifetime of this object, it unlocks it.
|
||||
*
|
||||
* Make sure you don't try to unlock mutexes that aren't actually locked!
|
||||
*
|
||||
* This is essentially a less-versatile boost::reverse_lock.
|
||||
*
|
||||
* e.g. @code
|
||||
*
|
||||
* std::mutex mut;
|
||||
*
|
||||
* for (;;)
|
||||
* {
|
||||
* std::unique_lock myScopedLock{mut};
|
||||
* // mut is now locked
|
||||
*
|
||||
* ... do some stuff with it locked ..
|
||||
*
|
||||
* while (xyz)
|
||||
* {
|
||||
* ... do some stuff with it locked ..
|
||||
*
|
||||
* scope_unlock unlocker{myScopedLock};
|
||||
*
|
||||
* // mut is now unlocked for the remainder of this block,
|
||||
* // and re-locked at the end.
|
||||
*
|
||||
* ...do some stuff with it unlocked ...
|
||||
* } // mut gets locked here.
|
||||
*
|
||||
* } // mut gets unlocked here
|
||||
* @endcode
|
||||
*/
|
||||
Automatically unlocks and re-locks a unique_lock object.
|
||||
|
||||
This is the reverse of a std::unique_lock object - instead of locking the
|
||||
mutex for the lifetime of this object, it unlocks it.
|
||||
|
||||
Make sure you don't try to unlock mutexes that aren't actually locked!
|
||||
|
||||
This is essentially a less-versatile boost::reverse_lock.
|
||||
|
||||
e.g. @code
|
||||
|
||||
std::mutex mut;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
std::unique_lock myScopedLock{mut};
|
||||
// mut is now locked
|
||||
|
||||
... do some stuff with it locked ..
|
||||
|
||||
while (xyz)
|
||||
{
|
||||
... do some stuff with it locked ..
|
||||
|
||||
scope_unlock unlocker{myScopedLock};
|
||||
|
||||
// mut is now unlocked for the remainder of this block,
|
||||
// and re-locked at the end.
|
||||
|
||||
...do some stuff with it unlocked ...
|
||||
} // mut gets locked here.
|
||||
|
||||
} // mut gets unlocked here
|
||||
@endcode
|
||||
*/
|
||||
|
||||
template <class Mutex>
|
||||
class ScopeUnlock
|
||||
|
||||
@@ -15,16 +15,15 @@
|
||||
namespace xrpl {
|
||||
|
||||
namespace detail {
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
/** 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
|
||||
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.
|
||||
*/
|
||||
inline void
|
||||
spinPause() noexcept
|
||||
@@ -39,39 +38,37 @@ spinPause() noexcept
|
||||
} // namespace detail
|
||||
|
||||
/** @{ */
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* First, the implementation is necessarily low-level and uses advanced
|
||||
* features like memory ordering and highly platform-specific tricks to
|
||||
* 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
|
||||
* performance implications stemming from data dependencies, increased
|
||||
* 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
|
||||
* way. The use cases for them are likely few and far between; without
|
||||
* a compelling reason to use them, backed by profiling data, it might
|
||||
* be best to use one of the standard locking primitives instead. Note
|
||||
* 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)
|
||||
/** 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.
|
||||
|
||||
First, the implementation is necessarily low-level and uses advanced
|
||||
features like memory ordering and highly platform-specific tricks to
|
||||
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
|
||||
performance implications stemming from data dependencies, increased
|
||||
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
|
||||
way. The use cases for them are likely few and far between; without
|
||||
a compelling reason to use them, backed by profiling data, it might
|
||||
be best to use one of the standard locking primitives instead. Note
|
||||
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)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
/** 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
|
||||
*/
|
||||
template <class T>
|
||||
class PackedSpinlock
|
||||
@@ -94,14 +91,13 @@ public:
|
||||
PackedSpinlock&
|
||||
operator=(PackedSpinlock const&) = delete;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @note For performance reasons, you should strive to have `lock` be
|
||||
* on a cacheline by itself.
|
||||
/** 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.
|
||||
|
||||
@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)
|
||||
{
|
||||
@@ -137,18 +133,17 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* acquire the lock during periods of high contention, because of how
|
||||
* the two locks operate: `spinlock` will spin trying to grab all the
|
||||
* 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
|
||||
/** 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
|
||||
acquire the lock during periods of high contention, because of how
|
||||
the two locks operate: `spinlock` will spin trying to grab all the
|
||||
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
|
||||
*/
|
||||
template <class T>
|
||||
class Spinlock
|
||||
@@ -164,13 +159,12 @@ public:
|
||||
Spinlock&
|
||||
operator=(Spinlock const&) = delete;
|
||||
|
||||
/**
|
||||
* Grabs the
|
||||
*
|
||||
* @param lock The atomic integer to spin against.
|
||||
*
|
||||
* @note For performance reasons, you should strive to have `lock` be
|
||||
* on a cacheline by itself.
|
||||
/** Grabs the
|
||||
|
||||
@param lock The atomic integer to spin against.
|
||||
|
||||
@note For performance reasons, you should strive to have `lock` be
|
||||
on a cacheline by itself.
|
||||
*/
|
||||
Spinlock(std::atomic<T>& lock) : lock_(lock)
|
||||
{
|
||||
|
||||
@@ -12,18 +12,17 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* type are distinct types (distinguished by tag) and will not interoperate. A
|
||||
* 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.
|
||||
*/
|
||||
/** 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
|
||||
type are distinct types (distinguished by tag) and will not interoperate. A
|
||||
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.
|
||||
*/
|
||||
template <class Int, class Tag>
|
||||
class TaggedInteger : boost::totally_ordered<
|
||||
TaggedInteger<Int, Tag>,
|
||||
@@ -44,10 +43,10 @@ public:
|
||||
|
||||
TaggedInteger() = default;
|
||||
|
||||
template <class OtherInt>
|
||||
explicit constexpr TaggedInteger(OtherInt value) noexcept
|
||||
requires(std::is_integral_v<OtherInt> && sizeof(OtherInt) <= sizeof(Int))
|
||||
: value_(value)
|
||||
template <
|
||||
class OtherInt,
|
||||
class = std::enable_if_t<std::is_integral_v<OtherInt> && sizeof(OtherInt) <= sizeof(Int)>>
|
||||
explicit constexpr TaggedInteger(OtherInt value) noexcept : value_(value)
|
||||
{
|
||||
static_assert(sizeof(TaggedInteger) == sizeof(Int), "tagged_integer is adding padding");
|
||||
}
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* Measures handler latency on an io_context queue.
|
||||
*/
|
||||
/** Measures handler latency on an io_context queue. */
|
||||
template <class Clock>
|
||||
class IOLatencyProbe
|
||||
{
|
||||
@@ -44,9 +42,7 @@ public:
|
||||
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&
|
||||
getIoContext()
|
||||
@@ -61,10 +57,9 @@ 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
|
||||
cancel()
|
||||
@@ -81,11 +76,10 @@ public:
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Measure one sample of i/o latency.
|
||||
* Handler will be called with this signature:
|
||||
* void Handler (Duration d);
|
||||
*/
|
||||
/** Measure one sample of i/o latency.
|
||||
Handler will be called with this signature:
|
||||
void Handler (Duration d);
|
||||
*/
|
||||
template <class Handler>
|
||||
void
|
||||
sampleOne(Handler&& handler)
|
||||
@@ -97,11 +91,10 @@ public:
|
||||
ios_, SampleOp<Handler>(std::forward<Handler>(handler), Clock::now(), false, this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate continuous i/o latency sampling.
|
||||
* Handler will be called with this signature:
|
||||
* void Handler (std::chrono::milliseconds);
|
||||
*/
|
||||
/** Initiate continuous i/o latency sampling.
|
||||
Handler will be called with this signature:
|
||||
void Handler (std::chrono::milliseconds);
|
||||
*/
|
||||
template <class Handler>
|
||||
void
|
||||
sample(Handler&& handler)
|
||||
|
||||
@@ -2,35 +2,34 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* unit tests where time may be controlled.
|
||||
*
|
||||
* An abstract_clock inherits all the nested types of the Clock
|
||||
* template parameter.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* struct Implementation
|
||||
* {
|
||||
* using clock_type = abstract_clock <std::chrono::steady_clock>;
|
||||
* clock_type& clock_;
|
||||
* explicit Implementation (clock_type& clock)
|
||||
* : clock_(clock)
|
||||
* {
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Clock A type meeting these requirements:
|
||||
* http://en.cppreference.com/w/cpp/concept/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
|
||||
unit tests where time may be controlled.
|
||||
|
||||
An abstract_clock inherits all the nested types of the Clock
|
||||
template parameter.
|
||||
|
||||
Example:
|
||||
|
||||
@code
|
||||
|
||||
struct Implementation
|
||||
{
|
||||
using clock_type = abstract_clock <std::chrono::steady_clock>;
|
||||
clock_type& clock_;
|
||||
explicit Implementation (clock_type& clock)
|
||||
: clock_(clock)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
@tparam Clock A type meeting these requirements:
|
||||
http://en.cppreference.com/w/cpp/concept/Clock
|
||||
*/
|
||||
template <class Clock>
|
||||
class AbstractClock
|
||||
{
|
||||
@@ -47,9 +46,7 @@ public:
|
||||
AbstractClock() = default;
|
||||
AbstractClock(AbstractClock const&) = default;
|
||||
|
||||
/**
|
||||
* Returns the current time.
|
||||
*/
|
||||
/** Returns the current time. */
|
||||
[[nodiscard]] virtual time_point
|
||||
now() const = 0;
|
||||
};
|
||||
@@ -77,12 +74,11 @@ struct AbstractClockWrapper : public AbstractClock<Facade>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns a global instance of an abstract clock.
|
||||
* @tparam Facade A type meeting these requirements:
|
||||
* http://en.cppreference.com/w/cpp/concept/Clock
|
||||
* @tparam Clock The actual concrete clock to use.
|
||||
*/
|
||||
/** Returns a global instance of an abstract clock.
|
||||
@tparam Facade A type meeting these requirements:
|
||||
http://en.cppreference.com/w/cpp/concept/Clock
|
||||
@tparam Clock The actual concrete clock to use.
|
||||
*/
|
||||
template <class Facade, class Clock = Facade>
|
||||
AbstractClock<Facade>&
|
||||
getAbstractClock()
|
||||
|
||||
@@ -4,16 +4,15 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* once per second to sample the requested trivial clock.
|
||||
*
|
||||
* @tparam Clock A type meeting these requirements:
|
||||
* http://en.cppreference.com/w/cpp/concept/Clock
|
||||
*/
|
||||
/** 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
|
||||
once per second to sample the requested trivial clock.
|
||||
|
||||
@tparam Clock A type meeting these requirements:
|
||||
http://en.cppreference.com/w/cpp/concept/Clock
|
||||
*/
|
||||
class BasicSecondsClock
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -7,16 +7,15 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* providing a clock in unit tests.
|
||||
*
|
||||
* @tparam Clock A type meeting these requirements:
|
||||
* http://en.cppreference.com/w/cpp/concept/Clock
|
||||
*/
|
||||
/** 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
|
||||
providing a clock in unit tests.
|
||||
|
||||
@tparam Clock A type meeting these requirements:
|
||||
http://en.cppreference.com/w/cpp/concept/Clock
|
||||
*/
|
||||
template <class Clock>
|
||||
class ManualClock : public AbstractClock<Clock>
|
||||
{
|
||||
@@ -39,9 +38,7 @@ public:
|
||||
return now_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current time of the manual clock.
|
||||
*/
|
||||
/** Set the current time of the manual clock. */
|
||||
void
|
||||
set(time_point const& when)
|
||||
{
|
||||
@@ -51,9 +48,7 @@ public:
|
||||
now_ = when;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience for setting the time in seconds from epoch.
|
||||
*/
|
||||
/** Convenience for setting the time in seconds from epoch. */
|
||||
template <class Integer>
|
||||
void
|
||||
set(Integer secondsFromEpoch)
|
||||
@@ -61,9 +56,7 @@ public:
|
||||
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>
|
||||
void
|
||||
advance(std::chrono::duration<Rep, Period> const& elapsed)
|
||||
@@ -74,9 +67,7 @@ public:
|
||||
now_ += elapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience for advancing the clock by one second.
|
||||
*/
|
||||
/** Convenience for advancing the clock by one second. */
|
||||
ManualClock&
|
||||
operator++()
|
||||
{
|
||||
|
||||
@@ -4,16 +4,14 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
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>
|
||||
std::size_t
|
||||
std::enable_if_t<IsAgedContainer<AgedContainer>::value, std::size_t>
|
||||
expire(AgedContainer& c, std::chrono::duration<Rep, Period> const& age)
|
||||
requires(IsAgedContainer<AgedContainer>::value)
|
||||
{
|
||||
std::size_t n(0);
|
||||
auto const expired(c.clock().now() - age);
|
||||
|
||||
@@ -30,19 +30,20 @@ public:
|
||||
|
||||
// Disable constructing a const_iterator from a non-const_iterator.
|
||||
// Converting between reverse and non-reverse iterators should be explicit.
|
||||
template <bool OtherIsConst, class OtherIterator>
|
||||
explicit AgedContainerIterator(AgedContainerIterator<OtherIsConst, OtherIterator> const& other)
|
||||
requires(
|
||||
template <
|
||||
bool OtherIsConst,
|
||||
class OtherIterator,
|
||||
class = std::enable_if_t<
|
||||
(!OtherIsConst || IsConst) &&
|
||||
!static_cast<bool>(std::is_same_v<Iterator, OtherIterator>))
|
||||
!static_cast<bool>(std::is_same_v<Iterator, OtherIterator>)>>
|
||||
explicit AgedContainerIterator(AgedContainerIterator<OtherIsConst, OtherIterator> const& other)
|
||||
: iter_(other.iter_)
|
||||
{
|
||||
}
|
||||
|
||||
// Disable constructing a const_iterator from a non-const_iterator.
|
||||
template <bool OtherIsConst>
|
||||
template <bool OtherIsConst, class = std::enable_if_t<!OtherIsConst || IsConst>>
|
||||
AgedContainerIterator(AgedContainerIterator<OtherIsConst, Iterator> const& other)
|
||||
requires(!OtherIsConst || IsConst)
|
||||
: iter_(other.iter_)
|
||||
{
|
||||
}
|
||||
@@ -51,8 +52,7 @@ public:
|
||||
template <bool OtherIsConst, class OtherIterator>
|
||||
auto
|
||||
operator=(AgedContainerIterator<OtherIsConst, OtherIterator> const& other)
|
||||
-> AgedContainerIterator&
|
||||
requires(!OtherIsConst || IsConst)
|
||||
-> std::enable_if_t<!OtherIsConst || IsConst, AgedContainerIterator&>
|
||||
{
|
||||
iter_ = other.iter_;
|
||||
return *this;
|
||||
|
||||
@@ -39,23 +39,22 @@ struct IsBoostReverseIterator<boost::intrusive::reverse_iterator<It>> : std::tru
|
||||
explicit IsBoostReverseIterator() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* with a `when` `time_point` which is obtained from the value of the clock's
|
||||
* `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
|
||||
* temporal order. This container is useful as a building block for caches
|
||||
* 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
|
||||
*/
|
||||
/** 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
|
||||
with a `when` `time_point` which is obtained from the value of the clock's
|
||||
`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
|
||||
temporal order. This container is useful as a building block for caches
|
||||
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
|
||||
*/
|
||||
template <
|
||||
bool IsMulti,
|
||||
bool IsMap,
|
||||
@@ -112,9 +111,10 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
template <
|
||||
class... Args,
|
||||
class = std::enable_if_t<std::is_constructible_v<value_type, Args...>>>
|
||||
Element(time_point const& when, Args&&... args)
|
||||
requires(std::is_constructible_v<value_type, Args...>)
|
||||
: value(std::forward<Args>(args)...), when(when)
|
||||
{
|
||||
}
|
||||
@@ -360,7 +360,6 @@ private:
|
||||
deleteElement(Element const* p)
|
||||
{
|
||||
ElementAllocatorTraits::destroy(config_.alloc(), p);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
|
||||
ElementAllocatorTraits::deallocate(config_.alloc(), const_cast<Element*>(p), 1);
|
||||
}
|
||||
|
||||
@@ -609,25 +608,35 @@ public:
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <class K, bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
template <
|
||||
class K,
|
||||
bool MaybeMulti = IsMulti,
|
||||
bool MaybeMap = IsMap,
|
||||
class = std::enable_if_t<MaybeMap && !MaybeMulti>>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
at(K const& k)
|
||||
requires(MaybeMap && !MaybeMulti);
|
||||
at(K const& k);
|
||||
|
||||
template <class K, bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
template <
|
||||
class K,
|
||||
bool MaybeMulti = IsMulti,
|
||||
bool MaybeMap = IsMap,
|
||||
class = std::enable_if_t<MaybeMap && !MaybeMulti>>
|
||||
std::conditional<IsMap, T, void*>::type const&
|
||||
at(K const& k) const
|
||||
requires(MaybeMap && !MaybeMulti);
|
||||
at(K const& k) const;
|
||||
|
||||
template <bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
template <
|
||||
bool MaybeMulti = IsMulti,
|
||||
bool MaybeMap = IsMap,
|
||||
class = std::enable_if_t<MaybeMap && !MaybeMulti>>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
operator[](Key const& key)
|
||||
requires(MaybeMap && !MaybeMulti);
|
||||
operator[](Key const& key);
|
||||
|
||||
template <bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
template <
|
||||
bool MaybeMulti = IsMulti,
|
||||
bool MaybeMap = IsMap,
|
||||
class = std::enable_if_t<MaybeMap && !MaybeMulti>>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
operator[](Key&& key)
|
||||
requires(MaybeMap && !MaybeMulti);
|
||||
operator[](Key&& key);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
@@ -761,40 +770,35 @@ public:
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
auto
|
||||
insert(value_type const& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti);
|
||||
insert(value_type const& value) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>;
|
||||
|
||||
// multimap, multiset
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
auto
|
||||
insert(value_type const& value) -> iterator
|
||||
requires MaybeMulti;
|
||||
insert(value_type const& value) -> std::enable_if_t<MaybeMulti, iterator>;
|
||||
|
||||
// set
|
||||
template <bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
auto
|
||||
insert(value_type&& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti && !MaybeMap);
|
||||
insert(value_type&& value)
|
||||
-> std::enable_if_t<!MaybeMulti && !MaybeMap, std::pair<iterator, bool>>;
|
||||
|
||||
// multiset
|
||||
template <bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
auto
|
||||
insert(value_type&& value) -> iterator
|
||||
requires(MaybeMulti && !MaybeMap);
|
||||
insert(value_type&& value) -> std::enable_if_t<MaybeMulti && !MaybeMap, iterator>;
|
||||
|
||||
//---
|
||||
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
auto
|
||||
insert(const_iterator hint, value_type const& value) -> iterator
|
||||
requires(!MaybeMulti);
|
||||
insert(const_iterator hint, value_type const& value) -> std::enable_if_t<!MaybeMulti, iterator>;
|
||||
|
||||
// multimap, multiset
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
iterator
|
||||
std::enable_if_t<MaybeMulti, iterator>
|
||||
insert(const_iterator /*hint*/, value_type const& value)
|
||||
requires MaybeMulti
|
||||
{
|
||||
// VFALCO TODO Figure out how to utilize 'hint'
|
||||
return insert(value);
|
||||
@@ -803,14 +807,12 @@ public:
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
auto
|
||||
insert(const_iterator hint, value_type&& value) -> iterator
|
||||
requires(!MaybeMulti);
|
||||
insert(const_iterator hint, value_type&& value) -> std::enable_if_t<!MaybeMulti, iterator>;
|
||||
|
||||
// multimap, multiset
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
iterator
|
||||
std::enable_if_t<MaybeMulti, iterator>
|
||||
insert(const_iterator /*hint*/, value_type&& value)
|
||||
requires MaybeMulti
|
||||
{
|
||||
// VFALCO TODO Figure out how to utilize 'hint'
|
||||
return insert(std::move(value));
|
||||
@@ -818,18 +820,20 @@ public:
|
||||
|
||||
// map, multimap
|
||||
template <class P, bool MaybeMap = IsMap>
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>
|
||||
std::enable_if_t<
|
||||
MaybeMap && std::is_constructible_v<value_type, P&&>,
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>>
|
||||
insert(P&& value)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P &&>)
|
||||
{
|
||||
return emplace(std::forward<P>(value));
|
||||
}
|
||||
|
||||
// map, multimap
|
||||
template <class P, bool MaybeMap = IsMap>
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>
|
||||
std::enable_if_t<
|
||||
MaybeMap && std::is_constructible_v<value_type, P&&>,
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>>
|
||||
insert(const_iterator hint, P&& value)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P &&>)
|
||||
{
|
||||
return emplaceHint(hint, std::forward<P>(value));
|
||||
}
|
||||
@@ -851,45 +855,46 @@ public:
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti, class... Args>
|
||||
auto
|
||||
emplace(Args&&... args) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti);
|
||||
emplace(Args&&... args) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>;
|
||||
|
||||
// multiset, multimap
|
||||
template <bool MaybeMulti = IsMulti, class... Args>
|
||||
auto
|
||||
emplace(Args&&... args) -> iterator
|
||||
requires MaybeMulti;
|
||||
emplace(Args&&... args) -> std::enable_if_t<MaybeMulti, iterator>;
|
||||
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti, class... Args>
|
||||
auto
|
||||
emplaceHint(const_iterator hint, Args&&... args) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti);
|
||||
emplaceHint(const_iterator hint, Args&&... args)
|
||||
-> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>;
|
||||
|
||||
// multiset, multimap
|
||||
template <bool MaybeMulti = IsMulti, class... Args>
|
||||
iterator
|
||||
std::enable_if_t<MaybeMulti, iterator>
|
||||
emplaceHint(const_iterator /*hint*/, Args&&... args)
|
||||
requires MaybeMulti
|
||||
{
|
||||
// VFALCO TODO Figure out how to utilize 'hint'
|
||||
return emplace<MaybeMulti>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// The constraint prevents erase (reverse_iterator pos) from compiling
|
||||
template <bool IsConst, class Iterator>
|
||||
// enable_if prevents erase (reverse_iterator pos) from compiling
|
||||
template <
|
||||
bool IsConst,
|
||||
class Iterator,
|
||||
class = std::enable_if_t<!IsBoostReverseIterator<Iterator>::value>>
|
||||
beast::detail::AgedContainerIterator<false, Iterator>
|
||||
erase(beast::detail::AgedContainerIterator<IsConst, Iterator> pos)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value);
|
||||
erase(beast::detail::AgedContainerIterator<IsConst, Iterator> pos);
|
||||
|
||||
// The constraint prevents erase (reverse_iterator first, reverse_iterator last)
|
||||
// enable_if prevents erase (reverse_iterator first, reverse_iterator last)
|
||||
// from compiling
|
||||
template <bool IsConst, class Iterator>
|
||||
template <
|
||||
bool IsConst,
|
||||
class Iterator,
|
||||
class = std::enable_if_t<!IsBoostReverseIterator<Iterator>::value>>
|
||||
beast::detail::AgedContainerIterator<false, Iterator>
|
||||
erase(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> first,
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> last)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value);
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> last);
|
||||
|
||||
template <class K>
|
||||
auto
|
||||
@@ -900,11 +905,13 @@ public:
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// The constraint prevents touch (reverse_iterator pos) from compiling
|
||||
template <bool IsConst, class Iterator>
|
||||
// enable_if prevents touch (reverse_iterator pos) from compiling
|
||||
template <
|
||||
bool IsConst,
|
||||
class Iterator,
|
||||
class = std::enable_if_t<!IsBoostReverseIterator<Iterator>::value>>
|
||||
void
|
||||
touch(beast::detail::AgedContainerIterator<IsConst, Iterator> pos)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value)
|
||||
{
|
||||
touch(pos, clock().now());
|
||||
}
|
||||
@@ -1135,25 +1142,25 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// The constraint prevents erase (reverse_iterator pos, now) from compiling
|
||||
template <bool IsConst, class Iterator>
|
||||
// enable_if prevents erase (reverse_iterator pos, now) from compiling
|
||||
template <
|
||||
bool IsConst,
|
||||
class Iterator,
|
||||
class = std::enable_if_t<!IsBoostReverseIterator<Iterator>::value>>
|
||||
void
|
||||
touch(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> pos,
|
||||
clock_type::time_point const& now)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value);
|
||||
clock_type::time_point const& now);
|
||||
|
||||
template <
|
||||
bool MaybePropagate = std::allocator_traits<Allocator>::propagate_on_container_swap::value>
|
||||
void
|
||||
swapData(AgedOrderedContainer& other) noexcept
|
||||
requires MaybePropagate;
|
||||
std::enable_if_t<MaybePropagate>
|
||||
swapData(AgedOrderedContainer& other) noexcept;
|
||||
|
||||
template <
|
||||
bool MaybePropagate = std::allocator_traits<Allocator>::propagate_on_container_swap::value>
|
||||
void
|
||||
swapData(AgedOrderedContainer& other) noexcept
|
||||
requires(!MaybePropagate);
|
||||
std::enable_if_t<!MaybePropagate>
|
||||
swapData(AgedOrderedContainer& other) noexcept;
|
||||
|
||||
private:
|
||||
ConfigT config_;
|
||||
@@ -1362,10 +1369,9 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::operato
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
template <class K, bool MaybeMulti, bool MaybeMap>
|
||||
template <class K, bool MaybeMulti, bool MaybeMap, class>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::at(K const& k)
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
auto const iter(cont_.find(k, std::cref(config_.keyCompare())));
|
||||
if (iter == cont_.end())
|
||||
@@ -1374,10 +1380,9 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::at(K co
|
||||
}
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
template <class K, bool MaybeMulti, bool MaybeMap>
|
||||
template <class K, bool MaybeMulti, bool MaybeMap, class>
|
||||
std::conditional<IsMap, T, void*>::type const&
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::at(K const& k) const
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
auto const iter(cont_.find(k, std::cref(config_.keyCompare())));
|
||||
if (iter == cont_.end())
|
||||
@@ -1386,10 +1391,9 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::at(K co
|
||||
}
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
template <bool MaybeMulti, bool MaybeMap>
|
||||
template <bool MaybeMulti, bool MaybeMap, class>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::operator[](Key const& key)
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(key, std::cref(config_.keyCompare()), d));
|
||||
@@ -1405,10 +1409,9 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::operato
|
||||
}
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
template <bool MaybeMulti, bool MaybeMap>
|
||||
template <bool MaybeMulti, bool MaybeMap, class>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::operator[](Key&& key)
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(key, std::cref(config_.keyCompare()), d));
|
||||
@@ -1442,8 +1445,7 @@ template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compa
|
||||
template <bool MaybeMulti>
|
||||
auto
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(
|
||||
value_type const& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti)
|
||||
value_type const& value) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(extract(value), std::cref(config_.keyCompare()), d));
|
||||
@@ -1462,8 +1464,7 @@ template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compa
|
||||
template <bool MaybeMulti>
|
||||
auto
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(
|
||||
value_type const& value) -> iterator
|
||||
requires MaybeMulti
|
||||
value_type const& value) -> std::enable_if_t<MaybeMulti, iterator>
|
||||
{
|
||||
auto const before(cont_.upper_bound(extract(value), std::cref(config_.keyCompare())));
|
||||
Element* const p(newElement(value));
|
||||
@@ -1477,8 +1478,7 @@ template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compa
|
||||
template <bool MaybeMulti, bool MaybeMap>
|
||||
auto
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(value_type&& value)
|
||||
-> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti && !MaybeMap)
|
||||
-> std::enable_if_t<!MaybeMulti && !MaybeMap, std::pair<iterator, bool>>
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(extract(value), std::cref(config_.keyCompare()), d));
|
||||
@@ -1497,8 +1497,7 @@ template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compa
|
||||
template <bool MaybeMulti, bool MaybeMap>
|
||||
auto
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(value_type&& value)
|
||||
-> iterator
|
||||
requires(MaybeMulti && !MaybeMap)
|
||||
-> std::enable_if_t<MaybeMulti && !MaybeMap, iterator>
|
||||
{
|
||||
auto const before(cont_.upper_bound(extract(value), std::cref(config_.keyCompare())));
|
||||
Element* const p(newElement(std::move(value)));
|
||||
@@ -1515,8 +1514,7 @@ template <bool MaybeMulti>
|
||||
auto
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(
|
||||
const_iterator hint,
|
||||
value_type const& value) -> iterator
|
||||
requires(!MaybeMulti)
|
||||
value_type const& value) -> std::enable_if_t<!MaybeMulti, iterator>
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
auto const result(
|
||||
@@ -1537,8 +1535,7 @@ template <bool MaybeMulti>
|
||||
auto
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::insert(
|
||||
const_iterator hint,
|
||||
value_type&& value) -> iterator
|
||||
requires(!MaybeMulti)
|
||||
value_type&& value) -> std::enable_if_t<!MaybeMulti, iterator>
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
auto const result(
|
||||
@@ -1558,8 +1555,7 @@ template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compa
|
||||
template <bool MaybeMulti, class... Args>
|
||||
auto
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::emplace(Args&&... args)
|
||||
-> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti)
|
||||
-> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>
|
||||
{
|
||||
// VFALCO NOTE Its unfortunate that we need to
|
||||
// construct element here
|
||||
@@ -1581,8 +1577,7 @@ template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compa
|
||||
template <bool MaybeMulti, class... Args>
|
||||
auto
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::emplace(Args&&... args)
|
||||
-> iterator
|
||||
requires MaybeMulti
|
||||
-> std::enable_if_t<MaybeMulti, iterator>
|
||||
{
|
||||
Element* const p(newElement(std::forward<Args>(args)...));
|
||||
auto const before(cont_.upper_bound(extract(p->value), std::cref(config_.keyCompare())));
|
||||
@@ -1597,8 +1592,7 @@ template <bool MaybeMulti, class... Args>
|
||||
auto
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::emplaceHint(
|
||||
const_iterator hint,
|
||||
Args&&... args) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti)
|
||||
Args&&... args) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>
|
||||
{
|
||||
// VFALCO NOTE Its unfortunate that we need to
|
||||
// construct element here
|
||||
@@ -1617,23 +1611,21 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::emplace
|
||||
}
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
template <bool IsConst, class Iterator>
|
||||
template <bool IsConst, class Iterator, class>
|
||||
beast::detail::AgedContainerIterator<false, Iterator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::erase(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> pos)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value)
|
||||
{
|
||||
unlinkAndDeleteElement(&*((pos++).iterator()));
|
||||
return beast::detail::AgedContainerIterator<false, Iterator>(pos.iterator());
|
||||
}
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
template <bool IsConst, class Iterator>
|
||||
template <bool IsConst, class Iterator, class>
|
||||
beast::detail::AgedContainerIterator<false, Iterator>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::erase(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> first,
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> last)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value)
|
||||
{
|
||||
for (; first != last;)
|
||||
unlinkAndDeleteElement(&*((first++).iterator()));
|
||||
@@ -1736,12 +1728,11 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::operato
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
template <bool IsConst, class Iterator>
|
||||
template <bool IsConst, class Iterator, class>
|
||||
void
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::touch(
|
||||
beast::detail::AgedContainerIterator<IsConst, Iterator> pos,
|
||||
clock_type::time_point const& now)
|
||||
requires(!IsBoostReverseIterator<Iterator>::value)
|
||||
{
|
||||
auto& e(*pos.iterator());
|
||||
e.when = now;
|
||||
@@ -1751,10 +1742,9 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::touch(
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
template <bool MaybePropagate>
|
||||
void
|
||||
std::enable_if_t<MaybePropagate>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::swapData(
|
||||
AgedOrderedContainer& other) noexcept
|
||||
requires MaybePropagate
|
||||
{
|
||||
std::swap(config_.keyCompare(), other.config_.keyCompare());
|
||||
std::swap(config_.alloc(), other.config_.alloc());
|
||||
@@ -1763,10 +1753,9 @@ AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::swapDat
|
||||
|
||||
template <bool IsMulti, bool IsMap, class Key, class T, class Clock, class Compare, class Allocator>
|
||||
template <bool MaybePropagate>
|
||||
void
|
||||
std::enable_if_t<!MaybePropagate>
|
||||
AgedOrderedContainer<IsMulti, IsMap, Key, T, Clock, Compare, Allocator>::swapData(
|
||||
AgedOrderedContainer& other) noexcept
|
||||
requires(!MaybePropagate)
|
||||
{
|
||||
std::swap(config_.keyCompare(), other.config_.keyCompare());
|
||||
std::swap(config_.clock, other.config_.clock);
|
||||
@@ -1796,9 +1785,7 @@ swap(
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire aged container items past the specified age.
|
||||
*/
|
||||
/** Expire aged container items past the specified age. */
|
||||
template <
|
||||
bool IsMulti,
|
||||
bool IsMap,
|
||||
|
||||
@@ -43,24 +43,23 @@ TODO
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* with a `when` `time_point` which is obtained from the value of the clock's
|
||||
* `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
|
||||
* temporal order. This container is useful as a building block for caches
|
||||
* 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
|
||||
*/
|
||||
/** 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
|
||||
with a `when` `time_point` which is obtained from the value of the clock's
|
||||
`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
|
||||
temporal order. This container is useful as a building block for caches
|
||||
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
|
||||
*/
|
||||
template <
|
||||
bool IsMulti,
|
||||
bool IsMap,
|
||||
@@ -118,9 +117,10 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
template <
|
||||
class... Args,
|
||||
class = std::enable_if_t<std::is_constructible_v<value_type, Args...>>>
|
||||
Element(time_point const& when, Args&&... args)
|
||||
requires(std::is_constructible_v<value_type, Args...>)
|
||||
: value(std::forward<Args>(args)...), when(when)
|
||||
{
|
||||
}
|
||||
@@ -529,7 +529,6 @@ private:
|
||||
deleteElement(Element const* p)
|
||||
{
|
||||
ElementAllocatorTraits::destroy(config_.alloc(), p);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
|
||||
ElementAllocatorTraits::deallocate(config_.alloc(), const_cast<Element*>(p), 1);
|
||||
}
|
||||
|
||||
@@ -842,25 +841,35 @@ public:
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <class K, bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
template <
|
||||
class K,
|
||||
bool MaybeMulti = IsMulti,
|
||||
bool MaybeMap = IsMap,
|
||||
class = std::enable_if_t<MaybeMap && !MaybeMulti>>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
at(K const& k)
|
||||
requires(MaybeMap && !MaybeMulti);
|
||||
at(K const& k);
|
||||
|
||||
template <class K, bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
template <
|
||||
class K,
|
||||
bool MaybeMulti = IsMulti,
|
||||
bool MaybeMap = IsMap,
|
||||
class = std::enable_if_t<MaybeMap && !MaybeMulti>>
|
||||
std::conditional<IsMap, T, void*>::type const&
|
||||
at(K const& k) const
|
||||
requires(MaybeMap && !MaybeMulti);
|
||||
at(K const& k) const;
|
||||
|
||||
template <bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
template <
|
||||
bool MaybeMulti = IsMulti,
|
||||
bool MaybeMap = IsMap,
|
||||
class = std::enable_if_t<MaybeMap && !MaybeMulti>>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
operator[](Key const& key)
|
||||
requires(MaybeMap && !MaybeMulti);
|
||||
operator[](Key const& key);
|
||||
|
||||
template <bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
template <
|
||||
bool MaybeMulti = IsMulti,
|
||||
bool MaybeMap = IsMap,
|
||||
class = std::enable_if_t<MaybeMap && !MaybeMulti>>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
operator[](Key&& key)
|
||||
requires(MaybeMap && !MaybeMulti);
|
||||
operator[](Key&& key);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
@@ -958,32 +967,28 @@ public:
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
auto
|
||||
insert(value_type const& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti);
|
||||
insert(value_type const& value) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>;
|
||||
|
||||
// multimap, multiset
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
auto
|
||||
insert(value_type const& value) -> iterator
|
||||
requires MaybeMulti;
|
||||
insert(value_type const& value) -> std::enable_if_t<MaybeMulti, iterator>;
|
||||
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
auto
|
||||
insert(value_type&& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti && !MaybeMap);
|
||||
insert(value_type&& value)
|
||||
-> std::enable_if_t<!MaybeMulti && !MaybeMap, std::pair<iterator, bool>>;
|
||||
|
||||
// multimap, multiset
|
||||
template <bool MaybeMulti = IsMulti, bool MaybeMap = IsMap>
|
||||
auto
|
||||
insert(value_type&& value) -> iterator
|
||||
requires(MaybeMulti && !MaybeMap);
|
||||
insert(value_type&& value) -> std::enable_if_t<MaybeMulti && !MaybeMap, iterator>;
|
||||
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
iterator
|
||||
std::enable_if_t<!MaybeMulti, iterator>
|
||||
insert(const_iterator /*hint*/, value_type const& value)
|
||||
requires(!MaybeMulti)
|
||||
{
|
||||
// Hint is ignored but we provide the interface so
|
||||
// callers may use ordered and unordered interchangeably.
|
||||
@@ -992,9 +997,8 @@ public:
|
||||
|
||||
// multimap, multiset
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
iterator
|
||||
std::enable_if_t<MaybeMulti, iterator>
|
||||
insert(const_iterator /*hint*/, value_type const& value)
|
||||
requires MaybeMulti
|
||||
{
|
||||
// VFALCO TODO The hint could be used to let
|
||||
// the client order equal ranges
|
||||
@@ -1003,9 +1007,8 @@ public:
|
||||
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
iterator
|
||||
std::enable_if_t<!MaybeMulti, iterator>
|
||||
insert(const_iterator /*hint*/, value_type&& value)
|
||||
requires(!MaybeMulti)
|
||||
{
|
||||
// Hint is ignored but we provide the interface so
|
||||
// callers may use ordered and unordered interchangeably.
|
||||
@@ -1014,9 +1017,8 @@ public:
|
||||
|
||||
// multimap, multiset
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
iterator
|
||||
std::enable_if_t<MaybeMulti, iterator>
|
||||
insert(const_iterator /*hint*/, value_type&& value)
|
||||
requires MaybeMulti
|
||||
{
|
||||
// VFALCO TODO The hint could be used to let
|
||||
// the client order equal ranges
|
||||
@@ -1025,18 +1027,20 @@ public:
|
||||
|
||||
// map, multimap
|
||||
template <class P, bool MaybeMap = IsMap>
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>
|
||||
std::enable_if_t<
|
||||
MaybeMap && std::is_constructible_v<value_type, P&&>,
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>>
|
||||
insert(P&& value)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P &&>)
|
||||
{
|
||||
return emplace(std::forward<P>(value));
|
||||
}
|
||||
|
||||
// map, multimap
|
||||
template <class P, bool MaybeMap = IsMap>
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>
|
||||
std::enable_if_t<
|
||||
MaybeMap && std::is_constructible_v<value_type, P&&>,
|
||||
std::conditional_t<IsMulti, iterator, std::pair<iterator, bool>>>
|
||||
insert(const_iterator hint, P&& value)
|
||||
requires(MaybeMap && std::is_constructible_v<value_type, P &&>)
|
||||
{
|
||||
return emplaceHint(hint, std::forward<P>(value));
|
||||
}
|
||||
@@ -1057,26 +1061,23 @@ public:
|
||||
// set, map
|
||||
template <bool MaybeMulti = IsMulti, class... Args>
|
||||
auto
|
||||
emplace(Args&&... args) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti);
|
||||
emplace(Args&&... args) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>;
|
||||
|
||||
// multiset, multimap
|
||||
template <bool MaybeMulti = IsMulti, class... Args>
|
||||
auto
|
||||
emplace(Args&&... args) -> iterator
|
||||
requires MaybeMulti;
|
||||
emplace(Args&&... args) -> std::enable_if_t<MaybeMulti, iterator>;
|
||||
|
||||
// set, map
|
||||
template <bool MaybeMulti = IsMulti, class... Args>
|
||||
auto
|
||||
emplaceHint(const_iterator /*hint*/, Args&&... args) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti);
|
||||
emplaceHint(const_iterator /*hint*/, Args&&... args)
|
||||
-> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>;
|
||||
|
||||
// multiset, multimap
|
||||
template <bool MaybeMulti = IsMulti, class... Args>
|
||||
iterator
|
||||
std::enable_if_t<MaybeMulti, iterator>
|
||||
emplaceHint(const_iterator /*hint*/, Args&&... args)
|
||||
requires MaybeMulti
|
||||
{
|
||||
// VFALCO TODO The hint could be used for multi, to let
|
||||
// the client order equal ranges
|
||||
@@ -1307,7 +1308,7 @@ public:
|
||||
class OtherHash,
|
||||
class OtherAllocator,
|
||||
bool MaybeMulti = IsMulti>
|
||||
bool
|
||||
std::enable_if_t<!MaybeMulti, bool>
|
||||
operator==(AgedUnorderedContainer<
|
||||
false,
|
||||
OtherIsMap,
|
||||
@@ -1316,8 +1317,7 @@ public:
|
||||
OtherDuration,
|
||||
OtherHash,
|
||||
KeyEqual,
|
||||
OtherAllocator> const& other) const
|
||||
requires(!MaybeMulti);
|
||||
OtherAllocator> const& other) const;
|
||||
|
||||
template <
|
||||
bool OtherIsMap,
|
||||
@@ -1327,7 +1327,7 @@ public:
|
||||
class OtherHash,
|
||||
class OtherAllocator,
|
||||
bool MaybeMulti = IsMulti>
|
||||
bool
|
||||
std::enable_if_t<MaybeMulti, bool>
|
||||
operator==(AgedUnorderedContainer<
|
||||
true,
|
||||
OtherIsMap,
|
||||
@@ -1336,8 +1336,7 @@ public:
|
||||
OtherDuration,
|
||||
OtherHash,
|
||||
KeyEqual,
|
||||
OtherAllocator> const& other) const
|
||||
requires MaybeMulti;
|
||||
OtherAllocator> const& other) const;
|
||||
|
||||
template <
|
||||
bool OtherIsMulti,
|
||||
@@ -1382,14 +1381,13 @@ private:
|
||||
// map, set
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
auto
|
||||
insertUnchecked(value_type const& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti);
|
||||
insertUnchecked(value_type const& value)
|
||||
-> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>;
|
||||
|
||||
// multimap, multiset
|
||||
template <bool MaybeMulti = IsMulti>
|
||||
auto
|
||||
insertUnchecked(value_type const& value) -> iterator
|
||||
requires MaybeMulti;
|
||||
insertUnchecked(value_type const& value) -> std::enable_if_t<MaybeMulti, iterator>;
|
||||
|
||||
template <class InputIt>
|
||||
void
|
||||
@@ -1430,9 +1428,8 @@ private:
|
||||
|
||||
template <
|
||||
bool MaybePropagate = std::allocator_traits<Allocator>::propagate_on_container_swap::value>
|
||||
void
|
||||
std::enable_if_t<MaybePropagate>
|
||||
swapData(AgedUnorderedContainer& other) noexcept
|
||||
requires MaybePropagate
|
||||
{
|
||||
std::swap(config_.hashFunction(), other.config_.hashFunction());
|
||||
std::swap(config_.keyEq(), other.config_.keyEq());
|
||||
@@ -1442,9 +1439,8 @@ private:
|
||||
|
||||
template <
|
||||
bool MaybePropagate = std::allocator_traits<Allocator>::propagate_on_container_swap::value>
|
||||
void
|
||||
std::enable_if_t<!MaybePropagate>
|
||||
swapData(AgedUnorderedContainer& other) noexcept
|
||||
requires(!MaybePropagate)
|
||||
{
|
||||
std::swap(config_.hashFunction(), other.config_.hashFunction());
|
||||
std::swap(config_.keyEq(), other.config_.keyEq());
|
||||
@@ -2098,10 +2094,9 @@ template <
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
template <class K, bool MaybeMulti, bool MaybeMap>
|
||||
template <class K, bool MaybeMulti, bool MaybeMap, class>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::at(K const& k)
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
auto const iter(
|
||||
cont_.find(k, std::cref(config_.hashFunction()), std::cref(config_.keyValueEqual())));
|
||||
@@ -2119,11 +2114,10 @@ template <
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
template <class K, bool MaybeMulti, bool MaybeMap>
|
||||
template <class K, bool MaybeMulti, bool MaybeMap, class>
|
||||
std::conditional<IsMap, T, void*>::type const&
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::at(
|
||||
K const& k) const
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
auto const iter(
|
||||
cont_.find(k, std::cref(config_.hashFunction()), std::cref(config_.keyValueEqual())));
|
||||
@@ -2141,11 +2135,10 @@ template <
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
template <bool MaybeMulti, bool MaybeMap>
|
||||
template <bool MaybeMulti, bool MaybeMap, class>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::operator[](
|
||||
Key const& key)
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
maybeRehash(1);
|
||||
typename cont_type::insert_commit_data d;
|
||||
@@ -2171,11 +2164,10 @@ template <
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
template <bool MaybeMulti, bool MaybeMap>
|
||||
template <bool MaybeMulti, bool MaybeMap, class>
|
||||
std::conditional_t<IsMap, T, void*>&
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::operator[](
|
||||
Key&& key)
|
||||
requires(MaybeMap && !MaybeMulti)
|
||||
{
|
||||
maybeRehash(1);
|
||||
typename cont_type::insert_commit_data d;
|
||||
@@ -2228,8 +2220,7 @@ template <
|
||||
template <bool MaybeMulti>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::insert(
|
||||
value_type const& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti)
|
||||
value_type const& value) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>
|
||||
{
|
||||
maybeRehash(1);
|
||||
typename cont_type::insert_commit_data d;
|
||||
@@ -2258,8 +2249,7 @@ template <
|
||||
template <bool MaybeMulti>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::insert(
|
||||
value_type const& value) -> iterator
|
||||
requires MaybeMulti
|
||||
value_type const& value) -> std::enable_if_t<MaybeMulti, iterator>
|
||||
{
|
||||
maybeRehash(1);
|
||||
Element* const p(newElement(value));
|
||||
@@ -2281,8 +2271,7 @@ template <
|
||||
template <bool MaybeMulti, bool MaybeMap>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::insert(
|
||||
value_type&& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti && !MaybeMap)
|
||||
value_type&& value) -> std::enable_if_t<!MaybeMulti && !MaybeMap, std::pair<iterator, bool>>
|
||||
{
|
||||
maybeRehash(1);
|
||||
typename cont_type::insert_commit_data d;
|
||||
@@ -2311,8 +2300,7 @@ template <
|
||||
template <bool MaybeMulti, bool MaybeMap>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::insert(
|
||||
value_type&& value) -> iterator
|
||||
requires(MaybeMulti && !MaybeMap)
|
||||
value_type&& value) -> std::enable_if_t<MaybeMulti && !MaybeMap, iterator>
|
||||
{
|
||||
maybeRehash(1);
|
||||
Element* const p(newElement(std::move(value)));
|
||||
@@ -2321,6 +2309,7 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
return iterator(iter);
|
||||
}
|
||||
|
||||
#if 1 // Use insert() instead of insert_check() insert_commit()
|
||||
// set, map
|
||||
template <
|
||||
bool IsMulti,
|
||||
@@ -2334,8 +2323,7 @@ template <
|
||||
template <bool MaybeMulti, class... Args>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::emplace(
|
||||
Args&&... args) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti)
|
||||
Args&&... args) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>
|
||||
{
|
||||
maybeRehash(1);
|
||||
// VFALCO NOTE Its unfortunate that we need to
|
||||
@@ -2350,6 +2338,42 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
deleteElement(p);
|
||||
return std::make_pair(iterator(result.first), false);
|
||||
}
|
||||
#else // As original, use insert_check() / insert_commit () pair.
|
||||
// set, map
|
||||
template <
|
||||
bool IsMulti,
|
||||
bool IsMap,
|
||||
class Key,
|
||||
class T,
|
||||
class Clock,
|
||||
class Hash,
|
||||
class KeyEqual,
|
||||
class Allocator>
|
||||
template <bool maybe_multi, class... Args>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::emplace(
|
||||
Args&&... args) -> typename std::enable_if<!maybe_multi, std::pair<iterator, bool>>::type
|
||||
{
|
||||
maybe_rehash(1);
|
||||
// VFALCO NOTE Its unfortunate that we need to
|
||||
// construct element here
|
||||
element* const p(new_element(std::forward<Args>(args)...));
|
||||
typename cont_type::insert_commit_data d;
|
||||
auto const result(m_cont.insert_check(
|
||||
extract(p->value),
|
||||
std::cref(m_config.hashFunction()),
|
||||
std::cref(m_config.keyValueEqual()),
|
||||
d));
|
||||
if (result.second)
|
||||
{
|
||||
auto const iter(m_cont.insert_commit(*p, d));
|
||||
chronological.list.push_back(*p);
|
||||
return std::make_pair(iterator(iter), true);
|
||||
}
|
||||
delete_element(p);
|
||||
return std::make_pair(iterator(result.first), false);
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
// multiset, multimap
|
||||
template <
|
||||
@@ -2364,8 +2388,7 @@ template <
|
||||
template <bool MaybeMulti, class... Args>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::emplace(
|
||||
Args&&... args) -> iterator
|
||||
requires MaybeMulti
|
||||
Args&&... args) -> std::enable_if_t<MaybeMulti, iterator>
|
||||
{
|
||||
maybeRehash(1);
|
||||
Element* const p(newElement(std::forward<Args>(args)...));
|
||||
@@ -2388,8 +2411,7 @@ template <bool MaybeMulti, class... Args>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::emplaceHint(
|
||||
const_iterator /*hint*/,
|
||||
Args&&... args) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti)
|
||||
Args&&... args) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>
|
||||
{
|
||||
maybeRehash(1);
|
||||
// VFALCO NOTE Its unfortunate that we need to
|
||||
@@ -2540,7 +2562,7 @@ template <
|
||||
class OtherHash,
|
||||
class OtherAllocator,
|
||||
bool MaybeMulti>
|
||||
bool
|
||||
std::enable_if_t<!MaybeMulti, bool>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::operator==(
|
||||
AgedUnorderedContainer<
|
||||
false,
|
||||
@@ -2551,7 +2573,6 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
OtherHash,
|
||||
KeyEqual,
|
||||
OtherAllocator> const& other) const
|
||||
requires(!MaybeMulti)
|
||||
{
|
||||
if (size() != other.size())
|
||||
return false;
|
||||
@@ -2581,7 +2602,7 @@ template <
|
||||
class OtherHash,
|
||||
class OtherAllocator,
|
||||
bool MaybeMulti>
|
||||
bool
|
||||
std::enable_if_t<MaybeMulti, bool>
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::operator==(
|
||||
AgedUnorderedContainer<
|
||||
true,
|
||||
@@ -2592,7 +2613,6 @@ AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>
|
||||
OtherHash,
|
||||
KeyEqual,
|
||||
OtherAllocator> const& other) const
|
||||
requires MaybeMulti
|
||||
{
|
||||
if (size() != other.size())
|
||||
return false;
|
||||
@@ -2629,8 +2649,7 @@ template <
|
||||
template <bool MaybeMulti>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::insertUnchecked(
|
||||
value_type const& value) -> std::pair<iterator, bool>
|
||||
requires(!MaybeMulti)
|
||||
value_type const& value) -> std::enable_if_t<!MaybeMulti, std::pair<iterator, bool>>
|
||||
{
|
||||
typename cont_type::insert_commit_data d;
|
||||
auto const result(cont_.insert_check(
|
||||
@@ -2658,8 +2677,7 @@ template <
|
||||
template <bool MaybeMulti>
|
||||
auto
|
||||
AgedUnorderedContainer<IsMulti, IsMap, Key, T, Clock, Hash, KeyEqual, Allocator>::insertUnchecked(
|
||||
value_type const& value) -> iterator
|
||||
requires MaybeMulti
|
||||
value_type const& value) -> std::enable_if_t<MaybeMulti, iterator>
|
||||
{
|
||||
Element* const p(newElement(value));
|
||||
chronological.list_.push_back(*p);
|
||||
@@ -2710,9 +2728,7 @@ swap(
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire aged container items past the specified age.
|
||||
*/
|
||||
/** Expire aged container items past the specified age. */
|
||||
template <
|
||||
bool IsMulti,
|
||||
bool IsMap,
|
||||
|
||||
@@ -12,10 +12,9 @@
|
||||
|
||||
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
|
||||
setCurrentThreadName(std::string_view newThreadName);
|
||||
|
||||
@@ -25,14 +24,13 @@ setCurrentThreadName(std::string_view newThreadName);
|
||||
// Maximum number of characters is therefore 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
|
||||
* @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.
|
||||
*/
|
||||
/** Sets the name of the caller thread with compile-time size checking.
|
||||
@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.
|
||||
*/
|
||||
template <std::size_t N>
|
||||
void
|
||||
setCurrentThreadName(char const (&newThreadName)[N])
|
||||
@@ -43,15 +41,14 @@ setCurrentThreadName(char const (&newThreadName)[N])
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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
|
||||
* will not be reported.
|
||||
*
|
||||
* If no name has ever been set, then the empty string is returned.
|
||||
*/
|
||||
/** 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
|
||||
will not be reported.
|
||||
|
||||
If no name has ever been set, then the empty string is returned.
|
||||
*/
|
||||
std::string
|
||||
getCurrentThreadName();
|
||||
|
||||
|
||||
@@ -29,18 +29,16 @@ struct LexicalCast<std::string, In>
|
||||
explicit LexicalCast() = default;
|
||||
|
||||
template <class Arithmetic = In>
|
||||
bool
|
||||
std::enable_if_t<std::is_arithmetic_v<Arithmetic>, bool>
|
||||
operator()(std::string& out, Arithmetic in)
|
||||
requires(std::is_arithmetic_v<Arithmetic>)
|
||||
{
|
||||
out = std::to_string(in);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Enumeration = In>
|
||||
bool
|
||||
std::enable_if_t<std::is_enum_v<Enumeration>, bool>
|
||||
operator()(std::string& out, Enumeration in)
|
||||
requires(std::is_enum_v<Enumeration>)
|
||||
{
|
||||
out = std::to_string(static_cast<std::underlying_type_t<Enumeration>>(in));
|
||||
return true;
|
||||
@@ -58,9 +56,8 @@ struct LexicalCast<Out, std::string_view>
|
||||
"beast::LexicalCast can only be used with integral types");
|
||||
|
||||
template <class Integral = Out>
|
||||
bool
|
||||
std::enable_if_t<std::is_integral_v<Integral> && !std::is_same_v<Integral, bool>, bool>
|
||||
operator()(Integral& out, std::string_view in) const
|
||||
requires(std::is_integral_v<Integral> && !std::is_same_v<Integral, bool>)
|
||||
{
|
||||
auto first = in.data();
|
||||
auto last = in.data() + in.size();
|
||||
@@ -163,19 +160,17 @@ 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
|
||||
{
|
||||
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>
|
||||
bool
|
||||
lexicalCastChecked(Out& out, In in)
|
||||
@@ -183,13 +178,12 @@ lexicalCastChecked(Out& out, In in)
|
||||
return detail::LexicalCast<Out, In>()(out, in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from one type to another, throw on error
|
||||
*
|
||||
* An exception of type BadLexicalCast is thrown if the conversion fails.
|
||||
*
|
||||
* @return The new type.
|
||||
*/
|
||||
/** Convert from one type to another, throw on error
|
||||
|
||||
An exception of type BadLexicalCast is thrown if the conversion fails.
|
||||
|
||||
@return The new type.
|
||||
*/
|
||||
template <class Out, class In>
|
||||
Out
|
||||
lexicalCastThrow(In in)
|
||||
@@ -200,12 +194,11 @@ lexicalCastThrow(In in)
|
||||
throw BadLexicalCast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from one type to another.
|
||||
*
|
||||
* @param defaultValue The value returned if parsing fails
|
||||
* @return The new type.
|
||||
*/
|
||||
/** Convert from one type to another.
|
||||
|
||||
@param defaultValue The value returned if parsing fails
|
||||
@return The new type.
|
||||
*/
|
||||
template <class Out, class In>
|
||||
Out
|
||||
lexicalCast(In in, Out defaultValue = Out())
|
||||
|
||||
@@ -11,9 +11,7 @@ class List;
|
||||
|
||||
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>
|
||||
struct CopyConst
|
||||
@@ -155,111 +153,110 @@ private:
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* 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
|
||||
* requires you to first derive your class from List<>::Node:
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* struct Object : List <Object>::Node
|
||||
* {
|
||||
* explicit Object (int value) : value_ (value)
|
||||
* {
|
||||
* }
|
||||
*
|
||||
* int value_;
|
||||
* };
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* Now we define the list, and add a couple of items.
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* List <Object> list;
|
||||
*
|
||||
* list.push_back (* (new Object (1)));
|
||||
* list.push_back (* (new Object (2)));
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* For compatibility with the standard containers, push_back() expects a
|
||||
* 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:
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* for (List <Object>::iterator iter = list.begin(); iter != list.end; ++iter)
|
||||
* std::cout << iter->value_;
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* You can even use BOOST_FOREACH, or range based for loops:
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* BOOST_FOREACH (Object& object, list) // boost only
|
||||
* std::cout << object.value_;
|
||||
*
|
||||
* for (Object& object : list) // C++11 only
|
||||
* std::cout << object.value_;
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* 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
|
||||
* the storage for the object, the caller is also responsible for deleting the
|
||||
* object. An object still exists after being removed from a List, until the
|
||||
* 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
|
||||
* 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
|
||||
* time. We wish to also maintain a list of the subset of actors that require
|
||||
* a domain-dependent update. To achieve this, we declare two tags, the
|
||||
* associated list types, and the list element thusly:
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* struct Actor; // Forward declaration required
|
||||
*
|
||||
* struct ProcessTag { };
|
||||
* struct 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.
|
||||
* //
|
||||
* struct Actor : ProcessList::Node, UpdateList::Node
|
||||
* {
|
||||
* bool process (); // returns true if we need an update
|
||||
* void update ();
|
||||
* };
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @ingroup beast_core intrusive
|
||||
*/
|
||||
/** 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
|
||||
requires you to first derive your class from List<>::Node:
|
||||
|
||||
@code
|
||||
|
||||
struct Object : List <Object>::Node
|
||||
{
|
||||
explicit Object (int value) : value_ (value)
|
||||
{
|
||||
}
|
||||
|
||||
int value_;
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
Now we define the list, and add a couple of items.
|
||||
|
||||
@code
|
||||
|
||||
List <Object> list;
|
||||
|
||||
list.push_back (* (new Object (1)));
|
||||
list.push_back (* (new Object (2)));
|
||||
|
||||
@endcode
|
||||
|
||||
For compatibility with the standard containers, push_back() expects a
|
||||
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:
|
||||
|
||||
@code
|
||||
|
||||
for (List <Object>::iterator iter = list.begin(); iter != list.end; ++iter)
|
||||
std::cout << iter->value_;
|
||||
|
||||
@endcode
|
||||
|
||||
You can even use BOOST_FOREACH, or range based for loops:
|
||||
|
||||
@code
|
||||
|
||||
BOOST_FOREACH (Object& object, list) // boost only
|
||||
std::cout << object.value_;
|
||||
|
||||
for (Object& object : list) // C++11 only
|
||||
std::cout << object.value_;
|
||||
|
||||
@endcode
|
||||
|
||||
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
|
||||
the storage for the object, the caller is also responsible for deleting the
|
||||
object. An object still exists after being removed from a List, until the
|
||||
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
|
||||
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
|
||||
time. We wish to also maintain a list of the subset of actors that require
|
||||
a domain-dependent update. To achieve this, we declare two tags, the
|
||||
associated list types, and the list element thusly:
|
||||
|
||||
@code
|
||||
|
||||
struct Actor; // Forward declaration required
|
||||
|
||||
struct ProcessTag { };
|
||||
struct 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.
|
||||
//
|
||||
struct Actor : ProcessList::Node, UpdateList::Node
|
||||
{
|
||||
bool process (); // returns true if we need an update
|
||||
void update ();
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
@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.
|
||||
|
||||
@ingroup beast_core intrusive
|
||||
*/
|
||||
template <typename T, typename Tag = void>
|
||||
class List
|
||||
{
|
||||
@@ -277,9 +274,7 @@ public:
|
||||
using iterator = detail::ListIterator<Node>;
|
||||
using const_iterator = detail::ListIterator<Node const>;
|
||||
|
||||
/**
|
||||
* Create an empty list.
|
||||
*/
|
||||
/** Create an empty list. */
|
||||
List()
|
||||
{
|
||||
head_.prev_ = nullptr; // identifies the head
|
||||
@@ -291,133 +286,119 @@ public:
|
||||
List&
|
||||
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
|
||||
empty() const noexcept
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of elements in the list.
|
||||
*/
|
||||
/** Returns the number of elements in the list. */
|
||||
[[nodiscard]] size_type
|
||||
size() const noexcept
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a reference to the first element.
|
||||
* @invariant The list may not be empty.
|
||||
* @return A reference to the first element.
|
||||
*/
|
||||
/** Obtain a reference to the first element.
|
||||
@invariant The list may not be empty.
|
||||
@return A reference to the first element.
|
||||
*/
|
||||
reference
|
||||
front() noexcept
|
||||
{
|
||||
return element_from(head_.next_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a const reference to the first element.
|
||||
* @invariant The list may not be empty.
|
||||
* @return A const reference to the first element.
|
||||
*/
|
||||
/** Obtain 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
|
||||
front() const noexcept
|
||||
{
|
||||
return element_from(head_.next_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a reference to the last element.
|
||||
* @invariant The list may not be empty.
|
||||
* @return A reference to the last element.
|
||||
*/
|
||||
/** Obtain a reference to the last element.
|
||||
@invariant The list may not be empty.
|
||||
@return A reference to the last element.
|
||||
*/
|
||||
reference
|
||||
back() noexcept
|
||||
{
|
||||
return element_from(tail_.prev_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a const reference to the last element.
|
||||
* @invariant The list may not be empty.
|
||||
* @return A const reference to the last element.
|
||||
*/
|
||||
/** Obtain 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
|
||||
back() const noexcept
|
||||
{
|
||||
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
|
||||
begin() noexcept
|
||||
{
|
||||
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
|
||||
begin() const noexcept
|
||||
{
|
||||
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
|
||||
cbegin() const noexcept
|
||||
{
|
||||
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
|
||||
end() noexcept
|
||||
{
|
||||
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
|
||||
end() const noexcept
|
||||
{
|
||||
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
|
||||
cend() const noexcept
|
||||
{
|
||||
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
|
||||
clear() noexcept
|
||||
{
|
||||
@@ -426,13 +407,12 @@ public:
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an element.
|
||||
* @invariant The element must not already be in the list.
|
||||
* @param pos The location to insert after.
|
||||
* @param element The element to insert.
|
||||
* @return An iterator pointing to the newly inserted element.
|
||||
*/
|
||||
/** Insert an element.
|
||||
@invariant The element must not already be in the list.
|
||||
@param pos The location to insert after.
|
||||
@param element The element to insert.
|
||||
@return An iterator pointing to the newly inserted element.
|
||||
*/
|
||||
iterator
|
||||
insert(iterator pos, T& element) noexcept
|
||||
{
|
||||
@@ -445,12 +425,11 @@ public:
|
||||
return iterator(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert another list into this one.
|
||||
* The other list is cleared.
|
||||
* @param pos The location to insert after.
|
||||
* @param other The list to insert.
|
||||
*/
|
||||
/** Insert another list into this one.
|
||||
The other list is cleared.
|
||||
@param pos The location to insert after.
|
||||
@param other The list to insert.
|
||||
*/
|
||||
void
|
||||
insert(iterator pos, List& other) noexcept
|
||||
{
|
||||
@@ -466,12 +445,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an element.
|
||||
* @invariant The element must exist in the list.
|
||||
* @param pos An iterator pointing to the element to remove.
|
||||
* @return An iterator pointing to the next element after the one removed.
|
||||
*/
|
||||
/** Remove an element.
|
||||
@invariant The element must exist in the list.
|
||||
@param pos An iterator pointing to the element to remove.
|
||||
@return An iterator pointing to the next element after the one removed.
|
||||
*/
|
||||
iterator
|
||||
erase(iterator pos) noexcept
|
||||
{
|
||||
@@ -483,22 +461,20 @@ public:
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an element at the beginning of the list.
|
||||
* @invariant The element must not exist in the list.
|
||||
* @param element The element to insert.
|
||||
*/
|
||||
/** Insert an element at the beginning of the list.
|
||||
@invariant The element must not exist in the list.
|
||||
@param element The element to insert.
|
||||
*/
|
||||
iterator
|
||||
pushFront(T& element) noexcept
|
||||
{
|
||||
return insert(begin(), element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the element at the beginning of the list.
|
||||
* @invariant The list must not be empty.
|
||||
* @return A reference to the popped element.
|
||||
*/
|
||||
/** Remove the element at the beginning of the list.
|
||||
@invariant The list must not be empty.
|
||||
@return A reference to the popped element.
|
||||
*/
|
||||
T&
|
||||
popFront() noexcept
|
||||
{
|
||||
@@ -507,22 +483,20 @@ public:
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an element at the end of the list.
|
||||
* @invariant The element must not exist in the list.
|
||||
* @param element The element to append.
|
||||
*/
|
||||
/** Append an element at the end of the list.
|
||||
@invariant The element must not exist in the list.
|
||||
@param element The element to append.
|
||||
*/
|
||||
iterator
|
||||
pushBack(T& element) noexcept
|
||||
{
|
||||
return insert(end(), element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the element at the end of the list.
|
||||
* @invariant The list must not be empty.
|
||||
* @return A reference to the popped element.
|
||||
*/
|
||||
/** Remove the element at the end of the list.
|
||||
@invariant The list must not be empty.
|
||||
@return A reference to the popped element.
|
||||
*/
|
||||
T&
|
||||
popBack() noexcept
|
||||
{
|
||||
@@ -531,9 +505,7 @@ public:
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap contents with another list.
|
||||
*/
|
||||
/** Swap contents with another list. */
|
||||
void
|
||||
swap(List& other) noexcept
|
||||
{
|
||||
@@ -543,46 +515,42 @@ public:
|
||||
append(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert another list at the beginning of this list.
|
||||
* The other list is cleared.
|
||||
* @param list The other list to insert.
|
||||
*/
|
||||
/** Insert another list at the beginning of this list.
|
||||
The other list is cleared.
|
||||
@param list The other list to insert.
|
||||
*/
|
||||
iterator
|
||||
prepend(List& list) noexcept
|
||||
{
|
||||
return insert(begin(), list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append another list at the end of this list.
|
||||
* The other list is cleared.
|
||||
* @param list the other list to append.
|
||||
*/
|
||||
/** Append another list at the end of this list.
|
||||
The other list is cleared.
|
||||
@param list the other list to append.
|
||||
*/
|
||||
iterator
|
||||
append(List& list) noexcept
|
||||
{
|
||||
return insert(end(), list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an iterator from an element.
|
||||
* @invariant The element must exist in the list.
|
||||
* @param element The element to obtain an iterator for.
|
||||
* @return An iterator to the element.
|
||||
*/
|
||||
/** Obtain an iterator from an element.
|
||||
@invariant The element must exist in the list.
|
||||
@param element The element to obtain an iterator for.
|
||||
@return An iterator to the element.
|
||||
*/
|
||||
iterator
|
||||
iteratorTo(T& element) const noexcept
|
||||
{
|
||||
return iterator(static_cast<Node*>(&element));
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a const iterator from an element.
|
||||
* @invariant The element must exist in the list.
|
||||
* @param element The element to obtain an iterator for.
|
||||
* @return A const iterator to the element.
|
||||
*/
|
||||
/** Obtain a const iterator from an element.
|
||||
@invariant The element must exist in the list.
|
||||
@param element The element to obtain an iterator for.
|
||||
@return A const iterator to the element.
|
||||
*/
|
||||
[[nodiscard]] const_iterator
|
||||
constIteratorTo(T const& element) const noexcept
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
operator=(NodePtr node)
|
||||
{
|
||||
node_ = node;
|
||||
return *this;
|
||||
return static_cast<LockFreeStackIterator&>(*this);
|
||||
}
|
||||
|
||||
LockFreeStackIterator&
|
||||
@@ -103,19 +103,18 @@ operator!=(
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Multiple Producer, Multiple Consumer (MPMC) intrusive stack.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @param Tag A type name used to distinguish lists and nodes, for
|
||||
* putting objects in multiple lists. If this parameter is
|
||||
* omitted, the default tag is used.
|
||||
*/
|
||||
/** Multiple Producer, Multiple Consumer (MPMC) intrusive stack.
|
||||
|
||||
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
|
||||
|
||||
@param Tag A type name used to distinguish lists and nodes, for
|
||||
putting objects in multiple lists. If this parameter is
|
||||
omitted, the default tag is used.
|
||||
*/
|
||||
template <class Element, class Tag = void>
|
||||
class LockFreeStack
|
||||
{
|
||||
@@ -163,27 +162,24 @@ public:
|
||||
LockFreeStack&
|
||||
operator=(LockFreeStack const&) = delete;
|
||||
|
||||
/**
|
||||
* Returns true if the stack is empty.
|
||||
*/
|
||||
/** Returns true if the stack is empty. */
|
||||
[[nodiscard]] bool
|
||||
empty() const
|
||||
{
|
||||
return head_.load() == &end_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a node onto the stack.
|
||||
* The caller is responsible for preventing the ABA problem.
|
||||
* This operation is lock-free.
|
||||
* Thread safety:
|
||||
* Safe to call from any thread.
|
||||
*
|
||||
* @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`.
|
||||
*/
|
||||
/** Push a node onto the stack.
|
||||
The caller is responsible for preventing the ABA problem.
|
||||
This operation is lock-free.
|
||||
Thread safety:
|
||||
Safe to call from any thread.
|
||||
|
||||
@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`.
|
||||
*/
|
||||
// VFALCO NOTE Fix this, shouldn't it be a reference like intrusive list?
|
||||
bool
|
||||
pushFront(Node* node)
|
||||
@@ -199,16 +195,15 @@ public:
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop an element off the stack.
|
||||
* The caller is responsible for preventing the ABA problem.
|
||||
* This operation is lock-free.
|
||||
* Thread safety:
|
||||
* Safe to call from any thread.
|
||||
*
|
||||
* @return The element that was popped, or `nullptr` if the stack
|
||||
* was empty.
|
||||
*/
|
||||
/** Pop an element off the stack.
|
||||
The caller is responsible for preventing the ABA problem.
|
||||
This operation is lock-free.
|
||||
Thread safety:
|
||||
Safe to call from any thread.
|
||||
|
||||
@return The element that was popped, or `nullptr` if the stack
|
||||
was empty.
|
||||
*/
|
||||
Element*
|
||||
popFront()
|
||||
{
|
||||
@@ -224,13 +219,12 @@ public:
|
||||
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
|
||||
* while an iteration is in progress.
|
||||
* Thread safety:
|
||||
* Caller is responsible for synchronization.
|
||||
*/
|
||||
/** Return a forward iterator to the beginning or end of the stack.
|
||||
Undefined behavior results if push_front or pop_front is called
|
||||
while an iteration is in progress.
|
||||
Thread safety:
|
||||
Caller is responsible for synchronization.
|
||||
*/
|
||||
/** @{ */
|
||||
iterator
|
||||
begin()
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* A Semantic Version number.
|
||||
*
|
||||
* Identifies the build of a particular version of software using
|
||||
* the Semantic Versioning Specification described here:
|
||||
*
|
||||
* http://semver.org/
|
||||
*/
|
||||
/** A Semantic Version number.
|
||||
|
||||
Identifies the build of a particular version of software using
|
||||
the Semantic Versioning Specification described here:
|
||||
|
||||
http://semver.org/
|
||||
*/
|
||||
class SemanticVersion
|
||||
{
|
||||
public:
|
||||
@@ -30,17 +29,14 @@ public:
|
||||
|
||||
SemanticVersion(std::string_view version);
|
||||
|
||||
/**
|
||||
* Parse a semantic version string.
|
||||
* The parsing is as strict as possible.
|
||||
* @return `true` if the string was parsed.
|
||||
*/
|
||||
/** Parse a semantic version string.
|
||||
The parsing is as strict as possible.
|
||||
@return `true` if the string was parsed.
|
||||
*/
|
||||
bool
|
||||
parse(std::string_view input);
|
||||
|
||||
/**
|
||||
* Produce a string from semantic version components.
|
||||
*/
|
||||
/** Produce a string from semantic version components. */
|
||||
[[nodiscard]] std::string
|
||||
print() const;
|
||||
|
||||
@@ -56,10 +52,9 @@ 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
|
||||
compare(SemanticVersion const& lhs, SemanticVersion const& rhs);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ template <class T>
|
||||
inline void
|
||||
reverseBytes(T& t)
|
||||
{
|
||||
auto* bytes =
|
||||
unsigned char* bytes =
|
||||
static_cast<unsigned char*>(std::memmove(std::addressof(t), std::addressof(t), sizeof(T)));
|
||||
for (unsigned i = 0; i < sizeof(T) / 2; ++i)
|
||||
std::swap(bytes[i], bytes[sizeof(T) - 1 - i]);
|
||||
@@ -135,20 +135,19 @@ struct IsUniquelyRepresented<std::array<T, N>>
|
||||
explicit IsUniquelyRepresented() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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`,
|
||||
* 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
|
||||
* any other bit pattern's value. A IEEE floating point should not be
|
||||
* contiguously hashable because -0. and 0. have different bit patterns,
|
||||
* though they compare equal.
|
||||
*/
|
||||
/** 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`,
|
||||
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
|
||||
any other bit pattern's value. A IEEE floating point should not be
|
||||
contiguously hashable because -0. and 0. have different bit patterns,
|
||||
though they compare equal.
|
||||
*/
|
||||
/** @{ */
|
||||
template <class T, class HashAlgorithm>
|
||||
struct IsContiguouslyHashable
|
||||
@@ -173,58 +172,54 @@ struct IsContiguouslyHashable<T[N], HashAlgorithm>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Logically concatenate input data to a `Hasher`.
|
||||
*
|
||||
* Hasher requirements:
|
||||
*
|
||||
* `X` is the type `Hasher`
|
||||
* `h` is a value of type `x`
|
||||
* `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);`
|
||||
* Throws:
|
||||
* Never
|
||||
* Effect:
|
||||
* Adds the input data to the hasher state.
|
||||
*
|
||||
* Expression:
|
||||
* `static_cast<std::size_t>(j)`
|
||||
* Throws:
|
||||
* Never
|
||||
* Effect:
|
||||
* Returns the resulting hash of all the input data.
|
||||
*/
|
||||
/** Logically concatenate input data to a `Hasher`.
|
||||
|
||||
Hasher requirements:
|
||||
|
||||
`X` is the type `Hasher`
|
||||
`h` is a value of type `x`
|
||||
`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);`
|
||||
Throws:
|
||||
Never
|
||||
Effect:
|
||||
Adds the input data to the hasher state.
|
||||
|
||||
Expression:
|
||||
`static_cast<std::size_t>(j)`
|
||||
Throws:
|
||||
Never
|
||||
Effect:
|
||||
Returns the resulting hash of all the input data.
|
||||
*/
|
||||
/** @{ */
|
||||
|
||||
// scalars
|
||||
|
||||
template <class Hasher, class T>
|
||||
inline void
|
||||
inline std::enable_if_t<IsContiguouslyHashable<T, Hasher>::value>
|
||||
hash_append(Hasher& h, T const& t) noexcept
|
||||
requires(IsContiguouslyHashable<T, Hasher>::value)
|
||||
{
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression)
|
||||
h(static_cast<void const*>(std::addressof(t)), sizeof(t));
|
||||
}
|
||||
|
||||
template <class Hasher, class T>
|
||||
inline void
|
||||
inline std::enable_if_t<
|
||||
!IsContiguouslyHashable<T, Hasher>::value &&
|
||||
(std::is_integral_v<T> || std::is_pointer_v<T> || std::is_enum_v<T>)>
|
||||
hash_append(Hasher& h, T t) noexcept
|
||||
requires(
|
||||
!IsContiguouslyHashable<T, Hasher>::value &&
|
||||
(std::is_integral_v<T> || std::is_pointer_v<T> || std::is_enum_v<T>))
|
||||
{
|
||||
detail::reverseBytes(t);
|
||||
h(std::addressof(t), sizeof(t));
|
||||
}
|
||||
|
||||
template <class Hasher, class T>
|
||||
inline void
|
||||
inline std::enable_if_t<std::is_floating_point_v<T>>
|
||||
hash_append(Hasher& h, T t) noexcept
|
||||
requires(std::is_floating_point_v<T>)
|
||||
{
|
||||
if (t == 0)
|
||||
t = 0;
|
||||
@@ -244,44 +239,36 @@ hash_append(Hasher& h, std::nullptr_t) noexcept
|
||||
// Forward declarations for ADL purposes
|
||||
|
||||
template <class Hasher, class T, std::size_t N>
|
||||
void
|
||||
hash_append(Hasher& h, T (&a)[N]) noexcept
|
||||
requires(!IsContiguouslyHashable<T, Hasher>::value);
|
||||
std::enable_if_t<!IsContiguouslyHashable<T, Hasher>::value>
|
||||
hash_append(Hasher& h, T (&a)[N]) noexcept;
|
||||
|
||||
template <class Hasher, class CharT, class Traits, class Alloc>
|
||||
void
|
||||
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept
|
||||
requires(!IsContiguouslyHashable<CharT, Hasher>::value);
|
||||
std::enable_if_t<!IsContiguouslyHashable<CharT, Hasher>::value>
|
||||
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept;
|
||||
|
||||
template <class Hasher, class CharT, class Traits, class Alloc>
|
||||
void
|
||||
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept
|
||||
requires(IsContiguouslyHashable<CharT, Hasher>::value);
|
||||
std::enable_if_t<IsContiguouslyHashable<CharT, Hasher>::value>
|
||||
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept;
|
||||
|
||||
template <class Hasher, class T, class U>
|
||||
void
|
||||
hash_append(Hasher& h, std::pair<T, U> const& p) noexcept
|
||||
requires(!IsContiguouslyHashable<std::pair<T, U>, Hasher>::value);
|
||||
std::enable_if_t<!IsContiguouslyHashable<std::pair<T, U>, Hasher>::value>
|
||||
hash_append(Hasher& h, std::pair<T, U> const& p) noexcept;
|
||||
|
||||
template <class Hasher, class T, class Alloc>
|
||||
void
|
||||
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept
|
||||
requires(!IsContiguouslyHashable<T, Hasher>::value);
|
||||
std::enable_if_t<!IsContiguouslyHashable<T, Hasher>::value>
|
||||
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept;
|
||||
|
||||
template <class Hasher, class T, class Alloc>
|
||||
void
|
||||
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept
|
||||
requires(IsContiguouslyHashable<T, Hasher>::value);
|
||||
std::enable_if_t<IsContiguouslyHashable<T, Hasher>::value>
|
||||
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept;
|
||||
|
||||
template <class Hasher, class T, std::size_t N>
|
||||
void
|
||||
hash_append(Hasher& h, std::array<T, N> const& a) noexcept
|
||||
requires(!IsContiguouslyHashable<std::array<T, N>, Hasher>::value);
|
||||
std::enable_if_t<!IsContiguouslyHashable<std::array<T, N>, Hasher>::value>
|
||||
hash_append(Hasher& h, std::array<T, N> const& a) noexcept;
|
||||
|
||||
template <class Hasher, class... T>
|
||||
void
|
||||
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept
|
||||
requires(!IsContiguouslyHashable<std::tuple<T...>, Hasher>::value);
|
||||
std::enable_if_t<!IsContiguouslyHashable<std::tuple<T...>, Hasher>::value>
|
||||
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept;
|
||||
|
||||
template <class Hasher, class Key, class T, class Hash, class Pred, class Alloc>
|
||||
void
|
||||
@@ -292,13 +279,11 @@ void
|
||||
hash_append(Hasher& h, std::unordered_set<Key, Hash, Pred, Alloc> const& s);
|
||||
|
||||
template <class Hasher, class Key, class Compare, class Alloc>
|
||||
void
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
|
||||
requires(!IsContiguouslyHashable<Key, Hasher>::value);
|
||||
std::enable_if_t<!IsContiguouslyHashable<Key, Hasher>::value>
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept;
|
||||
template <class Hasher, class Key, class Compare, class Alloc>
|
||||
void
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
|
||||
requires(IsContiguouslyHashable<Key, Hasher>::value);
|
||||
std::enable_if_t<IsContiguouslyHashable<Key, Hasher>::value>
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept;
|
||||
template <class Hasher, class T0, class T1, class... T>
|
||||
void
|
||||
hash_append(Hasher& h, T0 const& t0, T1 const& t1, T const&... t) noexcept;
|
||||
@@ -306,9 +291,8 @@ hash_append(Hasher& h, T0 const& t0, T1 const& t1, T const&... t) noexcept;
|
||||
// c-array
|
||||
|
||||
template <class Hasher, class T, std::size_t N>
|
||||
void
|
||||
std::enable_if_t<!IsContiguouslyHashable<T, Hasher>::value>
|
||||
hash_append(Hasher& h, T (&a)[N]) noexcept
|
||||
requires(!IsContiguouslyHashable<T, Hasher>::value)
|
||||
{
|
||||
for (auto const& t : a)
|
||||
hash_append(h, t);
|
||||
@@ -317,9 +301,8 @@ hash_append(Hasher& h, T (&a)[N]) noexcept
|
||||
// basic_string
|
||||
|
||||
template <class Hasher, class CharT, class Traits, class Alloc>
|
||||
inline void
|
||||
inline std::enable_if_t<!IsContiguouslyHashable<CharT, Hasher>::value>
|
||||
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept
|
||||
requires(!IsContiguouslyHashable<CharT, Hasher>::value)
|
||||
{
|
||||
for (auto c : s)
|
||||
hash_append(h, c);
|
||||
@@ -327,9 +310,8 @@ hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcep
|
||||
}
|
||||
|
||||
template <class Hasher, class CharT, class Traits, class Alloc>
|
||||
inline void
|
||||
inline std::enable_if_t<IsContiguouslyHashable<CharT, Hasher>::value>
|
||||
hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcept
|
||||
requires(IsContiguouslyHashable<CharT, Hasher>::value)
|
||||
{
|
||||
h(s.data(), s.size() * sizeof(CharT));
|
||||
hash_append(h, s.size());
|
||||
@@ -338,9 +320,8 @@ hash_append(Hasher& h, std::basic_string<CharT, Traits, Alloc> const& s) noexcep
|
||||
// pair
|
||||
|
||||
template <class Hasher, class T, class U>
|
||||
inline void
|
||||
inline std::enable_if_t<!IsContiguouslyHashable<std::pair<T, U>, Hasher>::value>
|
||||
hash_append(Hasher& h, std::pair<T, U> const& p) noexcept
|
||||
requires(!IsContiguouslyHashable<std::pair<T, U>, Hasher>::value)
|
||||
{
|
||||
hash_append(h, p.first, p.second);
|
||||
}
|
||||
@@ -348,9 +329,8 @@ hash_append(Hasher& h, std::pair<T, U> const& p) noexcept
|
||||
// vector
|
||||
|
||||
template <class Hasher, class T, class Alloc>
|
||||
inline void
|
||||
inline std::enable_if_t<!IsContiguouslyHashable<T, Hasher>::value>
|
||||
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept
|
||||
requires(!IsContiguouslyHashable<T, Hasher>::value)
|
||||
{
|
||||
for (auto const& t : v)
|
||||
hash_append(h, t);
|
||||
@@ -358,9 +338,8 @@ hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept
|
||||
}
|
||||
|
||||
template <class Hasher, class T, class Alloc>
|
||||
inline void
|
||||
inline std::enable_if_t<IsContiguouslyHashable<T, Hasher>::value>
|
||||
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept
|
||||
requires(IsContiguouslyHashable<T, Hasher>::value)
|
||||
{
|
||||
h(v.data(), v.size() * sizeof(T));
|
||||
hash_append(h, v.size());
|
||||
@@ -369,37 +348,57 @@ hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept
|
||||
// array
|
||||
|
||||
template <class Hasher, class T, std::size_t N>
|
||||
void
|
||||
std::enable_if_t<!IsContiguouslyHashable<std::array<T, N>, Hasher>::value>
|
||||
hash_append(Hasher& h, std::array<T, N> const& a) noexcept
|
||||
requires(!IsContiguouslyHashable<std::array<T, N>, Hasher>::value)
|
||||
{
|
||||
for (auto const& t : a)
|
||||
hash_append(h, t);
|
||||
}
|
||||
|
||||
template <class Hasher, class Key, class Compare, class Alloc>
|
||||
void
|
||||
std::enable_if_t<!IsContiguouslyHashable<Key, Hasher>::value>
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
|
||||
requires(!IsContiguouslyHashable<Key, Hasher>::value)
|
||||
{
|
||||
for (auto const& t : v)
|
||||
hash_append(h, t);
|
||||
}
|
||||
template <class Hasher, class Key, class Compare, class Alloc>
|
||||
void
|
||||
std::enable_if_t<IsContiguouslyHashable<Key, Hasher>::value>
|
||||
hash_append(Hasher& h, boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
|
||||
requires(IsContiguouslyHashable<Key, Hasher>::value)
|
||||
{
|
||||
h(&(v.begin()), v.size() * sizeof(Key));
|
||||
}
|
||||
// tuple
|
||||
|
||||
template <class Hasher, class... T>
|
||||
namespace detail {
|
||||
|
||||
inline void
|
||||
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept
|
||||
requires(!IsContiguouslyHashable<std::tuple<T...>, Hasher>::value)
|
||||
forEachItem(...) noexcept
|
||||
{
|
||||
std::apply([&h](auto const&... item) { (hash_append(h, item), ...); }, t);
|
||||
}
|
||||
|
||||
template <class Hasher, class T>
|
||||
inline int
|
||||
hashOne(Hasher& h, T const& t) noexcept
|
||||
{
|
||||
hash_append(h, t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class Hasher, class... T, std::size_t... I>
|
||||
inline void
|
||||
tuple_hash(Hasher& h, std::tuple<T...> const& t, std::index_sequence<I...>) noexcept
|
||||
{
|
||||
for_each_item(hash_one(h, std::get<I>(t))...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class Hasher, class... T>
|
||||
inline std::enable_if_t<!IsContiguouslyHashable<std::tuple<T...>, Hasher>::value>
|
||||
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept
|
||||
{
|
||||
detail::tuple_hash(h, t, std::index_sequence_for<T...>{});
|
||||
}
|
||||
|
||||
// shared_ptr
|
||||
|
||||
@@ -124,18 +124,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <class Seed>
|
||||
explicit Xxhasher(Seed seed)
|
||||
requires(std::is_unsigned_v<Seed>)
|
||||
: seed_(seed)
|
||||
template <class Seed, std::enable_if_t<std::is_unsigned_v<Seed>>* = nullptr>
|
||||
explicit Xxhasher(Seed seed) : seed_(seed)
|
||||
{
|
||||
resetBuffers();
|
||||
}
|
||||
|
||||
template <class Seed>
|
||||
Xxhasher(Seed seed, Seed)
|
||||
requires(std::is_unsigned_v<Seed>)
|
||||
: seed_(seed)
|
||||
template <class Seed, std::enable_if_t<std::is_unsigned_v<Seed>>* = nullptr>
|
||||
Xxhasher(Seed seed, Seed) : seed_(seed)
|
||||
{
|
||||
resetBuffers();
|
||||
}
|
||||
|
||||
@@ -12,17 +12,16 @@
|
||||
|
||||
namespace beast::insight {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* as desired (counters, events, gauges, meters, and an optional hook)
|
||||
* using the interface.
|
||||
*
|
||||
* @see Counter, Event, Gauge, Hook, Meter
|
||||
* @see NullCollector, StatsDCollector
|
||||
*/
|
||||
/** 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
|
||||
as desired (counters, events, gauges, meters, and an optional hook)
|
||||
using the interface.
|
||||
|
||||
@see Counter, Event, Gauge, Hook, Meter
|
||||
@see NullCollector, StatsDCollector
|
||||
*/
|
||||
class Collector
|
||||
{
|
||||
public:
|
||||
@@ -30,19 +29,18 @@ public:
|
||||
|
||||
virtual ~Collector() = 0;
|
||||
|
||||
/**
|
||||
* Create a hook.
|
||||
*
|
||||
* A hook is called at each collection interval, on an implementation
|
||||
* defined thread. This is a convenience facility for gathering metrics
|
||||
* 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)
|
||||
*
|
||||
* @see 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
|
||||
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)
|
||||
|
||||
@see Hook
|
||||
*/
|
||||
/** @{ */
|
||||
template <class Handler>
|
||||
Hook
|
||||
@@ -55,10 +53,9 @@ public:
|
||||
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
|
||||
makeCounter(std::string const& name) = 0;
|
||||
@@ -72,10 +69,9 @@ public:
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Create an event with the specified name.
|
||||
* @see Event
|
||||
*/
|
||||
/** Create an event with the specified name.
|
||||
@see Event
|
||||
*/
|
||||
/** @{ */
|
||||
virtual Event
|
||||
makeEvent(std::string const& name) = 0;
|
||||
@@ -89,10 +85,9 @@ public:
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Create a gauge with the specified name.
|
||||
* @see Gauge
|
||||
*/
|
||||
/** Create a gauge with the specified name.
|
||||
@see Gauge
|
||||
*/
|
||||
/** @{ */
|
||||
virtual Gauge
|
||||
makeGauge(std::string const& name) = 0;
|
||||
@@ -106,10 +101,9 @@ public:
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Create a meter with the specified name.
|
||||
* @see Meter
|
||||
*/
|
||||
/** Create a meter with the specified name.
|
||||
@see Meter
|
||||
*/
|
||||
/** @{ */
|
||||
virtual Meter
|
||||
makeMeter(std::string const& name) = 0;
|
||||
|
||||
@@ -7,39 +7,34 @@
|
||||
|
||||
namespace beast::insight {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/** 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.
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
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;
|
||||
|
||||
/**
|
||||
* Create the metric reference the specified implementation.
|
||||
* Normally this won't be called directly. Instead, call the appropriate
|
||||
* factory function in the Collector interface.
|
||||
* @see Collector.
|
||||
*/
|
||||
/** Create the metric reference the specified implementation.
|
||||
Normally this won't be called directly. Instead, call the appropriate
|
||||
factory function in the Collector interface.
|
||||
@see Collector.
|
||||
*/
|
||||
explicit Counter(std::shared_ptr<CounterImpl> impl) : impl_(std::move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the counter.
|
||||
*/
|
||||
/** Increment the counter. */
|
||||
/** @{ */
|
||||
void
|
||||
increment(value_type amount) const
|
||||
|
||||
@@ -8,40 +8,35 @@
|
||||
|
||||
namespace beast::insight {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
/** 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
|
||||
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.
|
||||
*/
|
||||
class Event final
|
||||
{
|
||||
public:
|
||||
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;
|
||||
|
||||
/**
|
||||
* Create the metric reference the specified implementation.
|
||||
* Normally this won't be called directly. Instead, call the appropriate
|
||||
* factory function in the Collector interface.
|
||||
* @see Collector.
|
||||
*/
|
||||
/** Create the metric reference the specified implementation.
|
||||
Normally this won't be called directly. Instead, call the appropriate
|
||||
factory function in the Collector interface.
|
||||
@see Collector.
|
||||
*/
|
||||
explicit Event(std::shared_ptr<EventImpl> impl) : impl_(std::move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an event notification.
|
||||
*/
|
||||
/** Push an event notification. */
|
||||
template <class Rep, class Period>
|
||||
void
|
||||
notify(std::chrono::duration<Rep, Period> const& value) const
|
||||
|
||||
@@ -7,44 +7,40 @@
|
||||
|
||||
namespace beast::insight {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
/** 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
|
||||
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.
|
||||
*/
|
||||
class Gauge final
|
||||
{
|
||||
public:
|
||||
using value_type = GaugeImpl::value_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;
|
||||
|
||||
/**
|
||||
* Create the metric reference the specified implementation.
|
||||
* Normally this won't be called directly. Instead, call the appropriate
|
||||
* factory function in the Collector interface.
|
||||
* @see Collector.
|
||||
*/
|
||||
/** Create the metric reference the specified implementation.
|
||||
Normally this won't be called directly. Instead, call the appropriate
|
||||
factory function in the Collector interface.
|
||||
@see Collector.
|
||||
*/
|
||||
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
|
||||
* changes into a single change if the calls occur within a single
|
||||
* collection interval.
|
||||
*/
|
||||
/** Set the value on the gauge.
|
||||
A Collector implementation should combine multiple calls to value
|
||||
changes into a single change if the calls occur within a single
|
||||
collection interval.
|
||||
*/
|
||||
/** @{ */
|
||||
void
|
||||
set(value_type value) const
|
||||
@@ -53,11 +49,6 @@ public:
|
||||
impl_->set(value);
|
||||
}
|
||||
|
||||
// This is a write-through handle: assignment sets the value of the
|
||||
// referenced metric. It is const-qualified and returns Gauge const&
|
||||
// (a non-const Gauge& would require a const_cast), so it does not follow
|
||||
// the conventional assignment-operator signature.
|
||||
// NOLINTNEXTLINE(misc-unconventional-assign-operator)
|
||||
Gauge const&
|
||||
operator=(value_type value) const
|
||||
{
|
||||
@@ -66,9 +57,7 @@ public:
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Adjust the value of the gauge.
|
||||
*/
|
||||
/** Adjust the value of the gauge. */
|
||||
/** @{ */
|
||||
void
|
||||
increment(difference_type amount) const
|
||||
|
||||
@@ -7,17 +7,13 @@
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
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&
|
||||
name() const = 0;
|
||||
};
|
||||
|
||||
@@ -8,17 +8,13 @@
|
||||
|
||||
namespace beast::insight {
|
||||
|
||||
/**
|
||||
* A container for managing a set of metric groups.
|
||||
*/
|
||||
/** A container for managing a set of metric groups. */
|
||||
class Groups
|
||||
{
|
||||
public:
|
||||
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&
|
||||
get(std::string const& name) = 0;
|
||||
@@ -31,9 +27,7 @@ public:
|
||||
/** @} */
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a group container that uses the specified collector.
|
||||
*/
|
||||
/** Create a group container that uses the specified collector. */
|
||||
std::unique_ptr<Groups>
|
||||
makeGroups(Collector::ptr const& collector);
|
||||
|
||||
|
||||
@@ -7,24 +7,20 @@
|
||||
|
||||
namespace beast::insight {
|
||||
|
||||
/**
|
||||
* A reference to a handler for performing polled collection.
|
||||
*/
|
||||
/** A reference to a handler for performing polled collection. */
|
||||
class Hook final
|
||||
{
|
||||
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;
|
||||
|
||||
/**
|
||||
* Create a hook referencing the specified implementation.
|
||||
* Normally this won't be called directly. Instead, call the appropriate
|
||||
* factory function in the Collector interface.
|
||||
* @see Collector.
|
||||
*/
|
||||
/** Create a hook referencing the specified implementation.
|
||||
Normally this won't be called directly. Instead, call the appropriate
|
||||
factory function in the Collector interface.
|
||||
@see Collector.
|
||||
*/
|
||||
explicit Hook(std::shared_ptr<HookImpl> impl) : impl_(std::move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -7,38 +7,33 @@
|
||||
|
||||
namespace beast::insight {
|
||||
|
||||
/**
|
||||
* A metric for measuring an integral value.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/** A metric for measuring an integral value.
|
||||
|
||||
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.
|
||||
*/
|
||||
class Meter final
|
||||
{
|
||||
public:
|
||||
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;
|
||||
|
||||
/**
|
||||
* Create the metric reference the specified implementation.
|
||||
* Normally this won't be called directly. Instead, call the appropriate
|
||||
* factory function in the Collector interface.
|
||||
* @see Collector.
|
||||
*/
|
||||
/** Create the metric reference the specified implementation.
|
||||
Normally this won't be called directly. Instead, call the appropriate
|
||||
factory function in the Collector interface.
|
||||
@see Collector.
|
||||
*/
|
||||
explicit Meter(std::shared_ptr<MeterImpl> impl) : impl_(std::move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the meter.
|
||||
*/
|
||||
/** Increment the meter. */
|
||||
/** @{ */
|
||||
void
|
||||
increment(value_type amount) const
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
|
||||
namespace beast::insight {
|
||||
|
||||
/**
|
||||
* A Collector which does not collect metrics.
|
||||
*/
|
||||
/** A Collector which does not collect metrics. */
|
||||
class NullCollector : public Collector
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -9,22 +9,20 @@
|
||||
|
||||
namespace beast::insight {
|
||||
|
||||
/**
|
||||
* A Collector that reports metrics to a StatsD server.
|
||||
* Reference:
|
||||
* https://github.com/b/statsd_spec
|
||||
*/
|
||||
/** A Collector that reports metrics to a StatsD server.
|
||||
Reference:
|
||||
https://github.com/b/statsd_spec
|
||||
*/
|
||||
class StatsDCollector : public Collector
|
||||
{
|
||||
public:
|
||||
explicit StatsDCollector() = default;
|
||||
|
||||
/**
|
||||
* Create a StatsD collector.
|
||||
* @param address The IP address and port of the StatsD server.
|
||||
* @param prefix A string pre-pended before each metric name.
|
||||
* @param journal Destination for logging output.
|
||||
*/
|
||||
/** Create a StatsD collector.
|
||||
@param address The IP address and port of the StatsD server.
|
||||
@param prefix A string pre-pended before each metric name.
|
||||
@param journal Destination for logging output.
|
||||
*/
|
||||
static std::shared_ptr<StatsDCollector>
|
||||
make(IP::Endpoint const& address, std::string const& prefix, Journal journal);
|
||||
};
|
||||
|
||||
@@ -19,54 +19,42 @@ namespace IP {
|
||||
|
||||
using Address = boost::asio::ip::address;
|
||||
|
||||
/**
|
||||
* Returns the address represented as a string.
|
||||
*/
|
||||
/** Returns the address represented as a string. */
|
||||
inline std::string
|
||||
to_string(Address const& addr)
|
||||
{
|
||||
return addr.to_string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if this is a loopback address.
|
||||
*/
|
||||
/** Returns `true` if this is a loopback address. */
|
||||
inline bool
|
||||
isLoopback(Address const& addr)
|
||||
{
|
||||
return addr.is_loopback();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the address is unspecified.
|
||||
*/
|
||||
/** Returns `true` if the address is unspecified. */
|
||||
inline bool
|
||||
isUnspecified(Address const& addr)
|
||||
{
|
||||
return addr.is_unspecified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the address is a multicast address.
|
||||
*/
|
||||
/** Returns `true` if the address is a multicast address. */
|
||||
inline bool
|
||||
isMulticast(Address const& addr)
|
||||
{
|
||||
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
|
||||
isPrivate(Address const& addr)
|
||||
{
|
||||
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
|
||||
isPublic(Address const& addr)
|
||||
{
|
||||
|
||||
@@ -6,29 +6,23 @@
|
||||
|
||||
namespace beast::IP {
|
||||
|
||||
/**
|
||||
* Convert to Endpoint.
|
||||
* The port is set to zero.
|
||||
*/
|
||||
/** Convert to Endpoint.
|
||||
The port is set to zero.
|
||||
*/
|
||||
Endpoint
|
||||
fromAsio(boost::asio::ip::address const& address);
|
||||
|
||||
/**
|
||||
* Convert to Endpoint.
|
||||
*/
|
||||
/** Convert to Endpoint. */
|
||||
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
|
||||
toAsioAddress(Endpoint const& endpoint);
|
||||
|
||||
/**
|
||||
* Convert to asio::ip::tcp::endpoint.
|
||||
*/
|
||||
/** Convert to asio::ip::tcp::endpoint. */
|
||||
boost::asio::ip::tcp::endpoint
|
||||
toAsioEndpoint(Endpoint const& endpoint);
|
||||
|
||||
|
||||
@@ -6,22 +6,17 @@ namespace beast::IP {
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
getClass(AddressV4 const& address);
|
||||
|
||||
|
||||
@@ -6,15 +6,11 @@ namespace beast::IP {
|
||||
|
||||
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
|
||||
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
|
||||
isPublic(AddressV6 const& addr);
|
||||
|
||||
|
||||
@@ -17,68 +17,51 @@ namespace beast::IP {
|
||||
|
||||
using Port = std::uint16_t;
|
||||
|
||||
/**
|
||||
* A version-independent IP address and port combination.
|
||||
*/
|
||||
/** A version-independent IP address and port combination. */
|
||||
class Endpoint
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create an unspecified endpoint.
|
||||
*/
|
||||
/** Create an unspecified 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);
|
||||
|
||||
/**
|
||||
* Create an Endpoint from a string.
|
||||
* If the port is omitted, the endpoint will have a zero port.
|
||||
* @return An optional endpoint; will be `std::nullopt` on failure
|
||||
*/
|
||||
/** Create an Endpoint from a string.
|
||||
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>
|
||||
fromStringChecked(std::string const& s);
|
||||
static Endpoint
|
||||
fromString(std::string const& s);
|
||||
|
||||
/**
|
||||
* Returns a string representing the endpoint.
|
||||
*/
|
||||
/** Returns a string representing the endpoint. */
|
||||
[[nodiscard]] std::string
|
||||
toString() const;
|
||||
|
||||
/**
|
||||
* Returns the port number on the endpoint.
|
||||
*/
|
||||
/** Returns the port number on the endpoint. */
|
||||
[[nodiscard]] Port
|
||||
port() const
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Endpoint with a different port.
|
||||
*/
|
||||
/** Returns a new Endpoint with a different port. */
|
||||
[[nodiscard]] Endpoint
|
||||
atPort(Port port) const
|
||||
{
|
||||
return Endpoint(addr_, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address portion of this endpoint.
|
||||
*/
|
||||
/** Returns the address portion of this endpoint. */
|
||||
[[nodiscard]] Address const&
|
||||
address() const
|
||||
{
|
||||
return addr_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience accessors for the address part.
|
||||
*/
|
||||
/** Convenience accessors for the address part. */
|
||||
/** @{ */
|
||||
[[nodiscard]] bool
|
||||
isV4() const
|
||||
@@ -102,9 +85,7 @@ public:
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Arithmetic comparison.
|
||||
*/
|
||||
/** Arithmetic comparison. */
|
||||
/** @{ */
|
||||
friend bool
|
||||
operator==(Endpoint const& lhs, Endpoint const& rhs);
|
||||
@@ -150,45 +131,35 @@ private:
|
||||
|
||||
// Properties
|
||||
|
||||
/**
|
||||
* Returns `true` if the endpoint is a loopback address.
|
||||
*/
|
||||
/** Returns `true` if the endpoint is a loopback address. */
|
||||
inline bool
|
||||
isLoopback(Endpoint const& endpoint)
|
||||
{
|
||||
return isLoopback(endpoint.address());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the endpoint is unspecified.
|
||||
*/
|
||||
/** Returns `true` if the endpoint is unspecified. */
|
||||
inline bool
|
||||
isUnspecified(Endpoint const& endpoint)
|
||||
{
|
||||
return isUnspecified(endpoint.address());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the endpoint is a multicast address.
|
||||
*/
|
||||
/** Returns `true` if the endpoint is a multicast address. */
|
||||
inline bool
|
||||
isMulticast(Endpoint const& endpoint)
|
||||
{
|
||||
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
|
||||
isPrivate(Endpoint const& endpoint)
|
||||
{
|
||||
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
|
||||
isPublic(Endpoint const& endpoint)
|
||||
{
|
||||
@@ -197,18 +168,14 @@ isPublic(Endpoint const& endpoint)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the endpoint represented as a string.
|
||||
*/
|
||||
/** Returns the endpoint represented as a string. */
|
||||
inline std::string
|
||||
to_string(Endpoint const& endpoint)
|
||||
{
|
||||
return endpoint.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output stream conversion.
|
||||
*/
|
||||
/** Output stream conversion. */
|
||||
template <typename OutputStream>
|
||||
OutputStream&
|
||||
operator<<(OutputStream& os, Endpoint const& endpoint)
|
||||
@@ -217,9 +184,7 @@ operator<<(OutputStream& os, Endpoint const& endpoint)
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input stream conversion.
|
||||
*/
|
||||
/** Input stream conversion. */
|
||||
std::istream&
|
||||
operator>>(std::istream& is, Endpoint& endpoint);
|
||||
|
||||
@@ -228,9 +193,7 @@ operator>>(std::istream& is, Endpoint& endpoint);
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace std {
|
||||
/**
|
||||
* std::hash support.
|
||||
*/
|
||||
/** std::hash support. */
|
||||
template <>
|
||||
struct hash<::beast::IP::Endpoint>
|
||||
{
|
||||
@@ -245,9 +208,7 @@ struct hash<::beast::IP::Endpoint>
|
||||
} // namespace std
|
||||
|
||||
namespace boost {
|
||||
/**
|
||||
* boost::hash support.
|
||||
*/
|
||||
/** boost::hash support. */
|
||||
template <>
|
||||
struct hash<::beast::IP::Endpoint>
|
||||
{
|
||||
|
||||
@@ -30,20 +30,17 @@ struct CiEqualPred
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns `true` if `c` is linear white space.
|
||||
*
|
||||
* This excludes the CRLF sequence allowed for line continuations.
|
||||
*/
|
||||
/** Returns `true` if `c` is linear white space.
|
||||
|
||||
This excludes the CRLF sequence allowed for line continuations.
|
||||
*/
|
||||
inline bool
|
||||
isLws(char c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if `c` is any whitespace character.
|
||||
*/
|
||||
/** Returns `true` if `c` is any whitespace character. */
|
||||
inline bool
|
||||
isWhite(char c)
|
||||
{
|
||||
@@ -90,15 +87,14 @@ trimRight(String const& s)
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Parse a character sequence of values separated by commas.
|
||||
* Double quotes and escape sequences will be converted. Excess white
|
||||
* space, commas, double quotes, and empty elements are not copied.
|
||||
* Format:
|
||||
* #(token|quoted-string)
|
||||
* Reference:
|
||||
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2
|
||||
*/
|
||||
/** Parse a character sequence of values separated by commas.
|
||||
Double quotes and escape sequences will be converted. Excess white
|
||||
space, commas, double quotes, and empty elements are not copied.
|
||||
Format:
|
||||
#(token|quoted-string)
|
||||
Reference:
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2
|
||||
*/
|
||||
template <
|
||||
class FwdIt,
|
||||
class Result = std::vector<std::basic_string<typename std::iterator_traits<FwdIt>::value_type>>,
|
||||
@@ -193,15 +189,14 @@ splitCommas(boost::beast::string_view const& s)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Iterates through a comma separated list.
|
||||
*
|
||||
* Meets the requirements of ForwardIterator.
|
||||
*
|
||||
* List defined in rfc2616 2.1.
|
||||
*
|
||||
* @note Values returned may contain backslash escapes.
|
||||
*/
|
||||
/** Iterates through a comma separated list.
|
||||
|
||||
Meets the requirements of ForwardIterator.
|
||||
|
||||
List defined in rfc2616 2.1.
|
||||
|
||||
@note Values returned may contain backslash escapes.
|
||||
*/
|
||||
class ListIterator
|
||||
{
|
||||
using iter_type = boost::string_ref::const_iterator;
|
||||
@@ -328,20 +323,17 @@ ListIterator::increment()
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns true if two strings are equal.
|
||||
*
|
||||
* A case-insensitive comparison is used.
|
||||
*/
|
||||
/** Returns true if two strings are equal.
|
||||
|
||||
A case-insensitive comparison is used.
|
||||
*/
|
||||
inline bool
|
||||
ciEqual(boost::string_ref s1, boost::string_ref s2)
|
||||
{
|
||||
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>
|
||||
makeList(boost::string_ref const& field)
|
||||
{
|
||||
@@ -349,11 +341,10 @@ makeList(boost::string_ref const& field)
|
||||
ListIterator{field.begin(), field.end()}, ListIterator{field.end(), field.end()}};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified token exists in the list.
|
||||
*
|
||||
* A case-insensitive comparison is used.
|
||||
*/
|
||||
/** Returns true if the specified token exists in the list.
|
||||
|
||||
A case-insensitive comparison is used.
|
||||
*/
|
||||
template <class = void>
|
||||
bool
|
||||
tokenInList(boost::string_ref const& value, boost::string_ref const& token)
|
||||
|
||||
@@ -19,13 +19,12 @@
|
||||
|
||||
namespace beast::test {
|
||||
|
||||
/**
|
||||
* 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
|
||||
* asynchronous asio code.
|
||||
*/
|
||||
/** 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
|
||||
asynchronous asio code.
|
||||
*/
|
||||
class EnableYieldTo
|
||||
{
|
||||
protected:
|
||||
@@ -39,9 +38,7 @@ private:
|
||||
std::size_t running_ = 0;
|
||||
|
||||
public:
|
||||
/**
|
||||
* The type of yield context passed to functions.
|
||||
*/
|
||||
/// The type of yield context passed to functions.
|
||||
using yield_context = boost::asio::yield_context;
|
||||
|
||||
explicit EnableYieldTo(std::size_t concurrency = 1) : work_(boost::asio::make_work_guard(ios_))
|
||||
@@ -60,27 +57,24 @@ public:
|
||||
t.join();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the `io_context` associated with the object
|
||||
*/
|
||||
/// Return the `io_context` associated with the object
|
||||
boost::asio::io_context&
|
||||
getIoContext()
|
||||
{
|
||||
return ios_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run one or more functions, each in a coroutine.
|
||||
*
|
||||
* This call will block until all coroutines terminate.
|
||||
*
|
||||
* Each functions should have this signature:
|
||||
* @code
|
||||
* void f(yield_context);
|
||||
* @endcode
|
||||
*
|
||||
* @param fn... One or more functions to invoke.
|
||||
*/
|
||||
/** Run one or more functions, each in a coroutine.
|
||||
|
||||
This call will block until all coroutines terminate.
|
||||
|
||||
Each functions should have this signature:
|
||||
@code
|
||||
void f(yield_context);
|
||||
@endcode
|
||||
|
||||
@param fn... One or more functions to invoke.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template <class... FN>
|
||||
void
|
||||
|
||||
@@ -23,7 +23,6 @@ typeName()
|
||||
if (auto s = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr))
|
||||
{
|
||||
name = s;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
|
||||
std::free(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
|
||||
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
|
||||
{
|
||||
private:
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
|
||||
namespace beast::unit_test::detail {
|
||||
|
||||
/**
|
||||
* Adapter to constrain a container interface.
|
||||
* The interface allows for limited read only operations. Derived classes
|
||||
* provide additional behavior.
|
||||
*/
|
||||
/** Adapter to constrain a container interface.
|
||||
The interface allows for limited read only operations. Derived classes
|
||||
provide additional behavior.
|
||||
*/
|
||||
template <class Container>
|
||||
class ConstContainer
|
||||
{
|
||||
@@ -39,27 +38,21 @@ public:
|
||||
using 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
|
||||
empty() const
|
||||
{
|
||||
return cont_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of items in the container.
|
||||
*/
|
||||
/** Returns the number of items in the container. */
|
||||
[[nodiscard]] size_type
|
||||
size() const
|
||||
{
|
||||
return cont_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns forward iterators for traversal.
|
||||
*/
|
||||
/** Returns forward iterators for traversal. */
|
||||
/** @{ */
|
||||
[[nodiscard]] const_iterator
|
||||
begin() const
|
||||
|
||||
@@ -10,9 +10,7 @@ namespace beast::unit_test {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Holds test suites registered during static initialization.
|
||||
*/
|
||||
/// Holds test suites registered during static initialization.
|
||||
inline SuiteList&
|
||||
globalSuites()
|
||||
{
|
||||
@@ -36,9 +34,7 @@ struct InsertSuite
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* Holds test suites registered during static initialization.
|
||||
*/
|
||||
/// Holds test suites registered during static initialization.
|
||||
inline SuiteList const&
|
||||
globalSuites()
|
||||
{
|
||||
|
||||
@@ -121,48 +121,42 @@ Selector::operator()(SuiteInfo const& s)
|
||||
|
||||
// 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
|
||||
* 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 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.
|
||||
*/
|
||||
/** Returns a predicate that implements a smart matching rule.
|
||||
The predicate checks the suite, module, and library fields of the
|
||||
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 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.
|
||||
|
||||
*/
|
||||
inline Selector
|
||||
matchAuto(std::string const& 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
|
||||
matchAll()
|
||||
{
|
||||
return Selector(Selector::ModeT::All);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a predicate that matches a specific suite.
|
||||
*/
|
||||
/** Returns a predicate that matches a specific suite. */
|
||||
inline Selector
|
||||
matchSuite(std::string const& 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
|
||||
matchLibrary(std::string const& name)
|
||||
{
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
|
||||
namespace beast::unit_test {
|
||||
|
||||
/**
|
||||
* A test runner that stores the results.
|
||||
*/
|
||||
/** A test runner that stores the results. */
|
||||
class Recorder : public Runner
|
||||
{
|
||||
private:
|
||||
@@ -26,9 +24,7 @@ private:
|
||||
public:
|
||||
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&
|
||||
report() const
|
||||
{
|
||||
|
||||
@@ -25,10 +25,9 @@ namespace beast::unit_test {
|
||||
|
||||
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>
|
||||
class Reporter : public Runner
|
||||
{
|
||||
|
||||
@@ -13,15 +13,11 @@
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Holds the result of evaluating one test condition.
|
||||
*/
|
||||
/** Holds the result of evaluating one test condition. */
|
||||
struct Test
|
||||
{
|
||||
explicit Test(bool pass) : pass(pass)
|
||||
@@ -45,36 +41,28 @@ private:
|
||||
public:
|
||||
TestsT() = default;
|
||||
|
||||
/**
|
||||
* Returns the total number of test conditions.
|
||||
*/
|
||||
/** Returns the total number of test conditions. */
|
||||
[[nodiscard]] std::size_t
|
||||
total() const
|
||||
{
|
||||
return cont().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of failed test conditions.
|
||||
*/
|
||||
/** Returns the number of failed test conditions. */
|
||||
[[nodiscard]] std::size_t
|
||||
failed() const
|
||||
{
|
||||
return failed_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a successful test condition.
|
||||
*/
|
||||
/** Register a successful test condition. */
|
||||
void
|
||||
pass()
|
||||
{
|
||||
cont().emplace_back(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a failed test condition.
|
||||
*/
|
||||
/** Register a failed test condition. */
|
||||
void
|
||||
fail(std::string const& reason = "")
|
||||
{
|
||||
@@ -86,9 +74,7 @@ private:
|
||||
class LogT : public detail::ConstContainer<std::vector<std::string>>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Insert a string into the log.
|
||||
*/
|
||||
/** Insert a string into the log. */
|
||||
void
|
||||
insert(std::string const& s)
|
||||
{
|
||||
@@ -103,31 +89,23 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this testcase.
|
||||
*/
|
||||
/** Returns the name of this testcase. */
|
||||
[[nodiscard]] std::string const&
|
||||
name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Memberspace for a container of test condition outcomes.
|
||||
*/
|
||||
/** Memberspace for a container of test condition outcomes. */
|
||||
TestsT tests;
|
||||
|
||||
/**
|
||||
* Memberspace for a container of testcase log messages.
|
||||
*/
|
||||
/** Memberspace for a container of testcase log messages. */
|
||||
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>>
|
||||
{
|
||||
private:
|
||||
@@ -140,36 +118,28 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this suite.
|
||||
*/
|
||||
/** Returns the name of this suite. */
|
||||
[[nodiscard]] std::string const&
|
||||
name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of test conditions.
|
||||
*/
|
||||
/** Returns the total number of test conditions. */
|
||||
[[nodiscard]] std::size_t
|
||||
total() const
|
||||
{
|
||||
return total_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of failures.
|
||||
*/
|
||||
/** Returns the number of failures. */
|
||||
[[nodiscard]] std::size_t
|
||||
failed() const
|
||||
{
|
||||
return failed_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a set of testcase results.
|
||||
*/
|
||||
/** Insert a set of testcase results. */
|
||||
/** @{ */
|
||||
void
|
||||
insert(CaseResults&& r)
|
||||
@@ -192,9 +162,7 @@ public:
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// 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>>
|
||||
{
|
||||
private:
|
||||
@@ -205,36 +173,28 @@ private:
|
||||
public:
|
||||
Results() = default;
|
||||
|
||||
/**
|
||||
* Returns the total number of test cases.
|
||||
*/
|
||||
/** Returns the total number of test cases. */
|
||||
[[nodiscard]] std::size_t
|
||||
cases() const
|
||||
{
|
||||
return cases_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of test conditions.
|
||||
*/
|
||||
/** Returns the total number of test conditions. */
|
||||
[[nodiscard]] std::size_t
|
||||
total() const
|
||||
{
|
||||
return total_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of failures.
|
||||
*/
|
||||
/** Returns the number of failures. */
|
||||
[[nodiscard]] std::size_t
|
||||
failed() const
|
||||
{
|
||||
return failed_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a set of suite results.
|
||||
*/
|
||||
/** Insert a set of suite results. */
|
||||
/** @{ */
|
||||
void
|
||||
insert(SuiteResults&& r)
|
||||
|
||||
@@ -13,12 +13,11 @@
|
||||
|
||||
namespace beast::unit_test {
|
||||
|
||||
/**
|
||||
* Unit test runner interface.
|
||||
*
|
||||
* Derived classes can customize the reporting behavior. This interface is
|
||||
* injected into the unit_test class to receive the results of the tests.
|
||||
*/
|
||||
/** Unit test runner interface.
|
||||
|
||||
Derived classes can customize the reporting behavior. This interface is
|
||||
injected into the unit_test class to receive the results of the tests.
|
||||
*/
|
||||
class Runner
|
||||
{
|
||||
std::string arg_;
|
||||
@@ -34,132 +33,110 @@ public:
|
||||
Runner&
|
||||
operator=(Runner const&) = delete;
|
||||
|
||||
/**
|
||||
* Set the argument string.
|
||||
*
|
||||
* The argument string is available to suites and
|
||||
* allows for customization of the test. Each suite
|
||||
* defines its own syntax for the argument string.
|
||||
* The same argument is passed to all suites.
|
||||
*/
|
||||
/** Set the argument string.
|
||||
|
||||
The argument string is available to suites and
|
||||
allows for customization of the test. Each suite
|
||||
defines its own syntax for the argument string.
|
||||
The same argument is passed to all suites.
|
||||
*/
|
||||
void
|
||||
arg(std::string const& s)
|
||||
{
|
||||
arg_ = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the argument string.
|
||||
*/
|
||||
/** Returns the argument string. */
|
||||
[[nodiscard]] std::string const&
|
||||
arg() const
|
||||
{
|
||||
return arg_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the specified suite.
|
||||
* @return `true` if any conditions failed.
|
||||
*/
|
||||
/** Run the specified suite.
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class = void>
|
||||
bool
|
||||
run(SuiteInfo const& s);
|
||||
|
||||
/**
|
||||
* Run a sequence of suites.
|
||||
* The expression
|
||||
* `FwdIter::value_type`
|
||||
* must be convertible to `SuiteInfo`.
|
||||
* @return `true` if any conditions failed.
|
||||
*/
|
||||
/** Run a sequence of suites.
|
||||
The expression
|
||||
`FwdIter::value_type`
|
||||
must be convertible to `SuiteInfo`.
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class FwdIter>
|
||||
bool
|
||||
run(FwdIter first, FwdIter last);
|
||||
|
||||
/**
|
||||
* Conditionally run a sequence of suites.
|
||||
* pred will be called as:
|
||||
* @code
|
||||
* bool pred(SuiteInfo const&);
|
||||
* @endcode
|
||||
* @return `true` if any conditions failed.
|
||||
*/
|
||||
/** Conditionally run a sequence of suites.
|
||||
pred will be called as:
|
||||
@code
|
||||
bool pred(SuiteInfo const&);
|
||||
@endcode
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class FwdIter, class Pred>
|
||||
bool
|
||||
runIf(FwdIter first, FwdIter last, Pred pred = Pred{});
|
||||
|
||||
/**
|
||||
* Run all suites in a container.
|
||||
* @return `true` if any conditions failed.
|
||||
*/
|
||||
/** Run all suites in a container.
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class SequenceContainer>
|
||||
bool
|
||||
runEach(SequenceContainer const& c);
|
||||
|
||||
/**
|
||||
* Conditionally run suites in a container.
|
||||
* pred will be called as:
|
||||
* @code
|
||||
* bool pred(SuiteInfo const&);
|
||||
* @endcode
|
||||
* @return `true` if any conditions failed.
|
||||
*/
|
||||
/** Conditionally run suites in a container.
|
||||
pred will be called as:
|
||||
@code
|
||||
bool pred(SuiteInfo const&);
|
||||
@endcode
|
||||
@return `true` if any conditions failed.
|
||||
*/
|
||||
template <class SequenceContainer, class Pred>
|
||||
bool
|
||||
runEachIf(SequenceContainer const& c, Pred pred = Pred{});
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called when a new suite starts.
|
||||
*/
|
||||
/// Called when a new suite starts.
|
||||
virtual void
|
||||
onSuiteBegin(SuiteInfo const&)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a suite ends.
|
||||
*/
|
||||
/// Called when a suite ends.
|
||||
virtual void
|
||||
onSuiteEnd()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new case starts.
|
||||
*/
|
||||
/// Called when a new case starts.
|
||||
virtual void
|
||||
onCaseBegin(std::string const&)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new case ends.
|
||||
*/
|
||||
/// Called when a new case ends.
|
||||
virtual void
|
||||
onCaseEnd()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for each passing condition.
|
||||
*/
|
||||
/// Called for each passing condition.
|
||||
virtual void
|
||||
onPass()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for each failing condition.
|
||||
*/
|
||||
/// Called for each failing condition.
|
||||
virtual void
|
||||
onFail(std::string const&)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a test logs output.
|
||||
*/
|
||||
/// Called when a test logs output.
|
||||
virtual void
|
||||
onLog(std::string const&)
|
||||
{
|
||||
|
||||
@@ -41,14 +41,13 @@ class Thread;
|
||||
|
||||
enum class AbortT { NoAbortOnFail, AbortOnFail };
|
||||
|
||||
/**
|
||||
* A testsuite class.
|
||||
*
|
||||
* Derived classes execute a series of testcases, where each testcase is
|
||||
* a series of pass/fail tests. To provide a unit test using this class,
|
||||
* derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a
|
||||
* translation unit.
|
||||
*/
|
||||
/** A testsuite class.
|
||||
|
||||
Derived classes execute a series of testcases, where each testcase is
|
||||
a series of pass/fail tests. To provide a unit test using this class,
|
||||
derive from it and use the BEAST_DEFINE_UNIT_TEST macro in a
|
||||
translation unit.
|
||||
*/
|
||||
class Suite
|
||||
{
|
||||
private:
|
||||
@@ -119,17 +118,16 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a new testcase.
|
||||
*
|
||||
* A testcase is a series of evaluated test conditions. A test
|
||||
* suite may have multiple test cases. A test is associated with
|
||||
* the last opened testcase. When the test first runs, a default
|
||||
* unnamed case is opened. Tests with only one case may omit the
|
||||
* call to testcase.
|
||||
*
|
||||
* @param abort Determines if suite continues running after a failure.
|
||||
*/
|
||||
/** Open a new testcase.
|
||||
|
||||
A testcase is a series of evaluated test conditions. A test
|
||||
suite may have multiple test cases. A test is associated with
|
||||
the last opened testcase. When the test first runs, a default
|
||||
unnamed case is opened. Tests with only one case may omit the
|
||||
call to testcase.
|
||||
|
||||
@param abort Determines if suite continues running after a failure.
|
||||
*/
|
||||
void
|
||||
operator()(std::string const& name, AbortT abort = AbortT::NoAbortOnFail);
|
||||
|
||||
@@ -142,23 +140,19 @@ private:
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Logging output stream.
|
||||
*
|
||||
* Text sent to the log output stream will be forwarded to
|
||||
* the output stream associated with the runner.
|
||||
*/
|
||||
/** Logging output stream.
|
||||
|
||||
Text sent to the log output stream will be forwarded to
|
||||
the output stream associated with the runner.
|
||||
*/
|
||||
LogOs<char> log;
|
||||
|
||||
/**
|
||||
* Memberspace for declaring test cases.
|
||||
*/
|
||||
/** Memberspace for declaring test cases. */
|
||||
TestcaseT testcase;
|
||||
|
||||
/**
|
||||
* Returns the "current" running suite.
|
||||
* If no suite is running, nullptr is returned.
|
||||
*/
|
||||
/** Returns the "current" running suite.
|
||||
If no suite is running, nullptr is returned.
|
||||
*/
|
||||
static Suite*
|
||||
thisSuite()
|
||||
{
|
||||
@@ -174,34 +168,30 @@ public:
|
||||
Suite&
|
||||
operator=(Suite const&) = delete;
|
||||
|
||||
/**
|
||||
* Invokes the test using the specified runner.
|
||||
*
|
||||
* Data members are set up here instead of the constructor as a
|
||||
* convenience to writing the derived class to avoid repetition of
|
||||
* forwarded constructor arguments to the base.
|
||||
* Normally this is called by the framework for you.
|
||||
*/
|
||||
/** Invokes the test using the specified runner.
|
||||
|
||||
Data members are set up here instead of the constructor as a
|
||||
convenience to writing the derived class to avoid repetition of
|
||||
forwarded constructor arguments to the base.
|
||||
Normally this is called by the framework for you.
|
||||
*/
|
||||
template <class = void>
|
||||
void
|
||||
operator()(Runner& r);
|
||||
|
||||
/**
|
||||
* Record a successful test condition.
|
||||
*/
|
||||
/** Record a successful test condition. */
|
||||
template <class = void>
|
||||
void
|
||||
pass();
|
||||
|
||||
/**
|
||||
* Record a failure.
|
||||
*
|
||||
* @param reason Optional text added to the output on a failure.
|
||||
*
|
||||
* @param file The source code file where the test failed.
|
||||
*
|
||||
* @param line The source code line number where the test failed.
|
||||
*/
|
||||
/** Record a failure.
|
||||
|
||||
@param reason Optional text added to the output on a failure.
|
||||
|
||||
@param file The source code file where the test failed.
|
||||
|
||||
@param line The source code line number where the test failed.
|
||||
*/
|
||||
/** @{ */
|
||||
template <class String>
|
||||
void
|
||||
@@ -212,24 +202,23 @@ public:
|
||||
fail(std::string const& reason = "");
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Evaluate a test condition.
|
||||
*
|
||||
* This function provides improved logging by incorporating the
|
||||
* file name and line number into the reported output on failure,
|
||||
* as well as additional text specified by the caller.
|
||||
*
|
||||
* @param shouldBeTrue The condition to test. The condition
|
||||
* is evaluated in a boolean context.
|
||||
*
|
||||
* @param reason Optional added text to output on a failure.
|
||||
*
|
||||
* @param file The source code file where the test failed.
|
||||
*
|
||||
* @param line The source code line number where the test failed.
|
||||
*
|
||||
* @return `true` if the test condition indicates success.
|
||||
*/
|
||||
/** Evaluate a test condition.
|
||||
|
||||
This function provides improved logging by incorporating the
|
||||
file name and line number into the reported output on failure,
|
||||
as well as additional text specified by the caller.
|
||||
|
||||
@param shouldBeTrue The condition to test. The condition
|
||||
is evaluated in a boolean context.
|
||||
|
||||
@param reason Optional added text to output on a failure.
|
||||
|
||||
@param file The source code file where the test failed.
|
||||
|
||||
@param line The source code line number where the test failed.
|
||||
|
||||
@return `true` if the test condition indicates success.
|
||||
*/
|
||||
/** @{ */
|
||||
template <class Condition>
|
||||
bool
|
||||
@@ -286,19 +275,15 @@ public:
|
||||
return unexcept(f, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the argument associated with the runner.
|
||||
*/
|
||||
/** Return the argument associated with the runner. */
|
||||
std::string const&
|
||||
arg() const
|
||||
{
|
||||
return runner_->arg();
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
* @return `true` if the test condition indicates success(a false value)
|
||||
*/
|
||||
// DEPRECATED
|
||||
// @return `true` if the test condition indicates success(a false value)
|
||||
template <class Condition, class String>
|
||||
bool
|
||||
unexpected(Condition shouldBeFalse, String const& reason);
|
||||
@@ -320,9 +305,7 @@ private:
|
||||
return &kPTs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the suite.
|
||||
*/
|
||||
/** Runs the suite. */
|
||||
virtual void
|
||||
run() = 0;
|
||||
|
||||
@@ -575,20 +558,18 @@ Suite::run(Runner& r)
|
||||
}
|
||||
|
||||
#ifndef BEAST_EXPECT
|
||||
/**
|
||||
* Check a precondition.
|
||||
*
|
||||
* If the condition is false, the file and line number are reported.
|
||||
*/
|
||||
/** Check a precondition.
|
||||
|
||||
If the condition is false, the file and line number are reported.
|
||||
*/
|
||||
#define BEAST_EXPECT(cond) expect(cond, __FILE__, __LINE__)
|
||||
#endif
|
||||
|
||||
#ifndef BEAST_EXPECTS
|
||||
/**
|
||||
* Check a precondition.
|
||||
*
|
||||
* If the condition is false, the file and line number are reported.
|
||||
*/
|
||||
/** Check a precondition.
|
||||
|
||||
If the condition is false, the file and line number are reported.
|
||||
*/
|
||||
#define BEAST_EXPECTS(cond, reason) \
|
||||
((cond) ? (pass(), true) : (fail((reason), __FILE__, __LINE__), false))
|
||||
#endif
|
||||
@@ -612,43 +593,41 @@ Suite::run(Runner& r)
|
||||
//
|
||||
#ifndef BEAST_DEFINE_TESTSUITE
|
||||
|
||||
/**
|
||||
* Enables insertion of test suites into the global container.
|
||||
* The default is to insert all test suite definitions into the global
|
||||
* container. If BEAST_DEFINE_TESTSUITE is user defined, this macro
|
||||
* has no effect.
|
||||
*/
|
||||
/** Enables insertion of test suites into the global container.
|
||||
The default is to insert all test suite definitions into the global
|
||||
container. If BEAST_DEFINE_TESTSUITE is user defined, this macro
|
||||
has no effect.
|
||||
*/
|
||||
#ifndef BEAST_NO_UNIT_TEST_INLINE
|
||||
#define BEAST_NO_UNIT_TEST_INLINE 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Define a unit test suite.
|
||||
*
|
||||
* Class The type representing the class being tested.
|
||||
* Module Identifies the module.
|
||||
* Library Identifies the library.
|
||||
*
|
||||
* The declaration for the class implementing the test should be the same
|
||||
* as Class ## _test. For example, if Class is aged_ordered_container, the
|
||||
* test class must be declared as:
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* struct aged_ordered_container_test : beast::unit_test::suite
|
||||
* {
|
||||
* //...
|
||||
* };
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
* The macro invocation must appear in the same namespace as the test class.
|
||||
*
|
||||
* Unit test priorities were introduced so parallel unit_test::suites would
|
||||
* execute faster. Suites with longer running times have higher priorities
|
||||
* than unit tests with shorter running times. Suites with no priorities
|
||||
* are assumed to run most quickly, so they run last.
|
||||
*/
|
||||
/** Define a unit test suite.
|
||||
|
||||
Class The type representing the class being tested.
|
||||
Module Identifies the module.
|
||||
Library Identifies the library.
|
||||
|
||||
The declaration for the class implementing the test should be the same
|
||||
as Class ## _test. For example, if Class is aged_ordered_container, the
|
||||
test class must be declared as:
|
||||
|
||||
@code
|
||||
|
||||
struct aged_ordered_container_test : beast::unit_test::suite
|
||||
{
|
||||
//...
|
||||
};
|
||||
|
||||
@endcode
|
||||
|
||||
The macro invocation must appear in the same namespace as the test class.
|
||||
|
||||
Unit test priorities were introduced so parallel unit_test::suites would
|
||||
execute faster. Suites with longer running times have higher priorities
|
||||
than unit tests with shorter running times. Suites with no priorities
|
||||
are assumed to run most quickly, so they run last.
|
||||
*/
|
||||
|
||||
#if BEAST_NO_UNIT_TEST_INLINE
|
||||
#define BEAST_DEFINE_TESTSUITE(Class, Module, Library)
|
||||
|
||||
@@ -13,9 +13,7 @@ namespace beast::unit_test {
|
||||
|
||||
class Runner;
|
||||
|
||||
/**
|
||||
* Associates a unit test type with metadata.
|
||||
*/
|
||||
/** Associates a unit test type with metadata. */
|
||||
class SuiteInfo
|
||||
{
|
||||
using run_type = std::function<void(Runner&)>;
|
||||
@@ -62,27 +60,21 @@ public:
|
||||
return library_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if this suite only runs manually.
|
||||
*/
|
||||
/// Returns `true` if this suite only runs manually.
|
||||
[[nodiscard]] bool
|
||||
manual() const
|
||||
{
|
||||
return manual_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the canonical suite name as a string.
|
||||
*/
|
||||
/// Return the canonical suite name as a string.
|
||||
[[nodiscard]] std::string
|
||||
fullName() const
|
||||
{
|
||||
return library_ + "." + module_ + "." + name_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a new instance of the associated test suite.
|
||||
*/
|
||||
/// Run a new instance of the associated test suite.
|
||||
void
|
||||
run(Runner& r) const
|
||||
{
|
||||
@@ -101,9 +93,7 @@ public:
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Convenience for producing SuiteInfo for a given test type.
|
||||
*/
|
||||
/// Convenience for producing SuiteInfo for a given test type.
|
||||
template <class Suite>
|
||||
SuiteInfo
|
||||
makeSuiteInfo(std::string name, std::string module, std::string library, bool manual, int priority)
|
||||
|
||||
@@ -10,15 +10,13 @@
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <string> // IWYU pragma: keep
|
||||
#include <typeindex> // IWYU pragma: keep
|
||||
#include <unordered_set> // IWYU pragma: keep
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace beast::unit_test {
|
||||
|
||||
/**
|
||||
* A container of test suites.
|
||||
*/
|
||||
/// A container of test suites.
|
||||
class SuiteList : public detail::ConstContainer<std::set<SuiteInfo>>
|
||||
{
|
||||
private:
|
||||
@@ -28,11 +26,10 @@ private:
|
||||
#endif
|
||||
|
||||
public:
|
||||
/**
|
||||
* Insert a suite into the set.
|
||||
*
|
||||
* The suite must not already exist.
|
||||
*/
|
||||
/** Insert a suite into the set.
|
||||
|
||||
The suite must not already exist.
|
||||
*/
|
||||
template <class Suite>
|
||||
void
|
||||
insert(char const* name, char const* module, char const* library, bool manual, int priority);
|
||||
|
||||
@@ -14,9 +14,7 @@
|
||||
|
||||
namespace beast::unit_test {
|
||||
|
||||
/**
|
||||
* Replacement for std::thread that handles exceptions in unit tests.
|
||||
*/
|
||||
/** Replacement for std::thread that handles exceptions in unit tests. */
|
||||
class Thread
|
||||
{
|
||||
private:
|
||||
@@ -47,10 +45,7 @@ public:
|
||||
template <class F, class... Args>
|
||||
explicit Thread(Suite& s, F&& f, Args&&... args) : s_(&s)
|
||||
{
|
||||
std::function<void(void)> b = [f = std::forward<F>(f),
|
||||
... args = std::forward<Args>(args)]() mutable {
|
||||
std::invoke(f, args...);
|
||||
};
|
||||
std::function<void(void)> b = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
|
||||
t_ = std::thread(&Thread::run, this, std::move(b));
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* Severity level / threshold of a Journal message.
|
||||
*/
|
||||
/** Severity level / threshold of a Journal message. */
|
||||
enum class Severity : std::uint8_t {
|
||||
All = 0,
|
||||
|
||||
@@ -27,19 +25,18 @@ enum class Severity : std::uint8_t {
|
||||
None = Disabled
|
||||
};
|
||||
|
||||
/**
|
||||
* A generic endpoint for log messages.
|
||||
*
|
||||
* The Journal has a few simple goals:
|
||||
*
|
||||
* * To be light-weight and copied by value.
|
||||
* * To allow logging statements to be left in source code.
|
||||
* * The logging is controlled at run-time based on a logging threshold.
|
||||
*
|
||||
* It is advisable to check Journal::active(level) prior to formatting log
|
||||
* text. Doing so sidesteps expensive text formatting when the results
|
||||
* will not be sent to the log.
|
||||
*/
|
||||
/** A generic endpoint for log messages.
|
||||
|
||||
The Journal has a few simple goals:
|
||||
|
||||
* To be light-weight and copied by value.
|
||||
* To allow logging statements to be left in source code.
|
||||
* The logging is controlled at run-time based on a logging threshold.
|
||||
|
||||
It is advisable to check Journal::active(level) prior to formatting log
|
||||
text. Doing so sidesteps expensive text formatting when the results
|
||||
will not be sent to the log.
|
||||
*/
|
||||
class Journal
|
||||
{
|
||||
public:
|
||||
@@ -52,9 +49,7 @@ private:
|
||||
public:
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Abstraction for the underlying message destination.
|
||||
*/
|
||||
/** Abstraction for the underlying message destination. */
|
||||
class Sink
|
||||
{
|
||||
protected:
|
||||
@@ -68,47 +63,36 @@ public:
|
||||
Sink&
|
||||
operator=(Sink const& lhs) = delete;
|
||||
|
||||
/**
|
||||
* Returns `true` if text at the passed severity produces output.
|
||||
*/
|
||||
/** Returns `true` if text at the passed severity produces output. */
|
||||
[[nodiscard]] virtual bool
|
||||
active(Severity level) const;
|
||||
|
||||
/**
|
||||
* Returns `true` if a message is also written to the Output Window
|
||||
* (MSVC).
|
||||
*/
|
||||
/** Returns `true` if a message is also written to the Output Window
|
||||
* (MSVC). */
|
||||
[[nodiscard]] virtual bool
|
||||
console() const;
|
||||
|
||||
/**
|
||||
* Set whether messages are also written to the Output Window (MSVC).
|
||||
/** Set whether messages are also written to the Output Window (MSVC).
|
||||
*/
|
||||
virtual void
|
||||
console(bool output);
|
||||
|
||||
/**
|
||||
* Returns the minimum severity level this sink will report.
|
||||
*/
|
||||
/** Returns the minimum severity level this sink will report. */
|
||||
[[nodiscard]] virtual Severity
|
||||
threshold() const;
|
||||
|
||||
/**
|
||||
* Set the minimum severity this sink will report.
|
||||
*/
|
||||
/** Set the minimum severity this sink will report. */
|
||||
virtual void
|
||||
threshold(Severity thresh);
|
||||
|
||||
/**
|
||||
* Write text to the sink at the specified severity.
|
||||
* A conforming implementation will not write the text if the passed
|
||||
* level is below the current threshold().
|
||||
*/
|
||||
/** Write text to the sink at the specified severity.
|
||||
A conforming implementation will not write the text if the passed
|
||||
level is below the current threshold().
|
||||
*/
|
||||
virtual void
|
||||
write(Severity level, std::string const& text) = 0;
|
||||
|
||||
/**
|
||||
* Bypass filter and write text to the sink at the specified severity.
|
||||
/** Bypass filter and write text to the sink at the specified severity.
|
||||
* Always write the message, but maintain the same formatting as if
|
||||
* it passed through a level filter.
|
||||
*
|
||||
@@ -124,17 +108,15 @@ public:
|
||||
};
|
||||
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(!std::is_default_constructible_v<Sink>);
|
||||
static_assert(!std::is_copy_constructible_v<Sink>);
|
||||
static_assert(!std::is_move_constructible_v<Sink>);
|
||||
static_assert(!std::is_copy_assignable_v<Sink>);
|
||||
static_assert(!std::is_move_assignable_v<Sink>);
|
||||
static_assert(std::is_nothrow_destructible_v<Sink>);
|
||||
static_assert(!std::is_default_constructible_v<Sink>, "");
|
||||
static_assert(!std::is_copy_constructible_v<Sink>, "");
|
||||
static_assert(!std::is_move_constructible_v<Sink>, "");
|
||||
static_assert(!std::is_copy_assignable_v<Sink>, "");
|
||||
static_assert(!std::is_move_assignable_v<Sink>, "");
|
||||
static_assert(std::is_nothrow_destructible_v<Sink>, "");
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns a Sink which does nothing.
|
||||
*/
|
||||
/** Returns a Sink which does nothing. */
|
||||
static Sink&
|
||||
getNullSink();
|
||||
|
||||
@@ -182,43 +164,36 @@ public:
|
||||
};
|
||||
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(!std::is_default_constructible_v<ScopedStream>);
|
||||
static_assert(std::is_copy_constructible_v<ScopedStream>);
|
||||
static_assert(std::is_move_constructible_v<ScopedStream>);
|
||||
static_assert(!std::is_copy_assignable_v<ScopedStream>);
|
||||
static_assert(!std::is_move_assignable_v<ScopedStream>);
|
||||
static_assert(std::is_nothrow_destructible_v<ScopedStream>);
|
||||
static_assert(!std::is_default_constructible_v<ScopedStream>, "");
|
||||
static_assert(std::is_copy_constructible_v<ScopedStream>, "");
|
||||
static_assert(std::is_move_constructible_v<ScopedStream>, "");
|
||||
static_assert(!std::is_copy_assignable_v<ScopedStream>, "");
|
||||
static_assert(!std::is_move_assignable_v<ScopedStream>, "");
|
||||
static_assert(std::is_nothrow_destructible_v<ScopedStream>, "");
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
public:
|
||||
/**
|
||||
* Provide a light-weight way to check active() before string formatting
|
||||
*/
|
||||
/** Provide a light-weight way to check active() before string formatting */
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a stream which produces no output.
|
||||
*/
|
||||
/** Create a stream which produces no output. */
|
||||
explicit Stream() : sink_(getNullSink()), level_(Severity::Disabled)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a stream that writes at the given level.
|
||||
*
|
||||
* Constructor is inlined so checking active() very inexpensive.
|
||||
*/
|
||||
/** Create a stream that writes at the given level.
|
||||
|
||||
Constructor is inlined so checking active() very inexpensive.
|
||||
*/
|
||||
Stream(Sink& sink, Severity level) : sink_(sink), level_(level)
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
level_ < Severity::Disabled, "beast::Journal::Stream::Stream : maximum level");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct or copy another Stream.
|
||||
*/
|
||||
/** Construct or copy another Stream. */
|
||||
Stream(Stream const& other) : Stream(other.sink_, other.level_)
|
||||
{
|
||||
}
|
||||
@@ -226,27 +201,21 @@ public:
|
||||
Stream&
|
||||
operator=(Stream const& other) = delete;
|
||||
|
||||
/**
|
||||
* Returns the Sink that this Stream writes to.
|
||||
*/
|
||||
/** Returns the Sink that this Stream writes to. */
|
||||
[[nodiscard]] Sink&
|
||||
sink() const
|
||||
{
|
||||
return sink_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Severity level of messages this Stream reports.
|
||||
*/
|
||||
/** Returns the Severity level of messages this Stream reports. */
|
||||
[[nodiscard]] Severity
|
||||
level() const
|
||||
{
|
||||
return level_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if sink logs anything at this stream's level.
|
||||
*/
|
||||
/** Returns `true` if sink logs anything at this stream's level. */
|
||||
/** @{ */
|
||||
[[nodiscard]] bool
|
||||
active() const
|
||||
@@ -261,9 +230,7 @@ public:
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Output stream support.
|
||||
*/
|
||||
/** Output stream support. */
|
||||
/** @{ */
|
||||
ScopedStream
|
||||
operator<<(std::ostream& manip(std::ostream&)) const;
|
||||
@@ -279,60 +246,49 @@ public:
|
||||
};
|
||||
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(std::is_default_constructible_v<Stream>);
|
||||
static_assert(std::is_copy_constructible_v<Stream>);
|
||||
static_assert(std::is_move_constructible_v<Stream>);
|
||||
static_assert(!std::is_copy_assignable_v<Stream>);
|
||||
static_assert(!std::is_move_assignable_v<Stream>);
|
||||
static_assert(std::is_nothrow_destructible_v<Stream>);
|
||||
static_assert(std::is_default_constructible_v<Stream>, "");
|
||||
static_assert(std::is_copy_constructible_v<Stream>, "");
|
||||
static_assert(std::is_move_constructible_v<Stream>, "");
|
||||
static_assert(!std::is_copy_assignable_v<Stream>, "");
|
||||
static_assert(!std::is_move_assignable_v<Stream>, "");
|
||||
static_assert(std::is_nothrow_destructible_v<Stream>, "");
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Journal has no default constructor.
|
||||
*/
|
||||
/** Journal has no default constructor. */
|
||||
Journal() = delete;
|
||||
|
||||
/**
|
||||
* Create a journal that writes to the specified sink.
|
||||
*/
|
||||
/** Create a journal that writes to the specified sink. */
|
||||
explicit Journal(Sink& sink) : sink_(&sink)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Sink associated with this Journal.
|
||||
*/
|
||||
/** Returns the Sink associated with this Journal. */
|
||||
[[nodiscard]] Sink&
|
||||
sink() const
|
||||
{
|
||||
return *sink_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream for this sink, with the specified severity level.
|
||||
*/
|
||||
/** Returns a stream for this sink, with the specified severity level. */
|
||||
[[nodiscard]] Stream
|
||||
stream(Severity level) const
|
||||
{
|
||||
return Stream(*sink_, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if any message would be logged at this severity level.
|
||||
* For a message to be logged, the severity must be at or above the
|
||||
* sink's severity threshold.
|
||||
*/
|
||||
/** Returns `true` if any message would be logged at this severity level.
|
||||
For a message to be logged, the severity must be at or above the
|
||||
sink's severity threshold.
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
active(Severity level) const
|
||||
{
|
||||
return sink_->active(level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Severity stream access functions.
|
||||
*/
|
||||
/** Severity stream access functions. */
|
||||
/** @{ */
|
||||
[[nodiscard]] Stream
|
||||
trace() const
|
||||
@@ -373,12 +329,12 @@ public:
|
||||
};
|
||||
|
||||
#ifndef __INTELLISENSE__
|
||||
static_assert(!std::is_default_constructible_v<Journal>);
|
||||
static_assert(std::is_copy_constructible_v<Journal>);
|
||||
static_assert(std::is_move_constructible_v<Journal>);
|
||||
static_assert(std::is_copy_assignable_v<Journal>);
|
||||
static_assert(std::is_move_assignable_v<Journal>);
|
||||
static_assert(std::is_nothrow_destructible_v<Journal>);
|
||||
static_assert(!std::is_default_constructible_v<Journal>, "");
|
||||
static_assert(std::is_copy_constructible_v<Journal>, "");
|
||||
static_assert(std::is_move_constructible_v<Journal>, "");
|
||||
static_assert(std::is_copy_assignable_v<Journal>, "");
|
||||
static_assert(std::is_move_assignable_v<Journal>, "");
|
||||
static_assert(std::is_nothrow_destructible_v<Journal>, "");
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -12,9 +12,7 @@ namespace beast {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Abstract stream with RAII containers that produce a property tree.
|
||||
*/
|
||||
/** Abstract stream with RAII containers that produce a property tree. */
|
||||
class PropertyStream
|
||||
{
|
||||
public:
|
||||
@@ -308,9 +306,7 @@ public:
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Subclasses can be called to write to a stream and have children.
|
||||
*/
|
||||
/** Subclasses can be called to write to a stream and have children. */
|
||||
class PropertyStream::Source
|
||||
{
|
||||
private:
|
||||
@@ -328,22 +324,17 @@ public:
|
||||
Source&
|
||||
operator=(Source const&) = delete;
|
||||
|
||||
/**
|
||||
* Returns the name of this source.
|
||||
*/
|
||||
/** Returns the name of this source. */
|
||||
[[nodiscard]] std::string const&
|
||||
name() const;
|
||||
|
||||
/**
|
||||
* Add a child source.
|
||||
*/
|
||||
/** Add a child source. */
|
||||
void
|
||||
add(Source& source);
|
||||
|
||||
/**
|
||||
* Add a child source by pointer.
|
||||
* The source pointer is returned so it can be used in ctor-initializers.
|
||||
*/
|
||||
/** Add a child source by pointer.
|
||||
The source pointer is returned so it can be used in ctor-initializers.
|
||||
*/
|
||||
template <class Derived>
|
||||
Derived*
|
||||
add(Derived* child)
|
||||
@@ -352,55 +343,45 @@ public:
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a child source from this Source.
|
||||
*/
|
||||
/** Remove a child source from this Source. */
|
||||
void
|
||||
remove(Source& child);
|
||||
|
||||
/**
|
||||
* Remove all child sources from this Source.
|
||||
*/
|
||||
/** Remove all child sources from this Source. */
|
||||
void
|
||||
removeAll();
|
||||
|
||||
/**
|
||||
* Write only this Source to the stream.
|
||||
*/
|
||||
/** Write only this Source to the stream. */
|
||||
void
|
||||
writeOne(PropertyStream& stream);
|
||||
|
||||
/**
|
||||
* write this source and all its children recursively to the stream.
|
||||
*/
|
||||
/** write this source and all its children recursively to the stream. */
|
||||
void
|
||||
write(PropertyStream& stream);
|
||||
|
||||
/**
|
||||
* Parse the path and write the corresponding Source and optional children.
|
||||
* If the source is found, it is written. If the wildcard character '*'
|
||||
* exists as the last character in the path, then all the children are
|
||||
* written recursively.
|
||||
*/
|
||||
/** Parse the path and write the corresponding Source and optional children.
|
||||
If the source is found, it is written. If the wildcard character '*'
|
||||
exists as the last character in the path, then all the children are
|
||||
written recursively.
|
||||
*/
|
||||
void
|
||||
write(PropertyStream& stream, std::string const& path);
|
||||
|
||||
/**
|
||||
* Parse the dot-delimited Source path and return the result.
|
||||
* The first value will be a pointer to the Source object corresponding
|
||||
* to the given path. If no Source object exists, then the first value
|
||||
* will be nullptr and the second value will be undefined.
|
||||
* The second value is a boolean indicating whether or not the path string
|
||||
* specifies the wildcard character '*' as the last character.
|
||||
*
|
||||
* print statement examples
|
||||
* "parent.child" prints child and all of its children
|
||||
* "parent.child." start at the parent and print down to child
|
||||
* "parent.grandchild" prints nothing- grandchild not direct descendent
|
||||
* "parent.grandchild." starts at the parent and prints down to grandchild
|
||||
* "parent.grandchild.*" starts at parent, print through grandchild
|
||||
* children
|
||||
*/
|
||||
/** Parse the dot-delimited Source path and return the result.
|
||||
The first value will be a pointer to the Source object corresponding
|
||||
to the given path. If no Source object exists, then the first value
|
||||
will be nullptr and the second value will be undefined.
|
||||
The second value is a boolean indicating whether or not the path string
|
||||
specifies the wildcard character '*' as the last character.
|
||||
|
||||
print statement examples
|
||||
"parent.child" prints child and all of its children
|
||||
"parent.child." start at the parent and print down to child
|
||||
"parent.grandchild" prints nothing- grandchild not direct descendent
|
||||
"parent.grandchild." starts at the parent and prints down to grandchild
|
||||
"parent.grandchild.*" starts at parent, print through grandchild
|
||||
children
|
||||
*/
|
||||
std::pair<Source*, bool>
|
||||
find(std::string path);
|
||||
|
||||
@@ -420,10 +401,9 @@ public:
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Subclass override.
|
||||
* The default version does nothing.
|
||||
*/
|
||||
/** Subclass override.
|
||||
The default version does nothing.
|
||||
*/
|
||||
virtual void
|
||||
onWrite(Map&);
|
||||
};
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* Wraps a Journal::Sink to prefix its output with a string.
|
||||
*/
|
||||
/** Wraps a Journal::Sink to prefix its output with a string. */
|
||||
|
||||
// A WrappedSink both is a Sink and has a Sink:
|
||||
// o It inherits from Sink so it has the correct interface.
|
||||
|
||||
@@ -4,34 +4,33 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* Zero allows classes to offer efficient comparisons to zero.
|
||||
*
|
||||
* Zero is a struct to allow classes to efficiently compare with zero without
|
||||
* requiring an rvalue construction.
|
||||
*
|
||||
* It's often the case that we have classes which combine a number and a unit.
|
||||
* In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
|
||||
* like t > 1 or t != 1 do not.
|
||||
*
|
||||
* The class Zero allows such comparisons to be easily made.
|
||||
*
|
||||
* The comparing class T either needs to have a method called signum() which
|
||||
* returns a positive number, 0, or a negative; or there needs to be a signum
|
||||
* function which resolves in the namespace which takes an instance of T and
|
||||
* returns a positive, zero or negative number.
|
||||
*/
|
||||
/** Zero allows classes to offer efficient comparisons to zero.
|
||||
|
||||
Zero is a struct to allow classes to efficiently compare with zero without
|
||||
requiring an rvalue construction.
|
||||
|
||||
It's often the case that we have classes which combine a number and a unit.
|
||||
In such cases, comparisons like t > 0 or t != 0 make sense, but comparisons
|
||||
like t > 1 or t != 1 do not.
|
||||
|
||||
The class Zero allows such comparisons to be easily made.
|
||||
|
||||
The comparing class T either needs to have a method called signum() which
|
||||
returns a positive number, 0, or a negative; or there needs to be a signum
|
||||
function which resolves in the namespace which takes an instance of T and
|
||||
returns a positive, zero or negative number.
|
||||
*/
|
||||
|
||||
struct Zero
|
||||
{
|
||||
explicit Zero() = default;
|
||||
};
|
||||
|
||||
inline constexpr Zero kZero{};
|
||||
namespace {
|
||||
constexpr Zero kZero{};
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* Default implementation of signum calls the method on the class.
|
||||
*/
|
||||
/** Default implementation of signum calls the method on the class. */
|
||||
template <typename T>
|
||||
auto
|
||||
signum(T const& t)
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/**
|
||||
* Makes T const or non const depending on a bool.
|
||||
*/
|
||||
/** Makes T const or non const depending on a bool. */
|
||||
template <bool IsConst, class T>
|
||||
struct MaybeConst
|
||||
{
|
||||
@@ -15,9 +13,7 @@ struct MaybeConst
|
||||
conditional_t<IsConst, typename std::remove_const<T>::type const, std::remove_const_t<T>>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Alias for omitting `typename`.
|
||||
*/
|
||||
/** Alias for omitting `typename`. */
|
||||
template <bool IsConst, class T>
|
||||
using maybe_const_t = MaybeConst<IsConst, T>::type;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user