libdpf/include/dpf/keyword.hpp
Ryan Henry 0d22946a0e Checkpoint the party/runtime stack before share-program and malicious-mode work.
Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-28 05:59:19 -06:00

619 lines
25 KiB
C++

/// @file dpf/keyword.hpp
/// @brief defines `dpf::keyword` and associated helpers
/// @details A `dpf::keyword` is an integer representation of a fixed-length
/// string over a given alphabet. The integer representation uses
/// the fewest bits possible for the given string length and alphabet
/// size and uses an encoding that preserves the lexicographic
/// ordering of the underlying strings. This type is inteded to be
/// used as an input type for a DPF and, as such, specializes
/// `dpf::utils::bitlength_of`, `dpf::utils::msb_of`, and
/// `dpf::utils::countl_zero_symmetric_difference`. When used as a
/// DPF input type, the aforementioned properties of the encoding
/// equate to minimizing the DPF tree depth and maximizing the
/// potential for effective memoization in the context of
/// `eval_point`- and `eval_sequence`-based evaluation. As a discreet
/// (non-numeric) type, `dpf::keyword`s are not optimized for use in
/// `eval_interval`-based evaluation.
///
/// This file also defines the `dpf::alphabets` namespace, which
/// defines various alphabets of interest, including the printable
/// ASCII characters (`dpf::alphabets::printable_ascii`), lowercase
/// Roman letters (`dpf::alphabets::lowercase_alpha`), lowercase
/// hexademical digits (`dpf::alpbabets::lowercase_hex`), among
/// others.
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
/// see [LICENSE.md](@ref license) for details.
#ifndef LIBDPF_INCLUDE_DPF_KEYWORD_HPP__
#define LIBDPF_INCLUDE_DPF_KEYWORD_HPP__
#include <cstddef>
#include <cmath>
#include <type_traits>
#include <limits>
#include <string>
#include <string_view>
#include <memory>
#include <iterator>
#include <algorithm>
#include <array>
#include <istream>
#include <ostream>
#include <stdexcept>
#include "hedley/hedley.h"
#include "dpf/utils.hpp"
#include "dpf/modint.hpp"
namespace dpf
{
/// @brief defines common alphabets for convenient use with `dpf::keyword`
/// N.B.: The first char in an alphabet has value `0`. All strings will be
/// implicitly padded to the max length by prepending this char. For
/// strings, it should typically be `\0`; for numbers, the zero digit.
namespace alphabets
{
/// @brief the printable ASCII chars
inline constexpr char printable_ascii[] = "\0 !\"#$%&'()*+,-./0123456789:"
";<=>?@ABCDEFGHIJKLMNOPQRSTUV"
"WXYZ[\\]^_`abcdefghijklmnopq"
"rstuvwxyz{|}~";
/// @brief the extended ASCII characters (0-255) using hexadecimal escape sequences with lowercase letters
inline constexpr char extended_ascii[] =
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff";
/// @brief the lowercase Roman alphabet
inline constexpr char lowercase_alpha[] = "\0abcdefghijklmnopqrstuvwxyz";
/// @brief the lowercase and uppercase Roman alphabet
inline constexpr char alpha[] = "\0abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL"
"MNOPQRSTUVWXYZ";
/// @brief the lowercase and uppercase Roman alphabet plus digits 0-9
inline constexpr char alphanumeric[] = "\0abcdefghijklmnopqrstuvwxyzABCDE"
"FGHIJKLMNOPQRSTUVWXYZ0123456789";
/// @brief the lowercase Roman alphabet plus digits 0-9
inline constexpr char lowercase_alphanumeric[] = "\0abcdefghijklmnopqrstu"
"vwxyz0123456789";
/// @brief hashtags
inline constexpr char hashtag[] = "\0abcdefghijklmnopqrstuvwxyz#-";
/// @brief binary
inline constexpr char binary[] = "01";
/// @brief octal
inline constexpr char octal[] = "01234567";
/// @brief decimal
inline constexpr char decimal[] = "0123456789";
/// @brief hex w/ lowercase letters
inline constexpr char hex[] = "0123456789abcdef";
/// @brief hex w/ uppercase letters
inline constexpr char uppercase_hex[] = "0123456789ABCDEF";
/// @brief base64 digits
inline constexpr char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk"
"lmnopqrstuvwxyz0123456789+/=";
/// @brief URL-safe base64 digits
inline constexpr char url64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk"
"lmnopqrstuvwxyz0123456789-_";
/// @brief URI alphabet
inline constexpr char uri[] = "\0:/?#[]@" // gen-delims
"!$&'()*+,;=" // sub-delims
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789-._~%";
/// @brief lowercase email address
inline constexpr char email[] = "\0abcdefghijklmnopqrstuvwxyz"
"0123456789.-_@";
/// @brief Distinct UTF-8 bytes of a small emoji sample.
/// A `char` alphabet cannot store one symbol per emoji: those
/// code points are several bytes long, and repeated bytes would
/// make `find` non-injective. This list keeps first-seen bytes only.
inline constexpr char emoji[] =
"\x00\xf0\x9f\x98\x80\x81\x82\xa4\xa3\x83\x84\x85\x86\x89\x8a\x8b"
"\x8e\x8d\xa5\xb0\x8f";
} // namespace alphabets
template <std::size_t MaxLen,
// std::size_t MinLen = 1,
typename CharT = char,
const CharT * Alphabet = alphabets::printable_ascii,
typename Traits = std::char_traits<CharT>,
typename Allocator = std::allocator<CharT>>
class basic_fixed_length_string : public dpf::modint<static_cast<std::size_t>(std::ceil(MaxLen*std::log2(std::basic_string_view<CharT, Traits>(&Alphabet[1]).size() + 1)))>
{
public:
using string_view = std::basic_string_view<CharT, Traits>;
/// @brief radix used by the integer representation of the string
static constexpr std::size_t radix = string_view(&Alphabet[1]).size() + 1;
/// @brief the alphabet over which the string is constructed
static constexpr string_view alphabet = string_view(Alphabet, radix);
// /// @brief the minimum explicit length of a string
// /// @details strings of length at least `min_length` are padded to
// /// `max_length` with the 0th character in the alphabet; strings
// /// of length less than `min_length` are out of range
// static constexpr std::size_t min_length = MinLen;
/// @brief the (maximum) length of a string
static constexpr std::size_t max_length = MaxLen;
/// @brief the number of bits needed to uniquely represent any string
/// of length at least `min_length` and at most `max_length` over
/// `alphabet`
static constexpr std::size_t bits
= std::ceil((max_length)*std::log2(radix));
static_assert(!alphabet.empty(), "alphabet must be non-empty");
// static_assert(MinLen != 0, "minimum string length must be positive");
static_assert(MaxLen != 0, "maximum string length must be positive");
// static_assert(MinLen <= MaxLen, "minimum string length must be less than or equal to maximum");
private:
using parent = dpf::modint<bits>;
public:
/// @brief the primitive integral type used to represent the string
using integral_type = dpf::utils::nonvoid_integral_type_from_bitlength_t<bits>;
/// @brief construct the `basic_fixed_length_string`
/// @{
/// @brief default constructor
/// @details Constructs the `basic_fixed_length_string` with a value
/// corresponding to the empty string.
HEDLEY_NO_THROW
constexpr basic_fixed_length_string() noexcept = default;
/// @brief copy constructor
/// @details Constructs the `basic_fixed_length_string` with a value
/// copied from another `basic_fixed_length_string`.
HEDLEY_NO_THROW
constexpr
basic_fixed_length_string(const basic_fixed_length_string &)
noexcept = default;
/// @brief move constructor
/// @details Constructs the `basic_fixed_length_string` from another
/// `basic_fixed_length_string` using move semantics.
HEDLEY_NO_THROW
constexpr
basic_fixed_length_string(basic_fixed_length_string &&)
noexcept = default;
/// @brief value constructor
/// @details Constructs a `basic_fixed_length_string` whose value is
/// initialized to the integer representation of `str`.
/// @param str the string to initialize with
constexpr
// cppcheck-suppress noExplicitConstructor
basic_fixed_length_string(string_view str) // NOLINT(runtime/explicit)
: parent::modint(encode_(str)) { }
/// @brief value constructor
/// @details Constructs a `basic_fixed_length_string` whose value is
/// initialized to the integer representation of `str`.
/// @param str the string to initialize with
constexpr
// cppcheck-suppress noExplicitConstructor
basic_fixed_length_string(const CharT * str) // NOLINT(runtime/explicit)
: parent::modint(encode_(str)) { }
/// @}
/// @brief assign the `basic_fixed_length_string`
/// @return `*this`
/// @{
/// @brief value assignment
/// @details Sets the value of this `basic_fixed_length_string` to
/// the integer representation of `str`.
/// @param str the string to assign with
constexpr basic_fixed_length_string & operator=(string_view str)
{
parent::operator=(encode_(str));
return *this;
}
/// @brief copy assignment
/// @details Assigns the `basic_fixed_length_string` with a value
/// copied from another `basic_fixed_length_string`.
constexpr basic_fixed_length_string &
operator=(const basic_fixed_length_string &) = default;
/// @brief move assignment
/// @details Assigns the `basic_fixed_length_string` from another
/// `basic_fixed_length_string` using move semantics.
HEDLEY_NO_THROW
constexpr basic_fixed_length_string &
operator=(basic_fixed_length_string &&) noexcept = default;
/// @}
~basic_fixed_length_string() = default;
HEDLEY_PURE
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr basic_fixed_length_string operator~() const noexcept
{
return basic_fixed_length_string{parent::operator~()};
}
/// @brief recreates the string representation of this
/// `basic_fixed_length_string`
/// @complexity `O(MaxLen)` where `MaxLen` is the maximum string length
constexpr
operator std::basic_string<CharT, Traits, Allocator>() const
{
auto tmp = parent::reduced_value();
std::basic_string<CharT, Traits, Allocator> rev;
rev.reserve(max_length);
while (tmp != 0)
{
rev.push_back(alphabet[static_cast<std::size_t>(tmp % radix)]);
tmp /= radix;
}
std::reverse(std::begin(rev), std::end(rev));
return rev;
}
private:
constexpr
// cppcheck-suppress noExplicitConstructor
HEDLEY_NO_THROW
basic_fixed_length_string(integral_type val) // NOLINT(runtime/explicit)
noexcept
: parent::modint(val) { }
HEDLEY_NO_THROW
constexpr basic_fixed_length_string(parent val) noexcept
: parent::modint(val) { }
/// @brief converts a string of length at-most `max_length` over
/// `alphabet` into an integer
/// @param str the source string
/// @return the returned `integral_type`
/// @throws std::length_error if `str` exceeds `max_length`
/// @throws std::domain_error if `str` contains a char not in `alphabet`
HEDLEY_ALWAYS_INLINE
static constexpr integral_type encode_(string_view str)
{
constexpr auto npos = string_view::npos;
using std::string_literals::operator""s;
utils::constexpr_maybe_throw<std::length_error>(
str.size() > max_length,
"str.size() cannot exceed max_length");
integral_type val{0};
for (CharT c : str)
{
auto next_digit = digit_of_(c);
utils::constexpr_maybe_throw<std::domain_error>(
next_digit == npos,
"str contains a disallowed char");
val = val * radix + next_digit;
}
return val;
}
/// @brief Index of `c` in `alphabet`, or `npos` when `c` is absent.
/// Byte alphabets use a 256-entry table; wider character types scan.
/// @param c the `c`
/// @return Index of `c` in `alphabet`, or `npos` when `c` is absent
static constexpr std::size_t digit_of_(CharT c)
{
constexpr auto missing = string_view::npos;
if constexpr (sizeof(CharT) == 1)
{
constexpr auto table = []()
{
std::array<std::size_t, 256> digits{};
for (auto & slot : digits)
slot = missing;
for (std::size_t i = 0; i < radix; ++i)
{
auto uc = static_cast<unsigned char>(alphabet[i]);
if (digits[uc] == missing)
digits[uc] = i;
}
return digits;
}();
return table[static_cast<unsigned char>(c)];
}
else
{
return alphabet.find(c);
}
}
/// @brief performs stream input and output on
/// `dpf::basic_fixed_length_string`s
/// @{
/// @brief Writes the decoded string, not the packed integer.
/// @param os the character output stream
/// @param k the `k`
/// @return the stream
friend std::basic_ostream<CharT, Traits> &
operator<<(std::basic_ostream<CharT, Traits> & os,
const basic_fixed_length_string & k)
{
return os << static_cast<std::basic_string<CharT, Traits, Allocator>>(k);
}
/// @brief Reads a whitespace-delimited token and encodes it.
/// @param is the character input stream
/// @param k the `k`
/// @return the stream
friend std::basic_istream<CharT, Traits> &
operator>>(std::basic_istream<CharT, Traits> & is,
basic_fixed_length_string & k)
{
std::basic_string<CharT, Traits, Allocator> tmp;
if (!(is >> tmp))
return is;
try
{
k = basic_fixed_length_string(string_view(tmp));
}
catch (const std::exception &)
{
is.setstate(std::ios::failbit);
}
return is;
}
/// @}
friend struct utils::countl_zero_symmetric_difference<basic_fixed_length_string>;
friend struct utils::msb_of<basic_fixed_length_string>;
friend struct utils::mod_pow_2<basic_fixed_length_string>;
friend struct utils::make_from_integral_value<basic_fixed_length_string>;
}; // class dpf::basic_fixed_length_string
/// @brief instantiation of the `dpf::basic_fixed_length_string` class that
/// uses `char` (i.e., bytes) as its **character type**, with its
/// default `char_traits` and `allocator` types (see
/// `dpf::basic_fixed_length_string` for more info on the template).
/// @note A fixed-alphabet domain. The successor is `dpf::keyword2`, which
/// stores a rank in a pattern language and is also not a leaf.
/// @see dpf::keyword2
/// @see dpf::alphabets
template <std::size_t MaxLen,
const char * Alphabet = alphabets::lowercase_alpha>
using keyword = basic_fixed_length_string<MaxLen, char, Alphabet>;
/// @brief convert a `dpf::basic_fixed_length_string` to a `std::basic_string`
/// @details Uses a `static_cast` to convert `str` to recreate the string
/// representation of a `basic_fixed_length_string`
/// @complexity `O(MaxLen)` where `MaxLen` is the maximum string length
/// @tparam MaxLen maximum string length
/// @tparam CharT character type
/// @tparam Alphabet alphabet the string is drawn from
/// @tparam Traits character traits
/// @tparam Allocator allocator type
/// @param str the source string
/// @return the returned `std::basic_string<CharT, Traits, Allocator>`
template <std::size_t MaxLen,
typename CharT,
const CharT * Alphabet,
typename Traits = std::char_traits<CharT>,
typename Allocator = std::allocator<CharT>>
static constexpr std::basic_string<CharT, Traits, Allocator>
to_string(basic_fixed_length_string<MaxLen, CharT, Alphabet, Traits, Allocator>
str)
{
return static_cast<std::basic_string<CharT, Traits, Allocator>>(str);
}
namespace utils
{
/// @brief specializes `dpf::bitlength_of` for `dpf::basic_fixed_length_string`
/// @tparam MaxLen maximum string length
/// @tparam CharT character type
/// @tparam Alpha alphabet the string is drawn from
/// @tparam Traits character traits
/// @tparam Alloc allocator type
template <std::size_t MaxLen,
typename CharT,
const CharT * Alpha,
typename Traits,
typename Alloc>
struct bitlength_of<
dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>>
: public std::integral_constant<std::size_t,
dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>::bits> { };
/// @brief specializes `dpf::msb_of` for `dpf::basic_fixed_length_string`
/// @tparam MaxLen maximum string length
/// @tparam CharT character type
/// @tparam Alpha alphabet the string is drawn from
/// @tparam Traits character traits
/// @tparam Alloc allocator type
template <std::size_t MaxLen,
typename CharT,
const CharT * Alpha,
typename Traits,
typename Alloc>
struct msb_of<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>>
{
using T = dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>;
using U = typename T::integral_type;
static constexpr T value = U{1} << bitlength_of_v<T> - 1ul;
};
/// @brief specializes `dpf::countl_zero_symmetric_difference` for
/// `dpf::basic_fixed_length_string`
/// @tparam MaxLen maximum string length
/// @tparam CharT character type
/// @tparam Alpha alphabet the string is drawn from
/// @tparam Traits character traits
/// @tparam Alloc allocator type
template <std::size_t MaxLen,
typename CharT,
const CharT * Alpha,
typename Traits,
typename Alloc>
struct countl_zero_symmetric_difference<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>>
: countl_zero_symmetric_difference<typename dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>::parent>
{ };
template <std::size_t MaxLen,
typename CharT,
const CharT * Alpha,
typename Traits,
typename Alloc>
struct mod_pow_2<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>>
: mod_pow_2<typename dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>::parent>
{ };
template <std::size_t MaxLen,
typename CharT,
const CharT * Alpha,
typename Traits,
typename Alloc>
struct make_from_integral_value<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>>
{
using T = dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>;
using integral_type = integral_type_from_bitlength_t<bitlength_of_v<T>>;
HEDLEY_NO_THROW
constexpr T operator()(integral_type val) const noexcept
{
return T{val};
}
};
} // namespace utils
} // namespace dpf
namespace std
{
/// @brief specializes `std::numeric_limits` for CV-qualified `dpf::keyword`s
/// @{
/// @details specializes `std::numeric_limits` for `dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>`
/// @tparam MaxLen maximum string length
/// @tparam CharT character type
/// @tparam Alpha alphabet the string is drawn from
/// @tparam Traits character traits
/// @tparam Alloc allocator type
template <std::size_t MaxLen,
typename CharT,
const CharT * Alpha,
typename Traits,
typename Alloc>
class numeric_limits<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>>
{
public:
using keyword_type = dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>;
static constexpr bool is_specialized = true;
static constexpr bool is_signed = false;
static constexpr bool is_integer = true;
static constexpr bool is_exact = true;
static constexpr bool has_infinity = false;
static constexpr bool has_quiet_NaN = false;
static constexpr bool has_signaling_NaN = false;
static constexpr std::float_denorm_style has_denorm = std::denorm_absent;
static constexpr bool has_denorm_loss = false;
static constexpr std::float_round_style round_style = std::round_toward_zero;
static constexpr bool is_iec559 = false;
static constexpr bool is_bounded = true;
static constexpr bool is_modulo = true;
static constexpr int digits = keyword_type::bits;
static constexpr int digits10 = static_cast<int>((static_cast<unsigned long long>(keyword_type::bits) * 30103ull) / 100000ull);
static constexpr int max_digits10 = 0;
static constexpr int radix = 2;
static constexpr int min_exponent = 0;
static constexpr int max_exponent = 0;
static constexpr int min_exponent10 = 0;
static constexpr int max_exponent10 = 0;
static constexpr bool traps
= std::numeric_limits<typename keyword_type::integral_type>::traps;
static constexpr bool tinyness_before = false;
HEDLEY_NO_THROW
static constexpr keyword_type min() noexcept { return keyword_type{""}; }
HEDLEY_NO_THROW
static constexpr keyword_type lowest() noexcept { return keyword_type{""}; }
HEDLEY_NO_THROW
static constexpr keyword_type max() noexcept { return ~keyword_type{""}; }
HEDLEY_NO_THROW
static constexpr keyword_type epsilon() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type round_error() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type infinity() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type quiet_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type signaling_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type denorm_min() noexcept { return 0; }
};
/// @details specializes `std::numeric_limits` for `dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc> const`
/// @tparam MaxLen maximum string length
/// @tparam CharT character type
/// @tparam Alpha alphabet the string is drawn from
/// @tparam Traits character traits
/// @tparam Alloc allocator type
template <std::size_t MaxLen,
typename CharT,
const CharT * Alpha,
typename Traits,
typename Alloc>
class numeric_limits<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc> const>
: public numeric_limits<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>> {};
/// @details specializes `std::numeric_limits` for
/// @brief `dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc> volatile`
/// @tparam MaxLen maximum string length
/// @tparam CharT character type
/// @tparam Alpha alphabet the string is drawn from
/// @tparam Traits character traits
/// @tparam Alloc allocator type
template <std::size_t MaxLen,
typename CharT,
const CharT * Alpha,
typename Traits,
typename Alloc>
class numeric_limits<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc> volatile>
: public numeric_limits<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>> {};
/// @details specializes `std::numeric_limits` for
/// @brief `dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc> const volatile`
/// @tparam MaxLen maximum string length
/// @tparam CharT character type
/// @tparam Alpha alphabet the string is drawn from
/// @tparam Traits character traits
/// @tparam Alloc allocator type
template <std::size_t MaxLen,
typename CharT,
const CharT * Alpha,
typename Traits,
typename Alloc>
class numeric_limits<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc> const volatile>
: public numeric_limits<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>> {};
/// @}
} // namespace std
#endif // LIBDPF_INCLUDE_DPF_KEYWORD_HPP__