initial basic utils

This commit is contained in:
2018-08-06 17:58:51 +07:00
parent 769c1f151a
commit 2ce653eacc
61 changed files with 7431 additions and 70 deletions

View File

@@ -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()
#

View File

@@ -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`

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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);
}
}}

View File

@@ -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>;
}

View File

@@ -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
//

View File

@@ -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"

View File

@@ -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;
};
}

View 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;
}

View 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;
}}

View 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;
}}

View 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;
}}

View 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;
}}

View 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();
}
}

View 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;
}}

View 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_)));
}
};
}}

View 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"

View 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);
}
}}

View 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>();
}
}}

View File

@@ -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)

View File

@@ -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

View File

@@ -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 ../..

View File

@@ -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

View File

@@ -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 ../..

View File

@@ -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

View 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);
}
}

View 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);
}
}}

View 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));
}
}}

View 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));
}
}}

View 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

View 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

View 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);
}}}

View 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

View 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

View 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);
}
}}

View 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;
}}}

View 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;
}
}}}

View 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;
}
}}}

View 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);
}
}}}

View 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;
}
}}}

View 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;
}
}}}

View 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;
}
}}}

View 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_;
}
}
}

View 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;
}
}
}}

View 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));
}
}}

View File

@@ -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)

View File

@@ -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>;
}

View 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"));
}
}

View 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));
}
}
}

View 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)));
}
}

View 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));
}
}

View 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));
}
}
}

View 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));
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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");
}
}

View 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);
}
}
}

View 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);
}
}