Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions
@@ -0,0 +1,169 @@
# clang-format 14
Language: Cpp
Standard: c++20
AccessModifierOffset: -4 #?
AlignAfterOpenBracket: AlwaysBreak
AlignArrayOfStructures: Left
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignConsecutiveMacros: true
AlignEscapedNewlines: DontAlign
AlignOperands: AlignAfterOperator
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLambdasOnASingleLine: Inline
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
AttributeMacros: []
BinPackArguments: true
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: MultiLine
AfterEnum: false
AfterFunction: false
AfterNamespace: false
#AfterObjCDeclaration
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: false
SplitEmptyNamespace: true
#BreakAfterJavaFieldAnnotations
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BreakBeforeConceptDeclarations: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakStringLiterals: false
ColumnLimit: 0
CommentPragmas: '' #?
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4 #?
ContinuationIndentWidth: 4 #?
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
EmptyLineAfterAccessModifier: Leave
EmptyLineBeforeAccessModifier: Leave
FixNamespaceComments: true
ForEachMacros: []
IfMacros: ['MPT_MAYBE_CONSTANT_IF']
IncludeBlocks: Preserve
IncludeCategories: [] #?
IncludeIsMainRegex: '' #?
IncludeIsMainSourceRegex: '' #?
IndentAccessModifiers: false
IndentCaseLabels: true
IndentCaseBlocks: true
IndentExternBlock: NoIndent
IndentGotoLabels: false
IndentPPDirectives: None
#IndentRequiresClause: true
InsertTrailingCommas: None
#BeforeHash
IndentWidth: 4
IndentWrappedFunctionNames: true
#JavaImportGroups
#JavaScriptQuotes
#JavaScriptWrapImports
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: OuterScope
MacroBlockBegin: '^MPT_TEST_GROUP_BEGIN$' #?
MacroBlockEnd: '^MPT_TEST_GROUP_END$' #?
MaxEmptyLinesToKeep: 5
NamespaceIndentation: None
NamespaceMacros: [] #?
#ObjCBinPackProtocolList
#ObjCBlockIndentWidth
#ObjCBreakBeforeNestedBlockParam
#ObjCSpaceAfterProperty
#ObjCSpaceBeforeProtocolList
PackConstructorInitializers: Never
#PenaltyBreakAssignment
#PenaltyBreakBeforeFirstCallParameter
#PenaltyBreakComment
#PenaltyBreakFirstLessLess
#PenaltyBreakOpenParenthesis
#PenaltyBreakString
#PenaltyBreakTemplateDeclaration
#PenaltyExcessCharacter
#PenaltyIndentedWhitespace
#PenaltyReturnTypeOnItsOwnLine
PointerAlignment: Middle
PPIndentWidth: -1
#RawStringFormats
QualifierAlignment: Leave
#QualifierOrder: ['static', 'inline', 'constexpr', 'volatile', 'const', 'restrict', 'type']
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: false
#SortJavaStaticImport
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDeclarationName: false
AfterFunctionDefinitionName: false
AfterIfMacros: true
AfterOverloadedOperator: false
#AfterRequiresInClause: false
#AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
StatementAttributeLikeMacros: []
StatementMacros: [ '_Pragma', '__pragma', 'MPT_WARNING', 'MPT_TEST_GROUP_INLINE_IDENTIFIER', 'MPT_TEST_GROUP_INLINE', 'MPT_TEST_GROUP_STATIC' ] #?
TabWidth: 4
TypenameMacros: [] #?
UseCRLF: false
UseTab: ForContinuationAndIndentation
WhitespaceSensitiveMacros:
- MPT_PP_STRINGIFY
@@ -0,0 +1,25 @@
Copyright (c) 2004-2022, OpenMPT Project Developers and Contributors
Copyright (c) 1997-2003, Olivier Lapicque
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the OpenMPT project nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_AUDIO_SAMPLE_HPP
#define MPT_AUDIO_SAMPLE_HPP
#include "mpt/base/floatingpoint.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include <type_traits>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
using audio_sample_int = int16;
using audio_sample_float = nativefloat;
using audio_sample = std::conditional<mpt::float_traits<audio_sample_float>::is_hard, audio_sample_float, audio_sample_int>::type;
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BINARY_HEX_HPP
@@ -0,0 +1,403 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_AUDIO_SPAN_HPP
#define MPT_AUDIO_SPAN_HPP
#include "mpt/base/macros.hpp"
#include "mpt/base/namespace.hpp"
#include <cassert>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
// LxxxLxxxLxxxLxxxLxxx xRxxxRxxxRxxxRxxxRxx
template <typename SampleType>
struct audio_span_planar_strided {
public:
using sample_type = SampleType;
private:
SampleType * const * m_buffers;
std::ptrdiff_t m_frame_stride;
std::size_t m_channels;
std::size_t m_frames;
public:
constexpr audio_span_planar_strided(SampleType * const * buffers, std::size_t channels, std::size_t frames, std::ptrdiff_t frame_stride) noexcept
: m_buffers(buffers)
, m_frame_stride(frame_stride)
, m_channels(channels)
, m_frames(frames) {
return;
}
SampleType * const * data_planar() const noexcept {
return m_buffers;
}
SampleType * data() const noexcept {
return nullptr;
}
SampleType & operator()(std::size_t channel, std::size_t frame) const {
return m_buffers[channel][static_cast<std::ptrdiff_t>(frame) * m_frame_stride];
}
bool is_contiguous() const noexcept {
return false;
}
bool channels_are_contiguous() const noexcept {
return false;
}
bool frames_are_contiguous() const noexcept {
return false;
}
std::size_t size_channels() const noexcept {
return m_channels;
}
std::size_t size_frames() const noexcept {
return m_frames;
}
std::size_t size_samples() const noexcept {
return m_channels * m_frames;
}
std::ptrdiff_t frame_stride() const noexcept {
return m_frame_stride;
}
};
// LLLLL RRRRR
template <typename SampleType>
struct audio_span_planar {
public:
using sample_type = SampleType;
private:
SampleType * const * m_buffers;
std::size_t m_channels;
std::size_t m_frames;
public:
constexpr audio_span_planar(SampleType * const * buffers, std::size_t channels, std::size_t frames) noexcept
: m_buffers(buffers)
, m_channels(channels)
, m_frames(frames) {
return;
}
SampleType * const * data_planar() const noexcept {
return m_buffers;
}
SampleType * data() const noexcept {
return nullptr;
}
SampleType & operator()(std::size_t channel, std::size_t frame) const {
return m_buffers[channel][frame];
}
bool is_contiguous() const noexcept {
return false;
}
bool channels_are_contiguous() const noexcept {
return false;
}
bool frames_are_contiguous() const noexcept {
return false;
}
std::size_t size_channels() const noexcept {
return m_channels;
}
std::size_t size_frames() const noexcept {
return m_frames;
}
std::size_t size_samples() const noexcept {
return m_channels * m_frames;
}
};
// LLLLLRRRRR
template <typename SampleType>
struct audio_span_contiguous {
public:
using sample_type = SampleType;
private:
SampleType * const m_buffer;
std::size_t m_channels;
std::size_t m_frames;
public:
constexpr audio_span_contiguous(SampleType * buffer, std::size_t channels, std::size_t frames) noexcept
: m_buffer(buffer)
, m_channels(channels)
, m_frames(frames) {
return;
}
SampleType * const * data_planar() const noexcept {
return nullptr;
}
SampleType * data() const noexcept {
return m_buffer;
}
SampleType & operator()(std::size_t channel, std::size_t frame) const {
return m_buffer[(m_frames * channel) + frame];
}
bool is_contiguous() const noexcept {
return true;
}
bool channels_are_contiguous() const noexcept {
return true;
}
bool frames_are_contiguous() const noexcept {
return false;
}
std::size_t size_channels() const noexcept {
return m_channels;
}
std::size_t size_frames() const noexcept {
return m_frames;
}
std::size_t size_samples() const noexcept {
return m_channels * m_frames;
}
};
// LRLRLRLRLR
template <typename SampleType>
struct audio_span_interleaved {
public:
using sample_type = SampleType;
private:
SampleType * const m_buffer;
std::size_t m_channels;
std::size_t m_frames;
public:
constexpr audio_span_interleaved(SampleType * buffer, std::size_t channels, std::size_t frames) noexcept
: m_buffer(buffer)
, m_channels(channels)
, m_frames(frames) {
return;
}
SampleType * const * data_planar() const noexcept {
return nullptr;
}
SampleType * data() const noexcept {
return m_buffer;
}
SampleType & operator()(std::size_t channel, std::size_t frame) const {
return m_buffer[m_channels * frame + channel];
}
bool is_contiguous() const noexcept {
return true;
}
bool channels_are_contiguous() const noexcept {
return false;
}
bool frames_are_contiguous() const noexcept {
return true;
}
std::size_t size_channels() const noexcept {
return m_channels;
}
std::size_t size_frames() const noexcept {
return m_frames;
}
std::size_t size_samples() const noexcept {
return m_channels * m_frames;
}
};
struct audio_span_frames_are_contiguous_t {
};
struct audio_span_channels_are_contiguous_t {
};
struct audio_span_channels_are_planar_t {
};
struct audio_span_channels_are_planar_and_strided_t {
};
// LRLRLRLRLR
inline constexpr audio_span_frames_are_contiguous_t audio_span_frames_are_contiguous;
// LLLLLRRRRR
inline constexpr audio_span_channels_are_contiguous_t audio_span_channels_are_contiguous;
// LLLLL RRRRR
inline constexpr audio_span_channels_are_planar_t audio_span_channels_are_planar;
// LxxxLxxxLxxxLxxxLxxx xRxxxRxxxRxxxRxxxRxx
inline constexpr audio_span_channels_are_planar_and_strided_t audio_span_channels_are_planar_and_strided;
template <typename SampleType>
struct audio_span {
public:
using sample_type = SampleType;
private:
union {
SampleType * const contiguous;
SampleType * const * const planes;
} m_buffer;
std::ptrdiff_t m_frame_stride;
std::ptrdiff_t m_channel_stride;
std::size_t m_channels;
std::size_t m_frames;
public:
constexpr audio_span(audio_span_interleaved<SampleType> buffer) noexcept
: m_frame_stride(static_cast<std::ptrdiff_t>(buffer.size_channels()))
, m_channel_stride(1)
, m_channels(buffer.size_channels())
, m_frames(buffer.size_frames()) {
m_buffer.contiguous = buffer.data();
}
constexpr audio_span(SampleType * buffer, std::size_t channels, std::size_t frames, audio_span_frames_are_contiguous_t) noexcept
: m_frame_stride(static_cast<std::ptrdiff_t>(channels))
, m_channel_stride(1)
, m_channels(channels)
, m_frames(frames) {
m_buffer.contiguous = buffer;
}
constexpr audio_span(audio_span_contiguous<SampleType> buffer) noexcept
: m_frame_stride(1)
, m_channel_stride(buffer.size_frames())
, m_channels(buffer.size_channels())
, m_frames(buffer.size_frames()) {
m_buffer.contiguous = buffer.data();
}
constexpr audio_span(SampleType * buffer, std::size_t channels, std::size_t frames, audio_span_channels_are_contiguous_t) noexcept
: m_frame_stride(1)
, m_channel_stride(static_cast<std::ptrdiff_t>(frames))
, m_channels(channels)
, m_frames(frames) {
m_buffer.contiguous = buffer;
}
constexpr audio_span(audio_span_planar<SampleType> buffer) noexcept
: m_frame_stride(1)
, m_channel_stride(0)
, m_channels(buffer.size_channels())
, m_frames(buffer.size_frames()) {
m_buffer.planes = buffer.data_planar();
}
constexpr audio_span(SampleType * const * planes, std::size_t channels, std::size_t frames, audio_span_channels_are_planar_t) noexcept
: m_frame_stride(1)
, m_channel_stride(0)
, m_channels(channels)
, m_frames(frames) {
m_buffer.planes = planes;
}
constexpr audio_span(audio_span_planar_strided<SampleType> buffer) noexcept
: m_frame_stride(static_cast<std::ptrdiff_t>(buffer.frame_stride()))
, m_channel_stride(0)
, m_channels(buffer.size_channels())
, m_frames(buffer.size_frames()) {
m_buffer.planes = buffer.data_planar();
}
constexpr audio_span(SampleType * const * planes, std::size_t channels, std::size_t frames, std::ptrdiff_t frame_stride, audio_span_channels_are_planar_and_strided_t) noexcept
: m_frame_stride(frame_stride)
, m_channel_stride(0)
, m_channels(channels)
, m_frames(frames) {
m_buffer.planes = planes;
}
bool is_contiguous() const noexcept {
return (m_channel_stride != 0);
}
SampleType * const * data_planar() const noexcept {
return (!is_contiguous()) ? m_buffer.planes : nullptr;
}
SampleType * data() const noexcept {
return is_contiguous() ? m_buffer.contiguous : nullptr;
}
SampleType & operator()(std::size_t channel, std::size_t frame) const {
return is_contiguous() ? m_buffer.contiguous[(m_channel_stride * static_cast<std::ptrdiff_t>(channel)) + (m_frame_stride * static_cast<std::ptrdiff_t>(frame))] : m_buffer.planes[channel][frame * static_cast<std::ptrdiff_t>(m_frame_stride)];
}
bool channels_are_contiguous() const noexcept {
return (m_channel_stride == static_cast<std::ptrdiff_t>(m_frames));
}
bool frames_are_contiguous() const noexcept {
return (m_frame_stride == static_cast<std::ptrdiff_t>(m_channels));
}
std::size_t size_channels() const noexcept {
return m_channels;
}
std::size_t size_frames() const noexcept {
return m_frames;
}
std::size_t size_samples() const noexcept {
return m_channels * m_frames;
}
};
template <typename Taudio_span>
struct audio_span_with_offset {
public:
using sample_type = typename Taudio_span::sample_type;
private:
Taudio_span m_buffer;
std::size_t m_offset;
public:
constexpr audio_span_with_offset(Taudio_span buffer, std::size_t offsetFrames) noexcept
: m_buffer(buffer)
, m_offset(offsetFrames) {
return;
}
sample_type * data() const noexcept {
if (!is_contiguous()) {
return nullptr;
}
return m_buffer.data() + (size_channels() * m_offset);
}
sample_type & operator()(std::size_t channel, std::size_t frame) const {
return m_buffer(channel, m_offset + frame);
}
bool is_contiguous() const noexcept {
return m_buffer.is_contiguous() && m_buffer.frames_are_contiguous();
}
bool channels_are_contiguous() const noexcept {
return m_buffer.channels_are_contiguous();
}
bool frames_are_contiguous() const noexcept {
return m_buffer.frames_are_contiguous();
}
std::size_t size_channels() const noexcept {
return m_buffer.size_channels();
}
std::size_t size_frames() const noexcept {
return m_buffer.size_frames() - m_offset;
}
std::size_t size_samples() const noexcept {
return size_channels() * size_frames();
}
};
template <typename BufferType>
inline audio_span_with_offset<BufferType> make_audio_span_with_offset(BufferType buf, std::size_t offsetFrames) noexcept {
assert(offsetFrames <= buf.size_frames());
return audio_span_with_offset<BufferType>{buf, offsetFrames};
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_AUDIO_SPAN_HPP
@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_ALGORITHM_HPP
#define MPT_BASE_ALGORITHM_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/saturate_cast.hpp"
#include <algorithm>
#include <limits>
namespace mpt {
inline namespace MPT_INLINE_NS {
// Grows x with an exponential factor suitable for increasing buffer sizes.
// Clamps the result at limit.
// And avoids integer overflows while doing its business.
// The growth factor is 1.5, rounding down, execpt for the initial x==1 case.
template <typename T, typename Tlimit>
inline T exponential_grow(const T & x, const Tlimit & limit) {
if (x <= 1) {
return 2;
}
T add = std::min(x >> 1, std::numeric_limits<T>::max() - x);
return std::min(x + add, mpt::saturate_cast<T>(limit));
}
template <typename T>
inline T exponential_grow(const T & x) {
return mpt::exponential_grow(x, std::numeric_limits<T>::max());
}
// Check if val is in [lo,hi] without causing compiler warnings
// if theses checks are always true due to the domain of T.
// GCC does not warn if the type is templated.
template <typename T, typename C>
constexpr bool is_in_range(const T & val, const C & lo, const C & hi) {
return lo <= val && val <= hi;
}
template <typename Tcontainer, typename Tval>
MPT_CONSTEXPR20_FUN bool contains(const Tcontainer & container, const Tval & value) noexcept(noexcept(std::find(std::begin(container), std::end(container), value))) {
return std::find(std::begin(container), std::end(container), value) != std::end(container);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_ALGORITHM_HPP
@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_ALIGNED_ARRAY_HPP
#define MPT_BASE_ALIGNED_ARRAY_HPP
#include "mpt/base/bit.hpp"
#include "mpt/base/namespace.hpp"
#include <array>
#include <memory>
#include <new>
#include <cassert>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename T, std::size_t count, std::align_val_t alignment>
struct alignas(static_cast<std::size_t>(alignment)) aligned_array
: std::array<T, count> {
static_assert(static_cast<std::size_t>(alignment) >= alignof(T));
static_assert(((count * sizeof(T)) % static_cast<std::size_t>(alignment)) == 0);
static_assert(sizeof(std::array<T, count>) == (sizeof(T) * count));
};
static_assert(sizeof(mpt::aligned_array<float, 4, std::align_val_t{sizeof(float) * 4}>) == sizeof(std::array<float, 4>));
template <std::size_t alignment_elements, std::size_t expected_elements, typename T, std::size_t N>
T * align_elements(std::array<T, N> & a) {
static_assert(mpt::has_single_bit(alignof(T)));
static_assert(mpt::has_single_bit(sizeof(T)));
static_assert(mpt::has_single_bit(alignment_elements));
static_assert((expected_elements + alignment_elements - 1) <= N);
void * buf = a.data();
std::size_t size = N * sizeof(T);
void * result = std::align(alignment_elements * sizeof(T), expected_elements * sizeof(T), buf, size);
assert(result);
return reinterpret_cast<T *>(result);
}
template <std::size_t alignment_elements, std::size_t expected_elements, typename T, std::size_t N>
T * align_elements(T (&a)[N]) {
static_assert(mpt::has_single_bit(alignof(T)));
static_assert(mpt::has_single_bit(sizeof(T)));
static_assert(mpt::has_single_bit(alignment_elements));
static_assert((expected_elements + alignment_elements - 1) <= N);
void * buf = a;
std::size_t size = N * sizeof(T);
void * result = std::align(alignment_elements * sizeof(T), expected_elements * sizeof(T), buf, size);
assert(result);
return reinterpret_cast<T *>(result);
}
template <std::size_t alignment_bytes, std::size_t expected_elements, typename T, std::size_t N>
T * align_bytes(std::array<T, N> & a) {
static_assert(mpt::has_single_bit(alignof(T)));
static_assert(mpt::has_single_bit(sizeof(T)));
static_assert(mpt::has_single_bit(alignment_bytes));
static_assert((alignment_bytes % alignof(T)) == 0);
static_assert(((expected_elements * sizeof(T)) + alignment_bytes - 1) <= (N * sizeof(T)));
void * buf = a.data();
std::size_t size = N * sizeof(T);
void * result = std::align(alignment_bytes, expected_elements * sizeof(T), buf, size);
assert(result);
return reinterpret_cast<T *>(result);
}
template <std::size_t alignment_bytes, std::size_t expected_elements, typename T, std::size_t N>
T * align_bytes(T (&a)[N]) {
static_assert(mpt::has_single_bit(alignof(T)));
static_assert(mpt::has_single_bit(sizeof(T)));
static_assert(mpt::has_single_bit(alignment_bytes));
static_assert((alignment_bytes % alignof(T)) == 0);
static_assert(((expected_elements * sizeof(T)) + alignment_bytes - 1) <= (N * sizeof(T)));
void * buf = a;
std::size_t size = N * sizeof(T);
void * result = std::align(alignment_bytes, expected_elements * sizeof(T), buf, size);
assert(result);
return reinterpret_cast<T *>(result);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_ALIGNED_ARRAY_HPP
@@ -0,0 +1,180 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_ALLOC_HPP
#define MPT_BASE_ALLOC_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/span.hpp"
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename T>
inline mpt::span<T> as_span(std::vector<T> & cont) {
return mpt::span<T>(cont.data(), cont.data() + cont.size());
}
template <typename T>
inline mpt::span<const T> as_span(const std::vector<T> & cont) {
return mpt::span<const T>(cont.data(), cont.data() + cont.size());
}
template <typename T>
inline span<T> as_span(std::basic_string<T> & str) {
return span<T>(str.data(), str.size());
}
template <typename T>
inline span<const T> as_span(const std::basic_string<T> & str) {
return span<const T>(str.data(), str.size());
}
template <typename T>
inline std::vector<typename std::remove_const<T>::type> make_vector(T * beg, T * end) {
return std::vector<typename std::remove_const<T>::type>(beg, end);
}
template <typename T>
inline std::vector<typename std::remove_const<T>::type> make_vector(T * data, std::size_t size) {
return std::vector<typename std::remove_const<T>::type>(data, data + size);
}
template <typename T>
inline std::vector<typename std::remove_const<T>::type> make_vector(mpt::span<T> data) {
return std::vector<typename std::remove_const<T>::type>(data.data(), data.data() + data.size());
}
template <typename T, std::size_t N>
inline std::vector<typename std::remove_const<T>::type> make_vector(T (&arr)[N]) {
return std::vector<typename std::remove_const<T>::type>(std::begin(arr), std::end(arr));
}
template <typename T>
inline std::vector<typename std::remove_const<T>::type> make_vector(const std::basic_string<T> & str) {
return std::vector<typename std::remove_const<T>::type>(str.begin(), str.end());
}
template <typename T>
inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(T * beg, T * end) {
return std::basic_string<typename std::remove_const<T>::type>(beg, end);
}
template <typename T>
inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(T * data, std::size_t size) {
return std::basic_string<typename std::remove_const<T>::type>(data, data + size);
}
template <typename T>
inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(mpt::span<T> data) {
return std::basic_string<typename std::remove_const<T>::type>(data.data(), data.data() + data.size());
}
template <typename T, std::size_t N>
inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(T (&arr)[N]) {
return std::basic_string<typename std::remove_const<T>::type>(std::begin(arr), std::end(arr));
}
template <typename T>
inline std::basic_string<typename std::remove_const<T>::type> make_basic_string(const std::vector<T> & str) {
return std::vector<typename std::remove_const<T>::type>(str.begin(), str.end());
}
template <typename Tcont2, typename Tcont1>
inline Tcont1 & append(Tcont1 & cont1, const Tcont2 & cont2) {
cont1.insert(cont1.end(), cont2.begin(), cont2.end());
return cont1;
}
template <typename Tit2, typename Tcont1>
inline Tcont1 & append(Tcont1 & cont1, Tit2 beg, Tit2 end) {
cont1.insert(cont1.end(), beg, end);
return cont1;
}
template <typename Tdst, typename Tsrc>
struct buffer_cast_impl {
inline Tdst operator()(const Tsrc & src) const {
return Tdst(mpt::byte_cast<const typename Tdst::value_type *>(src.data()), mpt::byte_cast<const typename Tdst::value_type *>(src.data()) + src.size());
}
};
// casts between vector<->string of byte-castable types
template <typename Tdst, typename Tsrc>
inline Tdst buffer_cast(Tsrc src) {
return buffer_cast_impl<Tdst, Tsrc>()(src);
}
template <typename T>
struct as_raw_memory_impl<std::vector<T>> {
inline mpt::const_byte_span operator()(const std::vector<T> & v) const {
static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::as_span(reinterpret_cast<const std::byte *>(v.data()), v.size() * sizeof(T));
}
inline mpt::byte_span operator()(std::vector<T> & v) const {
static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::as_span(reinterpret_cast<std::byte *>(v.data()), v.size() * sizeof(T));
}
};
template <typename T>
struct as_raw_memory_impl<const std::vector<T>> {
inline mpt::const_byte_span operator()(const std::vector<T> & v) const {
static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::as_span(reinterpret_cast<const std::byte *>(v.data()), v.size() * sizeof(T));
}
};
template <typename T>
class heap_value {
private:
std::unique_ptr<T> m_value{};
public:
template <typename... Targs>
heap_value(Targs &&... args)
: m_value(std::make_unique<T>(std::forward<Targs>(args)...)) {
return;
}
const T & operator*() const {
return *m_value;
}
T & operator*() {
return *m_value;
}
};
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_ALLOC_HPP
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_ARITHMETIC_SHIFT_HPP
#define MPT_BASE_ARITHMETIC_SHIFT_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/saturate_cast.hpp"
namespace mpt {
inline namespace MPT_INLINE_NS {
// mpt::rshift_signed
// mpt::lshift_signed
// Shift a signed integer value in a well-defined manner.
// Does the same thing as MSVC would do. This is verified by the test suite.
template <typename T>
constexpr auto rshift_signed_standard(T x, int y) noexcept -> decltype(x >> y) {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::numeric_limits<T>::is_signed);
using result_type = decltype(x >> y);
using unsigned_result_type = typename std::make_unsigned<result_type>::type;
const unsigned_result_type roffset = static_cast<unsigned_result_type>(1) << ((sizeof(result_type) * 8) - 1);
result_type rx = x;
unsigned_result_type urx = static_cast<unsigned_result_type>(rx);
urx += roffset;
urx >>= y;
urx -= roffset >> y;
return static_cast<result_type>(urx);
}
template <typename T>
constexpr auto lshift_signed_standard(T x, int y) noexcept -> decltype(x << y) {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::numeric_limits<T>::is_signed);
using result_type = decltype(x << y);
using unsigned_result_type = typename std::make_unsigned<result_type>::type;
const unsigned_result_type roffset = static_cast<unsigned_result_type>(1) << ((sizeof(result_type) * 8) - 1);
result_type rx = x;
unsigned_result_type urx = static_cast<unsigned_result_type>(rx);
urx += roffset;
urx <<= y;
urx -= roffset << y;
return static_cast<result_type>(urx);
}
#if MPT_COMPILER_SHIFT_SIGNED
template <typename T>
constexpr auto rshift_signed_undefined(T x, int y) noexcept -> decltype(x >> y) {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::numeric_limits<T>::is_signed);
return x >> y;
}
template <typename T>
constexpr auto lshift_signed_undefined(T x, int y) noexcept -> decltype(x << y) {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::numeric_limits<T>::is_signed);
return x << y;
}
template <typename T>
constexpr auto rshift_signed(T x, int y) noexcept -> decltype(x >> y) {
return mpt::rshift_signed_undefined(x, y);
}
template <typename T>
constexpr auto lshift_signed(T x, int y) noexcept -> decltype(x << y) {
return mpt::lshift_signed_undefined(x, y);
}
#else
template <typename T>
constexpr auto rshift_signed(T x, int y) noexcept -> decltype(x >> y) {
return mpt::rshift_signed_standard(x, y);
}
template <typename T>
constexpr auto lshift_signed(T x, int y) noexcept -> decltype(x << y) {
return mpt::lshift_signed_standard(x, y);
}
#endif
template <typename T>
constexpr auto arithmetic_shift_right(T x, int y) noexcept -> decltype(x >> y) {
return mpt::rshift_signed(x, y);
}
template <typename T>
constexpr auto arithmetic_shift_right(T x, int y) noexcept -> decltype(x << y) {
return mpt::lshift_signed(x, y);
}
template <typename T>
constexpr auto sar(T x, int y) noexcept -> decltype(x >> y) {
return mpt::rshift_signed(x, y);
}
template <typename T>
constexpr auto sal(T x, int y) noexcept -> decltype(x << y) {
return mpt::lshift_signed(x, y);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_ARITHMETIC_SHIFT_HPP
@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_ARRAY_HPP
#define MPT_BASE_ARRAY_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include <array>
#include <type_traits>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename T>
struct stdarray_extent : std::integral_constant<std::size_t, 0> { };
template <typename T, std::size_t N>
struct stdarray_extent<std::array<T, N>> : std::integral_constant<std::size_t, N> { };
template <typename T>
struct is_stdarray : std::false_type { };
template <typename T, std::size_t N>
struct is_stdarray<std::array<T, N>> : std::true_type { };
// mpt::extent is the same as std::extent,
// but also works for std::array,
// and asserts that the given type is actually an array type instead of returning 0.
// use as:
// mpt::extent<decltype(expr)>()
// mpt::extent<decltype(variable)>()
// mpt::extent<decltype(type)>()
// mpt::extent<type>()
template <typename T>
constexpr std::size_t extent() noexcept {
using Tarray = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
static_assert(std::is_array<Tarray>::value || mpt::is_stdarray<Tarray>::value);
if constexpr (mpt::is_stdarray<Tarray>::value) {
return mpt::stdarray_extent<Tarray>();
} else {
return std::extent<Tarray>();
}
}
template <typename>
struct array_size;
template <typename T, std::size_t N>
struct array_size<std::array<T, N>> {
static constexpr std::size_t size = N;
};
template <typename T, std::size_t N>
struct array_size<T[N]> {
static constexpr std::size_t size = N;
};
template <typename T, std::size_t N, typename Tx>
constexpr std::array<T, N> init_array(const Tx & x) {
std::array<T, N> result{};
for (std::size_t i = 0; i < N; ++i) {
result[i] = x;
}
return result;
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_ARRAY_HPP
@@ -0,0 +1,400 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_BIT_HPP
#define MPT_BASE_BIT_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/macros.hpp"
#if MPT_CXX_AT_LEAST(20)
#include <bit>
#else // !C++20
#include <array>
#include <limits>
#endif // C++20
#include <type_traits>
#include <cstddef>
#if MPT_CXX_BEFORE(20)
#include <cstring>
#endif // !C++20
namespace mpt {
inline namespace MPT_INLINE_NS {
#if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(14, 0, 0)
using std::bit_cast;
#else // !C++20
// C++2a compatible bit_cast.
// Not implementing constexpr because this is not easily possible pre C++20.
template <typename Tdst, typename Tsrc>
MPT_FORCEINLINE typename std::enable_if<(sizeof(Tdst) == sizeof(Tsrc)) && std::is_trivially_copyable<Tsrc>::value && std::is_trivially_copyable<Tdst>::value, Tdst>::type bit_cast(const Tsrc & src) noexcept {
Tdst dst{};
std::memcpy(&dst, &src, sizeof(Tdst));
return dst;
}
#endif // C++20
#if MPT_CXX_AT_LEAST(20)
using std::endian;
static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported");
constexpr mpt::endian get_endian() noexcept {
return mpt::endian::native;
}
constexpr bool endian_is_little() noexcept {
return get_endian() == mpt::endian::little;
}
constexpr bool endian_is_big() noexcept {
return get_endian() == mpt::endian::big;
}
constexpr bool endian_is_weird() noexcept {
return !endian_is_little() && !endian_is_big();
}
#else // !C++20
#if !MPT_COMPILER_GENERIC
#if MPT_COMPILER_MSVC
#define MPT_PLATFORM_LITTLE_ENDIAN
#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define MPT_PLATFORM_BIG_ENDIAN
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define MPT_PLATFORM_LITTLE_ENDIAN
#endif
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__)
#if __ORDER_BIG_ENDIAN__ != __ORDER_LITTLE_ENDIAN__
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define MPT_PLATFORM_BIG_ENDIAN
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define MPT_PLATFORM_LITTLE_ENDIAN
#endif
#endif
#endif
// fallback:
#if !defined(MPT_PLATFORM_BIG_ENDIAN) && !defined(MPT_PLATFORM_LITTLE_ENDIAN)
#if (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) \
|| (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \
|| (defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN))
#define MPT_PLATFORM_BIG_ENDIAN
#elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) \
|| (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \
|| (defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN))
#define MPT_PLATFORM_LITTLE_ENDIAN
#elif defined(__hpux) || defined(__hppa) \
|| defined(_MIPSEB) \
|| defined(__s390__)
#define MPT_PLATFORM_BIG_ENDIAN
#elif defined(__i386__) || defined(_M_IX86) \
|| defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|| defined(__bfin__)
#define MPT_PLATFORM_LITTLE_ENDIAN
#endif
#endif
#endif // !MPT_COMPILER_GENERIC
enum class endian {
little = 0x78563412u,
big = 0x12345678u,
weird = 1u,
#if MPT_COMPILER_GENERIC
native = 0u,
#elif defined(MPT_PLATFORM_LITTLE_ENDIAN)
native = little,
#elif defined(MPT_PLATFORM_BIG_ENDIAN)
native = big,
#else
native = 0u,
#endif
};
static_assert(mpt::endian::big != mpt::endian::little, "platform with all scalar types having size 1 is not supported");
MPT_FORCEINLINE mpt::endian endian_probe() noexcept {
using endian_probe_type = uint32;
static_assert(sizeof(endian_probe_type) == 4);
constexpr endian_probe_type endian_probe_big = 0x12345678u;
constexpr endian_probe_type endian_probe_little = 0x78563412u;
const std::array<std::byte, sizeof(endian_probe_type)> probe{
{std::byte{0x12}, std::byte{0x34}, std::byte{0x56}, std::byte{0x78}}
};
const endian_probe_type test = mpt::bit_cast<endian_probe_type>(probe);
mpt::endian result = mpt::endian::native;
switch (test) {
case endian_probe_big:
result = mpt::endian::big;
break;
case endian_probe_little:
result = mpt::endian::little;
break;
default:
result = mpt::endian::weird;
break;
}
return result;
}
MPT_FORCEINLINE mpt::endian get_endian() noexcept {
#if MPT_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable : 6285) // false-positive: (<non-zero constant> || <non-zero constant>) is always a non-zero constant.
#endif // MPT_COMPILER_MSVC
if constexpr ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) {
return mpt::endian::native;
} else {
return mpt::endian_probe();
}
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
}
MPT_FORCEINLINE bool endian_is_little() noexcept {
return get_endian() == mpt::endian::little;
}
MPT_FORCEINLINE bool endian_is_big() noexcept {
return get_endian() == mpt::endian::big;
}
MPT_FORCEINLINE bool endian_is_weird() noexcept {
return !endian_is_little() && !endian_is_big();
}
#endif // C++20
#if MPT_CXX_AT_LEAST(20) && MPT_MSVC_AT_LEAST(2022, 1) && !MPT_CLANG_BEFORE(12, 0, 0)
// Disabled for VS2022.0 because of
// <https://developercommunity.visualstudio.com/t/vs2022-cl-193030705-generates-non-universally-avai/1578571>
// / <https://github.com/microsoft/STL/issues/2330> with fix already queued
// (<https://github.com/microsoft/STL/pull/2333>).
using std::bit_ceil;
using std::bit_floor;
using std::bit_width;
using std::countl_one;
using std::countl_zero;
using std::countr_one;
using std::countr_zero;
using std::has_single_bit;
using std::popcount;
using std::rotl;
using std::rotr;
#else // !C++20
// C++20 <bit> header.
// Note that we do not use SFINAE here but instead rely on static_assert.
template <typename T>
constexpr int popcount(T val) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
int result = 0;
while (val > 0) {
if (val & 0x1) {
result++;
}
val >>= 1;
}
return result;
}
template <typename T>
constexpr bool has_single_bit(T x) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
return mpt::popcount(x) == 1;
}
template <typename T>
constexpr T bit_ceil(T x) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
T result = 1;
while (result < x) {
T newresult = result << 1;
if (newresult < result) {
return 0;
}
result = newresult;
}
return result;
}
template <typename T>
constexpr T bit_floor(T x) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
if (x == 0) {
return 0;
}
T result = 1;
do {
T newresult = result << 1;
if (newresult < result) {
return result;
}
result = newresult;
} while (result <= x);
return result >> 1;
}
template <typename T>
constexpr T bit_width(T x) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
T result = 0;
while (x > 0) {
x >>= 1;
result += 1;
}
return result;
}
template <typename T>
constexpr int countl_zero(T x) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
int count = 0;
for (int bit = std::numeric_limits<T>::digits - 1; bit >= 0; --bit) {
if ((x & (1u << bit)) == 0u) {
count++;
} else {
break;
}
}
return count;
}
template <typename T>
constexpr int countl_one(T x) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
int count = 0;
for (int bit = std::numeric_limits<T>::digits - 1; bit >= 0; --bit) {
if ((x & (1u << bit)) != 0u) {
count++;
} else {
break;
}
}
return count;
}
template <typename T>
constexpr int countr_zero(T x) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
int count = 0;
for (int bit = 0; bit < std::numeric_limits<T>::digits; ++bit) {
if ((x & (1u << bit)) == 0u) {
count++;
} else {
break;
}
}
return count;
}
template <typename T>
constexpr int countr_one(T x) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
int count = 0;
for (int bit = 0; bit < std::numeric_limits<T>::digits; ++bit) {
if ((x & (1u << bit)) != 0u) {
count++;
} else {
break;
}
}
return count;
}
template <typename T>
constexpr T rotl_impl(T x, int r) noexcept {
auto N = std::numeric_limits<T>::digits;
return (x >> (N - r)) | (x << r);
}
template <typename T>
constexpr T rotr_impl(T x, int r) noexcept {
auto N = std::numeric_limits<T>::digits;
return (x << (N - r)) | (x >> r);
}
template <typename T>
constexpr T rotl(T x, int s) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
auto N = std::numeric_limits<T>::digits;
auto r = s % N;
return (s < 0) ? mpt::rotr_impl(x, -s) : ((x >> (N - r)) | (x << r));
}
template <typename T>
constexpr T rotr(T x, int s) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
auto N = std::numeric_limits<T>::digits;
auto r = s % N;
return (s < 0) ? mpt::rotl_impl(x, -s) : ((x << (N - r)) | (x >> r));
}
#endif // C++20
template <typename T>
constexpr int lower_bound_entropy_bits(T x_) {
typename std::make_unsigned<T>::type x = static_cast<typename std::make_unsigned<T>::type>(x_);
return mpt::bit_width(x) == static_cast<typename std::make_unsigned<T>::type>(mpt::popcount(x)) ? mpt::bit_width(x) : mpt::bit_width(x) - 1;
}
template <typename T>
constexpr bool is_mask(T x) {
static_assert(std::is_integral<T>::value);
typedef typename std::make_unsigned<T>::type unsigned_T;
unsigned_T ux = static_cast<unsigned_T>(x);
unsigned_T mask = 0;
for (std::size_t bits = 0; bits <= (sizeof(unsigned_T) * 8); ++bits) {
mask = (mask << 1) | 1u;
if (ux == mask) {
return true;
}
}
return false;
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_BIT_HPP
@@ -0,0 +1,38 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_CHECK_PLATFORM_HPP
#define MPT_BASE_CHECK_PLATFORM_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/pointer.hpp"
#include <limits>
#include <cstddef>
#include <cstdint>
namespace mpt {
inline namespace MPT_INLINE_NS {
static_assert(sizeof(std::uintptr_t) == sizeof(void *));
static_assert(std::numeric_limits<unsigned char>::digits == 8);
static_assert(sizeof(char) == 1);
static_assert(sizeof(std::byte) == 1);
static_assert(alignof(std::byte) == 1);
static_assert(mpt::arch_bits == static_cast<int>(mpt::pointer_size) * 8);
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_CHECK_PLATFORM_HPP
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_COMPILETIME_WARNING_HPP
#define MPT_BASE_COMPILETIME_WARNING_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/preprocessor.hpp"
#if MPT_COMPILER_MSVC
#define MPT_WARNING(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text))
#define MPT_WARNING_STATEMENT(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text))
#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG
#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text))
#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text))
#else
// portable #pragma message or #warning replacement
#define MPT_WARNING(text) \
static inline int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME)() noexcept { \
int warning [[deprecated("Warning: " text)]] = 0; \
return warning; \
} \
/**/
#define MPT_WARNING_STATEMENT(text) \
int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) = []() { \
int warning [[deprecated("Warning: " text)]] = 0; \
return warning; \
}() /**/
#endif
#endif // MPT_BASE_COMPILETIME_WARNING_HPP
@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_CONSTEXPR_THROW_HPP
#define MPT_BASE_CONSTEXPR_THROW_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include <utility>
namespace mpt {
inline namespace MPT_INLINE_NS {
// Work-around for the requirement of at least 1 non-throwing function argument combination in C++ (17,2a).
template <typename Exception>
constexpr bool constexpr_throw_helper(Exception && e, bool really = true) {
//return !really ? really : throw std::forward<Exception>(e);
if (really) {
throw std::forward<Exception>(e);
}
// cppcheck-suppress identicalConditionAfterEarlyExit
return really;
}
template <typename Exception>
constexpr bool constexpr_throw(Exception && e) {
return mpt::constexpr_throw_helper(std::forward<Exception>(e));
}
template <typename T, typename Exception>
constexpr T constexpr_throw_helper(Exception && e, bool really = true) {
//return !really ? really : throw std::forward<Exception>(e);
if (really) {
throw std::forward<Exception>(e);
}
return T{};
}
template <typename T, typename Exception>
constexpr T constexpr_throw(Exception && e) {
return mpt::constexpr_throw_helper<T>(std::forward<Exception>(e));
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_CONSTEXPR_THROW_HPP
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_DETECT_HPP
#define MPT_BASE_DETECT_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/detect_libc.hpp"
#include "mpt/base/detect_libcxx.hpp"
#include "mpt/base/detect_os.hpp"
#include "mpt/base/detect_quirks.hpp"
#endif // MPT_BASE_DETECT_HPP
@@ -0,0 +1,190 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_DETECT_COMPILER_HPP
#define MPT_BASE_DETECT_COMPILER_HPP
#define MPT_COMPILER_MAKE_VERSION2(version, sp) ((version)*100 + (sp))
#define MPT_COMPILER_MAKE_VERSION3(major, minor, patch) ((major)*10000 + (minor)*100 + (patch))
#if defined(MPT_COMPILER_GENERIC)
#undef MPT_COMPILER_GENERIC
#define MPT_COMPILER_GENERIC 1
#elif defined(__clang__) && defined(_MSC_VER) && defined(__c2__)
#error "Clang/C2 is not supported. Please use Clang/LLVM for Windows instead."
#elif defined(__clang__)
#define MPT_COMPILER_CLANG 1
#define MPT_COMPILER_CLANG_VERSION MPT_COMPILER_MAKE_VERSION3(__clang_major__, __clang_minor__, __clang_patchlevel__)
#define MPT_CLANG_AT_LEAST(major, minor, patch) (MPT_COMPILER_CLANG_VERSION >= MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch)))
#define MPT_CLANG_BEFORE(major, minor, patch) (MPT_COMPILER_CLANG_VERSION < MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch)))
#if MPT_CLANG_BEFORE(7, 0, 0)
#error "clang version 7 required"
#endif
#if defined(__clang_analyzer__)
#ifndef MPT_BUILD_ANALYZED
#define MPT_BUILD_ANALYZED
#endif
#endif
#elif defined(__GNUC__)
#define MPT_COMPILER_GCC 1
#define MPT_COMPILER_GCC_VERSION MPT_COMPILER_MAKE_VERSION3(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#define MPT_GCC_AT_LEAST(major, minor, patch) (MPT_COMPILER_GCC_VERSION >= MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch)))
#define MPT_GCC_BEFORE(major, minor, patch) (MPT_COMPILER_GCC_VERSION < MPT_COMPILER_MAKE_VERSION3((major), (minor), (patch)))
#if MPT_GCC_BEFORE(8, 1, 0)
#error "GCC version 8.1 required"
#endif
#elif defined(_MSC_VER)
#define MPT_COMPILER_MSVC 1
#if (_MSC_VER >= 1933)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 3)
#elif (_MSC_VER >= 1932)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 2)
#elif (_MSC_VER >= 1931)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 1)
#elif (_MSC_VER >= 1930)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2022, 0)
#elif (_MSC_VER >= 1929)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 10)
#elif (_MSC_VER >= 1928)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 8)
#elif (_MSC_VER >= 1927)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 7)
#elif (_MSC_VER >= 1926)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 6)
#elif (_MSC_VER >= 1925)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 5)
#elif (_MSC_VER >= 1924)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 4)
#elif (_MSC_VER >= 1923)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 3)
#elif (_MSC_VER >= 1922)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 2)
#elif (_MSC_VER >= 1921)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 1)
#elif (_MSC_VER >= 1920)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2019, 0)
#elif (_MSC_VER >= 1916)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 9)
#elif (_MSC_VER >= 1915)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 8)
#elif (_MSC_VER >= 1914)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 7)
#elif (_MSC_VER >= 1913)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 6)
#elif (_MSC_VER >= 1912)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 5)
#elif (_MSC_VER >= 1911)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 3)
#elif (_MSC_VER >= 1910)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017, 0)
#elif (_MSC_VER >= 1900) && defined(_MSVC_LANG)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015, 3)
#elif (_MSC_VER >= 1900)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015, 0)
#elif (_MSC_VER >= 1800)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2013, 0)
#elif (_MSC_VER >= 1700)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2012, 0)
#elif (_MSC_VER >= 1600)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2010, 0)
#elif (_MSC_VER >= 1500)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2008, 0)
#else
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2005, 0)
#endif
#define MPT_MSVC_AT_LEAST(version, sp) (MPT_COMPILER_MSVC_VERSION >= MPT_COMPILER_MAKE_VERSION2((version), (sp)))
#define MPT_MSVC_BEFORE(version, sp) (MPT_COMPILER_MSVC_VERSION < MPT_COMPILER_MAKE_VERSION2((version), (sp)))
#if MPT_MSVC_BEFORE(2017, 9)
#error "MSVC version 2017 15.9 required"
#endif
#if defined(_PREFAST_)
#ifndef MPT_BUILD_ANALYZED
#define MPT_BUILD_ANALYZED
#endif
#endif
#else
#define MPT_COMPILER_GENERIC 1
#endif
#ifndef MPT_COMPILER_GENERIC
#define MPT_COMPILER_GENERIC 0
#endif
#ifndef MPT_COMPILER_CLANG
#define MPT_COMPILER_CLANG 0
#define MPT_CLANG_AT_LEAST(major, minor, patch) 0
#define MPT_CLANG_BEFORE(major, minor, patch) 0
#endif
#ifndef MPT_COMPILER_GCC
#define MPT_COMPILER_GCC 0
#define MPT_GCC_AT_LEAST(major, minor, patch) 0
#define MPT_GCC_BEFORE(major, minor, patch) 0
#endif
#ifndef MPT_COMPILER_MSVC
#define MPT_COMPILER_MSVC 0
#define MPT_MSVC_AT_LEAST(version, sp) 0
#define MPT_MSVC_BEFORE(version, sp) 0
#endif
#if MPT_COMPILER_GENERIC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG
#if (__cplusplus >= 202002)
// Support for C++20 is lacking across all compilers.
// Only assume C++17 for non-MSVC, even when in C++20 mode.
#define MPT_CXX 17
#elif (__cplusplus >= 201703)
#define MPT_CXX 17
#else
#define MPT_CXX 17
#endif
#elif MPT_COMPILER_MSVC
#if MPT_MSVC_AT_LEAST(2019, 10) && (_MSVC_LANG >= 201705)
#define MPT_CXX 20
#elif (_MSVC_LANG >= 201703)
#define MPT_CXX 17
#else
#define MPT_CXX 17
#endif
#else
#define MPT_CXX 17
#endif
// MPT_CXX is stricter than just using __cplusplus directly.
// We will only claim a language version as supported IFF all core language and
// library fatures that we need are actually supported AND working correctly
// (to our needs).
#define MPT_CXX_AT_LEAST(version) (MPT_CXX >= (version))
#define MPT_CXX_BEFORE(version) (MPT_CXX < (version))
#endif // MPT_BASE_DETECT_COMPILER_HPP
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_DETECT_LIBC_HPP
#define MPT_BASE_DETECT_LIBC_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/detect_os.hpp"
#include <cstddef>
// order of checks is important!
#if MPT_COMPILER_GENERIC
#define MPT_LIBC_GENERIC 1
#elif MPT_COMPILER_GCC && (defined(__MINGW32__) || defined(__MINGW64__))
#define MPT_LIBC_MS 1
#elif defined(__GLIBC__)
#define MPT_LIBC_GLIBC 1
#elif MPT_COMPILER_MSVC
#define MPT_LIBC_MS 1
#elif MPT_COMPILER_CLANG && MPT_OS_WINDOWS
#define MPT_LIBC_MS 1
#else
#define MPT_LIBC_GENERIC 1
#endif
#ifndef MPT_LIBC_GENERIC
#define MPT_LIBC_GENERIC 0
#endif
#ifndef MPT_LIBC_GLIBC
#define MPT_LIBC_GLIBC 0
#endif
#ifndef MPT_LIBC_MS
#define MPT_LIBC_MS 0
#endif
#endif // MPT_BASE_DETECT_LIBC_HPP
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_DETECT_LIBCXX_HPP
#define MPT_BASE_DETECT_LIBCXX_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/detect_os.hpp"
#if MPT_CXX_AT_LEAST(20)
#include <version>
#else // !C++20
#include <array>
#endif // C++20
// order of checks is important!
#if MPT_COMPILER_GENERIC
#define MPT_LIBCXX_GENERIC 1
#elif defined(_LIBCPP_VERSION)
#define MPT_LIBCXX_LLVM 1
#elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
#define MPT_LIBCXX_GNU 1
#elif MPT_COMPILER_MSVC
#define MPT_LIBCXX_MS 1
#elif MPT_COMPILER_CLANG && MPT_OS_WINDOWS
#define MPT_LIBCXX_MS 1
#else
#define MPT_LIBCXX_GENERIC 1
#endif
#ifndef MPT_LIBCXX_GENERIC
#define MPT_LIBCXX_GENERIC 0
#endif
#ifndef MPT_LIBCXX_LLVM
#define MPT_LIBCXX_LLVM 0
#endif
#ifndef MPT_LIBCXX_GNU
#define MPT_LIBCXX_GNU 0
#endif
#ifndef MPT_LIBCXX_MS
#define MPT_LIBCXX_MS 0
#endif
#endif // MPT_BASE_DETECT_LIBCXX_HPP
@@ -0,0 +1,117 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_DETECT_OS_HPP
#define MPT_BASE_DETECT_OS_HPP
// The order of the checks matters!
#if defined(__DJGPP__)
#define MPT_OS_DJGPP 1
#elif defined(__EMSCRIPTEN__)
#define MPT_OS_EMSCRIPTEN 1
#if !defined(__EMSCRIPTEN_major__) || !defined(__EMSCRIPTEN_minor__) || !defined(__EMSCRIPTEN_tiny__)
#include <emscripten/version.h>
#endif
#if defined(__EMSCRIPTEN_major__) && defined(__EMSCRIPTEN_minor__)
#if (__EMSCRIPTEN_major__ > 1)
// ok
#elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ > 39)
// ok
#elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ == 39) && (__EMSCRIPTEN_tiny__ >= 7)
// ok
#else
#error "Emscripten >= 1.39.7 is required."
#endif
#endif
#elif defined(_WIN32)
#define MPT_OS_WINDOWS 1
#if defined(WINAPI_FAMILY)
#include <winapifamily.h>
#if (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
#define MPT_OS_WINDOWS_WINRT 0
#else
#define MPT_OS_WINDOWS_WINRT 1
#endif
#else // !WINAPI_FAMILY
#define MPT_OS_WINDOWS_WINRT 0
#endif // WINAPI_FAMILY
#elif defined(__APPLE__)
#define MPT_OS_MACOSX_OR_IOS 1
#include <TargetConditionals.h>
#if defined(TARGET_OS_OSX)
#if (TARGET_OS_OSX != 0)
#include <AvailabilityMacros.h>
#endif
#endif
//#if TARGET_IPHONE_SIMULATOR
//#elif TARGET_OS_IPHONE
//#elif TARGET_OS_MAC
//#else
//#endif
#elif defined(__HAIKU__)
#define MPT_OS_HAIKU 1
#elif defined(__ANDROID__) || defined(ANDROID)
#define MPT_OS_ANDROID 1
#elif defined(__linux__)
#define MPT_OS_LINUX 1
#elif defined(__DragonFly__)
#define MPT_OS_DRAGONFLYBSD 1
#elif defined(__FreeBSD__)
#define MPT_OS_FREEBSD 1
#elif defined(__OpenBSD__)
#define MPT_OS_OPENBSD 1
#elif defined(__NetBSD__)
#define MPT_OS_NETBSD 1
#elif defined(__unix__)
#define MPT_OS_GENERIC_UNIX 1
#else
#define MPT_OS_UNKNOWN 1
#endif
#ifndef MPT_OS_DJGPP
#define MPT_OS_DJGPP 0
#endif
#ifndef MPT_OS_EMSCRIPTEN
#define MPT_OS_EMSCRIPTEN 0
#endif
#ifndef MPT_OS_WINDOWS
#define MPT_OS_WINDOWS 0
#endif
#ifndef MPT_OS_WINDOWS_WINRT
#define MPT_OS_WINDOWS_WINRT 0
#endif
#ifndef MPT_OS_MACOSX_OR_IOS
#define MPT_OS_MACOSX_OR_IOS 0
#endif
#ifndef MPT_OS_HAIKU
#define MPT_OS_HAIKU 0
#endif
#ifndef MPT_OS_ANDROID
#define MPT_OS_ANDROID 0
#endif
#ifndef MPT_OS_LINUX
#define MPT_OS_LINUX 0
#endif
#ifndef MPT_OS_DRAGONFLYBSD
#define MPT_OS_DRAGONFLYBSD 0
#endif
#ifndef MPT_OS_FREEBSD
#define MPT_OS_FREEBSD 0
#endif
#ifndef MPT_OS_OPENBSD
#define MPT_OS_OPENBSD 0
#endif
#ifndef MPT_OS_NETBSD
#define MPT_OS_NETBSD 0
#endif
#ifndef MPT_OS_GENERIC_UNIX
#define MPT_OS_GENERIC_UNIX 0
#endif
#ifndef MPT_OS_UNKNOWN
#define MPT_OS_UNKNOWN 0
#endif
#endif // MPT_BASE_DETECT_OS_HPP
@@ -0,0 +1,147 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_DETECT_QUIRKS_HPP
#define MPT_BASE_DETECT_QUIRKS_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/detect_libcxx.hpp"
#include "mpt/base/detect_os.hpp"
#if MPT_COMPILER_MSVC
// Compiler has multiplication/division semantics when shifting signed integers.
#define MPT_COMPILER_SHIFT_SIGNED 1
#endif
#ifndef MPT_COMPILER_SHIFT_SIGNED
#define MPT_COMPILER_SHIFT_SIGNED 0
#endif
// This should really be based on __STDCPP_THREADS__, but that is not defined by
// GCC or clang. Stupid.
// Just assume multithreaded and disable for platforms we know are
// singlethreaded later on.
#define MPT_PLATFORM_MULTITHREADED 1
#if MPT_OS_DJGPP
#undef MPT_PLATFORM_MULTITHREADED
#define MPT_PLATFORM_MULTITHREADED 0
#endif
#if (MPT_OS_EMSCRIPTEN && !defined(__EMSCRIPTEN_PTHREADS__))
#undef MPT_PLATFORM_MULTITHREADED
#define MPT_PLATFORM_MULTITHREADED 0
#endif
#if MPT_OS_WINDOWS && MPT_COMPILER_MSVC
#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) // _WIN32_WINNT_VISTA
#define MPT_COMPILER_QUIRK_COMPLEX_STD_MUTEX
#endif
#endif
#if MPT_OS_EMSCRIPTEN && defined(MPT_BUILD_AUDIOWORKLETPROCESSOR)
#define MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK
#endif
#if MPT_OS_EMSCRIPTEN && defined(MPT_BUILD_AUDIOWORKLETPROCESSOR)
#define MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
#endif
#if MPT_OS_DJGPP
#define MPT_COMPILER_QUIRK_NO_WCHAR
#endif
#if MPT_OS_WINDOWS && MPT_GCC_BEFORE(9, 1, 0)
// GCC C++ library has no wchar_t overloads
#define MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR
#endif
#if defined(__arm__)
#if defined(__SOFTFP__)
#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1
#else
#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0
#endif
#if defined(__VFP_FP__)
// native-endian IEEE754
#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0
#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0
#elif defined(__MAVERICK__)
// little-endian IEEE754, we assume native-endian though
#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1
#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0
#else
// not IEEE754
#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 1
#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 1
#endif
#elif defined(__mips__)
#if defined(__mips_soft_float)
#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 1
#else
#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0
#endif
#endif
#if MPT_OS_EMSCRIPTEN
#define MPT_COMPILER_QUIRK_FLOAT_PREFER64 1
#endif
#ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER32
#define MPT_COMPILER_QUIRK_FLOAT_PREFER32 0
#endif
#ifndef MPT_COMPILER_QUIRK_FLOAT_PREFER64
#define MPT_COMPILER_QUIRK_FLOAT_PREFER64 0
#endif
#ifndef MPT_COMPILER_QUIRK_FLOAT_EMULATED
#define MPT_COMPILER_QUIRK_FLOAT_EMULATED 0
#endif
#ifndef MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN
#define MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN 0
#endif
#ifndef MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754
#define MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754 0
#endif
#if MPT_OS_MACOSX_OR_IOS
#if defined(TARGET_OS_OSX)
#if TARGET_OS_OSX
#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_15)
#define MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
#endif
#endif
#endif
#endif
#if (MPT_LIBCXX_MS && (MPT_MSVC_BEFORE(2019, 4) || !MPT_COMPILER_MSVC)) || (MPT_LIBCXX_GNU && (MPT_GCC_BEFORE(11, 0, 0) || !MPT_COMPILER_GCC)) || MPT_LIBCXX_LLVM || MPT_LIBCXX_GENERIC
#define MPT_LIBCXX_QUIRK_NO_TO_CHARS_FLOAT
#endif
#endif // MPT_BASE_DETECT_QUIRKS_HPP
@@ -0,0 +1,93 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_FLOATINGPOINT_HPP
#define MPT_BASE_FLOATINGPOINT_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include <limits>
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
// fp half
// n/a
// fp single
using single = float;
namespace float_literals {
constexpr single operator"" _fs(long double lit) noexcept {
return static_cast<single>(lit);
}
} // namespace float_literals
// fp double
namespace float_literals {
constexpr double operator"" _fd(long double lit) noexcept {
return static_cast<double>(lit);
}
} // namespace float_literals
// fp extended
namespace float_literals {
constexpr long double operator"" _fe(long double lit) noexcept {
return static_cast<long double>(lit);
}
} // namespace float_literals
// fp quad
// n/a
using float32 = std::conditional<sizeof(float) == 4, float, std::conditional<sizeof(double) == 4, double, std::conditional<sizeof(long double) == 4, long double, float>::type>::type>::type;
namespace float_literals {
constexpr float32 operator"" _f32(long double lit) noexcept {
return static_cast<float32>(lit);
}
} // namespace float_literals
using float64 = std::conditional<sizeof(float) == 8, float, std::conditional<sizeof(double) == 8, double, std::conditional<sizeof(long double) == 8, long double, double>::type>::type>::type;
namespace float_literals {
constexpr float64 operator"" _f64(long double lit) noexcept {
return static_cast<float64>(lit);
}
} // namespace float_literals
template <typename T>
struct float_traits {
static constexpr bool is_float = !std::numeric_limits<T>::is_integer;
static constexpr bool is_hard = is_float && !MPT_COMPILER_QUIRK_FLOAT_EMULATED;
static constexpr bool is_soft = is_float && MPT_COMPILER_QUIRK_FLOAT_EMULATED;
static constexpr bool is_float32 = is_float && (sizeof(T) == 4);
static constexpr bool is_float64 = is_float && (sizeof(T) == 8);
static constexpr bool is_native_endian = is_float && !MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN;
static constexpr bool is_ieee754_binary = is_float && std::numeric_limits<T>::is_iec559 && !MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754;
static constexpr bool is_ieee754_binary32 = is_float && is_ieee754_binary && is_float32;
static constexpr bool is_ieee754_binary64 = is_float && is_ieee754_binary && is_float64;
static constexpr bool is_ieee754_binary32ne = is_float && is_ieee754_binary && is_float32 && is_native_endian;
static constexpr bool is_ieee754_binary64ne = is_float && is_ieee754_binary && is_float64 && is_native_endian;
static constexpr bool is_preferred = is_float && ((is_float32 && MPT_COMPILER_QUIRK_FLOAT_PREFER32) || (is_float64 && MPT_COMPILER_QUIRK_FLOAT_PREFER64));
};
// prefer smaller floats, but try to use IEEE754 floats
using nativefloat =
std::conditional<mpt::float_traits<float32>::is_preferred, float32, std::conditional<mpt::float_traits<float64>::is_preferred, float64, std::conditional<std::numeric_limits<float>::is_iec559, float, std::conditional<std::numeric_limits<double>::is_iec559, double, std::conditional<std::numeric_limits<long double>::is_iec559, long double, float>::type>::type>::type>::type>::type;
namespace float_literals {
constexpr nativefloat operator"" _nf(long double lit) noexcept {
return static_cast<nativefloat>(lit);
}
} // namespace float_literals
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_FLOATINGPOINT_HPP
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_INTEGER_HPP
#define MPT_BASE_INTEGER_HPP
#include "mpt/base/namespace.hpp"
#include <cstdint>
namespace mpt {
inline namespace MPT_INLINE_NS {
using int8 = std::int8_t;
using int16 = std::int16_t;
using int32 = std::int32_t;
using int64 = std::int64_t;
using uint8 = std::uint8_t;
using uint16 = std::uint16_t;
using uint32 = std::uint32_t;
using uint64 = std::uint64_t;
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_INTEGER_HPP
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_MACROS_HPP
#define MPT_BASE_MACROS_HPP
#include "mpt/base/detect.hpp"
#include <type_traits>
#if MPT_COMPILER_MSVC && MPT_OS_WINDOWS
#include <windows.h>
#endif // MPT_COMPILER_MSVC && MPT_OS_WINDOWS
// Advanced inline attributes
#if MPT_COMPILER_MSVC
#define MPT_FORCEINLINE __forceinline
#define MPT_NOINLINE __declspec(noinline)
#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG
#define MPT_FORCEINLINE __attribute__((always_inline)) inline
#define MPT_NOINLINE __attribute__((noinline))
#else
#define MPT_FORCEINLINE inline
#define MPT_NOINLINE
#endif
// constexpr
#define MPT_CONSTEXPRINLINE constexpr MPT_FORCEINLINE
#if MPT_CXX_AT_LEAST(20)
#define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE
#define MPT_CONSTEXPR20_VAR constexpr
#else // !C++20
#define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE
#define MPT_CONSTEXPR20_VAR const
#endif // C++20
#define MPT_FORCE_CONSTEXPR(expr) [&]() { \
constexpr auto x = (expr); \
return x; \
}()
#if MPT_CXX_AT_LEAST(20)
#define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated()
#define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated()
#else // !C++20
#define MPT_IS_CONSTANT_EVALUATED20() false
// this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code
#define MPT_IS_CONSTANT_EVALUATED() true
#endif // C++20
#if MPT_COMPILER_MSVC
#define MPT_MAYBE_CONSTANT_IF(x) \
__pragma(warning(push)) \
__pragma(warning(disable : 4127)) \
if (x) \
__pragma(warning(pop)) \
/**/
#endif
#if MPT_COMPILER_GCC
#define MPT_MAYBE_CONSTANT_IF(x) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \
if (x) \
_Pragma("GCC diagnostic pop") \
/**/
#endif
#if MPT_COMPILER_CLANG
#define MPT_MAYBE_CONSTANT_IF(x) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \
_Pragma("clang diagnostic ignored \"-Wtype-limits\"") \
_Pragma("clang diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") \
if (x) \
_Pragma("clang diagnostic pop") \
/**/
#endif
#if !defined(MPT_MAYBE_CONSTANT_IF)
// MPT_MAYBE_CONSTANT_IF disables compiler warnings for conditions that may in some case be either always false or always true (this may turn out to be useful in ASSERTions in some cases).
#define MPT_MAYBE_CONSTANT_IF(x) if (x)
#endif
#if MPT_COMPILER_MSVC && MPT_OS_WINDOWS
#define MPT_UNUSED(x) UNREFERENCED_PARAMETER(x)
#else
#define MPT_UNUSED(x) static_cast<void>(x)
#endif
#define MPT_DISCARD(expr) static_cast<void>(expr)
// Use MPT_RESTRICT to indicate that a pointer is guaranteed to not be aliased.
#if MPT_COMPILER_MSVC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG
#define MPT_RESTRICT __restrict
#else
#define MPT_RESTRICT
#endif
#endif // MPT_BASE_MACROS_HPP
@@ -0,0 +1,88 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_EMPTY_HPP
#define MPT_BASE_EMPTY_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include <algorithm>
#include <type_traits>
#include <cmath>
namespace mpt {
inline namespace MPT_INLINE_NS {
#if MPT_OS_DJGPP
inline long double log2(const long double val) {
return static_cast<long double>(::log2(static_cast<double>(val)));
}
inline double log2(const double val) {
return ::log2(val);
}
inline float log2(const float val) {
return ::log2f(val);
}
#else // !MPT_OS_DJGPP
// C++11 std::log2
using std::log2;
#endif // MPT_OS_DJGPP
#if MPT_OS_DJGPP
inline long double round(const long double val) {
return ::roundl(val);
}
inline double round(const double val) {
return ::round(val);
}
inline float round(const float val) {
return ::roundf(val);
}
#else // !MPT_OS_DJGPP
// C++11 std::round
using std::round;
#endif // MPT_OS_DJGPP
template <typename T>
inline T sanitize_nan(T val) {
static_assert(std::is_floating_point<T>::value);
if (std::isnan(val)) {
return T(0.0);
}
return val;
}
template <typename T>
inline T safe_clamp(T v, T lo, T hi) {
static_assert(std::is_floating_point<T>::value);
return std::clamp(mpt::sanitize_nan(v), lo, hi);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_EMPTY_HPP
@@ -0,0 +1,273 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_MEMORY_HPP
#define MPT_BASE_MEMORY_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/base/span.hpp"
#include <type_traits>
#include <cstddef>
#include <cstring>
namespace mpt {
inline namespace MPT_INLINE_NS {
using byte_span = mpt::span<std::byte>;
using const_byte_span = mpt::span<const std::byte>;
// Tell which types are safe for mpt::byte_cast.
// signed char is actually not allowed to alias into an object representation,
// which means that, if the actual type is not itself signed char but char or
// unsigned char instead, dereferencing the signed char pointer is undefined
// behaviour.
template <typename T>
struct is_byte_castable : public std::false_type { };
template <>
struct is_byte_castable<char> : public std::true_type { };
template <>
struct is_byte_castable<unsigned char> : public std::true_type { };
template <>
struct is_byte_castable<std::byte> : public std::true_type { };
template <>
struct is_byte_castable<const char> : public std::true_type { };
template <>
struct is_byte_castable<const unsigned char> : public std::true_type { };
template <>
struct is_byte_castable<const std::byte> : public std::true_type { };
template <typename T>
struct is_byte : public std::false_type { };
template <>
struct is_byte<std::byte> : public std::true_type { };
template <>
struct is_byte<const std::byte> : public std::true_type { };
template <typename T>
constexpr bool declare_binary_safe(const T &) noexcept {
return false;
}
constexpr bool declare_binary_safe(const char &) noexcept {
return true;
}
constexpr bool declare_binary_safe(const uint8 &) noexcept {
return true;
}
constexpr bool declare_binary_safe(const int8 &) noexcept {
return true;
}
constexpr bool declare_binary_safe(const std::byte &) noexcept {
return true;
}
// Tell which types are safe to binary write into files.
// By default, no types are safe.
// When a safe type gets defined,
// also specialize this template so that IO functions will work.
template <typename T>
struct is_binary_safe : public std::conditional<declare_binary_safe(T{}), std::true_type, std::false_type>::type { };
// Generic Specialization for arrays.
template <typename T, std::size_t N>
struct is_binary_safe<T[N]> : public is_binary_safe<T> { };
template <typename T, std::size_t N>
struct is_binary_safe<const T[N]> : public is_binary_safe<T> { };
template <typename T, std::size_t N>
struct is_binary_safe<std::array<T, N>> : public is_binary_safe<T> { };
template <typename T, std::size_t N>
struct is_binary_safe<const std::array<T, N>> : public is_binary_safe<T> { };
template <typename T>
constexpr bool check_binary_size(std::size_t size) noexcept {
return true
&& (sizeof(T) == size)
&& (alignof(T) == 1)
&& std::is_standard_layout<T>::value
&& std::has_unique_object_representations<T>::value
&& mpt::is_binary_safe<T>::value;
}
template <typename Tdst, typename Tsrc>
struct byte_cast_impl {
inline Tdst operator()(Tsrc src) const noexcept {
static_assert(sizeof(Tsrc) == sizeof(std::byte));
static_assert(sizeof(Tdst) == sizeof(std::byte));
// not checking is_byte_castable here because we are actually
// doing a static_cast and converting the value
static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
return static_cast<Tdst>(src);
}
};
template <typename Tdst, typename Tsrc>
struct byte_cast_impl<mpt::span<Tdst>, mpt::span<Tsrc>> {
inline mpt::span<Tdst> operator()(mpt::span<Tsrc> src) const noexcept {
static_assert(sizeof(Tsrc) == sizeof(std::byte));
static_assert(sizeof(Tdst) == sizeof(std::byte));
static_assert(mpt::is_byte_castable<Tsrc>::value);
static_assert(mpt::is_byte_castable<Tdst>::value);
static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
return mpt::as_span(mpt::byte_cast_impl<Tdst *, Tsrc *>()(src.data()), mpt::byte_cast_impl<Tdst *, Tsrc *>()(src.data() + src.size()));
}
};
template <typename Tdst, typename Tsrc>
struct byte_cast_impl<Tdst *, Tsrc *> {
inline Tdst * operator()(Tsrc * src) const noexcept {
static_assert(sizeof(Tsrc) == sizeof(std::byte));
static_assert(sizeof(Tdst) == sizeof(std::byte));
static_assert(mpt::is_byte_castable<Tsrc>::value);
static_assert(mpt::is_byte_castable<Tdst>::value);
static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
return reinterpret_cast<Tdst *>(src);
}
};
template <typename Tdst, typename Tsrc>
struct void_cast_impl;
template <typename Tdst>
struct void_cast_impl<Tdst *, void *> {
inline Tdst * operator()(void * src) const noexcept {
static_assert(sizeof(Tdst) == sizeof(std::byte));
static_assert(mpt::is_byte_castable<Tdst>::value);
static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
return reinterpret_cast<Tdst *>(src);
}
};
template <typename Tdst>
struct void_cast_impl<Tdst *, const void *> {
inline Tdst * operator()(const void * src) const noexcept {
static_assert(sizeof(Tdst) == sizeof(std::byte));
static_assert(mpt::is_byte_castable<Tdst>::value);
static_assert(std::is_integral<Tdst>::value || mpt::is_byte<Tdst>::value);
return reinterpret_cast<Tdst *>(src);
}
};
template <typename Tsrc>
struct void_cast_impl<void *, Tsrc *> {
inline void * operator()(Tsrc * src) const noexcept {
static_assert(sizeof(Tsrc) == sizeof(std::byte));
static_assert(mpt::is_byte_castable<Tsrc>::value);
static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
return reinterpret_cast<void *>(src);
}
};
template <typename Tsrc>
struct void_cast_impl<const void *, Tsrc *> {
inline const void * operator()(Tsrc * src) const noexcept {
static_assert(sizeof(Tsrc) == sizeof(std::byte));
static_assert(mpt::is_byte_castable<Tsrc>::value);
static_assert(std::is_integral<Tsrc>::value || mpt::is_byte<Tsrc>::value);
return reinterpret_cast<const void *>(src);
}
};
// casts between different byte (char) types or pointers to these types
template <typename Tdst, typename Tsrc>
inline Tdst byte_cast(Tsrc src) noexcept {
return byte_cast_impl<Tdst, Tsrc>()(src);
}
// casts between pointers to void and pointers to byte
template <typename Tdst, typename Tsrc>
inline Tdst void_cast(Tsrc src) noexcept {
return void_cast_impl<Tdst, Tsrc>()(src);
}
template <typename T>
MPT_CONSTEXPRINLINE std::byte as_byte(T src) noexcept {
static_assert(std::is_integral<T>::value);
return static_cast<std::byte>(static_cast<uint8>(src));
}
template <typename T>
struct as_raw_memory_impl {
inline mpt::const_byte_span operator()(const T & v) const {
static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::as_span(reinterpret_cast<const std::byte *>(&v), sizeof(T));
}
inline mpt::byte_span operator()(T & v) const {
static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::as_span(reinterpret_cast<std::byte *>(&v), sizeof(T));
}
};
template <typename T, std::size_t N>
struct as_raw_memory_impl<T[N]> {
inline mpt::const_byte_span operator()(const T (&v)[N]) const {
static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::as_span(reinterpret_cast<const std::byte *>(v), N * sizeof(T));
}
inline mpt::byte_span operator()(T (&v)[N]) const {
static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::as_span(reinterpret_cast<std::byte *>(v), N * sizeof(T));
}
};
template <typename T, std::size_t N>
struct as_raw_memory_impl<const T[N]> {
inline mpt::const_byte_span operator()(const T (&v)[N]) const {
static_assert(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::as_span(reinterpret_cast<const std::byte *>(v), N * sizeof(T));
}
};
// In order to be able to partially specialize it,
// as_raw_memory is implemented via a class template.
// Do not overload or specialize as_raw_memory directly.
// Using a wrapper (by default just around a cast to const std::byte *),
// allows for implementing raw memory access
// via on-demand generating a cached serialized representation.
template <typename T>
inline mpt::const_byte_span as_raw_memory(const T & v) {
return mpt::as_raw_memory_impl<T>()(v);
}
template <typename T>
inline mpt::byte_span as_raw_memory(T & v) {
return mpt::as_raw_memory_impl<T>()(v);
}
template <class T>
inline void memclear(T & x) {
static_assert(std::is_standard_layout<T>::value);
static_assert((std::is_trivially_default_constructible<T>::value && std::is_trivially_copyable<T>::value) || mpt::is_binary_safe<T>::value);
std::memset(&x, 0, sizeof(T));
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_MEMORY_HPP
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_NAMESPACE_HPP
#define MPT_BASE_NAMESPACE_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/version.hpp"
#include "mpt/base/compiletime_warning.hpp"
#if !defined(MPT_INLINE_NS)
#define MPT_BUILD_VERSION_NAMESPACE_IMPL(a, b, c, d) v##a##_##b##_##c##_##d
#define MPT_BUILD_VERSION_NAMESPACE(a, b, c, d) MPT_BUILD_VERSION_NAMESPACE_IMPL(a, b, c, d)
#define MPT_VERSION_NAMESPACE MPT_BUILD_VERSION_NAMESPACE(MPT_VERSION_MAJOR, MPT_VERSION_MINOR, MPT_VERSION_PATCH, MPT_VERSION_BUILD)
#if MPT_OS_WINDOWS
#ifdef UNICODE
#define MPT_VERSION_ABI_OS u
#else
#define MPT_VERSION_ABI_OS 8
#endif
#else
#define MPT_VERSION_ABI_OS _
#endif
#if MPT_LIBC_GENERIC
#define MPT_VERSION_ABI_LIBC _
#elif MPT_LIBC_MS
#ifdef _DLL
#ifdef _DEBUG
#define MPT_VERSION_ABI_LIBC MDd
#else
#define MPT_VERSION_ABI_LIBC MDr
#endif
#else
#ifdef _DEBUG
#define MPT_VERSION_ABI_LIBC MTd
#else
#define MPT_VERSION_ABI_LIBC MTr
#endif
#endif
#elif MPT_LIBC_GLIBC
#define MPT_VERSION_ABI_LIBC G
#else
#define MPT_VERSION_ABI_LIBC _
#endif
#define MPT_BUILD_ABI_NAMESPACE_IMPL(a, b) ABI_##a##_##b
#define MPT_BUILD_ABI_NAMESPACE(a, b) MPT_BUILD_ABI_NAMESPACE_IMPL(a, b)
#define MPT_ABI_NAMESPACE MPT_BUILD_ABI_NAMESPACE(MPT_VERSION_ABI_OS, MPT_VERSION_ABI_LIBC)
#if !defined(MPT_PROJECT_NAMESPACE)
MPT_WARNING("Please #define MPT_PROJECT_NAMESPACE or #define MPT_INLINE_NS in build configuration.")
#define MPT_PROJECT_NAMESPACE x
#endif // !MPT_PROJECT_NAMESPACE
#define MPT_BUILD_INLINE_NS_IMPL(a, b, c) a##_##b##_##c
#define MPT_BUILD_INLINE_NS(a, b, c) MPT_BUILD_INLINE_NS_IMPL(a, b, c)
#define MPT_INLINE_NS MPT_BUILD_INLINE_NS(MPT_VERSION_NAMESPACE, MPT_ABI_NAMESPACE, MPT_PROJECT_NAMESPACE)
#endif // !MPT_INLINE_NS
namespace mpt {
inline namespace MPT_INLINE_NS {
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_NAMESPACE_HPP
@@ -0,0 +1,177 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_NUMBRES_HPP
#define MPT_BASE_NUMBRES_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/namespace.hpp"
#if MPT_CXX_AT_LEAST(20)
#include <numbers>
#else
#include <type_traits>
#include <cmath>
#include <math.h>
#endif
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace numbers {
#if MPT_CXX_AT_LEAST(20)
template <typename T>
inline constexpr T e_v = std::numbers::e_v<T>;
template <typename T>
inline constexpr T log2e_v = std::numbers::log2e_v<T>;
template <typename T>
inline constexpr T log10e_v = std::numbers::log10e_v<T>;
template <typename T>
inline constexpr T pi_v = std::numbers::pi_v<T>;
template <typename T>
inline constexpr T inv_pi_v = std::numbers::inv_pi_v<T>;
template <typename T>
inline constexpr T inv_sqrtpi_v = std::numbers::inv_sqrtpi_v<T>;
template <typename T>
inline constexpr T ln2_v = std::numbers::ln2_v<T>;
template <typename T>
inline constexpr T ln10_v = std::numbers::ln10_v<T>;
template <typename T>
inline constexpr T sqrt2_v = std::numbers::sqrt2_v<T>;
template <typename T>
inline constexpr T sqrt3_v = std::numbers::sqrt3_v<T>;
template <typename T>
inline constexpr T inv_sqrt3_v = std::numbers::inv_sqrt3_v<T>;
template <typename T>
inline constexpr T egamma_v = std::numbers::egamma_v<T>;
template <typename T>
inline constexpr T phi_v = std::numbers::phi_v<T>;
inline constexpr double e = e_v<double>;
inline constexpr double log2e = log2e_v<double>;
inline constexpr double log10e = log10e_v<double>;
inline constexpr double pi = pi_v<double>;
inline constexpr double inv_pi = inv_pi_v<double>;
inline constexpr double inv_sqrtpi = inv_sqrtpi_v<double>;
inline constexpr double ln2 = ln2_v<double>;
inline constexpr double ln10 = ln10_v<double>;
inline constexpr double sqrt2 = sqrt2_v<double>;
inline constexpr double sqrt3 = sqrt3_v<double>;
inline constexpr double inv_sqrt3 = inv_sqrt3_v<double>;
inline constexpr double egamma = egamma_v<double>;
inline constexpr double phi = phi_v<double>;
#else
#ifdef M_E
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T e_v = static_cast<T>(M_E);
#else
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T e_v = static_cast<T>(2.71828182845904523536);
#endif
#ifdef M_LOG2E
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T log2e_v = static_cast<T>(M_LOG2E);
#else
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T log2e_v = static_cast<T>(1.44269504088896340736);
#endif
#ifdef M_LOG10E
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T log10e_v = static_cast<T>(M_LOG10E);
#else
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T log10e_v = static_cast<T>(0.434294481903251827651);
#endif
#ifdef M_PI
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T pi_v = static_cast<T>(M_PI);
#else
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T pi_v = static_cast<T>(3.14159265358979323846);
#endif
#ifdef M_1_PI
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T inv_pi_v = static_cast<T>(M_1_PI);
#else
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T inv_pi_v = static_cast<T>(0.318309886183790671538);
#endif
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T inv_sqrtpi_v = static_cast<T>(0.564189583547756286948079451560772586);
#ifdef M_LN2
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T ln2_v = static_cast<T>(M_LN2);
#else
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T ln2_v = static_cast<T>(0.693147180559945309417);
#endif
#ifdef M_LN10
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T ln10_v = static_cast<T>(M_LN10);
#else
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T ln10_v = static_cast<T>(2.30258509299404568402);
#endif
#ifdef M_SQRT2
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T sqrt2_v = static_cast<T>(M_SQRT2);
#else
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T sqrt2_v = static_cast<T>(1.41421356237309504880);
#endif
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T sqrt3_v = static_cast<T>(1.732050807568877293527446341505872367);
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T inv_sqrt3_v = static_cast<T>(0.577350269189625764509148780501957456);
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T egamma_v = static_cast<T>(0.577215664901532860606512090082402431);
template <typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = true>
inline constexpr T phi_v = static_cast<T>(1.618033988749894848204586834365638118);
inline constexpr double e = e_v<double>;
inline constexpr double log2e = log2e_v<double>;
inline constexpr double log10e = log10e_v<double>;
inline constexpr double pi = pi_v<double>;
inline constexpr double inv_pi = inv_pi_v<double>;
inline constexpr double inv_sqrtpi = inv_sqrtpi_v<double>;
inline constexpr double ln2 = ln2_v<double>;
inline constexpr double ln10 = ln10_v<double>;
inline constexpr double sqrt2 = sqrt2_v<double>;
inline constexpr double sqrt3 = sqrt3_v<double>;
inline constexpr double inv_sqrt3 = inv_sqrt3_v<double>;
inline constexpr double egamma = egamma_v<double>;
inline constexpr double phi = phi_v<double>;
#endif
} // namespace numbers
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_NUMBRES_HPP
@@ -0,0 +1,95 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_NUMERIC_HPP
#define MPT_BASE_NUMERIC_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/bit.hpp"
#include "mpt/base/saturate_cast.hpp"
#include <algorithm>
#include <limits>
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename Tmod, Tmod m>
struct ModIfNotZeroImpl {
template <typename Tval>
constexpr Tval mod(Tval x) {
static_assert(std::numeric_limits<Tmod>::is_integer);
static_assert(!std::numeric_limits<Tmod>::is_signed);
static_assert(std::numeric_limits<Tval>::is_integer);
static_assert(!std::numeric_limits<Tval>::is_signed);
return static_cast<Tval>(x % m);
}
};
template <>
struct ModIfNotZeroImpl<uint8, 0> {
template <typename Tval>
constexpr Tval mod(Tval x) {
return x;
}
};
template <>
struct ModIfNotZeroImpl<uint16, 0> {
template <typename Tval>
constexpr Tval mod(Tval x) {
return x;
}
};
template <>
struct ModIfNotZeroImpl<uint32, 0> {
template <typename Tval>
constexpr Tval mod(Tval x) {
return x;
}
};
template <>
struct ModIfNotZeroImpl<uint64, 0> {
template <typename Tval>
constexpr Tval mod(Tval x) {
return x;
}
};
// Returns x % m if m != 0, x otherwise.
// i.e. "return (m == 0) ? x : (x % m);", but without causing a warning with stupid older compilers
template <typename Tmod, Tmod m, typename Tval>
constexpr Tval modulo_if_not_zero(Tval x) {
return ModIfNotZeroImpl<Tmod, m>().mod(x);
}
// rounds x up to multiples of target
template <typename T>
constexpr T align_up(T x, T target) {
return ((x + (target - 1)) / target) * target;
}
// rounds x down to multiples of target
template <typename T>
constexpr T align_down(T x, T target) {
return (x / target) * target;
}
// Returns sign of a number (-1 for negative numbers, 1 for positive numbers, 0 for 0)
template <class T>
constexpr int signum(T value) {
return (value > T(0)) - (value < T(0));
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_ALGORITHM_HPP
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_POINTER_HPP
#define MPT_BASE_POINTER_HPP
#include "mpt/base/namespace.hpp"
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
inline constexpr int arch_bits = sizeof(void *) * 8;
inline constexpr std::size_t pointer_size = sizeof(void *);
template <typename Tdst, typename Tsrc>
struct pointer_cast_helper {
static constexpr Tdst cast(const Tsrc & src) noexcept {
return src;
}
};
template <typename Tdst, typename Tptr>
struct pointer_cast_helper<Tdst, const Tptr *> {
static constexpr Tdst cast(const Tptr * const & src) noexcept {
return reinterpret_cast<const Tdst>(src);
}
};
template <typename Tdst, typename Tptr>
struct pointer_cast_helper<Tdst, Tptr *> {
static constexpr Tdst cast(const Tptr * const & src) noexcept {
return reinterpret_cast<const Tdst>(src);
}
};
template <typename Tdst, typename Tsrc>
constexpr Tdst pointer_cast(const Tsrc & src) noexcept {
return pointer_cast_helper<Tdst, Tsrc>::cast(src);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_POINTER_HPP
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_PREPROCESSOR_HPP
#define MPT_BASE_PREPROCESSOR_HPP
#define MPT_PP_DEFER(m, ...) m(__VA_ARGS__)
#define MPT_PP_STRINGIFY(x) #x
#define MPT_PP_JOIN_HELPER(a, b) a##b
#define MPT_PP_JOIN(a, b) MPT_PP_JOIN_HELPER(a, b)
#define MPT_PP_UNIQUE_IDENTIFIER(prefix) MPT_PP_JOIN(prefix, __LINE__)
#endif // MPT_BASE_PREPROCESSOR_HPP
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_SATURATE_CAST_HPP
#define MPT_BASE_SATURATE_CAST_HPP
#include "mpt/base/namespace.hpp"
#include <limits>
namespace mpt {
inline namespace MPT_INLINE_NS {
// Saturate the value of src to the domain of Tdst
template <typename Tdst, typename Tsrc>
constexpr Tdst saturate_cast(Tsrc src) noexcept {
// This code tries not only to obviously avoid overflows but also to avoid signed/unsigned comparison warnings and type truncation warnings (which in fact would be safe here) by explicit casting.
static_assert(std::numeric_limits<Tdst>::is_integer);
static_assert(std::numeric_limits<Tsrc>::is_integer);
if constexpr (std::numeric_limits<Tdst>::is_signed && std::numeric_limits<Tsrc>::is_signed) {
if constexpr (sizeof(Tdst) >= sizeof(Tsrc)) {
return static_cast<Tdst>(src);
} else {
return static_cast<Tdst>(std::max(static_cast<Tsrc>(std::numeric_limits<Tdst>::min()), std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max()))));
}
} else if constexpr (!std::numeric_limits<Tdst>::is_signed && !std::numeric_limits<Tsrc>::is_signed) {
if constexpr (sizeof(Tdst) >= sizeof(Tsrc)) {
return static_cast<Tdst>(src);
} else {
return static_cast<Tdst>(std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max())));
}
} else if constexpr (std::numeric_limits<Tdst>::is_signed && !std::numeric_limits<Tsrc>::is_signed) {
if constexpr (sizeof(Tdst) > sizeof(Tsrc)) {
return static_cast<Tdst>(src);
} else if constexpr (sizeof(Tdst) == sizeof(Tsrc)) {
return static_cast<Tdst>(std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max())));
} else {
return static_cast<Tdst>(std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max())));
}
} else { // Tdst unsigned, Tsrc signed
if constexpr (sizeof(Tdst) >= sizeof(Tsrc)) {
return static_cast<Tdst>(std::max(static_cast<Tsrc>(0), src));
} else {
return static_cast<Tdst>(std::max(static_cast<Tsrc>(0), std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max()))));
}
}
}
template <typename Tdst>
constexpr Tdst saturate_cast(double src) {
if (src >= static_cast<double>(std::numeric_limits<Tdst>::max())) {
return std::numeric_limits<Tdst>::max();
}
if (src <= static_cast<double>(std::numeric_limits<Tdst>::min())) {
return std::numeric_limits<Tdst>::min();
}
return static_cast<Tdst>(src);
}
template <typename Tdst>
constexpr Tdst saturate_cast(float src) {
if (src >= static_cast<float>(std::numeric_limits<Tdst>::max())) {
return std::numeric_limits<Tdst>::max();
}
if (src <= static_cast<float>(std::numeric_limits<Tdst>::min())) {
return std::numeric_limits<Tdst>::min();
}
return static_cast<Tdst>(src);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_SATURATE_CAST_HPP
@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_SATURATE_ROUND_HPP
#define MPT_BASE_SATURATE_ROUND_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/base/math.hpp"
#include "mpt/base/saturate_cast.hpp"
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
// Rounds given double value to nearest integer value of type T.
// Out-of-range values are saturated to the specified integer type's limits.
template <typename T>
inline T saturate_round(float val) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::saturate_cast<T>(mpt::round(val));
}
template <typename T>
inline T saturate_round(double val) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::saturate_cast<T>(mpt::round(val));
}
template <typename T>
inline T saturate_round(long double val) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::saturate_cast<T>(mpt::round(val));
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_SATURATE_ROUND_HPP
@@ -0,0 +1,198 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_SECURE_HPP
#define MPT_BASE_SECURE_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/base/namespace.hpp"
#include <atomic>
#include <utility>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace secure {
inline MPT_NOINLINE void memzero(std::byte * const dst, std::size_t const len) noexcept {
std::atomic_thread_fence(std::memory_order_seq_cst);
volatile std::byte * volatile p = static_cast<volatile std::byte *>(dst);
std::atomic_thread_fence(std::memory_order_seq_cst);
for (volatile std::size_t i = 0; i < len; ++i) {
p[i] = std::byte{0};
}
std::atomic_thread_fence(std::memory_order_seq_cst);
}
inline MPT_NOINLINE void memzero(void * const dst, std::size_t const len) noexcept {
std::atomic_thread_fence(std::memory_order_seq_cst);
volatile std::byte * volatile p = static_cast<volatile std::byte *>(dst);
std::atomic_thread_fence(std::memory_order_seq_cst);
for (volatile std::size_t i = 0; i < len; ++i) {
p[i] = std::byte{0};
}
std::atomic_thread_fence(std::memory_order_seq_cst);
}
inline MPT_NOINLINE void memzero(char * const dst, std::size_t const len) noexcept {
std::atomic_thread_fence(std::memory_order_seq_cst);
volatile std::byte * volatile p = reinterpret_cast<volatile std::byte *>(dst);
std::atomic_thread_fence(std::memory_order_seq_cst);
for (volatile std::size_t i = 0; i < len; ++i) {
p[i] = std::byte{0};
}
std::atomic_thread_fence(std::memory_order_seq_cst);
}
inline MPT_NOINLINE void memzero(uint8 * const dst, std::size_t const len) noexcept {
std::atomic_thread_fence(std::memory_order_seq_cst);
volatile std::byte * volatile p = reinterpret_cast<volatile std::byte *>(dst);
std::atomic_thread_fence(std::memory_order_seq_cst);
for (volatile std::size_t i = 0; i < len; ++i) {
p[i] = std::byte{0};
}
std::atomic_thread_fence(std::memory_order_seq_cst);
}
template <typename T>
inline MPT_NOINLINE void clear(T & val) {
std::atomic_signal_fence(std::memory_order_seq_cst);
volatile T * volatile v = &val;
std::atomic_thread_fence(std::memory_order_seq_cst);
*v = T{};
std::atomic_signal_fence(std::memory_order_seq_cst);
}
class byte {
private:
std::byte value;
public:
byte() noexcept
: value(std::byte{0}) {
return;
}
explicit byte(std::byte value) noexcept
: value(value) {
return;
}
byte(const byte & other) noexcept
: value(other.value) {
return;
}
byte(byte && other) noexcept
: value(std::move(other.value)) {
mpt::secure::clear(other.value);
}
byte & operator=(const byte & other) noexcept {
if (&other == this) {
return *this;
}
value = other.value;
return *this;
}
byte & operator==(byte && other) noexcept {
if (&other == this) {
return *this;
}
value = std::move(other.value);
mpt::secure::clear(other.value);
return *this;
}
explicit operator std::byte() const noexcept {
return value;
}
~byte() {
mpt::secure::clear(value);
}
};
class buffer {
private:
std::vector<std::byte> m_data;
public:
buffer()
: m_data(0) {
return;
}
explicit buffer(const std::vector<std::byte> & data)
: m_data(data) {
return;
}
explicit buffer(const std::byte * beg, const std::byte * end)
: m_data(beg, end) {
return;
}
buffer(const buffer & other)
: m_data(other.m_data) {
return;
}
buffer(buffer && other) noexcept
: m_data(std::move(other.m_data)) {
mpt::secure::memzero(other.m_data.data(), other.m_data.size());
}
buffer & operator=(const buffer & other) {
if (&other == this) {
return *this;
}
m_data = other.m_data;
return *this;
}
buffer & operator=(buffer && other) noexcept {
if (&other == this) {
return *this;
}
m_data = std::move(other.m_data);
mpt::secure::memzero(other.m_data.data(), other.m_data.size());
return *this;
}
~buffer() {
mpt::secure::memzero(m_data.data(), m_data.size());
m_data.resize(0);
m_data.shrink_to_fit();
}
explicit operator std::vector<std::byte>() const {
return m_data;
}
const std::byte * data() const {
return m_data.data();
}
std::byte * data() {
return m_data.data();
}
std::size_t size() const {
return m_data.size();
}
};
} // namespace secure
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_SECURE_HPP
@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_SEMANTIC_VERSION_HPP
#define MPT_BASE_SEMANTIC_VERSION_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/version.hpp"
#include <tuple>
namespace mpt {
inline namespace MPT_INLINE_NS {
struct semantic_version {
unsigned long long major = 0;
unsigned long long minor = 0;
unsigned long long patch = 0;
constexpr std::tuple<unsigned long long, unsigned long long, unsigned long long> as_tuple() const noexcept {
return std::make_tuple(major, minor, patch);
}
};
constexpr bool operator==(const semantic_version a, const semantic_version b) noexcept {
return a.as_tuple() == b.as_tuple();
}
constexpr bool operator!=(const semantic_version a, const semantic_version b) noexcept {
return a.as_tuple() != b.as_tuple();
}
constexpr bool operator<(const semantic_version a, const semantic_version b) noexcept {
return a.as_tuple() < b.as_tuple();
}
constexpr bool operator>(const semantic_version a, const semantic_version b) noexcept {
return a.as_tuple() > b.as_tuple();
}
constexpr bool operator<=(const semantic_version a, const semantic_version b) noexcept {
return a.as_tuple() <= b.as_tuple();
}
constexpr bool operator>=(const semantic_version a, const semantic_version b) noexcept {
return a.as_tuple() >= b.as_tuple();
}
struct version_info {
semantic_version semver{};
unsigned long long build = 0;
constexpr std::tuple<std::tuple<unsigned long long, unsigned long long, unsigned long long>, unsigned long long> as_tuple() const noexcept {
return std::make_tuple(semver.as_tuple(), build);
}
template <typename Tostream>
friend Tostream & operator<<(Tostream & os, const version_info vi) {
if (vi.build > 0) {
os << vi.semver.major << "." << vi.semver.minor << "." << vi.semver.patch << "+build." << vi.build;
} else {
os << vi.semver.major << "." << vi.semver.minor << "." << vi.semver.patch;
}
return os;
}
};
constexpr bool operator==(const version_info a, const version_info b) noexcept {
return a.as_tuple() == b.as_tuple();
}
constexpr bool operator!=(const version_info a, const version_info b) noexcept {
return a.as_tuple() != b.as_tuple();
}
constexpr bool operator<(const version_info a, const version_info b) noexcept {
return a.as_tuple() < b.as_tuple();
}
constexpr bool operator>(const version_info a, const version_info b) noexcept {
return a.as_tuple() > b.as_tuple();
}
constexpr bool operator<=(const version_info a, const version_info b) noexcept {
return a.as_tuple() <= b.as_tuple();
}
constexpr bool operator>=(const version_info a, const version_info b) noexcept {
return a.as_tuple() >= b.as_tuple();
}
constexpr inline version_info Version = {
{MPT_VERSION_MAJOR, MPT_VERSION_MINOR, MPT_VERSION_PATCH},
MPT_VERSION_BUILD
};
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_SEMANTIC_VERSION_HPP
@@ -0,0 +1,115 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_SOURCE_LOCATION_HPP
#define MPT_BASE_SOURCE_LOCATION_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#if MPT_CXX_AT_LEAST(20) && !MPT_MSVC_BEFORE(2022, 0) && !MPT_COMPILER_CLANG
#include <source_location>
#endif // C++20
namespace mpt {
inline namespace MPT_INLINE_NS {
#if MPT_CXX_AT_LEAST(20) && !MPT_MSVC_BEFORE(2022, 0) && !MPT_COMPILER_CLANG
using std::source_location;
#define MPT_SOURCE_LOCATION_CURRENT() std::source_location::current()
#else // !C++20
#if MPT_COMPILER_MSVC && MPT_MSVC_AT_LEAST(2019, 6)
#define MPT_SOURCE_LOCATION_FILE __builtin_FILE()
#define MPT_SOURCE_LOCATION_FUNCTION __builtin_FUNCTION()
#define MPT_SOURCE_LOCATION_LINE __builtin_LINE()
#define MPT_SOURCE_LOCATION_COLUMN __builtin_COLUMN()
#elif MPT_COMPILER_GCC
#define MPT_SOURCE_LOCATION_FILE __builtin_FILE()
#define MPT_SOURCE_LOCATION_FUNCTION __builtin_FUNCTION()
#define MPT_SOURCE_LOCATION_LINE __builtin_LINE()
#define MPT_SOURCE_LOCATION_COLUMN 0
#elif MPT_COMPILER_CLANG && MPT_CLANG_AT_LEAST(9, 0, 0)
#define MPT_SOURCE_LOCATION_FILE __builtin_FILE()
#define MPT_SOURCE_LOCATION_FUNCTION __builtin_FUNCTION()
#define MPT_SOURCE_LOCATION_LINE __builtin_LINE()
#define MPT_SOURCE_LOCATION_COLUMN __builtin_COLUMN()
#else
#define MPT_SOURCE_LOCATION_FILE __FILE__
#define MPT_SOURCE_LOCATION_FUNCTION ""
#define MPT_SOURCE_LOCATION_LINE __LINE__
#define MPT_SOURCE_LOCATION_COLUMN 0
#endif
// compatible with C++20 std::source_location
struct source_location {
private:
const char * m_file_name;
const char * m_function_name;
uint32 m_line;
uint32 m_column;
public:
constexpr source_location() noexcept
: m_file_name("")
, m_function_name("")
, m_line(0)
, m_column(0) {
}
constexpr source_location(const char * file, const char * function, uint32 line, uint32 column) noexcept
: m_file_name(file)
, m_function_name(function)
, m_line(line)
, m_column(column) {
}
source_location(const source_location &) = default;
source_location(source_location &&) = default;
static constexpr source_location current(const char * file = MPT_SOURCE_LOCATION_FILE, const char * function = MPT_SOURCE_LOCATION_FUNCTION, uint32 line = MPT_SOURCE_LOCATION_LINE, uint32 column = MPT_SOURCE_LOCATION_COLUMN) noexcept {
return source_location(file, function, line, column);
}
constexpr uint32 line() const noexcept {
return m_line;
}
constexpr uint32 column() const noexcept {
return m_column;
}
constexpr const char * file_name() const noexcept {
return m_file_name;
}
constexpr const char * function_name() const noexcept {
return m_function_name;
}
};
#if (MPT_COMPILER_MSVC && MPT_MSVC_AT_LEAST(2019, 6)) || MPT_COMPILER_GCC || (MPT_COMPILER_CLANG && MPT_CLANG_AT_LEAST(9, 0, 0))
#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current()
#else
#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current(__FILE__, __func__, __LINE__, 0)
#endif
#endif // C++20
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_SOURCE_LOCATION_HPP
@@ -0,0 +1,196 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_SPAN_HPP
#define MPT_BASE_SPAN_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include <array>
#if MPT_CXX_AT_LEAST(20)
#include <span>
#else // !C++20
#include <iterator>
#include <limits>
#include <type_traits>
#endif // C++20
#if MPT_CXX_BEFORE(20)
#include <cstddef>
#endif // !C++20
namespace mpt {
inline namespace MPT_INLINE_NS {
#if MPT_CXX_AT_LEAST(20)
using std::dynamic_extent;
using std::span;
#else // !C++20
// Simplified version of gsl::span.
// Non-owning read-only or read-write view into a contiguous block of T
// objects, i.e. equivalent to a (beg,end) or (data,size) tuple.
// Can eventually be replaced without further modifications with a full C++20
// std::span.
inline constexpr std::size_t dynamic_extent = std::numeric_limits<std::size_t>::max();
template <typename T>
class span {
public:
using element_type = T;
using value_type = typename std::remove_cv<T>::type;
using index_type = std::size_t;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using iterator = pointer;
using difference_type = typename std::iterator_traits<iterator>::difference_type;
private:
T * m_data;
std::size_t m_size;
public:
span() noexcept
: m_data(nullptr)
, m_size(0) {
}
span(pointer beg, pointer end)
: m_data(beg)
, m_size(end - beg) {
}
span(pointer data, index_type size)
: m_data(data)
, m_size(size) {
}
template <std::size_t N>
span(element_type (&arr)[N])
: m_data(arr)
, m_size(N) {
}
template <std::size_t N>
span(std::array<value_type, N> & arr)
: m_data(arr.data())
, m_size(arr.size()) {
}
template <std::size_t N>
span(const std::array<value_type, N> & arr)
: m_data(arr.data())
, m_size(arr.size()) {
}
span(const span & other) noexcept = default;
template <typename U>
span(const span<U> & other)
: m_data(other.data())
, m_size(other.size()) {
}
span & operator=(const span & other) noexcept = default;
iterator begin() const {
return iterator(m_data);
}
iterator end() const {
return iterator(m_data + m_size);
}
reference operator[](index_type index) {
return m_data[index];
}
const_reference operator[](index_type index) const {
return m_data[index];
}
bool operator==(const span & other) const noexcept {
return size() == other.size() && (m_data == other.m_data || std::equal(begin(), end(), other.begin()));
}
bool operator!=(const span & other) const noexcept {
return !(*this == other);
}
pointer data() const noexcept {
return m_data;
}
bool empty() const noexcept {
return size() == 0;
}
index_type size() const noexcept {
return m_size;
}
index_type length() const noexcept {
return size();
}
span subspan(std::size_t offset, std::size_t count = mpt::dynamic_extent) const {
return span(data() + offset, (count == mpt::dynamic_extent) ? (size() - offset) : count);
}
span first(std::size_t count) const {
return span(data(), count);
}
span last(std::size_t count) const {
return span(data() + (size() - count), count);
}
}; // class span
#endif // C++20
template <typename T>
inline span<T> as_span(T * beg, T * end) {
return span<T>(beg, end);
}
template <typename T>
inline span<T> as_span(T * data, std::size_t size) {
return span<T>(data, size);
}
template <typename T, std::size_t N>
inline span<T> as_span(T (&arr)[N]) {
return span<T>(std::begin(arr), std::end(arr));
}
template <typename T, std::size_t N>
inline span<T> as_span(std::array<T, N> & cont) {
return span<T>(cont);
}
template <typename T, std::size_t N>
inline span<const T> as_span(const std::array<T, N> & cont) {
return span<const T>(cont);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_SPAN_HPP
@@ -0,0 +1,328 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_TESTS_ARITHMETIC_SHIFT_HPP
#define MPT_BASE_TESTS_ARITHMETIC_SHIFT_HPP
#include "mpt/base/arithmetic_shift.hpp"
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <limits>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace base {
namespace arithmetic_shift {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/base/arithmetic_shift")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 1), mpt::rshift_signed_standard<int16>(-32768, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 1), mpt::rshift_signed_standard<int16>(-32767, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 1), mpt::rshift_signed_standard<int16>(-32766, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 1), mpt::rshift_signed_standard<int16>(-2, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 1), mpt::rshift_signed_standard<int16>(-1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 1), mpt::rshift_signed_standard<int16>(0, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 1), mpt::rshift_signed_standard<int16>(1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 1), mpt::rshift_signed_standard<int16>(2, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 1), mpt::rshift_signed_standard<int16>(32766, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 1), mpt::rshift_signed_standard<int16>(32767, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 14), mpt::rshift_signed_standard<int16>(-32768, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 14), mpt::rshift_signed_standard<int16>(-32767, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 14), mpt::rshift_signed_standard<int16>(-32766, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 14), mpt::rshift_signed_standard<int16>(-2, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 14), mpt::rshift_signed_standard<int16>(-1, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 14), mpt::rshift_signed_standard<int16>(0, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 14), mpt::rshift_signed_standard<int16>(1, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 14), mpt::rshift_signed_standard<int16>(2, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 14), mpt::rshift_signed_standard<int16>(32766, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 14), mpt::rshift_signed_standard<int16>(32767, 14));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 15), mpt::rshift_signed_standard<int16>(-32768, 15));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 15), mpt::rshift_signed_standard<int16>(-32767, 15));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 15), mpt::rshift_signed_standard<int16>(-32766, 15));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 15), mpt::rshift_signed_standard<int16>(-2, 15));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 15), mpt::rshift_signed_standard<int16>(-1, 15));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 15), mpt::rshift_signed_standard<int16>(0, 15));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 15), mpt::rshift_signed_standard<int16>(1, 15));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 15), mpt::rshift_signed_standard<int16>(2, 15));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 15), mpt::rshift_signed_standard<int16>(32766, 15));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 15), mpt::rshift_signed_standard<int16>(32767, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 1), mpt::lshift_signed_standard<int16>(-32768, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 1), mpt::lshift_signed_standard<int16>(-32767, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 1), mpt::lshift_signed_standard<int16>(-32766, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 1), mpt::lshift_signed_standard<int16>(-2, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 1), mpt::lshift_signed_standard<int16>(-1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 1), mpt::lshift_signed_standard<int16>(0, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 1), mpt::lshift_signed_standard<int16>(1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 1), mpt::lshift_signed_standard<int16>(2, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 1), mpt::lshift_signed_standard<int16>(32766, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 1), mpt::lshift_signed_standard<int16>(32767, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 14), mpt::lshift_signed_standard<int16>(-32768, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 14), mpt::lshift_signed_standard<int16>(-32767, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 14), mpt::lshift_signed_standard<int16>(-32766, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 14), mpt::lshift_signed_standard<int16>(-2, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 14), mpt::lshift_signed_standard<int16>(-1, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 14), mpt::lshift_signed_standard<int16>(0, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 14), mpt::lshift_signed_standard<int16>(1, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 14), mpt::lshift_signed_standard<int16>(2, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 14), mpt::lshift_signed_standard<int16>(32766, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 14), mpt::lshift_signed_standard<int16>(32767, 14));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 15), mpt::lshift_signed_standard<int16>(-32768, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 15), mpt::lshift_signed_standard<int16>(-32767, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 15), mpt::lshift_signed_standard<int16>(-32766, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 15), mpt::lshift_signed_standard<int16>(-2, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 15), mpt::lshift_signed_standard<int16>(-1, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 15), mpt::lshift_signed_standard<int16>(0, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 15), mpt::lshift_signed_standard<int16>(1, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 15), mpt::lshift_signed_standard<int16>(2, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 15), mpt::lshift_signed_standard<int16>(32766, 15));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 15), mpt::lshift_signed_standard<int16>(32767, 15));
#if MPT_COMPILER_SHIFT_SIGNED
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 1), (-32768) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 1), (-32767) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 1), (-32766) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 1), (-2) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 1), (-1) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 1), (0) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 1), (1) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 1), (2) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 1), (32766) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 1), (32767) >> 1);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 14), (-32768) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 14), (-32767) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 14), (-32766) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 14), (-2) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 14), (-1) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 14), (0) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 14), (1) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 14), (2) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 14), (32766) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 14), (32767) >> 14);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32768, 15), (-32768) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32767, 15), (-32767) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-32766, 15), (-32766) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-2, 15), (-2) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(-1, 15), (-1) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(0, 15), (0) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(1, 15), (1) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(2, 15), (2) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32766, 15), (32766) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int16>(32767, 15), (32767) >> 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 1), (-32768) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 1), (-32767) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 1), (-32766) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 1), (-2) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 1), (-1) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 1), (0) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 1), (1) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 1), (2) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 1), (32766) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 1), (32767) << 1);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 14), (-32768) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 14), (-32767) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 14), (-32766) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 14), (-2) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 14), (-1) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 14), (0) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 14), (1) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 14), (2) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 14), (32766) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 14), (32767) << 14);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32768, 15), (-32768) << 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32767, 15), (-32767) << 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-32766, 15), (-32766) << 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-2, 15), (-2) << 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(-1, 15), (-1) << 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(0, 15), (0) << 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(1, 15), (1) << 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(2, 15), (2) << 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32766, 15), (32766) << 15);
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int16>(32767, 15), (32767) << 15);
#endif
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0 - 0x80000000, 1), mpt::rshift_signed_standard<int32>(0 - 0x80000000, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7fffffff, 1), mpt::rshift_signed_standard<int32>(-0x7fffffff, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7ffffffe, 1), mpt::rshift_signed_standard<int32>(-0x7ffffffe, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-1, 1), mpt::rshift_signed_standard<int32>(-1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0, 1), mpt::rshift_signed_standard<int32>(0, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(1, 1), mpt::rshift_signed_standard<int32>(1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7ffffffe, 1), mpt::rshift_signed_standard<int32>(0x7ffffffe, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7fffffff, 1), mpt::rshift_signed_standard<int32>(0x7fffffff, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0 - 0x80000000, 31), mpt::rshift_signed_standard<int32>(0 - 0x80000000, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7fffffff, 31), mpt::rshift_signed_standard<int32>(-0x7fffffff, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7ffffffe, 31), mpt::rshift_signed_standard<int32>(-0x7ffffffe, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-1, 31), mpt::rshift_signed_standard<int32>(-1, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0, 31), mpt::rshift_signed_standard<int32>(0, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(1, 31), mpt::rshift_signed_standard<int32>(1, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7ffffffe, 31), mpt::rshift_signed_standard<int32>(0x7ffffffe, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7fffffff, 31), mpt::rshift_signed_standard<int32>(0x7fffffff, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0 - 0x80000000, 1), mpt::lshift_signed_standard<int32>(0 - 0x80000000, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7fffffff, 1), mpt::lshift_signed_standard<int32>(-0x7fffffff, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7ffffffe, 1), mpt::lshift_signed_standard<int32>(-0x7ffffffe, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-1, 1), mpt::lshift_signed_standard<int32>(-1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0, 1), mpt::lshift_signed_standard<int32>(0, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(1, 1), mpt::lshift_signed_standard<int32>(1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7ffffffe, 1), mpt::lshift_signed_standard<int32>(0x7ffffffe, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7fffffff, 1), mpt::lshift_signed_standard<int32>(0x7fffffff, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0 - 0x80000000, 31), mpt::lshift_signed_standard<int32>(0 - 0x80000000, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7fffffff, 31), mpt::lshift_signed_standard<int32>(-0x7fffffff, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7ffffffe, 31), mpt::lshift_signed_standard<int32>(-0x7ffffffe, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-1, 31), mpt::lshift_signed_standard<int32>(-1, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0, 31), mpt::lshift_signed_standard<int32>(0, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(1, 31), mpt::lshift_signed_standard<int32>(1, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7ffffffe, 31), mpt::lshift_signed_standard<int32>(0x7ffffffe, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7fffffff, 31), mpt::lshift_signed_standard<int32>(0x7fffffff, 31));
#if MPT_COMPILER_SHIFT_SIGNED
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0 - 0x80000000, 1), mpt::rshift_signed_undefined<int32>(0 - 0x80000000, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7fffffff, 1), mpt::rshift_signed_undefined<int32>(-0x7fffffff, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7ffffffe, 1), mpt::rshift_signed_undefined<int32>(-0x7ffffffe, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-1, 1), mpt::rshift_signed_undefined<int32>(-1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0, 1), mpt::rshift_signed_undefined<int32>(0, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(1, 1), mpt::rshift_signed_undefined<int32>(1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7ffffffe, 1), mpt::rshift_signed_undefined<int32>(0x7ffffffe, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7fffffff, 1), mpt::rshift_signed_undefined<int32>(0x7fffffff, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0 - 0x80000000, 31), mpt::rshift_signed_undefined<int32>(0 - 0x80000000, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7fffffff, 31), mpt::rshift_signed_undefined<int32>(-0x7fffffff, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-0x7ffffffe, 31), mpt::rshift_signed_undefined<int32>(-0x7ffffffe, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(-1, 31), mpt::rshift_signed_undefined<int32>(-1, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0, 31), mpt::rshift_signed_undefined<int32>(0, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(1, 31), mpt::rshift_signed_undefined<int32>(1, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7ffffffe, 31), mpt::rshift_signed_undefined<int32>(0x7ffffffe, 31));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int32>(0x7fffffff, 31), mpt::rshift_signed_undefined<int32>(0x7fffffff, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0 - 0x80000000, 1), mpt::lshift_signed_undefined<int32>(0 - 0x80000000, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7fffffff, 1), mpt::lshift_signed_undefined<int32>(-0x7fffffff, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7ffffffe, 1), mpt::lshift_signed_undefined<int32>(-0x7ffffffe, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-1, 1), mpt::lshift_signed_undefined<int32>(-1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0, 1), mpt::lshift_signed_undefined<int32>(0, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(1, 1), mpt::lshift_signed_undefined<int32>(1, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7ffffffe, 1), mpt::lshift_signed_undefined<int32>(0x7ffffffe, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7fffffff, 1), mpt::lshift_signed_undefined<int32>(0x7fffffff, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0 - 0x80000000, 31), mpt::lshift_signed_undefined<int32>(0 - 0x80000000, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7fffffff, 31), mpt::lshift_signed_undefined<int32>(-0x7fffffff, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-0x7ffffffe, 31), mpt::lshift_signed_undefined<int32>(-0x7ffffffe, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(-1, 31), mpt::lshift_signed_undefined<int32>(-1, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0, 31), mpt::lshift_signed_undefined<int32>(0, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(1, 31), mpt::lshift_signed_undefined<int32>(1, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7ffffffe, 31), mpt::lshift_signed_undefined<int32>(0x7ffffffe, 31));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int32>(0x7fffffff, 31), mpt::lshift_signed_undefined<int32>(0x7fffffff, 31));
#endif
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ull - 0x8000000000000000ull, 1), mpt::rshift_signed_standard<int64>(0ull - 0x8000000000000000ull, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7fffffffffffffffll, 1), mpt::rshift_signed_standard<int64>(-0x7fffffffffffffffll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7ffffffffffffffell, 1), mpt::rshift_signed_standard<int64>(-0x7ffffffffffffffell, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-1ll, 1), mpt::rshift_signed_standard<int64>(-1ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ll, 1), mpt::rshift_signed_standard<int64>(0ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(1ll, 1), mpt::rshift_signed_standard<int64>(1ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7ffffffffffffffell, 1), mpt::rshift_signed_standard<int64>(0x7ffffffffffffffell, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7fffffffffffffffll, 1), mpt::rshift_signed_standard<int64>(0x7fffffffffffffffll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ull - 0x8000000000000000ull, 63), mpt::rshift_signed_standard<int64>(0ull - 0x8000000000000000ull, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7fffffffffffffffll, 63), mpt::rshift_signed_standard<int64>(-0x7fffffffffffffffll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7ffffffffffffffell, 63), mpt::rshift_signed_standard<int64>(-0x7ffffffffffffffell, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-1ll, 63), mpt::rshift_signed_standard<int64>(-1ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ll, 63), mpt::rshift_signed_standard<int64>(0ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(1ll, 63), mpt::rshift_signed_standard<int64>(1ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7ffffffffffffffell, 63), mpt::rshift_signed_standard<int64>(0x7ffffffffffffffell, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7fffffffffffffffll, 63), mpt::rshift_signed_standard<int64>(0x7fffffffffffffffll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ull - 0x8000000000000000ull, 1), mpt::lshift_signed_standard<int64>(0ull - 0x8000000000000000ull, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7fffffffffffffffll, 1), mpt::lshift_signed_standard<int64>(-0x7fffffffffffffffll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7ffffffffffffffell, 1), mpt::lshift_signed_standard<int64>(-0x7ffffffffffffffell, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-1ll, 1), mpt::lshift_signed_standard<int64>(-1ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ll, 1), mpt::lshift_signed_standard<int64>(0ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(1ll, 1), mpt::lshift_signed_standard<int64>(1ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7ffffffffffffffell, 1), mpt::lshift_signed_standard<int64>(0x7ffffffffffffffell, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7fffffffffffffffll, 1), mpt::lshift_signed_standard<int64>(0x7fffffffffffffffll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ull - 0x8000000000000000ull, 63), mpt::lshift_signed_standard<int64>(0ull - 0x8000000000000000ull, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7fffffffffffffffll, 63), mpt::lshift_signed_standard<int64>(-0x7fffffffffffffffll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7ffffffffffffffell, 63), mpt::lshift_signed_standard<int64>(-0x7ffffffffffffffell, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-1ll, 63), mpt::lshift_signed_standard<int64>(-1ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ll, 63), mpt::lshift_signed_standard<int64>(0ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(1ll, 63), mpt::lshift_signed_standard<int64>(1ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7ffffffffffffffell, 63), mpt::lshift_signed_standard<int64>(0x7ffffffffffffffell, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7fffffffffffffffll, 63), mpt::lshift_signed_standard<int64>(0x7fffffffffffffffll, 63));
#if MPT_COMPILER_SHIFT_SIGNED
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ull - 0x8000000000000000ull, 1), mpt::rshift_signed_undefined<int64>(0ull - 0x8000000000000000ull, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined<int64>(-0x7fffffffffffffffll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined<int64>(-0x7ffffffffffffffell, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-1ll, 1), mpt::rshift_signed_undefined<int64>(-1ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ll, 1), mpt::rshift_signed_undefined<int64>(0ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(1ll, 1), mpt::rshift_signed_undefined<int64>(1ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7ffffffffffffffell, 1), mpt::rshift_signed_undefined<int64>(0x7ffffffffffffffell, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7fffffffffffffffll, 1), mpt::rshift_signed_undefined<int64>(0x7fffffffffffffffll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ull - 0x8000000000000000ull, 63), mpt::rshift_signed_undefined<int64>(0ull - 0x8000000000000000ull, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined<int64>(-0x7fffffffffffffffll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined<int64>(-0x7ffffffffffffffell, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(-1ll, 63), mpt::rshift_signed_undefined<int64>(-1ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0ll, 63), mpt::rshift_signed_undefined<int64>(0ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(1ll, 63), mpt::rshift_signed_undefined<int64>(1ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7ffffffffffffffell, 63), mpt::rshift_signed_undefined<int64>(0x7ffffffffffffffell, 63));
MPT_TEST_EXPECT_EQUAL(mpt::rshift_signed<int64>(0x7fffffffffffffffll, 63), mpt::rshift_signed_undefined<int64>(0x7fffffffffffffffll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ull - 0x8000000000000000ull, 1), mpt::lshift_signed_undefined<int64>(0ull - 0x8000000000000000ull, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined<int64>(-0x7fffffffffffffffll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined<int64>(-0x7ffffffffffffffell, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-1ll, 1), mpt::lshift_signed_undefined<int64>(-1ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ll, 1), mpt::lshift_signed_undefined<int64>(0ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(1ll, 1), mpt::lshift_signed_undefined<int64>(1ll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7ffffffffffffffell, 1), mpt::lshift_signed_undefined<int64>(0x7ffffffffffffffell, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7fffffffffffffffll, 1), mpt::lshift_signed_undefined<int64>(0x7fffffffffffffffll, 1));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ull - 0x8000000000000000ull, 63), mpt::lshift_signed_undefined<int64>(0ull - 0x8000000000000000ull, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7fffffffffffffffll, 63), mpt::lshift_signed_undefined<int64>(-0x7fffffffffffffffll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-0x7ffffffffffffffell, 63), mpt::lshift_signed_undefined<int64>(-0x7ffffffffffffffell, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(-1ll, 63), mpt::lshift_signed_undefined<int64>(-1ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0ll, 63), mpt::lshift_signed_undefined<int64>(0ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(1ll, 63), mpt::lshift_signed_undefined<int64>(1ll, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7ffffffffffffffell, 63), mpt::lshift_signed_undefined<int64>(0x7ffffffffffffffell, 63));
MPT_TEST_EXPECT_EQUAL(mpt::lshift_signed<int64>(0x7fffffffffffffffll, 63), mpt::lshift_signed_undefined<int64>(0x7fffffffffffffffll, 63));
#endif
}
} // namespace arithmetic_shift
} // namespace base
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_TESTS_ARITHMETIC_SHIFT_HPP
@@ -0,0 +1,227 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_TESTS_BASE_BIT_HPP
#define MPT_BASE_TESTS_BASE_BIT_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace base {
namespace bit {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/base/bit")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
#if MPT_CXX_BEFORE(20)
MPT_TEST_EXPECT_EQUAL(mpt::get_endian(), mpt::endian_probe());
#endif
MPT_MAYBE_CONSTANT_IF (mpt::endian_is_little()) {
MPT_TEST_EXPECT_EQUAL(mpt::get_endian(), mpt::endian::little);
MPT_MAYBE_CONSTANT_IF ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) {
MPT_TEST_EXPECT_EQUAL(mpt::endian::native, mpt::endian::little);
}
#if MPT_CXX_BEFORE(20)
MPT_TEST_EXPECT_EQUAL(mpt::endian_probe(), mpt::endian::little);
#endif
}
MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
MPT_TEST_EXPECT_EQUAL(mpt::get_endian(), mpt::endian::big);
MPT_MAYBE_CONSTANT_IF ((mpt::endian::native == mpt::endian::little) || (mpt::endian::native == mpt::endian::big)) {
MPT_TEST_EXPECT_EQUAL(mpt::endian::native, mpt::endian::big);
}
#if MPT_CXX_BEFORE(20)
MPT_TEST_EXPECT_EQUAL(mpt::endian_probe(), mpt::endian::big);
#endif
}
MPT_TEST_EXPECT_EQUAL(mpt::popcount(static_cast<uint32>(int32(-1))), 32);
MPT_TEST_EXPECT_EQUAL(mpt::popcount(0u), 0);
MPT_TEST_EXPECT_EQUAL(mpt::popcount(1u), 1);
MPT_TEST_EXPECT_EQUAL(mpt::popcount(2u), 1);
MPT_TEST_EXPECT_EQUAL(mpt::popcount(3u), 2);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(0u), false);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(1u), true);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(2u), true);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(3u), false);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(4u), true);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(5u), false);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(6u), false);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(7u), false);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(8u), true);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(9u), false);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0x7fffffffu)), false);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0x80000000u)), true);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0x80000001u)), false);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0xfffffffeu)), false);
MPT_TEST_EXPECT_EQUAL(mpt::has_single_bit(uint32(0xffffffffu)), false);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(0u), 1u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(1u), 1u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(2u), 2u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(3u), 4u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(4u), 4u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(5u), 8u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(6u), 8u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(7u), 8u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(8u), 8u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(9u), 16u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0x7fffffffu)), 0x80000000u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0x80000000u)), 0x80000000u);
//MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0x80000001u)), 0u);
//MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0xfffffffeu)), 0u);
//MPT_TEST_EXPECT_EQUAL(mpt::bit_ceil(uint32(0xffffffffu)), 0u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(0u), 0u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(1u), 1u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(2u), 2u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(3u), 2u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(4u), 4u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(5u), 4u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(6u), 4u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(7u), 4u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(8u), 8u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(9u), 8u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0x7fffffffu)), 0x40000000u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0x80000000u)), 0x80000000u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0x80000001u)), 0x80000000u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0xfffffffeu)), 0x80000000u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_floor(uint32(0xffffffffu)), 0x80000000u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(0u), 0u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(1u), 1u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(2u), 2u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(3u), 2u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(4u), 3u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(5u), 3u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(6u), 3u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(7u), 3u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(8u), 4u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(9u), 4u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0x7fffffffu)), 31u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0x80000000u)), 32u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0x80000001u)), 32u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0xfffffffeu)), 32u);
MPT_TEST_EXPECT_EQUAL(mpt::bit_width(uint32(0xffffffffu)), 32u);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000001)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000011)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00001111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00011111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00111111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b01111111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111111)), 8);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111110)), 7);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111100)), 6);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11111000)), 5);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11110000)), 4);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11100000)), 3);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b11000000)), 2);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b10000000)), 1);
MPT_TEST_EXPECT_EQUAL(mpt::countl_one(uint8(0b00000000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000000)), 8);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000001)), 7);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000011)), 6);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000111)), 5);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00001111)), 4);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00011111)), 3);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00111111)), 2);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b01111111)), 1);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111110)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111100)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11111000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11110000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11100000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b11000000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b10000000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countl_zero(uint8(0b00000000)), 8);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000001)), 1);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000011)), 2);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000111)), 3);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00001111)), 4);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00011111)), 5);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00111111)), 6);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b01111111)), 7);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111111)), 8);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111110)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111100)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11111000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11110000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11100000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b11000000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b10000000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_one(uint8(0b00000000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000000)), 8);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000001)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000011)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00001111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00011111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00111111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b01111111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111111)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111110)), 1);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111100)), 2);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11111000)), 3);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11110000)), 4);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11100000)), 5);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b11000000)), 6);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b10000000)), 7);
MPT_TEST_EXPECT_EQUAL(mpt::countr_zero(uint8(0b00000000)), 8);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0xffffffffu), 32);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0xfffffffeu), 31);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x80000000u), 31);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x7fffffffu), 31);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x7ffffffeu), 30);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000007u), 3);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000006u), 2);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000005u), 2);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000004u), 2);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000003u), 2);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000002u), 1);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000001u), 1);
MPT_TEST_EXPECT_EQUAL(mpt::lower_bound_entropy_bits(0x00000000u), 0);
}
} // namespace bit
} // namespace base
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_TESTS_BASE_BIT_HPP
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_TESTS_MATH_HPP
#define MPT_BASE_TESTS_MATH_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/math.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace base {
namespace math {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/base/math")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
MPT_TEST_EXPECT_EQUAL(mpt::round(1.99), 2.0);
MPT_TEST_EXPECT_EQUAL(mpt::round(1.5), 2.0);
MPT_TEST_EXPECT_EQUAL(mpt::round(1.1), 1.0);
MPT_TEST_EXPECT_EQUAL(mpt::round(-0.1), 0.0);
MPT_TEST_EXPECT_EQUAL(mpt::round(-0.5), -1.0);
MPT_TEST_EXPECT_EQUAL(mpt::round(-0.9), -1.0);
MPT_TEST_EXPECT_EQUAL(mpt::round(-1.4), -1.0);
MPT_TEST_EXPECT_EQUAL(mpt::round(-1.7), -2.0);
}
} // namespace math
} // namespace base
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_TESTS_MATH_HPP
@@ -0,0 +1,112 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_TESTS_SATURATE_CAST_HPP
#define MPT_BASE_TESTS_SATURATE_CAST_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/saturate_cast.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <limits>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace base {
namespace saturate_cast {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/base/saturate_cast")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
// trivials
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(-1), -1);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(0), 0);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(1), 1);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(std::numeric_limits<int>::min()), std::numeric_limits<int>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int>(std::numeric_limits<int>::max()), std::numeric_limits<int>::max());
// signed / unsigned
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(std::numeric_limits<uint16>::min()), std::numeric_limits<uint16>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(std::numeric_limits<uint16>::max()), std::numeric_limits<int16>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<uint32>::min()), (int32)std::numeric_limits<uint32>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<uint32>::max()), std::numeric_limits<int32>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int64>(std::numeric_limits<uint64>::min()), (int64)std::numeric_limits<uint64>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int64>(std::numeric_limits<uint64>::max()), std::numeric_limits<int64>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(std::numeric_limits<int16>::min()), std::numeric_limits<uint16>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(std::numeric_limits<int16>::max()), std::numeric_limits<int16>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int32>::min()), std::numeric_limits<uint32>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int32>::max()), (uint32)std::numeric_limits<int32>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint64>(std::numeric_limits<int64>::min()), std::numeric_limits<uint64>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint64>(std::numeric_limits<int64>::max()), (uint64)std::numeric_limits<int64>::max());
// overflow
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(std::numeric_limits<int16>::min() - 1), std::numeric_limits<int16>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(std::numeric_limits<int16>::max() + 1), std::numeric_limits<int16>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<int32>::min() - int64(1)), std::numeric_limits<int32>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<int32>::max() + int64(1)), std::numeric_limits<int32>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(std::numeric_limits<int16>::min() - 1), std::numeric_limits<uint16>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(std::numeric_limits<int16>::max() + 1), (uint16)std::numeric_limits<int16>::max() + 1);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int32>::min() - int64(1)), std::numeric_limits<uint32>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int32>::max() + int64(1)), (uint32)std::numeric_limits<int32>::max() + 1);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int8>(int16(32000)), 127);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int8>(int16(-32000)), -128);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int8>(uint16(32000)), 127);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int8>(uint16(64000)), 127);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint8>(int16(32000)), 255);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint8>(int16(-32000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint8>(uint16(32000)), 255);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint8>(uint16(64000)), 255);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(int16(32000)), 32000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(int16(-32000)), -32000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(uint16(32000)), 32000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int16>(uint16(64000)), 32767);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(int16(32000)), 32000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(int16(-32000)), 0);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(uint16(32000)), 32000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint16>(uint16(64000)), 64000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(int16(32000)), 32000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(int16(-32000)), -32000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(uint16(32000)), 32000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(uint16(64000)), 64000);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(int16(32000)), 32000u);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(int16(-32000)), 0u);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(uint16(32000)), 32000u);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(uint16(64000)), 64000u);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(std::numeric_limits<int64>::max() - 1), std::numeric_limits<uint32>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<int32>(std::numeric_limits<uint64>::max() - 1), std::numeric_limits<int32>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_cast<uint32>(static_cast<double>(std::numeric_limits<int64>::max())), std::numeric_limits<uint32>::max());
}
} // namespace saturate_cast
} // namespace base
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_TESTS_SATURATE_CAST_HPP
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_TESTS_SATURATE_ROUND_HPP
#define MPT_BASE_TESTS_SATURATE_ROUND_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/saturate_round.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <limits>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace base {
namespace saturate_round {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/base/saturate_round")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int32>(std::numeric_limits<int32>::max() + 0.1), std::numeric_limits<int32>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int32>(std::numeric_limits<int32>::max() - 0.4), std::numeric_limits<int32>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int32>(std::numeric_limits<int32>::min() + 0.1), std::numeric_limits<int32>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int32>(std::numeric_limits<int32>::min() - 0.1), std::numeric_limits<int32>::min());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<uint32>(std::numeric_limits<uint32>::max() + 0.499), std::numeric_limits<uint32>::max());
MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int8>(110.1), 110);
MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int8>(-110.1), -110);
// These should fail to compile
//mpt::saturate_round<std::string>(1.0);
//mpt::saturate_round<int64>(1.0);
//mpt::saturate_round<uint64>(1.0);
// This should trigger assert in Round.
//MPT_TEST_EXPECT_EQUAL(mpt::saturate_round<int8>(-129), 0);
}
} // namespace saturate_round
} // namespace base
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_TESTS_SATURATE_ROUND_HPP
@@ -0,0 +1,178 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_TESTS_WRAPPING_DIVIDE_HPP
#define MPT_BASE_TESTS_WRAPPING_DIVIDE_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/wrapping_divide.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <limits>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace base {
namespace wrapping_divide {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/base/wrapping_divide")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-25, 12), 11);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-24, 12), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-23, 12), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-8, 7), 6);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-7, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-6, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-5, 7), 2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-4, 7), 3);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-3, 7), 4);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-2, 7), 5);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(-1, 7), 6);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(0, 12), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(0, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(1, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(2, 7), 2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(3, 7), 3);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(4, 7), 4);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(5, 7), 5);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(6, 7), 6);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(7, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(8, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(23, 12), 11);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(24, 12), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(25, 12), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(uint32(0x7fffffff), uint32(0x80000000)), uint32(0x7fffffff));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0x7ffffffe), int32(0x7fffffff)), int32(0x7ffffffe));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(1)), int32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(2)), int32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(1)), int32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(2)), int32(1));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(1)), int32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(2)), int32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(0x7fffffff)), int32(0x7ffffffe));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(0x7fffffff)), int32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(0x7fffffff)), int32(1));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(0x7ffffffe)), int32(0x7ffffffc));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(0x7ffffffe)), int32(0x7ffffffd));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(0x7ffffffe)), int32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), int32(0x7ffffffd)), int32(0x7ffffffa));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), int32(0x7ffffffd)), int32(0x7ffffffb));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), int32(0x7ffffffd)), int32(0x7ffffffc));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), int32(0x7fffffff)), int32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), int32(0x7fffffff)), int32(0x7ffffffe));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), int32(0x7fffffff)), int32(0x7ffffffd));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), int32(0x7ffffffe)), int32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), int32(0x7ffffffe)), int32(0x7ffffffd));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), int32(0x7ffffffe)), int32(0x7ffffffc));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(1)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(2)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(1)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(2)), uint32(1));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(1)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(2)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x40000001), uint32(0xffffffff)), uint32(0xbffffffe));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x40000000), uint32(0xffffffff)), uint32(0xbfffffff));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x3fffffff), uint32(0xffffffff)), uint32(0xc0000000));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x80000000)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x80000000)), uint32(1));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x80000000)), uint32(2));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x80000001)), uint32(1));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x80000001)), uint32(2));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x80000001)), uint32(3));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x80000000)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x80000000)), uint32(1));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x80000000)), uint32(2));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x7fffffff)), uint32(0x7ffffffe));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x7fffffff)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x7fffffff)), uint32(1));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x7ffffffe)), uint32(0x7ffffffc));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x7ffffffe)), uint32(0x7ffffffd));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x7ffffffe)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x80000000ll), uint32(0x7ffffffd)), uint32(0x7ffffffa));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7fffffff), uint32(0x7ffffffd)), uint32(0x7ffffffb));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-0x7ffffffe), uint32(0x7ffffffd)), uint32(0x7ffffffc));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), uint32(0x7fffffff)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), uint32(0x7fffffff)), uint32(0x7ffffffe));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), uint32(0x7fffffff)), uint32(0x7ffffffd));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(0), uint32(0x7ffffffe)), uint32(0));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-1), uint32(0x7ffffffe)), uint32(0x7ffffffd));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_modulo(int32(-2), uint32(0x7ffffffe)), uint32(0x7ffffffc));
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-15, 7), -3);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-14, 7), -2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-13, 7), -2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-12, 7), -2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-11, 7), -2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-10, 7), -2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-9, 7), -2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-8, 7), -2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-7, 7), -1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-6, 7), -1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-5, 7), -1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-4, 7), -1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-3, 7), -1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-2, 7), -1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(-1, 7), -1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(0, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(1, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(2, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(3, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(4, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(5, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(6, 7), 0);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(7, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(8, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(9, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(10, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(11, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(12, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(13, 7), 1);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(14, 7), 2);
MPT_TEST_EXPECT_EQUAL(mpt::wrapping_divide(15, 7), 2);
}
} // namespace wrapping_divide
} // namespace base
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_TESTS_WRAPPING_DIVIDE_HPP
@@ -0,0 +1,179 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_UTILITY_HPP
#define MPT_BASE_UTILITY_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/namespace.hpp"
#if MPT_CXX_BEFORE(20)
#include "mpt/base/saturate_cast.hpp"
#endif
#include <type_traits>
#include <utility>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
#if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(13, 0, 0)
using std::in_range;
#else
// Returns true iff Tdst can represent the value val.
// Use as if(mpt::in_range<uint8>(-1)).
template <typename Tdst, typename Tsrc>
constexpr bool in_range(Tsrc val) {
return (static_cast<Tsrc>(mpt::saturate_cast<Tdst>(val)) == val);
}
#endif
#if MPT_CXX_AT_LEAST(23)
using std::to_underlying;
#else
template <typename T>
constexpr std::underlying_type_t<T> to_underlying(T value) noexcept {
return static_cast<typename std::underlying_type<T>::type>(value);
}
#endif
template <typename T>
struct value_initializer {
inline void operator()(T & x) {
x = T{};
}
};
template <typename T, std::size_t N>
struct value_initializer<T[N]> {
inline void operator()(T (&a)[N]) {
for (auto & e : a)
{
value_initializer<T>{}(e);
}
}
};
template <typename T>
inline void reset(T & x) {
value_initializer<T>{}(x);
}
#if MPT_CXX_AT_LEAST(20) && !MPT_CLANG_BEFORE(13, 0, 0)
using std::cmp_equal;
using std::cmp_greater;
using std::cmp_greater_equal;
using std::cmp_less;
using std::cmp_less_equal;
using std::cmp_not_equal;
#else
template <typename Ta, typename Tb>
constexpr bool cmp_equal(Ta a, Tb b) noexcept {
using UTa = typename std::make_unsigned<Ta>::type;
using UTb = typename std::make_unsigned<Tb>::type;
if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
return a == b;
} else if constexpr (std::is_signed<Ta>::value) {
return (a < 0) ? false : static_cast<UTa>(a) == b;
} else {
return (b < 0) ? false : a == static_cast<UTb>(b);
}
}
template <typename Ta, typename Tb>
constexpr bool cmp_not_equal(Ta a, Tb b) noexcept {
using UTa = typename std::make_unsigned<Ta>::type;
using UTb = typename std::make_unsigned<Tb>::type;
if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
return a != b;
} else if constexpr (std::is_signed<Ta>::value) {
return (a < 0) ? true : static_cast<UTa>(a) != b;
} else {
return (b < 0) ? true : a != static_cast<UTb>(b);
}
}
template <typename Ta, typename Tb>
constexpr bool cmp_less(Ta a, Tb b) noexcept {
using UTa = typename std::make_unsigned<Ta>::type;
using UTb = typename std::make_unsigned<Tb>::type;
if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
return a < b;
} else if constexpr (std::is_signed<Ta>::value) {
return (a < 0) ? true : static_cast<UTa>(a) < b;
} else {
return (b < 0) ? false : a < static_cast<UTb>(b);
}
}
template <typename Ta, typename Tb>
constexpr bool cmp_greater(Ta a, Tb b) noexcept {
using UTa = typename std::make_unsigned<Ta>::type;
using UTb = typename std::make_unsigned<Tb>::type;
if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
return a > b;
} else if constexpr (std::is_signed<Ta>::value) {
return (a < 0) ? false : static_cast<UTa>(a) > b;
} else {
return (b < 0) ? true : a > static_cast<UTb>(b);
}
}
template <typename Ta, typename Tb>
constexpr bool cmp_less_equal(Ta a, Tb b) noexcept {
using UTa = typename std::make_unsigned<Ta>::type;
using UTb = typename std::make_unsigned<Tb>::type;
if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
return a <= b;
} else if constexpr (std::is_signed<Ta>::value) {
return (a < 0) ? true : static_cast<UTa>(a) <= b;
} else {
return (b < 0) ? false : a <= static_cast<UTb>(b);
}
}
template <typename Ta, typename Tb>
constexpr bool cmp_greater_equal(Ta a, Tb b) noexcept {
using UTa = typename std::make_unsigned<Ta>::type;
using UTb = typename std::make_unsigned<Tb>::type;
if constexpr (std::is_signed<Ta>::value == std::is_signed<Tb>::value) {
return a >= b;
} else if constexpr (std::is_signed<Ta>::value) {
return (a < 0) ? false : static_cast<UTa>(a) >= b;
} else {
return (b < 0) ? true : a >= static_cast<UTb>(b);
}
}
#endif
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_UTILITY_HPP
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_VERSION_HPP
#define MPT_BASE_VERSION_HPP
#define MPT_VERSION_MAJOR 0
#define MPT_VERSION_MINOR 0
#define MPT_VERSION_PATCH 0
#define MPT_VERSION_BUILD 0
#endif // MPT_BASE_VERSION_HPP
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_WRAPPING_DIVIDE_HPP
#define MPT_BASE_WRAPPING_DIVIDE_HPP
#include "mpt/base/namespace.hpp"
namespace mpt {
inline namespace MPT_INLINE_NS {
// Modulo with more intuitive behaviour for some contexts:
// Instead of being symmetrical around 0, the pattern for positive numbers is repeated in the negative range.
// For example, wrapping_modulo(-1, m) == (m - 1).
// Behaviour is undefined if m<=0.
template <typename T, typename M>
constexpr auto wrapping_modulo(T x, M m) -> decltype(x % m) {
return (x >= 0) ? (x % m) : (m - 1 - ((-1 - x) % m));
}
template <typename T, typename D>
constexpr auto wrapping_divide(T x, D d) -> decltype(x / d) {
return (x >= 0) ? (x / d) : (((x + 1) / d) - 1);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_WRAPPING_DIVIDE_HPP
@@ -0,0 +1,137 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BINARY_BASE64_HPP
#define MPT_BINARY_BASE64_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/string/types.hpp"
#include <array>
#include <stdexcept>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
class base64_parse_error : public std::runtime_error {
public:
base64_parse_error()
: std::runtime_error("invalid Base64 encoding") {
}
};
inline constexpr std::array<mpt::uchar, 64> base64 = {
{MPT_UCHAR('A'), MPT_UCHAR('B'), MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F'), MPT_UCHAR('G'), MPT_UCHAR('H'), MPT_UCHAR('I'), MPT_UCHAR('J'), MPT_UCHAR('K'), MPT_UCHAR('L'), MPT_UCHAR('M'), MPT_UCHAR('N'), MPT_UCHAR('O'), MPT_UCHAR('P'),
MPT_UCHAR('Q'), MPT_UCHAR('R'), MPT_UCHAR('S'), MPT_UCHAR('T'), MPT_UCHAR('U'), MPT_UCHAR('V'), MPT_UCHAR('W'), MPT_UCHAR('X'), MPT_UCHAR('Y'), MPT_UCHAR('Z'), MPT_UCHAR('a'), MPT_UCHAR('b'), MPT_UCHAR('c'), MPT_UCHAR('d'), MPT_UCHAR('e'), MPT_UCHAR('f'),
MPT_UCHAR('g'), MPT_UCHAR('h'), MPT_UCHAR('i'), MPT_UCHAR('j'), MPT_UCHAR('k'), MPT_UCHAR('l'), MPT_UCHAR('m'), MPT_UCHAR('n'), MPT_UCHAR('o'), MPT_UCHAR('p'), MPT_UCHAR('q'), MPT_UCHAR('r'), MPT_UCHAR('s'), MPT_UCHAR('t'), MPT_UCHAR('u'), MPT_UCHAR('v'),
MPT_UCHAR('w'), MPT_UCHAR('x'), MPT_UCHAR('y'), MPT_UCHAR('z'), MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('+'), MPT_UCHAR('/')}
};
template <typename Tbyte>
inline mpt::ustring encode_base64(mpt::span<Tbyte> src_) {
mpt::const_byte_span src = mpt::byte_cast<mpt::const_byte_span>(src_);
mpt::ustring result;
result.reserve(4 * ((src.size() + 2) / 3));
uint32 bits = 0;
std::size_t bytes = 0;
for (std::byte byte : src) {
bits <<= 8;
bits |= mpt::byte_cast<uint8>(byte);
bytes++;
if (bytes == 3) {
result.push_back(base64[(bits >> 18) & 0x3f]);
result.push_back(base64[(bits >> 12) & 0x3f]);
result.push_back(base64[(bits >> 6) & 0x3f]);
result.push_back(base64[(bits >> 0) & 0x3f]);
bits = 0;
bytes = 0;
}
}
std::size_t padding = 0;
while (bytes != 0) {
bits <<= 8;
padding++;
bytes++;
if (bytes == 3) {
result.push_back(base64[(bits >> 18) & 0x3f]);
result.push_back(base64[(bits >> 12) & 0x3f]);
if (padding > 1) {
result.push_back(MPT_UCHAR('='));
} else {
result.push_back(base64[(bits >> 6) & 0x3f]);
}
if (padding > 0) {
result.push_back(MPT_UCHAR('='));
} else {
result.push_back(base64[(bits >> 0) & 0x3f]);
}
bits = 0;
bytes = 0;
}
}
return result;
}
inline uint8 decode_base64_bits(mpt::uchar c) {
for (uint8 i = 0; i < 64; ++i) {
if (base64[i] == c) {
return i;
}
}
throw base64_parse_error();
}
inline std::vector<std::byte> decode_base64(const mpt::ustring & src) {
std::vector<std::byte> result;
result.reserve(3 * (src.length() / 4));
uint32 bits = 0;
std::size_t chars = 0;
std::size_t padding = 0;
for (mpt::uchar c : src) {
bits <<= 6;
if (c == MPT_UCHAR('=')) {
padding++;
} else {
bits |= decode_base64_bits(c);
}
chars++;
if (chars == 4) {
result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 16) & 0xff)));
if (padding < 2) {
result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 8) & 0xff)));
}
if (padding < 1) {
result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 0) & 0xff)));
}
bits = 0;
chars = 0;
padding = 0;
}
}
if (chars != 0) {
throw base64_parse_error();
}
return result;
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BINARY_BASE64_HPP
@@ -0,0 +1,140 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BINARY_BASE64URL_HPP
#define MPT_BINARY_BASE64URL_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/string/types.hpp"
#include <array>
#include <stdexcept>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
class base64url_parse_error : public std::runtime_error {
public:
base64url_parse_error()
: std::runtime_error("invalid Base64URL encoding") {
}
};
inline constexpr std::array<mpt::uchar, 64> base64url = {
{MPT_UCHAR('A'), MPT_UCHAR('B'), MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F'), MPT_UCHAR('G'), MPT_UCHAR('H'), MPT_UCHAR('I'), MPT_UCHAR('J'), MPT_UCHAR('K'), MPT_UCHAR('L'), MPT_UCHAR('M'), MPT_UCHAR('N'), MPT_UCHAR('O'), MPT_UCHAR('P'),
MPT_UCHAR('Q'), MPT_UCHAR('R'), MPT_UCHAR('S'), MPT_UCHAR('T'), MPT_UCHAR('U'), MPT_UCHAR('V'), MPT_UCHAR('W'), MPT_UCHAR('X'), MPT_UCHAR('Y'), MPT_UCHAR('Z'), MPT_UCHAR('a'), MPT_UCHAR('b'), MPT_UCHAR('c'), MPT_UCHAR('d'), MPT_UCHAR('e'), MPT_UCHAR('f'),
MPT_UCHAR('g'), MPT_UCHAR('h'), MPT_UCHAR('i'), MPT_UCHAR('j'), MPT_UCHAR('k'), MPT_UCHAR('l'), MPT_UCHAR('m'), MPT_UCHAR('n'), MPT_UCHAR('o'), MPT_UCHAR('p'), MPT_UCHAR('q'), MPT_UCHAR('r'), MPT_UCHAR('s'), MPT_UCHAR('t'), MPT_UCHAR('u'), MPT_UCHAR('v'),
MPT_UCHAR('w'), MPT_UCHAR('x'), MPT_UCHAR('y'), MPT_UCHAR('z'), MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'), MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'), MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('-'), MPT_UCHAR('_')}
};
template <typename Tbyte>
inline mpt::ustring encode_base64url(mpt::span<Tbyte> src_) {
mpt::const_byte_span src = mpt::byte_cast<mpt::const_byte_span>(src_);
mpt::ustring result;
result.reserve(4 * ((src.size() + 2) / 3));
uint32 bits = 0;
std::size_t bytes = 0;
for (std::byte byte : src) {
bits <<= 8;
bits |= mpt::byte_cast<uint8>(byte);
bytes++;
if (bytes == 3) {
result.push_back(base64url[(bits >> 18) & 0x3f]);
result.push_back(base64url[(bits >> 12) & 0x3f]);
result.push_back(base64url[(bits >> 6) & 0x3f]);
result.push_back(base64url[(bits >> 0) & 0x3f]);
bits = 0;
bytes = 0;
}
}
std::size_t padding = 0;
while (bytes != 0) {
bits <<= 8;
padding++;
bytes++;
if (bytes == 3) {
result.push_back(base64url[(bits >> 18) & 0x3f]);
result.push_back(base64url[(bits >> 12) & 0x3f]);
if (padding <= 1) {
result.push_back(base64url[(bits >> 6) & 0x3f]);
}
if (padding <= 0) {
result.push_back(base64url[(bits >> 0) & 0x3f]);
}
bits = 0;
bytes = 0;
}
}
return result;
}
inline uint8 decode_base64url_bits(mpt::uchar c) {
for (uint8 i = 0; i < 64; ++i)
{
if (base64url[i] == c)
{
return i;
}
}
throw base64url_parse_error();
}
inline std::vector<std::byte> decode_base64url(const mpt::ustring & src) {
std::vector<std::byte> result;
result.reserve(3 * ((src.length() + 2) / 4));
uint32 bits = 0;
std::size_t chars = 0;
for (mpt::uchar c : src) {
bits <<= 6;
bits |= decode_base64url_bits(c);
chars++;
if (chars == 4) {
result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 16) & 0xff)));
result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 8) & 0xff)));
result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 0) & 0xff)));
bits = 0;
chars = 0;
}
}
uint32 padding = 0;
if (chars != 0 && chars < 2) {
throw base64url_parse_error();
}
while (chars != 0) {
bits <<= 6;
padding++;
chars++;
if (chars == 4) {
result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 16) & 0xff)));
if (padding < 2) {
result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 8) & 0xff)));
}
if (padding < 1) {
result.push_back(mpt::byte_cast<std::byte>(static_cast<uint8>((bits >> 0) & 0xff)));
}
bits = 0;
chars = 0;
padding = 0;
}
}
return result;
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BINARY_BASE64URL_HPP
@@ -0,0 +1,89 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BINARY_HEX_HPP
#define MPT_BINARY_HEX_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/string/types.hpp"
#include <array>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
inline constexpr std::array<mpt::uchar, 16> encode_nibble = {
{MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'),
MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'),
MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('A'), MPT_UCHAR('B'),
MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F')}
};
inline bool decode_byte(uint8 & byte, mpt::uchar c1, mpt::uchar c2) {
byte = 0;
if (MPT_UCHAR('0') <= c1 && c1 <= MPT_UCHAR('9')) {
byte += static_cast<uint8>((c1 - MPT_UCHAR('0')) << 4);
} else if (MPT_UCHAR('A') <= c1 && c1 <= MPT_UCHAR('F')) {
byte += static_cast<uint8>((c1 - MPT_UCHAR('A') + 10) << 4);
} else if (MPT_UCHAR('a') <= c1 && c1 <= MPT_UCHAR('f')) {
byte += static_cast<uint8>((c1 - MPT_UCHAR('a') + 10) << 4);
} else {
return false;
}
if (MPT_UCHAR('0') <= c2 && c2 <= MPT_UCHAR('9')) {
byte += static_cast<uint8>(c2 - MPT_UCHAR('0'));
} else if (MPT_UCHAR('A') <= c2 && c2 <= MPT_UCHAR('F')) {
byte += static_cast<uint8>(c2 - MPT_UCHAR('A') + 10);
} else if (MPT_UCHAR('a') <= c2 && c2 <= MPT_UCHAR('f')) {
byte += static_cast<uint8>(c2 - MPT_UCHAR('a') + 10);
} else {
return false;
}
return true;
}
template <typename Tbyte>
inline mpt::ustring encode_hex(mpt::span<Tbyte> src_) {
mpt::const_byte_span src = mpt::byte_cast<mpt::const_byte_span>(src_);
mpt::ustring result;
result.reserve(src.size() * 2);
for (std::byte byte : src) {
result.push_back(encode_nibble[(mpt::byte_cast<uint8>(byte) & 0xf0) >> 4]);
result.push_back(encode_nibble[mpt::byte_cast<uint8>(byte) & 0x0f]);
}
return result;
}
inline std::vector<std::byte> decode_hex(const mpt::ustring & src) {
std::vector<std::byte> result;
result.reserve(src.size() / 2);
for (std::size_t i = 0; (i + 1) < src.size(); i += 2) {
uint8 byte = 0;
if (!decode_byte(byte, src[i], src[i + 1])) {
return result;
}
result.push_back(mpt::byte_cast<std::byte>(byte));
}
return result;
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BINARY_HEX_HPP
@@ -0,0 +1,115 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_TESTS_BINARY_HPP
#define MPT_BASE_TESTS_BINARY_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/span.hpp"
#include "mpt/binary/base64.hpp"
#include "mpt/binary/base64url.hpp"
#include "mpt/string/types.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <string>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace binary {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/binary")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
{
std::string expecteds = std::string("pleasure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(mpt::encode_base64(mpt::as_span(expected)), MPT_USTRING("cGxlYXN1cmUu"));
}
{
std::string expecteds = std::string("leasure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(mpt::encode_base64(mpt::as_span(expected)), MPT_USTRING("bGVhc3VyZS4="));
}
{
std::string expecteds = std::string("easure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(mpt::encode_base64(mpt::as_span(expected)), MPT_USTRING("ZWFzdXJlLg=="));
}
{
std::string expecteds = std::string("pleasure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64(MPT_USTRING("cGxlYXN1cmUu")));
}
{
std::string expecteds = std::string("leasure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64(MPT_USTRING("bGVhc3VyZS4=")));
}
{
std::string expecteds = std::string("easure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64(MPT_USTRING("ZWFzdXJlLg==")));
}
{
std::string expecteds = std::string("pleasure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(mpt::encode_base64url(mpt::as_span(expected)), MPT_USTRING("cGxlYXN1cmUu"));
}
{
std::string expecteds = std::string("leasure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(mpt::encode_base64url(mpt::as_span(expected)), MPT_USTRING("bGVhc3VyZS4"));
}
{
std::string expecteds = std::string("easure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(mpt::encode_base64url(mpt::as_span(expected)), MPT_USTRING("ZWFzdXJlLg"));
}
{
std::string expecteds = std::string("pleasure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64url(MPT_USTRING("cGxlYXN1cmUu")));
}
{
std::string expecteds = std::string("leasure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64url(MPT_USTRING("bGVhc3VyZS4")));
}
{
std::string expecteds = std::string("easure.");
std::vector<std::byte> expected(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data(), mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).data() + mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(expecteds)).size());
MPT_TEST_EXPECT_EQUAL(expected, mpt::decode_base64url(MPT_USTRING("ZWFzdXJlLg")));
}
}
} // namespace binary
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_TESTS_BINARY_HPP
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_CHECK_LIBC_HPP
#define MPT_CHECK_LIBC_HPP
#include "mpt/base/detect_os.hpp"
#include "mpt/base/compiletime_warning.hpp"
#ifndef __STDC_CONSTANT_MACROS
#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_STDC_CONSTANT_MACROS
MPT_WARNING("C stdlib does not provide constant macros. Please #define __STDC_CONSTANT_MACROS.")
#endif
#endif
#ifndef __STDC_FORMAT_MACROS
#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_STDC_FORMAT_MACROS
MPT_WARNING("C stdlib does not provide limit macros. Please #define __STDC_FORMAT_MACROS.")
#endif
#endif
#ifndef __STDC_LIMIT_MACROS
#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_STDC_LIMIT_MACROS
MPT_WARNING("C stdlib does not provide limit macros. Please #define __STDC_LIMIT_MACROS.")
#endif
#endif
#ifndef _USE_MATH_DEFINES
#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_USE_MATH_DEFINES
MPT_WARNING("C stdlib does not provide math constants. Please #define _USE_MATH_DEFINES.")
#endif
#endif
#if MPT_LIBC_GLIBC
#if !defined(_FILE_OFFSET_BITS)
#ifndef MPT_CHECK_LIBC_IGNORE_WARNING_NO_FILE_OFFSET_BITS
MPT_WARNING("C stdlib may not provide 64bit std::FILE access. Please #define _FILE_OFFSET_BITS=64.")
#endif
#endif
#endif
#endif // MPT_CHECK_LIBC_HPP
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_CHECK_MFC_HPP
#define MPT_CHECK_MFC_HPP
#include "mpt/base/compiletime_warning.hpp"
#include "mpt/detect/mfc.hpp"
#if MPT_DETECTED_MFC
#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
#ifndef MPT_CHECK_MFC_IGNORE_WARNING_NO_CSTRING_DISABLE_NARROW_WIDE_CONVERSION
MPT_WARNING("MFC uses CString with automatic encoding conversions. Please #define _CSTRING_DISABLE_NARROW_WIDE_CONVERSION.")
#endif
#endif
#endif // MPT_DETECTED_MFC
#endif // MPT_CHECK_MFC_HPP
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_CHECK_WINDOWS_HPP
#define MPT_CHECK_WINDOWS_HPP
#include "mpt/base/detect_os.hpp"
#include "mpt/base/compiletime_warning.hpp"
#if MPT_OS_WINDOWS
#ifndef UNICODE
#ifndef MPT_CHECK_WINDOWS_IGNORE_WARNING_NO_UNICODE
MPT_WARNING("windows.h uses MBCS TCHAR. Please #define UNICODE.")
#endif
#endif
#ifndef NOMINMAX
#ifndef MPT_CHECK_WINDOWS_IGNORE_WARNING_NO_NOMINMAX
MPT_WARNING("windows.h defines min and max which conflicts with C++. Please #define NOMINMAX.")
#endif
#endif
#endif // MPT_OS_WINDOWS
#endif // MPT_CHECK_WINDOWS_HPP
@@ -0,0 +1,205 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_CRC_CRC_HPP
#define MPT_CRC_CRC_HPP
#include "mpt/base/array.hpp"
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/memory.hpp"
#include <array>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename T, T polynomial, T initial, T resultXOR, bool reverseData>
class crc {
public:
using self_type = crc;
using value_type = T;
using byte_type = uint8;
static constexpr std::size_t size_bytes = sizeof(value_type);
static constexpr std::size_t size_bits = sizeof(value_type) * 8;
static constexpr value_type top_bit = static_cast<value_type>(1) << ((sizeof(value_type) * 8) - 1);
private:
template <typename Tint>
static constexpr Tint reverse(Tint value) noexcept {
const std::size_t bits = sizeof(Tint) * 8;
Tint result = 0;
for (std::size_t i = 0; i < bits; ++i) {
result <<= 1;
result |= static_cast<Tint>(value & 0x1);
value >>= 1;
}
return result;
}
static constexpr value_type calculate_table_entry(byte_type pos) noexcept {
value_type value = 0;
value = (static_cast<value_type>(reverseData ? reverse(pos) : pos) << (size_bits - 8));
for (std::size_t bit = 0; bit < 8; ++bit) {
if (value & top_bit) {
value = (value << 1) ^ polynomial;
} else {
value = (value << 1);
}
}
value = (reverseData ? reverse(value) : value);
return value;
}
private:
static constexpr std::array<value_type, 256> calculate_table() noexcept {
std::array<value_type, 256> t = mpt::init_array<value_type, 256>(value_type{});
for (std::size_t i = 0; i < 256; ++i) {
t[i] = calculate_table_entry(static_cast<byte_type>(i));
}
return t;
}
static constexpr std::array<value_type, 256> table = calculate_table();
private:
constexpr value_type read_table(byte_type pos) const noexcept {
return table[pos];
}
private:
value_type value;
public:
constexpr crc() noexcept
: value(initial) {
return;
}
constexpr void processByte(byte_type byte) noexcept {
if constexpr (reverseData) {
value = (value >> 8) ^ read_table(static_cast<byte_type>((value & 0xff) ^ byte));
} else {
value = (value << 8) ^ read_table(static_cast<byte_type>(((value >> (size_bits - 8)) & 0xff) ^ byte));
}
}
constexpr value_type result() const noexcept {
return (value ^ resultXOR);
}
public:
constexpr operator value_type() const noexcept {
return result();
}
inline crc & process(char c) noexcept {
processByte(mpt::byte_cast<byte_type>(c));
return *this;
}
inline crc & process(signed char c) noexcept {
processByte(static_cast<byte_type>(c));
return *this;
}
inline crc & process(unsigned char c) noexcept {
processByte(mpt::byte_cast<byte_type>(c));
return *this;
}
inline crc & process(std::byte c) noexcept {
processByte(mpt::byte_cast<byte_type>(c));
return *this;
}
template <typename InputIt>
inline crc & process(InputIt beg, InputIt end) {
for (InputIt it = beg; it != end; ++it) {
static_assert(sizeof(*it) == 1, "1 byte type required");
process(*it);
}
return *this;
}
template <typename Container>
inline crc & process(const Container & data) {
operator()(data.begin(), data.end());
return *this;
}
inline crc & operator()(char c) noexcept {
processByte(mpt::byte_cast<byte_type>(c));
return *this;
}
inline crc & operator()(signed char c) noexcept {
processByte(static_cast<byte_type>(c));
return *this;
}
inline crc & operator()(unsigned char c) noexcept {
processByte(mpt::byte_cast<byte_type>(c));
return *this;
}
inline crc & operator()(std::byte c) noexcept {
processByte(mpt::byte_cast<byte_type>(c));
return *this;
}
template <typename InputIt>
crc & operator()(InputIt beg, InputIt end) {
for (InputIt it = beg; it != end; ++it) {
static_assert(sizeof(*it) == 1, "1 byte type required");
operator()(*it);
}
return *this;
}
template <typename Container>
inline crc & operator()(const Container & data) {
operator()(data.begin(), data.end());
return *this;
}
template <typename InputIt>
crc(InputIt beg, InputIt end)
: value(initial) {
for (InputIt it = beg; it != end; ++it) {
static_assert(sizeof(*it) == 1, "1 byte type required");
process(*it);
}
}
template <typename Container>
inline crc(const Container & data)
: value(initial) {
process(data.begin(), data.end());
}
};
using crc16 = crc<uint16, 0x8005, 0, 0, true>;
using crc32 = crc<uint32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true>;
using crc32_ogg = crc<uint32, 0x04C11DB7, 0, 0, false>;
using crc32c = crc<uint32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true>;
using crc64_jones = crc<uint64, 0xAD93D23594C935A9ull, 0xFFFFFFFFFFFFFFFFull, 0, true>;
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_CRC_CRC_HPP
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_TESTS_CRC_HPP
#define MPT_BASE_TESTS_CRC_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/crc/crc.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <string>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace crc {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/crc")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
MPT_TEST_EXPECT_EQUAL(mpt::crc32(std::string("123456789")), 0xCBF43926u);
MPT_TEST_EXPECT_EQUAL(mpt::crc32_ogg(std::string("123456789")), 0x89a1897fu);
}
} // namespace crc
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_TESTS_CRC_HPP
@@ -0,0 +1,139 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_CRYPTO_EXCEPTION_HPP
#define MPT_CRYPTO_EXCEPTION_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/format/simple.hpp"
#include "mpt/out_of_memory/out_of_memory.hpp"
#include <stdexcept>
#include <string>
#if MPT_OS_WINDOWS
#include <windows.h> // must be before wincrypt.h for clang-cl
#include <wincrypt.h> // must be before ncrypt.h
#include <ncrypt.h>
#endif // MPT_OS_WINDOWS
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace crypto {
#if MPT_OS_WINDOWS
class exception
: public std::runtime_error {
private:
NTSTATUS m_Status;
public:
exception(NTSTATUS status)
: std::runtime_error(std::string("crypto error: NTSTATUS ") + mpt::format<std::string>::hex0<8>(static_cast<DWORD>(status)))
, m_Status(status) {
return;
}
exception(NTSTATUS status, const std::string & function)
: std::runtime_error(std::string("crypto error: ") + function + std::string(" NTSTATUS ") + mpt::format<std::string>::hex0<8>(static_cast<DWORD>(status)))
, m_Status(status) {
return;
}
public:
NTSTATUS code() const noexcept {
return m_Status;
}
};
class security_exception
: public std::runtime_error {
private:
SECURITY_STATUS m_Status;
public:
security_exception(SECURITY_STATUS status)
: std::runtime_error(std::string("crypto error: SECURITY_STATUS ") + mpt::format<std::string>::hex0<8>(static_cast<DWORD>(status)))
, m_Status(status) {
return;
}
security_exception(SECURITY_STATUS status, const std::string & function)
: std::runtime_error(std::string("crypto error: ") + function + std::string(" SECURITY_STATUS ") + mpt::format<std::string>::hex0<8>(static_cast<DWORD>(status)))
, m_Status(status) {
return;
}
public:
SECURITY_STATUS code() const noexcept {
return m_Status;
}
};
inline void CheckNTSTATUS(NTSTATUS status) {
if (status >= 0) {
return;
} else if (static_cast<DWORD>(status) == STATUS_NO_MEMORY) {
mpt::throw_out_of_memory();
} else {
throw exception(status);
}
}
inline void CheckNTSTATUS(NTSTATUS status, const std::string & function) {
if (status >= 0) {
return;
} else if (static_cast<DWORD>(status) == STATUS_NO_MEMORY) {
mpt::throw_out_of_memory();
} else {
throw exception(status, function);
}
}
inline void CheckSECURITY_STATUS(SECURITY_STATUS status) {
if (status == ERROR_SUCCESS) {
return;
} else if (status == NTE_NO_MEMORY) {
mpt::throw_out_of_memory();
} else {
throw security_exception(status);
}
}
inline void CheckSECURITY_STATUS(SECURITY_STATUS status, const std::string & function) {
if (status == ERROR_SUCCESS) {
return;
} else if (status == NTE_NO_MEMORY) {
mpt::throw_out_of_memory();
} else {
throw security_exception(status, function);
}
}
#endif // MPT_OS_WINDOWS
} // namespace crypto
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_CRYPTO_EXCEPTION_HPP
@@ -0,0 +1,180 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_CRYPTO_HASH_HPP
#define MPT_CRYPTO_HASH_HPP
#include "mpt/base/array.hpp"
#include "mpt/base/detect.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/saturate_cast.hpp"
#include "mpt/crypto/exception.hpp"
#include <algorithm>
#include <vector>
#include <cassert>
#include <cstddef>
#if MPT_OS_WINDOWS
#include <windows.h> // must be before wincrypt.h for clang-cl
#include <bcrypt.h>
#endif // MPT_OS_WINDOWS
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace crypto {
#if MPT_OS_WINDOWS
namespace hash {
struct hash_traits_md5 {
static constexpr std::size_t output_bits = 128;
static constexpr std::size_t output_bytes = output_bits / 8;
static constexpr const wchar_t * bcrypt_name = BCRYPT_MD5_ALGORITHM;
};
struct hash_traits_sha1 {
static constexpr std::size_t output_bits = 160;
static constexpr std::size_t output_bytes = output_bits / 8;
static constexpr const wchar_t * bcrypt_name = BCRYPT_SHA1_ALGORITHM;
};
struct hash_traits_sha256 {
static constexpr std::size_t output_bits = 256;
static constexpr std::size_t output_bytes = output_bits / 8;
static constexpr const wchar_t * bcrypt_name = BCRYPT_SHA256_ALGORITHM;
};
struct hash_traits_sha512 {
static constexpr std::size_t output_bits = 512;
static constexpr std::size_t output_bytes = output_bits / 8;
static constexpr const wchar_t * bcrypt_name = BCRYPT_SHA512_ALGORITHM;
};
template <typename Traits>
class hash_impl {
public:
using traits = Traits;
using result_type = std::array<std::byte, traits::output_bytes>;
private:
BCRYPT_ALG_HANDLE hAlg = NULL;
std::vector<BYTE> hashState;
std::vector<BYTE> hashResult;
BCRYPT_HASH_HANDLE hHash = NULL;
private:
void init() {
CheckNTSTATUS(BCryptOpenAlgorithmProvider(&hAlg, traits::bcrypt_name, NULL, 0), "BCryptOpenAlgorithmProvider");
if (!hAlg) {
throw exception(0);
}
DWORD hashStateSize = 0;
DWORD hashStateSizeSize = 0;
CheckNTSTATUS(BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&hashStateSize, sizeof(DWORD), &hashStateSizeSize, 0), "BCryptGetProperty");
if (hashStateSizeSize != sizeof(DWORD)) {
throw exception(0);
}
if (hashStateSize <= 0) {
throw exception(0);
}
hashState.resize(hashStateSize);
DWORD hashResultSize = 0;
DWORD hashResultSizeSize = 0;
CheckNTSTATUS(BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PBYTE)&hashResultSize, sizeof(DWORD), &hashResultSizeSize, 0), "BCryptGetProperty");
if (hashResultSizeSize != sizeof(DWORD)) {
throw exception(0);
}
if (hashResultSize <= 0) {
throw exception(0);
}
if (hashResultSize != mpt::extent<result_type>()) {
throw exception(0);
}
hashResult.resize(hashResultSize);
CheckNTSTATUS(BCryptCreateHash(hAlg, &hHash, hashState.data(), hashStateSize, NULL, 0, 0), "BCryptCreateHash");
if (!hHash) {
throw exception(0);
}
}
void cleanup() {
if (hHash) {
BCryptDestroyHash(hHash);
hHash = NULL;
}
hashResult.resize(0);
hashResult.shrink_to_fit();
hashState.resize(0);
hashState.shrink_to_fit();
if (hAlg) {
BCryptCloseAlgorithmProvider(hAlg, 0);
hAlg = NULL;
}
}
public:
hash_impl() {
try {
init();
} catch (...) {
cleanup();
throw;
}
}
hash_impl(const hash_impl &) = delete;
hash_impl & operator=(const hash_impl &) = delete;
~hash_impl() {
cleanup();
}
public:
hash_impl & process(mpt::const_byte_span data) {
CheckNTSTATUS(BCryptHashData(hHash, const_cast<UCHAR *>(mpt::byte_cast<const UCHAR *>(data.data())), mpt::saturate_cast<ULONG>(data.size()), 0), "BCryptHashData");
return *this;
}
result_type result() {
result_type res = mpt::init_array<std::byte, traits::output_bytes>(std::byte{0});
CheckNTSTATUS(BCryptFinishHash(hHash, hashResult.data(), mpt::saturate_cast<ULONG>(hashResult.size()), 0), "BCryptFinishHash");
assert(hashResult.size() == mpt::extent<result_type>());
std::transform(hashResult.begin(), hashResult.end(), res.begin(), [](BYTE b) { return mpt::as_byte(b); });
return res;
}
};
using MD5 = hash_impl<hash_traits_md5>;
using SHA1 = hash_impl<hash_traits_sha1>;
using SHA256 = hash_impl<hash_traits_sha256>;
using SHA512 = hash_impl<hash_traits_sha512>;
} // namespace hash
#endif // MPT_OS_WINDOWS
} // namespace crypto
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_CRYPTO_HASH_HPP
@@ -0,0 +1,531 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_CRYPTO_JWK_HPP
#define MPT_CRYPTO_JWK_HPP
#include "mpt/base/alloc.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/saturate_cast.hpp"
#include "mpt/base/span.hpp"
#include "mpt/binary/base64url.hpp"
#include "mpt/crypto/exception.hpp"
#include "mpt/crypto/hash.hpp"
#include "mpt/detect/nlohmann_json.hpp"
#include "mpt/json/json.hpp"
#include "mpt/out_of_memory/out_of_memory.hpp"
#include "mpt/string/types.hpp"
#include "mpt/string/utility.hpp"
#include "mpt/string_transcode/transcode.hpp"
#include <algorithm>
#include <stdexcept>
#include <string>
#include <vector>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#if MPT_OS_WINDOWS
#include <windows.h> // must be before wincrypt.h for clang-cl
#include <bcrypt.h>
#include <wincrypt.h> // must be before ncrypt.h
#include <ncrypt.h>
#endif // MPT_OS_WINDOWS
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace crypto {
#if MPT_OS_WINDOWS && MPT_DETECTED_NLOHMANN_JSON
class keystore {
public:
enum class domain {
system = 1,
user = 2,
};
private:
NCRYPT_PROV_HANDLE hProv = NULL;
domain ProvDomain = domain::user;
private:
void cleanup() {
if (hProv) {
NCryptFreeObject(hProv);
hProv = NULL;
}
}
public:
keystore(domain d)
: ProvDomain(d) {
try {
CheckSECURITY_STATUS(NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0), "NCryptOpenStorageProvider");
} catch (...) {
cleanup();
throw;
}
}
~keystore() {
return;
}
operator NCRYPT_PROV_HANDLE() {
return hProv;
}
keystore::domain store_domain() const {
return ProvDomain;
}
};
namespace asymmetric {
class signature_verification_failed
: public std::runtime_error {
public:
signature_verification_failed()
: std::runtime_error("Signature Verification failed.") {
return;
}
};
inline std::vector<mpt::ustring> jws_get_keynames(const mpt::ustring & jws_) {
std::vector<mpt::ustring> result;
nlohmann::json jws = nlohmann::json::parse(mpt::transcode<std::string>(mpt::common_encoding::utf8, jws_));
for (const auto & s : jws["signatures"]) {
result.push_back(s["header"]["kid"]);
}
return result;
}
struct RSASSA_PSS_SHA512_traits {
using hash_type = mpt::crypto::hash::SHA512;
static constexpr const char * jwk_alg = "PS512";
};
template <typename Traits = RSASSA_PSS_SHA512_traits, std::size_t keysize = 4096>
class rsassa_pss {
public:
using hash_type = typename Traits::hash_type;
static constexpr const char * jwk_alg = Traits::jwk_alg;
struct public_key_data {
mpt::ustring name;
uint32 length = 0;
std::vector<std::byte> public_exp;
std::vector<std::byte> modulus;
std::vector<std::byte> as_cng_blob() const {
BCRYPT_RSAKEY_BLOB rsakey_blob{};
rsakey_blob.Magic = BCRYPT_RSAPUBLIC_MAGIC;
rsakey_blob.BitLength = length;
rsakey_blob.cbPublicExp = mpt::saturate_cast<ULONG>(public_exp.size());
rsakey_blob.cbModulus = mpt::saturate_cast<ULONG>(modulus.size());
std::vector<std::byte> result(sizeof(BCRYPT_RSAKEY_BLOB) + public_exp.size() + modulus.size());
std::memcpy(result.data(), &rsakey_blob, sizeof(BCRYPT_RSAKEY_BLOB));
std::memcpy(result.data() + sizeof(BCRYPT_RSAKEY_BLOB), public_exp.data(), public_exp.size());
std::memcpy(result.data() + sizeof(BCRYPT_RSAKEY_BLOB) + public_exp.size(), modulus.data(), modulus.size());
return result;
}
mpt::ustring as_jwk() const {
nlohmann::json json = nlohmann::json::object();
json["kid"] = name;
json["kty"] = "RSA";
json["alg"] = jwk_alg;
json["use"] = "sig";
json["e"] = mpt::encode_base64url(mpt::as_span(public_exp));
json["n"] = mpt::encode_base64url(mpt::as_span(modulus));
return mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, json.dump());
}
static public_key_data from_jwk(const mpt::ustring & jwk) {
public_key_data result;
try {
nlohmann::json json = nlohmann::json::parse(mpt::transcode<std::string>(mpt::common_encoding::utf8, jwk));
if (json["kty"] != "RSA") {
throw std::runtime_error("Cannot parse RSA public key JWK.");
}
if (json["alg"] != jwk_alg) {
throw std::runtime_error("Cannot parse RSA public key JWK.");
}
if (json["use"] != "sig") {
throw std::runtime_error("Cannot parse RSA public key JWK.");
}
result.name = json["kid"].get<mpt::ustring>();
result.public_exp = mpt::decode_base64url(json["e"]);
result.modulus = mpt::decode_base64url(json["n"]);
result.length = mpt::saturate_cast<uint32>(result.modulus.size() * 8);
} catch (mpt::out_of_memory e) {
mpt::rethrow_out_of_memory(e);
} catch (...) {
throw std::runtime_error("Cannot parse RSA public key JWK.");
}
return result;
}
static public_key_data from_cng_blob(const mpt::ustring & name, const std::vector<std::byte> & blob) {
public_key_data result;
BCRYPT_RSAKEY_BLOB rsakey_blob{};
if (blob.size() < sizeof(BCRYPT_RSAKEY_BLOB)) {
throw std::runtime_error("Cannot parse RSA public key blob.");
}
std::memcpy(&rsakey_blob, blob.data(), sizeof(BCRYPT_RSAKEY_BLOB));
if (rsakey_blob.Magic != BCRYPT_RSAPUBLIC_MAGIC) {
throw std::runtime_error("Cannot parse RSA public key blob.");
}
if (blob.size() != sizeof(BCRYPT_RSAKEY_BLOB) + rsakey_blob.cbPublicExp + rsakey_blob.cbModulus) {
throw std::runtime_error("Cannot parse RSA public key blob.");
}
result.name = name;
result.length = rsakey_blob.BitLength;
result.public_exp = std::vector<std::byte>(blob.data() + sizeof(BCRYPT_RSAKEY_BLOB), blob.data() + sizeof(BCRYPT_RSAKEY_BLOB) + rsakey_blob.cbPublicExp);
result.modulus = std::vector<std::byte>(blob.data() + sizeof(BCRYPT_RSAKEY_BLOB) + rsakey_blob.cbPublicExp, blob.data() + sizeof(BCRYPT_RSAKEY_BLOB) + rsakey_blob.cbPublicExp + rsakey_blob.cbModulus);
return result;
}
};
static std::vector<public_key_data> parse_jwk_set(const mpt::ustring & jwk_set_) {
std::vector<public_key_data> result;
nlohmann::json jwk_set = nlohmann::json::parse(mpt::transcode<std::string>(mpt::common_encoding::utf8, jwk_set_));
for (const auto & k : jwk_set["keys"]) {
try {
result.push_back(public_key_data::from_jwk(mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, k.dump())));
} catch (...) {
// nothing
}
}
return result;
}
class public_key {
private:
mpt::ustring name;
BCRYPT_ALG_HANDLE hSignAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
private:
void cleanup() {
if (hKey) {
BCryptDestroyKey(hKey);
hKey = NULL;
}
if (hSignAlg) {
BCryptCloseAlgorithmProvider(hSignAlg, 0);
hSignAlg = NULL;
}
}
public:
public_key(const public_key_data & data) {
try {
name = data.name;
CheckNTSTATUS(BCryptOpenAlgorithmProvider(&hSignAlg, BCRYPT_RSA_ALGORITHM, NULL, 0), "BCryptOpenAlgorithmProvider");
std::vector<std::byte> blob = data.as_cng_blob();
CheckNTSTATUS(BCryptImportKeyPair(hSignAlg, NULL, BCRYPT_RSAPUBLIC_BLOB, &hKey, mpt::byte_cast<UCHAR *>(blob.data()), mpt::saturate_cast<ULONG>(blob.size()), 0), "BCryptImportKeyPair");
} catch (...) {
cleanup();
throw;
}
}
public_key(const public_key & other)
: public_key(other.get_public_key_data()) {
return;
}
public_key & operator=(const public_key & other) {
if (&other == this) {
return *this;
}
public_key copy(other);
{
using std::swap;
swap(copy.name, name);
swap(copy.hSignAlg, hSignAlg);
swap(copy.hKey, hKey);
}
return *this;
}
~public_key() {
cleanup();
}
mpt::ustring get_name() const {
return name;
}
public_key_data get_public_key_data() const {
DWORD bytes = 0;
CheckNTSTATUS(BCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, 0, &bytes, 0), "BCryptExportKey");
std::vector<std::byte> blob(bytes);
CheckNTSTATUS(BCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, mpt::byte_cast<BYTE *>(blob.data()), mpt::saturate_cast<DWORD>(blob.size()), &bytes, 0), "BCryptExportKey");
return public_key_data::from_cng_blob(name, blob);
}
void verify_hash(typename hash_type::result_type hash, std::vector<std::byte> signature) {
BCRYPT_PSS_PADDING_INFO paddinginfo;
paddinginfo.pszAlgId = hash_type::traits::bcrypt_name;
paddinginfo.cbSalt = mpt::saturate_cast<ULONG>(hash_type::traits::output_bytes);
NTSTATUS result = BCryptVerifySignature(hKey, &paddinginfo, mpt::byte_cast<UCHAR *>(hash.data()), mpt::saturate_cast<ULONG>(hash.size()), mpt::byte_cast<UCHAR *>(signature.data()), mpt::saturate_cast<ULONG>(signature.size()), BCRYPT_PAD_PSS);
if (result == 0x00000000 /*STATUS_SUCCESS*/) {
return;
}
if (result == 0xC000A000 /*STATUS_INVALID_SIGNATURE*/) {
throw signature_verification_failed();
}
CheckNTSTATUS(result, "BCryptVerifySignature");
throw signature_verification_failed();
}
void verify(mpt::const_byte_span payload, const std::vector<std::byte> & signature) {
verify_hash(hash_type().process(payload).result(), signature);
}
std::vector<std::byte> jws_verify(const mpt::ustring & jws_) {
nlohmann::json jws = nlohmann::json::parse(mpt::transcode<std::string>(mpt::common_encoding::utf8, jws_));
std::vector<std::byte> payload = mpt::decode_base64url(jws["payload"]);
nlohmann::json jsignature = nlohmann::json::object();
bool sigfound = false;
for (const auto & s : jws["signatures"]) {
if (s["header"]["kid"] == mpt::transcode<std::string>(mpt::common_encoding::utf8, name)) {
jsignature = s;
sigfound = true;
}
}
if (!sigfound) {
throw signature_verification_failed();
}
std::vector<std::byte> protectedheaderraw = mpt::decode_base64url(jsignature["protected"]);
std::vector<std::byte> signature = mpt::decode_base64url(jsignature["signature"]);
nlohmann::json header = nlohmann::json::parse(mpt::buffer_cast<std::string>(protectedheaderraw));
if (header["typ"] != "JWT") {
throw signature_verification_failed();
}
if (header["alg"] != jwk_alg) {
throw signature_verification_failed();
}
verify_hash(hash_type().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(mpt::transcode<std::string>(mpt::common_encoding::utf8, mpt::encode_base64url(mpt::as_span(protectedheaderraw)) + MPT_USTRING(".") + mpt::encode_base64url(mpt::as_span(payload)))))).result(), signature);
return payload;
}
std::vector<std::byte> jws_compact_verify(const mpt::ustring & jws) {
std::vector<mpt::ustring> parts = mpt::split<mpt::ustring>(jws, MPT_USTRING("."));
if (parts.size() != 3) {
throw signature_verification_failed();
}
std::vector<std::byte> protectedheaderraw = mpt::decode_base64url(parts[0]);
std::vector<std::byte> payload = mpt::decode_base64url(parts[1]);
std::vector<std::byte> signature = mpt::decode_base64url(parts[2]);
nlohmann::json header = nlohmann::json::parse(mpt::buffer_cast<std::string>(protectedheaderraw));
if (header["typ"] != "JWT") {
throw signature_verification_failed();
}
if (header["alg"] != jwk_alg) {
throw signature_verification_failed();
}
verify_hash(hash_type().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(mpt::transcode<std::string>(mpt::common_encoding::utf8, mpt::encode_base64url(mpt::as_span(protectedheaderraw)) + MPT_USTRING(".") + mpt::encode_base64url(mpt::as_span(payload)))))).result(), signature);
return payload;
}
};
static inline void jws_verify_at_least_one(std::vector<public_key> & keys, const std::vector<std::byte> & expectedPayload, const mpt::ustring & signature) {
std::vector<mpt::ustring> keynames = mpt::crypto::asymmetric::jws_get_keynames(signature);
bool sigchecked = false;
for (const auto & keyname : keynames) {
for (auto & key : keys) {
if (key.get_name() == keyname) {
if (expectedPayload != key.jws_verify(signature)) {
throw mpt::crypto::asymmetric::signature_verification_failed();
}
sigchecked = true;
}
}
}
if (!sigchecked) {
throw mpt::crypto::asymmetric::signature_verification_failed();
}
}
static inline std::vector<std::byte> jws_verify_at_least_one(std::vector<public_key> & keys, const mpt::ustring & signature) {
std::vector<mpt::ustring> keynames = mpt::crypto::asymmetric::jws_get_keynames(signature);
for (const auto & keyname : keynames) {
for (auto & key : keys) {
if (key.get_name() == keyname) {
return key.jws_verify(signature);
}
}
}
throw mpt::crypto::asymmetric::signature_verification_failed();
}
class managed_private_key {
private:
mpt::ustring name;
NCRYPT_KEY_HANDLE hKey = NULL;
private:
void cleanup() {
if (hKey) {
NCryptFreeObject(hKey);
hKey = NULL;
}
}
public:
managed_private_key() = delete;
managed_private_key(const managed_private_key &) = delete;
managed_private_key & operator=(const managed_private_key &) = delete;
managed_private_key(keystore & keystore) {
try {
CheckSECURITY_STATUS(NCryptCreatePersistedKey(keystore, &hKey, BCRYPT_RSA_ALGORITHM, NULL, 0, 0), "NCryptCreatePersistedKey");
} catch (...) {
cleanup();
throw;
}
}
managed_private_key(keystore & keystore, const mpt::ustring & name_)
: name(name_) {
try {
SECURITY_STATUS openKeyStatus = NCryptOpenKey(keystore, &hKey, mpt::transcode<std::wstring>(name).c_str(), 0, (keystore.store_domain() == keystore::domain::system ? NCRYPT_MACHINE_KEY_FLAG : 0));
if (openKeyStatus == NTE_BAD_KEYSET) {
CheckSECURITY_STATUS(NCryptCreatePersistedKey(keystore, &hKey, BCRYPT_RSA_ALGORITHM, mpt::transcode<std::wstring>(name).c_str(), 0, (keystore.store_domain() == keystore::domain::system ? NCRYPT_MACHINE_KEY_FLAG : 0)), "NCryptCreatePersistedKey");
DWORD length = mpt::saturate_cast<DWORD>(keysize);
CheckSECURITY_STATUS(NCryptSetProperty(hKey, NCRYPT_LENGTH_PROPERTY, (PBYTE)&length, mpt::saturate_cast<DWORD>(sizeof(DWORD)), 0), "NCryptSetProperty");
CheckSECURITY_STATUS(NCryptFinalizeKey(hKey, 0), "NCryptFinalizeKey");
} else {
CheckSECURITY_STATUS(openKeyStatus, "NCryptOpenKey");
}
} catch (...) {
cleanup();
throw;
}
}
~managed_private_key() {
cleanup();
}
void destroy() {
CheckSECURITY_STATUS(NCryptDeleteKey(hKey, 0), "NCryptDeleteKey");
name = mpt::ustring();
hKey = NULL;
}
public:
public_key_data get_public_key_data() const {
DWORD bytes = 0;
CheckSECURITY_STATUS(NCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, NULL, 0, &bytes, 0), "NCryptExportKey");
std::vector<std::byte> blob(bytes);
CheckSECURITY_STATUS(NCryptExportKey(hKey, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, mpt::byte_cast<BYTE *>(blob.data()), mpt::saturate_cast<DWORD>(blob.size()), &bytes, 0), "NCryptExportKey");
return public_key_data::from_cng_blob(name, blob);
}
std::vector<std::byte> sign_hash(typename hash_type::result_type hash) {
BCRYPT_PSS_PADDING_INFO paddinginfo;
paddinginfo.pszAlgId = hash_type::traits::bcrypt_name;
paddinginfo.cbSalt = mpt::saturate_cast<ULONG>(hash_type::traits::output_bytes);
DWORD bytes = 0;
CheckSECURITY_STATUS(NCryptSignHash(hKey, &paddinginfo, mpt::byte_cast<BYTE *>(hash.data()), mpt::saturate_cast<DWORD>(hash.size()), NULL, 0, &bytes, BCRYPT_PAD_PSS), "NCryptSignHash");
std::vector<std::byte> result(bytes);
CheckSECURITY_STATUS(NCryptSignHash(hKey, &paddinginfo, mpt::byte_cast<BYTE *>(hash.data()), mpt::saturate_cast<DWORD>(hash.size()), mpt::byte_cast<BYTE *>(result.data()), mpt::saturate_cast<DWORD>(result.size()), &bytes, BCRYPT_PAD_PSS), "NCryptSignHash");
return result;
}
std::vector<std::byte> sign(mpt::const_byte_span payload) {
return sign_hash(hash_type().process(payload).result());
}
mpt::ustring jws_compact_sign(mpt::const_byte_span payload) {
nlohmann::json protectedheader = nlohmann::json::object();
protectedheader["typ"] = "JWT";
protectedheader["alg"] = jwk_alg;
std::string protectedheaderstring = protectedheader.dump();
std::vector<std::byte> signature = sign_hash(hash_type().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(mpt::transcode<std::string>(mpt::common_encoding::utf8, mpt::encode_base64url(mpt::as_span(protectedheaderstring)) + MPT_USTRING(".") + mpt::encode_base64url(payload))))).result());
return mpt::encode_base64url(mpt::as_span(protectedheaderstring)) + MPT_USTRING(".") + mpt::encode_base64url(payload) + MPT_USTRING(".") + mpt::encode_base64url(mpt::as_span(signature));
}
mpt::ustring jws_sign(mpt::const_byte_span payload) {
nlohmann::json protectedheader = nlohmann::json::object();
protectedheader["typ"] = "JWT";
protectedheader["alg"] = jwk_alg;
std::string protectedheaderstring = protectedheader.dump();
nlohmann::json header = nlohmann::json::object();
header["kid"] = name;
std::vector<std::byte> signature = sign_hash(hash_type().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(mpt::transcode<std::string>(mpt::common_encoding::utf8, mpt::encode_base64url(mpt::as_span(protectedheaderstring)) + MPT_USTRING(".") + mpt::encode_base64url(payload))))).result());
nlohmann::json jws = nlohmann::json::object();
jws["payload"] = mpt::encode_base64url(payload);
jws["signatures"] = nlohmann::json::array();
nlohmann::json jsignature = nlohmann::json::object();
jsignature["header"] = header;
jsignature["protected"] = mpt::encode_base64url(mpt::as_span(protectedheaderstring));
jsignature["signature"] = mpt::encode_base64url(mpt::as_span(signature));
jws["signatures"].push_back(jsignature);
return mpt::transcode<mpt::ustring>(mpt::common_encoding::utf8, jws.dump());
}
};
}; // class rsassa_pss
} // namespace asymmetric
#endif // MPT_OS_WINDOWS && MPT_DETECTED_NLOHMANN_JSON
} // namespace crypto
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_CRYPTO_JWK_HPP
@@ -0,0 +1,106 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_BASE_TESTS_CRYPTO_HPP
#define MPT_BASE_TESTS_CRYPTO_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/span.hpp"
#include "mpt/crypto/exception.hpp"
#include "mpt/crypto/hash.hpp"
#include "mpt/crypto/jwk.hpp"
#include "mpt/detect/nlohmann_json.hpp"
#include "mpt/string/types.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <string>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace crypto {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/crypto")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
#if MPT_OS_WINDOWS
mpt::crypto::hash::SHA512::result_type sha512_abc{
std::byte{0xdd}, std::byte{0xaf}, std::byte{0x35}, std::byte{0xa1}, std::byte{0x93}, std::byte{0x61}, std::byte{0x7a}, std::byte{0xba},
std::byte{0xcc}, std::byte{0x41}, std::byte{0x73}, std::byte{0x49}, std::byte{0xae}, std::byte{0x20}, std::byte{0x41}, std::byte{0x31},
std::byte{0x12}, std::byte{0xe6}, std::byte{0xfa}, std::byte{0x4e}, std::byte{0x89}, std::byte{0xa9}, std::byte{0x7e}, std::byte{0xa2},
std::byte{0x0a}, std::byte{0x9e}, std::byte{0xee}, std::byte{0xe6}, std::byte{0x4b}, std::byte{0x55}, std::byte{0xd3}, std::byte{0x9a},
std::byte{0x21}, std::byte{0x92}, std::byte{0x99}, std::byte{0x2a}, std::byte{0x27}, std::byte{0x4f}, std::byte{0xc1}, std::byte{0xa8},
std::byte{0x36}, std::byte{0xba}, std::byte{0x3c}, std::byte{0x23}, std::byte{0xa3}, std::byte{0xfe}, std::byte{0xeb}, std::byte{0xbd},
std::byte{0x45}, std::byte{0x4d}, std::byte{0x44}, std::byte{0x23}, std::byte{0x64}, std::byte{0x3c}, std::byte{0xe8}, std::byte{0x0e},
std::byte{0x2a}, std::byte{0x9a}, std::byte{0xc9}, std::byte{0x4f}, std::byte{0xa5}, std::byte{0x4c}, std::byte{0xa4}, std::byte{0x9f}};
MPT_TEST_EXPECT_EQUAL(mpt::crypto::hash::SHA512().process(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(std::string("abc")))).result(), sha512_abc);
#endif // MPT_OS_WINDOWS
#if MPT_OS_WINDOWS && MPT_DETECTED_NLOHMANN_JSON
{
std::vector<std::byte> data = {std::byte{0x11}, std::byte{0x12}, std::byte{0x13}, std::byte{0x14}};
mpt::crypto::keystore keystore(mpt::crypto::keystore::domain::user);
mpt::crypto::asymmetric::rsassa_pss<>::managed_private_key key(keystore, U_("OpenMPT Test Key 1"));
auto publickeydata = key.get_public_key_data();
mpt::crypto::asymmetric::rsassa_pss<>::public_key pk{publickeydata};
mpt::crypto::asymmetric::rsassa_pss<>::public_key pk_copy{pk};
mpt::ustring jwk = publickeydata.as_jwk();
std::vector<std::byte> signature = key.sign(mpt::as_span(data));
mpt::ustring jws = key.jws_sign(mpt::as_span(data));
mpt::ustring jws_compact = key.jws_compact_sign(mpt::as_span(data));
try {
pk.verify(mpt::as_span(data), signature);
auto verifieddata1 = mpt::crypto::asymmetric::rsassa_pss<>::public_key(mpt::crypto::asymmetric::rsassa_pss<>::public_key_data::from_jwk(jwk)).jws_verify(jws);
auto verifieddata2 = mpt::crypto::asymmetric::rsassa_pss<>::public_key(mpt::crypto::asymmetric::rsassa_pss<>::public_key_data::from_jwk(jwk)).jws_compact_verify(jws_compact);
MPT_TEST_EXPECT_EQUAL(true, true);
MPT_TEST_EXPECT_EQUAL(data, verifieddata1);
MPT_TEST_EXPECT_EQUAL(data, verifieddata2);
} catch (const mpt::crypto::asymmetric::signature_verification_failed &) {
MPT_TEST_EXPECT_EQUAL(true, false);
}
key.destroy();
}
#endif // MPT_OS_WINDOWS && MPT_DETECTED_NLOHMANN_JSON
}
} // namespace crypto
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_BASE_TESTS_CRYPTO_HPP
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_DETECT_DL_HPP
#define MPT_DETECT_DL_HPP
#include "mpt/base/compiletime_warning.hpp"
#if defined(MPT_WITH_DL)
#if !defined(CPPCHECK)
#if !__has_include(<dlfcn.h>)
#error "MPT_WITH_DL defined but <dlfcn.h> not found."
#endif
#endif
#define MPT_DETECTED_DL 1
#else
#if defined(CPPCHECK)
#define MPT_DETECTED_DL 1
#else
#if __has_include(<dlfcn.h>)
#define MPT_DETECTED_DL 1
#else
#define MPT_DETECTED_DL 0
#endif
#endif
#endif
#endif // MPT_DETECT_DL_HPP
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_DETECT_LTDL_HPP
#define MPT_DETECT_LTDL_HPP
#include "mpt/base/compiletime_warning.hpp"
#if defined(MPT_WITH_LTDL)
#if !defined(CPPCHECK)
#if !__has_include(<ltdl.h>)
#error "MPT_WITH_LTDL defined but <ltdl.h> not found."
#endif
#endif
#define MPT_DETECTED_LTDL 1
#else
#if defined(CPPCHECK)
#define MPT_DETECTED_LTDL 1
#else
#if __has_include(<ltdl.h>)
#define MPT_DETECTED_LTDL 1
#else
#define MPT_DETECTED_LTDL 0
#endif
#endif
#endif
#endif // MPT_DETECT_LTDL_HPP
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_DETECT_MFC_HPP
#define MPT_DETECT_MFC_HPP
#include "mpt/base/compiletime_warning.hpp"
#if defined(MPT_WITH_MFC)
#if !defined(CPPCHECK)
#if !__has_include(<afx.h>)
#error "MPT_WITH_MFC defined but <afx.h> not found."
#endif
#endif
#if !MPT_COMPILER_GENERIC && !MPT_COMPILER_MSVC && !MPT_COMPILER_CLANG
MPT_WARNING("Using MFC with unsupported compiler.")
#endif
#define MPT_DETECTED_MFC 1
#else
#define MPT_DETECTED_MFC 0
#endif
#endif // MPT_DETECT_MFC_HPP
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_DETECT_NLOHMANN_JSON_HPP
#define MPT_DETECT_NLOHMANN_JSON_HPP
#if defined(MPT_WITH_NLOHMANN_JSON)
#if !defined(CPPCHECK)
#if !__has_include(<nlohmann/json.hpp>)
#error "MPT_WITH_NLOHMANN_JSON defined but <nlohmann/json.hpp> not found."
#endif
#endif
#define MPT_DETECTED_NLOHMANN_JSON 1
#else
#if defined(CPPCHECK)
#define MPT_DETECTED_NLOHMANN_JSON 1
#else
#if __has_include(<nlohmann/json.hpp>)
#define MPT_DETECTED_NLOHMANN_JSON 1
#else
#define MPT_DETECTED_NLOHMANN_JSON 0
#endif
#endif
#endif
#endif // MPT_DETECT_NLOHMANN_JSON_HPP
@@ -0,0 +1,485 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_ENDIAN_FLOATINGPOINT_HPP
#define MPT_ENDIAN_FLOATINGPOINT_HPP
#include "mpt/base/bit.hpp"
#include "mpt/base/floatingpoint.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include <limits>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
namespace mpt {
inline namespace MPT_INLINE_NS {
// 1.0f --> 0x3f800000u
MPT_FORCEINLINE uint32 EncodeIEEE754binary32(float32 f) {
if constexpr (mpt::float_traits<float32>::is_ieee754_binary32ne) {
return mpt::bit_cast<uint32>(f);
} else {
int e = 0;
float m = std::frexp(f, &e);
if (e == 0 && std::fabs(m) == 0.0f) {
uint32 expo = 0u;
uint32 sign = std::signbit(m) ? 0x01u : 0x00u;
uint32 mant = 0u;
uint32 i = 0u;
i |= (mant << 0) & 0x007fffffu;
i |= (expo << 23) & 0x7f800000u;
i |= (sign << 31) & 0x80000000u;
return i;
} else {
uint32 expo = e + 127 - 1;
uint32 sign = std::signbit(m) ? 0x01u : 0x00u;
uint32 mant = static_cast<uint32>(std::fabs(std::ldexp(m, 24)));
uint32 i = 0u;
i |= (mant << 0) & 0x007fffffu;
i |= (expo << 23) & 0x7f800000u;
i |= (sign << 31) & 0x80000000u;
return i;
}
}
}
MPT_FORCEINLINE uint64 EncodeIEEE754binary64(float64 f) {
if constexpr (mpt::float_traits<float64>::is_ieee754_binary64ne) {
return mpt::bit_cast<uint64>(f);
} else {
int e = 0;
double m = std::frexp(f, &e);
if (e == 0 && std::fabs(m) == 0.0) {
uint64 expo = 0u;
uint64 sign = std::signbit(m) ? 0x01u : 0x00u;
uint64 mant = 0u;
uint64 i = 0u;
i |= (mant << 0) & 0x000fffffffffffffull;
i |= (expo << 52) & 0x7ff0000000000000ull;
i |= (sign << 63) & 0x8000000000000000ull;
return i;
} else {
uint64 expo = static_cast<int64>(e) + 1023 - 1;
uint64 sign = std::signbit(m) ? 0x01u : 0x00u;
uint64 mant = static_cast<uint64>(std::fabs(std::ldexp(m, 53)));
uint64 i = 0u;
i |= (mant << 0) & 0x000fffffffffffffull;
i |= (expo << 52) & 0x7ff0000000000000ull;
i |= (sign << 63) & 0x8000000000000000ull;
return i;
}
}
}
// 0x3f800000u --> 1.0f
MPT_FORCEINLINE float32 DecodeIEEE754binary32(uint32 i) {
if constexpr (mpt::float_traits<float32>::is_ieee754_binary32ne) {
return mpt::bit_cast<float32>(i);
} else {
uint32 mant = (i & 0x007fffffu) >> 0;
uint32 expo = (i & 0x7f800000u) >> 23;
uint32 sign = (i & 0x80000000u) >> 31;
if (expo == 0) {
float m = sign ? -static_cast<float>(mant) : static_cast<float>(mant);
int e = static_cast<int>(expo) - 127 + 1 - 24;
float f = std::ldexp(m, e);
return static_cast<float32>(f);
} else {
mant |= 0x00800000u;
float m = sign ? -static_cast<float>(mant) : static_cast<float>(mant);
int e = static_cast<int>(expo) - 127 + 1 - 24;
float f = std::ldexp(m, e);
return static_cast<float32>(f);
}
}
}
MPT_FORCEINLINE float64 DecodeIEEE754binary64(uint64 i) {
if constexpr (mpt::float_traits<float64>::is_ieee754_binary64ne) {
return mpt::bit_cast<float64>(i);
} else {
uint64 mant = (i & 0x000fffffffffffffull) >> 0;
uint64 expo = (i & 0x7ff0000000000000ull) >> 52;
uint64 sign = (i & 0x8000000000000000ull) >> 63;
if (expo == 0) {
double m = sign ? -static_cast<double>(mant) : static_cast<double>(mant);
int e = static_cast<int>(expo) - 1023 + 1 - 53;
double f = std::ldexp(m, e);
return static_cast<float64>(f);
} else {
mant |= 0x0010000000000000ull;
double m = sign ? -static_cast<double>(mant) : static_cast<double>(mant);
int e = static_cast<int>(expo) - 1023 + 1 - 53;
double f = std::ldexp(m, e);
return static_cast<float64>(f);
}
}
}
// template parameters are byte indices corresponding to the individual bytes of iee754 in memory
template <std::size_t hihi, std::size_t hilo, std::size_t lohi, std::size_t lolo>
struct IEEE754binary32Emulated {
public:
using self_t = IEEE754binary32Emulated<hihi, hilo, lohi, lolo>;
std::byte bytes[4];
public:
MPT_FORCEINLINE std::byte GetByte(std::size_t i) const {
return bytes[i];
}
IEEE754binary32Emulated() = default;
MPT_FORCEINLINE explicit IEEE754binary32Emulated(float32 f) {
SetInt32(EncodeIEEE754binary32(f));
}
// b0...b3 are in memory order, i.e. depend on the endianness of this type
// little endian: (0x00,0x00,0x80,0x3f)
// big endian: (0x3f,0x80,0x00,0x00)
MPT_FORCEINLINE explicit IEEE754binary32Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3) {
bytes[0] = b0;
bytes[1] = b1;
bytes[2] = b2;
bytes[3] = b3;
}
MPT_FORCEINLINE operator float32() const {
return DecodeIEEE754binary32(GetInt32());
}
MPT_FORCEINLINE self_t & SetInt32(uint32 i) {
bytes[hihi] = static_cast<std::byte>(i >> 24);
bytes[hilo] = static_cast<std::byte>(i >> 16);
bytes[lohi] = static_cast<std::byte>(i >> 8);
bytes[lolo] = static_cast<std::byte>(i >> 0);
return *this;
}
MPT_FORCEINLINE uint32 GetInt32() const {
return 0u
| (static_cast<uint32>(bytes[hihi]) << 24)
| (static_cast<uint32>(bytes[hilo]) << 16)
| (static_cast<uint32>(bytes[lohi]) << 8)
| (static_cast<uint32>(bytes[lolo]) << 0);
}
MPT_FORCEINLINE bool operator==(const self_t & cmp) const {
return true
&& bytes[0] == cmp.bytes[0]
&& bytes[1] == cmp.bytes[1]
&& bytes[2] == cmp.bytes[2]
&& bytes[3] == cmp.bytes[3];
}
MPT_FORCEINLINE bool operator!=(const self_t & cmp) const {
return !(*this == cmp);
}
};
template <std::size_t hihihi, std::size_t hihilo, std::size_t hilohi, std::size_t hilolo, std::size_t lohihi, std::size_t lohilo, std::size_t lolohi, std::size_t lololo>
struct IEEE754binary64Emulated {
public:
using self_t = IEEE754binary64Emulated<hihihi, hihilo, hilohi, hilolo, lohihi, lohilo, lolohi, lololo>;
std::byte bytes[8];
public:
MPT_FORCEINLINE std::byte GetByte(std::size_t i) const {
return bytes[i];
}
IEEE754binary64Emulated() = default;
MPT_FORCEINLINE explicit IEEE754binary64Emulated(float64 f) {
SetInt64(EncodeIEEE754binary64(f));
}
MPT_FORCEINLINE explicit IEEE754binary64Emulated(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) {
bytes[0] = b0;
bytes[1] = b1;
bytes[2] = b2;
bytes[3] = b3;
bytes[4] = b4;
bytes[5] = b5;
bytes[6] = b6;
bytes[7] = b7;
}
MPT_FORCEINLINE operator float64() const {
return DecodeIEEE754binary64(GetInt64());
}
MPT_FORCEINLINE self_t & SetInt64(uint64 i) {
bytes[hihihi] = static_cast<std::byte>(i >> 56);
bytes[hihilo] = static_cast<std::byte>(i >> 48);
bytes[hilohi] = static_cast<std::byte>(i >> 40);
bytes[hilolo] = static_cast<std::byte>(i >> 32);
bytes[lohihi] = static_cast<std::byte>(i >> 24);
bytes[lohilo] = static_cast<std::byte>(i >> 16);
bytes[lolohi] = static_cast<std::byte>(i >> 8);
bytes[lololo] = static_cast<std::byte>(i >> 0);
return *this;
}
MPT_FORCEINLINE uint64 GetInt64() const {
return 0u
| (static_cast<uint64>(bytes[hihihi]) << 56)
| (static_cast<uint64>(bytes[hihilo]) << 48)
| (static_cast<uint64>(bytes[hilohi]) << 40)
| (static_cast<uint64>(bytes[hilolo]) << 32)
| (static_cast<uint64>(bytes[lohihi]) << 24)
| (static_cast<uint64>(bytes[lohilo]) << 16)
| (static_cast<uint64>(bytes[lolohi]) << 8)
| (static_cast<uint64>(bytes[lololo]) << 0);
}
MPT_FORCEINLINE bool operator==(const self_t & cmp) const {
return true
&& bytes[0] == cmp.bytes[0]
&& bytes[1] == cmp.bytes[1]
&& bytes[2] == cmp.bytes[2]
&& bytes[3] == cmp.bytes[3]
&& bytes[4] == cmp.bytes[4]
&& bytes[5] == cmp.bytes[5]
&& bytes[6] == cmp.bytes[6]
&& bytes[7] == cmp.bytes[7];
}
MPT_FORCEINLINE bool operator!=(const self_t & cmp) const {
return !(*this == cmp);
}
};
using IEEE754binary32EmulatedBE = IEEE754binary32Emulated<0, 1, 2, 3>;
using IEEE754binary32EmulatedLE = IEEE754binary32Emulated<3, 2, 1, 0>;
using IEEE754binary64EmulatedBE = IEEE754binary64Emulated<0, 1, 2, 3, 4, 5, 6, 7>;
using IEEE754binary64EmulatedLE = IEEE754binary64Emulated<7, 6, 5, 4, 3, 2, 1, 0>;
constexpr bool declare_binary_safe(const IEEE754binary32EmulatedBE &) {
return true;
}
constexpr bool declare_binary_safe(const IEEE754binary32EmulatedLE &) {
return true;
}
constexpr bool declare_binary_safe(const IEEE754binary64EmulatedBE &) {
return true;
}
constexpr bool declare_binary_safe(const IEEE754binary64EmulatedLE &) {
return true;
}
static_assert(mpt::check_binary_size<IEEE754binary32EmulatedBE>(4));
static_assert(mpt::check_binary_size<IEEE754binary32EmulatedLE>(4));
static_assert(mpt::check_binary_size<IEEE754binary64EmulatedBE>(8));
static_assert(mpt::check_binary_size<IEEE754binary64EmulatedLE>(8));
template <mpt::endian endian = mpt::endian::native>
struct IEEE754binary32Native {
public:
float32 value;
public:
MPT_FORCEINLINE std::byte GetByte(std::size_t i) const {
static_assert(endian == mpt::endian::little || endian == mpt::endian::big);
if constexpr (endian == mpt::endian::little) {
return static_cast<std::byte>(EncodeIEEE754binary32(value) >> (i * 8));
}
if constexpr (endian == mpt::endian::big) {
return static_cast<std::byte>(EncodeIEEE754binary32(value) >> ((4 - 1 - i) * 8));
}
}
IEEE754binary32Native() = default;
MPT_FORCEINLINE explicit IEEE754binary32Native(float32 f) {
value = f;
}
// b0...b3 are in memory order, i.e. depend on the endianness of this type
// little endian: (0x00,0x00,0x80,0x3f)
// big endian: (0x3f,0x80,0x00,0x00)
MPT_FORCEINLINE explicit IEEE754binary32Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3) {
static_assert(endian == mpt::endian::little || endian == mpt::endian::big);
if constexpr (endian == mpt::endian::little) {
value = DecodeIEEE754binary32(0u | (static_cast<uint32>(b0) << 0) | (static_cast<uint32>(b1) << 8) | (static_cast<uint32>(b2) << 16) | (static_cast<uint32>(b3) << 24));
}
if constexpr (endian == mpt::endian::big) {
value = DecodeIEEE754binary32(0u | (static_cast<uint32>(b0) << 24) | (static_cast<uint32>(b1) << 16) | (static_cast<uint32>(b2) << 8) | (static_cast<uint32>(b3) << 0));
}
}
MPT_FORCEINLINE operator float32() const {
return value;
}
MPT_FORCEINLINE IEEE754binary32Native & SetInt32(uint32 i) {
value = DecodeIEEE754binary32(i);
return *this;
}
MPT_FORCEINLINE uint32 GetInt32() const {
return EncodeIEEE754binary32(value);
}
MPT_FORCEINLINE bool operator==(const IEEE754binary32Native & cmp) const {
return value == cmp.value;
}
MPT_FORCEINLINE bool operator!=(const IEEE754binary32Native & cmp) const {
return value != cmp.value;
}
};
template <mpt::endian endian = mpt::endian::native>
struct IEEE754binary64Native {
public:
float64 value;
public:
MPT_FORCEINLINE std::byte GetByte(std::size_t i) const {
static_assert(endian == mpt::endian::little || endian == mpt::endian::big);
if constexpr (endian == mpt::endian::little) {
return mpt::byte_cast<std::byte>(static_cast<uint8>(EncodeIEEE754binary64(value) >> (i * 8)));
}
if constexpr (endian == mpt::endian::big) {
return mpt::byte_cast<std::byte>(static_cast<uint8>(EncodeIEEE754binary64(value) >> ((8 - 1 - i) * 8)));
}
}
IEEE754binary64Native() = default;
MPT_FORCEINLINE explicit IEEE754binary64Native(float64 f) {
value = f;
}
MPT_FORCEINLINE explicit IEEE754binary64Native(std::byte b0, std::byte b1, std::byte b2, std::byte b3, std::byte b4, std::byte b5, std::byte b6, std::byte b7) {
static_assert(endian == mpt::endian::little || endian == mpt::endian::big);
if constexpr (endian == mpt::endian::little) {
value = DecodeIEEE754binary64(0ull | (static_cast<uint64>(b0) << 0) | (static_cast<uint64>(b1) << 8) | (static_cast<uint64>(b2) << 16) | (static_cast<uint64>(b3) << 24) | (static_cast<uint64>(b4) << 32) | (static_cast<uint64>(b5) << 40) | (static_cast<uint64>(b6) << 48) | (static_cast<uint64>(b7) << 56));
}
if constexpr (endian == mpt::endian::big) {
value = DecodeIEEE754binary64(0ull | (static_cast<uint64>(b0) << 56) | (static_cast<uint64>(b1) << 48) | (static_cast<uint64>(b2) << 40) | (static_cast<uint64>(b3) << 32) | (static_cast<uint64>(b4) << 24) | (static_cast<uint64>(b5) << 16) | (static_cast<uint64>(b6) << 8) | (static_cast<uint64>(b7) << 0));
}
}
MPT_FORCEINLINE operator float64() const {
return value;
}
MPT_FORCEINLINE IEEE754binary64Native & SetInt64(uint64 i) {
value = DecodeIEEE754binary64(i);
return *this;
}
MPT_FORCEINLINE uint64 GetInt64() const {
return EncodeIEEE754binary64(value);
}
MPT_FORCEINLINE bool operator==(const IEEE754binary64Native & cmp) const {
return value == cmp.value;
}
MPT_FORCEINLINE bool operator!=(const IEEE754binary64Native & cmp) const {
return value != cmp.value;
}
};
static_assert((sizeof(IEEE754binary32Native<>) == 4));
static_assert((sizeof(IEEE754binary64Native<>) == 8));
constexpr bool declare_binary_safe(const IEEE754binary32Native<> &) noexcept {
return true;
}
constexpr bool declare_binary_safe(const IEEE754binary64Native<> &) noexcept {
return true;
}
template <bool is_ieee754, mpt::endian endian = mpt::endian::native>
struct IEEE754binary_types {
using IEEE754binary32LE = IEEE754binary32EmulatedLE;
using IEEE754binary32BE = IEEE754binary32EmulatedBE;
using IEEE754binary64LE = IEEE754binary64EmulatedLE;
using IEEE754binary64BE = IEEE754binary64EmulatedBE;
};
template <>
struct IEEE754binary_types<true, mpt::endian::little> {
using IEEE754binary32LE = IEEE754binary32Native<>;
using IEEE754binary32BE = IEEE754binary32EmulatedBE;
using IEEE754binary64LE = IEEE754binary64Native<>;
using IEEE754binary64BE = IEEE754binary64EmulatedBE;
};
template <>
struct IEEE754binary_types<true, mpt::endian::big> {
using IEEE754binary32LE = IEEE754binary32EmulatedLE;
using IEEE754binary32BE = IEEE754binary32Native<>;
using IEEE754binary64LE = IEEE754binary64EmulatedLE;
using IEEE754binary64BE = IEEE754binary64Native<>;
};
using IEEE754binary32LE = IEEE754binary_types<mpt::float_traits<float32>::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32LE;
using IEEE754binary32BE = IEEE754binary_types<mpt::float_traits<float32>::is_ieee754_binary32ne, mpt::endian::native>::IEEE754binary32BE;
using IEEE754binary64LE = IEEE754binary_types<mpt::float_traits<float64>::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64LE;
using IEEE754binary64BE = IEEE754binary_types<mpt::float_traits<float64>::is_ieee754_binary64ne, mpt::endian::native>::IEEE754binary64BE;
static_assert(sizeof(IEEE754binary32LE) == 4);
static_assert(sizeof(IEEE754binary32BE) == 4);
static_assert(sizeof(IEEE754binary64LE) == 8);
static_assert(sizeof(IEEE754binary64BE) == 8);
// unaligned
using float32le = IEEE754binary32EmulatedLE;
using float32be = IEEE754binary32EmulatedBE;
using float64le = IEEE754binary64EmulatedLE;
using float64be = IEEE754binary64EmulatedBE;
static_assert(sizeof(float32le) == 4);
static_assert(sizeof(float32be) == 4);
static_assert(sizeof(float64le) == 8);
static_assert(sizeof(float64be) == 8);
// potentially aligned
using float32le_fast = IEEE754binary32LE;
using float32be_fast = IEEE754binary32BE;
using float64le_fast = IEEE754binary64LE;
using float64be_fast = IEEE754binary64BE;
static_assert(sizeof(float32le_fast) == 4);
static_assert(sizeof(float32be_fast) == 4);
static_assert(sizeof(float64le_fast) == 8);
static_assert(sizeof(float64be_fast) == 8);
template <typename Tfloat>
struct make_float_be {
};
template <>
struct make_float_be<double> {
using type = IEEE754binary64BE;
};
template <>
struct make_float_be<float> {
using type = IEEE754binary32BE;
};
template <typename Tfloat>
struct make_float_le {
};
template <>
struct make_float_le<double> {
using type = IEEE754binary64LE;
};
template <>
struct make_float_le<float> {
using type = IEEE754binary32LE;
};
template <mpt::endian endian, typename T>
struct make_float_endian {
};
template <typename T>
struct make_float_endian<mpt::endian::little, T> {
using type = typename make_float_le<typename std::remove_const<T>::type>::type;
};
template <typename T>
struct make_float_endian<mpt::endian::big, T> {
using type = typename make_float_be<typename std::remove_const<T>::type>::type;
};
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_ENDIAN_FLOATINGPOINT_HPP
@@ -0,0 +1,122 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_ENDIAN_INT24_HPP
#define MPT_ENDIAN_INT24_HPP
#include "mpt/base/bit.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include <array>
#include <limits>
#include <type_traits>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
struct uint24 {
std::array<std::byte, 3> bytes;
uint24() = default;
template <typename T, typename std::enable_if<std::is_integral<T>::value, bool>::type = true>
explicit uint24(T other) noexcept {
using Tunsigned = typename std::make_unsigned<T>::type;
MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
bytes[0] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 16) & 0xff));
bytes[1] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 8) & 0xff));
bytes[2] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 0) & 0xff));
} else {
bytes[0] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 0) & 0xff));
bytes[1] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 8) & 0xff));
bytes[2] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 16) & 0xff));
}
}
operator int() const noexcept {
MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
return (mpt::byte_cast<uint8>(bytes[0]) * 65536) + (mpt::byte_cast<uint8>(bytes[1]) * 256) + mpt::byte_cast<uint8>(bytes[2]);
} else {
return (mpt::byte_cast<uint8>(bytes[2]) * 65536) + (mpt::byte_cast<uint8>(bytes[1]) * 256) + mpt::byte_cast<uint8>(bytes[0]);
}
}
};
static_assert(sizeof(uint24) == 3);
struct int24 {
std::array<std::byte, 3> bytes;
int24() = default;
template <typename T, typename std::enable_if<std::is_integral<T>::value, bool>::type = true>
explicit int24(T other) noexcept {
using Tunsigned = typename std::make_unsigned<T>::type;
MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
bytes[0] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 16) & 0xff));
bytes[1] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 8) & 0xff));
bytes[2] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 0) & 0xff));
} else {
bytes[0] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 0) & 0xff));
bytes[1] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 8) & 0xff));
bytes[2] = mpt::byte_cast<std::byte>(static_cast<uint8>((static_cast<Tunsigned>(other) >> 16) & 0xff));
}
}
operator int() const noexcept {
MPT_MAYBE_CONSTANT_IF (mpt::endian_is_big()) {
return (static_cast<int8>(mpt::byte_cast<uint8>(bytes[0])) * 65536) + (mpt::byte_cast<uint8>(bytes[1]) * 256) + mpt::byte_cast<uint8>(bytes[2]);
} else {
return (static_cast<int8>(mpt::byte_cast<uint8>(bytes[2])) * 65536) + (mpt::byte_cast<uint8>(bytes[1]) * 256) + mpt::byte_cast<uint8>(bytes[0]);
}
}
};
static_assert(sizeof(int24) == 3);
} // namespace MPT_INLINE_NS
} // namespace mpt
#if !defined(CPPCHECK)
// work-around crash in cppcheck 2.4.1
namespace std {
template <>
class numeric_limits<mpt::uint24> : public std::numeric_limits<mpt::uint32> {
public:
static constexpr mpt::uint32 min() noexcept {
return 0;
}
static constexpr mpt::uint32 lowest() noexcept {
return 0;
}
static constexpr mpt::uint32 max() noexcept {
return 0x00ffffff;
}
};
template <>
class numeric_limits<mpt::int24> : public std::numeric_limits<mpt::int32> {
public:
static constexpr mpt::int32 min() noexcept {
return 0 - 0x00800000;
}
static constexpr mpt::int32 lowest() noexcept {
return 0 - 0x00800000;
}
static constexpr mpt::int32 max() noexcept {
return 0 + 0x007fffff;
}
};
} // namespace std
#endif // !CPPCHECK
#endif // MPT_ENDIAN_INT24_HPP
@@ -0,0 +1,493 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_ENDIAN_INTEGER_HPP
#define MPT_ENDIAN_INTEGER_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/bit.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include <array>
#include <limits>
#include <type_traits>
#include <cstddef>
#include <cstdint>
#include <cstring>
#if MPT_COMPILER_MSVC
#include <intrin.h>
#endif
namespace mpt {
inline namespace MPT_INLINE_NS {
struct BigEndian_tag {
static constexpr mpt::endian endian = mpt::endian::big;
};
struct LittleEndian_tag {
static constexpr mpt::endian endian = mpt::endian::little;
};
constexpr inline uint16 constexpr_bswap16(uint16 x) noexcept {
return uint16(0)
| ((x >> 8) & 0x00FFu)
| ((x << 8) & 0xFF00u);
}
constexpr inline uint32 constexpr_bswap32(uint32 x) noexcept {
return uint32(0)
| ((x & 0x000000FFu) << 24)
| ((x & 0x0000FF00u) << 8)
| ((x & 0x00FF0000u) >> 8)
| ((x & 0xFF000000u) >> 24);
}
constexpr inline uint64 constexpr_bswap64(uint64 x) noexcept {
return uint64(0)
| (((x >> 0) & 0xffull) << 56)
| (((x >> 8) & 0xffull) << 48)
| (((x >> 16) & 0xffull) << 40)
| (((x >> 24) & 0xffull) << 32)
| (((x >> 32) & 0xffull) << 24)
| (((x >> 40) & 0xffull) << 16)
| (((x >> 48) & 0xffull) << 8)
| (((x >> 56) & 0xffull) << 0);
}
#if MPT_COMPILER_GCC
#define MPT_bswap16 __builtin_bswap16
#define MPT_bswap32 __builtin_bswap32
#define MPT_bswap64 __builtin_bswap64
#elif MPT_COMPILER_MSVC
#define MPT_bswap16 _byteswap_ushort
#define MPT_bswap32 _byteswap_ulong
#define MPT_bswap64 _byteswap_uint64
#endif
// No intrinsics available
#ifndef MPT_bswap16
#define MPT_bswap16(x) mpt::constexpr_bswap16(x)
#endif
#ifndef MPT_bswap32
#define MPT_bswap32(x) mpt::constexpr_bswap32(x)
#endif
#ifndef MPT_bswap64
#define MPT_bswap64(x) mpt::constexpr_bswap64(x)
#endif
template <typename T, typename Tendian, std::size_t size>
MPT_CONSTEXPRINLINE std::array<std::byte, size> EndianEncode(T val) noexcept {
static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big);
static_assert(std::numeric_limits<T>::is_integer);
static_assert(!std::numeric_limits<T>::is_signed);
static_assert(sizeof(T) == size);
using base_type = T;
using unsigned_base_type = typename std::make_unsigned<base_type>::type;
using endian_type = Tendian;
unsigned_base_type uval = static_cast<unsigned_base_type>(val);
std::array<std::byte, size> data{};
if constexpr (endian_type::endian == mpt::endian::little) {
for (std::size_t i = 0; i < sizeof(base_type); ++i) {
data[i] = static_cast<std::byte>(static_cast<uint8>((uval >> (i * 8)) & 0xffu));
}
} else {
for (std::size_t i = 0; i < sizeof(base_type); ++i) {
data[(sizeof(base_type) - 1) - i] = static_cast<std::byte>(static_cast<uint8>((uval >> (i * 8)) & 0xffu));
}
}
return data;
}
template <typename T, typename Tendian, std::size_t size>
MPT_CONSTEXPRINLINE T EndianDecode(std::array<std::byte, size> data) noexcept {
static_assert(Tendian::endian == mpt::endian::little || Tendian::endian == mpt::endian::big);
static_assert(std::numeric_limits<T>::is_integer);
static_assert(!std::numeric_limits<T>::is_signed);
static_assert(sizeof(T) == size);
using base_type = T;
using unsigned_base_type = typename std::make_unsigned<base_type>::type;
using endian_type = Tendian;
base_type val = base_type();
unsigned_base_type uval = unsigned_base_type();
if constexpr (endian_type::endian == mpt::endian::little) {
for (std::size_t i = 0; i < sizeof(base_type); ++i) {
uval |= static_cast<unsigned_base_type>(static_cast<uint8>(data[i])) << (i * 8);
}
} else {
for (std::size_t i = 0; i < sizeof(base_type); ++i) {
uval |= static_cast<unsigned_base_type>(static_cast<uint8>(data[(sizeof(base_type) - 1) - i])) << (i * 8);
}
}
val = static_cast<base_type>(uval);
return val;
}
MPT_CONSTEXPR20_FUN uint64 SwapBytesImpl(uint64 value) noexcept {
MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
return mpt::constexpr_bswap64(value);
} else {
return MPT_bswap64(value);
}
}
MPT_CONSTEXPR20_FUN uint32 SwapBytesImpl(uint32 value) noexcept {
MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
return mpt::constexpr_bswap32(value);
} else {
return MPT_bswap32(value);
}
}
MPT_CONSTEXPR20_FUN uint16 SwapBytesImpl(uint16 value) noexcept {
MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
return mpt::constexpr_bswap16(value);
} else {
return MPT_bswap16(value);
}
}
MPT_CONSTEXPR20_FUN int64 SwapBytesImpl(int64 value) noexcept {
MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
return mpt::constexpr_bswap64(value);
} else {
return MPT_bswap64(value);
}
}
MPT_CONSTEXPR20_FUN int32 SwapBytesImpl(int32 value) noexcept {
MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
return mpt::constexpr_bswap32(value);
} else {
return MPT_bswap32(value);
}
}
MPT_CONSTEXPR20_FUN int16 SwapBytesImpl(int16 value) noexcept {
MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
return mpt::constexpr_bswap16(value);
} else {
return MPT_bswap16(value);
}
}
// Do NOT remove these overloads, even if they seem useless.
// We do not want risking to extend 8bit integers to int and then
// endian-converting and casting back to int.
// Thus these overloads.
MPT_CONSTEXPR20_FUN uint8 SwapBytesImpl(uint8 value) noexcept {
return value;
}
MPT_CONSTEXPR20_FUN int8 SwapBytesImpl(int8 value) noexcept {
return value;
}
MPT_CONSTEXPR20_FUN char SwapBytesImpl(char value) noexcept {
return value;
}
#undef MPT_bswap16
#undef MPT_bswap32
#undef MPT_bswap64
// On-disk integer types with defined endianness and no alignemnt requirements
// Note: To easily debug module loaders (and anything else that uses this
// wrapper struct), you can use the Debugger Visualizers available in
// build/vs/debug/ to conveniently view the wrapped contents.
template <typename T, typename Tendian>
struct packed {
public:
using base_type = T;
using endian_type = Tendian;
public:
std::array<std::byte, sizeof(base_type)> data;
public:
MPT_CONSTEXPR20_FUN void set(base_type val) noexcept {
static_assert(std::numeric_limits<T>::is_integer);
MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
if constexpr (endian_type::endian == mpt::endian::big) {
typename std::make_unsigned<base_type>::type uval = val;
for (std::size_t i = 0; i < sizeof(base_type); ++i) {
data[i] = static_cast<std::byte>((uval >> (8 * (sizeof(base_type) - 1 - i))) & 0xffu);
}
} else {
typename std::make_unsigned<base_type>::type uval = val;
for (std::size_t i = 0; i < sizeof(base_type); ++i) {
data[i] = static_cast<std::byte>((uval >> (8 * i)) & 0xffu);
}
}
} else {
if constexpr (mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) {
if constexpr (mpt::endian::native != endian_type::endian) {
val = mpt::SwapBytesImpl(val);
}
std::memcpy(data.data(), &val, sizeof(val));
} else {
using unsigned_base_type = typename std::make_unsigned<base_type>::type;
data = EndianEncode<unsigned_base_type, Tendian, sizeof(T)>(val);
}
}
}
MPT_CONSTEXPR20_FUN base_type get() const noexcept {
static_assert(std::numeric_limits<T>::is_integer);
MPT_MAYBE_CONSTANT_IF (MPT_IS_CONSTANT_EVALUATED20()) {
if constexpr (endian_type::endian == mpt::endian::big) {
typename std::make_unsigned<base_type>::type uval = 0;
for (std::size_t i = 0; i < sizeof(base_type); ++i) {
uval |= static_cast<typename std::make_unsigned<base_type>::type>(data[i]) << (8 * (sizeof(base_type) - 1 - i));
}
return static_cast<base_type>(uval);
} else {
typename std::make_unsigned<base_type>::type uval = 0;
for (std::size_t i = 0; i < sizeof(base_type); ++i) {
uval |= static_cast<typename std::make_unsigned<base_type>::type>(data[i]) << (8 * i);
}
return static_cast<base_type>(uval);
}
} else {
if constexpr (mpt::endian::native == mpt::endian::little || mpt::endian::native == mpt::endian::big) {
base_type val = base_type();
std::memcpy(&val, data.data(), sizeof(val));
if constexpr (mpt::endian::native != endian_type::endian) {
val = mpt::SwapBytesImpl(val);
}
return val;
} else {
using unsigned_base_type = typename std::make_unsigned<base_type>::type;
return EndianDecode<unsigned_base_type, Tendian, sizeof(T)>(data);
}
}
}
MPT_CONSTEXPR20_FUN packed & operator=(const base_type & val) noexcept {
set(val);
return *this;
}
MPT_CONSTEXPR20_FUN operator base_type() const noexcept {
return get();
}
public:
MPT_CONSTEXPR20_FUN packed & operator&=(base_type val) noexcept {
set(get() & val);
return *this;
}
MPT_CONSTEXPR20_FUN packed & operator|=(base_type val) noexcept {
set(get() | val);
return *this;
}
MPT_CONSTEXPR20_FUN packed & operator^=(base_type val) noexcept {
set(get() ^ val);
return *this;
}
MPT_CONSTEXPR20_FUN packed & operator+=(base_type val) noexcept {
set(get() + val);
return *this;
}
MPT_CONSTEXPR20_FUN packed & operator-=(base_type val) noexcept {
set(get() - val);
return *this;
}
MPT_CONSTEXPR20_FUN packed & operator*=(base_type val) noexcept {
set(get() * val);
return *this;
}
MPT_CONSTEXPR20_FUN packed & operator/=(base_type val) noexcept {
set(get() / val);
return *this;
}
MPT_CONSTEXPR20_FUN packed & operator%=(base_type val) noexcept {
set(get() % val);
return *this;
}
MPT_CONSTEXPR20_FUN packed & operator++() noexcept { // prefix
set(get() + 1);
return *this;
}
MPT_CONSTEXPR20_FUN packed & operator--() noexcept { // prefix
set(get() - 1);
return *this;
}
MPT_CONSTEXPR20_FUN base_type operator++(int) noexcept { // postfix
base_type old = get();
set(old + 1);
return old;
}
MPT_CONSTEXPR20_FUN base_type operator--(int) noexcept { // postfix
base_type old = get();
set(old - 1);
return old;
}
};
using int64le = packed<int64, LittleEndian_tag>;
using int32le = packed<int32, LittleEndian_tag>;
using int16le = packed<int16, LittleEndian_tag>;
using int8le = packed<int8, LittleEndian_tag>;
using uint64le = packed<uint64, LittleEndian_tag>;
using uint32le = packed<uint32, LittleEndian_tag>;
using uint16le = packed<uint16, LittleEndian_tag>;
using uint8le = packed<uint8, LittleEndian_tag>;
using int64be = packed<int64, BigEndian_tag>;
using int32be = packed<int32, BigEndian_tag>;
using int16be = packed<int16, BigEndian_tag>;
using int8be = packed<int8, BigEndian_tag>;
using uint64be = packed<uint64, BigEndian_tag>;
using uint32be = packed<uint32, BigEndian_tag>;
using uint16be = packed<uint16, BigEndian_tag>;
using uint8be = packed<uint8, BigEndian_tag>;
constexpr bool declare_binary_safe(const int64le &) {
return true;
}
constexpr bool declare_binary_safe(const int32le &) {
return true;
}
constexpr bool declare_binary_safe(const int16le &) {
return true;
}
constexpr bool declare_binary_safe(const int8le &) {
return true;
}
constexpr bool declare_binary_safe(const uint64le &) {
return true;
}
constexpr bool declare_binary_safe(const uint32le &) {
return true;
}
constexpr bool declare_binary_safe(const uint16le &) {
return true;
}
constexpr bool declare_binary_safe(const uint8le &) {
return true;
}
constexpr bool declare_binary_safe(const int64be &) {
return true;
}
constexpr bool declare_binary_safe(const int32be &) {
return true;
}
constexpr bool declare_binary_safe(const int16be &) {
return true;
}
constexpr bool declare_binary_safe(const int8be &) {
return true;
}
constexpr bool declare_binary_safe(const uint64be &) {
return true;
}
constexpr bool declare_binary_safe(const uint32be &) {
return true;
}
constexpr bool declare_binary_safe(const uint16be &) {
return true;
}
constexpr bool declare_binary_safe(const uint8be &) {
return true;
}
static_assert(mpt::check_binary_size<int64le>(8));
static_assert(mpt::check_binary_size<int32le>(4));
static_assert(mpt::check_binary_size<int16le>(2));
static_assert(mpt::check_binary_size<int8le>(1));
static_assert(mpt::check_binary_size<uint64le>(8));
static_assert(mpt::check_binary_size<uint32le>(4));
static_assert(mpt::check_binary_size<uint16le>(2));
static_assert(mpt::check_binary_size<uint8le>(1));
static_assert(mpt::check_binary_size<int64be>(8));
static_assert(mpt::check_binary_size<int32be>(4));
static_assert(mpt::check_binary_size<int16be>(2));
static_assert(mpt::check_binary_size<int8be>(1));
static_assert(mpt::check_binary_size<uint64be>(8));
static_assert(mpt::check_binary_size<uint32be>(4));
static_assert(mpt::check_binary_size<uint16be>(2));
static_assert(mpt::check_binary_size<uint8be>(1));
template <typename T>
struct make_le {
using type = packed<typename std::remove_const<T>::type, LittleEndian_tag>;
};
template <typename T>
struct make_be {
using type = packed<typename std::remove_const<T>::type, BigEndian_tag>;
};
template <mpt::endian endian, typename T>
struct make_endian {
};
template <typename T>
struct make_endian<mpt::endian::little, T> {
using type = packed<typename std::remove_const<T>::type, LittleEndian_tag>;
};
template <typename T>
struct make_endian<mpt::endian::big, T> {
using type = packed<typename std::remove_const<T>::type, BigEndian_tag>;
};
template <typename T>
MPT_CONSTEXPR20_FUN auto as_le(T v) noexcept -> typename mpt::make_le<typename std::remove_const<T>::type>::type {
typename mpt::make_le<typename std::remove_const<T>::type>::type res{};
res = v;
return res;
}
template <typename T>
MPT_CONSTEXPR20_FUN auto as_be(T v) noexcept -> typename mpt::make_be<typename std::remove_const<T>::type>::type {
typename mpt::make_be<typename std::remove_const<T>::type>::type res{};
res = v;
return res;
}
template <typename Tpacked>
MPT_CONSTEXPR20_FUN Tpacked as_endian(typename Tpacked::base_type v) noexcept {
Tpacked res{};
res = v;
return res;
}
} // namespace MPT_INLINE_NS
} // namespace mpt
namespace std {
template <typename T, typename Tendian>
class numeric_limits<mpt::packed<T, Tendian>> : public std::numeric_limits<T> { };
template <typename T, typename Tendian>
class numeric_limits<const mpt::packed<T, Tendian>> : public std::numeric_limits<const T> { };
} // namespace std
#endif // MPT_ENDIAN_INTEGER_HPP
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_ENDIAN_TESTS_ENDIAN_FLOATINGPOINT_HPP
#define MPT_ENDIAN_TESTS_ENDIAN_FLOATINGPOINT_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/endian/floatingpoint.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace endian {
namespace floatingpoint {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/endian/floatingpoint")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary32(1.0f), 0x3f800000u);
MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary32(-1.0f), 0xbf800000u);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x00000000u), 0.0f);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x41840000u), 16.5f);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x3faa0000u), 1.328125f);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0xbfaa0000u), -1.328125f);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x3f800000u), 1.0f);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x00000000u), 0.0f);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0xbf800000u), -1.0f);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary32(0x3f800000u), 1.0f);
MPT_TEST_EXPECT_EQUAL(IEEE754binary32LE(1.0f).GetInt32(), 0x3f800000u);
MPT_TEST_EXPECT_EQUAL(IEEE754binary32BE(1.0f).GetInt32(), 0x3f800000u);
MPT_TEST_EXPECT_EQUAL(IEEE754binary32LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x80), mpt::as_byte(0x3f)), 1.0f);
MPT_TEST_EXPECT_EQUAL(IEEE754binary32BE(mpt::as_byte(0x3f), mpt::as_byte(0x80), mpt::as_byte(0x00), mpt::as_byte(0x00)), 1.0f);
MPT_TEST_EXPECT_EQUAL(IEEE754binary32LE(1.0f), IEEE754binary32LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x80), mpt::as_byte(0x3f)));
MPT_TEST_EXPECT_EQUAL(IEEE754binary32BE(1.0f), IEEE754binary32BE(mpt::as_byte(0x3f), mpt::as_byte(0x80), mpt::as_byte(0x00), mpt::as_byte(0x00)));
MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary64(1.0), 0x3ff0000000000000ull);
MPT_TEST_EXPECT_EQUAL(mpt::EncodeIEEE754binary64(-1.0), 0xbff0000000000000ull);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x0000000000000000ull), 0.0);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x4030800000000000ull), 16.5);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x3FF5400000000000ull), 1.328125);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0xBFF5400000000000ull), -1.328125);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x3ff0000000000000ull), 1.0);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x0000000000000000ull), 0.0);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0xbff0000000000000ull), -1.0);
MPT_TEST_EXPECT_EQUAL(mpt::DecodeIEEE754binary64(0x3ff0000000000000ull), 1.0);
MPT_TEST_EXPECT_EQUAL(IEEE754binary64LE(1.0).GetInt64(), 0x3ff0000000000000ull);
MPT_TEST_EXPECT_EQUAL(IEEE754binary64BE(1.0).GetInt64(), 0x3ff0000000000000ull);
MPT_TEST_EXPECT_EQUAL(IEEE754binary64LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0xf0), mpt::as_byte(0x3f)), 1.0);
MPT_TEST_EXPECT_EQUAL(IEEE754binary64BE(mpt::as_byte(0x3f), mpt::as_byte(0xf0), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00)), 1.0);
MPT_TEST_EXPECT_EQUAL(IEEE754binary64LE(1.0), IEEE754binary64LE(mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0xf0), mpt::as_byte(0x3f)));
MPT_TEST_EXPECT_EQUAL(IEEE754binary64BE(1.0), IEEE754binary64BE(mpt::as_byte(0x3f), mpt::as_byte(0xf0), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00), mpt::as_byte(0x00)));
}
} // namespace floatingpoint
} // namespace endian
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_ENDIAN_TESTS_ENDIAN_FLOATINGPOINT_HPP
@@ -0,0 +1,133 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_ENDIAN_TESTS_ENDIAN_INTEGER_HPP
#define MPT_ENDIAN_TESTS_ENDIAN_INTEGER_HPP
#include "mpt/base/detect_compiler.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/endian/integer.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <limits>
#include <cstring>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace endian {
namespace integer {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/endian/integer")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
static_assert(std::numeric_limits<int8le>::min() == std::numeric_limits<int8>::min());
static_assert(std::numeric_limits<uint8le>::min() == std::numeric_limits<uint8>::min());
static_assert(std::numeric_limits<int16le>::min() == std::numeric_limits<int16>::min());
static_assert(std::numeric_limits<uint16le>::min() == std::numeric_limits<uint16>::min());
static_assert(std::numeric_limits<int32le>::min() == std::numeric_limits<int32>::min());
static_assert(std::numeric_limits<uint32le>::min() == std::numeric_limits<uint32>::min());
static_assert(std::numeric_limits<int64le>::min() == std::numeric_limits<int64>::min());
static_assert(std::numeric_limits<uint64le>::min() == std::numeric_limits<uint64>::min());
static_assert(std::numeric_limits<int8le>::max() == std::numeric_limits<int8>::max());
static_assert(std::numeric_limits<uint8le>::max() == std::numeric_limits<uint8>::max());
static_assert(std::numeric_limits<int16le>::max() == std::numeric_limits<int16>::max());
static_assert(std::numeric_limits<uint16le>::max() == std::numeric_limits<uint16>::max());
static_assert(std::numeric_limits<int32le>::max() == std::numeric_limits<int32>::max());
static_assert(std::numeric_limits<uint32le>::max() == std::numeric_limits<uint32>::max());
static_assert(std::numeric_limits<int64le>::max() == std::numeric_limits<int64>::max());
static_assert(std::numeric_limits<uint64le>::max() == std::numeric_limits<uint64>::max());
struct test_endian_constexpr {
static MPT_CONSTEXPR20_FUN int32le test(uint32 x) {
int32le foo{};
foo = x;
return foo;
}
};
MPT_CONSTEXPR20_VAR int32le foo = test_endian_constexpr::test(23);
static_cast<void>(foo);
MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint8(0x12)), 0x12);
MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint16(0x1234)), 0x3412);
MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint32(0x12345678u)), 0x78563412u);
MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(uint64(0x123456789abcdef0ull)), 0xf0debc9a78563412ull);
MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int8(std::numeric_limits<int8>::min())), std::numeric_limits<int8>::min());
MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int16(std::numeric_limits<int16>::min())), int16(0x80));
MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int32(std::numeric_limits<int32>::min())), int32(0x80));
MPT_TEST_EXPECT_EQUAL(mpt::SwapBytesImpl(int64(std::numeric_limits<int64>::min())), int64(0x80));
// Packed integers with defined endianness
{
int8le le8;
le8.set(-128);
int8be be8;
be8.set(-128);
MPT_TEST_EXPECT_EQUAL(le8, -128);
MPT_TEST_EXPECT_EQUAL(be8, -128);
MPT_TEST_EXPECT_EQUAL(std::memcmp(&le8, "\x80", 1), 0);
MPT_TEST_EXPECT_EQUAL(std::memcmp(&be8, "\x80", 1), 0);
int16le le16;
le16.set(0x1234);
int16be be16;
be16.set(0x1234);
MPT_TEST_EXPECT_EQUAL(le16, 0x1234);
MPT_TEST_EXPECT_EQUAL(be16, 0x1234);
MPT_TEST_EXPECT_EQUAL(std::memcmp(&le16, "\x34\x12", 2), 0);
MPT_TEST_EXPECT_EQUAL(std::memcmp(&be16, "\x12\x34", 2), 0);
uint32le le32;
le32.set(0xFFEEDDCCu);
uint32be be32;
be32.set(0xFFEEDDCCu);
MPT_TEST_EXPECT_EQUAL(le32, 0xFFEEDDCCu);
MPT_TEST_EXPECT_EQUAL(be32, 0xFFEEDDCCu);
MPT_TEST_EXPECT_EQUAL(std::memcmp(&le32, "\xCC\xDD\xEE\xFF", 4), 0);
MPT_TEST_EXPECT_EQUAL(std::memcmp(&be32, "\xFF\xEE\xDD\xCC", 4), 0);
uint64le le64;
le64.set(0xDEADC0DE15C0FFEEull);
uint64be be64;
be64.set(0xDEADC0DE15C0FFEEull);
MPT_TEST_EXPECT_EQUAL(le64, 0xDEADC0DE15C0FFEEull);
MPT_TEST_EXPECT_EQUAL(be64, 0xDEADC0DE15C0FFEEull);
MPT_TEST_EXPECT_EQUAL(std::memcmp(&le64, "\xEE\xFF\xC0\x15\xDE\xC0\xAD\xDE", 8), 0);
MPT_TEST_EXPECT_EQUAL(std::memcmp(&be64, "\xDE\xAD\xC0\xDE\x15\xC0\xFF\xEE", 8), 0);
}
}
} // namespace integer
} // namespace endian
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_ENDIAN_TESTS_ENDIAN_INTEGER_HPP
@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_ENVIRONMENT_ENVIRONMENT_HPP
#define MPT_ENVIRONMENT_ENVIRONMENT_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/string/types.hpp"
#include "mpt/string_transcode/transcode.hpp"
#include "mpt/system_error/system_error.hpp"
#include <optional>
#if MPT_OS_WINDOWS
#if defined(UNICODE) && !MPT_OS_WINDOWS_WINRT
#include <vector>
#endif // !MPT_OS_WINDOWS_WINRT
#endif // MPT_OS_WINDOWS
#include <cstdlib>
#if MPT_OS_WINDOWS
#include <windows.h>
#endif // MPT_OS_WINDOWS
namespace mpt {
inline namespace MPT_INLINE_NS {
inline std::optional<mpt::ustring> getenv(const mpt::ustring & env_var) {
#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
MPT_UNUSED(env_var);
return std::nullopt;
#elif MPT_OS_WINDOWS && defined(UNICODE)
std::vector<WCHAR> buf(32767);
DWORD size = GetEnvironmentVariable(mpt::transcode<std::wstring>(env_var).c_str(), buf.data(), 32767);
if (size == 0) {
mpt::windows::ExpectError(ERROR_ENVVAR_NOT_FOUND);
return std::nullopt;
}
return mpt::transcode<mpt::ustring>(buf.data());
#else
const char * val = std::getenv(mpt::transcode<std::string>(mpt::environment_encoding, env_var).c_str());
if (!val) {
return std::nullopt;
}
return mpt::transcode<mpt::ustring>(mpt::environment_encoding, val);
#endif
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_ENVIRONMENT_ENVIRONMENT_HPP
@@ -0,0 +1,74 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_EXCEPTION_TEXT_EXCEPTION_TEXT_HPP
#define MPT_EXCEPTION_TEXT_EXCEPTION_TEXT_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/string/types.hpp"
#include "mpt/string_transcode/transcode.hpp"
#include <exception>
#include <cstring>
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename Tstring>
inline Tstring get_exception_text(const std::exception & e) {
if (e.what() && (std::strlen(e.what()) > 0)) {
return mpt::transcode<Tstring>(mpt::exception_string{e.what()});
} else if (typeid(e).name() && (std::strlen(typeid(e).name()) > 0)) {
return mpt::transcode<Tstring>(mpt::source_string{typeid(e).name()});
} else {
return mpt::transcode<Tstring>(mpt::source_string{"unknown exception name"});
}
}
template <>
inline std::string get_exception_text<std::string>(const std::exception & e) {
if (e.what() && (std::strlen(e.what()) > 0)) {
return std::string{e.what()};
} else if (typeid(e).name() && (std::strlen(typeid(e).name()) > 0)) {
return std::string{typeid(e).name()};
} else {
return std::string{"unknown exception name"};
}
}
template <typename Tstring>
inline Tstring get_current_exception_text() {
try {
throw;
} catch (const std::exception & e) {
return mpt::get_exception_text<Tstring>(e);
} catch (...) {
return mpt::transcode<Tstring>(mpt::source_string{"unknown exception"});
}
}
template <>
inline std::string get_current_exception_text<std::string>() {
try {
throw;
} catch (const std::exception & e) {
return mpt::get_exception_text<std::string>(e);
} catch (...) {
return std::string{"unknown exception"};
}
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_EXCEPTION_TEXT_EXCEPTION_TEXT_HPP
@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_DETAULT_FLOATINGPOINT_HPP
#define MPT_FORMAT_DETAULT_FLOATINGPOINT_HPP
#include "mpt/base/detect.hpp"
#if !defined(MPT_LIBCXX_QUIRK_NO_TO_CHARS_FLOAT)
#define MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 1
#else
#define MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17 0
#endif
#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
#include "mpt/base/algorithm.hpp"
#endif
#include "mpt/base/namespace.hpp"
#include "mpt/format/helpers.hpp"
#include "mpt/string_transcode/transcode.hpp"
#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
#include <charconv>
#endif
#if !MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
#include <iomanip>
#include <ios>
#include <limits>
#include <locale>
#include <sstream>
#endif
#include <string>
#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
#include <system_error>
#endif
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
#if MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
inline Tstring to_chars_string(const T & x) {
std::string str(1, '\0');
bool done = false;
while (!done) {
std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x);
if (result.ec != std::errc{}) {
str.resize(mpt::exponential_grow(str.size()), '\0');
} else {
str.resize(result.ptr - str.data());
done = true;
}
}
return mpt::convert_formatted_simple<Tstring>(str);
}
template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
inline Tstring format_value_default(const T & x) {
return mpt::transcode<Tstring>(mpt::to_chars_string<typename mpt::select_format_string_type<Tstring>::type>(x));
}
#endif
#if !MPT_FORMAT_FORMAT_DEFAULT_FLOAT_CXX17
template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
inline Tstring to_stream_string(const T & x) {
using stream_char_type = typename mpt::select_format_char_type<typename Tstring::value_type>::type;
std::basic_ostringstream<stream_char_type> s;
s.imbue(std::locale::classic());
s << std::setprecision(std::numeric_limits<T>::max_digits10) << x;
return mpt::convert_formatted_simple<Tstring>(s.str());
}
template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
inline Tstring format_value_default(const T & x) {
return mpt::transcode<Tstring>(mpt::to_stream_string<typename mpt::select_format_string_type<Tstring>::type>(x));
}
#endif
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_DETAULT_FLOATINGPOINT_HPP
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_DEFAULT_FORMATTER_HPP
#define MPT_FORMAT_DEFAULT_FORMATTER_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/format/default_floatingpoint.hpp"
#include "mpt/format/default_integer.hpp"
#include "mpt/format/default_string.hpp"
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
struct default_formatter {
template <typename Tstring, typename T>
static inline Tstring format(const T & value) {
using namespace mpt;
return format_value_default<Tstring>(value);
}
};
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_DEFAULT_FORMATTER_HPP
@@ -0,0 +1,114 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_DEFAULT_INTEGER_HPP
#define MPT_FORMAT_DEFAULT_INTEGER_HPP
#include "mpt/base/detect.hpp"
#if !defined(MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT)
#define MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 1
#else // MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
#define MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17 0
#endif // !MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
#include "mpt/base/algorithm.hpp"
#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
#include "mpt/base/namespace.hpp"
#include "mpt/base/utility.hpp"
#include "mpt/format/helpers.hpp"
#include "mpt/string_transcode/transcode.hpp"
#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
#include <charconv>
#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
#if !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
#include <locale>
#include <sstream>
#endif // !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
#include <string>
#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
#include <system_error>
#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
#if MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
inline Tstring to_chars_string(const T & x) {
std::string str(1, '\0');
bool done = false;
while (!done) {
if constexpr (std::is_same<T, bool>::value) {
std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), static_cast<int>(x));
if (result.ec != std::errc{}) {
str.resize(mpt::exponential_grow(str.size()), '\0');
} else {
str.resize(result.ptr - str.data());
done = true;
}
} else {
std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x);
if (result.ec != std::errc{}) {
str.resize(mpt::exponential_grow(str.size()), '\0');
} else {
str.resize(result.ptr - str.data());
done = true;
}
}
}
return mpt::convert_formatted_simple<Tstring>(str);
}
template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
inline Tstring format_value_default(const T & x) {
return mpt::transcode<Tstring>(mpt::to_chars_string<typename mpt::select_format_string_type<Tstring>::type>(x));
}
#endif // MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
#if !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
inline Tstring to_stream_string(const T & x) {
using stream_char_type = typename mpt::select_format_char_type<typename Tstring::value_type>::type;
std::basic_ostringstream<stream_char_type> s;
s.imbue(std::locale::classic());
if constexpr (std::is_same<T, bool>::value) {
s << static_cast<int>(x);
} else if constexpr (mpt::is_character<T>::value) {
s << (x + 0); // force integral promotion
} else {
s << x;
}
return mpt::convert_formatted_simple<Tstring>(s.str());
}
template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
inline Tstring format_value_default(const T & x) {
return mpt::transcode<Tstring>(mpt::to_stream_string<typename mpt::select_format_string_type<Tstring>::type>(x));
}
#endif // !MPT_FORMAT_FORMAT_DEFAULT_INT_CXX17
template <typename Tstring, typename T, std::enable_if_t<std::is_enum<T>::value, bool> = true>
inline Tstring format_value_default(const T & x) {
return mpt::format_value_default<Tstring>(mpt::to_underlying(x));
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_DEFAULT_INTEGER_HPP
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_DEFAULT_STRING_HPP
#define MPT_FORMAT_DEFAULT_STRING_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/string_transcode/transcode.hpp"
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename Tstring, typename T>
inline auto format_value_default(const T & x) -> decltype(mpt::transcode<Tstring>(x)) {
return mpt::transcode<Tstring>(x);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_DEFAULT_STRING_HPP
@@ -0,0 +1,106 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_HELPERS_HPP
#define MPT_FORMAT_HELPERS_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/string/types.hpp"
#include "mpt/string_transcode/transcode.hpp"
#include <string>
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename Tdststring, typename Tsrcstring>
inline Tdststring convert_formatted_simple(const Tsrcstring & src) {
if constexpr (std::is_same<Tdststring, Tsrcstring>::value) {
return src;
} else {
Tdststring dst;
dst.reserve(src.length());
for (std::size_t i = 0; i < src.length(); ++i) {
dst.push_back(mpt::unsafe_char_convert<typename Tdststring::value_type>(src[i]));
}
return dst;
}
}
template <typename Tchar>
struct select_format_char_type {
using type = char;
};
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
template <>
struct select_format_char_type<wchar_t> {
using type = wchar_t;
};
#if MPT_USTRING_MODE_WIDE
#if MPT_CXX_AT_LEAST(20)
template <>
struct select_format_char_type<char8_t> {
using type = wchar_t;
};
#endif // C++20
template <>
struct select_format_char_type<char16_t> {
using type = wchar_t;
};
template <>
struct select_format_char_type<char32_t> {
using type = wchar_t;
};
#endif // MPT_USTRING_MODE_WIDE
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
template <typename Tstring>
struct select_format_string_type {
using type = mpt::ustring;
};
template <>
struct select_format_string_type<std::string> {
using type = std::string;
};
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
template <>
struct select_format_string_type<std::wstring> {
using type = std::wstring;
};
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
#if MPT_CXX_AT_LEAST(20)
template <>
struct select_format_string_type<std::u8string> {
using type = std::u8string;
};
#endif // C++20
template <>
struct select_format_string_type<std::u16string> {
using type = std::u16string;
};
template <>
struct select_format_string_type<std::u32string> {
using type = std::u32string;
};
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_HELPERS_HPP
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_JOIN_HPP
#define MPT_FORMAT_JOIN_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/format/simple.hpp"
#include "mpt/string/utility.hpp"
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename Tstring, typename T>
Tstring join_format(const std::vector<T> & vals, const Tstring & sep = Tstring(1, char_constants<typename Tstring::value_type>::comma)) {
Tstring str;
for (std::size_t i = 0; i < vals.size(); ++i) {
if (i > 0) {
str += sep;
}
str += mpt::format<Tstring>::val(vals[i]);
}
return str;
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_JOIN_HPP
@@ -0,0 +1,332 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_MESSAGE_HPP
#define MPT_FORMAT_MESSAGE_HPP
#include "mpt/base/macros.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/span.hpp"
#include "mpt/string/types.hpp"
#include "mpt/string/utility.hpp"
#include <array>
#include <stdexcept>
#include <utility>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
class format_message_syntax_error
: public std::domain_error {
public:
format_message_syntax_error()
: std::domain_error("format string syntax error") {
return;
}
};
template <typename Tformatter, typename Tformat>
class message_formatter {
public:
using Tstring = typename mpt::make_string_type<Tformat>::type;
private:
Tstring format;
private:
MPT_NOINLINE Tstring do_format(const mpt::span<const Tstring> vals) const {
using traits = typename mpt::string_traits<Tstring>;
using char_type = typename traits::char_type;
using size_type = typename traits::size_type;
Tstring result;
const size_type len = traits::length(format);
traits::reserve(result, len);
std::size_t max_arg = 0;
//std::size_t args = 0;
bool success = true;
enum class state : int {
error = -1,
text = 0,
open_seen = 1,
number_seen = 2,
close_seen = 3,
};
state state = state::text;
bool numbered_args = false;
bool unnumbered_args = false;
std::size_t last_arg = 0;
std::size_t this_arg = 0;
std::size_t current_arg = 0;
for (size_type pos = 0; pos != len; ++pos) {
char_type c = format[pos];
switch (state) {
case state::text:
if (c == char_type('{')) {
state = state::open_seen;
} else if (c == char_type('}')) {
state = state::close_seen;
} else {
state = state::text;
traits::append(result, 1, c); // output c here
}
break;
case state::open_seen:
if (c == char_type('{')) {
state = state::text;
traits::append(result, 1, char_type('{')); // output { here
} else if (c == char_type('}')) {
state = state::text;
unnumbered_args = true;
last_arg++;
this_arg = last_arg;
{ // output this_arg here
const std::size_t n = this_arg - 1;
if (n < std::size(vals)) {
traits::append(result, vals[n]);
}
}
if (this_arg > max_arg) {
max_arg = this_arg;
}
//args += 1;
} else if (char_type('0') <= c && c <= char_type('9')) {
state = state::number_seen;
numbered_args = true;
current_arg = c - char_type('0');
} else {
state = state::error;
}
break;
case state::number_seen:
if (c == char_type('{')) {
state = state::error;
} else if (c == char_type('}')) {
state = state::text;
this_arg = current_arg + 1;
{ // output this_arg here
const std::size_t n = this_arg - 1;
if (n < std::size(vals)) {
traits::append(result, vals[n]);
}
}
if (this_arg > max_arg) {
max_arg = this_arg;
}
//args += 1;
} else if (char_type('0') <= c && c <= char_type('9')) {
state = state::number_seen;
numbered_args = true;
current_arg = (current_arg * 10) + (c - char_type('0'));
} else {
state = state::error;
}
break;
case state::close_seen:
if (c == char_type('{')) {
state = state::error;
} else if (c == char_type('}')) {
state = state::text;
traits::append(result, 1, char_type('}')); // output } here
} else {
state = state::error;
}
break;
case state::error:
state = state::error;
break;
}
}
if (state == state::error) {
success = false;
}
if (state != state::text) {
success = false;
}
if (numbered_args && unnumbered_args) {
success = false;
}
if (!success) {
throw format_message_syntax_error();
}
return result;
}
public:
MPT_FORCEINLINE message_formatter(Tstring format_)
: format(std::move(format_)) {
}
public:
template <typename... Ts>
MPT_NOINLINE Tstring operator()(Ts &&... xs) const {
const std::array<Tstring, sizeof...(xs)> vals{{Tformatter::template format<Tstring>(std::forward<Ts>(xs))...}};
return do_format(mpt::as_span(vals));
}
}; // struct message_formatter<Tformat>
template <typename Tformatter, std::ptrdiff_t N, typename Tchar, typename Tstring>
class message_formatter_counted {
private:
message_formatter<Tformatter, Tstring> formatter;
public:
template <std::size_t literal_length>
inline message_formatter_counted(const Tchar (&format)[literal_length])
: formatter(Tstring(format)) {
return;
}
public:
template <typename... Ts>
inline Tstring operator()(Ts &&... xs) const {
static_assert(static_cast<std::ptrdiff_t>(sizeof...(xs)) == N);
return formatter(std::forward<Ts>(xs)...);
}
}; // struct message_formatter_counted<Tformat>
template <typename Tchar>
MPT_CONSTEXPRINLINE std::ptrdiff_t parse_format_string_argument_count_impl(const Tchar * const format, const std::size_t len) {
std::size_t max_arg = 0;
std::size_t args = 0;
bool success = true;
enum class state : int {
error = -1,
text = 0,
open_seen = 1,
number_seen = 2,
close_seen = 3,
};
state state = state::text;
bool numbered_args = false;
bool unnumbered_args = false;
std::size_t last_arg = 0;
std::size_t this_arg = 0;
std::size_t current_arg = 0;
for (std::size_t pos = 0; pos != len; ++pos) {
Tchar c = format[pos];
switch (state) {
case state::text:
if (c == Tchar('{')) {
state = state::open_seen;
} else if (c == Tchar('}')) {
state = state::close_seen;
} else {
state = state::text;
// output c here
}
break;
case state::open_seen:
if (c == Tchar('{')) {
state = state::text;
// output { here
} else if (c == Tchar('}')) {
state = state::text;
unnumbered_args = true;
last_arg++;
this_arg = last_arg;
// output this_arg here
if (this_arg > max_arg)
{
max_arg = this_arg;
}
args += 1;
} else if (Tchar('0') <= c && c <= Tchar('9')) {
state = state::number_seen;
numbered_args = true;
current_arg = c - Tchar('0');
} else {
state = state::error;
}
break;
case state::number_seen:
if (c == Tchar('{')) {
state = state::error;
} else if (c == Tchar('}')) {
state = state::text;
this_arg = current_arg + 1;
// output this_arg here
if (this_arg > max_arg) {
max_arg = this_arg;
}
args += 1;
} else if (Tchar('0') <= c && c <= Tchar('9')) {
state = state::number_seen;
numbered_args = true;
current_arg = (current_arg * 10) + (c - Tchar('0'));
} else {
state = state::error;
}
break;
case state::close_seen:
if (c == Tchar('{')) {
state = state::error;
} else if (c == Tchar('}')) {
state = state::text;
// output } here
} else {
state = state::error;
}
break;
case state::error:
state = state::error;
break;
}
}
if (state == state::error) {
success = false;
}
if (state != state::text) {
success = false;
}
if (numbered_args && unnumbered_args) {
success = false;
}
if (!success) {
throw format_message_syntax_error();
}
if (max_arg != args) {
throw format_message_syntax_error();
}
return max_arg;
}
template <typename Tchar, std::size_t literal_length>
MPT_CONSTEXPRINLINE std::ptrdiff_t parse_format_string_argument_count(const Tchar (&format)[literal_length]) {
return parse_format_string_argument_count_impl(format, literal_length - 1);
}
template <typename Tformatter, std::size_t args, typename Tchar, std::size_t N>
inline auto format_message(const Tchar (&format)[N]) {
using Tstring = typename mpt::make_string_type<const Tchar *>::type;
return message_formatter_counted<Tformatter, args, Tchar, Tstring>(format);
}
template <typename Tformatter, std::size_t args, typename Tstring, typename Tchar, std::size_t N>
inline auto format_message_typed(const Tchar (&format)[N]) {
return message_formatter_counted<Tformatter, args, Tchar, Tstring>(format);
}
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_MESSAGE_HPP
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_MESSAGE_MACROS_HPP
#define MPT_FORMAT_MESSAGE_MACROS_HPP
#include "mpt/base/detect.hpp"
#include "mpt/detect/mfc.hpp"
#include "mpt/format/default_formatter.hpp"
#include "mpt/format/message.hpp"
#define MPT_AFORMAT_MESSAGE(f) mpt::format_message<mpt::default_formatter, mpt::parse_format_string_argument_count(f)>(f)
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
#define MPT_WFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(L##f), std::wstring>(L##f)
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
#define MPT_UFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(MPT_ULITERAL(f)), mpt::ustring>(MPT_ULITERAL(f))
#define MPT_LFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(f), mpt::lstring>(f)
#if MPT_OS_WINDOWS
#define MPT_TFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(TEXT(f)), mpt::tstring>(TEXT(f))
#endif // MPT_OS_WINDOWS
#if MPT_DETECTED_MFC
#define MPT_CWFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(L##f), CStringW>(L##f)
#define MPT_CAFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(f), CStringA>(f)
#define MPT_CFORMAT_MESSAGE(f) mpt::format_message_typed<mpt::default_formatter, mpt::parse_format_string_argument_count(TEXT(f)), CString>(TEXT(f))
#endif // MPT_DETECTED_MFC
#endif // MPT_FORMAT_MESSAGE_MACROS_HPP
@@ -0,0 +1,166 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_SIMPLE_HPP
#define MPT_FORMAT_SIMPLE_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/base/pointer.hpp"
#include "mpt/format/default_formatter.hpp"
#include "mpt/format/simple_floatingpoint.hpp"
#include "mpt/format/simple_integer.hpp"
#include "mpt/format/simple_spec.hpp"
#include "mpt/string/utility.hpp"
#include <type_traits>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
template <typename Tstring>
struct format : format_simple_base {
template <typename T>
static inline Tstring val(const T & x) {
return mpt::default_formatter::format<Tstring>(x);
}
template <typename T>
static inline Tstring fmt(const T & x, const format_simple_spec & f) {
return mpt::format_simple<Tstring>(x, f);
}
template <typename T>
static inline Tstring dec(const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseDec().FillOff());
}
template <int width, typename T>
static inline Tstring dec0(const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseDec().FillNul().Width(width));
}
template <typename T>
static inline Tstring dec(unsigned int g, char s, const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseDec().FillOff().Group(g).GroupSep(s));
}
template <int width, typename T>
static inline Tstring dec0(unsigned int g, char s, const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s));
}
template <typename T>
static inline Tstring hex(const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseLow().FillOff());
}
template <typename T>
static inline Tstring HEX(const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseUpp().FillOff());
}
template <int width, typename T>
static inline Tstring hex0(const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseLow().FillNul().Width(width));
}
template <int width, typename T>
static inline Tstring HEX0(const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseUpp().FillNul().Width(width));
}
template <typename T>
static inline Tstring hex(unsigned int g, char s, const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s));
}
template <typename T>
static inline Tstring HEX(unsigned int g, char s, const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s));
}
template <int width, typename T>
static inline Tstring hex0(unsigned int g, char s, const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseLow().FillNul().Width(width).Group(g).GroupSep(s));
}
template <int width, typename T>
static inline Tstring HEX0(unsigned int g, char s, const T & x) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::format_simple<Tstring>(x, format_simple_spec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s));
}
template <typename T>
static inline Tstring flt(const T & x, int precision = -1) {
static_assert(std::is_floating_point<T>::value);
return mpt::format_simple<Tstring>(x, format_simple_spec().NotaNrm().FillOff().Precision(precision));
}
template <typename T>
static inline Tstring fix(const T & x, int precision = -1) {
static_assert(std::is_floating_point<T>::value);
return mpt::format_simple<Tstring>(x, format_simple_spec().NotaFix().FillOff().Precision(precision));
}
template <typename T>
static inline Tstring sci(const T & x, int precision = -1) {
static_assert(std::is_floating_point<T>::value);
return mpt::format_simple<Tstring>(x, format_simple_spec().NotaSci().FillOff().Precision(precision));
}
template <typename T>
static inline Tstring ptr(const T & x) {
static_assert(std::is_pointer<T>::value || std::is_same<T, std::uintptr_t>::value || std::is_same<T, std::intptr_t>::value, "");
return hex0<mpt::pointer_size * 2>(mpt::pointer_cast<const std::uintptr_t>(x));
}
template <typename T>
static inline Tstring PTR(const T & x) {
static_assert(std::is_pointer<T>::value || std::is_same<T, std::uintptr_t>::value || std::is_same<T, std::intptr_t>::value, "");
return HEX0<mpt::pointer_size * 2>(mpt::pointer_cast<const std::uintptr_t>(x));
}
static inline Tstring pad_left(std::size_t width_, const Tstring & str) {
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return traits::pad(str, width, 0);
}
static inline Tstring pad_right(std::size_t width_, const Tstring & str) {
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return traits::pad(str, 0, width);
}
static inline Tstring left(std::size_t width_, const Tstring & str) {
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return (traits::length(str) < width) ? traits::pad(str, 0, width - traits::length(str)) : str;
}
static inline Tstring right(std::size_t width_, const Tstring & str) {
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return (traits::length(str) < width) ? traits::pad(str, width - traits::length(str), 0) : str;
}
static inline Tstring center(std::size_t width_, const Tstring & str) {
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return (traits::length(str) < width) ? traits::pad(str, (width - traits::length(str)) / 2, (width - traits::length(str) + 1) / 2) : str;
}
}; // struct format
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_SIMPLE_HPP
@@ -0,0 +1,250 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_SIMPLE_FLOATINGPOINT_HPP
#define MPT_FORMAT_SIMPLE_FLOATINGPOINT_HPP
#include "mpt/base/detect.hpp"
#if !defined(MPT_LIBCXX_QUIRK_NO_TO_CHARS_FLOAT)
#define MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 1
#else
#define MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17 0
#endif
#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
#include "mpt/base/algorithm.hpp"
#endif
#include "mpt/base/namespace.hpp"
#include "mpt/format/default_floatingpoint.hpp"
#include "mpt/format/helpers.hpp"
#include "mpt/format/simple_spec.hpp"
#include "mpt/string/types.hpp"
#include "mpt/string_transcode/transcode.hpp"
#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
#include <charconv>
#endif
#if !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
#include <iomanip>
#include <ios>
#endif
#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
#include <iterator>
#endif
#if !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
#include <limits>
#include <locale>
#include <sstream>
#endif
#include <string>
#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
#include <system_error>
#endif
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
#if MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
template <typename Tstring, typename T>
inline Tstring format_simple_floatingpoint_to_chars(const T & x, std::chars_format fmt) {
std::string str(1, '\0');
bool done = false;
while (!done) {
std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, fmt);
if (result.ec != std::errc{}) {
str.resize(mpt::exponential_grow(str.size()), '\0');
} else {
str.resize(result.ptr - str.data());
done = true;
}
}
return mpt::convert_formatted_simple<Tstring>(str);
}
template <typename Tstring, typename T>
inline Tstring format_simple_floatingpoint_to_chars(const T & x, std::chars_format fmt, int precision) {
std::string str(1, '\0');
bool done = false;
while (!done) {
std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, fmt, precision);
if (result.ec != std::errc{}) {
str.resize(mpt::exponential_grow(str.size()), '\0');
} else {
str.resize(result.ptr - str.data());
done = true;
}
}
return mpt::convert_formatted_simple<Tstring>(str);
}
template <typename Tstring>
inline Tstring format_simple_floatingpoint_postprocess_width(Tstring str, const format_simple_spec & format) {
format_simple_flags f = format.GetFlags();
std::size_t width = format.GetWidth();
if (f & format_simple_base::FillNul) {
auto pos = str.begin();
if (str.length() > 0) {
if (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('+')) {
pos++;
width++;
} else if (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('-')) {
pos++;
width++;
}
}
if (str.length() - std::distance(str.begin(), pos) < width) {
str.insert(pos, width - str.length() - std::distance(str.begin(), pos), '0');
}
} else {
if (str.length() < width) {
str.insert(0, width - str.length(), ' ');
}
}
return str;
}
template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
inline Tstring format_simple(const T & x, const format_simple_spec & f) {
using format_string_type = typename mpt::select_format_string_type<Tstring>::type;
if (f.GetPrecision() != -1) {
if (f.GetFlags() & format_simple_base::NotaSci) {
return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::scientific, f.GetPrecision()), f));
} else if (f.GetFlags() & format_simple_base::NotaFix) {
return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::fixed, f.GetPrecision()), f));
} else {
return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::general, f.GetPrecision()), f));
}
} else {
if (f.GetFlags() & format_simple_base::NotaSci) {
return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::scientific), f));
} else if (f.GetFlags() & format_simple_base::NotaFix) {
return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::fixed), f));
} else {
return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_postprocess_width(format_simple_floatingpoint_to_chars<format_string_type>(x, std::chars_format::general), f));
}
}
}
#else // !MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
template <typename Tchar>
struct NumPunct : std::numpunct<Tchar> {
private:
unsigned int group;
char sep;
public:
NumPunct(unsigned int g, char s)
: group(g)
, sep(s) { }
std::string do_grouping() const override {
return std::string(1, static_cast<char>(group));
}
Tchar do_thousands_sep() const override {
return static_cast<Tchar>(sep);
}
};
template <typename Tostream, typename T>
inline void format_simple_floatingpoint_apply_stream_format(Tostream & o, const format_simple_spec & format, const T &) {
if (format.GetGroup() > 0)
{
o.imbue(std::locale(o.getloc(), new NumPunct<typename Tostream::char_type>(format.GetGroup(), format.GetGroupSep())));
}
format_simple_flags f = format.GetFlags();
std::size_t width = format.GetWidth();
int precision = format.GetPrecision();
if (precision != -1 && width != 0 && !(f & format_simple_base::NotaFix) && !(f & format_simple_base::NotaSci))
{
// fixup:
// precision behaves differently from .#
// avoid default format when precision and width are set
f &= ~format_simple_base::NotaNrm;
f |= format_simple_base::NotaFix;
}
if (f & format_simple_base::BaseDec) {
o << std::dec;
} else if (f & format_simple_base::BaseHex) {
o << std::hex;
}
if (f & format_simple_base::NotaNrm) { /*nothing*/
} else if (f & format_simple_base::NotaFix) {
o << std::setiosflags(std::ios::fixed);
} else if (f & format_simple_base::NotaSci) {
o << std::setiosflags(std::ios::scientific);
}
if (f & format_simple_base::CaseLow) {
o << std::nouppercase;
} else if (f & format_simple_base::CaseUpp) {
o << std::uppercase;
}
if (f & format_simple_base::FillOff) { /* nothing */
} else if (f & format_simple_base::FillNul) {
o << std::setw(width) << std::setfill(typename Tostream::char_type('0'));
}
if (precision != -1)
{
o << std::setprecision(precision);
} else
{
if constexpr (std::is_floating_point<T>::value)
{
if (f & format_simple_base::NotaNrm)
{
o << std::setprecision(std::numeric_limits<T>::max_digits10);
} else if (f & format_simple_base::NotaFix)
{
o << std::setprecision(std::numeric_limits<T>::digits10);
} else if (f & format_simple_base::NotaSci)
{
o << std::setprecision(std::numeric_limits<T>::max_digits10 - 1);
} else
{
o << std::setprecision(std::numeric_limits<T>::max_digits10);
}
}
}
}
template <typename Tstring, typename T>
inline Tstring format_simple_floatingpoint_stream(const T & x, const format_simple_spec & f) {
using stream_char_type = typename mpt::select_format_char_type<typename Tstring::value_type>::type;
std::basic_ostringstream<stream_char_type> s;
s.imbue(std::locale::classic());
mpt::format_simple_floatingpoint_apply_stream_format(s, f, x);
s << x;
return mpt::convert_formatted_simple<Tstring>(s.str());
}
template <typename Tstring, typename T, std::enable_if_t<std::is_floating_point<T>::value, bool> = true>
inline Tstring format_simple(const T & x, const format_simple_spec & format) {
return mpt::transcode<Tstring>(mpt::format_simple_floatingpoint_stream<typename mpt::select_format_string_type<Tstring>::type>(x, format));
}
#endif // MPT_FORMAT_FORMAT_SIMPLE_FLOAT_CXX17
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_SIMPLE_FLOATINGPOINT_HPP
@@ -0,0 +1,251 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_SIMPLE_INTEGER_HPP
#define MPT_FORMAT_SIMPLE_INTEGER_HPP
#include "mpt/base/detect.hpp"
#if !defined(MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT)
#define MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 1
#else // MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
#define MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17 0
#endif // !MPT_LIBCXX_QUIRK_NO_TO_CHARS_INT
#include "mpt/base/algorithm.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/format/helpers.hpp"
#include "mpt/format/simple_spec.hpp"
#include "mpt/string/types.hpp"
#if !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#include <algorithm>
#endif // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#include <charconv>
#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#if !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#include <ios>
#include <locale>
#endif // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#include <string>
#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#if !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#include <sstream>
#endif // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#include <system_error>
#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#include <type_traits>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
inline Tstring format_simple_integer_to_chars(const T & x, int base) {
std::string str(1, '\0');
bool done = false;
while (!done) {
if constexpr (std::is_same<T, bool>::value) {
std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), static_cast<int>(x), base);
if (result.ec != std::errc{}) {
str.resize(mpt::exponential_grow(str.size()), '\0');
} else {
str.resize(result.ptr - str.data());
done = true;
}
} else {
std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, base);
if (result.ec != std::errc{}) {
str.resize(mpt::exponential_grow(str.size()), '\0');
} else {
str.resize(result.ptr - str.data());
done = true;
}
}
}
return mpt::convert_formatted_simple<Tstring>(str);
}
#else // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
inline Tstring format_simple_integer_to_stream(const T & x, int base) {
using stream_char_type = typename mpt::select_format_char_type<typename Tstring::value_type>::type;
if ((base == 10) || ((base == 16) && std::is_unsigned<T>::value) || ((base == 8) && std::is_unsigned<T>::value)) {
// fast path
std::basic_ostringstream<stream_char_type> s;
s.imbue(std::locale::classic());
if (base == 16) {
s << std::hex;
} else if (base == 8) {
s << std::oct;
}
if constexpr (std::is_same<T, bool>::value) {
s << static_cast<int>(x);
} else if constexpr (mpt::is_character<T>::value) {
s << (x + 0); // force integral promotion
} else {
s << x;
}
return mpt::convert_formatted_simple<Tstring>(s.str());
} else {
if constexpr (std::is_same<T, bool>::value) {
return x ? Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('1')) : Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('0'));
} else if constexpr (std::is_unsigned<T>::value) {
Tstring result;
T val = x;
if (val == 0) {
result += Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('0'));
} else {
using Tunsigned = typename std::make_unsigned<T>::type;
while (val > 0) {
Tunsigned digit = static_cast<Tunsigned>(val % static_cast<unsigned int>(base));
val = static_cast<Tunsigned>(val / static_cast<unsigned int>(base));
if (digit >= 10) {
result += Tstring(1, static_cast<typename Tstring::value_type>(mpt::unsafe_char_convert<typename Tstring::value_type>('a') - 10 + digit));
} else {
result += Tstring(1, static_cast<typename Tstring::value_type>(mpt::unsafe_char_convert<typename Tstring::value_type>('0') + digit));
}
}
std::reverse(result.begin(), result.end());
}
return result;
} else {
Tstring result;
if (x == 0) {
result += Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('0'));
} else {
using Tunsigned = typename std::make_unsigned<T>::type;
Tunsigned val = (x != -x) ? ((x >= 0) ? x : -x) : (static_cast<Tunsigned>(-(x + 1)) + 1);
while (val > 0) {
Tunsigned digit = static_cast<Tunsigned>(val % static_cast<unsigned int>(base));
val = static_cast<Tunsigned>(val / static_cast<unsigned int>(base));
if (digit >= 10) {
result += Tstring(1, static_cast<typename Tstring::value_type>(mpt::unsafe_char_convert<typename Tstring::value_type>('a') - 10 + digit));
} else {
result += Tstring(1, static_cast<typename Tstring::value_type>(mpt::unsafe_char_convert<typename Tstring::value_type>('0') + digit));
}
}
if (x < 0) {
result += Tstring(1, mpt::unsafe_char_convert<typename Tstring::value_type>('-'));
}
std::reverse(result.begin(), result.end());
}
return result;
}
}
}
#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
template <typename Tstring>
inline Tstring format_simple_integer_postprocess_case(Tstring str, const format_simple_spec & format) {
format_simple_flags f = format.GetFlags();
if (f & format_simple_base::CaseUpp) {
for (auto & c : str) {
if (mpt::unsafe_char_convert<typename Tstring::value_type>('a') <= c && c <= mpt::unsafe_char_convert<typename Tstring::value_type>('z')) {
c -= mpt::unsafe_char_convert<typename Tstring::value_type>('a') - mpt::unsafe_char_convert<typename Tstring::value_type>('A');
}
}
}
return str;
}
template <typename Tstring>
inline Tstring format_simple_integer_postprocess_digits(Tstring str, const format_simple_spec & format) {
format_simple_flags f = format.GetFlags();
std::size_t width = format.GetWidth();
if (f & format_simple_base::FillNul) {
auto pos = str.begin();
if (str.length() > 0) {
if (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('+')) {
pos++;
width++;
} else if (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('-')) {
pos++;
width++;
}
}
if (str.length() < width) {
str.insert(pos, width - str.length(), mpt::unsafe_char_convert<typename Tstring::value_type>('0'));
}
}
return str;
}
#if MPT_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable : 4723) // potential divide by 0
#endif // MPT_COMPILER_MSVC
template <typename Tstring>
inline Tstring format_simple_integer_postprocess_group(Tstring str, const format_simple_spec & format) {
if (format.GetGroup() > 0) {
const unsigned int groupSize = format.GetGroup();
const char groupSep = format.GetGroupSep();
std::size_t len = str.length();
for (std::size_t n = 0; n < len; ++n) {
if (n > 0 && (n % groupSize) == 0) {
if (!(n == (len - 1) && (str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('+') || str[0] == mpt::unsafe_char_convert<typename Tstring::value_type>('-')))) {
str.insert(str.begin() + (len - n), 1, mpt::unsafe_char_convert<typename Tstring::value_type>(groupSep));
}
}
}
}
return str;
}
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
inline Tstring format_simple(const T & x, const format_simple_spec & format) {
int base = 10;
if (format.GetFlags() & format_simple_base::BaseDec) {
base = 10;
}
if (format.GetFlags() & format_simple_base::BaseHex) {
base = 16;
}
using format_string_type = typename mpt::select_format_string_type<Tstring>::type;
return mpt::transcode<Tstring>(mpt::format_simple_integer_postprocess_group(mpt::format_simple_integer_postprocess_digits(mpt::format_simple_integer_postprocess_case(mpt::format_simple_integer_to_chars<format_string_type>(x, base), format), format), format));
}
#else // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
template <typename Tstring, typename T, std::enable_if_t<std::is_integral<T>::value, bool> = true>
inline Tstring format_simple(const T & x, const format_simple_spec & format) {
int base = 10;
if (format.GetFlags() & format_simple_base::BaseDec) {
base = 10;
}
if (format.GetFlags() & format_simple_base::BaseHex) {
base = 16;
}
using format_string_type = typename mpt::select_format_string_type<Tstring>::type;
return mpt::transcode<Tstring>(mpt::format_simple_integer_postprocess_group(mpt::format_simple_integer_postprocess_digits(mpt::format_simple_integer_postprocess_case(mpt::format_simple_integer_to_stream<format_string_type>(x, base), format), format), format));
}
#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_SIMPLE_INTEGER_HPP
@@ -0,0 +1,221 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_SIMPLE_SPEC_HPP
#define MPT_FORMAT_SIMPLE_SPEC_HPP
#include "mpt/base/macros.hpp"
#include "mpt/base/namespace.hpp"
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
struct format_simple_base {
enum FormatFlagsEnum {
BaseDec = 0x0001, // base 10 (integers only) // int+float
BaseHex = 0x0002, // base 16 (integers only) // int+float
CaseLow = 0x0010, // lower case hex digits // int+float
CaseUpp = 0x0020, // upper case hex digits // int+float
FillOff = 0x0100, // do not fill up width // int+float
FillNul = 0x0400, // fill up width with zeros // int+float
NotaNrm = 0x1000, // float: normal/default notation // float
NotaFix = 0x2000, // float: fixed point notation // float
NotaSci = 0x4000, // float: scientific notation // float
};
}; // struct format_simple_base
using format_simple_flags = unsigned int;
static_assert(sizeof(format_simple_flags) >= sizeof(format_simple_base::FormatFlagsEnum));
class format_simple_spec {
private:
format_simple_flags flags;
std::size_t width; // int+float
int precision; // float
unsigned int group; // int
char group_sep; // int
public:
MPT_CONSTEXPRINLINE format_simple_spec() noexcept
: flags(0)
, width(0)
, precision(-1)
, group(0)
, group_sep(',') { }
MPT_CONSTEXPRINLINE format_simple_flags GetFlags() const noexcept {
return flags;
}
MPT_CONSTEXPRINLINE std::size_t GetWidth() const noexcept {
return width;
}
MPT_CONSTEXPRINLINE int GetPrecision() const noexcept {
return precision;
}
MPT_CONSTEXPRINLINE unsigned int GetGroup() const noexcept {
return group;
}
MPT_CONSTEXPRINLINE char GetGroupSep() const noexcept {
return group_sep;
}
MPT_CONSTEXPRINLINE format_simple_spec & SetFlags(format_simple_flags f) noexcept {
flags = f;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & SetWidth(std::size_t w) noexcept {
width = w;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & SetPrecision(int p) noexcept {
precision = p;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & SetGroup(unsigned int g) noexcept {
group = g;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & SetGroupSep(char s) noexcept {
group_sep = s;
return *this;
}
public:
MPT_CONSTEXPRINLINE format_simple_spec & BaseDec() noexcept {
flags &= ~(format_simple_base::BaseDec | format_simple_base::BaseHex);
flags |= format_simple_base::BaseDec;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & BaseHex() noexcept {
flags &= ~(format_simple_base::BaseDec | format_simple_base::BaseHex);
flags |= format_simple_base::BaseHex;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & CaseLow() noexcept {
flags &= ~(format_simple_base::CaseLow | format_simple_base::CaseUpp);
flags |= format_simple_base::CaseLow;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & CaseUpp() noexcept {
flags &= ~(format_simple_base::CaseLow | format_simple_base::CaseUpp);
flags |= format_simple_base::CaseUpp;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & FillOff() noexcept {
flags &= ~(format_simple_base::FillOff | format_simple_base::FillNul);
flags |= format_simple_base::FillOff;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & FillNul() noexcept {
flags &= ~(format_simple_base::FillOff | format_simple_base::FillNul);
flags |= format_simple_base::FillNul;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & NotaNrm() noexcept {
flags &= ~(format_simple_base::NotaNrm | format_simple_base::NotaFix | format_simple_base::NotaSci);
flags |= format_simple_base::NotaNrm;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & NotaFix() noexcept {
flags &= ~(format_simple_base::NotaNrm | format_simple_base::NotaFix | format_simple_base::NotaSci);
flags |= format_simple_base::NotaFix;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & NotaSci() noexcept {
flags &= ~(format_simple_base::NotaNrm | format_simple_base::NotaFix | format_simple_base::NotaSci);
flags |= format_simple_base::NotaSci;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & Width(std::size_t w) noexcept {
width = w;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & Prec(int p) noexcept {
precision = p;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & Group(unsigned int g) noexcept {
group = g;
return *this;
}
MPT_CONSTEXPRINLINE format_simple_spec & GroupSep(char s) noexcept {
group_sep = s;
return *this;
}
public:
MPT_CONSTEXPRINLINE format_simple_spec & Dec() noexcept {
return BaseDec();
}
MPT_CONSTEXPRINLINE format_simple_spec & Hex() noexcept {
return BaseHex();
}
MPT_CONSTEXPRINLINE format_simple_spec & Low() noexcept {
return CaseLow();
}
MPT_CONSTEXPRINLINE format_simple_spec & Upp() noexcept {
return CaseUpp();
}
MPT_CONSTEXPRINLINE format_simple_spec & Off() noexcept {
return FillOff();
}
MPT_CONSTEXPRINLINE format_simple_spec & Nul() noexcept {
return FillNul();
}
MPT_CONSTEXPRINLINE format_simple_spec & Nrm() noexcept {
return NotaNrm();
}
MPT_CONSTEXPRINLINE format_simple_spec & Fix() noexcept {
return NotaFix();
}
MPT_CONSTEXPRINLINE format_simple_spec & Sci() noexcept {
return NotaSci();
}
public:
MPT_CONSTEXPRINLINE format_simple_spec & Decimal() noexcept {
return BaseDec();
}
MPT_CONSTEXPRINLINE format_simple_spec & Hexadecimal() noexcept {
return BaseHex();
}
MPT_CONSTEXPRINLINE format_simple_spec & Lower() noexcept {
return CaseLow();
}
MPT_CONSTEXPRINLINE format_simple_spec & Upper() noexcept {
return CaseUpp();
}
MPT_CONSTEXPRINLINE format_simple_spec & FillNone() noexcept {
return FillOff();
}
MPT_CONSTEXPRINLINE format_simple_spec & FillZero() noexcept {
return FillNul();
}
MPT_CONSTEXPRINLINE format_simple_spec & FloatNormal() noexcept {
return NotaNrm();
}
MPT_CONSTEXPRINLINE format_simple_spec & FloatFixed() noexcept {
return NotaFix();
}
MPT_CONSTEXPRINLINE format_simple_spec & FloatScientific() noexcept {
return NotaSci();
}
MPT_CONSTEXPRINLINE format_simple_spec & Precision(int p) noexcept {
return Prec(p);
}
};
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_SIMPLE_SPEC_HPP
@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_TESTS_FORMAT_MESSAGE_HPP
#define MPT_FORMAT_TESTS_FORMAT_MESSAGE_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/format/message.hpp"
#include "mpt/format/message_macros.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace format {
namespace message {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/format/message")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
static_assert(mpt::parse_format_string_argument_count("") == 0);
static_assert(mpt::parse_format_string_argument_count("{{") == 0);
static_assert(mpt::parse_format_string_argument_count("}}") == 0);
static_assert(mpt::parse_format_string_argument_count("{}") == 1);
static_assert(mpt::parse_format_string_argument_count("{}{}") == 2);
static_assert(mpt::parse_format_string_argument_count("{0}{1}") == 2);
// basic
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}{}{}")(1, 2, 3), "123");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{2}{1}{0}")(1, 2, 3), "321");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{2}{1}{0}{4}{3}{6}{5}{7}{10}{9}{8}")(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "a"), "21043657a98");
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE(L"{}{}{}")(1, 2, 3), L"123");
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
// escaping behviour
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%")(), "%");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%")(), "%");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%%")(), "%%");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}")("a"), "a");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}%")("a"), "a%");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}%")("a"), "a%");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{}%%")("a"), "a%%");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%1")(), "%1");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%{}")("a"), "%a");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("%b")(), "%b");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{{}}")(), "{}");
MPT_TEST_EXPECT_EQUAL(MPT_AFORMAT_MESSAGE("{{{}}}")("a"), "{a}");
}
} // namespace message
} // namespace format
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_TESTS_FORMAT_MESSAGE_HPP
@@ -0,0 +1,189 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_FORMAT_TESTS_FORMAT_SIMPLE_HPP
#define MPT_FORMAT_TESTS_FORMAT_SIMPLE_HPP
#include "mpt/base/detect.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/format/simple.hpp"
#include "mpt/format/simple_integer.hpp"
#include "mpt/string/types.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <limits>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace format {
namespace simple {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/format/simple")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(1.5f), "1.5");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(true), "1");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(false), "0");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(0), "0");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(-23), "-23");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(42), "42");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex0<3>((int32)-1), "-001");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex((int32)-1), "-1");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(-0xabcde), "-abcde");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(std::numeric_limits<int32>::min()), "-80000000");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(std::numeric_limits<int32>::min() + 1), "-7fffffff");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(0x123e), "123e");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex0<6>(0x123e), "00123e");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex0<2>(0x123e), "123e");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<0>(1), "1");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<1>(1), "1");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<2>(1), "01");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<3>(1), "001");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<0>(11), "11");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<1>(11), "11");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<2>(11), "11");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<3>(11), "011");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<0>(-1), "-1");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<1>(-1), "-1");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<2>(-1), "-01");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec0<3>(-1), "-001");
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(0xa2345678), MPT_USTRING("A2345678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<8>(0xa2345678), MPT_USTRING("A2345678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<9>(0xa2345678), MPT_USTRING("0A2345678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<10>(0xa2345678), MPT_USTRING("00A2345678"));
#if MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::min(), 10), "-32768");
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::max(), 10), "32767");
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::min(), 7), "-164351");
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::min() + 1, 7), "-164350");
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_chars<std::string>(std::numeric_limits<int16>::max(), 7), "164350");
#else // !MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::min(), 10), "-32768");
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::max(), 10), "32767");
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::min(), 7), "-164351");
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::min() + 1, 7), "-164350");
MPT_TEST_EXPECT_EQUAL(mpt::format_simple_integer_to_stream<std::string>(std::numeric_limits<int16>::max(), 7), "164350");
#endif // MPT_FORMAT_FORMAT_SIMPLE_INT_CXX17
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::hex(0x123e), L"123e");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::hex0<6>(0x123e), L"00123e");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::hex0<2>(0x123e), L"123e");
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::val(-87.0f), "-87");
if (mpt::format<std::string>::val(-0.5e-6) != "-5e-007"
&& mpt::format<std::string>::val(-0.5e-6) != "-5e-07"
&& mpt::format<std::string>::val(-0.5e-6) != "-5e-7"
&& mpt::format<std::string>::val(-0.5e-6) != "-4.9999999999999998e-7"
&& mpt::format<std::string>::val(-0.5e-6) != "-4.9999999999999998e-07"
&& mpt::format<std::string>::val(-0.5e-6) != "-4.9999999999999998e-007")
{
MPT_TEST_EXPECT_EQUAL(true, false);
}
if (mpt::format<std::string>::val(-1.0 / 65536.0) != "-1.52587890625e-005"
&& mpt::format<std::string>::val(-1.0 / 65536.0) != "-1.52587890625e-05"
&& mpt::format<std::string>::val(-1.0 / 65536.0) != "-1.52587890625e-5")
{
MPT_TEST_EXPECT_EQUAL(true, false);
}
if (mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.52587891e-005"
&& mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.52587891e-05"
&& mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.52587891e-5"
&& mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.5258789e-005"
&& mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.5258789e-05"
&& mpt::format<std::string>::val(-1.0f / 65536.0f) != "-1.5258789e-5")
{
MPT_TEST_EXPECT_EQUAL(true, false);
}
if (mpt::format<std::string>::val(58.65403492763) != "58.654034927630001"
&& mpt::format<std::string>::val(58.65403492763) != "58.65403492763")
{
MPT_TEST_EXPECT_EQUAL(true, false);
}
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::flt(58.65403492763, 6), "58.654");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(23.42, 1), "23.4");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(234.2, 1), "234.2");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(2342.0, 1), "2342.0");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec(2, ';', 2345678), std::string("2;34;56;78"));
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::dec(2, ';', 12345678), std::string("12;34;56;78"));
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::hex(3, ':', 0xa2345678), std::string("a2:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::dec(2, ';', 12345678), MPT_USTRING("12;34;56;78"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::hex(3, ':', 0xa2345678), MPT_USTRING("a2:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(3, ':', 0xa2345678), MPT_USTRING("A2:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<8>(3, ':', 0xa2345678), MPT_USTRING("A2:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<9>(3, ':', 0xa2345678), MPT_USTRING("0A2:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<10>(3, ':', 0xa2345678), MPT_USTRING("0:0A2:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<11>(3, ':', 0xa2345678), MPT_USTRING("00:0A2:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<12>(3, ':', 0xa2345678), MPT_USTRING("000:0A2:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(3, ':', -0x12345678), MPT_USTRING("-12:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<8>(3, ':', -0x12345678), MPT_USTRING("-12:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<9>(3, ':', -0x12345678), MPT_USTRING("-012:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<10>(3, ':', -0x12345678), MPT_USTRING("-0:012:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<11>(3, ':', -0x12345678), MPT_USTRING("-00:012:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<12>(3, ':', -0x12345678), MPT_USTRING("-000:012:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<5>(3, ':', 0x345678), MPT_USTRING("345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<6>(3, ':', 0x345678), MPT_USTRING("345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(3, ':', 0x345678), MPT_USTRING("0:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<5>(3, ':', -0x345678), MPT_USTRING("-345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<6>(3, ':', -0x345678), MPT_USTRING("-345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<mpt::ustring>::HEX0<7>(3, ':', -0x345678), MPT_USTRING("-0:345:678"));
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::left(3, "a"), "a ");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::right(3, "a"), " a");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::center(3, "a"), " a ");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::center(4, "a"), " a ");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::flt(6.12345, 3), "6.12");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(6.12345, 3), "6.123");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::flt(6.12345, 4), "6.123");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::string>::fix(6.12345, 4), "6.1235");
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::flt(6.12345, 3), L"6.12");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::fix(6.12345, 3), L"6.123");
MPT_TEST_EXPECT_EQUAL(mpt::format<std::wstring>::flt(6.12345, 4), L"6.123");
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
}
} // namespace simple
} // namespace format
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_FORMAT_TESTS_FORMAT_SIMPLE_HPP
@@ -0,0 +1,122 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_BASE_HPP
#define MPT_IO_BASE_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
using Offset = int64;
inline constexpr std::size_t BUFFERSIZE_MINUSCULE = 1 * 256; // on stack usage, tuned for single word/line buffers
inline constexpr std::size_t BUFFERSIZE_TINY = 1 * 1024; // on stack usage
inline constexpr std::size_t BUFFERSIZE_SMALL = 4 * 1024; // on heap
inline constexpr std::size_t BUFFERSIZE_NORMAL = 64 * 1024; // FILE I/O
inline constexpr std::size_t BUFFERSIZE_LARGE = 1024 * 1024;
template <typename Tfile, typename Enable = void>
struct FileOperations {
};
template <typename Tfile>
inline FileOperations<Tfile> FileOps(Tfile & f) {
;
return FileOperations<Tfile>{f};
}
template <typename Tfile>
inline bool IsValid(Tfile & f) {
return FileOps(f).IsValid();
}
template <typename Tfile>
inline bool IsReadSeekable(Tfile & f) {
return FileOps(f).IsReadSeekable();
}
template <typename Tfile>
inline bool IsWriteSeekable(Tfile & f) {
return FileOps(f).IsWriteSeekable();
}
template <typename Tfile>
inline IO::Offset TellRead(Tfile & f) {
return FileOps(f).TellRead();
}
template <typename Tfile>
inline IO::Offset TellWrite(Tfile & f) {
return FileOps(f).TellWrite();
}
template <typename Tfile>
inline bool SeekBegin(Tfile & f) {
return FileOps(f).SeekBegin();
}
template <typename Tfile>
inline bool SeekEnd(Tfile & f) {
return FileOps(f).SeekEnd();
}
template <typename Tfile>
inline bool SeekAbsolute(Tfile & f, IO::Offset pos) {
return FileOps(f).SeekAbsolute(pos);
}
template <typename Tfile>
inline bool SeekRelative(Tfile & f, IO::Offset off) {
return FileOps(f).SeekRelative(off);
}
template <typename Tfile>
inline mpt::byte_span ReadRawImpl(Tfile & f, mpt::byte_span data) {
return FileOps(f).ReadRawImpl(data);
}
template <typename Tfile>
inline bool WriteRawImpl(Tfile & f, mpt::const_byte_span data) {
return FileOps(f).WriteRawImpl(data);
}
template <typename Tfile>
inline bool IsEof(Tfile & f) {
return FileOps(f).IsEof();
}
template <typename Tfile>
inline bool Flush(Tfile & f) {
return FileOps(f).Flush();
}
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_BASE_HPP
@@ -0,0 +1,367 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_IO_HPP
#define MPT_IO_IO_HPP
#include "mpt/base/array.hpp"
#include "mpt/base/bit.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/span.hpp"
#include "mpt/endian/integer.hpp"
#include "mpt/io/base.hpp"
#include <algorithm>
#include <limits>
#include <string>
#include <vector>
#include <cassert>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
template <typename Tbyte, typename Tfile>
inline mpt::byte_span ReadRaw(Tfile & f, Tbyte * data, std::size_t size) {
return mpt::IO::ReadRawImpl(f, mpt::as_span(mpt::byte_cast<std::byte *>(data), size));
}
template <typename Tbyte, typename Tfile>
inline mpt::byte_span ReadRaw(Tfile & f, mpt::span<Tbyte> data) {
return mpt::IO::ReadRawImpl(f, mpt::byte_cast<mpt::byte_span>(data));
}
template <typename Tbyte, typename Tfile>
inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size) {
return mpt::IO::WriteRawImpl(f, mpt::as_span(mpt::byte_cast<const std::byte *>(data), size));
}
template <typename Tbyte, typename Tfile>
inline bool WriteRaw(Tfile & f, mpt::span<Tbyte> data) {
return mpt::IO::WriteRawImpl(f, mpt::byte_cast<mpt::const_byte_span>(data));
}
template <typename Tbinary, typename Tfile>
inline bool Read(Tfile & f, Tbinary & v) {
return mpt::IO::ReadRaw(f, mpt::as_raw_memory(v)).size() == mpt::as_raw_memory(v).size();
}
template <typename Tbinary, typename Tfile>
inline bool Write(Tfile & f, const Tbinary & v) {
return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v));
}
template <typename Tbinary, typename Tfile>
inline bool Write(Tfile & f, const std::vector<Tbinary> & v) {
static_assert(mpt::is_binary_safe<Tbinary>::value);
return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v));
}
template <typename T, typename Tfile>
inline bool WritePartial(Tfile & f, const T & v, std::size_t size = sizeof(T)) {
assert(size <= sizeof(T));
return mpt::IO::WriteRaw(f, mpt::as_span(mpt::as_raw_memory(v).data(), size));
}
template <typename Tfile>
inline bool ReadByte(Tfile & f, std::byte & v) {
bool result = false;
std::byte byte = mpt::as_byte(0);
const std::size_t readResult = mpt::IO::ReadRaw(f, &byte, sizeof(std::byte)).size();
result = (readResult == sizeof(std::byte));
v = byte;
return result;
}
template <typename T, typename Tfile>
inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) {
bool result = false;
static_assert(std::numeric_limits<T>::is_integer);
std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
const std::size_t readResult = mpt::IO::ReadRaw(f, bytes.data(), std::min(size, sizeof(T))).size();
result = (readResult == std::min(size, sizeof(T)));
v = mpt::bit_cast<typename mpt::make_le<T>::type>(bytes);
return result;
}
template <typename T, typename Tfile>
inline bool ReadIntLE(Tfile & f, T & v) {
bool result = false;
static_assert(std::numeric_limits<T>::is_integer);
std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size();
result = (readResult == sizeof(T));
v = mpt::bit_cast<typename mpt::make_le<T>::type>(bytes);
return result;
}
template <typename T, typename Tfile>
inline bool ReadIntBE(Tfile & f, T & v) {
bool result = false;
static_assert(std::numeric_limits<T>::is_integer);
std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size();
result = (readResult == sizeof(T));
v = mpt::bit_cast<typename mpt::make_be<T>::type>(bytes);
return result;
}
template <typename Tfile>
inline bool ReadAdaptiveInt16LE(Tfile & f, uint16 & v) {
bool result = true;
uint8 byte = 0;
std::size_t additionalBytes = 0;
v = 0;
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
additionalBytes = (byte & 0x01);
v = byte >> 1;
for (std::size_t i = 0; i < additionalBytes; ++i) {
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
v |= (static_cast<uint16>(byte) << (((i + 1) * 8) - 1));
}
return result;
}
template <typename Tfile>
inline bool ReadAdaptiveInt32LE(Tfile & f, uint32 & v) {
bool result = true;
uint8 byte = 0;
std::size_t additionalBytes = 0;
v = 0;
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
additionalBytes = (byte & 0x03);
v = byte >> 2;
for (std::size_t i = 0; i < additionalBytes; ++i) {
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
v |= (static_cast<uint32>(byte) << (((i + 1) * 8) - 2));
}
return result;
}
template <typename Tfile>
inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v) {
bool result = true;
uint8 byte = 0;
std::size_t additionalBytes = 0;
v = 0;
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
additionalBytes = (1 << (byte & 0x03)) - 1;
v = byte >> 2;
for (std::size_t i = 0; i < additionalBytes; ++i) {
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
v |= (static_cast<uint64>(byte) << (((i + 1) * 8) - 2));
}
return result;
}
template <typename Tsize, typename Tfile>
inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std::numeric_limits<Tsize>::max()) {
static_assert(std::numeric_limits<Tsize>::is_integer);
str.clear();
Tsize size = 0;
if (!mpt::IO::ReadIntLE(f, size)) {
return false;
}
if (size > maxSize) {
return false;
}
for (Tsize i = 0; i != size; ++i) {
char c = '\0';
if (!mpt::IO::ReadIntLE(f, c)) {
return false;
}
str.push_back(c);
}
return true;
}
template <typename T, typename Tfile>
inline bool WriteIntLE(Tfile & f, const T v) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::IO::Write(f, mpt::as_le(v));
}
template <typename T, typename Tfile>
inline bool WriteIntBE(Tfile & f, const T v) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::IO::Write(f, mpt::as_be(v));
}
template <typename Tfile>
inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t fixedSize = 0) {
std::size_t minSize = fixedSize;
std::size_t maxSize = fixedSize;
assert(minSize == 0 || minSize == 1 || minSize == 2);
assert(maxSize == 0 || maxSize == 1 || maxSize == 2);
assert(maxSize == 0 || maxSize >= minSize);
if (maxSize == 0) {
maxSize = 2;
}
if (v < 0x80 && minSize <= 1 && 1 <= maxSize) {
return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 1) | 0x00);
} else if (v < 0x8000 && minSize <= 2 && 2 <= maxSize) {
return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 1) | 0x01);
} else {
assert(false);
return false;
}
}
template <typename Tfile>
inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t fixedSize = 0) {
std::size_t minSize = fixedSize;
std::size_t maxSize = fixedSize;
assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 3 || minSize == 4);
assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 3 || maxSize == 4);
assert(maxSize == 0 || maxSize >= minSize);
if (maxSize == 0) {
maxSize = 4;
}
if (v < 0x40 && minSize <= 1 && 1 <= maxSize) {
return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
} else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) {
return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
} else if (v < 0x400000 && minSize <= 3 && 3 <= maxSize) {
uint32 value = static_cast<uint32>(v << 2) | 0x02;
std::byte bytes[3];
bytes[0] = static_cast<std::byte>(value >> 0);
bytes[1] = static_cast<std::byte>(value >> 8);
bytes[2] = static_cast<std::byte>(value >> 16);
return mpt::IO::WriteRaw(f, bytes, 3);
} else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) {
return mpt::IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x03);
} else {
assert(false);
return false;
}
}
template <typename Tfile>
inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t fixedSize = 0) {
std::size_t minSize = fixedSize;
std::size_t maxSize = fixedSize;
assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 4 || minSize == 8);
assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 4 || maxSize == 8);
assert(maxSize == 0 || maxSize >= minSize);
if (maxSize == 0) {
maxSize = 8;
}
if (v < 0x40 && minSize <= 1 && 1 <= maxSize) {
return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
} else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) {
return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
} else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) {
return mpt::IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x02);
} else if (v < 0x4000000000000000ull && minSize <= 8 && 8 <= maxSize) {
return mpt::IO::WriteIntLE<uint64>(f, static_cast<uint64>(v << 2) | 0x03);
} else {
assert(false);
return false;
}
}
// Write a variable-length integer, as found in MIDI files. The number of written bytes is placed in the bytesWritten parameter.
template <typename Tfile, typename T>
bool WriteVarInt(Tfile & f, const T v, std::size_t * bytesWritten = nullptr) {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(!std::numeric_limits<T>::is_signed);
std::byte out[(sizeof(T) * 8 + 6) / 7];
std::size_t numBytes = 0;
for (uint32 n = (sizeof(T) * 8) / 7; n > 0; n--) {
if (v >= (static_cast<T>(1) << (n * 7u))) {
out[numBytes++] = static_cast<std::byte>(((v >> (n * 7u)) & 0x7F) | 0x80);
}
}
out[numBytes++] = static_cast<std::byte>(v & 0x7F);
assert(numBytes <= std::size(out));
if (bytesWritten != nullptr) {
*bytesWritten = numBytes;
}
return mpt::IO::WriteRaw(f, out, numBytes);
}
template <typename Tsize, typename Tfile>
inline bool WriteSizedStringLE(Tfile & f, const std::string & str) {
static_assert(std::numeric_limits<Tsize>::is_integer);
if (str.size() > std::numeric_limits<Tsize>::max()) {
return false;
}
Tsize size = static_cast<Tsize>(str.size());
if (!mpt::IO::WriteIntLE(f, size)) {
return false;
}
if (!mpt::IO::WriteRaw(f, str.data(), str.size())) {
return false;
}
return true;
}
template <typename Tfile>
inline bool WriteText(Tfile & f, const std::string & s) {
return mpt::IO::WriteRaw(f, s.data(), s.size());
}
template <typename Tfile>
inline bool WriteTextCRLF(Tfile & f) {
return mpt::IO::WriteText(f, "\r\n");
}
template <typename Tfile>
inline bool WriteTextLF(Tfile & f) {
return mpt::IO::WriteText(f, "\n");
}
template <typename Tfile>
inline bool WriteTextCRLF(Tfile & f, const std::string & s) {
return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextCRLF(f);
}
template <typename Tfile>
inline bool WriteTextLF(Tfile & f, const std::string & s) {
return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextLF(f);
}
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_IO_HPP
@@ -0,0 +1,146 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_IO_SPAN_HPP
#define MPT_IO_IO_SPAN_HPP
#include "mpt/base/macros.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/saturate_cast.hpp"
#include "mpt/base/span.hpp"
#include "mpt/io/base.hpp"
#include <algorithm>
#include <utility>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
template <typename Tbyte>
struct FileOperations<std::pair<mpt::span<Tbyte>, IO::Offset>> {
private:
std::pair<mpt::span<Tbyte>, IO::Offset> & f;
public:
FileOperations(std::pair<mpt::span<Tbyte>, IO::Offset> & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return (f.second >= 0);
}
inline bool IsReadSeekable() {
MPT_UNUSED(f);
return true;
}
inline bool IsWriteSeekable() {
MPT_UNUSED(f);
return true;
}
inline IO::Offset TellRead() {
return f.second;
}
inline IO::Offset TellWrite() {
return f.second;
}
inline bool SeekBegin() {
f.second = 0;
return true;
}
inline bool SeekEnd() {
f.second = f.first.size();
return true;
}
inline bool SeekAbsolute(IO::Offset pos) {
f.second = pos;
return true;
}
inline bool SeekRelative(IO::Offset off) {
if (f.second < 0)
{
return false;
}
f.second += off;
return true;
}
inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
if (f.second < 0)
{
return data.first(0);
}
if (f.second >= static_cast<IO::Offset>(f.first.size()))
{
return data.first(0);
}
std::size_t num = mpt::saturate_cast<std::size_t>(std::min(static_cast<IO::Offset>(f.first.size()) - f.second, static_cast<IO::Offset>(data.size())));
std::copy(mpt::byte_cast<const std::byte *>(f.first.data() + f.second), mpt::byte_cast<const std::byte *>(f.first.data() + f.second + num), data.data());
f.second += num;
return data.first(num);
}
inline bool WriteRawImpl(mpt::const_byte_span data) {
if (f.second < 0)
{
return false;
}
if (f.second > static_cast<IO::Offset>(f.first.size()))
{
return false;
}
std::size_t num = mpt::saturate_cast<std::size_t>(std::min(static_cast<IO::Offset>(f.first.size()) - f.second, static_cast<IO::Offset>(data.size())));
if (num != data.size())
{
return false;
}
std::copy(data.data(), data.data() + num, mpt::byte_cast<std::byte *>(f.first.data() + f.second));
f.second += num;
return true;
}
inline bool IsEof() {
return (f.second >= static_cast<IO::Offset>(f.first.size()));
}
inline bool Flush() {
MPT_UNUSED(f);
return true;
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_IO_SPAN_HPP
@@ -0,0 +1,332 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_IO_STDSTREAM_HPP
#define MPT_IO_IO_STDSTREAM_HPP
#include "mpt/base/macros.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/utility.hpp"
#include "mpt/io/base.hpp"
#include <ios>
#include <istream>
#include <ostream>
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
//static_assert(sizeof(std::streamoff) == 8); // Assert 64bit file support.
struct FileOperationsStdIos {
private:
std::ios & f;
public:
FileOperationsStdIos(std::ios & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return !f.fail();
}
};
struct FileOperationsStdIstream
: public FileOperationsStdIos {
private:
std::istream & f;
public:
FileOperationsStdIstream(std::istream & f_)
: FileOperationsStdIos(f_)
, f(f_) {
return;
}
public:
inline bool IsReadSeekable() {
f.clear();
std::streampos oldpos = f.tellg();
if (f.fail() || oldpos == std::streampos(-1))
{
f.clear();
return false;
}
f.seekg(0, std::ios::beg);
if (f.fail())
{
f.clear();
f.seekg(oldpos);
f.clear();
return false;
}
f.seekg(0, std::ios::end);
if (f.fail())
{
f.clear();
f.seekg(oldpos);
f.clear();
return false;
}
std::streampos length = f.tellg();
if (f.fail() || length == std::streampos(-1))
{
f.clear();
f.seekg(oldpos);
f.clear();
return false;
}
f.seekg(oldpos);
f.clear();
return true;
}
inline IO::Offset TellRead() {
return f.tellg();
}
inline bool SeekBegin() {
f.seekg(0);
return !f.fail();
}
inline bool SeekEnd() {
f.seekg(0, std::ios::end);
return !f.fail();
}
inline bool SeekAbsolute(IO::Offset pos) {
if (!mpt::in_range<std::streamoff>(pos))
{
return false;
}
f.seekg(static_cast<std::streamoff>(pos), std::ios::beg);
return !f.fail();
}
inline bool SeekRelative(IO::Offset off) {
if (!mpt::in_range<std::streamoff>(off))
{
return false;
}
f.seekg(static_cast<std::streamoff>(off), std::ios::cur);
return !f.fail();
}
inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
f.read(mpt::byte_cast<char *>(data.data()), data.size());
return data.first(mpt::saturate_cast<std::size_t>(f.gcount()));
}
inline bool IsEof() {
return f.eof();
}
};
struct FileOperationsStdOstream
: public FileOperationsStdIos {
private:
std::ostream & f;
public:
FileOperationsStdOstream(std::ostream & f_)
: FileOperationsStdIos(f_)
, f(f_) {
return;
}
public:
inline bool IsWriteSeekable() {
f.clear();
std::streampos oldpos = f.tellp();
if (f.fail() || oldpos == std::streampos(-1))
{
f.clear();
return false;
}
f.seekp(0, std::ios::beg);
if (f.fail())
{
f.clear();
f.seekp(oldpos);
f.clear();
return false;
}
f.seekp(0, std::ios::end);
if (f.fail())
{
f.clear();
f.seekp(oldpos);
f.clear();
return false;
}
std::streampos length = f.tellp();
if (f.fail() || length == std::streampos(-1))
{
f.clear();
f.seekp(oldpos);
f.clear();
return false;
}
f.seekp(oldpos);
f.clear();
return true;
}
inline IO::Offset TellWrite() {
return f.tellp();
}
inline bool SeekBegin() {
f.seekp(0);
return !f.fail();
}
inline bool SeekEnd() {
f.seekp(0, std::ios::end);
return !f.fail();
}
inline bool SeekAbsolute(IO::Offset pos) {
if (!mpt::in_range<std::streamoff>(pos))
{
return false;
}
f.seekp(static_cast<std::streamoff>(pos), std::ios::beg);
return !f.fail();
}
inline bool SeekRelative(IO::Offset off) {
if (!mpt::in_range<std::streamoff>(off))
{
return false;
}
f.seekp(static_cast<std::streamoff>(off), std::ios::cur);
return !f.fail();
}
inline bool WriteRawImpl(mpt::const_byte_span data) {
f.write(mpt::byte_cast<const char *>(data.data()), data.size());
return !f.fail();
}
inline bool Flush() {
f.flush();
return !f.fail();
}
};
struct FileOperationsStdIOstream
: public FileOperationsStdIstream
, public FileOperationsStdOstream {
private:
std::iostream & f;
public:
FileOperationsStdIOstream(std::iostream & f_)
: FileOperationsStdIstream(f_)
, FileOperationsStdOstream(f_)
, f(f_) {
return;
}
public:
inline bool SeekBegin() {
FileOperationsStdIstream::SeekBegin();
FileOperationsStdOstream::SeekBegin();
return !f.fail();
}
inline bool SeekEnd() {
FileOperationsStdIstream::SeekEnd();
FileOperationsStdOstream::SeekEnd();
return !f.fail();
}
inline bool SeekAbsolute(IO::Offset pos) {
if (!mpt::in_range<std::streamoff>(pos))
{
return false;
}
FileOperationsStdIstream::SeekAbsolute(pos);
FileOperationsStdOstream::SeekAbsolute(pos);
return !f.fail();
}
inline bool SeekRelative(IO::Offset off) {
if (!mpt::in_range<std::streamoff>(off))
{
return false;
}
FileOperationsStdIstream::SeekRelative(off);
FileOperationsStdOstream::SeekRelative(off);
return !f.fail();
}
};
template <typename Tstream>
struct FileOperations<Tstream, typename std::enable_if_t<std::is_base_of<std::iostream, Tstream>::value>>
: public FileOperationsStdIOstream {
public:
FileOperations(Tstream & f)
: FileOperationsStdIOstream(f) {
return;
}
};
template <typename Tstream>
struct FileOperations<Tstream, typename std::enable_if_t<std::is_base_of<std::istream, Tstream>::value && !std::is_base_of<std::iostream, Tstream>::value>>
: public FileOperationsStdIstream {
public:
FileOperations(Tstream & f)
: FileOperationsStdIstream(f) {
return;
}
};
template <typename Tstream>
struct FileOperations<Tstream, typename std::enable_if_t<std::is_base_of<std::ostream, Tstream>::value && !std::is_base_of<std::iostream, Tstream>::value>>
: public FileOperationsStdOstream {
public:
FileOperations(Tstream & f)
: FileOperationsStdOstream(f) {
return;
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_IO_STDSTREAM_HPP
@@ -0,0 +1,405 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_IO_VIRTUAL_WRAPPER_HPP
#define MPT_IO_IO_VIRTUAL_WRAPPER_HPP
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/io/base.hpp"
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
class IFileBase {
protected:
IFileBase() = default;
virtual ~IFileBase() = default;
public:
virtual bool IsValid() = 0;
virtual bool IsReadSeekable() = 0;
virtual IO::Offset TellRead() = 0;
virtual bool SeekBegin() = 0;
virtual bool SeekEnd() = 0;
virtual bool SeekAbsolute(IO::Offset pos) = 0;
virtual bool SeekRelative(IO::Offset off) = 0;
virtual mpt::byte_span ReadRawImpl(mpt::byte_span data) = 0;
virtual bool IsEof() = 0;
};
template <typename Tfile>
class IFile
: public IFileBase {
private:
Tfile & f;
public:
IFile(Tfile & f_)
: f(f_) {
}
~IFile() override = default;
public:
bool IsValid() override {
return mpt::IO::IsValid(f);
}
bool IsReadSeekable() override {
return mpt::IO::IsReadSeekable(f);
}
IO::Offset TellRead() override {
return mpt::IO::TellRead(f);
}
bool SeekBegin() override {
return mpt::IO::SeekBegin(f);
}
bool SeekEnd() override {
return mpt::IO::SeekEnd(f);
}
bool SeekAbsolute(IO::Offset pos) override {
return mpt::IO::SeekAbsolute(f, pos);
}
bool SeekRelative(IO::Offset off) override {
return mpt::IO::SeekRelative(f, off);
}
mpt::byte_span ReadRawImpl(mpt::byte_span data) override {
return mpt::IO::ReadRawImpl(f, data);
}
bool IsEof() override {
return mpt::IO::IsEof(f);
}
};
class OFileBase {
protected:
OFileBase() = default;
virtual ~OFileBase() = default;
public:
virtual bool IsValid() = 0;
virtual bool IsWriteSeekable() = 0;
virtual IO::Offset TellWrite() = 0;
virtual bool SeekBegin() = 0;
virtual bool SeekEnd() = 0;
virtual bool SeekAbsolute(IO::Offset pos) = 0;
virtual bool SeekRelative(IO::Offset off) = 0;
virtual bool WriteRawImpl(mpt::const_byte_span data) = 0;
virtual bool Flush() = 0;
};
template <typename Tfile>
class OFile
: public OFileBase {
private:
Tfile & f;
public:
OFile(Tfile & f_)
: f(f_) {
}
~OFile() override = default;
public:
bool IsValid() override {
return mpt::IO::IsValid(f);
}
bool IsWriteSeekable() override {
return mpt::IO::IsWriteSeekable(f);
}
IO::Offset TellWrite() override {
return mpt::IO::TellWrite(f);
}
bool SeekBegin() override {
return mpt::IO::SeekBegin(f);
}
bool SeekEnd() override {
return mpt::IO::SeekEnd(f);
}
bool SeekAbsolute(IO::Offset pos) override {
return mpt::IO::SeekAbsolute(f, pos);
}
bool SeekRelative(IO::Offset off) override {
return mpt::IO::SeekRelative(f, off);
}
bool WriteRawImpl(mpt::const_byte_span data) override {
return mpt::IO::WriteRawImpl(f, data);
}
bool Flush() override {
return mpt::IO::Flush(f);
}
};
class IOFileBase {
protected:
IOFileBase() = default;
virtual ~IOFileBase() = default;
public:
virtual bool IsValid() = 0;
virtual bool IsReadSeekable() = 0;
virtual bool IsWriteSeekable() = 0;
virtual IO::Offset TellRead() = 0;
virtual IO::Offset TellWrite() = 0;
virtual bool SeekBegin() = 0;
virtual bool SeekEnd() = 0;
virtual bool SeekAbsolute(IO::Offset pos) = 0;
virtual bool SeekRelative(IO::Offset off) = 0;
virtual mpt::byte_span ReadRawImpl(mpt::byte_span data) = 0;
virtual bool WriteRawImpl(mpt::const_byte_span data) = 0;
virtual bool IsEof() = 0;
virtual bool Flush() = 0;
};
template <typename Tfile>
class IOFile
: public IOFileBase {
private:
Tfile & f;
public:
IOFile(Tfile & f_)
: f(f_) {
}
~IOFile() override = default;
public:
bool IsValid() override {
return mpt::IO::IsValid(f);
}
bool IsReadSeekable() override {
return mpt::IO::IsReadSeekable(f);
}
bool IsWriteSeekable() override {
return mpt::IO::IsWriteSeekable(f);
}
IO::Offset TellRead() override {
return mpt::IO::TellRead(f);
}
IO::Offset TellWrite() override {
return mpt::IO::TellWrite(f);
}
bool SeekBegin() override {
return mpt::IO::SeekBegin(f);
}
bool SeekEnd() override {
return mpt::IO::SeekEnd(f);
}
bool SeekAbsolute(IO::Offset pos) override {
return mpt::IO::SeekAbsolute(f, pos);
}
bool SeekRelative(IO::Offset off) override {
return mpt::IO::SeekRelative(f, off);
}
mpt::byte_span ReadRawImpl(mpt::byte_span data) override {
return mpt::IO::ReadRawImpl(f, data);
}
bool WriteRawImpl(mpt::const_byte_span data) override {
return mpt::IO::WriteRawImpl(f, data);
}
bool IsEof() override {
return mpt::IO::IsEof(f);
}
bool Flush() override {
return mpt::IO::Flush(f);
}
};
template <typename Tfile>
struct FileOperations<Tfile, typename std::enable_if_t<std::is_base_of<IFileBase, Tfile>::value>> {
private:
IFileBase & f;
public:
FileOperations(IFileBase & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return f.IsValid();
}
inline bool IsReadSeekable() {
return f.IsReadSeekable();
}
inline IO::Offset TellRead() {
return f.TellRead();
}
inline bool SeekBegin() {
return f.SeekBegin();
}
inline bool SeekEnd() {
return f.SeekEnd();
}
inline bool SeekAbsolute(IO::Offset pos) {
return f.SeekAbsolute(pos);
}
inline bool SeekRelative(IO::Offset off) {
return f.SeekRelative(off);
}
inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
return f.ReadRawImpl(data);
}
inline bool IsEof() {
return f.IsEof();
}
};
template <typename Tfile>
struct FileOperations<Tfile, typename std::enable_if_t<std::is_base_of<OFileBase, Tfile>::value>> {
private:
OFileBase & f;
public:
FileOperations(OFileBase & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return f.IsValid();
}
inline bool IsWriteSeekable() {
return f.IsWriteSeekable();
}
inline IO::Offset TellWrite() {
return f.TellWrite();
}
inline bool SeekBegin() {
return f.SeekBegin();
}
inline bool SeekEnd() {
return f.SeekEnd();
}
inline bool SeekAbsolute(IO::Offset pos) {
return f.SeekAbsolute(pos);
}
inline bool SeekRelative(IO::Offset off) {
return f.SeekRelative(off);
}
inline bool WriteRawImpl(mpt::const_byte_span data) {
return f.WriteRawImpl(data);
}
inline bool Flush() {
return f.Flush();
}
};
template <typename Tfile>
struct FileOperations<Tfile, typename std::enable_if_t<std::is_base_of<IOFileBase, Tfile>::value>> {
private:
IOFileBase & f;
public:
FileOperations(IOFileBase & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return f.IsValid();
}
inline bool IsReadSeekable() {
return f.IsReadSeekable();
}
inline bool IsWriteSeekable() {
return f.IsWriteSeekable();
}
inline IO::Offset TellRead() {
return f.TellRead();
}
inline IO::Offset TellWrite() {
return f.TellWrite();
}
inline bool SeekBegin() {
return f.SeekBegin();
}
inline bool SeekEnd() {
return f.SeekEnd();
}
inline bool SeekAbsolute(IO::Offset pos) {
return f.SeekAbsolute(pos);
}
inline bool SeekRelative(IO::Offset off) {
return f.SeekRelative(off);
}
inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
return f.ReadRawImpl(data);
}
inline bool WriteRawImpl(mpt::const_byte_span data) {
return f.WriteRawImpl(data);
}
inline bool IsEof() {
return f.IsEof();
}
inline bool Flush() {
return f.Flush();
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_IO_VIRTUAL_WRAPPER_HPP
@@ -0,0 +1,573 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_TESTS_IO_HPP
#define MPT_IO_TESTS_IO_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/utility.hpp"
#include "mpt/endian/integer.hpp"
#include "mpt/io/base.hpp"
#include "mpt/io/io.hpp"
#include "mpt/io/io_stdstream.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <ios>
#include <sstream>
#include <string>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace io {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/io")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
// check that empty stringstream behaves correctly with our MSVC workarounds when using iostream interface directly
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(ss.tellp(), std::streampos(0));
}
{
std::ostringstream ss;
ss.seekp(0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::ostringstream ss;
ss.seekp(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
}
{
std::ostringstream ss;
ss.seekp(0, std::ios_base::cur);
MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(ss.tellg(), std::streampos(0));
}
{
std::istringstream ss;
ss.seekg(0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::istringstream ss;
ss.seekg(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
}
{
std::istringstream ss;
ss.seekg(0, std::ios_base::cur);
MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
}
{
std::ostringstream s;
char b = 23;
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekp(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.write(&b, 1);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(1));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekp(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekp(0, std::ios_base::end);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(1));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b));
}
{
std::istringstream s;
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::end);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
}
{
std::istringstream s("a");
char a = 0;
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.read(&a, 1);
MPT_TEST_EXPECT_EQUAL(a, 'a');
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(1));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::end);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(1));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a'));
}
// check that empty native and fixed stringstream both behaves correctly with out IO functions
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(ss), 0);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(ss), 0);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(ss), 0);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(ss), 0);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
}
{
std::ostringstream s;
char b = 23;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteRaw(s, &b, 1), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b));
}
{
std::istringstream s;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
}
{
std::istringstream s("a");
char a = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadRaw(s, &a, 1).size(), 1u);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a'));
}
{
std::ostringstream s;
char b = 23;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteRaw(s, &b, 1), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b));
}
{
std::istringstream s;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
}
{
std::istringstream s("a");
char a = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadRaw(s, &a, 1).size(), 1u);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a'));
}
// General file I/O tests
{
// Verify that writing arrays does not confuse the compiler.
// This is both, compile-time and run-time cheking.
// Run-time in case some weird compiler gets confused by our templates
// and only writes the first array element.
std::ostringstream f;
uint16be data[2];
mpt::reset(data);
data[0] = 0x1234;
data[1] = 0x5678;
mpt::IO::Write(f, data);
MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78"));
}
{
std::ostringstream f;
std::vector<int16be> data;
data.resize(3);
data[0] = 0x1234;
data[1] = 0x5678;
data[2] = 0x1234;
mpt::IO::Write(f, data);
MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34"));
}
{
std::ostringstream f;
int16be data[3];
mpt::reset(data);
data[0] = 0x1234;
data[1] = 0x5678;
data[2] = 0x1234;
mpt::IO::Write(f, data);
MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34"));
}
{
auto TestAdaptive16 = [&](uint16 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) {
std::stringstream f;
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt16LE(f, value, fixedSize), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size);
if (bytes) {
mpt::IO::SeekBegin(f);
for (mpt::IO::Offset i = 0; i < expected_size; ++i) {
uint8 val = 0;
mpt::IO::ReadIntLE<uint8>(f, val);
MPT_TEST_EXPECT_EQUAL(val, static_cast<uint8>(bytes[i]));
}
}
mpt::IO::SeekBegin(f);
uint16 result = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt16LE(f, result), true);
MPT_TEST_EXPECT_EQUAL(result, value);
};
auto TestAdaptive32 = [&](uint32 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) {
std::stringstream f;
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt32LE(f, value, fixedSize), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size);
if (bytes) {
mpt::IO::SeekBegin(f);
for (mpt::IO::Offset i = 0; i < expected_size; ++i) {
uint8 val = 0;
mpt::IO::ReadIntLE<uint8>(f, val);
MPT_TEST_EXPECT_EQUAL(val, static_cast<uint8>(bytes[i]));
}
}
mpt::IO::SeekBegin(f);
uint32 result = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt32LE(f, result), true);
MPT_TEST_EXPECT_EQUAL(result, value);
};
auto TestAdaptive64 = [&](uint64 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) {
std::stringstream f;
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt64LE(f, value, fixedSize), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size);
if (bytes) {
mpt::IO::SeekBegin(f);
for (mpt::IO::Offset i = 0; i < expected_size; ++i) {
uint8 val = 0;
mpt::IO::ReadIntLE<uint8>(f, val);
MPT_TEST_EXPECT_EQUAL(val, static_cast<uint8>(bytes[i]));
}
}
mpt::IO::SeekBegin(f);
uint64 result = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt64LE(f, result), true);
MPT_TEST_EXPECT_EQUAL(result, value);
};
TestAdaptive16(0, 1, 0, "\x00");
TestAdaptive16(1, 1, 0, "\x02");
TestAdaptive16(2, 1, 0, nullptr);
TestAdaptive16(0x7f, 1, 0, nullptr);
TestAdaptive16(0x80, 2, 0, "\x01\x01");
TestAdaptive16(0x81, 2, 0, "\x03\x01");
TestAdaptive16(0x7fff, 2, 0, "\xff\xff");
TestAdaptive16(0, 1, 1, nullptr);
TestAdaptive16(1, 1, 1, nullptr);
TestAdaptive16(2, 1, 1, nullptr);
TestAdaptive16(0x7f, 1, 1, nullptr);
TestAdaptive16(0x80, 2, 0, nullptr);
TestAdaptive16(0x81, 2, 0, nullptr);
TestAdaptive16(0x7fff, 2, 0, nullptr);
TestAdaptive16(0, 2, 2, "\x01\x00");
TestAdaptive16(1, 2, 2, "\x03\x00");
TestAdaptive16(2, 2, 2, nullptr);
TestAdaptive16(0x7f, 2, 2, nullptr);
TestAdaptive16(0x80, 2, 2, nullptr);
TestAdaptive16(0x81, 2, 2, nullptr);
TestAdaptive16(0x7fff, 2, 2, nullptr);
TestAdaptive32(0, 1, 0, "\x00");
TestAdaptive32(1, 1, 0, nullptr);
TestAdaptive32(2, 1, 0, nullptr);
TestAdaptive32(0x3f, 1, 0, nullptr);
TestAdaptive32(0x40, 2, 0, "\x01\x01");
TestAdaptive32(0x41, 2, 0, "\x05\x01");
TestAdaptive32(0x7f, 2, 0, nullptr);
TestAdaptive32(0x80, 2, 0, nullptr);
TestAdaptive32(0x3fff, 2, 0, nullptr);
TestAdaptive32(0x4000, 3, 0, "\x02\x00\x01");
TestAdaptive32(0x4001, 3, 0, nullptr);
TestAdaptive32(0x3fffff, 3, 0, nullptr);
TestAdaptive32(0x400000, 4, 0, "\x03\x00\x00\x01");
TestAdaptive32(0x400001, 4, 0, nullptr);
TestAdaptive32(0x3fffffff, 4, 0, "\xff\xff\xff\xff");
TestAdaptive32(0, 2, 2, nullptr);
TestAdaptive32(1, 2, 2, nullptr);
TestAdaptive32(2, 2, 2, nullptr);
TestAdaptive32(0x3f, 2, 2, nullptr);
TestAdaptive32(0x40, 2, 2, nullptr);
TestAdaptive32(0x41, 2, 2, nullptr);
TestAdaptive32(0x7f, 2, 2, nullptr);
TestAdaptive32(0x80, 2, 2, nullptr);
TestAdaptive32(0x3fff, 2, 2, nullptr);
TestAdaptive32(0, 3, 3, nullptr);
TestAdaptive32(1, 3, 3, nullptr);
TestAdaptive32(2, 3, 3, nullptr);
TestAdaptive32(0x3f, 3, 3, nullptr);
TestAdaptive32(0x40, 3, 3, nullptr);
TestAdaptive32(0x41, 3, 3, nullptr);
TestAdaptive32(0x7f, 3, 3, nullptr);
TestAdaptive32(0x80, 3, 3, nullptr);
TestAdaptive32(0x3fff, 3, 3, nullptr);
TestAdaptive32(0x4000, 3, 3, nullptr);
TestAdaptive32(0x4001, 3, 3, nullptr);
TestAdaptive32(0x3fffff, 3, 3, nullptr);
TestAdaptive32(0, 4, 4, nullptr);
TestAdaptive32(1, 4, 4, nullptr);
TestAdaptive32(2, 4, 4, nullptr);
TestAdaptive32(0x3f, 4, 4, nullptr);
TestAdaptive32(0x40, 4, 4, nullptr);
TestAdaptive32(0x41, 4, 4, nullptr);
TestAdaptive32(0x7f, 4, 4, nullptr);
TestAdaptive32(0x80, 4, 4, nullptr);
TestAdaptive32(0x3fff, 4, 4, nullptr);
TestAdaptive32(0x4000, 4, 4, nullptr);
TestAdaptive32(0x4001, 4, 4, nullptr);
TestAdaptive32(0x3fffff, 4, 4, nullptr);
TestAdaptive32(0x400000, 4, 4, nullptr);
TestAdaptive32(0x400001, 4, 4, nullptr);
TestAdaptive32(0x3fffffff, 4, 4, nullptr);
TestAdaptive64(0, 1, 0, nullptr);
TestAdaptive64(1, 1, 0, nullptr);
TestAdaptive64(2, 1, 0, nullptr);
TestAdaptive64(0x3f, 1, 0, nullptr);
TestAdaptive64(0x40, 2, 0, nullptr);
TestAdaptive64(0x41, 2, 0, nullptr);
TestAdaptive64(0x7f, 2, 0, nullptr);
TestAdaptive64(0x80, 2, 0, nullptr);
TestAdaptive64(0x3fff, 2, 0, nullptr);
TestAdaptive64(0x4000, 4, 0, nullptr);
TestAdaptive64(0x4001, 4, 0, nullptr);
TestAdaptive64(0x3fffff, 4, 0, nullptr);
TestAdaptive64(0x400000, 4, 0, nullptr);
TestAdaptive64(0x400001, 4, 0, nullptr);
TestAdaptive64(0x3fffffff, 4, 0, nullptr);
TestAdaptive64(0x40000000, 8, 0, nullptr);
TestAdaptive64(0x40000001, 8, 0, nullptr);
TestAdaptive64(0x3fffffffffffffffull, 8, 0, nullptr);
TestAdaptive64(0, 2, 2, nullptr);
TestAdaptive64(1, 2, 2, nullptr);
TestAdaptive64(2, 2, 2, nullptr);
TestAdaptive64(0x3f, 2, 2, nullptr);
TestAdaptive64(0, 4, 4, nullptr);
TestAdaptive64(1, 4, 4, nullptr);
TestAdaptive64(2, 4, 4, nullptr);
TestAdaptive64(0x3f, 4, 4, nullptr);
TestAdaptive64(0x40, 4, 4, nullptr);
TestAdaptive64(0x41, 4, 4, nullptr);
TestAdaptive64(0x7f, 4, 4, nullptr);
TestAdaptive64(0x80, 4, 4, nullptr);
TestAdaptive64(0x3fff, 4, 4, nullptr);
TestAdaptive64(0, 8, 8, nullptr);
TestAdaptive64(1, 8, 8, nullptr);
TestAdaptive64(2, 8, 8, nullptr);
TestAdaptive64(0x3f, 8, 8, nullptr);
TestAdaptive64(0x40, 8, 8, nullptr);
TestAdaptive64(0x41, 8, 8, nullptr);
TestAdaptive64(0x7f, 8, 8, nullptr);
TestAdaptive64(0x80, 8, 8, nullptr);
TestAdaptive64(0x3fff, 8, 8, nullptr);
TestAdaptive64(0x4000, 8, 8, nullptr);
TestAdaptive64(0x4001, 8, 8, nullptr);
TestAdaptive64(0x3fffff, 8, 8, nullptr);
TestAdaptive64(0x400000, 8, 8, nullptr);
TestAdaptive64(0x400001, 8, 8, nullptr);
TestAdaptive64(0x3fffffff, 8, 8, nullptr);
TestAdaptive64(0x40000000, 8, 8, nullptr);
TestAdaptive64(0x40000001, 8, 8, nullptr);
TestAdaptive64(0x3fffffffffffffffull, 8, 8, nullptr);
}
}
} // namespace io
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_TESTS_IO_HPP
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_CALLBACKSTREAM_HPP
#define MPT_IO_READ_CALLBACKSTREAM_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
template <typename Tstream>
struct CallbackStreamTemplate {
enum : int {
SeekSet = 0,
SeekCur = 1,
SeekEnd = 2
};
Tstream stream;
std::size_t (*read)(Tstream stream, void * dst, std::size_t bytes);
int (*seek)(Tstream stream, int64 offset, int whence);
int64 (*tell)(Tstream stream);
};
using CallbackStream = CallbackStreamTemplate<void *>;
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_CALLBACKSTREAM_HPP
@@ -0,0 +1,419 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILECURSOR_HPP
#define MPT_IO_READ_FILECURSOR_HPP
#include "mpt/base/alloc.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/span.hpp"
#include <algorithm>
#include <optional>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
// change to show warnings for functions which trigger pre-caching the whole file for unseekable streams
//#define MPT_FILECURSOR_DEPRECATED [[deprecated]]
#define MPT_FILECURSOR_DEPRECATED
template <typename Ttraits, typename Tfilenametraits>
class FileCursor {
private:
using traits_type = Ttraits;
using filename_traits_type = Tfilenametraits;
public:
using pos_type = typename traits_type::pos_type;
using data_type = typename traits_type::data_type;
using ref_data_type = typename traits_type::ref_data_type;
using shared_data_type = typename traits_type::shared_data_type;
using value_data_type = typename traits_type::value_data_type;
using filename_type = typename filename_traits_type::filename_type;
using shared_filename_type = typename filename_traits_type::shared_filename_type;
protected:
shared_data_type SharedDataContainer() const {
return traits_type::get_shared(m_data);
}
ref_data_type DataContainer() const {
return traits_type::get_ref(m_data);
}
static value_data_type DataInitializer() {
return traits_type::make_data();
}
static value_data_type DataInitializer(mpt::const_byte_span data) {
return traits_type::make_data(data);
}
static value_data_type CreateChunkImpl(shared_data_type data, pos_type position, pos_type size) {
return traits_type::make_chunk(data, position, size);
}
private:
data_type m_data;
pos_type streamPos; // Cursor location in the file
shared_filename_type m_fileName; // Filename that corresponds to this FileCursor. It is only set if this FileCursor represents the whole contents of fileName. May be nullopt.
public:
// Initialize invalid file reader object.
FileCursor()
: m_data(DataInitializer())
, streamPos(0)
, m_fileName(nullptr) {
return;
}
// Initialize file reader object with pointer to data and data length.
template <typename Tbyte>
explicit FileCursor(mpt::span<Tbyte> bytedata, shared_filename_type filename = shared_filename_type{})
: m_data(DataInitializer(mpt::byte_cast<mpt::const_byte_span>(bytedata)))
, streamPos(0)
, m_fileName(std::move(filename)) {
return;
}
// Initialize file reader object based on an existing file reader object window.
explicit FileCursor(value_data_type other, shared_filename_type filename = shared_filename_type{})
: m_data(std::move(other))
, streamPos(0)
, m_fileName(std::move(filename)) {
return;
}
public:
std::optional<filename_type> GetOptionalFileName() const {
return filename_traits_type::get_optional_filename(m_fileName);
}
// Returns true if the object points to a valid (non-empty) stream.
operator bool() const {
return IsValid();
}
// Returns true if the object points to a valid (non-empty) stream.
bool IsValid() const {
return DataContainer().IsValid();
}
// Reset cursor to first byte in file.
void Rewind() {
streamPos = 0;
}
// Seek to a position in the mapped file.
// Returns false if position is invalid.
bool Seek(pos_type position) {
if (position <= streamPos) {
streamPos = position;
return true;
}
if (position <= DataContainer().GetLength()) {
streamPos = position;
return true;
} else {
return false;
}
}
// Increases position by skipBytes.
// Returns true if skipBytes could be skipped or false if the file end was reached earlier.
bool Skip(pos_type skipBytes) {
if (CanRead(skipBytes)) {
streamPos += skipBytes;
return true;
} else {
streamPos = DataContainer().GetLength();
return false;
}
}
// Decreases position by skipBytes.
// Returns true if skipBytes could be skipped or false if the file start was reached earlier.
bool SkipBack(pos_type skipBytes) {
if (streamPos >= skipBytes) {
streamPos -= skipBytes;
return true;
} else {
streamPos = 0;
return false;
}
}
// Returns cursor position in the mapped file.
pos_type GetPosition() const {
return streamPos;
}
// Return true IFF seeking and GetLength() is fast.
// In particular, it returns false for unseekable stream where GetLength()
// requires pre-caching.
bool HasFastGetLength() const {
return DataContainer().HasFastGetLength();
}
// Returns size of the mapped file in bytes.
MPT_FILECURSOR_DEPRECATED pos_type GetLength() const {
// deprecated because in case of an unseekable std::istream, this triggers caching of the whole file
return DataContainer().GetLength();
}
// Return byte count between cursor position and end of file, i.e. how many bytes can still be read.
MPT_FILECURSOR_DEPRECATED pos_type BytesLeft() const {
// deprecated because in case of an unseekable std::istream, this triggers caching of the whole file
return DataContainer().GetLength() - streamPos;
}
bool EndOfFile() const {
return !CanRead(1);
}
bool NoBytesLeft() const {
return !CanRead(1);
}
// Check if "amount" bytes can be read from the current position in the stream.
bool CanRead(pos_type amount) const {
return DataContainer().CanRead(streamPos, amount);
}
// Check if file size is at least size, without potentially caching the whole file to query the exact file length.
bool LengthIsAtLeast(pos_type size) const {
return DataContainer().CanRead(0, size);
}
// Check if file size is exactly size, without potentially caching the whole file if it is larger.
bool LengthIs(pos_type size) const {
return DataContainer().CanRead(0, size) && !DataContainer().CanRead(size, 1);
}
protected:
FileCursor CreateChunk(pos_type position, pos_type length) const {
pos_type readableLength = DataContainer().GetReadableLength(position, length);
if (readableLength == 0)
{
return FileCursor();
}
return FileCursor(CreateChunkImpl(SharedDataContainer(), position, std::min(length, DataContainer().GetLength() - position)));
}
public:
// Create a new FileCursor object for parsing a sub chunk at a given position with a given length.
// The file cursor is not modified.
FileCursor GetChunkAt(pos_type position, pos_type length) const {
return CreateChunk(position, length);
}
// Create a new FileCursor object for parsing a sub chunk at the current position with a given length.
// The file cursor is not advanced.
FileCursor GetChunk(pos_type length) {
return CreateChunk(streamPos, length);
}
// Create a new FileCursor object for parsing a sub chunk at the current position with a given length.
// The file cursor is advanced by "length" bytes.
FileCursor ReadChunk(pos_type length) {
pos_type position = streamPos;
Skip(length);
return CreateChunk(position, length);
}
class PinnedView {
private:
std::size_t size_;
const std::byte * pinnedData;
std::vector<std::byte> cache;
private:
void Init(const FileCursor & file, std::size_t size) {
size_ = 0;
pinnedData = nullptr;
if (!file.CanRead(size)) {
size = file.BytesLeft();
}
size_ = size;
if (file.DataContainer().HasPinnedView()) {
pinnedData = file.DataContainer().GetRawData() + file.GetPosition();
} else {
cache.resize(size_);
if (!cache.empty()) {
file.GetRaw(mpt::as_span(cache));
}
}
}
public:
PinnedView()
: size_(0)
, pinnedData(nullptr) {
}
PinnedView(const FileCursor & file) {
Init(file, file.BytesLeft());
}
PinnedView(const FileCursor & file, std::size_t size) {
Init(file, size);
}
PinnedView(FileCursor & file, bool advance) {
Init(file, file.BytesLeft());
if (advance)
{
file.Skip(size_);
}
}
PinnedView(FileCursor & file, std::size_t size, bool advance) {
Init(file, size);
if (advance) {
file.Skip(size_);
}
}
public:
mpt::const_byte_span GetSpan() const {
if (pinnedData) {
return mpt::as_span(pinnedData, size_);
} else if (!cache.empty()) {
return mpt::as_span(cache);
} else {
return mpt::const_byte_span();
}
}
mpt::const_byte_span span() const {
return GetSpan();
}
void invalidate() {
size_ = 0;
pinnedData = nullptr;
cache = std::vector<std::byte>();
}
const std::byte * data() const {
return span().data();
}
std::size_t size() const {
return size_;
}
mpt::const_byte_span::pointer begin() const {
return span().data();
}
mpt::const_byte_span::pointer end() const {
return span().data() + span().size();
}
mpt::const_byte_span::const_pointer cbegin() const {
return span().data();
}
mpt::const_byte_span::const_pointer cend() const {
return span().data() + span().size();
}
};
// Returns a pinned view into the remaining raw data from cursor position.
PinnedView GetPinnedView() const {
return PinnedView(*this);
}
// Returns a pinned view into the remeining raw data from cursor position, clamped at size.
PinnedView GetPinnedView(std::size_t size) const {
return PinnedView(*this, size);
}
// Returns a pinned view into the remeining raw data from cursor position.
// File cursor is advaned by the size of the returned pinned view.
PinnedView ReadPinnedView() {
return PinnedView(*this, true);
}
// Returns a pinned view into the remeining raw data from cursor position, clamped at size.
// File cursor is advaned by the size of the returned pinned view.
PinnedView ReadPinnedView(std::size_t size) {
return PinnedView(*this, size, true);
}
// Returns raw stream data at cursor position.
// Should only be used if absolutely necessary, for example for sample reading, or when used with a small chunk of the file retrieved by ReadChunk().
// Use GetPinnedView(size) whenever possible.
MPT_FILECURSOR_DEPRECATED mpt::const_byte_span GetRawData() const {
// deprecated because in case of an unseekable std::istream, this triggers caching of the whole file
return mpt::span(DataContainer().GetRawData() + streamPos, DataContainer().GetLength() - streamPos);
}
template <typename T>
MPT_FILECURSOR_DEPRECATED mpt::span<const T> GetRawData() const {
// deprecated because in case of an unseekable std::istream, this triggers caching of the whole file
return mpt::span(mpt::byte_cast<const T *>(DataContainer().GetRawData() + streamPos), DataContainer().GetLength() - streamPos);
}
mpt::byte_span GetRawWithOffset(std::size_t offset, mpt::byte_span dst) const {
return DataContainer().Read(streamPos + offset, dst);
}
template <typename Tspan>
Tspan GetRawWithOffset(std::size_t offset, Tspan dst) const {
return mpt::byte_cast<Tspan>(DataContainer().Read(streamPos + offset, mpt::byte_cast<mpt::byte_span>(dst)));
}
mpt::byte_span GetRaw(mpt::byte_span dst) const {
return DataContainer().Read(streamPos, dst);
}
template <typename Tspan>
Tspan GetRaw(Tspan dst) const {
return mpt::byte_cast<Tspan>(DataContainer().Read(streamPos, mpt::byte_cast<mpt::byte_span>(dst)));
}
mpt::byte_span ReadRaw(mpt::byte_span dst) {
mpt::byte_span result = DataContainer().Read(streamPos, dst);
streamPos += result.size();
return result;
}
template <typename Tspan>
Tspan ReadRaw(Tspan dst) {
Tspan result = mpt::byte_cast<Tspan>(DataContainer().Read(streamPos, mpt::byte_cast<mpt::byte_span>(dst)));
streamPos += result.size();
return result;
}
std::vector<std::byte> GetRawDataAsByteVector() const {
PinnedView view = GetPinnedView();
return mpt::make_vector(view.span());
}
std::vector<std::byte> ReadRawDataAsByteVector() {
PinnedView view = ReadPinnedView();
return mpt::make_vector(view.span());
}
std::vector<std::byte> GetRawDataAsByteVector(std::size_t size) const {
PinnedView view = GetPinnedView(size);
return mpt::make_vector(view.span());
}
std::vector<std::byte> ReadRawDataAsByteVector(std::size_t size) {
PinnedView view = ReadPinnedView(size);
return mpt::make_vector(view.span());
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILECURSOR_HPP
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILECURSOR_CALLBACKSTREAM_HPP
#define MPT_IO_READ_FILECURSOR_CALLBACKSTREAM_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/io_read/callbackstream.hpp"
#include "mpt/io_read/filecursor.hpp"
#include "mpt/io_read/filecursor_traits_filedata.hpp"
#include "mpt/io_read/filecursor_filename_traits.hpp"
#include "mpt/io_read/filedata_callbackstream.hpp"
#include <memory>
#include <utility>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
// Initialize file reader object with a CallbackStream.
template <typename Tpath, typename Tstream>
inline FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>> make_FileCursor(CallbackStreamTemplate<Tstream> s, std::shared_ptr<Tpath> filename = nullptr) {
if (FileDataCallbackStreamTemplate<Tstream>::IsSeekable(s)) {
return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(std::static_pointer_cast<IFileData>(std::make_shared<FileDataCallbackStreamSeekableTemplate<Tstream>>(s)), std::move(filename));
} else {
return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(std::static_pointer_cast<IFileData>(std::make_shared<FileDataCallbackStreamUnseekableTemplate<Tstream>>(s)), std::move(filename));
}
}
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILECURSOR_CALLBACKSTREAM_HPP
@@ -0,0 +1,63 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILECURSOR_FILENAME_TRAITS_HPP
#define MPT_IO_READ_FILECURSOR_FILENAME_TRAITS_HPP
#include "mpt/base/namespace.hpp"
#include <memory>
#include <optional>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
class FileCursorFilenameTraitsNone {
public:
struct empty_type { };
using filename_type = empty_type;
using shared_filename_type = empty_type;
static std::optional<filename_type> get_optional_filename(shared_filename_type /* filename */) {
return std::nullopt;
}
};
template <typename Tpath>
class FileCursorFilenameTraits {
public:
using filename_type = Tpath;
using shared_filename_type = std::shared_ptr<Tpath>;
static std::optional<filename_type> get_optional_filename(shared_filename_type filename) {
if (!filename) {
return std::nullopt;
}
return *filename;
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILECURSOR_FILENAME_TRAITS_HPP
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILECURSOR_MEMORY_HPP
#define MPT_IO_READ_FILECURSOR_MEMORY_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/base/span.hpp"
#include "mpt/io_read/filecursor.hpp"
#include "mpt/io_read/filecursor_traits_filedata.hpp"
#include "mpt/io_read/filecursor_filename_traits.hpp"
#include <memory>
#include <utility>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
// Initialize file reader object with pointer to data and data length.
template <typename Tpath, typename Tbyte>
inline FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>> make_FileCursor(mpt::span<Tbyte> bytedata, std::shared_ptr<Tpath> filename = nullptr) {
return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(mpt::byte_cast<mpt::const_byte_span>(bytedata), std::move(filename));
}
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILECURSOR_STDSTREAM_HPP
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILECURSOR_STDSTREAM_HPP
#define MPT_IO_READ_FILECURSOR_STDSTREAM_HPP
#include "mpt/base/namespace.hpp"
#include "mpt/io_read/filecursor.hpp"
#include "mpt/io_read/filecursor_traits_filedata.hpp"
#include "mpt/io_read/filecursor_filename_traits.hpp"
#include "mpt/io_read/filedata_stdstream.hpp"
#include <istream>
#include <memory>
#include <utility>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
// Initialize file reader object with a std::istream.
template <typename Tpath>
inline FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>> make_FileCursor(std::istream & s, std::shared_ptr<Tpath> filename = nullptr) {
if (FileDataStdStream::IsSeekable(s)) {
return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(std::static_pointer_cast<IFileData>(std::make_shared<FileDataStdStreamSeekable>(s)), std::move(filename));
} else {
return FileCursor<FileCursorTraitsFileData, FileCursorFilenameTraits<Tpath>>(std::static_pointer_cast<IFileData>(std::make_shared<FileDataStdStreamUnseekable>(s)), std::move(filename));
}
}
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILECURSOR_STDSTREAM_HPP
@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILECURSOR_TRAITS_FILEDATA_HPP
#define MPT_IO_READ_FILECURSOR_TRAITS_FILEDATA_HPP
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/io_read/filedata.hpp"
#include "mpt/io_read/filedata_base.hpp"
#include "mpt/io_read/filedata_memory.hpp"
#include <memory>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
class FileCursorTraitsFileData {
public:
using pos_type = IFileData::pos_type;
using data_type = std::shared_ptr<const IFileData>;
using ref_data_type = const IFileData &;
using shared_data_type = std::shared_ptr<const IFileData>;
using value_data_type = std::shared_ptr<const IFileData>;
static shared_data_type get_shared(const data_type & data) {
return data;
}
static ref_data_type get_ref(const data_type & data) {
return *data;
}
static value_data_type make_data() {
return std::make_shared<FileDataDummy>();
}
static value_data_type make_data(mpt::const_byte_span data) {
return std::make_shared<FileDataMemory>(data);
}
static value_data_type make_chunk(shared_data_type data, pos_type position, pos_type size) {
return std::static_pointer_cast<IFileData>(std::make_shared<FileDataWindow>(data, position, size));
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILECURSOR_TRAITS_FILEDATA_HPP
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILECURSOR_TRAITS_MEMORY_HPP
#define MPT_IO_READ_FILECURSOR_TRAITS_MEMORY_HPP
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/span.hpp"
#include "mpt/io_read/filedata.hpp"
#include "mpt/io_read/filedata_memory.hpp"
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
class FileCursorTraitsMemory {
public:
using pos_type = FileDataMemory::pos_type;
using data_type = FileDataMemory;
using ref_data_type = const FileDataMemory &;
using shared_data_type = const FileDataMemory &;
using value_data_type = FileDataMemory;
static shared_data_type get_shared(const data_type & data) {
return data;
}
static ref_data_type get_ref(const data_type & data) {
return data;
}
static value_data_type make_data() {
return mpt::const_byte_span();
}
static value_data_type make_data(mpt::const_byte_span data) {
return data;
}
static value_data_type make_chunk(shared_data_type data, pos_type position, pos_type size) {
return mpt::as_span(data.GetRawData() + position, size);
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILECURSOR_TRAITS_MEMORY_HPP
@@ -0,0 +1,77 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILEDATA_HPP
#define MPT_IO_READ_FILEDATA_HPP
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include <algorithm>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
class IFileData {
public:
typedef std::size_t pos_type;
protected:
IFileData() = default;
public:
IFileData(const IFileData &) = default;
IFileData & operator=(const IFileData &) = default;
virtual ~IFileData() = default;
public:
virtual bool IsValid() const = 0;
virtual bool HasFastGetLength() const = 0;
virtual bool HasPinnedView() const = 0;
virtual const std::byte * GetRawData() const = 0;
virtual pos_type GetLength() const = 0;
virtual mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const = 0;
virtual bool CanRead(pos_type pos, std::size_t length) const {
pos_type dataLength = GetLength();
if ((pos == dataLength) && (length == 0)) {
return true;
}
if (pos >= dataLength) {
return false;
}
return length <= dataLength - pos;
}
virtual std::size_t GetReadableLength(pos_type pos, std::size_t length) const {
pos_type dataLength = GetLength();
if (pos >= dataLength) {
return 0;
}
return std::min(length, dataLength - pos);
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILEDATA_HPP
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILEDATA_BASE_HPP
#define MPT_IO_READ_FILEDATA_BASE_HPP
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/io_read/filedata.hpp"
#include <algorithm>
#include <memory>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
class FileDataDummy : public IFileData {
public:
FileDataDummy() { }
public:
bool IsValid() const override {
return false;
}
bool HasFastGetLength() const override {
return true;
}
bool HasPinnedView() const override {
return true;
}
const std::byte * GetRawData() const override {
return nullptr;
}
pos_type GetLength() const override {
return 0;
}
mpt::byte_span Read(pos_type /* pos */, mpt::byte_span dst) const override {
return dst.first(0);
}
};
class FileDataWindow : public IFileData {
private:
std::shared_ptr<const IFileData> data;
const pos_type dataOffset;
const pos_type dataLength;
public:
FileDataWindow(std::shared_ptr<const IFileData> src, pos_type off, pos_type len)
: data(src)
, dataOffset(off)
, dataLength(len) { }
bool IsValid() const override {
return data->IsValid();
}
bool HasFastGetLength() const override {
return data->HasFastGetLength();
}
bool HasPinnedView() const override {
return data->HasPinnedView();
}
const std::byte * GetRawData() const override {
return data->GetRawData() + dataOffset;
}
pos_type GetLength() const override {
return dataLength;
}
mpt::byte_span Read(pos_type pos, mpt::byte_span dst) const override {
if (pos >= dataLength) {
return dst.first(0);
}
return data->Read(dataOffset + pos, dst.first(std::min(dst.size(), dataLength - pos)));
}
bool CanRead(pos_type pos, std::size_t length) const override {
if ((pos == dataLength) && (length == 0)) {
return true;
}
if (pos >= dataLength) {
return false;
}
return (length <= dataLength - pos);
}
pos_type GetReadableLength(pos_type pos, std::size_t length) const override {
if (pos >= dataLength) {
return 0;
}
return std::min(length, dataLength - pos);
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILEDATA_BASE_HPP
@@ -0,0 +1,122 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_READ_FILEDATA_BASE_BUFFERED_HPP
#define MPT_IO_READ_FILEDATA_BASE_BUFFERED_HPP
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/numeric.hpp"
#include "mpt/io/base.hpp"
#include "mpt/io_read/filedata.hpp"
#include "mpt/io_read/filedata_base_seekable.hpp"
#include <algorithm>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
class FileDataSeekableBuffered : public FileDataSeekable {
private:
enum : std::size_t {
CHUNK_SIZE = mpt::IO::BUFFERSIZE_SMALL,
BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL
};
enum : std::size_t {
NUM_CHUNKS = BUFFER_SIZE / CHUNK_SIZE
};
struct chunk_info {
pos_type ChunkOffset = 0;
pos_type ChunkLength = 0;
bool ChunkValid = false;
};
mutable std::vector<std::byte> m_Buffer = std::vector<std::byte>(BUFFER_SIZE);
mpt::byte_span chunk_data(std::size_t chunkIndex) const {
return mpt::byte_span(m_Buffer.data() + (chunkIndex * CHUNK_SIZE), CHUNK_SIZE);
}
mutable std::array<chunk_info, NUM_CHUNKS> m_ChunkInfo = {};
mutable std::array<std::size_t, NUM_CHUNKS> m_ChunkIndexLRU = {};
std::size_t InternalFillPageAndReturnIndex(pos_type pos) const {
pos = mpt::align_down(pos, static_cast<pos_type>(CHUNK_SIZE));
for (std::size_t chunkLRUIndex = 0; chunkLRUIndex < NUM_CHUNKS; ++chunkLRUIndex) {
std::size_t chunkIndex = m_ChunkIndexLRU[chunkLRUIndex];
if (m_ChunkInfo[chunkIndex].ChunkValid && (m_ChunkInfo[chunkIndex].ChunkOffset == pos)) {
std::size_t chunk = std::move(m_ChunkIndexLRU[chunkLRUIndex]);
std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + chunkLRUIndex, m_ChunkIndexLRU.begin() + (chunkLRUIndex + 1));
m_ChunkIndexLRU[0] = std::move(chunk);
return chunkIndex;
}
}
{
std::size_t chunk = std::move(m_ChunkIndexLRU[NUM_CHUNKS - 1]);
std::move_backward(m_ChunkIndexLRU.begin(), m_ChunkIndexLRU.begin() + (NUM_CHUNKS - 1), m_ChunkIndexLRU.begin() + NUM_CHUNKS);
m_ChunkIndexLRU[0] = std::move(chunk);
}
std::size_t chunkIndex = m_ChunkIndexLRU[0];
chunk_info & chunk = m_ChunkInfo[chunkIndex];
chunk.ChunkOffset = pos;
chunk.ChunkLength = InternalReadBuffered(pos, chunk_data(chunkIndex)).size();
chunk.ChunkValid = true;
return chunkIndex;
}
protected:
FileDataSeekableBuffered(pos_type streamLength_)
: FileDataSeekable(streamLength_) {
return;
}
private:
mpt::byte_span InternalReadSeekable(pos_type pos, mpt::byte_span dst) const override {
pos_type totalRead = 0;
std::byte * pdst = dst.data();
std::size_t count = dst.size();
while (count > 0) {
std::size_t chunkIndex = InternalFillPageAndReturnIndex(pos);
pos_type pageSkip = pos - m_ChunkInfo[chunkIndex].ChunkOffset;
pos_type chunkWanted = std::min(static_cast<pos_type>(CHUNK_SIZE) - pageSkip, count);
pos_type chunkGot = (m_ChunkInfo[chunkIndex].ChunkLength > pageSkip) ? (m_ChunkInfo[chunkIndex].ChunkLength - pageSkip) : 0;
pos_type chunk = std::min(chunkWanted, chunkGot);
std::copy(chunk_data(chunkIndex).data() + pageSkip, chunk_data(chunkIndex).data() + pageSkip + chunk, pdst);
pos += chunk;
pdst += chunk;
totalRead += chunk;
count -= chunk;
if (chunkWanted > chunk) {
return dst.first(totalRead);
}
}
return dst.first(totalRead);
}
virtual mpt::byte_span InternalReadBuffered(pos_type pos, mpt::byte_span dst) const = 0;
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_READ_FILEDATA_BASE_BUFFERED_HPP

Some files were not shown because too many files have changed in this diff Show More