mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-16 14:08:59 +07:00
initial basic utils
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.9.2 FATAL_ERROR)
|
||||
project(enduro2d)
|
||||
|
||||
#
|
||||
# external packages
|
||||
#
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
#
|
||||
# global defines
|
||||
#
|
||||
@@ -23,12 +29,11 @@ set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}${E2D_RELEASE_FLAGS})
|
||||
#
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Werror -Weverything)
|
||||
add_compile_options(-Wno-c++98-compat -Wno-c++98-compat-pedantic)
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
add_compile_options(-Werror -Wall -Wextra -Wpedantic)
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
add_compile_options(/WX /W4)
|
||||
add_compile_options(/W4)
|
||||
endif()
|
||||
|
||||
#
|
||||
|
||||
21
ROADMAP.md
21
ROADMAP.md
@@ -16,25 +16,24 @@
|
||||
```
|
||||
|
||||
- ### `basic utils`
|
||||
- [ ] `path`
|
||||
- [x] `path`
|
||||
```
|
||||
functions to manipulating paths in a portable way
|
||||
```
|
||||
- [ ] `color, color32`
|
||||
- [ ] `basic string functions`
|
||||
- [x] `color, color32`
|
||||
- [x] `basic string functions`
|
||||
```
|
||||
unicode convertions, wildcard patterns, type safe format
|
||||
```
|
||||
- [ ] `timer and time functions`
|
||||
- [ ] `image, image loaders`
|
||||
- [x] `timer and time functions`
|
||||
- [x] `image, image loaders`
|
||||
```
|
||||
basic raw and compressed formats
|
||||
internal: g8, ga8, rgb8, rgba8, dxt1/3/5
|
||||
external: png, jpg, tga, dds
|
||||
basic true color formats
|
||||
internal: g8, ga8, rgb8, rgba8
|
||||
external: png, jpg, tga
|
||||
```
|
||||
- [ ] `streams and native file system`
|
||||
- [ ] `thread pool and async tasks`
|
||||
- [ ] `pseudo-random number generator`
|
||||
- [x] `streams and native file system`
|
||||
- [x] `thread pool and async tasks`
|
||||
|
||||
## `Milestone II`
|
||||
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -25,9 +27,11 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include <new>
|
||||
#include <memory>
|
||||
#include <ratio>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <exception>
|
||||
|
||||
@@ -30,4 +30,22 @@
|
||||
// E2D_UNUSED
|
||||
//
|
||||
|
||||
#define E2D_UNUSED(expr) (void)(expr)
|
||||
template < typename T >
|
||||
void E2D_UNUSED(T&& arg) noexcept {
|
||||
(void)arg;
|
||||
}
|
||||
|
||||
template < typename T, typename... Ts >
|
||||
void E2D_UNUSED(T&& arg, Ts&&... args) noexcept {
|
||||
E2D_UNUSED(arg);
|
||||
E2D_UNUSED(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
//
|
||||
// E2D_COUNTOF
|
||||
//
|
||||
|
||||
template < typename T, std::size_t N >
|
||||
constexpr std::size_t E2D_COUNTOF(const T(&)[N]) noexcept {
|
||||
return N;
|
||||
}
|
||||
|
||||
@@ -6,44 +6,517 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_base.hpp"
|
||||
#include "macros.hpp"
|
||||
|
||||
#define E2D_STDEX_NOEXCEPT_RETURN(...) \
|
||||
noexcept(noexcept(__VA_ARGS__)) { return __VA_ARGS__; }
|
||||
|
||||
#define E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(...) \
|
||||
noexcept(noexcept(__VA_ARGS__)) -> decltype (__VA_ARGS__) { return __VA_ARGS__; }
|
||||
|
||||
//
|
||||
// void_t
|
||||
//
|
||||
|
||||
namespace e2d { namespace stdex
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
template < typename... Args >
|
||||
struct make_void {
|
||||
using type = void;
|
||||
};
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
using void_t = typename impl::make_void<Args...>::type;
|
||||
}}
|
||||
|
||||
//
|
||||
// is_reference_wrapper
|
||||
//
|
||||
|
||||
namespace e2d { namespace stdex
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
template < typename T >
|
||||
struct unique_if {
|
||||
using unique_single = std::unique_ptr<T>;
|
||||
};
|
||||
struct is_reference_wrapper_impl
|
||||
: std::false_type {};
|
||||
|
||||
template < typename T >
|
||||
struct unique_if<T[]> {
|
||||
using unique_array_unknown_bound = std::unique_ptr<T[]>;
|
||||
};
|
||||
|
||||
template < typename T, std::size_t N >
|
||||
struct unique_if<T[N]> {
|
||||
using unique_array_known_bound = void;
|
||||
};
|
||||
}
|
||||
|
||||
template < typename T, typename... Args >
|
||||
typename impl::unique_if<T>::unique_single
|
||||
make_unique(Args&&... args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
template < typename U >
|
||||
struct is_reference_wrapper_impl<std::reference_wrapper<U>>
|
||||
: std::true_type {};
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
typename impl::unique_if<T>::unique_array_unknown_bound
|
||||
make_unique(std::size_t n)
|
||||
{
|
||||
using U = std::remove_extent_t<T>;
|
||||
return std::unique_ptr<T>(new U[n]());
|
||||
}
|
||||
|
||||
template < typename T, typename... Args >
|
||||
typename impl::unique_if<T>::unique_array_known_bound
|
||||
make_unique(Args&&...) = delete;
|
||||
struct is_reference_wrapper
|
||||
: impl::is_reference_wrapper_impl<std::remove_cv_t<T>> {};
|
||||
}}
|
||||
|
||||
//
|
||||
// invoke
|
||||
//
|
||||
|
||||
namespace e2d { namespace stdex
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
//
|
||||
// invoke_member_object_impl
|
||||
//
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename Derived,
|
||||
typename std::enable_if_t<std::is_base_of<Base, std::decay_t<Derived>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_object_impl(F Base::* f, Derived&& ref)
|
||||
E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(
|
||||
std::forward<Derived>(ref).*f)
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename RefWrap,
|
||||
typename std::enable_if_t<is_reference_wrapper<std::decay_t<RefWrap>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_object_impl(F Base::* f, RefWrap&& ref)
|
||||
E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(
|
||||
ref.get().*f)
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename Pointer,
|
||||
typename std::enable_if_t<
|
||||
!std::is_base_of<Base, std::decay_t<Pointer>>::value &&
|
||||
!is_reference_wrapper<std::decay_t<Pointer>>::value
|
||||
, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_object_impl(F Base::* f, Pointer&& ptr)
|
||||
E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(
|
||||
(*std::forward<Pointer>(ptr)).*f)
|
||||
|
||||
//
|
||||
// invoke_member_function_impl
|
||||
//
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename Derived, typename... Args,
|
||||
typename std::enable_if_t<std::is_base_of<Base, std::decay_t<Derived>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_function_impl(F Base::* f, Derived&& ref, Args&&... args)
|
||||
E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(
|
||||
(std::forward<Derived>(ref).*f)(std::forward<Args>(args)...))
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename RefWrap, typename... Args,
|
||||
typename std::enable_if_t<is_reference_wrapper<std::decay_t<RefWrap>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_function_impl(F Base::* f, RefWrap&& ref, Args&&... args)
|
||||
E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(
|
||||
(ref.get().*f)(std::forward<Args>(args)...))
|
||||
|
||||
template
|
||||
<
|
||||
typename Base, typename F, typename Pointer, typename... Args,
|
||||
typename std::enable_if_t<
|
||||
!std::is_base_of<Base, std::decay_t<Pointer>>::value &&
|
||||
!is_reference_wrapper<std::decay_t<Pointer>>::value
|
||||
, int> = 0
|
||||
>
|
||||
constexpr auto invoke_member_function_impl(F Base::* f, Pointer&& ptr, Args&&... args)
|
||||
E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(
|
||||
((*std::forward<Pointer>(ptr)).*f)(std::forward<Args>(args)...))
|
||||
}
|
||||
|
||||
template
|
||||
<
|
||||
typename F, typename... Args,
|
||||
typename std::enable_if_t<!std::is_member_pointer<std::decay_t<F>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke(F&& f, Args&&... args)
|
||||
E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(
|
||||
std::forward<F>(f)(std::forward<Args>(args)...))
|
||||
|
||||
template
|
||||
<
|
||||
typename F, typename T,
|
||||
typename std::enable_if_t<std::is_member_object_pointer<std::decay_t<F>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke(F&& f, T&& t)
|
||||
E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(
|
||||
impl::invoke_member_object_impl(std::forward<F>(f), std::forward<T>(t)))
|
||||
|
||||
template
|
||||
<
|
||||
typename F, typename... Args,
|
||||
typename std::enable_if_t<std::is_member_function_pointer<std::decay_t<F>>::value, int> = 0
|
||||
>
|
||||
constexpr auto invoke(F&& f, Args&&... args)
|
||||
E2D_STDEX_NOEXCEPT_DECLTYPE_RETURN(
|
||||
impl::invoke_member_function_impl(std::forward<F>(f), std::forward<Args>(args)...))
|
||||
}}
|
||||
|
||||
//
|
||||
// invoke_result
|
||||
//
|
||||
|
||||
namespace e2d { namespace stdex
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
struct invoke_result_impl_tag {};
|
||||
|
||||
template < typename Void, typename F, typename... Args >
|
||||
struct invoke_result_impl {};
|
||||
|
||||
template < typename F, typename... Args >
|
||||
struct invoke_result_impl<void_t<invoke_result_impl_tag, decltype(stdex::invoke(std::declval<F>(), std::declval<Args>()...))>, F, Args...> {
|
||||
using type = decltype(stdex::invoke(std::declval<F>(), std::declval<Args>()...));
|
||||
};
|
||||
}
|
||||
|
||||
template < typename F, typename... Args >
|
||||
struct invoke_result
|
||||
: impl::invoke_result_impl<void, F, Args...> {};
|
||||
|
||||
template < typename F, typename... Args >
|
||||
using invoke_result_t = typename invoke_result<F, Args...>::type;
|
||||
}}
|
||||
|
||||
//
|
||||
// apply
|
||||
//
|
||||
|
||||
namespace e2d { namespace stdex
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
template < typename F, typename Tuple, std::size_t... I >
|
||||
constexpr decltype(auto) apply_impl(F&& f, Tuple&& args, std::index_sequence<I...>)
|
||||
E2D_STDEX_NOEXCEPT_RETURN(
|
||||
stdex::invoke(
|
||||
std::forward<F>(f),
|
||||
std::get<I>(std::forward<Tuple>(args))...))
|
||||
}
|
||||
|
||||
template < typename F, typename Tuple >
|
||||
constexpr decltype(auto) apply(F&& f, Tuple&& args)
|
||||
E2D_STDEX_NOEXCEPT_RETURN(
|
||||
impl::apply_impl(
|
||||
std::forward<F>(f),
|
||||
std::forward<Tuple>(args),
|
||||
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>()))
|
||||
}}
|
||||
|
||||
//
|
||||
// basic_string_view
|
||||
//
|
||||
|
||||
namespace e2d { namespace stdex
|
||||
{
|
||||
template < typename Char, typename Traits = std::char_traits<Char> >
|
||||
class basic_string_view {
|
||||
public:
|
||||
using value_type = Char;
|
||||
using traits_type = Traits;
|
||||
|
||||
using pointer = const Char*;
|
||||
using const_pointer = const Char*;
|
||||
|
||||
using reference = const Char&;
|
||||
using const_reference = const Char&;
|
||||
|
||||
using const_iterator = const_pointer;
|
||||
using iterator = const_iterator;
|
||||
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using reverse_iterator = const_reverse_iterator;
|
||||
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
static const size_type npos = size_type(-1);
|
||||
|
||||
static_assert(
|
||||
std::is_pod<value_type>::value,
|
||||
"value_type must be a POD");
|
||||
static_assert(
|
||||
std::is_same<value_type, typename traits_type::char_type>::value,
|
||||
"traits_type::char_type must be the same type as value_type");
|
||||
public:
|
||||
basic_string_view() noexcept = default;
|
||||
basic_string_view(const basic_string_view&) noexcept = default;
|
||||
basic_string_view& operator=(const basic_string_view&) noexcept = default;
|
||||
|
||||
basic_string_view(std::nullptr_t) noexcept = delete;
|
||||
basic_string_view(std::nullptr_t, size_type) noexcept = delete;
|
||||
|
||||
basic_string_view(const Char* str) noexcept {
|
||||
E2D_ASSERT(str);
|
||||
data_ = str;
|
||||
size_ = Traits::length(str);
|
||||
}
|
||||
|
||||
basic_string_view(const Char* str, size_type size) noexcept {
|
||||
E2D_ASSERT(!size || str);
|
||||
data_ = str;
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
template < typename Alloc >
|
||||
basic_string_view(const std::basic_string<Char, Traits, Alloc>& str) noexcept
|
||||
: data_(str.data())
|
||||
, size_(str.size()) {}
|
||||
|
||||
template < typename Alloc >
|
||||
operator std::basic_string<Char, Traits, Alloc>() const {
|
||||
return {cbegin(), cend()};
|
||||
}
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return data_;
|
||||
}
|
||||
|
||||
const_iterator cbegin() const noexcept {
|
||||
return data_;
|
||||
}
|
||||
|
||||
const_iterator end() const noexcept {
|
||||
return data_ + size_;
|
||||
}
|
||||
|
||||
const_iterator cend() const noexcept {
|
||||
return data_ + size_;
|
||||
}
|
||||
|
||||
const_reverse_iterator rbegin() const noexcept {
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
const_reverse_iterator crbegin() const noexcept {
|
||||
return const_reverse_iterator(cend());
|
||||
}
|
||||
|
||||
const_reverse_iterator rend() const noexcept {
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
const_reverse_iterator crend() const noexcept {
|
||||
return const_reverse_iterator(cbegin());
|
||||
}
|
||||
|
||||
size_type size() const noexcept {
|
||||
return size_;
|
||||
}
|
||||
|
||||
size_type length() const noexcept {
|
||||
return size_;
|
||||
}
|
||||
|
||||
size_type max_size() const noexcept {
|
||||
return std::numeric_limits<size_type>::max();
|
||||
}
|
||||
|
||||
bool empty() const noexcept {
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
const_reference operator[](size_type index) const noexcept {
|
||||
E2D_ASSERT(index < size_);
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
const_reference at(size_type index) const {
|
||||
if ( index < size_ ) {
|
||||
return data_[index];
|
||||
}
|
||||
throw std::out_of_range("basic_string_view::at");
|
||||
}
|
||||
|
||||
const_reference front() const noexcept {
|
||||
E2D_ASSERT(size_ > 0);
|
||||
return data_[0];
|
||||
}
|
||||
|
||||
const_reference back() const noexcept {
|
||||
E2D_ASSERT(size_ > 0);
|
||||
return data_[size_ - 1];
|
||||
}
|
||||
|
||||
const_pointer data() const noexcept {
|
||||
return data_;
|
||||
}
|
||||
|
||||
void swap(basic_string_view& other) noexcept {
|
||||
std::swap(data_, other.data_);
|
||||
std::swap(size_, other.size_);
|
||||
}
|
||||
|
||||
void remove_prefix(size_type n) noexcept {
|
||||
E2D_ASSERT(n <= size_);
|
||||
data_ += n;
|
||||
size_ -= n;
|
||||
}
|
||||
|
||||
void remove_suffix(size_type n) noexcept {
|
||||
E2D_ASSERT(n <= size_);
|
||||
size_ -= n;
|
||||
}
|
||||
|
||||
int compare(basic_string_view str) const noexcept {
|
||||
const auto ms = std::min(size(), str.size());
|
||||
const auto cr = Traits::compare(data(), str.data(), ms);
|
||||
return cr == 0
|
||||
? (size() == str.size()
|
||||
? 0
|
||||
: (size() < str.size() ? -1 : 1))
|
||||
: cr;
|
||||
}
|
||||
|
||||
size_type copy(Char* dst, size_type size, size_type index = 0) const {
|
||||
if ( index > size_ ) {
|
||||
throw std::out_of_range("basic_string_view::copy");
|
||||
}
|
||||
size_type ms = std::min(size, size_ - index);
|
||||
Traits::copy(dst, data_ + index, ms);
|
||||
return ms;
|
||||
}
|
||||
|
||||
basic_string_view substr(size_type index = 0, size_type size = npos) const {
|
||||
if ( index <= size_ ) {
|
||||
return basic_string_view(
|
||||
data_ + index,
|
||||
std::min(size, size_ - index));
|
||||
}
|
||||
throw std::out_of_range("basic_string_view::substr");
|
||||
}
|
||||
private:
|
||||
const Char* data_ = nullptr;
|
||||
size_type size_ = 0;
|
||||
};
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator<(
|
||||
basic_string_view<Char, Traits> l,
|
||||
basic_string_view<Char, Traits> r) noexcept
|
||||
{
|
||||
return l.compare(r) < 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator<(
|
||||
basic_string_view<Char, Traits> l,
|
||||
const char* r) noexcept
|
||||
{
|
||||
return l.compare(basic_string_view<Char, Traits>(r)) < 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator<(
|
||||
basic_string_view<Char, Traits> l,
|
||||
const std::basic_string<Char, Traits>& r) noexcept
|
||||
{
|
||||
return l.compare(basic_string_view<Char, Traits>(r)) < 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator<(
|
||||
const char* l,
|
||||
basic_string_view<Char, Traits> r) noexcept
|
||||
{
|
||||
return basic_string_view<Char, Traits>(l).compare(r) < 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator<(
|
||||
const std::basic_string<Char, Traits>& l,
|
||||
basic_string_view<Char, Traits> r) noexcept
|
||||
{
|
||||
return basic_string_view<Char, Traits>(l).compare(r) < 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator==(
|
||||
basic_string_view<Char, Traits> l,
|
||||
basic_string_view<Char, Traits> r) noexcept
|
||||
{
|
||||
const auto ls = l.size();
|
||||
const auto rs = r.size();
|
||||
return ls == rs && Traits::compare(l.data(), r.data(), ls) == 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator==(
|
||||
basic_string_view<Char, Traits> l,
|
||||
const char* r) noexcept
|
||||
{
|
||||
return l.compare(basic_string_view<Char, Traits>(r)) == 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator==(
|
||||
basic_string_view<Char, Traits> l,
|
||||
const std::basic_string<Char, Traits>& r) noexcept
|
||||
{
|
||||
return l.compare(basic_string_view<Char, Traits>(r)) == 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator==(
|
||||
const char* l,
|
||||
basic_string_view<Char, Traits> r) noexcept
|
||||
{
|
||||
return basic_string_view<Char, Traits>(l).compare(r) == 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator==(
|
||||
const std::basic_string<Char, Traits>& l,
|
||||
basic_string_view<Char, Traits> r) noexcept
|
||||
{
|
||||
return basic_string_view<Char, Traits>(l).compare(r) == 0;
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator!=(
|
||||
basic_string_view<Char, Traits> l,
|
||||
basic_string_view<Char, Traits> r) noexcept
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator!=(
|
||||
basic_string_view<Char, Traits> l,
|
||||
const char* r) noexcept
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator!=(
|
||||
basic_string_view<Char, Traits> l,
|
||||
const std::basic_string<Char, Traits>& r) noexcept
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator!=(
|
||||
const char* l,
|
||||
basic_string_view<Char, Traits> r) noexcept
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
template < typename Char, typename Traits >
|
||||
bool operator!=(
|
||||
const std::basic_string<Char, Traits>& l,
|
||||
basic_string_view<Char, Traits> r) noexcept
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_base.hpp"
|
||||
#include "stdex.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
@@ -26,28 +26,36 @@ namespace e2d
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class exception
|
||||
: public std::exception {};
|
||||
|
||||
template < typename Value
|
||||
, std::size_t Size >
|
||||
using array = std::array<Value, Size>;
|
||||
|
||||
template < typename Value
|
||||
, typename Alloc = std::allocator<Value> >
|
||||
using vector = std::vector<Value, Alloc>;
|
||||
, typename Allocator = std::allocator<Value> >
|
||||
using vector = std::vector<Value, Allocator>;
|
||||
|
||||
template < typename Value
|
||||
, typename Hash = std::hash<Value>
|
||||
, typename Pred = std::equal_to<Value>
|
||||
, typename Alloc = std::allocator<Value> >
|
||||
using hash_set = std::unordered_set<Value, Hash, Pred, Alloc>;
|
||||
, typename Allocator = std::allocator<Value> >
|
||||
using hash_set = std::unordered_set<Value, Hash, Pred, Allocator>;
|
||||
|
||||
template < typename Key
|
||||
, typename Value
|
||||
, typename Hash = std::hash<Key>
|
||||
, typename Pred = std::equal_to<Key>
|
||||
, typename Alloc = std::allocator<std::pair<const Key, Value>> >
|
||||
using hash_map = std::unordered_map<Key, Value, Hash, Pred, Alloc>;
|
||||
, typename Allocator = std::allocator<std::pair<const Key, Value>> >
|
||||
using hash_map = std::unordered_map<Key, Value, Hash, Pred, Allocator>;
|
||||
|
||||
template < typename Char
|
||||
, typename Alloc = std::allocator<Char> >
|
||||
using basic_string = std::basic_string<Char, std::char_traits<Char>, Alloc>;
|
||||
, typename Traits = std::char_traits<Char>
|
||||
, typename Allocator = std::allocator<Char> >
|
||||
using basic_string = std::basic_string<Char, Traits, Allocator>;
|
||||
|
||||
template < typename Char
|
||||
, typename Traits = std::char_traits<Char> >
|
||||
using basic_string_view = stdex::basic_string_view<Char, Traits>;
|
||||
}
|
||||
|
||||
@@ -425,15 +425,6 @@ namespace e2d { namespace math
|
||||
template < typename T >
|
||||
using make_distance_t = typename make_distance<T>::type;
|
||||
|
||||
template < typename T >
|
||||
std::enable_if_t<
|
||||
std::is_unsigned<T>::value || std::is_floating_point<T>::value,
|
||||
make_distance_t<T>>
|
||||
distance(T l, T r) noexcept {
|
||||
std::tie(l, r) = minmax(l, r);
|
||||
return r - l;
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
std::enable_if_t<
|
||||
std::is_integral<T>::value && std::is_signed<T>::value,
|
||||
@@ -445,6 +436,15 @@ namespace e2d { namespace math
|
||||
: abs_to_unsigned(l) + abs_to_unsigned(r);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
std::enable_if_t<
|
||||
std::is_unsigned<T>::value || std::is_floating_point<T>::value,
|
||||
make_distance_t<T>>
|
||||
distance(T l, T r) noexcept {
|
||||
std::tie(l, r) = minmax(l, r);
|
||||
return r - l;
|
||||
}
|
||||
|
||||
//
|
||||
// approximately
|
||||
//
|
||||
|
||||
@@ -7,3 +7,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "color.hpp"
|
||||
#include "color32.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "image.hpp"
|
||||
#include "jobber.hpp"
|
||||
#include "streams.hpp"
|
||||
#include "strfmts.hpp"
|
||||
#include "strings.hpp"
|
||||
#include "time.hpp"
|
||||
|
||||
@@ -8,3 +8,49 @@
|
||||
|
||||
#include "../base/_all.hpp"
|
||||
#include "../math/_all.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class buffer;
|
||||
class color;
|
||||
class color32;
|
||||
class image;
|
||||
class jobber;
|
||||
class input_stream;
|
||||
class output_stream;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
using str = basic_string<char>;
|
||||
using wstr = basic_string<wchar_t>;
|
||||
using str16 = basic_string<char16_t>;
|
||||
using str32 = basic_string<char32_t>;
|
||||
|
||||
using str_view = basic_string_view<char>;
|
||||
using wstr_view = basic_string_view<wchar_t>;
|
||||
using str16_view = basic_string_view<char16_t>;
|
||||
using str32_view = basic_string_view<char32_t>;
|
||||
|
||||
struct seconds_tag {};
|
||||
struct milliseconds_tag {};
|
||||
struct microseconds_tag {};
|
||||
|
||||
template < typename T >
|
||||
using seconds = unit<T, seconds_tag>;
|
||||
template < typename T >
|
||||
using milliseconds = unit<T, milliseconds_tag>;
|
||||
template < typename T >
|
||||
using microseconds = unit<T, microseconds_tag>;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class noncopyable {
|
||||
protected:
|
||||
noncopyable() = default;
|
||||
~noncopyable() = default;
|
||||
noncopyable(const noncopyable&) = delete;
|
||||
noncopyable& operator=(const noncopyable&) = delete;
|
||||
};
|
||||
}
|
||||
|
||||
51
headers/enduro2d/utils/buffer.hpp
Normal file
51
headers/enduro2d/utils/buffer.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class buffer final {
|
||||
public:
|
||||
buffer() noexcept = default;
|
||||
~buffer() noexcept = default;
|
||||
|
||||
buffer(buffer&& other) noexcept;
|
||||
buffer& operator=(buffer&& other) noexcept;
|
||||
|
||||
buffer(const buffer& other);
|
||||
buffer& operator=(const buffer& other);
|
||||
|
||||
explicit buffer(std::size_t size);
|
||||
buffer(const void* src, std::size_t size);
|
||||
|
||||
buffer& fill(u8 ch) noexcept;
|
||||
buffer& resize(std::size_t nsize);
|
||||
|
||||
buffer& assign(buffer&& other) noexcept;
|
||||
buffer& assign(const buffer& other);
|
||||
buffer& assign(const void* src, std::size_t nsize);
|
||||
|
||||
void swap(buffer& other) noexcept;
|
||||
void clear() noexcept;
|
||||
bool empty() const noexcept;
|
||||
|
||||
u8* data() noexcept;
|
||||
const u8* data() const noexcept;
|
||||
std::size_t size() const noexcept;
|
||||
private:
|
||||
using data_t = std::unique_ptr<u8[]>;
|
||||
data_t data_;
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
void swap(buffer& l, buffer& r) noexcept;
|
||||
bool operator<(const buffer& l, const buffer& r) noexcept;
|
||||
bool operator==(const buffer& l, const buffer& r) noexcept;
|
||||
bool operator!=(const buffer& l, const buffer& r) noexcept;
|
||||
}
|
||||
101
headers/enduro2d/utils/color.hpp
Normal file
101
headers/enduro2d/utils/color.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class color final {
|
||||
public:
|
||||
f32 r = 1.f;
|
||||
f32 g = 1.f;
|
||||
f32 b = 1.f;
|
||||
f32 a = 1.f;
|
||||
public:
|
||||
static const color& clear() noexcept; /// (0; 0; 0; 0)
|
||||
static const color& black() noexcept; /// (0; 0; 0; 1)
|
||||
static const color& white() noexcept; /// (1; 1; 1; 1)
|
||||
static const color& red() noexcept; /// (1; 0; 0; 1)
|
||||
static const color& green() noexcept; /// (0; 1; 0; 1)
|
||||
static const color& blue() noexcept; /// (0; 0; 1; 1)
|
||||
static const color& yellow() noexcept; /// (1; 1; 0; 1)
|
||||
static const color& magenta() noexcept; /// (1; 0; 1; 1)
|
||||
static const color& cyan() noexcept; /// (0; 1; 1; 1)
|
||||
public:
|
||||
color() noexcept = default;
|
||||
color(const color& other) noexcept = default;
|
||||
color& operator=(const color& other) noexcept = default;
|
||||
|
||||
color(f32 r, f32 g, f32 b, f32 a = 1.f) noexcept;
|
||||
explicit color(const color32& other) noexcept;
|
||||
|
||||
f32* data() noexcept;
|
||||
const f32* data() const noexcept;
|
||||
|
||||
f32& operator[](std::size_t index) noexcept;
|
||||
f32 operator[](std::size_t index) const noexcept;
|
||||
|
||||
color& operator+=(f32 v) noexcept;
|
||||
color& operator-=(f32 v) noexcept;
|
||||
color& operator*=(f32 v) noexcept;
|
||||
color& operator/=(f32 v) noexcept;
|
||||
|
||||
color& operator+=(const color& other) noexcept;
|
||||
color& operator-=(const color& other) noexcept;
|
||||
color& operator*=(const color& other) noexcept;
|
||||
color& operator/=(const color& other) noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
bool operator<(const color& l, const color& r) noexcept;
|
||||
bool operator==(const color& l, const color& r) noexcept;
|
||||
bool operator!=(const color& l, const color& r) noexcept;
|
||||
|
||||
color operator+(color l, f32 v) noexcept;
|
||||
color operator-(color l, f32 v) noexcept;
|
||||
color operator*(color l, f32 v) noexcept;
|
||||
color operator/(color l, f32 v) noexcept;
|
||||
|
||||
color operator+(f32 v, const color& r) noexcept;
|
||||
color operator-(f32 v, const color& r) noexcept;
|
||||
color operator*(f32 v, const color& r) noexcept;
|
||||
color operator/(f32 v, const color& r) noexcept;
|
||||
|
||||
color operator+(color l, const color& r) noexcept;
|
||||
color operator-(color l, const color& r) noexcept;
|
||||
color operator*(color l, const color& r) noexcept;
|
||||
color operator/(color l, const color& r) noexcept;
|
||||
}
|
||||
|
||||
namespace e2d { namespace math
|
||||
{
|
||||
bool approximately(const color& l, const color& r) noexcept;
|
||||
bool approximately(const color& l, const color& r, f32 precision) noexcept;
|
||||
|
||||
f32 minimum(const color& c) noexcept;
|
||||
f32 maximum(const color& c) noexcept;
|
||||
|
||||
color minimized(const color& c, const color& cmin) noexcept;
|
||||
color maximized(const color& c, const color& cmax) noexcept;
|
||||
color clamped(const color& c, const color& cmin, const color& cmax) noexcept;
|
||||
color saturated(const color& c) noexcept;
|
||||
|
||||
bool contains(
|
||||
const color& c,
|
||||
f32 value,
|
||||
f32 precision = math::default_precision<f32>()) noexcept;
|
||||
bool contains_nan(const color& c) noexcept;
|
||||
}}
|
||||
|
||||
namespace e2d { namespace colors
|
||||
{
|
||||
u32 pack_color(const color& c) noexcept;
|
||||
color unpack_color(u32 argb) noexcept;
|
||||
}}
|
||||
99
headers/enduro2d/utils/color32.hpp
Normal file
99
headers/enduro2d/utils/color32.hpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class color32 final {
|
||||
public:
|
||||
u8 r = 255;
|
||||
u8 g = 255;
|
||||
u8 b = 255;
|
||||
u8 a = 255;
|
||||
public:
|
||||
static const color32& clear() noexcept; /// ( 0; 0; 0; 0 )
|
||||
static const color32& black() noexcept; /// ( 0; 0; 0; 255)
|
||||
static const color32& white() noexcept; /// (255; 255; 255; 255)
|
||||
static const color32& red() noexcept; /// (255; 0; 0; 255)
|
||||
static const color32& green() noexcept; /// ( 0; 255; 0; 255)
|
||||
static const color32& blue() noexcept; /// ( 0; 0; 255; 255)
|
||||
static const color32& yellow() noexcept; /// (255; 255; 0; 255)
|
||||
static const color32& magenta() noexcept; /// (255; 0; 255; 255)
|
||||
static const color32& cyan() noexcept; /// ( 0; 255; 255; 255)
|
||||
public:
|
||||
color32() noexcept = default;
|
||||
color32(const color32& other) noexcept = default;
|
||||
color32& operator=(const color32& other) noexcept = default;
|
||||
|
||||
color32(u8 r, u8 g, u8 b, u8 a = 255) noexcept;
|
||||
explicit color32(const color& other) noexcept;
|
||||
|
||||
u8* data() noexcept;
|
||||
const u8* data() const noexcept;
|
||||
|
||||
u8& operator[](std::size_t index) noexcept;
|
||||
u8 operator[](std::size_t index) const noexcept;
|
||||
|
||||
color32& operator+=(u8 v) noexcept;
|
||||
color32& operator-=(u8 v) noexcept;
|
||||
color32& operator*=(u8 v) noexcept;
|
||||
color32& operator/=(u8 v) noexcept;
|
||||
|
||||
color32& operator+=(const color32& other) noexcept;
|
||||
color32& operator-=(const color32& other) noexcept;
|
||||
color32& operator*=(const color32& other) noexcept;
|
||||
color32& operator/=(const color32& other) noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
bool operator<(const color32& l, const color32& r) noexcept;
|
||||
bool operator==(const color32& l, const color32& r) noexcept;
|
||||
bool operator!=(const color32& l, const color32& r) noexcept;
|
||||
|
||||
color32 operator+(color32 l, u8 v) noexcept;
|
||||
color32 operator-(color32 l, u8 v) noexcept;
|
||||
color32 operator*(color32 l, u8 v) noexcept;
|
||||
color32 operator/(color32 l, u8 v) noexcept;
|
||||
|
||||
color32 operator+(u8 v, const color32& r) noexcept;
|
||||
color32 operator-(u8 v, const color32& r) noexcept;
|
||||
color32 operator*(u8 v, const color32& r) noexcept;
|
||||
color32 operator/(u8 v, const color32& r) noexcept;
|
||||
|
||||
color32 operator+(color32 l, const color32& r) noexcept;
|
||||
color32 operator-(color32 l, const color32& r) noexcept;
|
||||
color32 operator*(color32 l, const color32& r) noexcept;
|
||||
color32 operator/(color32 l, const color32& r) noexcept;
|
||||
}
|
||||
|
||||
namespace e2d { namespace math
|
||||
{
|
||||
bool approximately(const color32& l, const color32& r) noexcept;
|
||||
bool approximately(const color32& l, const color32& r, u8 precision) noexcept;
|
||||
|
||||
u8 minimum(const color32& c) noexcept;
|
||||
u8 maximum(const color32& c) noexcept;
|
||||
|
||||
color32 minimized(const color32& c, const color32& cmin) noexcept;
|
||||
color32 maximized(const color32& c, const color32& cmax) noexcept;
|
||||
color32 clamped(const color32& c, const color32& cmin, const color32& cmax) noexcept;
|
||||
|
||||
bool contains(
|
||||
const color32& c,
|
||||
u8 value,
|
||||
u8 precision = math::default_precision<u8>()) noexcept;
|
||||
}}
|
||||
|
||||
namespace e2d { namespace colors
|
||||
{
|
||||
u32 pack_color32(const color32& c) noexcept;
|
||||
color32 unpack_color32(u32 argb) noexcept;
|
||||
}}
|
||||
70
headers/enduro2d/utils/filesystem.hpp
Normal file
70
headers/enduro2d/utils/filesystem.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
#include "streams.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class read_file : public input_stream {
|
||||
public:
|
||||
virtual const str& path() const noexcept = 0;
|
||||
};
|
||||
using read_file_uptr = std::unique_ptr<read_file>;
|
||||
|
||||
class write_file : public output_stream {
|
||||
public:
|
||||
virtual const str& path() const noexcept = 0;
|
||||
};
|
||||
using write_file_uptr = std::unique_ptr<write_file>;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
read_file_uptr make_read_file(str_view path) noexcept;
|
||||
write_file_uptr make_write_file(str_view path, bool append) noexcept;
|
||||
}
|
||||
|
||||
namespace e2d { namespace path
|
||||
{
|
||||
str combine(str_view lhs, str_view rhs);
|
||||
|
||||
str remove_filename(str_view path);
|
||||
str remove_extension(str_view path);
|
||||
|
||||
str replace_filename(str_view path, str_view filename);
|
||||
str replace_extension(str_view path, str_view extension);
|
||||
|
||||
str stem(str_view path);
|
||||
str filename(str_view path);
|
||||
str extension(str_view path);
|
||||
str parent_path(str_view path);
|
||||
|
||||
bool is_absolute(str_view path) noexcept;
|
||||
bool is_relative(str_view path) noexcept;
|
||||
}}
|
||||
|
||||
|
||||
namespace e2d { namespace filesystem
|
||||
{
|
||||
bool remove(str_view path);
|
||||
bool exists(str_view path);
|
||||
|
||||
bool remove_file(str_view path);
|
||||
bool remove_directory(str_view path);
|
||||
|
||||
bool file_exists(str_view path);
|
||||
bool directory_exists(str_view path);
|
||||
|
||||
bool create_file(str_view path);
|
||||
bool create_directory(str_view path);
|
||||
bool create_directory_recursive(str_view path);
|
||||
|
||||
bool try_read_all(buffer& dst, str_view path) noexcept;
|
||||
bool try_write_all(const buffer& src, str_view path, bool append) noexcept;
|
||||
}}
|
||||
126
headers/enduro2d/utils/image.hpp
Normal file
126
headers/enduro2d/utils/image.hpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "color.hpp"
|
||||
#include "color32.hpp"
|
||||
#include "streams.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
enum class image_file_format : u8 {
|
||||
dds,
|
||||
jpg,
|
||||
png,
|
||||
pvr,
|
||||
tga,
|
||||
|
||||
unknown
|
||||
};
|
||||
|
||||
enum class image_data_format : u8 {
|
||||
g8,
|
||||
ga8,
|
||||
rgb8,
|
||||
rgba8,
|
||||
|
||||
dxt1,
|
||||
dxt3,
|
||||
dxt5,
|
||||
|
||||
rgb_pvrtc2,
|
||||
rgb_pvrtc4,
|
||||
|
||||
rgba_pvrtc2,
|
||||
rgba_pvrtc4,
|
||||
|
||||
unknown
|
||||
};
|
||||
|
||||
class bad_image_access final : public exception {
|
||||
public:
|
||||
const char* what() const noexcept final {
|
||||
return "bad image access";
|
||||
}
|
||||
};
|
||||
|
||||
class bad_image_data_format final : public exception {
|
||||
public:
|
||||
const char* what() const noexcept final {
|
||||
return "bad image data format";
|
||||
}
|
||||
};
|
||||
|
||||
class image final {
|
||||
public:
|
||||
image() noexcept = default;
|
||||
~image() noexcept = default;
|
||||
|
||||
image(image&& other) noexcept;
|
||||
image& operator=(image&& other) noexcept;
|
||||
|
||||
image(const image& other);
|
||||
image& operator=(const image& other);
|
||||
|
||||
image(const v2u& size, image_data_format format);
|
||||
image(const v2u& size, image_data_format format, buffer&& data);
|
||||
image(const v2u& size, image_data_format format, const buffer& data);
|
||||
|
||||
image& assign(image&& other) noexcept;
|
||||
image& assign(const image& other);
|
||||
|
||||
image& assign(const v2u& size, image_data_format format);
|
||||
image& assign(const v2u& size, image_data_format format, buffer&& data);
|
||||
image& assign(const v2u& size, image_data_format format, const buffer& data);
|
||||
|
||||
void swap(image& other) noexcept;
|
||||
void clear() noexcept;
|
||||
bool empty() const noexcept;
|
||||
|
||||
color pixel(u32 u, u32 v) const;
|
||||
color pixel(const v2u& uv) const;
|
||||
color32 pixel32(u32 u, u32 v) const;
|
||||
color32 pixel32(const v2u& uv) const;
|
||||
|
||||
const v2u& size() const noexcept;
|
||||
image_data_format format() const noexcept;
|
||||
const buffer& data() const noexcept;
|
||||
private:
|
||||
buffer data_;
|
||||
v2u size_;
|
||||
image_data_format format_ = image_data_format::unknown;
|
||||
};
|
||||
|
||||
void swap(image& l, image& r) noexcept;
|
||||
bool operator<(const image& l, const image& r) noexcept;
|
||||
bool operator==(const image& l, const image& r) noexcept;
|
||||
bool operator!=(const image& l, const image& r) noexcept;
|
||||
}
|
||||
|
||||
namespace e2d { namespace images
|
||||
{
|
||||
bool try_load_image(
|
||||
image& dst,
|
||||
const buffer& src) noexcept;
|
||||
|
||||
bool try_load_image(
|
||||
image& dst,
|
||||
const input_stream_uptr& src) noexcept;
|
||||
|
||||
bool try_save_image(
|
||||
const image& src,
|
||||
image_file_format format,
|
||||
buffer& dst) noexcept;
|
||||
|
||||
bool try_save_image(
|
||||
const image& src,
|
||||
image_file_format format,
|
||||
const output_stream_uptr& dst) noexcept;
|
||||
}}
|
||||
175
headers/enduro2d/utils/jobber.hpp
Normal file
175
headers/enduro2d/utils/jobber.hpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class jobber final : private noncopyable {
|
||||
public:
|
||||
enum class priority {
|
||||
lowest,
|
||||
below_normal,
|
||||
normal,
|
||||
above_normal,
|
||||
highest
|
||||
};
|
||||
public:
|
||||
explicit jobber(u32 threads);
|
||||
~jobber() noexcept;
|
||||
|
||||
template < typename F, typename... Args >
|
||||
using async_invoke_result_t = stdex::invoke_result_t<
|
||||
std::decay_t<F>,
|
||||
std::decay_t<Args>...>;
|
||||
|
||||
template < typename F, typename... Args
|
||||
, typename R = async_invoke_result_t<F, Args...> >
|
||||
std::future<R> async(F&& f, Args&&... args);
|
||||
|
||||
template < typename F, typename... Args
|
||||
, typename R = async_invoke_result_t<F, Args...> >
|
||||
std::future<R> async(priority priority, F&& f, Args&&... args);
|
||||
|
||||
void pause() noexcept;
|
||||
void resume() noexcept;
|
||||
bool is_paused() const noexcept;
|
||||
|
||||
void wait_all() const noexcept;
|
||||
void active_wait_all() noexcept;
|
||||
private:
|
||||
class task;
|
||||
using task_ptr = std::unique_ptr<task>;
|
||||
template < typename R, typename F, typename... Args >
|
||||
class concrete_task;
|
||||
private:
|
||||
void push_task_(priority priority, task_ptr task);
|
||||
task_ptr pop_task_() noexcept;
|
||||
void shutdown_() noexcept;
|
||||
void worker_main_() noexcept;
|
||||
void process_task_(std::unique_lock<std::mutex> lock) noexcept;
|
||||
private:
|
||||
vector<std::thread> threads_;
|
||||
vector<std::pair<priority, task_ptr>> tasks_;
|
||||
std::mutex tasks_mutex_;
|
||||
std::condition_variable cond_var_;
|
||||
std::atomic_bool paused_{false};
|
||||
std::atomic_bool cancelled_{false};
|
||||
std::atomic_size_t active_task_count_{0};
|
||||
};
|
||||
|
||||
class jobber::task : private noncopyable {
|
||||
public:
|
||||
virtual ~task() noexcept = default;
|
||||
virtual void run() noexcept = 0;
|
||||
};
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
class jobber::concrete_task : public task {
|
||||
F f_;
|
||||
std::tuple<Args...> args_;
|
||||
std::promise<R> promise_;
|
||||
public:
|
||||
template < typename U >
|
||||
concrete_task(U&& u, std::tuple<Args...>&& args);
|
||||
void run() noexcept final;
|
||||
std::future<R> future() noexcept;
|
||||
};
|
||||
|
||||
template < typename F, typename... Args >
|
||||
class jobber::concrete_task<void, F, Args...> : public task {
|
||||
F f_;
|
||||
std::tuple<Args...> args_;
|
||||
std::promise<void> promise_;
|
||||
public:
|
||||
template < typename U >
|
||||
concrete_task(U&& u, std::tuple<Args...>&& args);
|
||||
void run() noexcept final;
|
||||
std::future<void> future() noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
//
|
||||
// async
|
||||
//
|
||||
|
||||
template < typename F, typename... Args, typename R >
|
||||
std::future<R> jobber::async(F&& f, Args&&... args) {
|
||||
return async(
|
||||
priority::normal,
|
||||
std::forward<F>(f),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template < typename F, typename... Args, typename R >
|
||||
std::future<R> jobber::async(priority priority, F&& f, Args&&... args) {
|
||||
using task_t = concrete_task<
|
||||
R,
|
||||
std::decay_t<F>,
|
||||
std::decay_t<Args>...>;
|
||||
std::unique_ptr<task_t> task = std::make_unique<task_t>(
|
||||
std::forward<F>(f),
|
||||
std::make_tuple(std::forward<Args>(args)...));
|
||||
std::future<R> future = task->future();
|
||||
std::lock_guard<std::mutex> guard(tasks_mutex_);
|
||||
push_task_(priority, std::move(task));
|
||||
return future;
|
||||
}
|
||||
|
||||
//
|
||||
// concrete_task<R, F, Args...>
|
||||
//
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
template < typename U >
|
||||
jobber::concrete_task<R, F, Args...>::concrete_task(U&& u, std::tuple<Args...>&& args)
|
||||
: f_(std::forward<U>(u))
|
||||
, args_(std::move(args)) {}
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
void jobber::concrete_task<R, F, Args...>::run() noexcept {
|
||||
try {
|
||||
R value = stdex::apply(std::move(f_), std::move(args_));
|
||||
promise_.set_value(std::move(value));
|
||||
} catch (...) {
|
||||
promise_.set_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename R, typename F, typename... Args >
|
||||
std::future<R> jobber::concrete_task<R, F, Args...>::future() noexcept {
|
||||
return promise_.get_future();
|
||||
}
|
||||
|
||||
//
|
||||
// concrete_task<void, F, Args...>
|
||||
//
|
||||
|
||||
template < typename F, typename... Args >
|
||||
template < typename U >
|
||||
jobber::concrete_task<void, F, Args...>::concrete_task(U&& u, std::tuple<Args...>&& args)
|
||||
: f_(std::forward<U>(u))
|
||||
, args_(std::move(args)) {}
|
||||
|
||||
template < typename F, typename... Args >
|
||||
void jobber::concrete_task<void, F, Args...>::run() noexcept {
|
||||
try {
|
||||
stdex::apply(std::move(f_), std::move(args_));
|
||||
promise_.set_value();
|
||||
} catch (...) {
|
||||
promise_.set_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
template < typename F, typename... Args >
|
||||
std::future<void> jobber::concrete_task<void, F, Args...>::future() noexcept {
|
||||
return promise_.get_future();
|
||||
}
|
||||
}
|
||||
54
headers/enduro2d/utils/streams.hpp
Normal file
54
headers/enduro2d/utils/streams.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
class bad_stream_operation final : public exception {
|
||||
public:
|
||||
const char* what() const noexcept final {
|
||||
return "bad stream operation";
|
||||
}
|
||||
};
|
||||
|
||||
class input_stream : private noncopyable {
|
||||
public:
|
||||
virtual ~input_stream() noexcept = default;
|
||||
virtual std::size_t read(void* dst, std::size_t size) = 0;
|
||||
virtual std::size_t seek(std::ptrdiff_t offset, bool relative) = 0;
|
||||
virtual std::size_t tell() const = 0;
|
||||
virtual std::size_t length() const = 0;
|
||||
};
|
||||
using input_stream_uptr = std::unique_ptr<input_stream>;
|
||||
|
||||
class output_stream : private noncopyable {
|
||||
public:
|
||||
virtual ~output_stream() noexcept = default;
|
||||
virtual std::size_t write(const void* src, std::size_t size) = 0;
|
||||
virtual std::size_t seek(std::ptrdiff_t offset, bool relative) = 0;
|
||||
virtual std::size_t tell() const = 0;
|
||||
};
|
||||
using output_stream_uptr = std::unique_ptr<output_stream>;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
input_stream_uptr make_memory_stream(buffer data) noexcept;
|
||||
}
|
||||
|
||||
namespace e2d { namespace streams
|
||||
{
|
||||
bool try_read_tail(
|
||||
buffer& dst,
|
||||
const input_stream_uptr& stream) noexcept;
|
||||
|
||||
bool try_write_tail(
|
||||
const buffer& src,
|
||||
const output_stream_uptr& stream) noexcept;
|
||||
}}
|
||||
570
headers/enduro2d/utils/strfmts.hpp
Normal file
570
headers/enduro2d/utils/strfmts.hpp
Normal file
@@ -0,0 +1,570 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
#include "color.hpp"
|
||||
#include "color32.hpp"
|
||||
#include "strings.hpp"
|
||||
|
||||
namespace e2d { namespace strings
|
||||
{
|
||||
//
|
||||
// vec2
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<vec2<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
vec2<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1)",
|
||||
make_format_arg(value_.x, width_),
|
||||
make_format_arg(value_.y, width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<vec2<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
vec2<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1)",
|
||||
make_format_arg(value_.x, width_, precision_),
|
||||
make_format_arg(value_.y, width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// vec3
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<vec3<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
vec3<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2)",
|
||||
make_format_arg(value_.x, width_),
|
||||
make_format_arg(value_.y, width_),
|
||||
make_format_arg(value_.z, width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<vec3<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
vec3<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2)",
|
||||
make_format_arg(value_.x, width_, precision_),
|
||||
make_format_arg(value_.y, width_, precision_),
|
||||
make_format_arg(value_.z, width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// vec4
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<vec4<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
vec4<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2,%3)",
|
||||
make_format_arg(value_.x, width_),
|
||||
make_format_arg(value_.y, width_),
|
||||
make_format_arg(value_.z, width_),
|
||||
make_format_arg(value_.w, width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<vec4<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
vec4<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2,%3)",
|
||||
make_format_arg(value_.x, width_, precision_),
|
||||
make_format_arg(value_.y, width_, precision_),
|
||||
make_format_arg(value_.z, width_, precision_),
|
||||
make_format_arg(value_.w, width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// mat2
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<mat2<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
mat2<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1)",
|
||||
make_format_arg(value_[0], width_),
|
||||
make_format_arg(value_[1], width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<mat2<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
mat2<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1)",
|
||||
make_format_arg(value_[0], width_, precision_),
|
||||
make_format_arg(value_[1], width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// mat3
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<mat3<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
mat3<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2)",
|
||||
make_format_arg(value_[0], width_),
|
||||
make_format_arg(value_[1], width_),
|
||||
make_format_arg(value_[2], width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<mat3<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
mat3<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2)",
|
||||
make_format_arg(value_[0], width_, precision_),
|
||||
make_format_arg(value_[1], width_, precision_),
|
||||
make_format_arg(value_[2], width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// mat4
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<mat4<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
mat4<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2,%3)",
|
||||
make_format_arg(value_[0], width_),
|
||||
make_format_arg(value_[1], width_),
|
||||
make_format_arg(value_[2], width_),
|
||||
make_format_arg(value_[3], width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<mat4<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
mat4<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2,%3)",
|
||||
make_format_arg(value_[0], width_, precision_),
|
||||
make_format_arg(value_[1], width_, precision_),
|
||||
make_format_arg(value_[2], width_, precision_),
|
||||
make_format_arg(value_[3], width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// rad
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<rad<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
rad<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0rad",
|
||||
make_format_arg(value_.value, width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<rad<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
rad<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0rad",
|
||||
make_format_arg(value_.value, width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// deg
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<deg<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
deg<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0deg",
|
||||
make_format_arg(value_.value, width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<deg<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
deg<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0deg",
|
||||
make_format_arg(value_.value, width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// str
|
||||
//
|
||||
|
||||
template <>
|
||||
class format_arg<str> {
|
||||
str value_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value)
|
||||
noexcept(noexcept(std::is_nothrow_constructible<str, U>::value))
|
||||
: value_(std::forward<U>(value)) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0", value_.c_str()));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// wstr
|
||||
//
|
||||
|
||||
template <>
|
||||
class format_arg<wstr> {
|
||||
wstr value_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value)
|
||||
noexcept(noexcept(std::is_nothrow_constructible<wstr, U>::value))
|
||||
: value_(std::forward<U>(value)) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0", make_utf8(value_.c_str())));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// str16
|
||||
//
|
||||
|
||||
template <>
|
||||
class format_arg<str16> {
|
||||
str16 value_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value)
|
||||
noexcept(noexcept(std::is_nothrow_constructible<str16, U>::value))
|
||||
: value_(std::forward<U>(value)) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0", make_utf8(value_.c_str())));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// str32
|
||||
//
|
||||
|
||||
template <>
|
||||
class format_arg<str32> {
|
||||
str32 value_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value)
|
||||
noexcept(noexcept(std::is_nothrow_constructible<str32, U>::value))
|
||||
: value_(std::forward<U>(value)) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0", make_utf8(value_.c_str())));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// color
|
||||
//
|
||||
|
||||
template <>
|
||||
class format_arg<color> {
|
||||
color value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2,%3)",
|
||||
make_format_arg(value_.r, width_, precision_),
|
||||
make_format_arg(value_.g, width_, precision_),
|
||||
make_format_arg(value_.b, width_, precision_),
|
||||
make_format_arg(value_.a, width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// color32
|
||||
//
|
||||
|
||||
template <>
|
||||
class format_arg<color32> {
|
||||
color32 value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "(%0,%1,%2,%3)",
|
||||
make_format_arg(value_.r, width_),
|
||||
make_format_arg(value_.g, width_),
|
||||
make_format_arg(value_.b, width_),
|
||||
make_format_arg(value_.a, width_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// seconds
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<seconds<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
seconds<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0s",
|
||||
make_format_arg(value_.value, width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<seconds<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
seconds<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0s",
|
||||
make_format_arg(value_.value, width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// milliseconds
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<milliseconds<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
milliseconds<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0ms",
|
||||
make_format_arg(value_.value, width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<milliseconds<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
milliseconds<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0ms",
|
||||
make_format_arg(value_.value, width_, precision_)));
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// microseconds
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
class format_arg<microseconds<T>, std::enable_if_t<std::is_integral<T>::value>> {
|
||||
microseconds<T> value_;
|
||||
u8 width_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0us",
|
||||
make_format_arg(value_.value, width_)));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<microseconds<T>, std::enable_if_t<std::is_floating_point<T>::value>> {
|
||||
microseconds<T> value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
template < typename U >
|
||||
explicit format_arg(U&& value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(std::forward<U>(value)), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const {
|
||||
return math::numeric_cast<std::ptrdiff_t>(
|
||||
format(dst, size, "%0us",
|
||||
make_format_arg(value_.value, width_, precision_)));
|
||||
}
|
||||
};
|
||||
}}
|
||||
58
headers/enduro2d/utils/strings.hpp
Normal file
58
headers/enduro2d/utils/strings.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
str make_utf8(str_view src);
|
||||
str make_utf8(wstr_view src);
|
||||
str make_utf8(str16_view src);
|
||||
str make_utf8(str32_view src);
|
||||
|
||||
wstr make_wide(str_view src);
|
||||
wstr make_wide(wstr_view src);
|
||||
wstr make_wide(str16_view src);
|
||||
wstr make_wide(str32_view src);
|
||||
|
||||
str16 make_utf16(str_view src);
|
||||
str16 make_utf16(wstr_view src);
|
||||
str16 make_utf16(str16_view src);
|
||||
str16 make_utf16(str32_view src);
|
||||
|
||||
str32 make_utf32(str_view src);
|
||||
str32 make_utf32(wstr_view src);
|
||||
str32 make_utf32(str16_view src);
|
||||
str32 make_utf32(str32_view src);
|
||||
|
||||
namespace strings
|
||||
{
|
||||
class format_error;
|
||||
class bad_format;
|
||||
class bad_format_buffer;
|
||||
class bad_format_argument;
|
||||
|
||||
template < typename T, typename = void >
|
||||
class format_arg;
|
||||
|
||||
template < typename T, typename... Args >
|
||||
format_arg<std::decay_t<T>> make_format_arg(T&& v, Args&&... args);
|
||||
|
||||
template < typename... Args >
|
||||
std::size_t format(
|
||||
char* dst, std::size_t size,
|
||||
const char* fmt, Args&&... args);
|
||||
|
||||
template < typename... Args >
|
||||
str rformat(const char* fmt, Args&&... args);
|
||||
|
||||
bool wildcard_match(str_view string, str_view pattern);
|
||||
}
|
||||
}
|
||||
|
||||
#include "strings.inl"
|
||||
375
headers/enduro2d/utils/strings.inl
Normal file
375
headers/enduro2d/utils/strings.inl
Normal file
@@ -0,0 +1,375 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
#include "strings.hpp"
|
||||
|
||||
namespace e2d { namespace strings
|
||||
{
|
||||
//
|
||||
// exceptions
|
||||
//
|
||||
|
||||
class format_error : public exception {
|
||||
public:
|
||||
const char* what() const noexcept override {
|
||||
return "format error";
|
||||
}
|
||||
};
|
||||
|
||||
class bad_format final : public format_error {
|
||||
public:
|
||||
const char* what() const noexcept final {
|
||||
return "bad format";
|
||||
}
|
||||
};
|
||||
|
||||
class bad_format_buffer final : public format_error {
|
||||
public:
|
||||
const char* what() const noexcept final {
|
||||
return "bad format buffer";
|
||||
}
|
||||
};
|
||||
|
||||
class bad_format_argument final : public format_error {
|
||||
public:
|
||||
const char* what() const noexcept final {
|
||||
return "bad format argument";
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// arguments
|
||||
//
|
||||
|
||||
template < typename T, typename... Args >
|
||||
format_arg<std::decay_t<T>> make_format_arg(T&& v, Args&&... args) {
|
||||
return format_arg<std::decay_t<T>>(
|
||||
std::forward<T>(v), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
namespace impl
|
||||
{
|
||||
// Inspired by:
|
||||
// https://github.com/miloyip/itoa-benchmark
|
||||
|
||||
static constexpr char u8toa_lt[200] = {
|
||||
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
|
||||
'1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
|
||||
'2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
|
||||
'3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
|
||||
'4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
|
||||
'5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
|
||||
'6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
|
||||
'7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
|
||||
'8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
|
||||
'9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
|
||||
};
|
||||
|
||||
inline char* u8toa(u8 value, char* buffer) noexcept {
|
||||
E2D_ASSERT(buffer);
|
||||
const i32 d1 = (value / 100) << 1;
|
||||
const i32 d2 = (value % 100) << 1;
|
||||
if ( value >= 100 ) {
|
||||
*buffer++ = u8toa_lt[d1 + 1];
|
||||
}
|
||||
if ( value >= 10 ) {
|
||||
*buffer++ = u8toa_lt[d2];
|
||||
}
|
||||
*buffer++ = u8toa_lt[d2 + 1];
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
class format_arg<T, std::enable_if_t<
|
||||
std::is_integral<T>::value && std::is_signed<T>::value>>
|
||||
{
|
||||
T value_;
|
||||
u8 width_;
|
||||
public:
|
||||
explicit format_arg(T value, u8 width = 0) noexcept
|
||||
: value_(value), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const noexcept {
|
||||
char format[7] = {0};
|
||||
char* b_format = format;
|
||||
*b_format++ = '%';
|
||||
b_format = impl::u8toa(width_, b_format);
|
||||
*b_format++ = 'j';
|
||||
*b_format++ = 'i';
|
||||
E2D_ASSERT(b_format < format + sizeof(format));
|
||||
return std::snprintf(
|
||||
dst, size, format,
|
||||
math::numeric_cast<std::intmax_t>(value_));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<T, std::enable_if_t<
|
||||
std::is_integral<T>::value && std::is_unsigned<T>::value>>
|
||||
{
|
||||
T value_;
|
||||
u8 width_;
|
||||
public:
|
||||
explicit format_arg(T value, u8 width = 0) noexcept
|
||||
: value_(value), width_(width) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const noexcept {
|
||||
char format[7] = {0};
|
||||
char* b_format = format;
|
||||
*b_format++ = '%';
|
||||
b_format = impl::u8toa(width_, b_format);
|
||||
*b_format++ = 'j';
|
||||
*b_format++ = 'u';
|
||||
E2D_ASSERT(b_format < format + sizeof(format));
|
||||
return std::snprintf(
|
||||
dst, size, format,
|
||||
math::numeric_cast<std::uintmax_t>(value_));
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class format_arg<T, std::enable_if_t<
|
||||
std::is_floating_point<T>::value>>
|
||||
{
|
||||
T value_;
|
||||
u8 width_;
|
||||
u8 precision_;
|
||||
public:
|
||||
explicit format_arg(T value, u8 width = 0, u8 precision = 6) noexcept
|
||||
: value_(value), width_(width), precision_(precision) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const noexcept {
|
||||
char format[10] = {0};
|
||||
char* b_format = format;
|
||||
*b_format++ = '%';
|
||||
b_format = impl::u8toa(width_, b_format);
|
||||
*b_format++ = '.';
|
||||
b_format = impl::u8toa(precision_, b_format);
|
||||
*b_format++ = 'L';
|
||||
*b_format++ = 'f';
|
||||
E2D_ASSERT(b_format < format + sizeof(format));
|
||||
return std::snprintf(
|
||||
dst, size, format,
|
||||
math::numeric_cast<long double>(value_));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class format_arg<bool> {
|
||||
bool value_;
|
||||
public:
|
||||
explicit format_arg(bool value) noexcept
|
||||
: value_(value) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const noexcept {
|
||||
return std::snprintf(
|
||||
dst, size, "%s",
|
||||
value_ ? "true" : "false");
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class format_arg<char*> {
|
||||
const char* value_;
|
||||
public:
|
||||
explicit format_arg(const char* value) noexcept
|
||||
: value_(value) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const noexcept {
|
||||
return std::snprintf(dst, size, "%s", value_);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class format_arg<const char*> {
|
||||
const char* value_;
|
||||
public:
|
||||
explicit format_arg(const char* value) noexcept
|
||||
: value_(value) {}
|
||||
|
||||
std::ptrdiff_t write(char* dst, size_t size) const noexcept {
|
||||
return std::snprintf(dst, size, "%s", value_);
|
||||
}
|
||||
};
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template < typename T >
|
||||
struct is_arg_impl : std::false_type {};
|
||||
template < typename U >
|
||||
struct is_arg_impl<format_arg<U>> : std::true_type {};
|
||||
template < typename T >
|
||||
struct is_arg : is_arg_impl<std::remove_cv_t<T>> {};
|
||||
|
||||
template < typename T >
|
||||
std::enable_if_t<is_arg<std::decay_t<T>>::value, T>
|
||||
wrap_arg(T&& arg) {
|
||||
return std::forward<T>(arg);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
std::enable_if_t<!is_arg<std::decay_t<T>>::value, format_arg<std::decay_t<T>>>
|
||||
wrap_arg(T&& value) {
|
||||
return make_format_arg(std::forward<T>(value));
|
||||
}
|
||||
|
||||
template < std::size_t N, typename Tuple >
|
||||
std::enable_if_t<N < std::tuple_size<Tuple>::value, std::ptrdiff_t>
|
||||
write_arg_n(char* dst, std::size_t size, const Tuple& targs) {
|
||||
return std::get<N>(targs).write(dst, size);
|
||||
}
|
||||
|
||||
template < std::size_t N, typename Tuple >
|
||||
std::enable_if_t<N >= std::tuple_size<Tuple>::value, std::ptrdiff_t>
|
||||
write_arg_n(char* dst, std::size_t size, const Tuple& targs) {
|
||||
E2D_UNUSED(dst, size, targs);
|
||||
if ( dst ) {
|
||||
*dst = '\0';
|
||||
}
|
||||
throw bad_format();
|
||||
}
|
||||
|
||||
template < typename Tuple >
|
||||
std::enable_if_t<std::tuple_size<Tuple>::value <= 10, std::size_t>
|
||||
format_impl(char* dst, std::size_t size, const char* format, const Tuple& targs) {
|
||||
if ( !format ) {
|
||||
throw bad_format();
|
||||
}
|
||||
if ( !dst != !size ) {
|
||||
throw bad_format_buffer();
|
||||
}
|
||||
std::size_t result = 0;
|
||||
const char* const b_dst = dst;
|
||||
const char* const e_dst = b_dst ? b_dst + size : nullptr;
|
||||
while ( *format ) {
|
||||
if ( dst && dst == e_dst - 1 ) {
|
||||
*dst = '\0';
|
||||
throw bad_format_buffer();
|
||||
}
|
||||
if ( *format != '%' ) {
|
||||
if ( dst ) {
|
||||
*dst++ = *format;
|
||||
}
|
||||
++result;
|
||||
++format;
|
||||
} else {
|
||||
const char n_param = *(++format);
|
||||
if ( !n_param ) {
|
||||
// "hello%"
|
||||
if ( dst ) {
|
||||
*dst = '\0';
|
||||
}
|
||||
throw bad_format();
|
||||
}
|
||||
std::ptrdiff_t write_arg_r = 0;
|
||||
std::size_t dst_tail_size = dst
|
||||
? math::numeric_cast<std::size_t>(e_dst - dst)
|
||||
: 0;
|
||||
E2D_ASSERT(!dst || dst_tail_size);
|
||||
switch ( n_param ) {
|
||||
case '0' :
|
||||
write_arg_r = write_arg_n<0>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '1' :
|
||||
write_arg_r = write_arg_n<1>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '2' :
|
||||
write_arg_r = write_arg_n<2>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '3' :
|
||||
write_arg_r = write_arg_n<3>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '4' :
|
||||
write_arg_r = write_arg_n<4>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '5' :
|
||||
write_arg_r = write_arg_n<5>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '6' :
|
||||
write_arg_r = write_arg_n<6>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '7' :
|
||||
write_arg_r = write_arg_n<7>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '8' :
|
||||
write_arg_r = write_arg_n<8>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '9' :
|
||||
write_arg_r = write_arg_n<9>(dst, dst_tail_size, targs);
|
||||
break;
|
||||
case '%':
|
||||
// "hel%%lo" -> "hel%lo"
|
||||
if ( dst ) {
|
||||
*dst = '%';
|
||||
}
|
||||
write_arg_r = 1;
|
||||
break;
|
||||
default:
|
||||
// "hel%xlo"
|
||||
if ( dst ) {
|
||||
*dst = '\0';
|
||||
}
|
||||
throw bad_format();
|
||||
}
|
||||
if ( write_arg_r < 0 ) {
|
||||
if ( dst ) {
|
||||
*dst = '\0';
|
||||
}
|
||||
throw bad_format_argument();
|
||||
}
|
||||
const std::size_t write_bytes =
|
||||
math::abs_to_unsigned(write_arg_r);
|
||||
if ( size && write_bytes >= dst_tail_size ) {
|
||||
if ( dst ) {
|
||||
E2D_ASSERT(b_dst + size == dst + dst_tail_size);
|
||||
*(dst + dst_tail_size - 1) = '\0';
|
||||
}
|
||||
throw bad_format_buffer();
|
||||
}
|
||||
if ( dst ) {
|
||||
dst += write_bytes;
|
||||
}
|
||||
result += write_bytes;
|
||||
++format;
|
||||
}
|
||||
}
|
||||
if ( dst ) {
|
||||
*dst = '\0';
|
||||
}
|
||||
E2D_ASSERT(
|
||||
!dst ||
|
||||
result == math::numeric_cast<std::size_t>(dst - b_dst));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
std::size_t format(
|
||||
char* dst, std::size_t size,
|
||||
const char* fmt, Args&&... args)
|
||||
{
|
||||
return impl::format_impl(
|
||||
dst, size, fmt,
|
||||
std::make_tuple(impl::wrap_arg(std::forward<Args>(args))...));
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
str rformat(const char* fmt, Args&&... args) {
|
||||
auto targs = std::make_tuple(
|
||||
impl::wrap_arg(std::forward<Args>(args))...);
|
||||
const std::size_t expected_format_size = impl::format_impl(
|
||||
nullptr, 0, fmt, targs);
|
||||
vector<char> buffer(expected_format_size + 1, '\0');
|
||||
const std::size_t actual_format_size = impl::format_impl(
|
||||
buffer.data(), buffer.size(), fmt, targs);
|
||||
E2D_ASSERT(expected_format_size == actual_format_size);
|
||||
return str(buffer.data(), buffer.data() + actual_format_size);
|
||||
}
|
||||
}}
|
||||
168
headers/enduro2d/utils/time.hpp
Normal file
168
headers/enduro2d/utils/time.hpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
namespace e2d { namespace time
|
||||
{
|
||||
template < typename T >
|
||||
const seconds<T>& second() noexcept {
|
||||
static seconds<T> second = seconds<T>(T(1));
|
||||
return second;
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
const seconds<T>& minute() noexcept {
|
||||
static seconds<T> minute = second<T>() * T(60);
|
||||
return minute;
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
const seconds<T>& hour() noexcept {
|
||||
static seconds<T> hour = minute<T>() * T(60);
|
||||
return hour;
|
||||
}
|
||||
}}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename T >
|
||||
seconds<T> make_seconds(T v) noexcept {
|
||||
return make_unit<seconds_tag>(v);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
milliseconds<T> make_milliseconds(T v) noexcept {
|
||||
return make_unit<milliseconds_tag>(v);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
microseconds<T> make_microseconds(T v) noexcept {
|
||||
return make_unit<microseconds_tag>(v);
|
||||
}
|
||||
|
||||
template <>
|
||||
struct unit_converter<seconds_tag, milliseconds_tag> {
|
||||
template < typename T >
|
||||
milliseconds<T> operator()(const seconds<T>& u) const noexcept {
|
||||
const i64 seconds_to_milli = 1000;
|
||||
return make_milliseconds(u.value * seconds_to_milli)
|
||||
.template cast_to<T>();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct unit_converter<seconds_tag, microseconds_tag> {
|
||||
template < typename T >
|
||||
microseconds<T> operator()(const seconds<T>& u) const noexcept {
|
||||
const i64 seconds_to_micro = 1000 * 1000;
|
||||
return make_microseconds(u.value * seconds_to_micro)
|
||||
.template cast_to<T>();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct unit_converter<milliseconds_tag, seconds_tag> {
|
||||
template < typename T >
|
||||
seconds<T> operator()(const milliseconds<T>& u) const noexcept {
|
||||
const f64 milli_to_seconds = 1.0 / 1000.0;
|
||||
return make_seconds(u.value * milli_to_seconds)
|
||||
.template cast_to<T>();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct unit_converter<milliseconds_tag, microseconds_tag> {
|
||||
template < typename T >
|
||||
microseconds<T> operator()(const milliseconds<T>& u) const noexcept {
|
||||
const i64 milli_to_micro = 1000;
|
||||
return make_microseconds(u.value * milli_to_micro)
|
||||
.template cast_to<T>();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct unit_converter<microseconds_tag, seconds_tag> {
|
||||
template < typename T >
|
||||
seconds<T> operator()(const microseconds<T>& u) const noexcept {
|
||||
const f64 micro_to_seconds = 1.0 / 1000.0 / 1000.0;
|
||||
return make_seconds(u.value * micro_to_seconds)
|
||||
.template cast_to<T>();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct unit_converter<microseconds_tag, milliseconds_tag> {
|
||||
template < typename T >
|
||||
milliseconds<T> operator()(const microseconds<T>& u) const noexcept {
|
||||
const f64 micro_to_milli = 1.0 / 1000.0;
|
||||
return make_milliseconds(u.value * micro_to_milli)
|
||||
.template cast_to<T>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d { namespace time
|
||||
{
|
||||
template < typename T >
|
||||
std::chrono::duration<T, std::ratio<1ll, 1ll>>
|
||||
to_chrono(const unit<T, seconds_tag>& u) noexcept {
|
||||
return std::chrono::duration<T, std::ratio<1ll>>(u.value);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
std::chrono::duration<T, std::ratio<1ll, 1000ll>>
|
||||
to_chrono(const unit<T, milliseconds_tag>& u) noexcept {
|
||||
return std::chrono::duration<T, std::ratio<1ll, 1000ll>>(u.value);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
std::chrono::duration<T, std::ratio<1ll, 1'000'000ll>>
|
||||
to_chrono(const unit<T, microseconds_tag>& u) noexcept {
|
||||
return std::chrono::duration<T, std::ratio<1ll, 1'000'000ll>>(u.value);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
seconds<T> to_seconds(const unit<T, Tag>& u) noexcept {
|
||||
return u.template convert_to<seconds_tag>();
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
milliseconds<T> to_milliseconds(const unit<T, Tag>& u) noexcept {
|
||||
return u.template convert_to<milliseconds_tag>();
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
microseconds<T> to_microseconds(const unit<T, Tag>& u) noexcept {
|
||||
return u.template convert_to<microseconds_tag>();
|
||||
}
|
||||
}}
|
||||
|
||||
namespace e2d { namespace time
|
||||
{
|
||||
template < typename TimeTag >
|
||||
unit<i64, TimeTag> now() noexcept {
|
||||
namespace ch = std::chrono;
|
||||
const auto n = ch::high_resolution_clock::now();
|
||||
const auto m = ch::time_point_cast<ch::microseconds>(n);
|
||||
const auto c = m.time_since_epoch().count();
|
||||
return make_microseconds(c).cast_to<i64>().convert_to<TimeTag>();
|
||||
}
|
||||
|
||||
inline unit<i64, seconds_tag> now_s() noexcept {
|
||||
return now<seconds_tag>();
|
||||
}
|
||||
|
||||
inline unit<i64, milliseconds_tag> now_ms() noexcept {
|
||||
return now<milliseconds_tag>();
|
||||
}
|
||||
|
||||
inline unit<i64, microseconds_tag> now_us() noexcept {
|
||||
return now<microseconds_tag>();
|
||||
}
|
||||
}}
|
||||
Submodule modules/catch2 updated: c9de7dd12d...38e1731f69
Submodule modules/utfcpp updated: 1537543999...e6bde7819c
@@ -8,6 +8,7 @@ function(add_e2d_sample NAME)
|
||||
add_executable(${SAMPLE_NAME} ${SAMPLE_SOURCES})
|
||||
target_include_directories(${SAMPLE_NAME} PRIVATE "../headers")
|
||||
target_link_libraries(${SAMPLE_NAME} enduro2d)
|
||||
target_link_libraries(${SAMPLE_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||
endfunction(add_e2d_sample)
|
||||
|
||||
add_e2d_sample(00)
|
||||
|
||||
@@ -4,7 +4,7 @@ mkdir %BUILD_DIR%\debug || goto :error
|
||||
cd %BUILD_DIR%\debug || goto :error
|
||||
cmake ../.. || goto :error
|
||||
cmake --build . --config Debug || goto :error
|
||||
ctest || goto :error
|
||||
ctest --verbose || goto :error
|
||||
cd ..\.. || goto :error
|
||||
|
||||
goto :EOF
|
||||
|
||||
@@ -5,5 +5,5 @@ mkdir -p $BUILD_DIR/debug
|
||||
cd $BUILD_DIR/debug
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug ../..
|
||||
cmake --build . -- -j8
|
||||
ctest
|
||||
ctest --verbose
|
||||
cd ../..
|
||||
|
||||
@@ -4,7 +4,7 @@ mkdir %BUILD_DIR%\release || goto :error
|
||||
cd %BUILD_DIR%\release || goto :error
|
||||
cmake ../.. || goto :error
|
||||
cmake --build . --config Release || goto :error
|
||||
ctest || goto :error
|
||||
ctest --verbose || goto :error
|
||||
cd ..\.. || goto :error
|
||||
|
||||
goto :EOF
|
||||
|
||||
@@ -5,5 +5,5 @@ mkdir -p $BUILD_DIR/release
|
||||
cd $BUILD_DIR/release
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ../..
|
||||
cmake --build . -- -j8
|
||||
ctest
|
||||
ctest --verbose
|
||||
cd ../..
|
||||
|
||||
@@ -10,7 +10,8 @@ mkdir -p $RDPARTY_DIR
|
||||
|
||||
git submodule init
|
||||
git submodule update
|
||||
git submodule foreach git pull
|
||||
git pull --recurse-submodules
|
||||
git submodule update --remote --recursive
|
||||
|
||||
mkdir -p $ROOT_DIR/untests/catch
|
||||
cp -fv $MODULES_DIR/catch2/single_include/catch2/catch.hpp $ROOT_DIR/untests/catch/catch.hpp
|
||||
|
||||
141
sources/enduro2d/utils/buffer.cpp
Normal file
141
sources/enduro2d/utils/buffer.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/utils/buffer.hpp>
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
buffer::buffer(buffer&& other) noexcept {
|
||||
assign(std::move(other));
|
||||
}
|
||||
|
||||
buffer& buffer::operator=(buffer&& other) noexcept {
|
||||
return assign(std::move(other));
|
||||
}
|
||||
|
||||
buffer::buffer(const buffer& other) {
|
||||
assign(other);
|
||||
}
|
||||
|
||||
buffer& buffer::operator=(const buffer& other) {
|
||||
return assign(other);
|
||||
}
|
||||
|
||||
buffer::buffer(std::size_t size) {
|
||||
resize(size);
|
||||
}
|
||||
|
||||
buffer::buffer(const void* src, std::size_t size) {
|
||||
assign(src, size);
|
||||
}
|
||||
|
||||
buffer& buffer::fill(u8 ch) noexcept {
|
||||
if ( data_ && size_) {
|
||||
std::memset(data_.get(), ch, size_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
buffer& buffer::resize(std::size_t nsize) {
|
||||
data_t ndata = size_ == nsize
|
||||
? std::move(data_)
|
||||
: (nsize
|
||||
? std::make_unique<u8[]>(nsize)
|
||||
: data_t());
|
||||
if ( ndata && data_ && size_ ) {
|
||||
std::memcpy(ndata.get(), data_.get(), math::min(size_, nsize));
|
||||
}
|
||||
data_.swap(ndata);
|
||||
size_ = nsize;
|
||||
return *this;
|
||||
}
|
||||
|
||||
buffer& buffer::assign(buffer&& other) noexcept {
|
||||
if ( this != &other ) {
|
||||
swap(other);
|
||||
other.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
buffer& buffer::assign(const buffer& other) {
|
||||
return this != &other
|
||||
? assign(other.data(), other.size())
|
||||
: *this;
|
||||
}
|
||||
|
||||
buffer& buffer::assign(const void* src, std::size_t nsize) {
|
||||
data_t ndata = size_ == nsize
|
||||
? std::move(data_)
|
||||
: (nsize
|
||||
? std::make_unique<u8[]>(nsize)
|
||||
: data_t());
|
||||
if ( ndata && src && nsize ) {
|
||||
std::memcpy(ndata.get(), src, nsize);
|
||||
}
|
||||
data_.swap(ndata);
|
||||
size_ = nsize;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void buffer::swap(buffer& other) noexcept {
|
||||
using std::swap;
|
||||
swap(data_, other.data_);
|
||||
swap(size_, other.size_);
|
||||
}
|
||||
|
||||
void buffer::clear() noexcept {
|
||||
data_.reset();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
bool buffer::empty() const noexcept {
|
||||
return !size_;
|
||||
}
|
||||
|
||||
u8* buffer::data() noexcept {
|
||||
return data_.get();
|
||||
}
|
||||
|
||||
const u8* buffer::data() const noexcept {
|
||||
return data_.get();
|
||||
}
|
||||
|
||||
std::size_t buffer::size() const noexcept {
|
||||
return size_;
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
void swap(buffer& l, buffer& r) noexcept {
|
||||
l.swap(r);
|
||||
}
|
||||
|
||||
bool operator<(const buffer& l, const buffer& r) noexcept {
|
||||
const u8* ld = l.data();
|
||||
const u8* rd = r.data();
|
||||
const std::size_t ls = l.size();
|
||||
const std::size_t rs = r.size();
|
||||
return
|
||||
(ls < rs) ||
|
||||
(ls == rs && ls > 0 && std::memcmp(ld, rd, ls) < 0);
|
||||
}
|
||||
|
||||
bool operator==(const buffer& l, const buffer& r) noexcept {
|
||||
const u8* ld = l.data();
|
||||
const u8* rd = r.data();
|
||||
const std::size_t ls = l.size();
|
||||
const std::size_t rs = r.size();
|
||||
return
|
||||
(ls == rs) &&
|
||||
(ls == 0 || std::memcmp(ld, rd, ls) == 0);
|
||||
}
|
||||
|
||||
bool operator!=(const buffer& l, const buffer& r) noexcept {
|
||||
return !(l == r);
|
||||
}
|
||||
}
|
||||
338
sources/enduro2d/utils/color.cpp
Normal file
338
sources/enduro2d/utils/color.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/utils/color.hpp>
|
||||
#include <enduro2d/utils/color32.hpp>
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
const color& color::clear() noexcept {
|
||||
static const color c(0, 0, 0, 0);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color& color::black() noexcept {
|
||||
static const color c(0, 0, 0, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color& color::white() noexcept {
|
||||
static const color c(1, 1, 1, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color& color::red() noexcept {
|
||||
static const color c(1, 0, 0, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color& color::green() noexcept {
|
||||
static const color c(0, 1, 0, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color& color::blue() noexcept {
|
||||
static const color c(0, 0, 1, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color& color::yellow() noexcept {
|
||||
static const color c(1, 1, 0, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color& color::magenta() noexcept {
|
||||
static const color c(1, 0, 1, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color& color::cyan() noexcept {
|
||||
static const color c(0, 1, 1, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
color::color(const color32& other) noexcept
|
||||
: r(other.r / 255.f)
|
||||
, g(other.g / 255.f)
|
||||
, b(other.b / 255.f)
|
||||
, a(other.a / 255.f) {}
|
||||
|
||||
color::color(f32 nr, f32 ng, f32 nb, f32 na) noexcept
|
||||
: r(nr)
|
||||
, g(ng)
|
||||
, b(nb)
|
||||
, a(na) {}
|
||||
|
||||
f32* color::data() noexcept {
|
||||
return &r;
|
||||
}
|
||||
|
||||
const f32* color::data() const noexcept {
|
||||
return &r;
|
||||
}
|
||||
|
||||
f32& color::operator[](std::size_t index) noexcept {
|
||||
E2D_ASSERT(index < 4);
|
||||
return data()[index];
|
||||
}
|
||||
|
||||
f32 color::operator[](std::size_t index) const noexcept {
|
||||
E2D_ASSERT(index < 4);
|
||||
return data()[index];
|
||||
}
|
||||
|
||||
color& color::operator+=(f32 v) noexcept {
|
||||
return *this += color(v,v,v,v);
|
||||
}
|
||||
|
||||
color& color::operator-=(f32 v) noexcept {
|
||||
return *this -= color(v,v,v,v);
|
||||
}
|
||||
|
||||
color& color::operator*=(f32 v) noexcept {
|
||||
return *this *= color(v,v,v,v);
|
||||
}
|
||||
|
||||
color& color::operator/=(f32 v) noexcept {
|
||||
return *this /= color(v,v,v,v);
|
||||
}
|
||||
|
||||
color& color::operator+=(const color& other) noexcept {
|
||||
r += other.r;
|
||||
g += other.g;
|
||||
b += other.b;
|
||||
a += other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
color& color::operator-=(const color& other) noexcept {
|
||||
r -= other.r;
|
||||
g -= other.g;
|
||||
b -= other.b;
|
||||
a -= other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
color& color::operator*=(const color& other) noexcept {
|
||||
r *= other.r;
|
||||
g *= other.g;
|
||||
b *= other.b;
|
||||
a *= other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
color& color::operator/=(const color& other) noexcept {
|
||||
E2D_ASSERT(!math::contains(other, 0.f, 0.f));
|
||||
r /= other.r;
|
||||
g /= other.g;
|
||||
b /= other.b;
|
||||
a /= other.a;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
//
|
||||
// color (<,==,!=) color
|
||||
//
|
||||
|
||||
bool operator<(const color& l, const color& r) noexcept {
|
||||
return (l.r < r.r)
|
||||
|| (l.r == r.r && l.g < r.g)
|
||||
|| (l.r == r.r && l.g == r.g && l.b < r.b)
|
||||
|| (l.r == r.r && l.g == r.g && l.b == r.b && l.a < r.a);
|
||||
}
|
||||
|
||||
bool operator==(const color& l, const color& r) noexcept {
|
||||
return math::approximately(l.r, r.r)
|
||||
&& math::approximately(l.g, r.g)
|
||||
&& math::approximately(l.b, r.b)
|
||||
&& math::approximately(l.a, r.a);
|
||||
}
|
||||
|
||||
bool operator!=(const color& l, const color& r) noexcept {
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
//
|
||||
// color (+,-,*,/) value
|
||||
//
|
||||
|
||||
color operator+(color l, f32 v) noexcept {
|
||||
l += v;
|
||||
return l;
|
||||
}
|
||||
|
||||
color operator-(color l, f32 v) noexcept {
|
||||
l -= v;
|
||||
return l;
|
||||
}
|
||||
|
||||
color operator*(color l, f32 v) noexcept {
|
||||
l *= v;
|
||||
return l;
|
||||
}
|
||||
|
||||
color operator/(color l, f32 v) noexcept {
|
||||
l /= v;
|
||||
return l;
|
||||
}
|
||||
|
||||
//
|
||||
// value (+,-,*,/) color
|
||||
//
|
||||
|
||||
color operator+(f32 v, const color& r) noexcept {
|
||||
color l(v,v,v,v);
|
||||
l += r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color operator-(f32 v, const color& r) noexcept {
|
||||
color l(v,v,v,v);
|
||||
l -= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color operator*(f32 v, const color& r) noexcept {
|
||||
color l(v,v,v,v);
|
||||
l *= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color operator/(f32 v, const color& r) noexcept {
|
||||
color l(v,v,v,v);
|
||||
l /= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
//
|
||||
// color (+,-,*,/) color
|
||||
//
|
||||
|
||||
color operator+(color l, const color& r) noexcept {
|
||||
l += r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color operator-(color l, const color& r) noexcept {
|
||||
l -= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color operator*(color l, const color& r) noexcept {
|
||||
l *= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color operator/(color l, const color& r) noexcept {
|
||||
l /= r;
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace math
|
||||
{
|
||||
//
|
||||
// approximately
|
||||
//
|
||||
|
||||
bool approximately(const color& l, const color& r) noexcept {
|
||||
return math::approximately(l.r, r.r)
|
||||
&& math::approximately(l.g, r.g)
|
||||
&& math::approximately(l.b, r.b)
|
||||
&& math::approximately(l.a, r.a);
|
||||
}
|
||||
|
||||
bool approximately(const color& l, const color& r, f32 precision) noexcept {
|
||||
return math::approximately(l.r, r.r, precision)
|
||||
&& math::approximately(l.g, r.g, precision)
|
||||
&& math::approximately(l.b, r.b, precision)
|
||||
&& math::approximately(l.a, r.a, precision);
|
||||
}
|
||||
|
||||
//
|
||||
// minimum/maximum
|
||||
//
|
||||
|
||||
f32 minimum(const color& c) noexcept {
|
||||
return math::min(math::min(math::min(c.r, c.g), c.b), c.a);
|
||||
}
|
||||
|
||||
f32 maximum(const color& c) noexcept {
|
||||
return math::max(math::max(math::max(c.r, c.g), c.b), c.a);
|
||||
}
|
||||
|
||||
//
|
||||
// minimized/maximized/clamped
|
||||
//
|
||||
|
||||
color minimized(const color& c, const color& cmin) noexcept {
|
||||
return color(
|
||||
math::min(c.r, cmin.r),
|
||||
math::min(c.g, cmin.g),
|
||||
math::min(c.b, cmin.b),
|
||||
math::min(c.a, cmin.a));
|
||||
}
|
||||
|
||||
color maximized(const color& c, const color& cmax) noexcept {
|
||||
return color(
|
||||
math::max(c.r, cmax.r),
|
||||
math::max(c.g, cmax.g),
|
||||
math::max(c.b, cmax.b),
|
||||
math::max(c.a, cmax.a));
|
||||
}
|
||||
|
||||
color clamped(const color& c, const color& cmin, const color& cmax) noexcept {
|
||||
return color(
|
||||
math::clamp(c.r, cmin.r, cmax.r),
|
||||
math::clamp(c.g, cmin.g, cmax.g),
|
||||
math::clamp(c.b, cmin.b, cmax.b),
|
||||
math::clamp(c.a, cmin.a, cmax.a));
|
||||
}
|
||||
|
||||
color saturated(const color& c) noexcept {
|
||||
return clamped(c, color::clear(), color::white());
|
||||
}
|
||||
|
||||
//
|
||||
// contains
|
||||
//
|
||||
|
||||
bool contains(const color& c, f32 value, f32 precision) noexcept {
|
||||
return math::approximately(c.r, value, precision)
|
||||
|| math::approximately(c.g, value, precision)
|
||||
|| math::approximately(c.b, value, precision)
|
||||
|| math::approximately(c.a, value, precision);
|
||||
}
|
||||
|
||||
bool contains_nan(const color& c) noexcept {
|
||||
return !math::is_finite(c.r)
|
||||
|| !math::is_finite(c.g)
|
||||
|| !math::is_finite(c.b)
|
||||
|| !math::is_finite(c.a);
|
||||
}
|
||||
}}
|
||||
|
||||
namespace e2d { namespace colors
|
||||
{
|
||||
u32 pack_color(const color& c) noexcept {
|
||||
return
|
||||
math::numeric_cast<u32>(math::round(math::saturate(c.a) * 255.f)) << 24 |
|
||||
math::numeric_cast<u32>(math::round(math::saturate(c.r) * 255.f)) << 16 |
|
||||
math::numeric_cast<u32>(math::round(math::saturate(c.g) * 255.f)) << 8 |
|
||||
math::numeric_cast<u32>(math::round(math::saturate(c.b) * 255.f)) << 0;
|
||||
}
|
||||
|
||||
color unpack_color(u32 argb) noexcept {
|
||||
return color(
|
||||
math::numeric_cast<u8>((argb >> 16) & 0xFF) / 255.f,
|
||||
math::numeric_cast<u8>((argb >> 8) & 0xFF) / 255.f,
|
||||
math::numeric_cast<u8>((argb >> 0) & 0xFF) / 255.f,
|
||||
math::numeric_cast<u8>((argb >> 24) & 0xFF) / 255.f);
|
||||
}
|
||||
}}
|
||||
327
sources/enduro2d/utils/color32.cpp
Normal file
327
sources/enduro2d/utils/color32.cpp
Normal file
@@ -0,0 +1,327 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/utils/color32.hpp>
|
||||
#include <enduro2d/utils/color.hpp>
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
const color32& color32::clear() noexcept {
|
||||
static const color32 c(0, 0, 0, 0);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color32& color32::black() noexcept {
|
||||
static const color32 c(0, 0, 0, 255);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color32& color32::white() noexcept {
|
||||
static const color32 c(255, 255, 255, 255);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color32& color32::red() noexcept {
|
||||
static const color32 c(255, 0, 0, 255);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color32& color32::green() noexcept {
|
||||
static const color32 c(0, 255, 0, 255);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color32& color32::blue() noexcept {
|
||||
static const color32 c(0, 0, 255, 255);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color32& color32::yellow() noexcept {
|
||||
static const color32 c(255, 255, 0, 255);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color32& color32::magenta() noexcept {
|
||||
static const color32 c(255, 0, 255, 255);
|
||||
return c;
|
||||
}
|
||||
|
||||
const color32& color32::cyan() noexcept {
|
||||
static const color32 c(0, 255, 255, 255);
|
||||
return c;
|
||||
}
|
||||
|
||||
color32::color32(const color& other) noexcept
|
||||
: r(math::numeric_cast<u8>(math::round(math::saturate(other.r) * 255.f)))
|
||||
, g(math::numeric_cast<u8>(math::round(math::saturate(other.g) * 255.f)))
|
||||
, b(math::numeric_cast<u8>(math::round(math::saturate(other.b) * 255.f)))
|
||||
, a(math::numeric_cast<u8>(math::round(math::saturate(other.a) * 255.f))) {}
|
||||
|
||||
color32::color32(u8 nr, u8 ng, u8 nb, u8 na) noexcept
|
||||
: r(nr)
|
||||
, g(ng)
|
||||
, b(nb)
|
||||
, a(na) {}
|
||||
|
||||
u8* color32::data() noexcept {
|
||||
return &r;
|
||||
}
|
||||
|
||||
const u8* color32::data() const noexcept {
|
||||
return &r;
|
||||
}
|
||||
|
||||
u8& color32::operator[](std::size_t index) noexcept {
|
||||
E2D_ASSERT(index < 4);
|
||||
return data()[index];
|
||||
}
|
||||
|
||||
u8 color32::operator[](std::size_t index) const noexcept {
|
||||
E2D_ASSERT(index < 4);
|
||||
return data()[index];
|
||||
}
|
||||
|
||||
color32& color32::operator+=(u8 v) noexcept {
|
||||
return *this += color32(v,v,v,v);
|
||||
}
|
||||
|
||||
color32& color32::operator-=(u8 v) noexcept {
|
||||
return *this -= color32(v,v,v,v);
|
||||
}
|
||||
|
||||
color32& color32::operator*=(u8 v) noexcept {
|
||||
return *this *= color32(v,v,v,v);
|
||||
}
|
||||
|
||||
color32& color32::operator/=(u8 v) noexcept {
|
||||
return *this /= color32(v,v,v,v);
|
||||
}
|
||||
|
||||
color32& color32::operator+=(const color32& other) noexcept {
|
||||
r += other.r;
|
||||
g += other.g;
|
||||
b += other.b;
|
||||
a += other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
color32& color32::operator-=(const color32& other) noexcept {
|
||||
r -= other.r;
|
||||
g -= other.g;
|
||||
b -= other.b;
|
||||
a -= other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
color32& color32::operator*=(const color32& other) noexcept {
|
||||
r *= other.r;
|
||||
g *= other.g;
|
||||
b *= other.b;
|
||||
a *= other.a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
color32& color32::operator/=(const color32& other) noexcept {
|
||||
E2D_ASSERT(!math::contains(other, u8(0), u8(0)));
|
||||
r /= other.r;
|
||||
g /= other.g;
|
||||
b /= other.b;
|
||||
a /= other.a;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
//
|
||||
// color32 (<,==,!=) color32
|
||||
//
|
||||
|
||||
bool operator<(const color32& l, const color32& r) noexcept {
|
||||
return (l.r < r.r)
|
||||
|| (l.r == r.r && l.g < r.g)
|
||||
|| (l.r == r.r && l.g == r.g && l.b < r.b)
|
||||
|| (l.r == r.r && l.g == r.g && l.b == r.b && l.a < r.a);
|
||||
}
|
||||
|
||||
bool operator==(const color32& l, const color32& r) noexcept {
|
||||
return math::approximately(l.r, r.r)
|
||||
&& math::approximately(l.g, r.g)
|
||||
&& math::approximately(l.b, r.b)
|
||||
&& math::approximately(l.a, r.a);
|
||||
}
|
||||
|
||||
bool operator!=(const color32& l, const color32& r) noexcept {
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
//
|
||||
// color32 (+,-,*,/) value
|
||||
//
|
||||
|
||||
color32 operator+(color32 l, u8 v) noexcept {
|
||||
l += v;
|
||||
return l;
|
||||
}
|
||||
|
||||
color32 operator-(color32 l, u8 v) noexcept {
|
||||
l -= v;
|
||||
return l;
|
||||
}
|
||||
|
||||
color32 operator*(color32 l, u8 v) noexcept {
|
||||
l *= v;
|
||||
return l;
|
||||
}
|
||||
|
||||
color32 operator/(color32 l, u8 v) noexcept {
|
||||
l /= v;
|
||||
return l;
|
||||
}
|
||||
|
||||
//
|
||||
// value (+,-,*,/) color32
|
||||
//
|
||||
|
||||
color32 operator+(u8 v, const color32& r) noexcept {
|
||||
color32 l(v,v,v,v);
|
||||
l += r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color32 operator-(u8 v, const color32& r) noexcept {
|
||||
color32 l(v,v,v,v);
|
||||
l -= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color32 operator*(u8 v, const color32& r) noexcept {
|
||||
color32 l(v,v,v,v);
|
||||
l *= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color32 operator/(u8 v, const color32& r) noexcept {
|
||||
color32 l(v,v,v,v);
|
||||
l /= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
//
|
||||
// color32 (+,-,*,/) color32
|
||||
//
|
||||
|
||||
color32 operator+(color32 l, const color32& r) noexcept {
|
||||
l += r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color32 operator-(color32 l, const color32& r) noexcept {
|
||||
l -= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color32 operator*(color32 l, const color32& r) noexcept {
|
||||
l *= r;
|
||||
return l;
|
||||
}
|
||||
|
||||
color32 operator/(color32 l, const color32& r) noexcept {
|
||||
l /= r;
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace math
|
||||
{
|
||||
//
|
||||
// approximately
|
||||
//
|
||||
|
||||
bool approximately(const color32& l, const color32& r) noexcept {
|
||||
return math::approximately(l.r, r.r)
|
||||
&& math::approximately(l.g, r.g)
|
||||
&& math::approximately(l.b, r.b)
|
||||
&& math::approximately(l.a, r.a);
|
||||
}
|
||||
|
||||
bool approximately(const color32& l, const color32& r, u8 precision) noexcept {
|
||||
return math::approximately(l.r, r.r, precision)
|
||||
&& math::approximately(l.g, r.g, precision)
|
||||
&& math::approximately(l.b, r.b, precision)
|
||||
&& math::approximately(l.a, r.a, precision);
|
||||
}
|
||||
|
||||
//
|
||||
// minimum/maximum
|
||||
//
|
||||
|
||||
u8 minimum(const color32& c) noexcept {
|
||||
return math::min(math::min(math::min(c.r, c.g), c.b), c.a);
|
||||
}
|
||||
|
||||
u8 maximum(const color32& c) noexcept {
|
||||
return math::max(math::max(math::max(c.r, c.g), c.b), c.a);
|
||||
}
|
||||
|
||||
//
|
||||
// minimized/maximized/clamped
|
||||
//
|
||||
|
||||
color32 minimized(const color32& c, const color32& cmin) noexcept {
|
||||
return color32(
|
||||
math::min(c.r, cmin.r),
|
||||
math::min(c.g, cmin.g),
|
||||
math::min(c.b, cmin.b),
|
||||
math::min(c.a, cmin.a));
|
||||
}
|
||||
|
||||
color32 maximized(const color32& c, const color32& cmax) noexcept {
|
||||
return color32(
|
||||
math::max(c.r, cmax.r),
|
||||
math::max(c.g, cmax.g),
|
||||
math::max(c.b, cmax.b),
|
||||
math::max(c.a, cmax.a));
|
||||
}
|
||||
|
||||
color32 clamped(const color32& c, const color32& cmin, const color32& cmax) noexcept {
|
||||
return color32(
|
||||
math::clamp(c.r, cmin.r, cmax.r),
|
||||
math::clamp(c.g, cmin.g, cmax.g),
|
||||
math::clamp(c.b, cmin.b, cmax.b),
|
||||
math::clamp(c.a, cmin.a, cmax.a));
|
||||
}
|
||||
|
||||
//
|
||||
// contains
|
||||
//
|
||||
|
||||
bool contains(const color32& c, u8 value, u8 precision) noexcept {
|
||||
return math::approximately(c.r, value, precision)
|
||||
|| math::approximately(c.g, value, precision)
|
||||
|| math::approximately(c.b, value, precision)
|
||||
|| math::approximately(c.a, value, precision);
|
||||
}
|
||||
}}
|
||||
|
||||
namespace e2d { namespace colors
|
||||
{
|
||||
u32 pack_color32(const color32& c) noexcept {
|
||||
return
|
||||
math::numeric_cast<u32>(c.a) << 24 |
|
||||
math::numeric_cast<u32>(c.r) << 16 |
|
||||
math::numeric_cast<u32>(c.g) << 8 |
|
||||
math::numeric_cast<u32>(c.b) << 0;
|
||||
}
|
||||
|
||||
color32 unpack_color32(u32 argb) noexcept {
|
||||
return color32(
|
||||
math::numeric_cast<u8>((argb >> 16) & 0xFF),
|
||||
math::numeric_cast<u8>((argb >> 8) & 0xFF),
|
||||
math::numeric_cast<u8>((argb >> 0) & 0xFF),
|
||||
math::numeric_cast<u8>((argb >> 24) & 0xFF));
|
||||
}
|
||||
}}
|
||||
203
sources/enduro2d/utils/filesystem.cpp
Normal file
203
sources/enduro2d/utils/filesystem.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "filesystem_impl/filesystem_impl.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
const str_view dot = ".";
|
||||
const str_view dot_dot = "..";
|
||||
const str_view path_separators = "/\\";
|
||||
|
||||
bool is_directory_separator(char ch) noexcept {
|
||||
return path_separators.cend() != std::find(
|
||||
path_separators.cbegin(), path_separators.cend(), ch);
|
||||
}
|
||||
|
||||
str str_view_concat(str_view v1, str_view v2) {
|
||||
str result;
|
||||
result.reserve(v1.size() + v2.size());
|
||||
result.append(v1.cbegin(), v1.cend());
|
||||
result.append(v2.cbegin(), v2.cend());
|
||||
return result;
|
||||
}
|
||||
|
||||
str str_view_concat(str_view v1, str_view v2, str_view v3) {
|
||||
str result;
|
||||
result.reserve(v1.size() + v2.size() + v3.size());
|
||||
result.append(v1.cbegin(), v1.cend());
|
||||
result.append(v2.cbegin(), v2.cend());
|
||||
result.append(v3.cbegin(), v3.cend());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
read_file_uptr make_read_file(str_view path) noexcept {
|
||||
return impl::make_read_file(path);
|
||||
}
|
||||
|
||||
write_file_uptr make_write_file(str_view path, bool append) noexcept {
|
||||
return impl::make_write_file(path, append);
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace path
|
||||
{
|
||||
str combine(str_view lhs, str_view rhs) {
|
||||
if ( lhs.empty() || is_absolute(rhs) ) {
|
||||
return rhs;
|
||||
} else if ( rhs.empty() ) {
|
||||
return lhs;
|
||||
}
|
||||
return is_directory_separator(lhs.back())
|
||||
? str_view_concat(lhs, rhs)
|
||||
: str_view_concat(lhs, "/", rhs);
|
||||
}
|
||||
|
||||
str remove_filename(str_view path) {
|
||||
const str name = filename(path);
|
||||
return name.empty()
|
||||
? path
|
||||
: path.substr(0, path.length() - name.length());
|
||||
}
|
||||
|
||||
str remove_extension(str_view path) {
|
||||
const str ext = extension(path);
|
||||
return ext.empty()
|
||||
? path
|
||||
: path.substr(0, path.length() - ext.length());
|
||||
}
|
||||
|
||||
str replace_filename(str_view path, str_view filename) {
|
||||
const str without_name = remove_filename(path);
|
||||
return str_view_concat(without_name, filename);
|
||||
}
|
||||
|
||||
str replace_extension(str_view path, str_view extension) {
|
||||
const str without_ext = remove_extension(path);
|
||||
return extension.empty()
|
||||
? without_ext
|
||||
: (extension.front() == '.'
|
||||
? str_view_concat(without_ext, extension)
|
||||
: str_view_concat(without_ext, dot, extension));
|
||||
}
|
||||
|
||||
str stem(str_view path) {
|
||||
const str name = filename(path);
|
||||
if ( name.empty() || name == dot || name == dot_dot ) {
|
||||
return name;
|
||||
}
|
||||
const str::size_type ext_pos = name.rfind('.');
|
||||
return ext_pos != str::npos
|
||||
? name.substr(0, ext_pos)
|
||||
: name;
|
||||
}
|
||||
|
||||
str filename(str_view path) {
|
||||
const auto sep_e = std::find_if(
|
||||
path.crbegin(), path.crend(), &is_directory_separator);
|
||||
const auto sep_d = std::distance(sep_e, path.crend());
|
||||
return path.substr(static_cast<std::size_t>(sep_d));
|
||||
}
|
||||
|
||||
str extension(str_view path) {
|
||||
const str name = filename(path);
|
||||
if ( name.empty() || name == dot || name == dot_dot ) {
|
||||
return str();
|
||||
}
|
||||
const str::size_type ext_pos = name.rfind('.');
|
||||
return ext_pos != str::npos
|
||||
? name.substr(ext_pos)
|
||||
: str();
|
||||
}
|
||||
|
||||
str parent_path(str_view path) {
|
||||
const auto sep_e = std::find_if(
|
||||
path.crbegin(), path.crend(), &is_directory_separator);
|
||||
if ( sep_e == path.crend() ) {
|
||||
return str();
|
||||
}
|
||||
const auto sep_b = std::find_if_not(
|
||||
sep_e, path.crend(), &is_directory_separator);
|
||||
if ( sep_b == path.crend() ) {
|
||||
return str();
|
||||
}
|
||||
const auto sep_d = std::distance(sep_b, path.crend());
|
||||
return path.substr(0, static_cast<std::size_t>(sep_d));
|
||||
}
|
||||
|
||||
bool is_absolute(str_view path) noexcept {
|
||||
return
|
||||
(path.size() >= 1 && (path[0] == '/' || path[0] == '\\')) ||
|
||||
(path.size() >= 2 && path[1] == ':');
|
||||
}
|
||||
|
||||
bool is_relative(str_view path) noexcept {
|
||||
return !is_absolute(path);
|
||||
}
|
||||
}}
|
||||
|
||||
namespace e2d { namespace filesystem
|
||||
{
|
||||
bool remove(str_view path) {
|
||||
return impl::remove_file(path)
|
||||
|| impl::remove_directory(path);
|
||||
}
|
||||
|
||||
bool exists(str_view path) {
|
||||
return impl::file_exists(path)
|
||||
|| impl::directory_exists(path);
|
||||
}
|
||||
|
||||
bool remove_file(str_view path) {
|
||||
return impl::remove_file(path);
|
||||
}
|
||||
|
||||
bool remove_directory(str_view path) {
|
||||
return impl::remove_directory(path);
|
||||
}
|
||||
|
||||
bool file_exists(str_view path) {
|
||||
return impl::file_exists(path);
|
||||
}
|
||||
|
||||
bool directory_exists(str_view path) {
|
||||
return impl::directory_exists(path);
|
||||
}
|
||||
|
||||
bool create_file(str_view path) {
|
||||
return create_directory_recursive(path::parent_path(path))
|
||||
&& make_write_file(path, true);
|
||||
}
|
||||
|
||||
bool create_directory(str_view path) {
|
||||
return impl::create_directory(path);
|
||||
}
|
||||
|
||||
bool create_directory_recursive(str_view path) {
|
||||
if ( path.empty() || directory_exists(path) ) {
|
||||
return true;
|
||||
} else if ( !create_directory(path::parent_path(path)) ) {
|
||||
return false;
|
||||
} else {
|
||||
return impl::create_directory(path);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_read_all(buffer& dst, str_view path) noexcept {
|
||||
return streams::try_read_tail(
|
||||
dst, make_read_file(path));
|
||||
}
|
||||
|
||||
bool try_write_all(const buffer& src, str_view path, bool append) noexcept {
|
||||
return streams::try_write_tail(
|
||||
src, make_write_file(path, append));
|
||||
}
|
||||
}}
|
||||
198
sources/enduro2d/utils/filesystem_impl/files_posix.cpp
Normal file
198
sources/enduro2d/utils/filesystem_impl/files_posix.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "filesystem_impl.hpp"
|
||||
|
||||
#if defined(E2D_FILESYSTEM_MODE) && E2D_FILESYSTEM_MODE == E2D_FILESYSTEM_MODE_POSIX
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
class read_file_posix final : public read_file {
|
||||
public:
|
||||
read_file_posix(str path)
|
||||
: path_(std::move(path))
|
||||
{
|
||||
if ( !open_() ) {
|
||||
throw bad_stream_operation();
|
||||
}
|
||||
}
|
||||
|
||||
~read_file_posix() noexcept final {
|
||||
close_();
|
||||
}
|
||||
public:
|
||||
std::size_t read(void* dst, std::size_t size) final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const std::ptrdiff_t rread = ::read(handle_, dst, size);
|
||||
return rread >= 0
|
||||
? math::numeric_cast<std::size_t>(rread)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t seek(std::ptrdiff_t offset, bool relative) final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const std::ptrdiff_t rlseek = ::lseek(handle_, offset, relative ? SEEK_CUR : SEEK_SET);
|
||||
return rlseek >= 0
|
||||
? math::numeric_cast<std::size_t>(rlseek)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t tell() const final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const std::ptrdiff_t rlseek = ::lseek(handle_, 0, SEEK_CUR);
|
||||
return rlseek >= 0
|
||||
? math::numeric_cast<std::size_t>(rlseek)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t length() const final {
|
||||
return length_;
|
||||
}
|
||||
|
||||
const str& path() const noexcept final {
|
||||
return path_;
|
||||
}
|
||||
private:
|
||||
bool open_() noexcept {
|
||||
E2D_ASSERT(!is_opened_());
|
||||
handle_ = ::open(path_.c_str(), O_RDONLY);
|
||||
if ( handle_ < 0 ) {
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
if ( ::lseek(handle_, 0, SEEK_END) < 0 ) {
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
const std::ptrdiff_t rlseek = ::lseek(handle_, 0, SEEK_CUR);
|
||||
if ( rlseek < 0 ) {
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
length_ = math::numeric_cast<std::size_t>(rlseek);
|
||||
if ( ::lseek(handle_, 0, SEEK_SET) < 0 ) {
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void close_() noexcept {
|
||||
if ( handle_ >= 0 ) {
|
||||
::close(handle_);
|
||||
handle_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_opened_() const noexcept {
|
||||
return handle_ >= 0;
|
||||
}
|
||||
private:
|
||||
str path_;
|
||||
std::size_t length_ = 0;
|
||||
int handle_ = -1;
|
||||
};
|
||||
|
||||
class write_file_posix : public write_file {
|
||||
public:
|
||||
write_file_posix(str path, bool append)
|
||||
: path_(std::move(path))
|
||||
{
|
||||
if ( !open_(append) ) {
|
||||
throw bad_stream_operation();
|
||||
}
|
||||
}
|
||||
|
||||
~write_file_posix() noexcept final {
|
||||
close_();
|
||||
}
|
||||
public:
|
||||
std::size_t write(const void* src, std::size_t size) final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const std::ptrdiff_t rwrite = ::write(handle_, src, size);
|
||||
return rwrite >= 0
|
||||
? math::numeric_cast<std::size_t>(rwrite)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t seek(std::ptrdiff_t offset, bool relative) final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const std::ptrdiff_t rlseek = ::lseek(handle_, offset, relative ? SEEK_CUR : SEEK_SET);
|
||||
return rlseek >= 0
|
||||
? math::numeric_cast<std::size_t>(rlseek)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t tell() const final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const std::ptrdiff_t rlseek = ::lseek(handle_, 0, SEEK_CUR);
|
||||
return rlseek >= 0
|
||||
? math::numeric_cast<std::size_t>(rlseek)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
const str& path() const noexcept final {
|
||||
return path_;
|
||||
}
|
||||
private:
|
||||
bool open_(bool append) noexcept {
|
||||
E2D_ASSERT(!is_opened_());
|
||||
handle_ = ::open(
|
||||
path_.c_str(),
|
||||
append ? (O_WRONLY|O_CREAT) : (O_WRONLY|O_CREAT|O_TRUNC),
|
||||
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
||||
if ( handle_ < 0 ) {
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
if ( ::lseek(handle_, 0, SEEK_END) < 0 ) {
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void close_() noexcept {
|
||||
if ( handle_ >= 0 ) {
|
||||
::close(handle_);
|
||||
handle_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_opened_() const noexcept {
|
||||
return handle_ >= 0;
|
||||
}
|
||||
private:
|
||||
str path_;
|
||||
int handle_ = -1;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d { namespace impl
|
||||
{
|
||||
read_file_uptr make_read_file(str_view path) noexcept {
|
||||
try {
|
||||
return std::make_unique<read_file_posix>(path);
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
write_file_uptr make_write_file(str_view path, bool append) noexcept {
|
||||
try {
|
||||
return std::make_unique<write_file_posix>(path, append);
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
#endif
|
||||
237
sources/enduro2d/utils/filesystem_impl/files_winapi.cpp
Normal file
237
sources/enduro2d/utils/filesystem_impl/files_winapi.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "filesystem_impl.hpp"
|
||||
|
||||
#if defined(E2D_FILESYSTEM_MODE) && E2D_FILESYSTEM_MODE == E2D_FILESYSTEM_MODE_WINAPI
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
class read_file_winapi : public read_file {
|
||||
public:
|
||||
read_file_winapi(str path)
|
||||
: path_(std::move(path))
|
||||
{
|
||||
if ( !open_() ) {
|
||||
throw bad_stream_operation();
|
||||
}
|
||||
}
|
||||
|
||||
~read_file_winapi() noexcept final {
|
||||
close_();
|
||||
}
|
||||
public:
|
||||
std::size_t read(void* dst, std::size_t size) final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
DWORD read_bytes = 0;
|
||||
const BOOL rread = ::ReadFile(
|
||||
handle_,
|
||||
dst,
|
||||
math::numeric_cast<DWORD>(size),
|
||||
&read_bytes,
|
||||
NULL);
|
||||
return TRUE == rread
|
||||
? math::numeric_cast<std::size_t>(read_bytes)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t seek(std::ptrdiff_t offset, bool relative) final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const DWORD rseek = ::SetFilePointer(
|
||||
handle_,
|
||||
math::numeric_cast<LONG>(offset),
|
||||
NULL,
|
||||
relative ? FILE_CURRENT : FILE_BEGIN);
|
||||
return rseek != INVALID_SET_FILE_POINTER
|
||||
? math::numeric_cast<std::size_t>(rseek)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t tell() const final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const DWORD rseek = ::SetFilePointer(handle_, 0, NULL, FILE_CURRENT);
|
||||
return rseek != INVALID_SET_FILE_POINTER
|
||||
? math::numeric_cast<std::size_t>(rseek)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t length() const final {
|
||||
return length_;
|
||||
}
|
||||
|
||||
const str& path() const noexcept final {
|
||||
return path_;
|
||||
}
|
||||
private:
|
||||
bool open_() {
|
||||
E2D_ASSERT(!is_opened_());
|
||||
const wstr wide_path = make_wide(path_);
|
||||
handle_ = ::CreateFileW(
|
||||
wide_path.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_READONLY,
|
||||
NULL);
|
||||
if ( INVALID_HANDLE_VALUE == handle_ ) {
|
||||
handle_ = ::CreateFileW(
|
||||
wide_path.c_str(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_READONLY,
|
||||
NULL);
|
||||
}
|
||||
if ( INVALID_HANDLE_VALUE == handle_ ) {
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
const DWORD file_size = ::GetFileSize(handle_, NULL);
|
||||
if ( INVALID_FILE_SIZE == file_size ) {
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
length_ = math::numeric_cast<std::size_t>(file_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void close_() noexcept {
|
||||
if ( INVALID_HANDLE_VALUE != handle_ ) {
|
||||
::CloseHandle(handle_);
|
||||
handle_ = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_opened_() const noexcept {
|
||||
return INVALID_HANDLE_VALUE != handle_;
|
||||
}
|
||||
private:
|
||||
str path_;
|
||||
std::size_t length_ = 0;
|
||||
HANDLE handle_ = INVALID_HANDLE_VALUE;
|
||||
};
|
||||
|
||||
class write_file_winapi : public write_file {
|
||||
public:
|
||||
write_file_winapi(str path, bool append)
|
||||
: path_(std::move(path))
|
||||
{
|
||||
if ( !open_(append) ) {
|
||||
throw bad_stream_operation();
|
||||
}
|
||||
}
|
||||
|
||||
~write_file_winapi() noexcept final {
|
||||
close_();
|
||||
}
|
||||
public:
|
||||
std::size_t write(const void* src, std::size_t size) final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
DWORD write_bytes = 0;
|
||||
const BOOL rwrite = ::WriteFile(
|
||||
handle_,
|
||||
src,
|
||||
math::numeric_cast<DWORD>(size),
|
||||
&write_bytes,
|
||||
NULL);
|
||||
return TRUE == rwrite
|
||||
? math::numeric_cast<std::size_t>(write_bytes)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t seek(std::ptrdiff_t offset, bool relative) final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const DWORD rseek = ::SetFilePointer(
|
||||
handle_,
|
||||
math::numeric_cast<LONG>(offset),
|
||||
NULL,
|
||||
relative ? FILE_CURRENT : FILE_BEGIN);
|
||||
return rseek != INVALID_SET_FILE_POINTER
|
||||
? math::numeric_cast<std::size_t>(rseek)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
std::size_t tell() const final {
|
||||
E2D_ASSERT(is_opened_());
|
||||
const DWORD rseek = ::SetFilePointer(handle_, 0, NULL, FILE_CURRENT);
|
||||
return rseek != INVALID_SET_FILE_POINTER
|
||||
? math::numeric_cast<std::size_t>(rseek)
|
||||
: throw bad_stream_operation();
|
||||
}
|
||||
|
||||
const str& path() const noexcept final {
|
||||
return path_;
|
||||
}
|
||||
private:
|
||||
bool open_(bool append) {
|
||||
E2D_ASSERT(!is_opened_());
|
||||
const wstr wide_path = make_wide(path_);
|
||||
handle_ = ::CreateFileW(
|
||||
wide_path.c_str(),
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
append ? OPEN_ALWAYS : CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if ( INVALID_HANDLE_VALUE == handle_ ) {
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
if ( append && INVALID_SET_FILE_POINTER == ::SetFilePointer(
|
||||
handle_,
|
||||
0,
|
||||
NULL,
|
||||
FILE_END) )
|
||||
{
|
||||
close_();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void close_() noexcept {
|
||||
if ( INVALID_HANDLE_VALUE != handle_ ) {
|
||||
::CloseHandle(handle_);
|
||||
handle_ = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_opened_() const noexcept {
|
||||
return INVALID_HANDLE_VALUE != handle_;
|
||||
}
|
||||
private:
|
||||
str path_;
|
||||
HANDLE handle_ = INVALID_HANDLE_VALUE;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d { namespace impl
|
||||
{
|
||||
read_file_uptr make_read_file(str_view path) noexcept {
|
||||
try {
|
||||
return std::make_unique<read_file_winapi>(path);
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
write_file_uptr make_write_file(str_view path, bool append) noexcept {
|
||||
try {
|
||||
return std::make_unique<write_file_winapi>(path, append);
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
#endif
|
||||
44
sources/enduro2d/utils/filesystem_impl/filesystem_impl.hpp
Normal file
44
sources/enduro2d/utils/filesystem_impl/filesystem_impl.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <enduro2d/utils/strings.hpp>
|
||||
#include <enduro2d/utils/filesystem.hpp>
|
||||
|
||||
#define E2D_FILESYSTEM_MODE_POSIX 1
|
||||
#define E2D_FILESYSTEM_MODE_WINAPI 2
|
||||
|
||||
#ifndef E2D_FILESYSTEM_MODE
|
||||
# if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_ANDROID
|
||||
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_POSIX
|
||||
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_IOS
|
||||
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_POSIX
|
||||
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_LINUX
|
||||
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_POSIX
|
||||
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_MACOSX
|
||||
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_POSIX
|
||||
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_WINDOWS
|
||||
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_WINAPI
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace e2d { namespace impl
|
||||
{
|
||||
read_file_uptr make_read_file(str_view path) noexcept;
|
||||
write_file_uptr make_write_file(str_view path, bool append) noexcept;
|
||||
}}
|
||||
|
||||
namespace e2d { namespace filesystem { namespace impl
|
||||
{
|
||||
bool remove_file(str_view path);
|
||||
bool remove_directory(str_view path);
|
||||
|
||||
bool file_exists(str_view path);
|
||||
bool directory_exists(str_view path);
|
||||
|
||||
bool create_directory(str_view path);
|
||||
}}}
|
||||
55
sources/enduro2d/utils/filesystem_impl/filesystem_posix.cpp
Normal file
55
sources/enduro2d/utils/filesystem_impl/filesystem_posix.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "filesystem_impl.hpp"
|
||||
|
||||
#if defined(E2D_FILESYSTEM_MODE) && E2D_FILESYSTEM_MODE == E2D_FILESYSTEM_MODE_POSIX
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
mode_t default_directory_mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
|
||||
}
|
||||
|
||||
namespace e2d { namespace filesystem { namespace impl
|
||||
{
|
||||
bool remove_file(str_view path) {
|
||||
return
|
||||
0 == ::unlink(make_utf8(path).c_str())
|
||||
|| errno == ENOENT;
|
||||
}
|
||||
|
||||
bool remove_directory(str_view path) {
|
||||
return
|
||||
0 == ::rmdir(make_utf8(path).c_str())
|
||||
|| errno == ENOENT;
|
||||
}
|
||||
|
||||
bool file_exists(str_view path) {
|
||||
struct stat st;
|
||||
return
|
||||
0 == ::stat(make_utf8(path).c_str(), &st)
|
||||
&& S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
bool directory_exists(str_view path) {
|
||||
struct stat st;
|
||||
return
|
||||
0 == ::stat(make_utf8(path).c_str(), &st)
|
||||
&& S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
bool create_directory(str_view path) {
|
||||
return
|
||||
0 == ::mkdir(make_utf8(path).c_str(), default_directory_mode)
|
||||
|| errno == EEXIST;
|
||||
}
|
||||
}}}
|
||||
|
||||
#endif
|
||||
55
sources/enduro2d/utils/filesystem_impl/filesystem_winapi.cpp
Normal file
55
sources/enduro2d/utils/filesystem_impl/filesystem_winapi.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "enduro2d"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "filesystem_impl.hpp"
|
||||
|
||||
#if defined(E2D_FILESYSTEM_MODE) && E2D_FILESYSTEM_MODE == E2D_FILESYSTEM_MODE_WINAPI
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace e2d { namespace filesystem { namespace impl
|
||||
{
|
||||
bool remove_file(str_view path) {
|
||||
const wstr wide_path = make_wide(path);
|
||||
return
|
||||
::DeleteFileW(wide_path.c_str())
|
||||
|| ::GetLastError() == ERROR_FILE_NOT_FOUND
|
||||
|| ::GetLastError() == ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool remove_directory(str_view path) {
|
||||
const wstr wide_path = make_wide(path);
|
||||
return
|
||||
::RemoveDirectoryW(wide_path.c_str())
|
||||
|| ::GetLastError() == ERROR_FILE_NOT_FOUND
|
||||
|| ::GetLastError() == ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool file_exists(str_view path) {
|
||||
const wstr wide_path = make_wide(path);
|
||||
DWORD attributes = ::GetFileAttributesW(wide_path.c_str());
|
||||
return
|
||||
attributes != INVALID_FILE_ATTRIBUTES &&
|
||||
!(attributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
bool directory_exists(str_view path) {
|
||||
const wstr wide_path = make_wide(path);
|
||||
DWORD attributes = ::GetFileAttributesW(wide_path.c_str());
|
||||
return
|
||||
attributes != INVALID_FILE_ATTRIBUTES &&
|
||||
(attributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
bool create_directory(str_view path) {
|
||||
const wstr wide_path = make_wide(path);
|
||||
return
|
||||
::CreateDirectoryW(wide_path.c_str(), nullptr) ||
|
||||
::GetLastError() == ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
}}}
|
||||
|
||||
#endif
|
||||
323
sources/enduro2d/utils/image.cpp
Normal file
323
sources/enduro2d/utils/image.cpp
Normal file
@@ -0,0 +1,323 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "image_impl/image_impl.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
struct data_format_description {
|
||||
u32 minimal_size;
|
||||
u32 bits_per_pixel;
|
||||
image_data_format format;
|
||||
bool compressed;
|
||||
bool must_be_square;
|
||||
bool must_be_power_of_two;
|
||||
};
|
||||
|
||||
const data_format_description data_format_descriptions[] = {
|
||||
{1, 8, image_data_format::g8, false, false, false},
|
||||
{1, 16, image_data_format::ga8, false, false, false},
|
||||
{1, 24, image_data_format::rgb8, false, false, false},
|
||||
{1, 32, image_data_format::rgba8, false, false, false},
|
||||
|
||||
{4, 4, image_data_format::dxt1, true, false, true},
|
||||
{4, 8, image_data_format::dxt3, true, false, true},
|
||||
{4, 8, image_data_format::dxt5, true, false, true},
|
||||
|
||||
{8, 2, image_data_format::rgb_pvrtc2, true, true, true},
|
||||
{8, 4, image_data_format::rgb_pvrtc4, true, true, true},
|
||||
{8, 2, image_data_format::rgba_pvrtc2, true, true, true},
|
||||
{8, 4, image_data_format::rgba_pvrtc4, true, true, true},
|
||||
|
||||
{0, 0, image_data_format::unknown, false, false, false}
|
||||
};
|
||||
|
||||
const data_format_description& get_data_format_description(image_data_format format) noexcept {
|
||||
const std::size_t findex = static_cast<std::underlying_type_t<image_data_format>>(format);
|
||||
E2D_ASSERT(findex < E2D_COUNTOF(data_format_descriptions));
|
||||
const data_format_description& fdesc = data_format_descriptions[findex];
|
||||
E2D_ASSERT(fdesc.format == format);
|
||||
return fdesc;
|
||||
}
|
||||
|
||||
v2u adjust_image_size(const v2u& size, image_data_format format) noexcept {
|
||||
const data_format_description& format_desc = get_data_format_description(format);
|
||||
v2u nsize = math::maximized(size, v2u(format_desc.minimal_size));
|
||||
if ( format_desc.must_be_square ) {
|
||||
nsize = v2u(math::maximum(nsize));
|
||||
}
|
||||
if ( format_desc.must_be_power_of_two ) {
|
||||
nsize = v2u(math::next_power_of_2(nsize.x),
|
||||
math::next_power_of_2(nsize.y));
|
||||
}
|
||||
return nsize;
|
||||
}
|
||||
|
||||
std::size_t buffer_size_for_image(const v2u& size, image_data_format format) noexcept {
|
||||
const v2u nsize = adjust_image_size(size, format);
|
||||
const std::size_t bpp = get_data_format_description(format).bits_per_pixel;
|
||||
const std::size_t bits = nsize.x * nsize.y * bpp;
|
||||
E2D_ASSERT(bits % 8 == 0);
|
||||
return bits / 8;
|
||||
}
|
||||
|
||||
bool check_image_format(const v2u& size, image_data_format format) noexcept {
|
||||
return size == adjust_image_size(size, format)
|
||||
&& (format != image_data_format::unknown || size == v2u::zero());
|
||||
}
|
||||
|
||||
bool check_image_format(const v2u& size, image_data_format format, const buffer& data) noexcept {
|
||||
return check_image_format(size, format)
|
||||
&& data.size() == buffer_size_for_image(size, format);
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
image::image(image&& other) noexcept {
|
||||
assign(std::move(other));
|
||||
}
|
||||
|
||||
image& image::operator=(image&& other) noexcept {
|
||||
return assign(std::move(other));
|
||||
}
|
||||
|
||||
image::image(const image& other) {
|
||||
assign(other);
|
||||
}
|
||||
|
||||
image& image::operator=(const image& other) {
|
||||
return assign(other);
|
||||
}
|
||||
|
||||
image::image(const v2u& size, image_data_format format) {
|
||||
assign(size, format);
|
||||
}
|
||||
|
||||
image::image(const v2u& size, image_data_format format, buffer&& data) {
|
||||
assign(size, format, std::move(data));
|
||||
}
|
||||
|
||||
image::image(const v2u& size, image_data_format format, const buffer& data) {
|
||||
assign(size, format, data);
|
||||
}
|
||||
|
||||
image& image::assign(image&& other) noexcept {
|
||||
if ( this != &other ) {
|
||||
swap(other);
|
||||
other.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
image& image::assign(const image& other) {
|
||||
if ( this != &other ) {
|
||||
data_.assign(other.data_);
|
||||
size_ = other.size_;
|
||||
format_ = other.format_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
image& image::assign(const v2u& size, image_data_format format) {
|
||||
if ( !check_image_format(size, format) ) {
|
||||
throw bad_image_data_format();
|
||||
}
|
||||
data_.resize(buffer_size_for_image(size, format));
|
||||
size_ = size;
|
||||
format_ = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
image& image::assign(const v2u& size, image_data_format format, buffer&& data) {
|
||||
if ( !check_image_format(size, format, data) ) {
|
||||
throw bad_image_data_format();
|
||||
}
|
||||
data_.assign(std::move(data));
|
||||
size_ = size;
|
||||
format_ = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
image& image::assign(const v2u& size, image_data_format format, const buffer& data) {
|
||||
if ( !check_image_format(size, format, data) ) {
|
||||
throw bad_image_data_format();
|
||||
}
|
||||
data_.assign(data);
|
||||
size_ = size;
|
||||
format_ = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void image::swap(image& other) noexcept {
|
||||
using std::swap;
|
||||
swap(data_, other.data_);
|
||||
swap(size_, other.size_);
|
||||
swap(format_, other.format_);
|
||||
}
|
||||
|
||||
void image::clear() noexcept {
|
||||
data_.clear();
|
||||
size_ = v2u::zero();
|
||||
format_ = image_data_format::unknown;
|
||||
}
|
||||
|
||||
bool image::empty() const noexcept {
|
||||
return data_.empty();
|
||||
}
|
||||
|
||||
color image::pixel(u32 u, u32 v) const {
|
||||
return color(pixel32(u, v));
|
||||
}
|
||||
|
||||
color image::pixel(const v2u& uv) const {
|
||||
return color(pixel32(uv));
|
||||
}
|
||||
|
||||
color32 image::pixel32(u32 u, u32 v) const {
|
||||
const data_format_description& format_desc =
|
||||
get_data_format_description(format_);
|
||||
|
||||
if ( empty() || u >= size_.x || v >= size_.y ) {
|
||||
throw bad_image_access();
|
||||
} else if ( format_desc.format == image_data_format::unknown || format_desc.compressed ) {
|
||||
throw bad_image_access();
|
||||
}
|
||||
|
||||
const std::size_t bits_per_pixel = format_desc.bits_per_pixel;
|
||||
const std::size_t bytes_per_pixel = bits_per_pixel / 8;
|
||||
const std::size_t stride_in_bytes = size_.x * bytes_per_pixel;
|
||||
E2D_ASSERT(bits_per_pixel % 8 == 0);
|
||||
|
||||
const std::size_t pixel_index = v * stride_in_bytes + u * bytes_per_pixel;
|
||||
E2D_ASSERT(pixel_index + bytes_per_pixel <= data_.size());
|
||||
const u8* const pixel = data_.data() + pixel_index;
|
||||
|
||||
switch ( format_ ) {
|
||||
case image_data_format::g8:
|
||||
E2D_ASSERT(bytes_per_pixel == 1);
|
||||
return color32(pixel[0],
|
||||
pixel[0],
|
||||
pixel[0]);
|
||||
case image_data_format::ga8:
|
||||
E2D_ASSERT(bytes_per_pixel == 2);
|
||||
return color32(pixel[0],
|
||||
pixel[0],
|
||||
pixel[0],
|
||||
pixel[1]);
|
||||
case image_data_format::rgb8:
|
||||
E2D_ASSERT(bytes_per_pixel == 3);
|
||||
return color32(pixel[0],
|
||||
pixel[1],
|
||||
pixel[2]);
|
||||
case image_data_format::rgba8:
|
||||
E2D_ASSERT(bytes_per_pixel == 4);
|
||||
return color32(pixel[0],
|
||||
pixel[1],
|
||||
pixel[2],
|
||||
pixel[3]);
|
||||
default:
|
||||
E2D_ASSERT_MSG(false, "unexpected image data format");
|
||||
throw bad_image_access();
|
||||
}
|
||||
}
|
||||
|
||||
color32 image::pixel32(const v2u& uv) const {
|
||||
return pixel32(uv.x, uv.y);
|
||||
}
|
||||
|
||||
const v2u& image::size() const noexcept {
|
||||
return size_;
|
||||
}
|
||||
|
||||
image_data_format image::format() const noexcept {
|
||||
return format_;
|
||||
}
|
||||
|
||||
const buffer& image::data() const noexcept {
|
||||
return data_;
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
void swap(image& l, image& r) noexcept {
|
||||
l.swap(r);
|
||||
}
|
||||
|
||||
bool operator<(const image& l, const image& r) noexcept {
|
||||
return l.format() < r.format()
|
||||
|| (l.format() == r.format() && l.size() < r.size())
|
||||
|| (l.format() == r.format() && l.size() == r.size() && l.data() < r.data());
|
||||
}
|
||||
|
||||
bool operator==(const image& l, const image& r) noexcept {
|
||||
return l.format() == r.format()
|
||||
&& l.size() == r.size()
|
||||
&& l.data() == r.data();
|
||||
}
|
||||
|
||||
bool operator!=(const image& l, const image& r) noexcept {
|
||||
return !(l == r);
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace images
|
||||
{
|
||||
bool try_load_image(
|
||||
image& dst,
|
||||
const buffer& src) noexcept
|
||||
{
|
||||
return impl::try_load_image_dds(dst, src)
|
||||
|| impl::try_load_image_pvr(dst, src)
|
||||
|| impl::try_load_image_stb(dst, src);
|
||||
}
|
||||
|
||||
bool try_load_image(
|
||||
image& dst,
|
||||
const input_stream_uptr& src) noexcept
|
||||
{
|
||||
buffer file_data;
|
||||
return streams::try_read_tail(file_data, src)
|
||||
&& try_load_image(dst, file_data);
|
||||
}
|
||||
|
||||
bool try_save_image(
|
||||
const image& src,
|
||||
image_file_format format,
|
||||
buffer& dst) noexcept
|
||||
{
|
||||
switch ( format ) {
|
||||
case image_file_format::dds:
|
||||
return impl::try_save_image_dds(src, dst);
|
||||
case image_file_format::jpg:
|
||||
return impl::try_save_image_jpg(src, dst);
|
||||
case image_file_format::png:
|
||||
return impl::try_save_image_png(src, dst);
|
||||
case image_file_format::pvr:
|
||||
return impl::try_save_image_pvr(src, dst);
|
||||
case image_file_format::tga:
|
||||
return impl::try_save_image_tga(src, dst);
|
||||
case image_file_format::unknown:
|
||||
return false;
|
||||
default:
|
||||
E2D_ASSERT_MSG(false, "unexpected image file format");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool try_save_image(
|
||||
const image& src,
|
||||
image_file_format format,
|
||||
const output_stream_uptr& dst) noexcept
|
||||
{
|
||||
buffer file_data;
|
||||
return try_save_image(src, format, file_data)
|
||||
&& streams::try_write_tail(file_data, dst);
|
||||
}
|
||||
}}
|
||||
23
sources/enduro2d/utils/image_impl/image_impl.hpp
Normal file
23
sources/enduro2d/utils/image_impl/image_impl.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <enduro2d/utils/image.hpp>
|
||||
#include <enduro2d/utils/buffer.hpp>
|
||||
|
||||
namespace e2d { namespace images { namespace impl
|
||||
{
|
||||
bool try_load_image_dds(image& dst, const buffer& src) noexcept;
|
||||
bool try_load_image_pvr(image& dst, const buffer& src) noexcept;
|
||||
bool try_load_image_stb(image& dst, const buffer& src) noexcept;
|
||||
|
||||
bool try_save_image_dds(const image& src, buffer& dst) noexcept;
|
||||
bool try_save_image_jpg(const image& src, buffer& dst) noexcept;
|
||||
bool try_save_image_png(const image& src, buffer& dst) noexcept;
|
||||
bool try_save_image_pvr(const image& src, buffer& dst) noexcept;
|
||||
bool try_save_image_tga(const image& src, buffer& dst) noexcept;
|
||||
}}}
|
||||
16
sources/enduro2d/utils/image_impl/image_reader_dds.cpp
Normal file
16
sources/enduro2d/utils/image_impl/image_reader_dds.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "image_impl.hpp"
|
||||
|
||||
namespace e2d { namespace images { namespace impl
|
||||
{
|
||||
bool try_load_image_dds(image& dst, const buffer& src) noexcept {
|
||||
//TODO: implme
|
||||
E2D_UNUSED(dst, src);
|
||||
return false;
|
||||
}
|
||||
}}}
|
||||
16
sources/enduro2d/utils/image_impl/image_reader_pvr.cpp
Normal file
16
sources/enduro2d/utils/image_impl/image_reader_pvr.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "image_impl.hpp"
|
||||
|
||||
namespace e2d { namespace images { namespace impl
|
||||
{
|
||||
bool try_load_image_pvr(image& dst, const buffer& src) noexcept {
|
||||
//TODO: implme
|
||||
E2D_UNUSED(dst, src);
|
||||
return false;
|
||||
}
|
||||
}}}
|
||||
87
sources/enduro2d/utils/image_impl/image_reader_stb.cpp
Normal file
87
sources/enduro2d/utils/image_impl/image_reader_stb.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "image_impl.hpp"
|
||||
|
||||
#define STBI_NO_STDIO
|
||||
#define STBI_NO_FAILURE_STRINGS
|
||||
|
||||
#define STBI_NO_HDR
|
||||
#define STBI_NO_LINEAR
|
||||
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_ONLY_TGA
|
||||
#define STBI_ONLY_JPEG
|
||||
|
||||
#define STBI_FREE(ptr) std::free(ptr)
|
||||
#define STBI_MALLOC(size) std::malloc(size)
|
||||
#define STBI_REALLOC(ptr,nsize) std::realloc(ptr, nsize)
|
||||
|
||||
#define STBI_ASSERT(expr) E2D_ASSERT(expr)
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <3rdparty/stb/stb_image.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
using stbi_img_uptr = std::unique_ptr<stbi_uc, decltype(&stbi_image_free)>;
|
||||
|
||||
stbi_img_uptr load_stb_image(const buffer& data, v2u& out_size, u32& out_channels) noexcept {
|
||||
int img_w = 0, img_h = 0, img_c = 0;
|
||||
stbi_uc* img = stbi_load_from_memory(
|
||||
static_cast<const stbi_uc*>(data.data()),
|
||||
math::numeric_cast<int>(data.size()),
|
||||
&img_w, &img_h, &img_c, 0);
|
||||
stbi_img_uptr img_ptr(img, stbi_image_free);
|
||||
if ( img_ptr && img_w > 0 && img_h > 0 && img_c >= 1 && img_c <= 4 ) {
|
||||
out_size = v2u(math::numeric_cast<u32>(img_w), math::numeric_cast<u32>(img_h));
|
||||
out_channels = math::numeric_cast<u32>(img_c);
|
||||
return img_ptr;
|
||||
}
|
||||
return stbi_img_uptr(nullptr, stbi_image_free);
|
||||
}
|
||||
|
||||
image_data_format image_format_from_stb_channels(u32 channels) noexcept {
|
||||
switch ( channels ) {
|
||||
case 1: return image_data_format::g8;
|
||||
case 2: return image_data_format::ga8;
|
||||
case 3: return image_data_format::rgb8;
|
||||
case 4: return image_data_format::rgba8;
|
||||
default: return image_data_format::unknown;
|
||||
}
|
||||
}
|
||||
|
||||
bool image_from_stb_description(
|
||||
image& dst, const stbi_img_uptr& img, const v2u& img_size, u32 img_channels) noexcept
|
||||
{
|
||||
try {
|
||||
const image_data_format img_format = image_format_from_stb_channels(img_channels);
|
||||
if ( img && img_size.x > 0 && img_size.y > 0 && img_format != image_data_format::unknown ) {
|
||||
buffer img_buffer(
|
||||
img.get(),
|
||||
math::numeric_cast<std::size_t>(img_size.x * img_size.y * img_channels));
|
||||
dst.assign(img_size, img_format, std::move(img_buffer));
|
||||
return true;
|
||||
}
|
||||
} catch (std::exception&) {
|
||||
// nothing
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace images { namespace impl
|
||||
{
|
||||
bool try_load_image_stb(image& dst, const buffer& src) noexcept {
|
||||
v2u img_size;
|
||||
u32 img_channels = 0;
|
||||
const stbi_img_uptr img_ptr = load_stb_image(src, img_size, img_channels);
|
||||
return img_ptr
|
||||
&& image_from_stb_description(dst, img_ptr, img_size, img_channels);
|
||||
}
|
||||
}}}
|
||||
39
sources/enduro2d/utils/image_impl/image_writer_dds.cpp
Normal file
39
sources/enduro2d/utils/image_impl/image_writer_dds.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "image_impl.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
bool is_save_image_dds_supported(image_data_format data_format) noexcept {
|
||||
switch ( data_format ) {
|
||||
case image_data_format::g8:
|
||||
case image_data_format::ga8:
|
||||
case image_data_format::rgb8:
|
||||
case image_data_format::rgba8:
|
||||
case image_data_format::dxt1:
|
||||
case image_data_format::dxt3:
|
||||
case image_data_format::dxt5:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace images { namespace impl
|
||||
{
|
||||
bool try_save_image_dds(const image& src, buffer& dst) noexcept {
|
||||
E2D_UNUSED(src, dst);
|
||||
if ( is_save_image_dds_supported(src.format()) ) {
|
||||
//TODO: implme
|
||||
E2D_ASSERT_MSG(false, "implme");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}}}
|
||||
40
sources/enduro2d/utils/image_impl/image_writer_pvr.cpp
Normal file
40
sources/enduro2d/utils/image_impl/image_writer_pvr.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "image_impl.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
bool is_save_image_pvr_supported(image_data_format data_format) noexcept {
|
||||
switch ( data_format ) {
|
||||
case image_data_format::g8:
|
||||
case image_data_format::ga8:
|
||||
case image_data_format::rgb8:
|
||||
case image_data_format::rgba8:
|
||||
case image_data_format::rgb_pvrtc2:
|
||||
case image_data_format::rgb_pvrtc4:
|
||||
case image_data_format::rgba_pvrtc2:
|
||||
case image_data_format::rgba_pvrtc4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace images { namespace impl
|
||||
{
|
||||
bool try_save_image_pvr(const image& src, buffer& dst) noexcept {
|
||||
E2D_UNUSED(src, dst);
|
||||
if ( is_save_image_pvr_supported(src.format()) ) {
|
||||
//TODO: implme
|
||||
E2D_ASSERT_MSG(false, "implme");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}}}
|
||||
99
sources/enduro2d/utils/image_impl/image_writer_stb.cpp
Normal file
99
sources/enduro2d/utils/image_impl/image_writer_stb.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "image_impl.hpp"
|
||||
|
||||
#define STBI_WRITE_NO_STDIO
|
||||
|
||||
#define STBIW_FREE(ptr) std::free(ptr)
|
||||
#define STBIW_MALLOC(size) std::malloc(size)
|
||||
#define STBIW_REALLOC(ptr,nsize) std::realloc(ptr, nsize)
|
||||
|
||||
#define STBIW_ASSERT(expr) E2D_ASSERT(expr)
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <3rdparty/stb/stb_image_write.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
int stb_channels_from_image_format(image_data_format format) noexcept {
|
||||
switch ( format ) {
|
||||
case image_data_format::g8: return 1;
|
||||
case image_data_format::ga8: return 2;
|
||||
case image_data_format::rgb8: return 3;
|
||||
case image_data_format::rgba8: return 4;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void stb_write_callback(void *context, void *data, int size) {
|
||||
E2D_ASSERT(context && data && size > 0);
|
||||
vector<u8>& ctx = *static_cast<vector<u8>*>(context);
|
||||
ctx.insert(
|
||||
ctx.cend(),
|
||||
reinterpret_cast<u8*>(data),
|
||||
reinterpret_cast<u8*>(data) + math::numeric_cast<std::size_t>(size));
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace images { namespace impl
|
||||
{
|
||||
bool try_save_image_jpg(const image& src, buffer& dst) noexcept {
|
||||
int img_w = math::numeric_cast<int>(src.size().x);
|
||||
int img_h = math::numeric_cast<int>(src.size().y);
|
||||
int img_c = stb_channels_from_image_format(src.format());
|
||||
try {
|
||||
vector<u8> data;
|
||||
if ( 0 != stbi_write_jpg_to_func(
|
||||
stb_write_callback, &data, img_w, img_h, img_c, src.data().data(), 80) )
|
||||
{
|
||||
dst.assign(data.data(), data.size());
|
||||
return true;
|
||||
}
|
||||
} catch (...) {
|
||||
// nothing
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_save_image_png(const image& src, buffer& dst) noexcept {
|
||||
int img_w = math::numeric_cast<int>(src.size().x);
|
||||
int img_h = math::numeric_cast<int>(src.size().y);
|
||||
int img_c = stb_channels_from_image_format(src.format());
|
||||
try {
|
||||
vector<u8> data;
|
||||
if ( 0 != stbi_write_png_to_func(
|
||||
stb_write_callback, &data, img_w, img_h, img_c, src.data().data(), img_w * img_c) )
|
||||
{
|
||||
dst.assign(data.data(), data.size());
|
||||
return true;
|
||||
}
|
||||
} catch (...) {
|
||||
// nothing
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_save_image_tga(const image& src, buffer& dst) noexcept {
|
||||
int img_w = math::numeric_cast<int>(src.size().x);
|
||||
int img_h = math::numeric_cast<int>(src.size().y);
|
||||
int img_c = stb_channels_from_image_format(src.format());
|
||||
try {
|
||||
vector<u8> data;
|
||||
if ( 0 != stbi_write_tga_to_func(
|
||||
stb_write_callback, &data, img_w, img_h, img_c, src.data().data()) )
|
||||
{
|
||||
dst.assign(data.data(), data.size());
|
||||
return true;
|
||||
}
|
||||
} catch (...) {
|
||||
// nothing
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}}}
|
||||
108
sources/enduro2d/utils/jobber.cpp
Normal file
108
sources/enduro2d/utils/jobber.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/utils/jobber.hpp>
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
jobber::jobber(u32 threads) {
|
||||
try {
|
||||
threads_.resize(math::max(1u, threads));
|
||||
for ( std::thread& thread : threads_ ) {
|
||||
thread = std::thread(&jobber::worker_main_, this);
|
||||
}
|
||||
} catch (...) {
|
||||
shutdown_();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
jobber::~jobber() noexcept {
|
||||
shutdown_();
|
||||
}
|
||||
|
||||
void jobber::pause() noexcept {
|
||||
paused_.store(true);
|
||||
}
|
||||
|
||||
void jobber::resume() noexcept {
|
||||
paused_.store(false);
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(tasks_mutex_);
|
||||
cond_var_.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
bool jobber::is_paused() const noexcept {
|
||||
return paused_;
|
||||
}
|
||||
|
||||
void jobber::wait_all() const noexcept {
|
||||
while ( active_task_count_ ) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void jobber::active_wait_all() noexcept {
|
||||
while ( active_task_count_ ) {
|
||||
std::unique_lock<std::mutex> lock(tasks_mutex_);
|
||||
process_task_(std::move(lock));
|
||||
}
|
||||
}
|
||||
|
||||
void jobber::push_task_(priority priority, task_ptr task) {
|
||||
tasks_.emplace_back(priority, std::move(task));
|
||||
std::push_heap(tasks_.begin(), tasks_.end());
|
||||
++active_task_count_;
|
||||
cond_var_.notify_one();
|
||||
}
|
||||
|
||||
jobber::task_ptr jobber::pop_task_() noexcept {
|
||||
if ( !tasks_.empty() ) {
|
||||
std::pop_heap(tasks_.begin(), tasks_.end());
|
||||
task_ptr task = std::move(tasks_.back().second);
|
||||
tasks_.pop_back();
|
||||
return task;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void jobber::shutdown_() noexcept {
|
||||
cancelled_.store(true);
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(tasks_mutex_);
|
||||
cond_var_.notify_all();
|
||||
}
|
||||
for ( std::thread& thread : threads_ ) {
|
||||
if ( thread.joinable() ) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void jobber::worker_main_() noexcept {
|
||||
for (;;) {
|
||||
std::unique_lock<std::mutex> lock(tasks_mutex_);
|
||||
cond_var_.wait(lock, [this](){
|
||||
return cancelled_ || (!paused_ && !tasks_.empty());
|
||||
});
|
||||
if ( cancelled_ ) {
|
||||
break;
|
||||
}
|
||||
process_task_(std::move(lock));
|
||||
}
|
||||
}
|
||||
|
||||
void jobber::process_task_(std::unique_lock<std::mutex> lock) noexcept {
|
||||
E2D_ASSERT(lock.owns_lock());
|
||||
task_ptr task = pop_task_();
|
||||
if ( task ) {
|
||||
lock.unlock();
|
||||
task->run();
|
||||
--active_task_count_;
|
||||
}
|
||||
}
|
||||
}
|
||||
103
sources/enduro2d/utils/streams.cpp
Normal file
103
sources/enduro2d/utils/streams.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/utils/streams.hpp>
|
||||
#include <enduro2d/utils/buffer.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
class memory_stream final : public input_stream {
|
||||
public:
|
||||
memory_stream(buffer data) noexcept
|
||||
: data_(std::move(data)) {}
|
||||
|
||||
std::size_t read(void* dst, std::size_t size) final {
|
||||
const std::size_t read_bytes = dst
|
||||
? math::min(size, data_.size() - pos_)
|
||||
: 0;
|
||||
if ( read_bytes > 0 ) {
|
||||
std::memcpy(dst, data_.data() + pos_, read_bytes);
|
||||
pos_ += read_bytes;
|
||||
}
|
||||
return read_bytes;
|
||||
}
|
||||
|
||||
std::size_t seek(std::ptrdiff_t offset, bool relative) final {
|
||||
if ( offset < 0 ) {
|
||||
const std::size_t uoffset = math::abs_to_unsigned(offset);
|
||||
if ( !relative || uoffset > pos_ ) {
|
||||
throw bad_stream_operation();
|
||||
} else {
|
||||
pos_ -= uoffset;
|
||||
return pos_;
|
||||
}
|
||||
} else {
|
||||
const std::size_t uoffset = math::abs_to_unsigned(offset);
|
||||
const std::size_t available_bytes = relative
|
||||
? data_.size() - pos_
|
||||
: data_.size();
|
||||
if ( uoffset > available_bytes ) {
|
||||
throw bad_stream_operation();
|
||||
} else {
|
||||
pos_ = uoffset + (data_.size() - available_bytes);
|
||||
return pos_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t tell() const final {
|
||||
return pos_;
|
||||
}
|
||||
|
||||
std::size_t length() const final {
|
||||
return data_.size();
|
||||
}
|
||||
private:
|
||||
buffer data_;
|
||||
std::size_t pos_ = 0;
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
input_stream_uptr make_memory_stream(buffer data) noexcept {
|
||||
try {
|
||||
return std::make_unique<memory_stream>(std::move(data));
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace streams
|
||||
{
|
||||
bool try_read_tail(buffer& dst, const input_stream_uptr& stream) noexcept {
|
||||
try {
|
||||
if ( stream ) {
|
||||
buffer tail(stream->length() - stream->tell());
|
||||
if ( tail.size() == stream->read(tail.data(), tail.size()) ) {
|
||||
dst.swap(tail);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// nothing
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool try_write_tail(const buffer& src, const output_stream_uptr& stream) noexcept {
|
||||
try {
|
||||
return
|
||||
stream &&
|
||||
src.size() == stream->write(src.data(), src.size());
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}}
|
||||
310
sources/enduro2d/utils/strings.cpp
Normal file
310
sources/enduro2d/utils/strings.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/utils/strings.hpp>
|
||||
|
||||
#include <3rdparty/utfcpp/utf8.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
//
|
||||
// utf8_to_X
|
||||
//
|
||||
|
||||
str16 utf8_to_16(str_view src) {
|
||||
str16 dst;
|
||||
dst.reserve(src.size());
|
||||
utf8::utf8to16(src.cbegin(), src.cend(), std::back_inserter(dst));
|
||||
dst.shrink_to_fit();
|
||||
return dst;
|
||||
}
|
||||
|
||||
str32 utf8_to_32(str_view src) {
|
||||
str32 dst;
|
||||
dst.reserve(src.size());
|
||||
utf8::utf8to32(src.cbegin(), src.cend(), std::back_inserter(dst));
|
||||
dst.shrink_to_fit();
|
||||
return dst;
|
||||
}
|
||||
|
||||
template < typename WChar = wchar_t >
|
||||
std::enable_if_t<sizeof(WChar) == 2, wstr>
|
||||
utf8_to_wide(str_view src) {
|
||||
wstr dst;
|
||||
dst.reserve(src.size());
|
||||
utf8::utf8to16(src.cbegin(), src.cend(), std::back_inserter(dst));
|
||||
dst.shrink_to_fit();
|
||||
return dst;
|
||||
}
|
||||
|
||||
template < typename WChar = wchar_t >
|
||||
std::enable_if_t<sizeof(WChar) == 4, wstr>
|
||||
utf8_to_wide(str_view src) {
|
||||
wstr dst;
|
||||
dst.reserve(src.size());
|
||||
utf8::utf8to32(src.cbegin(), src.cend(), std::back_inserter(dst));
|
||||
dst.shrink_to_fit();
|
||||
return dst;
|
||||
}
|
||||
|
||||
//
|
||||
// utf16_to_X
|
||||
//
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 2, str>
|
||||
utf16_to_8(basic_string_view<Char> src) {
|
||||
str dst;
|
||||
dst.reserve(src.size() * 2);
|
||||
utf8::utf16to8(src.cbegin(), src.cend(), std::back_inserter(dst));
|
||||
dst.shrink_to_fit();
|
||||
return dst;
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 2, str32>
|
||||
utf16_to_32(basic_string_view<Char> src) {
|
||||
return utf8_to_32(utf16_to_8(src));
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 2, wstr>
|
||||
utf16_to_wide(basic_string_view<Char> src) {
|
||||
return utf8_to_wide(utf16_to_8(src));
|
||||
}
|
||||
|
||||
//
|
||||
// utf32_to_X
|
||||
//
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 4, str>
|
||||
utf32_to_8(basic_string_view<Char> src) {
|
||||
str dst;
|
||||
dst.reserve(src.size() * 4);
|
||||
utf8::utf32to8(src.cbegin(), src.cend(), std::back_inserter(dst));
|
||||
dst.shrink_to_fit();
|
||||
return dst;
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 4, str16>
|
||||
utf32_to_16(basic_string_view<Char> src) {
|
||||
return utf8_to_16(utf32_to_8(src));
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 4, wstr>
|
||||
utf32_to_wide(basic_string_view<Char> src) {
|
||||
return utf8_to_wide(utf32_to_8(src));
|
||||
}
|
||||
|
||||
//
|
||||
// wide_to_X
|
||||
//
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 2, str>
|
||||
wide_to_utf8(basic_string_view<Char> src) {
|
||||
return utf16_to_8(src);
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 4, str>
|
||||
wide_to_utf8(basic_string_view<Char> src) {
|
||||
return utf32_to_8(src);
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 2, str16>
|
||||
wide_to_utf16(basic_string_view<Char> src) {
|
||||
return src.empty()
|
||||
? str16()
|
||||
: str16(src.cbegin(), src.cend());
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 4, str16>
|
||||
wide_to_utf16(basic_string_view<Char> src) {
|
||||
return utf32_to_16(src);
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 2, str32>
|
||||
wide_to_utf32(basic_string_view<Char> src) {
|
||||
return utf16_to_32(src);
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::enable_if_t<sizeof(Char) == 4, str32>
|
||||
wide_to_utf32(basic_string_view<Char> src) {
|
||||
return src.empty()
|
||||
? str32()
|
||||
: str32(src.cbegin(), src.cend());
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
//
|
||||
// make_utf8
|
||||
//
|
||||
|
||||
str make_utf8(str_view src) {
|
||||
return src.empty()
|
||||
? str()
|
||||
: str(src.cbegin(), src.cend());
|
||||
}
|
||||
|
||||
str make_utf8(wstr_view src) {
|
||||
return wide_to_utf8(src);
|
||||
}
|
||||
|
||||
str make_utf8(str16_view src) {
|
||||
return utf16_to_8(src);
|
||||
}
|
||||
|
||||
str make_utf8(str32_view src) {
|
||||
return utf32_to_8(src);
|
||||
}
|
||||
|
||||
//
|
||||
// make_wide
|
||||
//
|
||||
|
||||
wstr make_wide(str_view src) {
|
||||
return utf8_to_wide(src);
|
||||
}
|
||||
|
||||
wstr make_wide(wstr_view src) {
|
||||
return src.empty()
|
||||
? wstr()
|
||||
: wstr(src.cbegin(), src.cend());
|
||||
}
|
||||
|
||||
wstr make_wide(str16_view src) {
|
||||
return utf16_to_wide(src);
|
||||
}
|
||||
|
||||
wstr make_wide(str32_view src) {
|
||||
return utf32_to_wide(src);
|
||||
}
|
||||
|
||||
//
|
||||
// make_utf16
|
||||
//
|
||||
|
||||
str16 make_utf16(str_view src) {
|
||||
return utf8_to_16(src);
|
||||
}
|
||||
|
||||
str16 make_utf16(wstr_view src) {
|
||||
return wide_to_utf16(src);
|
||||
}
|
||||
|
||||
str16 make_utf16(str16_view src) {
|
||||
return src.empty()
|
||||
? str16()
|
||||
: str16(src.cbegin(), src.cend());
|
||||
}
|
||||
|
||||
str16 make_utf16(str32_view src) {
|
||||
return utf32_to_16(src);
|
||||
}
|
||||
|
||||
//
|
||||
// make_utf32
|
||||
//
|
||||
|
||||
str32 make_utf32(str_view src) {
|
||||
return utf8_to_32(src);
|
||||
}
|
||||
|
||||
str32 make_utf32(wstr_view src) {
|
||||
return wide_to_utf32(src);
|
||||
}
|
||||
|
||||
str32 make_utf32(str16_view src) {
|
||||
return utf16_to_32(src);
|
||||
}
|
||||
|
||||
str32 make_utf32(str32_view src) {
|
||||
return src.empty()
|
||||
? str32()
|
||||
: str32(src.cbegin(), src.cend());
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace strings
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
// Inspired by:
|
||||
// https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing
|
||||
|
||||
using utf8_iter = utf8::iterator<const char*>;
|
||||
|
||||
static bool wildcard_match_impl(
|
||||
utf8_iter string_i, utf8_iter string_e,
|
||||
utf8_iter pattern_i, utf8_iter pattern_e)
|
||||
{
|
||||
while ( pattern_i != pattern_e && *pattern_i != '*' ) {
|
||||
if ( string_i == string_e ) {
|
||||
break;
|
||||
}
|
||||
if ( *pattern_i != *string_i && *pattern_i != '?' ) {
|
||||
return false;
|
||||
}
|
||||
++string_i;
|
||||
++pattern_i;
|
||||
}
|
||||
if ( pattern_i == pattern_e ) {
|
||||
return string_i == string_e;
|
||||
}
|
||||
utf8_iter s_mark = string_e;
|
||||
utf8_iter p_mark = pattern_e;
|
||||
while ( string_i != string_e ) {
|
||||
if ( pattern_i != pattern_e ) {
|
||||
if ( *pattern_i == '*' ) {
|
||||
if ( ++pattern_i == pattern_e ) {
|
||||
return true;
|
||||
}
|
||||
s_mark = string_i;
|
||||
p_mark = pattern_i;
|
||||
++s_mark;
|
||||
continue;
|
||||
} else if ( *pattern_i == *string_i || *pattern_i == '?' ) {
|
||||
++string_i;
|
||||
++pattern_i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
string_i = s_mark;
|
||||
pattern_i = p_mark;
|
||||
if ( s_mark != string_e ) {
|
||||
++s_mark;
|
||||
}
|
||||
}
|
||||
while ( pattern_i != pattern_e && *pattern_i == '*' ) {
|
||||
++pattern_i;
|
||||
}
|
||||
return pattern_i == pattern_e;
|
||||
}
|
||||
}
|
||||
|
||||
bool wildcard_match(str_view string, str_view pattern) {
|
||||
using namespace impl;
|
||||
str_view::const_iterator si = string.cbegin();
|
||||
str_view::const_iterator se = string.cend();
|
||||
str_view::const_iterator pi = pattern.cbegin();
|
||||
str_view::const_iterator pe = pattern.cend();
|
||||
return wildcard_match_impl(
|
||||
utf8_iter(si, si, se), utf8_iter(se, si, se),
|
||||
utf8_iter(pi, pi, pe), utf8_iter(pe, pi, pe));
|
||||
}
|
||||
}}
|
||||
@@ -8,6 +8,7 @@ function(add_e2d_tests NAME)
|
||||
add_executable(${TESTS_NAME} ${TESTS_SOURCES})
|
||||
target_include_directories(${TESTS_NAME} PRIVATE "../headers")
|
||||
target_link_libraries(${TESTS_NAME} enduro2d)
|
||||
target_link_libraries(${TESTS_NAME} "${CMAKE_THREAD_LIBS_INIT}")
|
||||
add_test(${TESTS_NAME} ${TESTS_NAME})
|
||||
endfunction(add_e2d_tests)
|
||||
|
||||
|
||||
@@ -8,3 +8,32 @@
|
||||
|
||||
#include "../catch/catch.hpp"
|
||||
#include <enduro2d/enduro2d.hpp>
|
||||
|
||||
namespace e2d_untests
|
||||
{
|
||||
using namespace e2d;
|
||||
|
||||
template < typename TimeTag >
|
||||
class verbose_profiler : noncopyable {
|
||||
public:
|
||||
verbose_profiler(const str& desc)
|
||||
: desc_(desc) {
|
||||
begin_ = time::now<TimeTag>();
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
void done(const T& result) const {
|
||||
const auto end = time::now<TimeTag>();
|
||||
std::printf(
|
||||
"result: %s, time: %s, desc: %s\n",
|
||||
std::to_string(result).c_str(),
|
||||
std::to_string((end - begin_).value).c_str(),
|
||||
desc_.c_str());
|
||||
}
|
||||
private:
|
||||
str desc_;
|
||||
unit<i64, TimeTag> begin_;
|
||||
};
|
||||
using verbose_profiler_us = verbose_profiler<microseconds_tag>;
|
||||
using verbose_profiler_ms = verbose_profiler<milliseconds_tag>;
|
||||
}
|
||||
|
||||
171
untests/sources/untests_base/stdex.cpp
Normal file
171
untests/sources/untests_base/stdex.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_base.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("stdex") {
|
||||
{
|
||||
basic_string_view<char> v0;
|
||||
REQUIRE(v0.data() == nullptr);
|
||||
REQUIRE(v0.size() == 0);
|
||||
REQUIRE(v0.length() == 0);
|
||||
|
||||
const char* s = "hello";
|
||||
|
||||
basic_string_view<char> v1(s);
|
||||
REQUIRE(v1.data() == s);
|
||||
REQUIRE(v1.size() == 5);
|
||||
REQUIRE(v1.length() == 5);
|
||||
|
||||
basic_string_view<char> v2(s, 4);
|
||||
REQUIRE(v2.data() == s);
|
||||
REQUIRE(v2.size() == 4);
|
||||
REQUIRE(v2.length() == 4);
|
||||
}
|
||||
{
|
||||
const char* s0 = "hell";
|
||||
const char* s1 = "world";
|
||||
basic_string_view<char> v0(s0);
|
||||
basic_string_view<char> v1(s1);
|
||||
v0.swap(v1);
|
||||
REQUIRE(v0.data() == s1);
|
||||
REQUIRE(v0.size() == 5);
|
||||
REQUIRE(v1.data() == s0);
|
||||
REQUIRE(v1.size() == 4);
|
||||
}
|
||||
{
|
||||
const char* s = "hello";
|
||||
basic_string_view<char> v0(s);
|
||||
REQUIRE(v0.data() == s);
|
||||
REQUIRE(v0.size() == 5);
|
||||
REQUIRE(v0.length() == 5);
|
||||
basic_string_view<char> v1(v0);
|
||||
REQUIRE(v1.data() == s);
|
||||
REQUIRE(v1.size() == 5);
|
||||
REQUIRE(v1.length() == 5);
|
||||
basic_string_view<char> v2;
|
||||
v2 = v1;
|
||||
REQUIRE(v2.data() == s);
|
||||
REQUIRE(v2.size() == 5);
|
||||
REQUIRE(v2.length() == 5);
|
||||
}
|
||||
{
|
||||
const char* s = "hello";
|
||||
basic_string_view<char> v0(s);
|
||||
|
||||
REQUIRE(v0.begin() == s);
|
||||
REQUIRE(v0.cbegin() == s);
|
||||
REQUIRE(v0.rend().base() == s);
|
||||
REQUIRE(v0.crend().base() == s);
|
||||
|
||||
REQUIRE(v0.rbegin().base() == s + 5);
|
||||
REQUIRE(v0.crbegin().base() == s + 5);
|
||||
REQUIRE(v0.end() == s + 5);
|
||||
REQUIRE(v0.cend() == s + 5);
|
||||
}
|
||||
{
|
||||
basic_string_view<char> v0;
|
||||
REQUIRE(v0.empty());
|
||||
v0 = basic_string_view<char>("world");
|
||||
REQUIRE_FALSE(v0.empty());
|
||||
}
|
||||
{
|
||||
basic_string_view<char> v0("world");
|
||||
REQUIRE(v0[0] == 'w');
|
||||
REQUIRE(v0[4] == 'd');
|
||||
REQUIRE(v0.at(1) == 'o');
|
||||
REQUIRE(v0.at(3) == 'l');
|
||||
REQUIRE_THROWS_AS(v0.at(5), std::out_of_range);
|
||||
REQUIRE(v0.front() == 'w');
|
||||
REQUIRE(v0.back() == 'd');
|
||||
}
|
||||
{
|
||||
const char* s0 = "world";
|
||||
basic_string_view<char> v0(s0);
|
||||
v0.remove_prefix(1);
|
||||
REQUIRE(v0.data() == s0 + 1);
|
||||
REQUIRE(v0.size() == 4);
|
||||
v0.remove_suffix(2);
|
||||
REQUIRE(v0.data() == s0 + 1);
|
||||
REQUIRE(v0.size() == 2);
|
||||
}
|
||||
{
|
||||
char buf[6] = {0};
|
||||
basic_string_view<char>("hello").copy(buf, 5);
|
||||
REQUIRE(std::strcpy(buf, "hello"));
|
||||
basic_string_view<char>("world").copy(buf, 3, 1);
|
||||
REQUIRE(std::memcmp(buf, "orllo\0", 6) == 0);
|
||||
basic_string_view<char>("hell").copy(buf, 0);
|
||||
REQUIRE(std::memcmp(buf, "orllo\0", 6) == 0);
|
||||
|
||||
basic_string_view<char>().copy(buf, 0);
|
||||
REQUIRE(std::memcmp(buf, "orllo\0", 6) == 0);
|
||||
basic_string_view<char>().copy(buf, 10);
|
||||
REQUIRE(std::memcmp(buf, "orllo\0", 6) == 0);
|
||||
basic_string_view<char>("hell").copy(buf, 10, 4);
|
||||
REQUIRE(std::memcmp(buf, "orllo\0", 6) == 0);
|
||||
|
||||
REQUIRE_THROWS_AS(basic_string_view<char>("hell").copy(buf, 1, 5), std::out_of_range);
|
||||
REQUIRE(std::memcmp(buf, "orllo\0", 6) == 0);
|
||||
}
|
||||
{
|
||||
const char* s0 = "world";
|
||||
basic_string_view<char> v0(s0);
|
||||
auto v1 = v0.substr(1, 3);
|
||||
REQUIRE(v1.data() == v0.data() + 1);
|
||||
REQUIRE(v1.size() == 3);
|
||||
v1 = basic_string_view<char>(s0).substr(3);
|
||||
REQUIRE(v1.data() == v0.data() + 3);
|
||||
REQUIRE(v1.size() == 2);
|
||||
v1 = basic_string_view<char>(s0).substr(5);
|
||||
REQUIRE(v1.data() == v0.data() + 5);
|
||||
REQUIRE(v1.size() == 0);
|
||||
REQUIRE_THROWS_AS(basic_string_view<char>("hello").substr(6), std::out_of_range);
|
||||
}
|
||||
{
|
||||
using sw = basic_string_view<char>;
|
||||
|
||||
REQUIRE(sw("hello") == sw("hello"));
|
||||
REQUIRE(sw("hello") != sw("world"));
|
||||
REQUIRE_FALSE(sw("hello") != sw("hello"));
|
||||
REQUIRE_FALSE(sw("hello") == sw("world"));
|
||||
|
||||
REQUIRE_FALSE(sw("hello") == sw("hell"));
|
||||
REQUIRE_FALSE(sw("hell") == sw("hello"));
|
||||
|
||||
REQUIRE(sw("hello") != sw("hell"));
|
||||
REQUIRE(sw("hell") != sw("hello"));
|
||||
|
||||
REQUIRE_FALSE(sw("abc") < sw("abc"));
|
||||
REQUIRE(sw("abc") < sw("abcd"));
|
||||
REQUIRE_FALSE(sw("abcd") < sw("abc"));
|
||||
REQUIRE(sw("abc") < sw("acc"));
|
||||
REQUIRE_FALSE(sw("acc") < sw("abc"));
|
||||
|
||||
REQUIRE(sw("world") == "world");
|
||||
REQUIRE(("world" == sw("world")));
|
||||
REQUIRE_FALSE(sw("world") != "world");
|
||||
REQUIRE_FALSE(("world" != sw("world")));
|
||||
|
||||
REQUIRE(sw("hell") < "hello");
|
||||
REQUIRE_FALSE(sw("hello") < "hell");
|
||||
|
||||
REQUIRE_FALSE("hello" < sw("hell"));
|
||||
REQUIRE("hell" < sw("hello"));
|
||||
|
||||
REQUIRE(sw("world") == str("world"));
|
||||
REQUIRE((str("world") == sw("world")));
|
||||
REQUIRE_FALSE(sw("world") != str("world"));
|
||||
REQUIRE_FALSE((str("world") != sw("world")));
|
||||
|
||||
REQUIRE(sw("hell") < str("hello"));
|
||||
REQUIRE_FALSE(sw("hello") < str("hell"));
|
||||
|
||||
REQUIRE_FALSE(str("hello") < sw("hell"));
|
||||
REQUIRE(str("hell") < sw("hello"));
|
||||
}
|
||||
}
|
||||
171
untests/sources/untests_utils/buffer.cpp
Normal file
171
untests/sources/untests_utils/buffer.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("buffer") {
|
||||
{
|
||||
REQUIRE(buffer().size() == 0);
|
||||
REQUIRE(buffer().data() == nullptr);
|
||||
REQUIRE(buffer(10).size() == 10);
|
||||
REQUIRE(buffer(10).data() != nullptr);
|
||||
}
|
||||
{
|
||||
buffer b("hello", 5);
|
||||
REQUIRE(b.size() == 5);
|
||||
REQUIRE(std::memcmp(b.data(), "hello", 5) == 0);
|
||||
b.clear();
|
||||
REQUIRE(b.size() == 0);
|
||||
REQUIRE(b.data() == nullptr);
|
||||
}
|
||||
{
|
||||
const buffer b0("hello", 5);
|
||||
buffer b1(b0);
|
||||
REQUIRE(b0.size() == 5);
|
||||
REQUIRE(std::memcmp(b0.data(), "hello", 5) == 0);
|
||||
REQUIRE(b1.size() == 5);
|
||||
REQUIRE(std::memcmp(b1.data(), "hello", 5) == 0);
|
||||
REQUIRE(b0.data() != b1.data());
|
||||
const u8* b1_data = b1.data();
|
||||
const buffer b2("world", 5);
|
||||
b1 = b2;
|
||||
REQUIRE(b1_data == b1.data());
|
||||
REQUIRE(b1.size() == 5);
|
||||
REQUIRE(std::memcmp(b1.data(), "world", 5) == 0);
|
||||
}
|
||||
{
|
||||
buffer b0("hello", 5);
|
||||
buffer b1(std::move(b0));
|
||||
REQUIRE(b0.size() == 0);
|
||||
REQUIRE(b0.data() == nullptr);
|
||||
REQUIRE(b1.size() == 5);
|
||||
REQUIRE(std::memcmp(b1.data(), "hello", 5) == 0);
|
||||
b1 = buffer("hello, world", 12);
|
||||
REQUIRE(b1.size() == 12);
|
||||
REQUIRE(std::memcmp(b1.data(), "hello, world", 12) == 0);
|
||||
}
|
||||
{
|
||||
buffer b0("hello", 5);
|
||||
buffer b1 = b0;
|
||||
REQUIRE(b0 == b1);
|
||||
REQUIRE_FALSE(b0 == buffer("world", 5));
|
||||
REQUIRE_FALSE(b0 != b1);
|
||||
REQUIRE(b0 != buffer("world", 5));
|
||||
REQUIRE_FALSE(b0 == buffer());
|
||||
REQUIRE(b0 != buffer());
|
||||
REQUIRE(buffer() == buffer());
|
||||
REQUIRE_FALSE(buffer() != buffer());
|
||||
}
|
||||
{
|
||||
buffer b0("hello", 5);
|
||||
buffer b1 = b0;
|
||||
b0.clear();
|
||||
REQUIRE(b1.size() == 5);
|
||||
REQUIRE(std::memcmp(b1.data(), "hello", 5) == 0);
|
||||
}
|
||||
{
|
||||
buffer b0;
|
||||
REQUIRE((&b0 == &b0.fill('a') && b0.size() == 0));
|
||||
b0.resize(5);
|
||||
REQUIRE_FALSE(std::memcmp(b0.data(), "aaaaa", 5) == 0);
|
||||
REQUIRE((&b0 == &b0.fill('a') && b0.size() == 5));
|
||||
REQUIRE(std::memcmp(b0.data(), "aaaaa", 5) == 0);
|
||||
REQUIRE((&b0 == &b0.fill('b') && b0.size() == 5));
|
||||
REQUIRE(std::memcmp(b0.data(), "bbbbb", 5) == 0);
|
||||
b0.resize(7);
|
||||
REQUIRE_FALSE(std::memcmp(b0.data(), "ccccccc", 7) == 0);
|
||||
b0.fill('c');
|
||||
REQUIRE(std::memcmp(b0.data(), "ccccccc", 7) == 0);
|
||||
b0.resize(7);
|
||||
REQUIRE(std::memcmp(b0.data(), "ccccccc", 7) == 0);
|
||||
}
|
||||
{
|
||||
buffer b0("hello", 5);
|
||||
buffer b1;
|
||||
REQUIRE(b1.empty());
|
||||
REQUIRE(&b1 == &b1.assign(std::move(b0)));
|
||||
REQUIRE_FALSE(b1.empty());
|
||||
REQUIRE((b0.empty() && b0.data() == nullptr && b0.size() == 0));
|
||||
REQUIRE((std::memcmp(b1.data(), "hello", 5) == 0 && b1.size() == 5));
|
||||
REQUIRE(&b1 == &b1.resize(4));
|
||||
REQUIRE((std::memcmp(b1.data(), "hell", 4) == 0 && b1.size() == 4));
|
||||
REQUIRE(&b1 == &b1.assign("world", 5));
|
||||
REQUIRE((std::memcmp(b1.data(), "world", 5) == 0 && b1.size() == 5));
|
||||
REQUIRE(&b1 == &b1.assign("hello", 5));
|
||||
REQUIRE((std::memcmp(b1.data(), "hello", 5) == 0 && b1.size() == 5));
|
||||
REQUIRE(&b1 == &b1.resize(0));
|
||||
REQUIRE((b1.data() == nullptr && b1.size() == 0));
|
||||
}
|
||||
{
|
||||
buffer b1("hello", 5);
|
||||
b1.assign(b1.data(), 3);
|
||||
REQUIRE(b1.size() == 3);
|
||||
REQUIRE(std::memcmp(b1.data(), "hel", 3) == 0);
|
||||
}
|
||||
{
|
||||
REQUIRE_FALSE(buffer() < buffer());
|
||||
REQUIRE_FALSE(buffer("aa",2) < buffer("aa",2));
|
||||
|
||||
REQUIRE(buffer() < buffer("a",1));
|
||||
REQUIRE_FALSE(buffer("a",1) < buffer());
|
||||
|
||||
REQUIRE(buffer("aa",2) < buffer("ab",2));
|
||||
REQUIRE_FALSE(buffer("ab",2) < buffer("aa",2));
|
||||
|
||||
REQUIRE(buffer("aa",2) < buffer("aaa",3));
|
||||
REQUIRE(buffer("aa",2) < buffer("abb",3));
|
||||
REQUIRE_FALSE(buffer("aaa",3) < buffer("aa",2));
|
||||
REQUIRE_FALSE(buffer("abb",3) < buffer("aa",2));
|
||||
}
|
||||
{
|
||||
REQUIRE(buffer() == buffer());
|
||||
REQUIRE_FALSE(buffer() != buffer());
|
||||
|
||||
REQUIRE_FALSE(buffer() == buffer("hello",5));
|
||||
REQUIRE_FALSE(buffer("hello",5) == buffer());
|
||||
REQUIRE(buffer("hello",5) == buffer("hello",5));
|
||||
REQUIRE_FALSE(buffer("hello",5) != buffer("hello",5));
|
||||
|
||||
REQUIRE(buffer("hello",5) != buffer("world",5));
|
||||
REQUIRE_FALSE(buffer("hello",5) == buffer("world",5));
|
||||
|
||||
REQUIRE(buffer("hello",5) != buffer("hello, world",12));
|
||||
REQUIRE_FALSE(buffer("hello",5) == buffer("hello, world",12));
|
||||
}
|
||||
{
|
||||
buffer b0("hello", 5);
|
||||
buffer b1("hello world", 11);
|
||||
b0.swap(b1);
|
||||
REQUIRE((std::memcmp(b1.data(), "hello", 5) == 0 && b1.size() == 5));
|
||||
REQUIRE((std::memcmp(b0.data(), "hello world", 11) == 0 && b0.size() == 11));
|
||||
swap(b0, b1);
|
||||
REQUIRE((std::memcmp(b0.data(), "hello", 5) == 0 && b0.size() == 5));
|
||||
REQUIRE((std::memcmp(b1.data(), "hello world", 11) == 0 && b1.size() == 11));
|
||||
}
|
||||
{
|
||||
const std::size_t msize = std::numeric_limits<std::size_t>::max();
|
||||
REQUIRE_THROWS_AS(buffer(msize), std::bad_alloc);
|
||||
REQUIRE_THROWS_AS(buffer(nullptr, msize), std::bad_alloc);
|
||||
{
|
||||
buffer b0;
|
||||
REQUIRE_THROWS_AS(b0.resize(msize), std::bad_alloc);
|
||||
REQUIRE((b0.data() == nullptr && b0.size() == 0));
|
||||
buffer b1("hello", 5);
|
||||
REQUIRE_THROWS_AS(b1.resize(msize), std::bad_alloc);
|
||||
REQUIRE((std::memcmp(b1.data(), "hello", 5) == 0 && b1.size() == 5));
|
||||
}
|
||||
{
|
||||
buffer b0;
|
||||
REQUIRE_THROWS_AS(b0.assign(nullptr, msize), std::bad_alloc);
|
||||
REQUIRE((b0.data() == nullptr && b0.size() == 0));
|
||||
|
||||
buffer b1("hello", 5);
|
||||
REQUIRE_THROWS_AS(b1.assign(nullptr, msize), std::bad_alloc);
|
||||
REQUIRE((std::memcmp(b1.data(), "hello", 5) == 0 && b1.size() == 5));
|
||||
}
|
||||
}
|
||||
}
|
||||
150
untests/sources/untests_utils/color.cpp
Normal file
150
untests/sources/untests_utils/color.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("color") {
|
||||
{
|
||||
REQUIRE(color() == color::white());
|
||||
REQUIRE(color(color::black()) == color::black());
|
||||
REQUIRE(color(color32::yellow()) == color::yellow());
|
||||
color c = color::white();
|
||||
c = color::red();
|
||||
REQUIRE(c == color::red());
|
||||
REQUIRE((c = color(0.5f, 0.6f, 0.7f, 0.8f)) == color(0.5f, 0.6f, 0.7f, 0.8f));
|
||||
REQUIRE(math::approximately(c[0], 0.5f));
|
||||
REQUIRE(math::approximately(c[1], 0.6f));
|
||||
REQUIRE(math::approximately(c[2], 0.7f));
|
||||
REQUIRE(math::approximately(c[3], 0.8f));
|
||||
c[0] = 0.1f;
|
||||
c[1] = 0.2f;
|
||||
c[2] = 0.3f;
|
||||
c[3] = 0.4f;
|
||||
REQUIRE(math::approximately(c[0], 0.1f));
|
||||
REQUIRE(math::approximately(c[1], 0.2f));
|
||||
REQUIRE(math::approximately(c[2], 0.3f));
|
||||
REQUIRE(math::approximately(c[3], 0.4f));
|
||||
}
|
||||
{
|
||||
color c = color(0.1f,0.2f,0.3f,0.4f);
|
||||
c += 0.1f;
|
||||
REQUIRE(c == color(0.2f,0.3f,0.4f,0.5f));
|
||||
c -= -0.3f;
|
||||
REQUIRE(c == color(0.5f,0.6f,0.7f,0.8f));
|
||||
c *= 2.f;
|
||||
REQUIRE(c == color(1.0f,1.2f,1.4f,1.6f));
|
||||
c /= 2.f;
|
||||
REQUIRE(c == color(0.5f,0.6f,0.7f,0.8f));
|
||||
c += color(0.1f,0.2f,0.3f,0.4f);
|
||||
REQUIRE(c == color(0.6f,0.8f,1.0f,1.2f));
|
||||
c -= color(0.1f,0.2f,0.3f,0.4f);
|
||||
REQUIRE(c == color(0.5f,0.6f,0.7f,0.8f));
|
||||
c *= color(0.1f,0.2f,0.3f,0.4f);
|
||||
REQUIRE(c == color(0.05f,0.12f,0.21f,0.32f));
|
||||
c /= color(0.1f,0.2f,0.3f,0.4f);
|
||||
REQUIRE(c == color(0.5f,0.6f,0.7f,0.8f));
|
||||
}
|
||||
{
|
||||
REQUIRE(color(0.1f,0.2f,0.3f,0.4f) + 0.1f == color(0.2f,0.3f,0.4f,0.5f));
|
||||
REQUIRE(color(0.1f,0.2f,0.3f,0.4f) - 0.1f == color(0.0f,0.1f,0.2f,0.3f));
|
||||
REQUIRE(color(0.1f,0.2f,0.3f,0.4f) * 2.f == color(0.2f,0.4f,0.6f,0.8f));
|
||||
REQUIRE(color(0.1f,0.2f,0.3f,0.4f) / 2.f == color(0.05f,0.1f,0.15f,0.2f));
|
||||
|
||||
REQUIRE(0.1f + color(0.1f,0.2f,0.3f,0.4f) == color(0.2f,0.3f,0.4f,0.5f));
|
||||
REQUIRE(0.6f - color(0.1f,0.2f,0.3f,0.4f) == color(0.5f,0.4f,0.3f,0.2f));
|
||||
REQUIRE(2.0f * color(0.1f,0.2f,0.3f,0.4f) == color(0.2f,0.4f,0.6f,0.8f));
|
||||
REQUIRE(0.1f / color(0.1f,0.2f,0.4f,0.5f) == color(1.f,0.5f,0.25f,0.2f));
|
||||
|
||||
REQUIRE(color(0.1f,0.2f,0.3f,0.4f) + color(0.2f,0.3f,0.4f,0.5f) == color(0.3f,0.5f,0.7f,0.9f));
|
||||
REQUIRE(color(0.3f,0.5f,0.7f,0.9f) - color(0.2f,0.3f,0.4f,0.5f) == color(0.1f,0.2f,0.3f,0.4f));
|
||||
REQUIRE(color(0.1f,0.2f,0.3f,0.4f) * color(0.2f,0.3f,0.4f,0.5f) == color(0.02f,0.06f,0.12f,0.2f));
|
||||
REQUIRE(color(0.1f,0.2f,0.3f,0.4f) / color(0.2f,0.1f,0.6f,0.5f) == color(0.5f,2.0f,0.5f,0.8f));
|
||||
}
|
||||
{
|
||||
REQUIRE(math::approximately(color::white(), color(1.f,1.f,1.f,1.000001f)));
|
||||
REQUIRE(math::approximately(color::white(), color(1.f,0.999999f,1.f,1.000001f)));
|
||||
REQUIRE(math::approximately(color::white(), color(0.9f,1.f,1.1f,1.f), 0.15f));
|
||||
|
||||
color c0(1,2,3,4);
|
||||
REQUIRE(math::clamped(c0, color(0,0,0,0), color(0,1,2,3)) == color(0,1,2,3));
|
||||
REQUIRE(c0 == color(1,2,3,4));
|
||||
REQUIRE(math::clamped(c0, color(2,3,4,5), color(9,9,9,9)) == color(2,3,4,5));
|
||||
REQUIRE(c0 == color(1,2,3,4));
|
||||
|
||||
REQUIRE(math::clamped(c0, color(3,4,5,6), color(6,7,8,9)) == color(3,4,5,6));
|
||||
REQUIRE(c0 == color(1,2,3,4));
|
||||
REQUIRE(math::clamped(c0, color(2,3,4,5), color(6,7,8,9)) == color(2,3,4,5));
|
||||
REQUIRE(c0 == color(1,2,3,4));
|
||||
}
|
||||
{
|
||||
REQUIRE(math::approximately(math::minimum(color(1,2,3,4)), 1.f));
|
||||
REQUIRE(math::approximately(math::minimum(color(2,1,3,4)), 1.f));
|
||||
REQUIRE(math::approximately(math::minimum(color(4,3,2,1)), 1.f));
|
||||
|
||||
REQUIRE(math::approximately(math::maximum(color(1,2,3,4)), 4.f));
|
||||
REQUIRE(math::approximately(math::maximum(color(2,1,3,4)), 4.f));
|
||||
REQUIRE(math::approximately(math::maximum(color(4,3,2,1)), 4.f));
|
||||
}
|
||||
{
|
||||
color c0(1,2,3,4);
|
||||
REQUIRE(math::minimized(c0, color(0,1,2,3)) == color(0,1,2,3));
|
||||
REQUIRE(c0 == color(1,2,3,4));
|
||||
REQUIRE(math::maximized(c0, color(2,3,4,5)) == color(2,3,4,5));
|
||||
REQUIRE(c0 == color(1,2,3,4));
|
||||
|
||||
REQUIRE(math::maximized(c0, color(3,4,5,6)) == color(3,4,5,6));
|
||||
REQUIRE(c0 == color(1,2,3,4));
|
||||
REQUIRE(math::minimized(c0, color(2,3,4,5)) == color(1,2,3,4));
|
||||
REQUIRE(c0 == color(1,2,3,4));
|
||||
}
|
||||
{
|
||||
color v0(3,4,5,6);
|
||||
REQUIRE(math::clamped(v0, color(0,0,0,0), color(2,3,4,5)) == color(2,3,4,5));
|
||||
REQUIRE(v0 == color(3,4,5,6));
|
||||
REQUIRE(math::clamped(v0, color(3,4,5,6), color(3,4,5,6)) == color(3,4,5,6));
|
||||
REQUIRE(v0 == color(3,4,5,6));
|
||||
|
||||
REQUIRE(math::clamped(v0, color(0,0,0,0), color(0,0,0,0)) == color(0,0,0,0));
|
||||
REQUIRE(math::clamped(v0, color(0,0,0,0), color(3,2,1,0)) == color(3,2,1,0));
|
||||
REQUIRE(math::clamped(v0, color(0,0,0,0), color(4,3,2,1)) == color(3,3,2,1));
|
||||
|
||||
REQUIRE(math::clamped(v0, color(4,5,6,7), color(9,9,9,9)) == color(4,5,6,7));
|
||||
REQUIRE(math::clamped(v0, color(6,5,4,3), color(9,9,9,9)) == color(6,5,5,6));
|
||||
REQUIRE(math::clamped(v0, color(7,6,5,4), color(9,9,9,9)) == color(7,6,5,6));
|
||||
|
||||
REQUIRE(math::saturated(color(-1,-2,-3,-4)) == color(0,0,0,0));
|
||||
REQUIRE(math::saturated(color( 2, 3, 4, 5)) == color(1,1,1,1));
|
||||
REQUIRE(math::saturated(color(-1, 3, 4, 5)) == color(0,1,1,1));
|
||||
|
||||
REQUIRE(math::saturated(color(2,0.6f,2,0.7f)) == color(1,0.6f,1,0.7f));
|
||||
REQUIRE(math::saturated(color(0.6f,-2,2,2)) == color(0.6f,0,1,1));
|
||||
}
|
||||
{
|
||||
{
|
||||
REQUIRE_FALSE(math::contains_nan(color(0,1,2,3)));
|
||||
REQUIRE_FALSE(math::contains_nan(color(0.f,1.f,2.f,3.f)));
|
||||
REQUIRE(math::contains_nan(color(0.f,1.f,2.f,std::numeric_limits<f32>::quiet_NaN())));
|
||||
REQUIRE(math::contains_nan(color(0.f,1.f,std::numeric_limits<f32>::quiet_NaN(),2.f)));
|
||||
REQUIRE(math::contains_nan(color(std::numeric_limits<f32>::infinity(),1.f,2.f,3.f)));
|
||||
REQUIRE(math::contains_nan(color(1.f,std::numeric_limits<f32>::infinity(),2.f,3.f)));
|
||||
|
||||
REQUIRE_FALSE(math::contains(color(0.1f,0.2f,0.3f,0.4f), 0.f));
|
||||
REQUIRE(math::contains(color(0.1f,0.2f,0.3f,0.4f), 0.f, 0.45f));
|
||||
REQUIRE(math::contains(color(0,1.f,2.f,3.f), 0.f));
|
||||
REQUIRE(math::contains(color(1.f,0,2.f,3.f), 0.f));
|
||||
REQUIRE(math::contains(color(1.f,2.f,0,3.f), 0.f));
|
||||
REQUIRE(math::contains(color(1.f,2.f,3.f,0), 0.f));
|
||||
REQUIRE(math::contains(color(0,0,0,0), 0.f));
|
||||
}
|
||||
}
|
||||
{
|
||||
REQUIRE(colors::pack_color(color(color32(1,2,3,4))) == 0x04010203);
|
||||
REQUIRE(colors::pack_color(color(color32(0x12,0x34,0x56,0x78))) == 0x78123456);
|
||||
REQUIRE(colors::unpack_color(0x04010203) == color(color32(1,2,3,4)));
|
||||
REQUIRE(colors::unpack_color(0x78123456) == color(color32(0x12,0x34,0x56,0x78)));
|
||||
}
|
||||
}
|
||||
136
untests/sources/untests_utils/color32.cpp
Normal file
136
untests/sources/untests_utils/color32.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("color32") {
|
||||
{
|
||||
REQUIRE(color32() == color32::white());
|
||||
REQUIRE(color32(color32::black()) == color32::black());
|
||||
REQUIRE(color32(color::yellow()) == color32::yellow());
|
||||
REQUIRE(color32(color(0.9999f,0.9999f,0.9999f,0.9999f)) == color32::white());
|
||||
REQUIRE(color32(color(0.0001f,0.0001f,0.0001f,0.0001f)) == color32::clear());
|
||||
color32 c = color32::white();
|
||||
c = color32::red();
|
||||
REQUIRE(c == color32::red());
|
||||
REQUIRE((c = color32(10,20,30,40)) == color32(10,20,30,40));
|
||||
REQUIRE(c[0] == 10);
|
||||
REQUIRE(c[1] == 20);
|
||||
REQUIRE(c[2] == 30);
|
||||
REQUIRE(c[3] == 40);
|
||||
c[0] = u8(20);
|
||||
c[1] = u8(30);
|
||||
c[2] = u8(40);
|
||||
c[3] = u8(50);
|
||||
REQUIRE(c[0] == 20);
|
||||
REQUIRE(c[1] == 30);
|
||||
REQUIRE(c[2] == 40);
|
||||
REQUIRE(c[3] == 50);
|
||||
}
|
||||
{
|
||||
color32 c = color32(10,20,30,40);
|
||||
c += 40;
|
||||
REQUIRE(c == color32(50,60,70,80));
|
||||
c -= 10;
|
||||
REQUIRE(c == color32(40,50,60,70));
|
||||
c *= 2;
|
||||
REQUIRE(c == color32(80,100,120,140));
|
||||
c /= 2;
|
||||
REQUIRE(c == color32(40,50,60,70));
|
||||
c += color32(10,20,30,40);
|
||||
REQUIRE(c == color32(50,70,90,110));
|
||||
c -= color32(10,20,30,40);
|
||||
REQUIRE(c == color32(40,50,60,70));
|
||||
c *= color32(2,3,4,2);
|
||||
REQUIRE(c == color32(80,150,240,140));
|
||||
c /= color32(2,3,6,7);
|
||||
REQUIRE(c == color32(40,50,40,20));
|
||||
}
|
||||
{
|
||||
REQUIRE(color32(10,20,30,40) + 10 == color32(20,30,40,50));
|
||||
REQUIRE(color32(10,20,30,40) - 10 == color32(0,10,20,30));
|
||||
REQUIRE(color32(10,20,30,40) * 2 == color32(20,40,60,80));
|
||||
REQUIRE(color32(10,20,30,40) / 2 == color32(5,10,15,20));
|
||||
|
||||
REQUIRE(10 + color32(10,20,30,40) == color32(20,30,40,50));
|
||||
REQUIRE(60 - color32(10,20,30,40) == color32(50,40,30,20));
|
||||
REQUIRE(2 * color32(10,20,30,40) == color32(20,40,60,80));
|
||||
REQUIRE(100 / color32(10,20,25,50) == color32(10,5,4,2));
|
||||
|
||||
REQUIRE(color32(10,20,30,40) + color32(20,30,40,50) == color32(30,50,70,90));
|
||||
REQUIRE(color32(10,20,30,40) - color32(1,2,3,4) == color32(9,18,27,36));
|
||||
REQUIRE(color32(10,20,30,40) * color32(3,4,2,5) == color32(30,80,60,200));
|
||||
REQUIRE(color32(80,150,180,140) / color32(2,3,6,7) == color32(40,50,30,20));
|
||||
}
|
||||
{
|
||||
REQUIRE(math::approximately(color32::white(), color32(255,255,255,255)));
|
||||
REQUIRE(math::approximately(color32::white(), color32(255,255,255,255)));
|
||||
REQUIRE(math::approximately(color32::white(), color32(253,255,255,254), 3));
|
||||
|
||||
color32 c0(1,2,3,4);
|
||||
REQUIRE(math::clamped(c0, color32(0,0,0,0), color32(0,1,2,3)) == color32(0,1,2,3));
|
||||
REQUIRE(c0 == color32(1,2,3,4));
|
||||
REQUIRE(math::clamped(c0, color32(2,3,4,5), color32(9,9,9,9)) == color32(2,3,4,5));
|
||||
REQUIRE(c0 == color32(1,2,3,4));
|
||||
|
||||
REQUIRE(math::clamped(c0, color32(3,4,5,6), color32(6,7,8,9)) == color32(3,4,5,6));
|
||||
REQUIRE(c0 == color32(1,2,3,4));
|
||||
REQUIRE(math::clamped(c0, color32(2,3,4,5), color32(6,7,8,9)) == color32(2,3,4,5));
|
||||
REQUIRE(c0 == color32(1,2,3,4));
|
||||
}
|
||||
{
|
||||
REQUIRE(math::minimum(color32(1,2,3,4)) == 1);
|
||||
REQUIRE(math::minimum(color32(2,1,3,4)) == 1);
|
||||
REQUIRE(math::minimum(color32(4,3,2,1)) == 1);
|
||||
|
||||
REQUIRE(math::maximum(color32(1,2,3,4)) == 4);
|
||||
REQUIRE(math::maximum(color32(2,1,3,4)) == 4);
|
||||
REQUIRE(math::maximum(color32(4,3,2,1)) == 4);
|
||||
}
|
||||
{
|
||||
color32 v0(3,4,5,6);
|
||||
REQUIRE(math::clamped(v0, color32(0,0,0,0), color32(2,3,4,5)) == color32(2,3,4,5));
|
||||
REQUIRE(v0 == color32(3,4,5,6));
|
||||
REQUIRE(math::clamped(v0, color32(3,4,5,6), color32(3,4,5,6)) == color32(3,4,5,6));
|
||||
REQUIRE(v0 == color32(3,4,5,6));
|
||||
|
||||
REQUIRE(math::clamped(v0, color32(0,0,0,0), color32(0,0,0,0)) == color32(0,0,0,0));
|
||||
REQUIRE(math::clamped(v0, color32(0,0,0,0), color32(3,2,1,0)) == color32(3,2,1,0));
|
||||
REQUIRE(math::clamped(v0, color32(0,0,0,0), color32(4,3,2,1)) == color32(3,3,2,1));
|
||||
|
||||
REQUIRE(math::clamped(v0, color32(4,5,6,7), color32(9,9,9,9)) == color32(4,5,6,7));
|
||||
REQUIRE(math::clamped(v0, color32(6,5,4,3), color32(9,9,9,9)) == color32(6,5,5,6));
|
||||
REQUIRE(math::clamped(v0, color32(7,6,5,4), color32(9,9,9,9)) == color32(7,6,5,6));
|
||||
}
|
||||
{
|
||||
color32 c0(1,2,3,4);
|
||||
REQUIRE(math::minimized(c0, color32(0,1,2,3)) == color32(0,1,2,3));
|
||||
REQUIRE(c0 == color32(1,2,3,4));
|
||||
REQUIRE(math::maximized(c0, color32(2,3,4,5)) == color32(2,3,4,5));
|
||||
REQUIRE(c0 == color32(1,2,3,4));
|
||||
|
||||
REQUIRE(math::maximized(c0, color32(3,4,5,6)) == color32(3,4,5,6));
|
||||
REQUIRE(c0 == color32(1,2,3,4));
|
||||
REQUIRE(math::minimized(c0, color32(2,3,4,5)) == color32(1,2,3,4));
|
||||
REQUIRE(c0 == color32(1,2,3,4));
|
||||
}
|
||||
{
|
||||
REQUIRE_FALSE(math::contains(color32(1,1,1,1), 0));
|
||||
REQUIRE(math::contains(color32(1,1,1,1), 0, 1));
|
||||
REQUIRE(math::contains(color32(0,1,1,1), 0));
|
||||
REQUIRE(math::contains(color32(1,0,1,1), 0));
|
||||
REQUIRE(math::contains(color32(1,1,0,1), 0));
|
||||
REQUIRE(math::contains(color32(1,1,1,0), 0));
|
||||
REQUIRE(math::contains(color32(0,0,0,0), 0));
|
||||
}
|
||||
{
|
||||
REQUIRE(colors::pack_color32(color32(1,2,3,4)) == 0x04010203);
|
||||
REQUIRE(colors::pack_color32(color32(0x12,0x34,0x56,0x78)) == 0x78123456);
|
||||
REQUIRE(colors::unpack_color32(0x04010203) == color32(1,2,3,4));
|
||||
REQUIRE(colors::unpack_color32(0x78123456) == color32(0x12,0x34,0x56,0x78));
|
||||
}
|
||||
}
|
||||
457
untests/sources/untests_utils/filesystem.cpp
Normal file
457
untests/sources/untests_utils/filesystem.cpp
Normal file
@@ -0,0 +1,457 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("filesystem") {
|
||||
SECTION("path") {
|
||||
{
|
||||
const vector<str> absolute_paths = {
|
||||
"/",
|
||||
"/usr",
|
||||
"/usr/",
|
||||
"/usr/local",
|
||||
|
||||
"C:",
|
||||
"C:\\",
|
||||
"C:\\windows",
|
||||
"C:\\windows\\",
|
||||
"C:\\windows\\system32",
|
||||
|
||||
"\\\\.\\COM1",
|
||||
"\\\\?\\C:",
|
||||
"\\\\?\\UNC",
|
||||
"\\\\server",
|
||||
};
|
||||
const vector<str> relative_paths = {
|
||||
"",
|
||||
|
||||
"usr",
|
||||
"usr/",
|
||||
"usr/local",
|
||||
|
||||
"windows",
|
||||
"windows\\",
|
||||
"windows\\system32",
|
||||
};
|
||||
for ( const str& s : absolute_paths ) {
|
||||
REQUIRE(path::is_absolute(s));
|
||||
REQUIRE_FALSE(path::is_relative(s));
|
||||
}
|
||||
for ( const str& s : relative_paths ) {
|
||||
REQUIRE(path::is_relative(s));
|
||||
REQUIRE_FALSE(path::is_absolute(s));
|
||||
}
|
||||
}
|
||||
{
|
||||
// [lsh, rhs, result]
|
||||
using t = std::tuple<str, str, str>;
|
||||
const vector<t> combinations = {
|
||||
t{"", "", ""},
|
||||
|
||||
t{".", ".", "./."},
|
||||
t{"..", "..", "../.."},
|
||||
|
||||
t{".", "..", "./.."},
|
||||
t{"..", ".", "../."},
|
||||
|
||||
t{"file", "", "file"},
|
||||
t{"", "file", "file"},
|
||||
|
||||
t{"folder", "file", "folder/file"},
|
||||
t{"folder/", "file", "folder/file"},
|
||||
t{"folder\\", "file", "folder\\file"},
|
||||
|
||||
t{"folder", "other/file", "folder/other/file"},
|
||||
t{"folder/", "other/file", "folder/other/file"},
|
||||
t{"folder\\", "other/file", "folder\\other/file"},
|
||||
|
||||
t{"folder", "/file", "/file"},
|
||||
t{"folder/", "\\file", "\\file"},
|
||||
t{"folder\\", "C:\\file", "C:\\file"},
|
||||
t{"folder\\", "C:/file", "C:/file"},
|
||||
};
|
||||
for ( const auto& combination : combinations ) {
|
||||
str lhs, rhs, res;
|
||||
std::tie(lhs, rhs, res) = combination;
|
||||
REQUIRE(path::combine(lhs, rhs) == res);
|
||||
}
|
||||
}
|
||||
{
|
||||
// [path, stem, filename, extension]
|
||||
using t = std::tuple<str, str, str, str>;
|
||||
const vector<t> combinations = {
|
||||
t{"", "", "", ""},
|
||||
|
||||
t{".", ".", ".", ""},
|
||||
t{"/.", ".", ".", ""},
|
||||
t{"\\.", ".", ".", ""},
|
||||
t{"folder.jpg/.", ".", ".", ""},
|
||||
t{"folder.jpg\\.", ".", ".", ""},
|
||||
|
||||
t{"..", "..", "..", ""},
|
||||
t{"/..", "..", "..", ""},
|
||||
t{"\\..", "..", "..", ""},
|
||||
t{"folder.jpg/..", "..", "..", ""},
|
||||
t{"folder.jpg\\..", "..", "..", ""},
|
||||
|
||||
t{"/", "", "", ""},
|
||||
t{"file.jpg/", "", "", ""},
|
||||
|
||||
t{"\\", "", "", ""},
|
||||
t{"file.jpg\\", "", "", ""},
|
||||
|
||||
t{"C:", "C:", "C:", ""},
|
||||
t{"C:/", "", "", ""},
|
||||
t{"C:\\", "", "", ""},
|
||||
|
||||
t{"file", "file", "file", ""},
|
||||
t{"file.png", "file", "file.png", ".png"},
|
||||
t{"file.jpg.png", "file.jpg", "file.jpg.png", ".png"},
|
||||
|
||||
t{"/file", "file", "file", ""},
|
||||
t{"/file.png", "file", "file.png", ".png"},
|
||||
t{"/file.jpg.png", "file.jpg", "file.jpg.png", ".png"},
|
||||
|
||||
t{"\\file", "file", "file", ""},
|
||||
t{"\\file.png", "file", "file.png", ".png"},
|
||||
t{"\\file.jpg.png", "file.jpg", "file.jpg.png", ".png"},
|
||||
|
||||
t{"folder.jpg/file", "file", "file", ""},
|
||||
t{"folder.jpg/file.png", "file", "file.png", ".png"},
|
||||
t{"folder.jpg/file.jpg.png", "file.jpg", "file.jpg.png", ".png"},
|
||||
|
||||
t{"/folder.jpg/file", "file", "file", ""},
|
||||
t{"/folder.jpg/file.png", "file", "file.png", ".png"},
|
||||
t{"/folder.jpg/file.jpg.png", "file.jpg", "file.jpg.png", ".png"},
|
||||
|
||||
t{"\\folder.jpg\\file", "file", "file", ""},
|
||||
t{"\\folder.jpg\\file.png", "file", "file.png", ".png"},
|
||||
t{"\\folder.jpg/file.jpg.png", "file.jpg", "file.jpg.png", ".png"},
|
||||
};
|
||||
for ( const auto& combination : combinations ) {
|
||||
const str path = std::get<0>(combination);
|
||||
REQUIRE(path::stem(path) == std::get<1>(combination));
|
||||
REQUIRE(path::filename(path) == std::get<2>(combination));
|
||||
REQUIRE(path::extension(path) == std::get<3>(combination));
|
||||
}
|
||||
}
|
||||
{
|
||||
// [path, remove_filename, remove_extension]
|
||||
using t = std::tuple<str, str, str>;
|
||||
const vector<t> combinations = {
|
||||
t{"", "", ""},
|
||||
t{"/", "/", "/"},
|
||||
t{"\\", "\\", "\\"},
|
||||
|
||||
t{".", "", "."},
|
||||
t{"/.", "/", "/."},
|
||||
t{"\\.", "\\", "\\."},
|
||||
|
||||
t{"..", "", ".."},
|
||||
t{"/..", "/", "/.."},
|
||||
t{"\\..", "\\", "\\.."},
|
||||
|
||||
t{"usr", "", "usr"},
|
||||
t{"usr.", "", "usr"},
|
||||
t{"usr.png", "", "usr"},
|
||||
t{"usr.jpg.png", "", "usr.jpg"},
|
||||
|
||||
t{"/usr", "/", "/usr"},
|
||||
t{"/usr.", "/", "/usr"},
|
||||
t{"/usr.png", "/", "/usr"},
|
||||
t{"/usr.jpg.png", "/", "/usr.jpg"},
|
||||
|
||||
t{"\\usr", "\\", "\\usr"},
|
||||
t{"\\usr.", "\\", "\\usr"},
|
||||
t{"\\usr.png", "\\", "\\usr"},
|
||||
t{"\\usr.jpg.png", "\\", "\\usr.jpg"},
|
||||
|
||||
t{"folder.jpg/usr", "folder.jpg/", "folder.jpg/usr"},
|
||||
t{"folder.jpg/usr.", "folder.jpg/", "folder.jpg/usr"},
|
||||
t{"folder.jpg/usr.png", "folder.jpg/", "folder.jpg/usr"},
|
||||
t{"folder.jpg/usr.jpg.png", "folder.jpg/", "folder.jpg/usr.jpg"},
|
||||
|
||||
t{"/folder.jpg/usr", "/folder.jpg/", "/folder.jpg/usr"},
|
||||
t{"/folder.jpg/usr.", "/folder.jpg/", "/folder.jpg/usr"},
|
||||
t{"/folder.jpg/usr.png", "/folder.jpg/", "/folder.jpg/usr"},
|
||||
t{"/folder.jpg/usr.jpg.png", "/folder.jpg/", "/folder.jpg/usr.jpg"},
|
||||
|
||||
t{"\\folder.jpg/usr", "\\folder.jpg/", "\\folder.jpg/usr"},
|
||||
t{"\\folder.jpg/usr.", "\\folder.jpg/", "\\folder.jpg/usr"},
|
||||
t{"\\folder.jpg/usr.png", "\\folder.jpg/", "\\folder.jpg/usr"},
|
||||
t{"\\folder.jpg/usr.jpg.png", "\\folder.jpg/", "\\folder.jpg/usr.jpg"},
|
||||
};
|
||||
for ( const auto& combination : combinations ) {
|
||||
const str path = std::get<0>(combination);
|
||||
REQUIRE(path::remove_filename(path) == std::get<1>(combination));
|
||||
REQUIRE(path::remove_extension(path) == std::get<2>(combination));
|
||||
}
|
||||
}
|
||||
{
|
||||
// [path, filename, result]
|
||||
using t = std::tuple<str, str, str>;
|
||||
const vector<t> combinations = {
|
||||
t{ "", "", "" },
|
||||
t{ "/", "", "/" },
|
||||
t{ "\\", "", "\\" },
|
||||
|
||||
t{ "", "file", "file" },
|
||||
t{ "/", "file", "/file" },
|
||||
t{ "\\", "file", "\\file" },
|
||||
|
||||
t{ ".", "file", "file" },
|
||||
t{ "/.", "file", "/file" },
|
||||
t{ "\\.", "file", "\\file" },
|
||||
t{ ".", "", "" },
|
||||
t{ "/.", "", "/" },
|
||||
t{ "\\.", "", "\\" },
|
||||
|
||||
t{ "..", "file", "file" },
|
||||
t{ "/..", "file", "/file" },
|
||||
t{ "\\..", "file", "\\file" },
|
||||
t{ "..", "", "" },
|
||||
t{ "/..", "", "/" },
|
||||
t{ "\\..", "", "\\" },
|
||||
|
||||
t{ "usr", "file", "file" },
|
||||
t{ "/usr", "file", "/file" },
|
||||
t{ "\\usr", "file", "\\file" },
|
||||
t{ "usr", "", "" },
|
||||
t{ "/usr", "", "/" },
|
||||
t{ "\\usr", "", "\\" },
|
||||
|
||||
t{ "usr/", "file", "usr/file" },
|
||||
t{ "/usr/", "file", "/usr/file" },
|
||||
t{ "\\usr/", "file", "\\usr/file" },
|
||||
t{ "usr/", "", "usr/" },
|
||||
t{ "/usr/", "", "/usr/" },
|
||||
t{ "\\usr/", "", "\\usr/" },
|
||||
|
||||
t{ "folder/usr", "file", "folder/file" },
|
||||
t{ "/folder/usr", "file", "/folder/file" },
|
||||
t{ "\\folder/usr", "file", "\\folder/file" },
|
||||
t{ "folder/usr", "", "folder/" },
|
||||
t{ "/folder/usr", "", "/folder/" },
|
||||
t{ "\\folder/usr", "", "\\folder/" },
|
||||
|
||||
t{ "folder/usr/", "file", "folder/usr/file" },
|
||||
t{ "/folder/usr/", "file", "/folder/usr/file" },
|
||||
t{ "\\folder/usr/", "file", "\\folder/usr/file" },
|
||||
t{ "folder/usr/", "", "folder/usr/" },
|
||||
t{ "/folder/usr/", "", "/folder/usr/" },
|
||||
t{ "\\folder/usr/", "", "\\folder/usr/" },
|
||||
};
|
||||
for ( const auto& combination : combinations ) {
|
||||
str path, name, result;
|
||||
std::tie(path, name, result) = combination;
|
||||
REQUIRE(path::replace_filename(path, name) == result);
|
||||
}
|
||||
}
|
||||
{
|
||||
// [path, extension, result]
|
||||
using t = std::tuple<str, str, str>;
|
||||
const vector<t> combinations = {
|
||||
t{"", "", ""},
|
||||
t{"/", "", "/"},
|
||||
t{"\\", "", "\\"},
|
||||
t{"C:", "", "C:"},
|
||||
t{".", "", "."},
|
||||
t{"..", "", ".."},
|
||||
|
||||
t{".jpg", "png", ".png"},
|
||||
t{".jpg", ".png", ".png"},
|
||||
|
||||
t{".jpg/", "png", ".jpg/.png"},
|
||||
t{".jpg/", ".png", ".jpg/.png"},
|
||||
|
||||
t{"file", "png", "file.png"},
|
||||
t{"file.", "png", "file.png"},
|
||||
t{"file.jpg", "png", "file.png"},
|
||||
|
||||
t{"file/", "png", "file/.png"},
|
||||
t{"file./", "png", "file./.png"},
|
||||
t{"file.jpg/", "png", "file.jpg/.png"},
|
||||
|
||||
t{"/file", "png", "/file.png"},
|
||||
t{"/file.", "png", "/file.png"},
|
||||
t{"/file.jpg", "png", "/file.png"},
|
||||
|
||||
t{"/file/", "png", "/file/.png"},
|
||||
t{"/file./", "png", "/file./.png"},
|
||||
t{"/file.jpg/", "png", "/file.jpg/.png"},
|
||||
|
||||
t{"folder.jpg/file", "png", "folder.jpg/file.png"},
|
||||
t{"folder.jpg/file.", "png", "folder.jpg/file.png"},
|
||||
t{"folder.jpg/file.jpg", "png", "folder.jpg/file.png"},
|
||||
};
|
||||
for ( const auto& combination : combinations ) {
|
||||
str path, extension, result;
|
||||
std::tie(path, extension, result) = combination;
|
||||
REQUIRE(path::replace_extension(path, extension) == result);
|
||||
}
|
||||
}
|
||||
{
|
||||
// [path, result]
|
||||
using t = std::tuple<str, str>;
|
||||
const vector<t> combinations = {
|
||||
t{"usr/local/", "usr/local"},
|
||||
t{"usr///local///", "usr///local"},
|
||||
|
||||
t{"usr/local", "usr"},
|
||||
t{"usr///local", "usr"},
|
||||
|
||||
t{"usr/", "usr"},
|
||||
t{"usr///", "usr"},
|
||||
|
||||
t{"usr", ""},
|
||||
t{"", ""},
|
||||
|
||||
t{"/usr/local/", "/usr/local"},
|
||||
t{"/usr/local///", "/usr/local"},
|
||||
|
||||
t{"/usr/local", "/usr"},
|
||||
t{"/usr///local", "/usr"},
|
||||
|
||||
t{"/usr/", "/usr"},
|
||||
t{"/usr///", "/usr"},
|
||||
|
||||
t{"/usr", ""},
|
||||
t{"///usr", ""},
|
||||
|
||||
t{"/", ""},
|
||||
t{"//", ""},
|
||||
t{"///", ""},
|
||||
|
||||
//
|
||||
// incorrect but expected
|
||||
//
|
||||
|
||||
t{"C:/windows", "C:"},
|
||||
t{"C:/", "C:"},
|
||||
t{"C:", ""},
|
||||
|
||||
t{"\\\\.\\COM", "\\\\."},
|
||||
t{"\\\\.", ""},
|
||||
|
||||
t{"\\\\?\\UNC", "\\\\?"},
|
||||
t{"\\\\?", ""},
|
||||
|
||||
t{"\\\\?\\C:\\", "\\\\?\\C:"},
|
||||
t{"\\\\?\\C:", "\\\\?"},
|
||||
};
|
||||
for ( const auto& combination : combinations ) {
|
||||
str path, result;
|
||||
std::tie(path, result) = combination;
|
||||
REQUIRE(path::parent_path(path) == result);
|
||||
}
|
||||
}
|
||||
}
|
||||
SECTION("files") {
|
||||
{
|
||||
auto f = make_write_file("files_test", false);
|
||||
REQUIRE(f);
|
||||
REQUIRE(f->path() == "files_test");
|
||||
REQUIRE(f->tell() == 0);
|
||||
REQUIRE(f->write("hello", 5) == 5);
|
||||
REQUIRE(f->tell() == 5);
|
||||
}
|
||||
{
|
||||
auto f = make_read_file("files_test");
|
||||
REQUIRE(f);
|
||||
REQUIRE(f->path() == "files_test");
|
||||
REQUIRE(f->tell() == 0);
|
||||
REQUIRE(f->length() == 5);
|
||||
char buf[5] = {'\0'};
|
||||
REQUIRE(f->read(buf, 5) == 5);
|
||||
REQUIRE(f->tell() == 5);
|
||||
REQUIRE(std::memcmp(buf, "hello", 5) == 0);
|
||||
}
|
||||
{
|
||||
auto f = make_write_file("files_test", true);
|
||||
REQUIRE(f);
|
||||
REQUIRE(f->path() == "files_test");
|
||||
REQUIRE(f->tell() == 5);
|
||||
REQUIRE(f->write("world", 5) == 5);
|
||||
REQUIRE(f->tell() == 10);
|
||||
}
|
||||
{
|
||||
auto f = make_read_file("files_test");
|
||||
REQUIRE(f);
|
||||
REQUIRE(f->path() == "files_test");
|
||||
REQUIRE(f->tell() == 0);
|
||||
REQUIRE(f->length() == 10);
|
||||
char buf[10] = {'\0'};
|
||||
REQUIRE(f->read(buf, 10) == 10);
|
||||
REQUIRE(f->tell() == 10);
|
||||
REQUIRE(std::memcmp(buf, "helloworld", 10) == 0);
|
||||
}
|
||||
}
|
||||
SECTION("filesystem") {
|
||||
{
|
||||
const str_view child_dir_name = "test_filesystem_file_name";
|
||||
REQUIRE(filesystem::remove_file(child_dir_name));
|
||||
REQUIRE_FALSE(filesystem::exists(child_dir_name));
|
||||
|
||||
REQUIRE(filesystem::create_file(child_dir_name));
|
||||
REQUIRE(filesystem::exists(child_dir_name));
|
||||
REQUIRE(filesystem::file_exists(child_dir_name));
|
||||
|
||||
buffer data("hello", 5);
|
||||
REQUIRE(filesystem::try_write_all(data, child_dir_name, false));
|
||||
{
|
||||
buffer d1;
|
||||
REQUIRE(filesystem::try_read_all(d1, child_dir_name));
|
||||
REQUIRE(data == d1);
|
||||
}
|
||||
REQUIRE(filesystem::create_file(child_dir_name));
|
||||
REQUIRE(filesystem::file_exists(child_dir_name));
|
||||
{
|
||||
buffer d1;
|
||||
REQUIRE(filesystem::try_read_all(d1, child_dir_name));
|
||||
REQUIRE(data == d1);
|
||||
}
|
||||
REQUIRE(filesystem::remove_file(child_dir_name));
|
||||
REQUIRE_FALSE(filesystem::exists(child_dir_name));
|
||||
}
|
||||
{
|
||||
const str child_dir_name = "test_filesystem_child_dir";
|
||||
const str parent_dir_path = "test_filesystem_parent_dir";
|
||||
const str child_dir_path = path::combine(parent_dir_path, child_dir_name);
|
||||
REQUIRE(filesystem::remove(child_dir_path));
|
||||
REQUIRE(filesystem::remove(parent_dir_path));
|
||||
REQUIRE_FALSE(filesystem::exists(child_dir_path));
|
||||
REQUIRE_FALSE(filesystem::exists(parent_dir_path));
|
||||
REQUIRE_FALSE(filesystem::file_exists(child_dir_path));
|
||||
REQUIRE_FALSE(filesystem::file_exists(parent_dir_path));
|
||||
REQUIRE_FALSE(filesystem::directory_exists(child_dir_path));
|
||||
REQUIRE_FALSE(filesystem::directory_exists(parent_dir_path));
|
||||
|
||||
REQUIRE_FALSE(filesystem::create_directory(child_dir_path));
|
||||
REQUIRE_FALSE(filesystem::exists(child_dir_path));
|
||||
|
||||
REQUIRE(filesystem::create_directory(parent_dir_path));
|
||||
|
||||
REQUIRE(filesystem::exists(parent_dir_path));
|
||||
REQUIRE(filesystem::directory_exists(parent_dir_path));
|
||||
REQUIRE_FALSE(filesystem::file_exists(parent_dir_path));
|
||||
|
||||
REQUIRE(filesystem::create_directory(child_dir_path));
|
||||
|
||||
REQUIRE(filesystem::exists(child_dir_path));
|
||||
REQUIRE(filesystem::directory_exists(child_dir_path));
|
||||
REQUIRE_FALSE(filesystem::file_exists(child_dir_path));
|
||||
|
||||
REQUIRE_FALSE(filesystem::remove(parent_dir_path));
|
||||
|
||||
REQUIRE(filesystem::remove_directory(child_dir_path));
|
||||
REQUIRE(filesystem::remove_directory(parent_dir_path));
|
||||
REQUIRE_FALSE(filesystem::exists(child_dir_path));
|
||||
REQUIRE_FALSE(filesystem::exists(parent_dir_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
169
untests/sources/untests_utils/image.cpp
Normal file
169
untests/sources/untests_utils/image.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("images") {
|
||||
{
|
||||
image i;
|
||||
REQUIRE(i.size() == v2u::zero());
|
||||
REQUIRE(i.format() == image_data_format::unknown);
|
||||
REQUIRE(i.data().empty());
|
||||
REQUIRE(i.empty());
|
||||
}
|
||||
{
|
||||
image i(v2u::zero(), image_data_format::unknown);
|
||||
REQUIRE(i.size() == v2u::zero());
|
||||
REQUIRE(i.format() == image_data_format::unknown);
|
||||
REQUIRE(i.data().empty());
|
||||
REQUIRE(i.empty());
|
||||
}
|
||||
{
|
||||
image i(v2u::zero(), image_data_format::unknown, buffer());
|
||||
REQUIRE(i.size() == v2u::zero());
|
||||
REQUIRE(i.format() == image_data_format::unknown);
|
||||
REQUIRE(i.data().empty());
|
||||
REQUIRE(i.empty());
|
||||
}
|
||||
{
|
||||
const buffer b{};
|
||||
image i(v2u::zero(), image_data_format::unknown, b);
|
||||
REQUIRE(i.size() == v2u::zero());
|
||||
REQUIRE(i.format() == image_data_format::unknown);
|
||||
REQUIRE(i.data().empty());
|
||||
REQUIRE(i.empty());
|
||||
}
|
||||
{
|
||||
REQUIRE_THROWS_AS(image(v2u::zero(), image_data_format::g8), bad_image_data_format);
|
||||
REQUIRE_THROWS_AS(image(v2u::zero(), image_data_format::ga8), bad_image_data_format);
|
||||
REQUIRE_THROWS_AS(image(v2u::zero(), image_data_format::rgb8), bad_image_data_format);
|
||||
REQUIRE_THROWS_AS(image(v2u::zero(), image_data_format::rgba8), bad_image_data_format);
|
||||
|
||||
REQUIRE(image(v2u::unit(), image_data_format::g8).data().size() == 1);
|
||||
REQUIRE(image(v2u::unit(), image_data_format::ga8).data().size() == 2);
|
||||
REQUIRE(image(v2u::unit(), image_data_format::rgb8).data().size() == 3);
|
||||
REQUIRE(image(v2u::unit(), image_data_format::rgba8).data().size() == 4);
|
||||
|
||||
REQUIRE_THROWS_AS(image(v2u::unit() * 1u, image_data_format::dxt1), bad_image_data_format);
|
||||
REQUIRE_THROWS_AS(image(v2u::unit() * 2u, image_data_format::dxt1), bad_image_data_format);
|
||||
REQUIRE_THROWS_AS(image(v2u::unit() * 3u, image_data_format::dxt1), bad_image_data_format);
|
||||
REQUIRE(image(v2u::unit() * 4u, image_data_format::dxt1).data().size() == 8);
|
||||
REQUIRE(image(v2u::unit() * 4u, image_data_format::dxt5).data().size() == 16);
|
||||
}
|
||||
{
|
||||
const u8 img[] = {1,2,3,4,5,6,7,8};
|
||||
image i0(v2u(2,2), image_data_format::g8, {img,4});
|
||||
image i1(v2u(2,1), image_data_format::ga8, {img,4});
|
||||
image i2(v2u(1,2), image_data_format::ga8, {img,4});
|
||||
image i3(v2u(2,1), image_data_format::rgb8, {img,6});
|
||||
image i4(v2u(1,2), image_data_format::rgb8, {img,6});
|
||||
image i5(v2u(2,1), image_data_format::rgba8, {img,8});
|
||||
image i6(v2u(1,2), image_data_format::rgba8, {img,8});
|
||||
REQUIRE(i0.format() == image_data_format::g8);
|
||||
REQUIRE(i1.format() == image_data_format::ga8);
|
||||
REQUIRE(i2.format() == image_data_format::ga8);
|
||||
REQUIRE(i3.format() == image_data_format::rgb8);
|
||||
REQUIRE(i4.format() == image_data_format::rgb8);
|
||||
REQUIRE(i5.format() == image_data_format::rgba8);
|
||||
REQUIRE(i6.format() == image_data_format::rgba8);
|
||||
}
|
||||
{
|
||||
REQUIRE(filesystem::remove_file("image_save_test.jpg"));
|
||||
REQUIRE(filesystem::remove_file("image_save_test.png"));
|
||||
REQUIRE(filesystem::remove_file("image_save_test.tga"));
|
||||
const u8 img_data[] = {255,0,0, 0,255,0, 0,0,255};
|
||||
image img(v2u(3,1), image_data_format::rgb8, buffer(img_data, sizeof(img_data)));
|
||||
REQUIRE(images::try_save_image(
|
||||
img,
|
||||
image_file_format::jpg,
|
||||
make_write_file("image_save_test.jpg", false)));
|
||||
REQUIRE(filesystem::file_exists("image_save_test.jpg"));
|
||||
REQUIRE(images::try_save_image(
|
||||
img,
|
||||
image_file_format::png,
|
||||
make_write_file("image_save_test.png", false)));
|
||||
REQUIRE(filesystem::file_exists("image_save_test.png"));
|
||||
REQUIRE(images::try_save_image(
|
||||
img,
|
||||
image_file_format::tga,
|
||||
make_write_file("image_save_test.tga", false)));
|
||||
REQUIRE(filesystem::file_exists("image_save_test.tga"));
|
||||
}
|
||||
{
|
||||
image img;
|
||||
REQUIRE(images::try_load_image(img, make_read_file("image_save_test.jpg")));
|
||||
REQUIRE(img.size() == v2u(3,1));
|
||||
REQUIRE(img.format() == image_data_format::rgb8);
|
||||
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 10u));
|
||||
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 10u));
|
||||
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 10u));
|
||||
|
||||
REQUIRE(images::try_load_image(img, make_read_file("image_save_test.png")));
|
||||
REQUIRE(img.size() == v2u(3,1));
|
||||
REQUIRE(img.format() == image_data_format::rgb8);
|
||||
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0u));
|
||||
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0u));
|
||||
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0u));
|
||||
|
||||
REQUIRE(images::try_load_image(img, make_read_file("image_save_test.tga")));
|
||||
REQUIRE(img.size() == v2u(3,1));
|
||||
REQUIRE(img.format() == image_data_format::rgb8);
|
||||
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0u));
|
||||
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0u));
|
||||
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0u));
|
||||
}
|
||||
{
|
||||
REQUIRE(filesystem::remove_file("image_save_test.jpg"));
|
||||
REQUIRE(filesystem::remove_file("image_save_test.png"));
|
||||
REQUIRE(filesystem::remove_file("image_save_test.tga"));
|
||||
const u8 img_data[] = {255,0,0, 0,255,0, 0,0,255};
|
||||
image img(v2u(3,1), image_data_format::rgb8, buffer(img_data, sizeof(img_data)));
|
||||
buffer buf;
|
||||
REQUIRE(images::try_save_image(
|
||||
img,
|
||||
image_file_format::jpg,
|
||||
buf));
|
||||
REQUIRE(filesystem::try_write_all(buf, "image_save_test.jpg", false));
|
||||
REQUIRE(images::try_save_image(
|
||||
img,
|
||||
image_file_format::png,
|
||||
buf));
|
||||
REQUIRE(filesystem::try_write_all(buf, "image_save_test.png", false));
|
||||
REQUIRE(images::try_save_image(
|
||||
img,
|
||||
image_file_format::tga,
|
||||
buf));
|
||||
REQUIRE(filesystem::try_write_all(buf, "image_save_test.tga", false));
|
||||
}
|
||||
{
|
||||
image img;
|
||||
buffer buf;
|
||||
REQUIRE(filesystem::try_read_all(buf, "image_save_test.jpg"));
|
||||
REQUIRE(images::try_load_image(img, buf));
|
||||
REQUIRE(img.size() == v2u(3,1));
|
||||
REQUIRE(img.format() == image_data_format::rgb8);
|
||||
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 10));
|
||||
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 10));
|
||||
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 10));
|
||||
|
||||
REQUIRE(filesystem::try_read_all(buf, "image_save_test.png"));
|
||||
REQUIRE(images::try_load_image(img, buf));
|
||||
REQUIRE(img.size() == v2u(3,1));
|
||||
REQUIRE(img.format() == image_data_format::rgb8);
|
||||
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0));
|
||||
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0));
|
||||
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0));
|
||||
|
||||
REQUIRE(filesystem::try_read_all(buf, "image_save_test.tga"));
|
||||
REQUIRE(images::try_load_image(img, buf));
|
||||
REQUIRE(img.size() == v2u(3,1));
|
||||
REQUIRE(img.format() == image_data_format::rgb8);
|
||||
REQUIRE(math::approximately(img.pixel32(0,0), color32::red(), 0));
|
||||
REQUIRE(math::approximately(img.pixel32(1,0), color32::green(), 0));
|
||||
REQUIRE(math::approximately(img.pixel32(2,0), color32::blue(), 0));
|
||||
}
|
||||
}
|
||||
138
untests/sources/untests_utils/jobber.cpp
Normal file
138
untests/sources/untests_utils/jobber.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("jobber") {
|
||||
{
|
||||
jobber j(1);
|
||||
auto pv0 = j.async([](){
|
||||
throw exception();
|
||||
});
|
||||
REQUIRE_THROWS_AS(pv0.get(), exception);
|
||||
}
|
||||
{
|
||||
i32 v5 = 5;
|
||||
|
||||
jobber j(1);
|
||||
auto pv0 = j.async([](i32 v){
|
||||
REQUIRE(v == 5);
|
||||
throw exception();
|
||||
}, v5);
|
||||
REQUIRE_THROWS_AS(pv0.get(), exception);
|
||||
|
||||
auto pv1 = j.async([](int& v){
|
||||
REQUIRE(v == 5);
|
||||
return v != 5
|
||||
? 0
|
||||
: throw exception();
|
||||
}, std::ref(v5));
|
||||
REQUIRE_THROWS_AS(pv1.get(), exception);
|
||||
|
||||
auto pv3 = j.async([](int& v){
|
||||
v = 4;
|
||||
return v;
|
||||
}, std::ref(v5));
|
||||
REQUIRE(pv3.get() == v5);
|
||||
REQUIRE(v5 == 4);
|
||||
}
|
||||
{
|
||||
jobber j(1);
|
||||
auto p0 = j.async([](rad<f32> angle){
|
||||
return math::sin(angle);
|
||||
}, math::pi<f32>());
|
||||
auto p1 = j.async([](rad<f32> angle){
|
||||
return math::cos(angle);
|
||||
}, math::two_pi<f32>());
|
||||
REQUIRE(math::approximately(p0.get(), 0.f));
|
||||
REQUIRE(math::approximately(p1.get(), 1.f));
|
||||
}
|
||||
{
|
||||
jobber j(1);
|
||||
j.pause();
|
||||
jobber::priority max_priority = jobber::priority::highest;
|
||||
j.async([](){
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
});
|
||||
for ( std::size_t i = 0; i < 10; ++i ) {
|
||||
jobber::priority p = static_cast<jobber::priority>(
|
||||
i % static_cast<std::size_t>(jobber::priority::highest));
|
||||
j.async(p, [&max_priority](jobber::priority priority) {
|
||||
REQUIRE(priority <= max_priority);
|
||||
max_priority = priority;
|
||||
}, p);
|
||||
}
|
||||
j.resume();
|
||||
j.wait_all();
|
||||
}
|
||||
{
|
||||
jobber j(2);
|
||||
jobber g(2);
|
||||
|
||||
std::vector<std::future<f32>> jp(50);
|
||||
for ( std::size_t i = 0; i < jp.size(); ++i ) {
|
||||
jp[i] = j.async([&g](){
|
||||
std::vector<std::future<f32>> gp(50);
|
||||
for ( std::size_t ii = 0; ii < gp.size(); ++ii ) {
|
||||
gp[ii] = g.async([](rad<f32> angle){
|
||||
return math::sin(angle);
|
||||
}, make_rad(ii).cast_to<f32>());
|
||||
}
|
||||
return std::accumulate(gp.begin(), gp.end(), 0.f,
|
||||
[](f32 r, std::future<f32>& f){
|
||||
return r + f.get();
|
||||
});
|
||||
});
|
||||
}
|
||||
f32 r0 = std::accumulate(jp.begin(), jp.end(), 0.f,
|
||||
[](f32 r, std::future<f32>& f){
|
||||
return r + f.get();
|
||||
});
|
||||
f32 r1 = 0.f;
|
||||
for ( std::size_t i = 0; i < 50; ++i ) {
|
||||
r1 += math::sin(make_rad(i).cast_to<f32>());
|
||||
}
|
||||
REQUIRE(math::approximately(r0, r1 * 50.f));
|
||||
}
|
||||
SECTION("performance") {
|
||||
std::printf("-= jobber::performance tests =-\n");
|
||||
#if defined(E2D_BUILD_MODE) && E2D_BUILD_MODE == E2D_BUILD_MODE_DEBUG
|
||||
const std::size_t task_n = 100'000;
|
||||
#else
|
||||
const std::size_t task_n = 1'000'000;
|
||||
#endif
|
||||
const auto big_task = [](std::size_t b, std::size_t n){
|
||||
f32 result = 0.f;
|
||||
for ( std::size_t i = 0; i < n; ++i ) {
|
||||
result += math::sin(make_rad(b + i).cast_to<f32>());
|
||||
result += math::cos(make_rad(b + i).cast_to<f32>());
|
||||
}
|
||||
return result;
|
||||
};
|
||||
const auto multi_task = [&](u32 threads, std::size_t tasks){
|
||||
e2d_untests::verbose_profiler_ms p(
|
||||
str("cores: ") + std::to_string(threads) +
|
||||
str(", tasks: ") + std::to_string(tasks));
|
||||
jobber j(threads);
|
||||
std::vector<std::future<f32>> rs(tasks);
|
||||
for ( std::size_t i = 0; i < tasks; ++i ) {
|
||||
const std::size_t n = task_n / tasks;
|
||||
rs[i] = j.async(big_task, i * n, n);
|
||||
}
|
||||
f32 result = std::accumulate(rs.begin(), rs.end(), 0.f,
|
||||
[](f32 r, std::future<f32>& f){
|
||||
return r + f.get();
|
||||
});
|
||||
p.done(result);
|
||||
};
|
||||
for ( u32 i = 1; i <= 4; ++i ) {
|
||||
multi_task(i, 10);
|
||||
multi_task(i, 100);
|
||||
multi_task(i, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
106
untests/sources/untests_utils/streams.cpp
Normal file
106
untests/sources/untests_utils/streams.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("streams") {
|
||||
buffer hello_data("hello", 5);
|
||||
{
|
||||
input_stream_uptr s = make_memory_stream(hello_data);
|
||||
REQUIRE_THROWS_AS(s->seek(-10, false), bad_stream_operation);
|
||||
REQUIRE(s->tell() == 0);
|
||||
REQUIRE_THROWS_AS(s->seek(10, false), bad_stream_operation);
|
||||
REQUIRE_THROWS_AS(
|
||||
s->seek(std::numeric_limits<std::ptrdiff_t>::min(), false),
|
||||
bad_stream_operation);
|
||||
REQUIRE_THROWS_AS(
|
||||
s->seek(std::numeric_limits<std::ptrdiff_t>::max(), false),
|
||||
bad_stream_operation);
|
||||
REQUIRE(s->tell() == 0);
|
||||
REQUIRE((s->seek(5, false) == 5 && s->tell() == 5));
|
||||
REQUIRE((s->seek(3, false) == 3 && s->tell() == 3));
|
||||
REQUIRE((s->seek(0, false) == 0 && s->tell() == 0));
|
||||
}
|
||||
{
|
||||
input_stream_uptr s = make_memory_stream(hello_data);
|
||||
REQUIRE((s->seek(3, false) == 3 && s->tell() == 3));
|
||||
REQUIRE_THROWS_AS(s->seek(3, true), bad_stream_operation);
|
||||
REQUIRE(s->tell() == 3);
|
||||
REQUIRE((s->seek(2, true) == 5 && s->tell() == 5));
|
||||
REQUIRE_THROWS_AS(s->seek(-6, true), bad_stream_operation);
|
||||
REQUIRE(s->tell() == 5);
|
||||
REQUIRE((s->seek(-5, true) == 0 && s->tell() == 0));
|
||||
}
|
||||
{
|
||||
input_stream_uptr s = make_memory_stream(hello_data);
|
||||
REQUIRE((s->seek(3, false) == 3 && s->tell() == 3));
|
||||
REQUIRE_THROWS_AS(s->seek(-4, true), bad_stream_operation);
|
||||
REQUIRE(s->tell() == 3);
|
||||
REQUIRE((s->seek(-3, true) == 0 && s->tell() == 0));
|
||||
REQUIRE_THROWS_AS(s->seek(6, true), bad_stream_operation);
|
||||
REQUIRE(s->tell() == 0);
|
||||
REQUIRE((s->seek(5, true) == 5 && s->tell() == 5));
|
||||
}
|
||||
{
|
||||
input_stream_uptr s = make_memory_stream(hello_data);
|
||||
REQUIRE(s->tell() == 0);
|
||||
REQUIRE(s->length() == 5);
|
||||
char buf[5] = {'\0'};
|
||||
REQUIRE(s->read(buf, 5) == 5);
|
||||
REQUIRE(std::memcmp(buf, "hello", 5) == 0);
|
||||
REQUIRE(s->tell() == 5);
|
||||
REQUIRE(s->length() == 5);
|
||||
}
|
||||
{
|
||||
input_stream_uptr s = make_memory_stream(hello_data);
|
||||
char buf[10] = {'\0'};
|
||||
{
|
||||
REQUIRE(s->read(buf, 0) == 0);
|
||||
REQUIRE(std::memcmp(buf, "\0\0\0\0\0\0\0\0\0\0", 10) == 0);
|
||||
REQUIRE(s->tell() == 0);
|
||||
REQUIRE(s->length() == 5);
|
||||
}
|
||||
{
|
||||
REQUIRE(s->read(buf, 10) == 5);
|
||||
REQUIRE(std::memcmp(buf, "hello\0\0\0\0\0", 10) == 0);
|
||||
REQUIRE(s->tell() == 5);
|
||||
REQUIRE(s->length() == 5);
|
||||
}
|
||||
{
|
||||
REQUIRE(s->seek(2, false) == 2);
|
||||
REQUIRE(s->read(buf, 10) == 3);
|
||||
REQUIRE(std::memcmp(buf, "llolo\0\0\0\0\0", 10) == 0);
|
||||
REQUIRE(s->tell() == 5);
|
||||
REQUIRE(s->length() == 5);
|
||||
}
|
||||
{
|
||||
REQUIRE(s->seek(0, false) == 0);
|
||||
REQUIRE(s->read(buf, 3) == 3);
|
||||
REQUIRE(std::memcmp(buf, "hello\0\0\0\0\0", 10) == 0);
|
||||
REQUIRE(s->tell() == 3);
|
||||
REQUIRE(s->length() == 5);
|
||||
}
|
||||
}
|
||||
{
|
||||
input_stream_uptr s = make_memory_stream(hello_data);
|
||||
char buf[10] = {'\0'};
|
||||
{
|
||||
REQUIRE(s->read(buf, 1) == 1);
|
||||
REQUIRE(std::memcmp(buf, "h\0\0\0\0\0\0\0\0\0", 10) == 0);
|
||||
REQUIRE(s->tell() == 1);
|
||||
REQUIRE(s->length() == 5);
|
||||
}
|
||||
{
|
||||
buffer b;
|
||||
REQUIRE(streams::try_read_tail(b, s));
|
||||
REQUIRE(b.size() == 4);
|
||||
REQUIRE(std::memcmp(b.data(), "ello", 4) == 0);
|
||||
REQUIRE(s->tell() == 5);
|
||||
REQUIRE(s->length() == 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
158
untests/sources/untests_utils/strfmts.cpp
Normal file
158
untests/sources/untests_utils/strfmts.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("strfmts") {
|
||||
{
|
||||
char buf[2];
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", v2i::zero()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", v3i::zero()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", v4i::zero()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", m2i::identity()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", m3i::identity()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", m4i::identity()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_rad(10)), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_deg(10)), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", v2f::zero()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", v3f::zero()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", v4f::zero()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", m2f::identity()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", m3f::identity()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", m4f::identity()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_rad(10.f)), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_deg(10.0)), strings::bad_format_buffer);
|
||||
}
|
||||
{
|
||||
char buf[2];
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", color::white()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", color32::white()), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_seconds(0)), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_milliseconds(0)), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_microseconds(0)), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_seconds(0.f)), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_milliseconds(0.f)), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(
|
||||
strings::format(buf, sizeof(buf), "%0", make_microseconds(0.f)), strings::bad_format_buffer);
|
||||
}
|
||||
{
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
v2u(1,2)) == "(1,2)");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(v3u(1,2,3), 3)) == "( 1, 2, 3)");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(v4i(1,2,3,4), 2)) == "( 1, 2, 3, 4)");
|
||||
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
v2f(1.f,2.f)) == "(1.000000,2.000000)");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(v3d(1,2,3), 5, 2)) == "( 1.00, 2.00, 3.00)");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(v4f(1,2,3,4),0,1)) == "(1.0,2.0,3.0,4.0)");
|
||||
}
|
||||
{
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(m2i(1,2,3,4), 3)) ==
|
||||
"(( 1, 2),( 3, 4))");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(m3i(1,2,3,4,5,6,7,8,9), 1)) ==
|
||||
"((1,2,3),(4,5,6),(7,8,9))");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(m4i(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16), 2)) ==
|
||||
"(( 1, 2, 3, 4),( 5, 6, 7, 8),( 9,10,11,12),(13,14,15,16))");
|
||||
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(m2f(1,2,3,4), 5, 2)) ==
|
||||
"(( 1.00, 2.00),( 3.00, 4.00))");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(m3f(1,2,3,4,5,6,7,8,9), 4, 1)) ==
|
||||
"(( 1.0, 2.0, 3.0),( 4.0, 5.0, 6.0),( 7.0, 8.0, 9.0))");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(m4f(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16), 2,0)) ==
|
||||
"(( 1, 2, 3, 4),( 5, 6, 7, 8),( 9,10,11,12),(13,14,15,16))");
|
||||
}
|
||||
{
|
||||
REQUIRE(strings::rformat("%0", make_rad(2)) == "2rad");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(make_rad(2.f), 5, 2)) ==
|
||||
" 2.00rad");
|
||||
|
||||
REQUIRE(strings::rformat("%0", make_deg(3)) == "3deg");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(make_deg(2.0), 5, 2)) ==
|
||||
" 2.00deg");
|
||||
}
|
||||
{
|
||||
str utf8 = make_utf8("hello");
|
||||
wstr wide = make_wide("hello");
|
||||
str16 utf16 = make_utf16("hello");
|
||||
str32 utf32 = make_utf32("hello");
|
||||
|
||||
REQUIRE(strings::rformat("%0", utf8) == utf8);
|
||||
REQUIRE(strings::rformat("%0", wide) == utf8);
|
||||
REQUIRE(strings::rformat("%0", utf16) == utf8);
|
||||
REQUIRE(strings::rformat("%0", utf32) == utf8);
|
||||
}
|
||||
{
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(color(1.f,2.f,3.f,4.f), 0, 2)) ==
|
||||
"(1.00,2.00,3.00,4.00)");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(color32(1,2,3,4), 2)) == "( 1, 2, 3, 4)");
|
||||
}
|
||||
{
|
||||
REQUIRE(strings::rformat("%0", make_seconds(2)) == "2s");
|
||||
REQUIRE(strings::rformat("%0", make_milliseconds(3)) == "3ms");
|
||||
REQUIRE(strings::rformat("%0", make_microseconds(4)) == "4us");
|
||||
|
||||
REQUIRE(strings::rformat("%0", make_seconds(2.f)) == "2.000000s");
|
||||
REQUIRE(strings::rformat("%0", make_milliseconds(3.0)) == "3.000000ms");
|
||||
REQUIRE(strings::rformat(
|
||||
"%0",
|
||||
strings::make_format_arg(make_microseconds(4.f), 5, 2)) ==
|
||||
" 4.00us");
|
||||
}
|
||||
}
|
||||
444
untests/sources/untests_utils/strings.cpp
Normal file
444
untests/sources/untests_utils/strings.cpp
Normal file
@@ -0,0 +1,444 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("strings") {
|
||||
{
|
||||
REQUIRE(make_utf8("hello") == "hello");
|
||||
REQUIRE(make_utf8(L"hello") == "hello");
|
||||
REQUIRE(make_utf8(u"hello") == "hello");
|
||||
REQUIRE(make_utf8(U"hello") == "hello");
|
||||
|
||||
REQUIRE(make_wide("hello") == L"hello");
|
||||
REQUIRE(make_wide(L"hello") == L"hello");
|
||||
REQUIRE(make_wide(u"hello") == L"hello");
|
||||
REQUIRE(make_wide(U"hello") == L"hello");
|
||||
|
||||
REQUIRE(make_utf16("hello") == u"hello");
|
||||
REQUIRE(make_utf16(L"hello") == u"hello");
|
||||
REQUIRE(make_utf16(u"hello") == u"hello");
|
||||
REQUIRE(make_utf16(U"hello") == u"hello");
|
||||
|
||||
REQUIRE(make_utf32("hello") == U"hello");
|
||||
REQUIRE(make_utf32(L"hello") == U"hello");
|
||||
REQUIRE(make_utf32(u"hello") == U"hello");
|
||||
REQUIRE(make_utf32(U"hello") == U"hello");
|
||||
}
|
||||
{
|
||||
const char* null_utf8 = nullptr;
|
||||
const wchar_t* null_wide = nullptr;
|
||||
const char16_t* null_utf16 = nullptr;
|
||||
const char32_t* null_utf32 = nullptr;
|
||||
|
||||
REQUIRE(make_utf8(str_view(null_utf8, 0)) == make_utf8(u""));
|
||||
REQUIRE(make_utf8(wstr_view(null_wide, 0)) == make_utf8(L""));
|
||||
REQUIRE(make_utf8(str16_view(null_utf16, 0)) == make_utf8(u""));
|
||||
REQUIRE(make_utf8(str32_view(null_utf32, 0)) == make_utf8(U""));
|
||||
|
||||
REQUIRE(make_wide(str_view(null_utf8, 0)) == make_wide(u""));
|
||||
REQUIRE(make_wide(wstr_view(null_wide, 0)) == make_wide(L""));
|
||||
REQUIRE(make_wide(str16_view(null_utf16, 0)) == make_wide(u""));
|
||||
REQUIRE(make_wide(str32_view(null_utf32, 0)) == make_wide(U""));
|
||||
|
||||
REQUIRE(make_utf16(str_view(null_utf8, 0)) == make_utf16(u""));
|
||||
REQUIRE(make_utf16(wstr_view(null_wide, 0)) == make_utf16(L""));
|
||||
REQUIRE(make_utf16(str16_view(null_utf16, 0)) == make_utf16(u""));
|
||||
REQUIRE(make_utf16(str32_view(null_utf32, 0)) == make_utf16(U""));
|
||||
|
||||
REQUIRE(make_utf32(str_view(null_utf8, 0)) == make_utf32(u""));
|
||||
REQUIRE(make_utf32(wstr_view(null_wide, 0)) == make_utf32(L""));
|
||||
REQUIRE(make_utf32(str16_view(null_utf16, 0)) == make_utf32(u""));
|
||||
REQUIRE(make_utf32(str32_view(null_utf32, 0)) == make_utf32(U""));
|
||||
}
|
||||
{
|
||||
using strings::wildcard_match;
|
||||
|
||||
char invalid_utf[] = "\xe6\x97\xa5\xd1\x88\xfa";
|
||||
REQUIRE_THROWS_AS(wildcard_match(invalid_utf, "???"), std::exception);
|
||||
|
||||
const auto mark_string = [](const char* str) -> str_view {
|
||||
static char string_buf[1024] = {0};
|
||||
std::memset(string_buf, '*', sizeof(string_buf));
|
||||
const std::size_t str_len = std::strlen(str);
|
||||
E2D_ASSERT(str_len < sizeof(string_buf));
|
||||
std::memcpy(string_buf, str, str_len);
|
||||
return str_view(string_buf, str_len);
|
||||
};
|
||||
|
||||
const auto mark_pattern = [](const char* str) -> str_view {
|
||||
static char pattern_buf[1024] = {0};
|
||||
std::memset(pattern_buf, '*', sizeof(pattern_buf));
|
||||
const std::size_t str_len = std::strlen(str);
|
||||
E2D_ASSERT(str_len < sizeof(pattern_buf));
|
||||
std::memcpy(pattern_buf, str, str_len);
|
||||
return str_view(pattern_buf, str_len);
|
||||
};
|
||||
|
||||
// 你好!
|
||||
REQUIRE(wildcard_match(u8"\u4F60\u597D!", u8"\u4F60\u597D!") == true);
|
||||
REQUIRE(wildcard_match(u8"\u4F60\u597D!", u8"?\u597D!") == true);
|
||||
REQUIRE(wildcard_match(u8"\u4F60\u597D!", u8"\u4F60?!") == true);
|
||||
|
||||
REQUIRE(wildcard_match(
|
||||
// 你好你好你好你好世界世界世界世界世界世界世界世界彡ಠ
|
||||
mark_string("\u4F60\u597D\u4F60\u597D\u4F60\u597D\u4F60\u597D\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u4E16\u754C\u5F61\u0CA0"),
|
||||
// 你好你好你好你好*世界世界彡*ಠ
|
||||
mark_pattern("\u4F60\u597D\u4F60\u597D\u4F60\u597D\u4F60\u597D*\u4E16\u754C\u4E16\u754C\u5F61*\u0CA0")) == true);
|
||||
|
||||
REQUIRE(wildcard_match("", "") == true);
|
||||
REQUIRE(wildcard_match("a", "") == false);
|
||||
REQUIRE(wildcard_match("", "*") == true);
|
||||
REQUIRE(wildcard_match("", "?") == false);
|
||||
|
||||
const char* null_utf8 = nullptr;
|
||||
str_view null_view = {null_utf8, 0};
|
||||
REQUIRE(wildcard_match(null_view, null_view) == true);
|
||||
REQUIRE(wildcard_match("a", null_view) == false);
|
||||
REQUIRE(wildcard_match(null_view, "*") == true);
|
||||
REQUIRE(wildcard_match(null_view, "?") == false);
|
||||
|
||||
// tests source:
|
||||
// http://developforperformance.com/MatchingWildcards_AnImprovedAlgorithmForBigData.html
|
||||
|
||||
REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("ab*d")) == false);
|
||||
|
||||
REQUIRE(wildcard_match(mark_string("abcccd"), mark_pattern("*ccd")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("mississipissippi"), mark_pattern("*issip*ss*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("xxxx*zzzzzzzzy*f"), mark_pattern("xxxx*zzy*fffff")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("xxxx*zzzzzzzzy*f"), mark_pattern("xxx*zzy*f")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("xxxxzzzzzzzzyf"), mark_pattern("xxxx*zzy*fffff")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("xxxxzzzzzzzzyf"), mark_pattern("xxxx*zzy*f")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("xyxyxyzyxyz"), mark_pattern("xy*z*xyz")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("mississippi"), mark_pattern("*sip*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("xyxyxyxyz"), mark_pattern("xy*xyz")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("mississippi"), mark_pattern("mi*sip*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("ababac"), mark_pattern("*abac*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("ababac"), mark_pattern("*abac*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("aaazz"), mark_pattern("a*zz*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("a12b12"), mark_pattern("*12*23")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("a12b12"), mark_pattern("a12b")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("a12b12"), mark_pattern("*12*12*")) == true);
|
||||
|
||||
REQUIRE(wildcard_match(mark_string("caaab"), mark_pattern("*a?b")) == true);
|
||||
|
||||
REQUIRE(wildcard_match(mark_string("*"), mark_pattern("*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("a*abab"), mark_pattern("a*b")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("a*r"), mark_pattern("a*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("a*ar"), mark_pattern("a*aar")) == false);
|
||||
|
||||
REQUIRE(wildcard_match(mark_string("XYXYXYZYXYz"), mark_pattern("XY*Z*XYz")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("missisSIPpi"), mark_pattern("*SIP*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("mississipPI"), mark_pattern("*issip*PI")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("xyxyxyxyz"), mark_pattern("xy*xyz")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("miSsissippi"), mark_pattern("mi*sip*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("miSsissippi"), mark_pattern("mi*Sip*")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("abAbac"), mark_pattern("*Abac*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("abAbac"), mark_pattern("*Abac*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("aAazz"), mark_pattern("a*zz*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("A12b12"), mark_pattern("*12*23")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("a12B12"), mark_pattern("*12*12*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("oWn"), mark_pattern("*oWn*")) == true);
|
||||
|
||||
REQUIRE(wildcard_match(mark_string("bLah"), mark_pattern("bLah")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("bLah"), mark_pattern("bLaH")) == false);
|
||||
|
||||
REQUIRE(wildcard_match(mark_string("a"), mark_pattern("*?")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("ab"), mark_pattern("*?")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("*?")) == true);
|
||||
|
||||
REQUIRE(wildcard_match(mark_string("a"), mark_pattern("??")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("ab"), mark_pattern("?*?")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("ab"), mark_pattern("*?*?*")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("?**?*?")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("?**?*&?")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("abcd"), mark_pattern("?b*??")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("abcd"), mark_pattern("?a*??")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("abcd"), mark_pattern("?**?c?")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("abcd"), mark_pattern("?**?d?")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("abcde"), mark_pattern("?*b*?*d*?")) == true);
|
||||
|
||||
REQUIRE(wildcard_match(mark_string("bLah"), mark_pattern("bL?h")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("bLaaa"), mark_pattern("bLa?")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("bLah"), mark_pattern("bLa?")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("bLaH"), mark_pattern("?Lah")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("bLaH"), mark_pattern("?LaH")) == true);
|
||||
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"),
|
||||
mark_pattern("a*a*a*a*a*a*aa*aaa*a*a*b")) == true);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab"),
|
||||
mark_pattern("*a*b*ba*ca*a*aa*aaa*fa*ga*b*")) == true);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab"),
|
||||
mark_pattern("*a*b*ba*ca*a*x*aaa*fa*ga*b*")) == false);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab"),
|
||||
mark_pattern("*a*b*ba*ca*aaaa*fa*ga*gggg*b*")) == false);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("abababababababababababababababababababaacacacacacacacadaeafagahaiajakalaaaaaaaaaaaaaaaaaffafagaagggagaaaaaaaab"),
|
||||
mark_pattern("*a*b*ba*ca*aaaa*fa*ga*ggg*b*")) == true);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("aaabbaabbaab"),
|
||||
mark_pattern("*aabbaa*a*")) == true);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*"),
|
||||
mark_pattern("a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*")) == true);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("aaaaaaaaaaaaaaaaa"),
|
||||
mark_pattern("*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*")) == true);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("aaaaaaaaaaaaaaaa"),
|
||||
mark_pattern("*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*")) == false);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("abc*abcd*abcde*abcdef*abcdefg*abcdefgh*abcdefghi*abcdefghij*abcdefghijk*abcdefghijkl*abcdefghijklm*abcdefghijklmn"),
|
||||
mark_pattern("abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*")) == false);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("abc*abcd*abcde*abcdef*abcdefg*abcdefgh*abcdefghi*abcdefghij*abcdefghijk*abcdefghijkl*abcdefghijklm*abcdefghijklmn"),
|
||||
mark_pattern("abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*")) == true);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("abc*abcd*abcd*abc*abcd"),
|
||||
mark_pattern("abc*abc*abc*abc*abc")) == false);
|
||||
REQUIRE(wildcard_match(
|
||||
mark_string("abc*abcd*abcd*abc*abcd*abcd*abc*abcd*abc*abc*abcd"),
|
||||
mark_pattern("abc*abc*abc*abc*abc*abc*abc*abc*abc*abc*abcd")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("********a********b********c********")) == true);
|
||||
REQUIRE(wildcard_match(mark_string("********a********b********c********"), mark_pattern("abc")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("abc"), mark_pattern("********a********b********b********")) == false);
|
||||
REQUIRE(wildcard_match(mark_string("*abc*"), mark_pattern("***a*b*c***")) == true);
|
||||
}
|
||||
{
|
||||
char buf[6];
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), nullptr), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::format(buf, 0, "hello"), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(strings::format(nullptr, sizeof(buf), "hello"), strings::bad_format_buffer);
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "helloE"), strings::bad_format_buffer);
|
||||
REQUIRE_NOTHROW(strings::format(buf, sizeof(buf), "hello"));
|
||||
REQUIRE_NOTHROW(strings::format(nullptr, 0, "hello"));
|
||||
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%hell"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%ll"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "hell%"), strings::bad_format);
|
||||
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%10%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "hell%10%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%10%hell"), strings::bad_format);
|
||||
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%x%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "hell%y%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%z%hell"), strings::bad_format);
|
||||
}
|
||||
{
|
||||
REQUIRE_THROWS_AS(strings::rformat(nullptr), strings::bad_format);
|
||||
|
||||
REQUIRE_THROWS_AS(strings::rformat("%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::rformat("%hell"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::rformat("he%ll"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::rformat("hell%"), strings::bad_format);
|
||||
|
||||
REQUIRE_THROWS_AS(strings::rformat("%10%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::rformat("hell%10%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::rformat("%10%hell"), strings::bad_format);
|
||||
|
||||
REQUIRE_THROWS_AS(strings::rformat("%x%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::rformat("hell%y%"), strings::bad_format);
|
||||
REQUIRE_THROWS_AS(strings::rformat("%z%hell"), strings::bad_format);
|
||||
}
|
||||
{
|
||||
char buf[1];
|
||||
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE(strings::format(buf, sizeof(buf), "") == 0);
|
||||
REQUIRE(str(buf) == str(""));
|
||||
}
|
||||
{
|
||||
char buf[6];
|
||||
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE(strings::format(buf, sizeof(buf), "hello") == 5);
|
||||
REQUIRE(strings::format(nullptr, 0, "hello") == 5);
|
||||
REQUIRE(str(buf) == str("hello"));
|
||||
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE(strings::format(buf, sizeof(buf), "hell%%") == 5);
|
||||
REQUIRE(strings::format(nullptr, 0, "hell%%") == 5);
|
||||
REQUIRE(str(buf) == str("hell%"));
|
||||
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE(strings::format(buf, sizeof(buf), "%%hell") == 5);
|
||||
REQUIRE(strings::format(nullptr, 0, "%%hell") == 5);
|
||||
REQUIRE(str(buf) == str("%hell"));
|
||||
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE(strings::format(buf, sizeof(buf), "he%%ll") == 5);
|
||||
REQUIRE(strings::format(nullptr, 0, "he%%ll") == 5);
|
||||
REQUIRE(str(buf) == str("he%ll"));
|
||||
}
|
||||
{
|
||||
char buf[5];
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "hello"), strings::bad_format_buffer);
|
||||
REQUIRE(str(buf) == str("hell"));
|
||||
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%"), strings::bad_format);
|
||||
REQUIRE(str(buf) == str("he"));
|
||||
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%99%"), strings::bad_format);
|
||||
REQUIRE(str(buf) == str("he"));
|
||||
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%x%"), strings::bad_format);
|
||||
REQUIRE(str(buf) == str("he"));
|
||||
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "he%0%", 1234), strings::bad_format_buffer);
|
||||
REQUIRE(str(buf) == str("he12"));
|
||||
}
|
||||
{
|
||||
char buf[10];
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%0%", "hello world"), strings::bad_format_buffer);
|
||||
REQUIRE(str(buf) == str("hello wor"));
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "test%0%", "hello world"), strings::bad_format_buffer);
|
||||
REQUIRE(str(buf) == str("testhello"));
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "%0%test", "hello world"), strings::bad_format_buffer);
|
||||
REQUIRE(str(buf) == str("hello wor"));
|
||||
std::memset(buf, 0xAB, sizeof(buf));
|
||||
REQUIRE_THROWS_AS(strings::format(buf, sizeof(buf), "te%0%st", "hello world"), strings::bad_format_buffer);
|
||||
REQUIRE(str(buf) == str("tehello w"));
|
||||
}
|
||||
{
|
||||
REQUIRE(strings::rformat("%0 %1 %2", "hello", "world", 5) == str("hello world 5"));
|
||||
REQUIRE(strings::rformat("%1 %0 %2", "hello", "world", 5) == str("world hello 5"));
|
||||
REQUIRE(strings::rformat("%2 %1 %0", "hello", "world", 5) == str("5 world hello"));
|
||||
REQUIRE(strings::rformat("%0 %0 %1", "hello", "world", 5) == str("hello hello world"));
|
||||
REQUIRE(strings::rformat("%2 %1 %1", "hello", "world", 5) == str("5 world world"));
|
||||
|
||||
REQUIRE(
|
||||
strings::rformat(
|
||||
"%0 %2 %1 %4 %3 %6 %7 %5 %8 %9",
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9) ==
|
||||
str("0 2 1 4 3 6 7 5 8 9"));
|
||||
}
|
||||
{
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(-5, 3)) == " -5");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(-5, 4)) == " -5");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(21, 1)) == "21");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(21, 2)) == "21");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(42, 3)) == " 42");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(42u, 3)) == " 42");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f)) == "1.230000");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,0)) == "1.230000");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,0,2)) == "1.23");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,5,2)) == " 1.23");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(true)) == "true");
|
||||
REQUIRE(strings::rformat("%0", strings::make_format_arg(false)) == "false");
|
||||
|
||||
const char* s0 = "hello";
|
||||
char s1[64];
|
||||
std::strcpy(s1, "world");
|
||||
REQUIRE(strings::rformat("%0", s0) == "hello");
|
||||
REQUIRE(strings::rformat("%0", s1) == "world");
|
||||
}
|
||||
{
|
||||
REQUIRE(
|
||||
strings::rformat("%0", std::numeric_limits<i8>::max()) ==
|
||||
std::to_string(std::numeric_limits<i8>::max()));
|
||||
REQUIRE(
|
||||
strings::rformat("%0", std::numeric_limits<i8>::min()) ==
|
||||
std::to_string(std::numeric_limits<i8>::min()));
|
||||
REQUIRE(
|
||||
strings::rformat("%0", std::numeric_limits<i64>::max()) ==
|
||||
std::to_string(std::numeric_limits<i64>::max()));
|
||||
REQUIRE(
|
||||
strings::rformat("%0", std::numeric_limits<i64>::min()) ==
|
||||
std::to_string(std::numeric_limits<i64>::min()));
|
||||
REQUIRE(
|
||||
strings::rformat("%0", std::numeric_limits<u64>::max()) ==
|
||||
std::to_string(std::numeric_limits<u64>::max()));
|
||||
REQUIRE(
|
||||
strings::rformat("%0", std::numeric_limits<f32>::max()) ==
|
||||
std::to_string(std::numeric_limits<f32>::max()));
|
||||
REQUIRE(
|
||||
strings::rformat("%0", std::numeric_limits<f32>::min()) ==
|
||||
std::to_string(std::numeric_limits<f32>::min()));
|
||||
REQUIRE(
|
||||
strings::rformat("%0", std::numeric_limits<f64>::max()) ==
|
||||
std::to_string(std::numeric_limits<f64>::max()));
|
||||
REQUIRE(
|
||||
strings::rformat("%0", std::numeric_limits<f64>::min()) ==
|
||||
std::to_string(std::numeric_limits<f64>::min()));
|
||||
}
|
||||
SECTION("performance") {
|
||||
std::printf("-= strings::performance tests =-\n");
|
||||
#if defined(E2D_BUILD_MODE) && E2D_BUILD_MODE == E2D_BUILD_MODE_DEBUG
|
||||
const std::size_t task_n = 100'000;
|
||||
#else
|
||||
const std::size_t task_n = 1'000'000;
|
||||
#endif
|
||||
{
|
||||
std::size_t result = 0;
|
||||
e2d_untests::verbose_profiler_ms p("format(int, int)");
|
||||
for ( std::size_t i = 0; i < task_n; ++i ) {
|
||||
char buffer[128];
|
||||
result += strings::format(buffer, sizeof(buffer), "hello %0 world %1 !", 1000, 123);
|
||||
}
|
||||
p.done(result);
|
||||
}
|
||||
{
|
||||
std::ptrdiff_t result = 0;
|
||||
e2d_untests::verbose_profiler_ms p("snprintf(int, int)");
|
||||
for ( std::size_t i = 0; i < task_n; ++i ) {
|
||||
char buffer[128];
|
||||
result += std::snprintf(buffer, sizeof(buffer), "hello %i world %i !", 1000, 123);
|
||||
}
|
||||
p.done(result);
|
||||
}
|
||||
{
|
||||
std::ptrdiff_t result = 0;
|
||||
e2d_untests::verbose_profiler_ms p("format(float, float)");
|
||||
for ( std::size_t i = 0; i < task_n; ++i ) {
|
||||
char buffer[128];
|
||||
result += strings::format(buffer, sizeof(buffer), "hello %0 world %1 !", 1000.f, 123.f);
|
||||
}
|
||||
p.done(result);
|
||||
}
|
||||
{
|
||||
std::ptrdiff_t result = 0;
|
||||
e2d_untests::verbose_profiler_ms p("snprintf(float, float)");
|
||||
for ( std::size_t i = 0; i < task_n; ++i ) {
|
||||
char buffer[128];
|
||||
result += std::snprintf(buffer, sizeof(buffer), "hello %f world %f !", 1000.0, 123.0);
|
||||
}
|
||||
p.done(result);
|
||||
}
|
||||
{
|
||||
std::ptrdiff_t result = 0;
|
||||
e2d_untests::verbose_profiler_ms p("format(const char*)");
|
||||
for ( std::size_t i = 0; i < task_n; ++i ) {
|
||||
char buffer[128];
|
||||
result += strings::format(buffer, sizeof(buffer), "hello %0 world %1 !", "foo", "bar");
|
||||
}
|
||||
p.done(result);
|
||||
}
|
||||
{
|
||||
std::ptrdiff_t result = 0;
|
||||
e2d_untests::verbose_profiler_ms p("snprintf(const char*)");
|
||||
for ( std::size_t i = 0; i < task_n; ++i ) {
|
||||
char buffer[128];
|
||||
result += std::snprintf(buffer, sizeof(buffer), "hello %s world %s !", "foo", "bar");
|
||||
}
|
||||
p.done(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
56
untests/sources/untests_utils/time.cpp
Normal file
56
untests/sources/untests_utils/time.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
TEST_CASE("time") {
|
||||
{
|
||||
REQUIRE(time::second<i32>().value == 1);
|
||||
REQUIRE(time::minute<i32>().value == 60);
|
||||
REQUIRE(time::hour<i32>().value == 60 * 60);
|
||||
}
|
||||
{
|
||||
REQUIRE(make_seconds(1).convert_to<milliseconds_tag>().value == 1000);
|
||||
REQUIRE(make_seconds(1).convert_to<microseconds_tag>().value == 1000000);
|
||||
|
||||
REQUIRE(make_milliseconds(1000).convert_to<seconds_tag>().value == 1);
|
||||
REQUIRE(make_milliseconds(1000).convert_to<microseconds_tag>().value == 1000000);
|
||||
|
||||
REQUIRE(make_microseconds(1000000).convert_to<seconds_tag>().value == 1);
|
||||
REQUIRE(make_microseconds(1000000).convert_to<milliseconds_tag>().value == 1000);
|
||||
}
|
||||
{
|
||||
namespace ch = std::chrono;
|
||||
|
||||
REQUIRE(time::to_chrono(make_seconds<i8>(2)) == ch::seconds(2));
|
||||
REQUIRE(time::to_chrono(make_milliseconds<i8>(3)) == ch::milliseconds(3));
|
||||
REQUIRE(time::to_chrono(make_microseconds<i8>(4)) == ch::microseconds(4));
|
||||
|
||||
REQUIRE(time::to_chrono(make_seconds<u8>(2)) == ch::seconds(2));
|
||||
REQUIRE(time::to_chrono(make_milliseconds<u8>(3)) == ch::milliseconds(3));
|
||||
REQUIRE(time::to_chrono(make_microseconds<u8>(4)) == ch::microseconds(4));
|
||||
}
|
||||
{
|
||||
REQUIRE(time::to_seconds(make_seconds(2)).value == 2);
|
||||
REQUIRE(time::to_milliseconds(make_seconds(2)).value == 2000);
|
||||
REQUIRE(time::to_microseconds(make_seconds(2)).value == 2000000);
|
||||
|
||||
REQUIRE(time::to_seconds(make_milliseconds(2000)).value == 2);
|
||||
REQUIRE(time::to_milliseconds(make_milliseconds(2000)).value == 2000);
|
||||
REQUIRE(time::to_microseconds(make_milliseconds(2000)).value == 2000000);
|
||||
|
||||
REQUIRE(time::to_seconds(make_microseconds(2000000)).value == 2);
|
||||
REQUIRE(time::to_milliseconds(make_microseconds(2000000)).value == 2000);
|
||||
REQUIRE(time::to_microseconds(make_microseconds(2000000)).value == 2000000);
|
||||
}
|
||||
{
|
||||
auto b = time::now<milliseconds_tag>();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
auto e = time::now<milliseconds_tag>();
|
||||
REQUIRE(e > b);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user