Initial community commit
This commit is contained in:
@@ -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
|
||||
+328
@@ -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
|
||||
+112
@@ -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
|
||||
+65
@@ -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
|
||||
+178
@@ -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
|
||||
+81
@@ -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
|
||||
+133
@@ -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
|
||||
+63
@@ -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
|
||||
+67
@@ -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
Reference in New Issue
Block a user