diff --git a/public/amtl/am-algorithm.h b/public/amtl/am-algorithm.h new file mode 100644 index 00000000..ef206bbc --- /dev/null +++ b/public/amtl/am-algorithm.h @@ -0,0 +1,58 @@ +// vim: set sts=8 ts=2 sw=2 tw=99 et: +// +// Copyright (C) 2013-2014, David Anderson and AlliedModders LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of AlliedModders LLC nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#ifndef _include_amtl_algorithm_h_ +#define _include_amtl_algorithm_h_ + +#include + +namespace ke { + +template static inline T +Min(const T &t1, const T &t2) +{ + return t1 < t2 ? t1 : t2; +} + +template static inline T +Max(const T &t1, const T &t2) +{ + return t1 > t2 ? t1 : t2; +} + +template static inline void +Swap(T &left, T &right) +{ + T tmp(Move(left)); + left = Move(right); + right = Move(tmp); +} + +} // namespace ke + +#endif // _include_amtl_algorithm_h_ diff --git a/public/amtl/am-arithmetic.h b/public/amtl/am-arithmetic.h new file mode 100644 index 00000000..bd31e6c6 --- /dev/null +++ b/public/amtl/am-arithmetic.h @@ -0,0 +1,67 @@ +// vim: set sts=8 ts=2 sw=2 tw=99 et: +// +// Copyright (C) 2013-2014, David Anderson and AlliedModders LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of AlliedModders LLC nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#ifndef _include_amtl_bits_h_ +#define _include_amtl_bits_h_ + +#include + +namespace ke { + +static inline bool +TryUint64Multiply(uint64_t left, uint64_t right, uint64_t *out) +{ + uint64_t r = left * right; + if (r != 0 && ((r / left) != right)) + return false; + + *out = r; + return true; +} + +static inline bool +TryUint32Add(uint32_t left, uint32_t right, uint32_t *out) +{ + if (left + right < Max(left, right)) + return false; + *out = left + right; + return true; +} + +static inline bool +TryUint64Add(uint64_t left, uint64_t right, uint64_t *out) +{ + if (left + right < Max(left, right)) + return false; + *out = left + right; + return true; +} + +} // namespace ke + +#endif // _include_amtl_bits_h_ diff --git a/public/amtl/am-atomics.h b/public/amtl/am-atomics.h index a9926d92..c8f2ffad 100644 --- a/public/amtl/am-atomics.h +++ b/public/amtl/am-atomics.h @@ -38,13 +38,69 @@ namespace ke { extern "C" { long __cdecl _InterlockedIncrement(long volatile *dest); long __cdecl _InterlockedDecrement(long volatile *dest); - long __cdecl _InterlockedIncrement64(long long volatile *dest); - long __cdecl _InterlockedDecrement64(long long volatile *dest); + long long __cdecl _InterlockedIncrement64(long long volatile *dest); + long long __cdecl _InterlockedDecrement64(long long volatile *dest); + long __cdecl _InterlockedCompareExchange(long volatile *dest, long exchange, long comparand); +# if _MSC_VER > 1600 || (_MSC_VER == 1600 && !defined(_M_IX86)) + void * __cdecl _InterlockedCompareExchangePointer( + void * volatile *Destination, + void * Exchange, + void * Comparand + ); +#else + static inline void * _InterlockedCompareExchangePointer( + void * volatile *Destination, + void * Exchange, + void * Comparand) + { + return (void *)_InterlockedCompareExchange((long volatile *)Destination, (long)Exchange, (long)Comparand); + } +#endif } # pragma intrinsic(_InterlockedIncrement) # pragma intrinsic(_InterlockedDecrement) -# pragma intrinsic(_InterlockedIncrement64) -# pragma intrinsic(_InterlockedDecrement64) +# pragma intrinsic(_InterlockedCompareExchange) +# if _MSC_VER > 1600 || (_MSC_VER == 1600 && !defined(_M_IX86)) +# pragma intrinsic(_InterlockedCompareExchangePointer) +# endif +# if defined(_WIN64) +# pragma intrinsic(_InterlockedIncrement64) +# pragma intrinsic(_InterlockedDecrement64) +# endif +#endif + +#if defined(__GNUC__) +# if defined(i386) || defined(__x86_64__) +# if defined(__clang__) + static inline void YieldProcessor() { asm("pause"); } +# else +# if KE_GCC_AT_LEAST(4, 7) +# define YieldProcessor() __builtin_ia32_pause() +# else + static inline void YieldProcessor() { asm("pause"); } +# endif +# endif +# else +# define YieldProcessor() +# endif +#elif defined(_MSC_VER) +# if !defined(YieldProcessor) +# define YieldProcessor _mm_pause +# endif +#endif + +#if defined(_MSC_VER) +static inline void * +CompareAndSwapPtr(void *volatile *Destination, void *Exchange, void *Comparand) +{ + return _InterlockedCompareExchangePointer(Destination, Exchange, Comparand); +} +#else +static inline void * +CompareAndSwapPtr(void *volatile *dest, void *newval, void *oldval) +{ + return __sync_val_compare_and_swap(dest, oldval, newval); +} #endif template @@ -104,13 +160,12 @@ struct AtomicOps<8> #endif }; - -class AtomicRefCount +class KE_LINK AtomicRefcount { typedef AtomicOps Ops; public: - AtomicRefCount(uintptr_t value) + AtomicRefcount(uintptr_t value) : value_(value) { } diff --git a/public/amtl/am-cxx.h b/public/amtl/am-cxx.h new file mode 100644 index 00000000..b761d963 --- /dev/null +++ b/public/amtl/am-cxx.h @@ -0,0 +1,213 @@ +// vim: set sts=8 ts=2 sw=2 tw=99 et: +// +// Copyright (C) 2014, David Anderson and AlliedModders LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of AlliedModders LLC nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#ifndef _include_amtl_cxx_support_h_ +#define _include_amtl_cxx_support_h_ + +#if defined(__clang__) +# if !(defined(__clang_major__) && defined(__clang_minor__)) +# define KE_CLANG_MAJOR 1 +# define KE_CLANG_MINOR __GNUC_MINOR__ +# else +# if defined(__apple_build_version__) && clang_major__ > 3 + // 4.0 => 3.1, 4.1 => 3.2 +# if __clang_major__ == 4 +# define KE_CLANG_MAJOR 3 +# if __clang_minor__ == 0 +# define KE_CLANG_MINOR 1 +# else +# define KE_CLANG_MINOR 2 +# endif + // 5.0 => 3.3, 5.1 => 3.4 +# elif __clang_major__ == 5 +# define KE_CLANG_MAJOR 3 +# if __clang_minor__ == 0 +# define KE_CLANG_MINOR 3 +# else +# define KE_CLANG_MINOR 4 +# endif +# elif __clang_major__ == 6 +# define KE_CLANG_MAJOR 3 +# define KE_CLANG_MINOR 5 +# endif +# endif +# if !defined(KE_CLANG_MAJOR) +# define KE_CLANG_MAJOR __clang_major__ +# endif +# if !defined(KE_CLANG_MINOR) +# define KE_CLANG_MINOR __clang_minor__ +# endif +# endif + +// Done with horrible clang version detection. +# define KE_CLANG_AT_LEAST(x, y) \ + ((__clang_major__ > (x)) || (__clang_major__ == x && __clang_minor__ >= y)) + +# if KE_CLANG_AT_LEAST(2, 9) +# define KE_CXX_HAS_RVAL_REFS 30 +# define KE_CXX_HAS_DELETE +# define KE_CXX_HAS_STATIC_ASSERT +# define KE_CXX_HAS_DOUBLE_GT +# define KE_CXX_HAS_ENUM_CLASS +# endif +# if KE_CLANG_AT_LEAST(3, 0) +# define KE_CXX_HAS_OVERRIDE +# define KE_CXX_HAS_EXPLICIT_BOOL +# define KE_CXX_HAS_NULLPTR +# define KE_CXX_HAS_NOEXCEPT +# endif +# if KE_CLANG_AT_LEAST(3, 1) +# define KE_CXX_HAS_CONSTEXPR +# endif + +#elif defined(__GNUC__) +# define KE_GCC_AT_LEAST(x, y) ((__GNUC__ > (x)) || (__GNUC__ == x && __GNUC_MINOR__ >= y)) + +# if KE_GCC_AT_LEAST(4, 3) +# define KE_CXX_HAS_RVAL_REFS 10 +# define KE_CXX_HAS_STATIC_ASSERT +# define KE_CXX_HAS_DOUBLE_GT +# endif +# if KE_GCC_AT_LEAST(4, 4) +# define KE_CXX_HAS_DELETE +# define KE_CXX_HAS_ENUM_CLASS +# endif +# if KE_GCC_AT_LEAST(4, 5) +# define KE_CXX_HAS_EXPLICIT_BOOL +# undef KE_CXX_HAS_RVAL_REFS +# define KE_CXX_HAS_RVAL_REFS 21 +# endif +# if KE_GCC_AT_LEAST(4, 6) +# define KE_CXX_HAS_NULLPTR +# define KE_CXX_HAS_NOEXCEPT +# define KE_CXX_HAS_CONSTEXPR +# undef KE_CXX_HAS_RVAL_REFS +# define KE_CXX_HAS_RVAL_REFS 30 +# endif +# if KE_GCC_AT_LEAST(4, 7) +# define KE_CXX_HAS_OVERRIDE +# endif + +#elif defined(_MSC_VER) +# if _MSC_VER >= 1600 +# define KE_CXX_HAS_RVAL_REFS 20 +# define KE_CXX_HAS_STATIC_ASSERT +# define KE_CXX_HAS_DOUBLE_GT +# define KE_CXX_HAS_NULLPTR +# endif +# if _MSC_VER >= 1700 +# undef KE_CXX_HAS_RVAL_REFS +# define KE_CXX_HAS_RVAL_REFS 21 +# define KE_CXX_HAS_OVERRIDE +# define KE_CXX_HAS_ENUM_CLASS +# endif +# if _MSC_VER >= 1800 +# define KE_CXX_HAS_DELETE +# define KE_CXX_HAS_EXPLICIT_BOOL +# endif +# if _MSC_VER == 1800 && _MSC_FULL_VER == 180021114 +# define KE_CXX_HAS_CONSTEXPR +# endif +#else +# error Unrecognized compiler. +#endif + +// Done with compiler feature detection. + +#if defined(KE_CXX_HAS_OVERRIDE) +# define KE_OVERRIDE override +#else +# define KE_OVERRIDE +#endif + +#if defined(KE_CXX_HAS_DELETE) +# define KE_DELETE = delete +#else +# define KE_DELETE +#endif + +#if defined(KE_CXX_HAS_NOEXCEPT) +# define KE_NOEXCEPT noexcept +#else +# define KE_NOEXCEPT +#endif + +#if defined(KE_CXX_HAS_CONSTEXPR) +# define KE_CONSTEXPR constexpr +#else +# define KE_CONSTEXPR +#endif + +#if defined(KE_CXX_HAS_STATIC_ASSERT) +# define KE_STATIC_ASSERT(cond) static_assert(cond, #cond) +#else +# define KE_STATIC_ASSERT(cond) extern int static_assert_f(int a[(cond) ? 1 : -1]) +#endif + +#if !defined(KE_CXX_HAS_RVAL_REFS) || KE_CXX_HAS_RVAL_REFS < 21 +//# error AMTL requires rvalue reference 2.1 support (N2844+) +#endif +#if !defined(KE_CXX_HAS_DOUBLE_GT) +# error AMTL requires support for >> in template names +#endif +#if !defined(KE_CXX_HAS_NULLPTR) +# if defined(__GNUC__) && !defined(__clang__) +# define nullptr __null +# define KE_CXX_HAS_NULLPTR +# else +# error AMTL requires nullptr support +# endif +#endif + +#define KE_DEFINE_ENUM_OPERATORS(EnumName) \ + static inline EnumName operator |(const EnumName &left, const EnumName &right) { \ + return EnumName(uint32_t(left) | uint32_t(right)); \ + } \ + static inline EnumName operator &(const EnumName &left, const EnumName &right) { \ + return EnumName(uint32_t(left) & uint32_t(right)); \ + } \ + static inline EnumName operator ^(const EnumName &left, const EnumName &right) { \ + return EnumName(uint32_t(left) ^ uint32_t(right)); \ + } \ + static inline EnumName operator ~(const EnumName &flags) { \ + return EnumName(~uint32_t(flags)); \ + } \ + static inline EnumName & operator |=(EnumName &left, const EnumName &right) { \ + return left = left | right; \ + } \ + static inline EnumName & operator &=(EnumName &left, const EnumName &right) { \ + return left = left & right; \ + } \ + static inline EnumName & operator ^=(EnumName &left, const EnumName &right) { \ + return left = left ^ right; \ + } \ + static inline bool operator !(const EnumName &obj) { \ + return uint32_t(obj) == 0; \ + } + +#endif // _include_amtl_cxx_support_h_ diff --git a/public/amtl/am-deque.h b/public/amtl/am-deque.h new file mode 100644 index 00000000..5fe513be --- /dev/null +++ b/public/amtl/am-deque.h @@ -0,0 +1,260 @@ +// vim: set sts=8 ts=2 sw=2 tw=99 et: +// +// Copyright (C) 2014, David Anderson and AlliedModders LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of AlliedModders LLC nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#ifndef _INCLUDE_KEIMA_TPL_CPP_DEQUE_H_ +#define _INCLUDE_KEIMA_TPL_CPP_DEQUE_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace ke { + +template +class Deque : public AllocPolicy +{ + static const size_t kInvalidIndex = ~size_t(0); + + public: + Deque(AllocPolicy = AllocPolicy()) + : buffer_(NULL), + maxlength_(0), + first_(0), + last_(0) + { + } + Deque(Deque &&other) + : buffer_(other.buffer_), + maxlength_(other.maxlength_), + first_(other.first_), + last_(other.last_) + { + other.reset(); + } + ~Deque() { + zap(); + } + + Deque &operator =(Deque &&other) { + zap(); + buffer_ = other.buffer_; + maxlength_ = other.maxlength_; + first_ = other.first_; + last_ = other.last_; + other.reset(); + } + + bool empty() const { + return first_ == last_; + } + + template + bool append(U &&other) { + size_t next = ensureCanAppend(); + if (next == kInvalidIndex) + return false; + new (&buffer_[last_]) T(ke::Forward(other)); + last_ = next; + return true; + } + + template + bool prepend(U &&other) { + size_t prev = ensureCanPrepend(); + if (prev == kInvalidIndex) + return false; + first_ = prev; + new (&buffer_[first_]) T(ke::Forward(other)); + return true; + } + + void popFront() { + assert(!empty()); + buffer_[first_].~T(); + if (first_ == maxlength_ - 1) + first_ = 0; + else + first_++; + } + void popBack() { + assert(!empty()); + if (last_ == 0) + last_ = maxlength_ - 1; + else + last_--; + buffer_[last_].~T(); + } + + T popFrontCopy() { + T t = front(); + popFront(); + return t; + } + T popBackCopy() { + T t = back(); + popBack(); + return t; + } + + const T &front() const { + assert(!empty()); + return buffer_[first_]; + } + T &front() { + assert(!empty()); + return buffer_[first_]; + } + const T &back() const { + assert(!empty()); + if (last_ == 0) + return buffer_[maxlength_ - 1]; + return buffer_[last_ - 1]; + } + T &back() { + assert(!empty()); + if (last_ == 0) + return buffer_[maxlength_ - 1]; + return buffer_[last_ - 1]; + } + + size_t length() const { + if (first_ == last_) + return 0; + return first_ < last_ + ? (last_ - first_) + : (last_ + (maxlength_ - first_)); + } + size_t capacity() const { + return maxlength_; + } + + private: + Deque(const Deque &other) KE_DELETE; + Deque &operator =(const Deque &other) KE_DELETE; + + // Return the next value of first_. + size_t ensureCanPrepend() { + if (first_ == 0) { + if (maxlength_ && (last_ != maxlength_ - 1)) + return maxlength_ - 1; + } else if (first_ - 1 != last_) { + return first_ - 1; + } + + // The ring is full. + if (!growByOne()) + return kInvalidIndex; + return maxlength_ - 1; + } + + // Return the next value of last_. + size_t ensureCanAppend() { + if (last_ < first_) { + if (last_ + 1 != first_) + return last_ + 1; + } else{ + if (last_ + 1 < maxlength_) + return last_ + 1; + if (first_ != 0) + return 0; + } + + // The ring is full. + if (!growByOne()) + return kInvalidIndex; + return last_ + 1; + } + + bool growByOne() { + if (!IsUintPtrMultiplySafe(maxlength_, 2)) { + this->reportAllocationOverflow(); + return false; + } + + size_t new_maxlength = maxlength_ ? maxlength_ * 2 : 8; + T *new_buffer = (T *)this->malloc(sizeof(T) * new_maxlength); + if (!new_buffer) + return false; + + // Move everything to the bottom of the new buffer, and reset our indices + // so that first is at 0. + if (first_ < last_) { + MoveRange(new_buffer, buffer_ + first_, last_ - first_); + last_ = last_ - first_; + first_ = 0; + } else { + MoveRange(new_buffer, buffer_ + first_, maxlength_ - first_); + MoveRange(new_buffer + (maxlength_ - first_), buffer_, last_); + last_ = last_ + (maxlength_ - first_); + first_ = 0; + } + this->free(buffer_); + + buffer_ = new_buffer; + maxlength_ = new_maxlength; + return true; + } + + void reset() { + buffer_ = NULL; + maxlength_ = 0; + first_ = 0; + last_ = 0; + } + + void zap() { + if (first_ < last_) { + for (size_t i = first_; i < last_; i++) + buffer_[i].~T(); + } else { + for (size_t i = first_; i < maxlength_; i++) + buffer_[i].~T(); + for (size_t i = 0; i < last_; i++) + buffer_[i].~T(); + } + this->free(buffer_); + } + + private: + T *buffer_; + size_t maxlength_; + + // Always points to the first readable item. + size_t first_; + + // Always points to where the next item can be appended. + size_t last_; +}; + +} + +#endif // _INCLUDE_KEIMA_TPL_CPP_DEQUE_H_ diff --git a/public/amtl/am-fixedarray.h b/public/amtl/am-fixedarray.h index 4681651d..f58efb8f 100644 --- a/public/amtl/am-fixedarray.h +++ b/public/amtl/am-fixedarray.h @@ -76,13 +76,17 @@ class FixedArray : public AllocPolicy assert(index < length()); return data_[index]; } - void set(size_t index, const T &t) { - assert(index < length()); - data_[index] = t; + T &back() { + assert(length() > 0); + return data_[length() - 1]; } - void set(size_t index, ke::Moveable t) { - assert(index < length()); - data_[index] = t; + const T &back() const { + assert(length() > 0); + return data_[length() - 1]; + } + + T *buffer() const { + return data_; } private: diff --git a/public/amtl/am-float.h b/public/amtl/am-float.h index e53f25a0..26588f9e 100644 --- a/public/amtl/am-float.h +++ b/public/amtl/am-float.h @@ -35,8 +35,55 @@ namespace ke { +static const uint32_t kFloat32ExponentMask = 0x7F800000; +static const uint64_t kFloat64ExponentMask = 0x7FFF000000000000ULL; + +struct float32_bits +{ + static const uint32_t kExponentMask = kFloat32ExponentMask; + + typedef uint32_t Bits; + + union layout { + uint32_t bits; + float value; + }; + + static layout to_layout(float value) { + layout impl; + impl.value = value; + return impl; + } +}; + +struct float64_bits +{ + static const uint64_t kExponentMask = kFloat64ExponentMask; + + typedef uint64_t Bits; + + union layout { + uint64_t bits; + float value; + }; + + static layout to_layout(float value) { + layout impl; + impl.value = value; + return impl; + } +}; + +template +struct float_bits; +template <> +struct float_bits : public float32_bits {}; +template <> +struct float_bits : public float64_bits {}; + +template static inline bool -IsNaN(double v) +IsNaN(T v) { #ifdef _MSC_VER return !!_isnan(v); @@ -45,6 +92,48 @@ IsNaN(double v) #endif } +template static inline bool +IsInfinite(T value) +{ + typedef float_bits Properties; + typedef typename Properties::Bits Bits; + Bits bits = Properties::to_layout(value).bits; + return (bits & Properties::kExponentMask) == Properties::kExponentMask; +}; + +// Performs the operation (x % y) where x and y are floating-point values. +// +// To compute a floating point modulus, this function returns "r", where r +// satisfies the following equation: +// +// x = (I * y) + r +// +// Where I is an integer <= x, and r is a value less than y. If no such +// integer I exists, the result is NaN. +// +// If x or y are NaN, the result is NaN. +// If x is +/-Infinity, the result is NaN. +// If y is 0, the result is NaN (as a divide by zero is implied). +// +// If y is Infinity, then r = x (and I = 0). +// If x is +/-0, then r = +/-0. +template static inline T +FloatModulo(T left, T right) +{ +#if defined(KE_WINDOWS) + // Windows fmod() does not follow the contract above, in that: + // 42 % Infinity => NaN, instead of 42, and + // -0 % -N => 0, instead of -0. + if ((!IsInfinite(left) && IsInfinite(right)) || + (left == 0 && !IsInfinite(right))) + { + return left; + } +#endif + + return fmod(left, right); +} + } // namespace ke #endif // _include_amtl_float_h_ diff --git a/public/amtl/am-hashmap.h b/public/amtl/am-hashmap.h index 7d5c3839..05d81d86 100644 --- a/public/amtl/am-hashmap.h +++ b/public/amtl/am-hashmap.h @@ -58,29 +58,20 @@ class HashMap : public AllocPolicy V value; Entry() - { - } - Entry(Moveable other) - : key(Moveable(other->key)), - value(Moveable(other->value)) - { - } + {} + Entry(const Entry &other) + : key(other.key), + value(other.value) + {} + Entry(Entry &&other) + : key(ke::Move(other.key)), + value(ke::Move(other.value)) + {} - Entry(const K &aKey, const V &aValue) - : key(aKey), - value(aValue) - { } - Entry(const K &aKey, Moveable aValue) - : key(aKey), - value(aValue) - { } - Entry(Moveable aKey, const V &aValue) - : key(aKey), - value(aValue) - { } - Entry(Moveable aKey, Moveable aValue) - : key(aKey), - value(aValue) + template + Entry(UK &&aKey, UV &&aValue) + : key(ke::Forward(aKey)), + value(ke::Forward(aValue)) { } }; @@ -137,24 +128,14 @@ class HashMap : public AllocPolicy // The map must not have been mutated in between findForAdd() and add(). // The Insert object is still valid after add() returns, however. - bool add(Insert &i, const K &key, const V &value) { - Entry entry(key, value); + template + bool add(Insert &i, UK &&key, UV &&value) { + Entry entry(ke::Forward(key), ke::Forward(value)); return table_.add(i, ke::Move(entry)); } - bool add(Insert &i, Moveable key, const V &value) { - Entry entry(key, value); - return table_.add(i, ke::Move(entry)); - } - bool add(Insert &i, const K &key, Moveable value) { - Entry entry(key, value); - return table_.add(i, ke::Move(entry)); - } - bool add(Insert &i, Moveable key, Moveable value) { - Entry entry(key, value); - return table_.add(i, ke::Move(entry)); - } - bool add(Insert &i, Moveable key) { - Entry entry(key, V()); + template + bool add(Insert &i, UK &&key) { + Entry entry(ke::Forward(key), V()); return table_.add(i, ke::Move(entry)); } diff --git a/public/amtl/am-hashset.h b/public/amtl/am-hashset.h index 13f8eb8f..48e185e7 100644 --- a/public/amtl/am-hashset.h +++ b/public/amtl/am-hashset.h @@ -27,8 +27,8 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -#ifndef _include_amtl_hashmap_h_ -#define _include_amtl_hashmap_h_ +#ifndef _include_amtl_hashset_h_ +#define _include_amtl_hashset_h_ #include @@ -76,6 +76,7 @@ class HashSet : public AllocPolicy typedef typename Internal::Result Result; typedef typename Internal::Insert Insert; + typedef typename Internal::iterator iterator; template Result find(const Lookup &key) { @@ -98,11 +99,9 @@ class HashSet : public AllocPolicy // The map must not have been mutated in between findForAdd() and add(). // The Insert object is still valid after add() returns, however. - bool add(Insert &i, const K &key) { - return table_.add(i, key); - } - bool add(Insert &i, Moveable key) { - return table_.add(i, key); + template + bool add(Insert &i, UK &&key) { + return table_.add(i, ke::Forward(key)); } // This can be used to avoid compiler constructed temporaries, since AMTL @@ -112,6 +111,9 @@ class HashSet : public AllocPolicy return table_.add(i); } + iterator iter() { + return iterator(&table_); + } void clear() { table_.clear(); } diff --git a/public/amtl/am-hashtable.h b/public/amtl/am-hashtable.h index 1101a247..7f6d4dfa 100644 --- a/public/amtl/am-hashtable.h +++ b/public/amtl/am-hashtable.h @@ -54,14 +54,9 @@ namespace detail { void setHash(uint32_t hash) { hash_ = hash; } - void construct() { - new (&t_) T(); - } - void construct(const T &t) { - new (&t_) T(t); - } - void construct(Moveable t) { - new (&t_) T(t); + template + void construct(U &&u) { + new (&t_) T(ke::Forward(u)); } uint32_t hash() const { return hash_; @@ -151,7 +146,7 @@ class HashTable : public AllocPolicy Entry *table = (Entry *)this->malloc(capacity * sizeof(Entry)); if (!table) - return NULL; + return nullptr; for (size_t i = 0; i < capacity; i++) table[i].initialize(); @@ -267,7 +262,7 @@ class HashTable : public AllocPolicy if (oldEntry.isLive()) { Insert p = insertUnique(oldEntry.hash()); p.entry().setHash(p.hash()); - p.entry().construct(Moveable(oldEntry.payload())); + p.entry().construct(ke::Move(oldEntry.payload())); } oldEntry.destruct(); } @@ -373,7 +368,7 @@ class HashTable : public AllocPolicy capacity_(0), nelements_(0), ndeleted_(0), - table_(NULL), + table_(nullptr), minCapacity_(kMinCapacity) { } @@ -385,7 +380,7 @@ class HashTable : public AllocPolicy this->free(table_); } - bool init(uint32_t capacity = 0) { + bool init(size_t capacity = 0) { if (capacity < kMinCapacity) { capacity = kMinCapacity; } else if (capacity > kMaxCapacity) { @@ -393,10 +388,10 @@ class HashTable : public AllocPolicy return false; } - minCapacity_ = capacity; + minCapacity_ = uint32_t(capacity); assert(IsPowerOfTwo(capacity)); - capacity_ = capacity; + capacity_ = uint32_t(capacity); table_ = createTable(capacity_); if (!table_) @@ -432,16 +427,11 @@ class HashTable : public AllocPolicy // The table must not have been mutated in between findForAdd() and add(). // The Insert object is still valid after add() returns, however. - bool add(Insert &i, const Payload &payload) { + template + bool add(Insert &i, U &&payload) { if (!internalAdd(i)) return false; - i.entry().construct(payload); - return true; - } - bool add(Insert &i, Moveable payload) { - if (!internalAdd(i)) - return false; - i.entry().construct(payload); + i.entry().construct(ke::Forward(payload)); return true; } bool add(Insert &i) { @@ -610,7 +600,7 @@ template <> inline uint32_t HashInteger<4>(uintptr_t value) { - return HashInt32(value); + return HashInt32(uint32_t(value)); } template <> diff --git a/public/amtl/am-inlinelist.h b/public/amtl/am-inlinelist.h index 99c37793..86e66aeb 100644 --- a/public/amtl/am-inlinelist.h +++ b/public/amtl/am-inlinelist.h @@ -32,6 +32,7 @@ #include #include +#include namespace ke { @@ -46,8 +47,8 @@ class InlineListNode public: InlineListNode() - : next_(NULL), - prev_(NULL) + : next_(nullptr), + prev_(nullptr) { } @@ -147,7 +148,7 @@ class InlineList remove(at.iter_); // Iterator is no longer valid. - at.iter_ = NULL; + at.iter_ = nullptr; return next; } @@ -161,8 +162,8 @@ class InlineList t->next_->prev_ = t->prev_; #if !defined(NDEBUG) - t->next_ = NULL; - t->prev_ = NULL; + t->next_ = nullptr; + t->prev_ = nullptr; #endif } diff --git a/public/amtl/am-linkedlist.h b/public/amtl/am-linkedlist.h index 488e97a2..0f2fa846 100644 --- a/public/amtl/am-linkedlist.h +++ b/public/amtl/am-linkedlist.h @@ -52,18 +52,8 @@ class LinkedList : public AllocPolicy public: friend class iterator; - class Node + struct Node { - public: - Node(const T &o) - : obj(o) - { - } - Node(Moveable o) - : obj(o) - { - } - T obj; Node *next; Node *prev; @@ -80,18 +70,14 @@ public: clear(); } - bool append(const T &obj) { - return insertBefore(end(), obj) != end(); - } - bool append(Moveable obj) { - return insertBefore(end(), obj) != end(); + template + bool append(U &&obj) { + return insertBefore(end(), ke::Forward(obj)) != end(); } - bool prepend(const T &obj) { - return insertBefore(begin(), obj) != begin(); - } - bool prepend(Moveable obj) { - return insertBefore(begin(), obj) != begin(); + template + bool prepend(U &&obj) { + return insertBefore(begin(), ke::Forward(obj)) != begin(); } size_t length() const { @@ -134,18 +120,12 @@ public: return sentinel_.address(); } - Node *allocNode(const T &obj) { + template + Node *allocNode(U &&obj) { Node *node = (Node *)this->malloc(sizeof(Node)); if (!node) - return NULL; - new (node) Node(obj); - return node; - } - Node *allocNode(Moveable obj) { - Node *node = (Node *)this->malloc(sizeof(Node)); - if (!node) - return NULL; - new (node) Node(obj); + return nullptr; + new (&node->obj) T(ke::Forward(obj)); return node; } @@ -165,7 +145,7 @@ public: public: iterator() - : this_(NULL) + : this_(nullptr) { } iterator(const LinkedList &src) @@ -269,11 +249,9 @@ public: return iter; } - iterator insertBefore(iterator where, const T &obj) { - return insert(where, allocNode(obj)); - } - iterator insertBefore(iterator where, Moveable obj) { - return insert(where, allocNode(obj)); + template + iterator insertBefore(iterator where, U &&obj) { + return insert(where, allocNode(ke::Forward(obj))); } public: diff --git a/public/amtl/am-moveable.h b/public/amtl/am-moveable.h index e500dbc7..2bae9754 100644 --- a/public/amtl/am-moveable.h +++ b/public/amtl/am-moveable.h @@ -30,42 +30,44 @@ #ifndef _include_amtl_moveable_h_ #define _include_amtl_moveable_h_ +#include + namespace ke { -// This is a feature in C++11, but since AM projects do not have access to -// C++11 yet, we provide templates to implement move semantics. A class can -// provide a constructor for (ke::Moveable t) which containers will try -// to use. -// -// When implementing a constructor that takes a Moveable, the object being -// moved should be left in a state that is safe, since its destructor will -// be called even though it has been moved. +// Previously, we implemented Move semantics without C++11. Now that we use +// C++11, we implement this as STL does for std::move. +template +static inline typename remove_reference::type && +Move(T &&t) +{ + return static_cast::type &&>(t); +} + +// std::forward replacement. See: +// http://thbecker.net/articles/rvalue_references/section_07.html and +// http://thbecker.net/articles/rvalue_references/section_08.html +template +static KE_CONSTEXPR inline T && +Forward(typename remove_reference::type &t) KE_NOEXCEPT +{ + return static_cast(t); +} template -struct Moveable +static KE_CONSTEXPR inline T && +Forward(typename remove_reference::type &&t) KE_NOEXCEPT { - public: - explicit Moveable(T &t) - : t_(t) - { - } - - T *operator ->() { - return &t_; - } - operator T &() { - return t_; - } - - private: - T &t_; -}; + return static_cast(t); +} template -static inline Moveable -Move(T &t) +static inline void +MoveRange(T *dest, T *src, size_t length) { - return Moveable(t); + for (size_t i = 0; i < length; i++) { + new (&dest[i]) T(ke::Move(src[i])); + src[i].~T(); + } } } // namespace ke diff --git a/public/amtl/am-platform.h b/public/amtl/am-platform.h new file mode 100644 index 00000000..f1cf38fd --- /dev/null +++ b/public/amtl/am-platform.h @@ -0,0 +1,66 @@ +// vim: set sts=8 ts=2 sw=2 tw=99 et: +// +// Copyright (C) 2013, David Anderson and AlliedModders LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of AlliedModders LLC nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +#ifndef _include_amtl_platform_h_ +#define _include_amtl_platform_h_ + +namespace ke { + +#if defined(__NetBSD__) +# define KE_NETBSD +# define KE_BSD +#elif defined(__FreeBSD__) +# define KE_FREEBSD +# define KE_BSD +#elif defined(__OpenBSD__) +# define KE_OPENBSD +# define KE_BSD +#elif defined(__APPLE__) +# define KE_MACOSX +# define KE_MACH +# define KE_BSD +#elif defined(__MACH__) +# define KE_MACH +# define KE_BSD +#elif defined(_WIN32) +# define KE_WINDOWS +#elif defined(__linux__) +# define KE_LINUX +# define KE_POSIX +#elif defined(__sun__) +# define KE_SOLARIS +# define KE_POSIX +#endif + +#if defined(KE_BSD) +# define KE_POSIX +#endif + +} // ke + +#endif // _include_amtl_platform_h_ diff --git a/public/amtl/am-refcounting-threadsafe.h b/public/amtl/am-refcounting-threadsafe.h index 785ba62b..7b37fe01 100644 --- a/public/amtl/am-refcounting-threadsafe.h +++ b/public/amtl/am-refcounting-threadsafe.h @@ -39,7 +39,7 @@ namespace ke { // identical, except changing the reference count is guaranteed to be atomic // with respect to other threads changing the reference count. template -class RefcountedThreadsafe +class KE_LINK RefcountedThreadsafe { public: RefcountedThreadsafe() @@ -63,7 +63,132 @@ class RefcountedThreadsafe } private: - AtomicRefCount refcount_; + AtomicRefcount refcount_; +}; + +// Use this to forward to ke::Refcounted, when implementing IRefcounted. +#define KE_IMPL_REFCOUNTING_TS(classname) \ + void AddRef() { \ + ke::RefcountedThreadsafe::AddRef(); \ + } \ + void Release() { \ + ke::RefcountedThreadsafe::Release(); \ + } + +// Classes may be multiply-inherited may wish to derive from this Refcounted +// instead. +class VirtualRefcountedThreadsafe : public IRefcounted +{ + public: + VirtualRefcountedThreadsafe() : refcount_(0) + { +#if !defined(NDEBUG) + destroying_ = false; +#endif + } + virtual ~VirtualRefcountedThreadsafe() + {} + void AddRef() KE_OVERRIDE { + assert(!destroying_); + refcount_.increment(); + } + void Release() KE_OVERRIDE { + if (!refcount_.decrement()) { +#if !defined(NDEBUG) + destroying_ = true; +#endif + delete this; + } + } + + private: + AtomicRefcount refcount_; +#if !defined(NDEBUG) + bool destroying_; +#endif +}; + +// This is a specialized version of Ref<> that is safe to read and write from +// multiple threads. It is not recommended for general use, since it imposes +// a CAS spin-lock on every read/write. +// +// Normally, assigning Ref<> to Ref<> has a race condition where in between +// the read and incref, another thread can assign, decref, and ultimately +// destroy the left-hand side. This prevents such a scenario by making reads +// atomic with respect to the incref operation. +// +// Pointers stored in an AtomicRef<> must be at least sizeof(void *) aligned. +template +class AtomicRef +{ + public: + AtomicRef() + : thing_(nullptr) + {} + AtomicRef(T *thing) + : thing_(thing) + { + assert(IsAligned(thing, sizeof(void *))); + if (thing) + thing->AddRef(); + } + ~AtomicRef() { + assert(thing_ == untagged(thing_)); + if (thing_) + reinterpret_cast(thing_)->Release(); + } + + // Atomically retrieve and add a reference to the contained value. + AlreadyRefed get() { + T *value = lock(); + if (value) + value->AddRef(); + unlock(value); + return AdoptRef(value); + } + + // Atomically incref the new value and replace the old value. + void operator =(T *other) { + T *value = lock(); + if (other) + other->AddRef(); + unlock(other); + if (value) + value->Release(); + } + + private: + AtomicRef(const AtomicRef &other) KE_DELETE; + void operator =(const AtomicRef &other) KE_DELETE; + + private: + // We represent a locked state with a tag bit. + void *tagged(void *ptr) { + return reinterpret_cast(uintptr_t(ptr) | 1); + } + void *untagged(void *ptr) { + return reinterpret_cast(uintptr_t(ptr) & ~uintptr_t(1)); + } + + T *lock() { + // Spin until we can replace an untagged ptr with the tagged version. + void *oldval = untagged(thing_); + while (CompareAndSwapPtr(&thing_, tagged(oldval), oldval) != oldval) { + YieldProcessor(); + oldval = untagged(thing_); + } + return reinterpret_cast(oldval); + } + void unlock(T *ptr) { + // Nothing should have mutated the value, and the new value should be + // untagged. + assert(thing_ == tagged(thing_)); + assert(ptr == untagged(ptr)); + thing_ = ptr; + } + + private: + void * volatile thing_; }; } // namespace ke diff --git a/public/amtl/am-refcounting.h b/public/amtl/am-refcounting.h index ea2dc244..0127385f 100644 --- a/public/amtl/am-refcounting.h +++ b/public/amtl/am-refcounting.h @@ -60,13 +60,26 @@ class AlreadyRefed { // If copy elision for some reason doesn't happen (for example, when // returning from AdoptRef), just null out the source ref. - other.thing_ = NULL; + other.thing_ = nullptr; } ~AlreadyRefed() { if (thing_) thing_->Release(); } + bool operator !() const { + return !thing_; + } + T *operator ->() const { + return thing_; + } + bool operator ==(T *other) const { + return thing_ == other; + } + bool operator !=(T *other) const { + return thing_ != other; + } + T *release() const { return ReturnAndVoid(thing_); } @@ -94,18 +107,18 @@ class PassRef AddRef(); } PassRef() - : thing_(NULL) + : thing_(nullptr) { } - PassRef(const AlreadyRefed &other) + PassRef(const AlreadyRefed &other) : thing_(other.release()) { // Don't addref, newborn means already addref'd. } template - PassRef(const AlreadyRefed &other) + PassRef(const AlreadyRefed &other) : thing_(other.release()) { // Don't addref, newborn means already addref'd. @@ -185,7 +198,7 @@ class PassRef // must either be assigned to a Ref or PassRef (NOT an AdoptRef/AlreadyRefed), // or must be deleted using |delete|. template -class Refcounted +class KE_LINK Refcounted { public: Refcounted() @@ -210,6 +223,58 @@ class Refcounted uintptr_t refcount_; }; +// Use this to forward to ke::Refcounted, when implementing IRefcounted. +#define KE_IMPL_REFCOUNTING(classname) \ + void AddRef() { \ + ke::Refcounted::AddRef(); \ + } \ + void Release() { \ + ke::Refcounted::Release(); \ + } + +// This can be used for classes which will inherit from VirtualRefcounted. +class KE_LINK IRefcounted +{ + public: + virtual ~IRefcounted() {} + virtual void AddRef() = 0; + virtual void Release() = 0; +}; + +// Classes may be multiply-inherited may wish to derive from this Refcounted +// instead. +class KE_LINK VirtualRefcounted : public IRefcounted +{ + public: + VirtualRefcounted() : refcount_(0) + { +#if !defined(NDEBUG) + destroying_ = false; +#endif + } + virtual ~VirtualRefcounted() + {} + void AddRef() KE_OVERRIDE { + assert(!destroying_); + refcount_++; + } + void Release() KE_OVERRIDE { + assert(refcount_ > 0); + if (--refcount_ == 0) { +#if !defined(NDEBUG) + destroying_ = true; +#endif + delete this; + } + } + + private: + uintptr_t refcount_; +#if !defined(NDEBUG) + bool destroying_; +#endif +}; + // Simple class for automatic refcounting. template class Ref @@ -222,7 +287,7 @@ class Ref } Ref() - : thing_(NULL) + : thing_(nullptr) { } @@ -231,10 +296,10 @@ class Ref { AddRef(); } - Ref(Moveable other) - : thing_(other->thing_) + Ref(Ref &&other) + : thing_(other.thing_) { - other->thing_ = NULL; + other.thing_ = nullptr; } template Ref(const Ref &other) @@ -274,10 +339,27 @@ class Ref operator T *() { return thing_; } + operator T *() const { + return thing_; + } bool operator !() const { return !thing_; } + AlreadyRefed take() { + return AlreadyRefed(ReturnAndVoid(thing_)); + } + AlreadyRefed forget() { + return AlreadyRefed(ReturnAndVoid(thing_)); + } + + bool operator ==(const Ref &other) { + return thing_ == other.thing_; + } + bool operator !=(const Ref &other) { + return thing_ != other.thing_; + } + template Ref &operator =(S *thing) { Release(); @@ -307,10 +389,10 @@ class Ref return *this; } - Ref &operator =(Moveable other) { + Ref &operator =(Ref &&other) { Release(); - thing_ = other->thing_; - other->thing_ = NULL; + thing_ = other.thing_; + other.thing_ = nullptr; return *this; } diff --git a/public/amtl/am-string.h b/public/amtl/am-string.h index af875111..c7a317a8 100644 --- a/public/amtl/am-string.h +++ b/public/amtl/am-string.h @@ -58,18 +58,18 @@ class AString else length_ = 0; } - AString(Moveable other) - : chars_(other->chars_.take()), - length_(other->length_) + AString(AString &&other) + : chars_(other.chars_.take()), + length_(other.length_) { - other->length_ = 0; + other.length_ = 0; } AString &operator =(const char *str) { if (str && str[0]) { set(str, strlen(str)); } else { - chars_ = NULL; + chars_ = nullptr; length_ = 0; } return *this; @@ -78,15 +78,15 @@ class AString if (other.length_) { set(other.chars_, other.length_); } else { - chars_ = NULL; + chars_ = nullptr; length_ = 0; } return *this; } - AString &operator =(Moveable other) { - chars_ = other->chars_.take(); - length_ = other->length_; - other->length_ = 0; + AString &operator =(AString &&other) { + chars_ = other.chars_.take(); + length_ = other.length_; + other.length_ = 0; return *this; } @@ -106,23 +106,13 @@ class AString return chars()[index]; } - void setVoid() { - chars_ = NULL; - length_ = kInvalidLength; - } - - bool isVoid() const { - return length_ == kInvalidLength; - } - size_t length() const { - assert(!isVoid()); return length_; } const char *chars() const { if (!chars_) - return isVoid() ? NULL : ""; + return ""; return chars_; } diff --git a/public/amtl/am-thread-posix.h b/public/amtl/am-thread-posix.h index bc654261..e70676c0 100644 --- a/public/amtl/am-thread-posix.h +++ b/public/amtl/am-thread-posix.h @@ -50,7 +50,7 @@ class Mutex : public Lockable #if !defined(NDEBUG) int rv = #endif - pthread_mutex_init(&mutex_, NULL); + pthread_mutex_init(&mutex_, nullptr); assert(rv == 0); } ~Mutex() { @@ -85,7 +85,7 @@ class ConditionVariable : public Lockable #if !defined(NDEBUG) int rv = #endif - pthread_cond_init(&cv_, NULL); + pthread_cond_init(&cv_, nullptr); assert(rv == 0); } ~ConditionVariable() { @@ -116,7 +116,7 @@ class ConditionVariable : public Lockable return Wait_Error; #else struct timeval tv; - gettimeofday(&tv, NULL); + gettimeofday(&tv, nullptr); struct timespec ts; ts.tv_sec = tv.tv_sec; @@ -165,12 +165,12 @@ class Thread char name[17]; }; public: - Thread(IRunnable *run, const char *name = NULL) { + Thread(IRunnable *run, const char *name = nullptr) { ThreadData *data = new ThreadData; data->run = run; snprintf(data->name, sizeof(data->name), "%s", name ? name : ""); - initialized_ = (pthread_create(&thread_, NULL, Main, data) == 0); + initialized_ = (pthread_create(&thread_, nullptr, Main, data) == 0); if (!initialized_) delete data; } @@ -182,7 +182,7 @@ class Thread void Join() { if (!Succeeded()) return; - pthread_join(thread_, NULL); + pthread_join(thread_, nullptr); } private: @@ -199,7 +199,7 @@ class Thread #endif } data->run->Run(); - return NULL; + return nullptr; } private: diff --git a/public/amtl/am-thread-utils.h b/public/amtl/am-thread-utils.h index 98db2616..35be6637 100644 --- a/public/amtl/am-thread-utils.h +++ b/public/amtl/am-thread-utils.h @@ -199,12 +199,74 @@ class AutoLock Lockable *lock_; }; +class AutoMaybeLock +{ + friend class AutoMaybeUnlock; + + public: + AutoMaybeLock(Lockable *lock) + : lock_(lock) + { + if (lock_) + lock_->Lock(); + } + ~AutoMaybeLock() { + if (lock_) + lock_->Unlock(); + } + + // Unlock and void the locked object. After calling this, the region covered + // by the AutoMaybeLocked is not guaranteed to be locked! This is useful for + // patterns like: + // + // AutoMaybeLock lock(x); + // { + // ... + // return helper(&lock); + // } + // + // helper_while_locked(AutoMaybeLock *mlock) { + // ... + // mlock->unlock(); + // callback + // } + // + // In this situation, we can avoid using AutoMaybeUnlock which would re-lock + // only to unlock again immediately. + void unlock() { + if (lock_) { + lock_->Unlock(); + lock_ = nullptr; + } + } + + private: + Lockable *lock_; +}; + +class AutoMaybeUnlock +{ + public: + AutoMaybeUnlock(Lockable *lock) + : lock_(lock) + { + if (lock_) + lock_->Unlock(); + } + ~AutoMaybeUnlock() { + if (lock_) + lock_->Lock(); + } + + private: + Lockable *lock_; +}; + class AutoTryLock { public: - AutoTryLock(Lockable *lock) - { - lock_ = lock->TryLock() ? lock : NULL; + AutoTryLock(Lockable *lock) { + lock_ = lock->TryLock() ? lock : nullptr; } ~AutoTryLock() { if (lock_) diff --git a/public/amtl/am-thread-windows.h b/public/amtl/am-thread-windows.h index c4e34ed2..1a52eb88 100644 --- a/public/amtl/am-thread-windows.h +++ b/public/amtl/am-thread-windows.h @@ -66,7 +66,7 @@ class ConditionVariable : public Lockable { public: ConditionVariable() { - event_ = CreateEvent(NULL, FALSE, FALSE, NULL); + event_ = CreateEvent(nullptr, FALSE, FALSE, nullptr); } ~ConditionVariable() { CloseHandle(event_); @@ -87,12 +87,12 @@ class ConditionVariable : public Lockable SetEvent(event_); } - WaitResult Wait(size_t timeout_ms) { + WaitResult Wait(size_t timeoutMs) { // This will assert if the lock has not been acquired. We don't need to be // atomic here, like pthread_cond_wait, because the event bit will stick // until reset by a wait function. Unlock(); - DWORD rv = WaitForSingleObject(event_, timeout_ms); + DWORD rv = WaitForSingleObject(event_, int(timeoutMs)); Lock(); if (rv == WAIT_TIMEOUT) @@ -114,8 +114,8 @@ class ConditionVariable : public Lockable class Thread { public: - Thread(IRunnable *run, const char *name = NULL) { - thread_ = CreateThread(NULL, 0, Main, run, 0, NULL); + Thread(IRunnable *run, const char *name = nullptr) { + thread_ = CreateThread(nullptr, 0, Main, run, 0, nullptr); } ~Thread() { if (!thread_) diff --git a/public/amtl/am-threadlocal.h b/public/amtl/am-threadlocal.h index bacecd4b..19340859 100644 --- a/public/amtl/am-threadlocal.h +++ b/public/amtl/am-threadlocal.h @@ -135,7 +135,7 @@ class ThreadLocal bool allocate() { if (!__sync_bool_compare_and_swap(&allocated_, 0, 1)) return true; - return pthread_key_create(&key_, NULL) == 0; + return pthread_key_create(&key_, nullptr) == 0; } private: diff --git a/public/amtl/am-type-traits.h b/public/amtl/am-type-traits.h new file mode 100644 index 00000000..984db436 --- /dev/null +++ b/public/amtl/am-type-traits.h @@ -0,0 +1,64 @@ +// vim: set sts=8 ts=2 sw=2 tw=99 et: +// +// Copyright (C) 2013, David Anderson and AlliedModders LLC +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of AlliedModders LLC nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef _include_amtl_type_traits_h_ +#define _include_amtl_type_traits_h_ + +#include + +namespace ke { + +// Remove references from types. +template +struct remove_reference { + typedef T type; +}; +template +struct remove_reference { + typedef T type; +}; +template +struct remove_reference { + typedef T type; +}; + +template +struct integral_constant { + static const T value = Value; +}; + +typedef integral_constant true_type; +typedef integral_constant false_type; + +template struct is_lvalue_reference : false_type{}; +template struct is_lvalue_reference : true_type {}; + +} // namespace ke + +#endif // _include_amtl_type_traits_h_ diff --git a/public/amtl/am-utility.h b/public/amtl/am-utility.h index bec35efa..cabe95ff 100644 --- a/public/amtl/am-utility.h +++ b/public/amtl/am-utility.h @@ -1,6 +1,6 @@ // vim: set sts=8 ts=2 sw=2 tw=99 et: // -// Copyright (C) 2013, David Anderson and AlliedModders LLC +// Copyright (C) 2013-2014, David Anderson and AlliedModders LLC // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -30,6 +30,7 @@ #ifndef _include_amtl_utility_h_ #define _include_amtl_utility_h_ +#define __STDC_FORMAT_MACROS #include #include #include @@ -40,10 +41,11 @@ # include #endif #include - -#define KE_32BIT +#include +#include #if defined(_MSC_VER) +// Mac file format warning. # pragma warning(disable:4355) #endif @@ -55,8 +57,6 @@ static const size_t kKB = 1024; static const size_t kMB = 1024 * kKB; static const size_t kGB = 1024 * kMB; -typedef uint8_t * Address; - template T ReturnAndVoid(T &t) { @@ -65,75 +65,70 @@ ReturnAndVoid(T &t) return saved; } -#if __cplusplus >= 201103L -# define KE_CXX11 -#endif - -#if defined(KE_CXX11) -# define KE_DELETE = delete -# define KE_OVERRIDE override -#else -# define KE_DELETE -# define KE_OVERRIDE -#endif - // Wrapper that automatically deletes its contents. The pointer can be taken // to avoid destruction. template class AutoPtr { - T *t_; + public: + AutoPtr() + : t_(nullptr) + { + } + explicit AutoPtr(T *t) + : t_(t) + { + } + AutoPtr(AutoPtr &&other) + { + t_ = other.t_; + other.t_ = nullptr; + } + ~AutoPtr() { + delete t_; + } + T *get() { + return t_; + } + T *take() { + return ReturnAndVoid(t_); + } + T *forget() { + return ReturnAndVoid(t_); + } + T *operator *() const { + return t_; + } + T *operator ->() const { + return t_; + } + operator T *() const { + return t_; + } + T *operator =(T *t) { + delete t_; + t_ = t; + return t_; + } + T **address() { + return &t_; + } + T *operator =(AutoPtr &&other) { + delete t_; + t_ = other.t_; + other.t_ = nullptr; + return t_; + } + bool operator !() const { + return !t_; + } - public: - AutoPtr() - : t_(NULL) - { - } - explicit AutoPtr(T *t) - : t_(t) - { - } - AutoPtr(Moveable > other) - { - t_ = other->t_; - other->t_ = NULL; - } - ~AutoPtr() { - delete t_; - } - T *take() { - return ReturnAndVoid(t_); - } - T *operator *() const { - return t_; - } - T *operator ->() const { - return t_; - } - operator T *() const { - return t_; - } - T *operator =(T *t) { - delete t_; - t_ = t; - return t_; - } - T **address() { - return &t_; - } - T *operator =(Moveable > other) { - delete t_; - t_ = other->t_; - other->t_ = NULL; - return t_; - } - bool operator !() const { - return !t_; - } + private: + AutoPtr(const AutoPtr &other) KE_DELETE; + AutoPtr &operator =(const AutoPtr &other) KE_DELETE; - private: - AutoPtr(const AutoPtr &other) KE_DELETE; - AutoPtr &operator =(const AutoPtr &other) KE_DELETE; + private: + T *t_; }; // Wrapper that automatically deletes its contents. The pointer can be taken @@ -145,7 +140,7 @@ class AutoArray public: AutoArray() - : t_(NULL) + : t_(nullptr) { } explicit AutoArray(T *t) @@ -158,15 +153,15 @@ class AutoArray T *take() { return ReturnAndVoid(t_); } - T *operator *() const { + T *forget() { + return ReturnAndVoid(t_); + } + T **address() { + return &t_; + } + T &operator *() const { return t_; } - T &operator [](size_t index) { - return t_[index]; - } - const T &operator [](size_t index) const { - return t_[index]; - } operator T *() const { return t_; } @@ -283,8 +278,6 @@ IsUintPtrMultiplySafe(size_t a, size_t b) } #define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0])) -#define STATIC_ASSERT(cond) extern int static_assert_f(int a[(cond) ? 1 : -1]) - #define IS_ALIGNED(addr, alignment) (!(uintptr_t(addr) & ((alignment) - 1))) template @@ -295,23 +288,11 @@ IsAligned(T addr, size_t alignment) return !(uintptr_t(addr) & (alignment - 1)); } -static inline Address -AlignedBase(Address addr, size_t alignment) +static inline void * +AlignedBase(void *addr, size_t alignment) { assert(IsPowerOfTwo(alignment)); - return Address(uintptr_t(addr) & ~(alignment - 1)); -} - -template static inline T -Min(const T &t1, const T &t2) -{ - return t1 < t2 ? t1 : t2; -} - -template static inline T -Max(const T &t1, const T &t2) -{ - return t1 > t2 ? t1 : t2; + return reinterpret_cast(uintptr_t(addr) & ~(alignment - 1)); } template @@ -351,6 +332,36 @@ class SaveAndSet T old_; }; +template +class Maybe +{ + public: + Maybe() + : initialized_(false) + {} + ~Maybe() { + if (initialized_) + t_.address()->~T(); + } + + void init() { + new (t_.address()) T(); + initialized_ = true; + } + template + void init(U &&u) { + new (t_.address()) T(Forward(u)); + initialized_ = true; + } + bool initialized() const { + return initialized_; + } + + private: + bool initialized_; + StorageBuffer t_; +}; + template class StackLinked { @@ -366,31 +377,19 @@ class StackLinked *prevp_ = prev_; } - private: + protected: T **prevp_; T *prev_; }; -#if __cplusplus >= 201103L -# define KE_CXX11 -#endif - -#if defined(KE_CXX11) -# define KE_DELETE = delete -# define KE_OVERRIDE override -#else -# define KE_DELETE -# define KE_OVERRIDE -#endif - #if defined(_MSC_VER) -# define KE_SIZET_FMT "%Iu" -# define KE_I64_FMT "%I64d" -# define KE_U64_FMT "%I64u" +# define KE_FMT_SIZET "Iu" +# define KE_FMT_I64 "I64d" +# define KE_FMT_U64 "I64u" #elif defined(__GNUC__) -# define KE_SIZET_FMT "%zu" -# define KE_I64_FMT "%" PRId64 -# define KE_U64_FMT "%" PRIu64 +# define KE_FMT_SIZET "zu" +# define KE_FMT_I64 PRId64 +# define KE_FMT_U64 PRIu64 #else # error "Implement format specifier string" #endif @@ -401,6 +400,22 @@ class StackLinked # define KE_CRITICAL_LIKELY(x) x #endif -} +#if defined(_WIN32) +# define KE_IMPORT __declspec(dllimport) +# define KE_EXPORT __declspec(dllexport) +#else +# define KE_IMPORT +# define KE_EXPORT __attribute__((visibility("default"))) +#endif + +#if defined(KE_EXPORTING) +# define KE_LINK KE_EXPORT +#elif defined(KE_IMPORTING) +# define KE_LINK KE_IMPORT +#else +# define KE_LINK +#endif + +} // namespace ke #endif // _include_amtl_utility_h_ diff --git a/public/amtl/am-vector.h b/public/amtl/am-vector.h index d6d5a55f..4002b5b3 100644 --- a/public/amtl/am-vector.h +++ b/public/amtl/am-vector.h @@ -43,45 +43,35 @@ class Vector : public AllocPolicy { public: Vector(AllocPolicy = AllocPolicy()) - : data_(NULL), + : data_(nullptr), nitems_(0), maxsize_(0) { } - Vector(Moveable > other) { - data_ = other->data_; - nitems_ = other->nitems_; - maxsize_ = other->maxsize_; - other->reset(); + Vector(Vector &&other) { + data_ = other.data_; + nitems_ = other.nitems_; + maxsize_ = other.maxsize_; + other.reset(); } ~Vector() { zap(); } - bool append(const T &item) { + template + bool append(U &&item) { if (!growIfNeeded(1)) return false; - new (&data_[nitems_]) T(item); + new (&data_[nitems_]) T(ke::Forward(item)); nitems_++; return true; } - bool append(Moveable item) { - if (!growIfNeeded(1)) - return false; - new (&data_[nitems_]) T(item); - nitems_++; - return true; - } - void infallibleAppend(const T &item) { + template + void infallibleAppend(U &&item) { assert(growIfNeeded(1)); - new (&data_[nitems_]) T(item); - nitems_++; - } - void infallibleAppend(Moveable item) { - assert(growIfNeeded(1)); - new (&data_[nitems_]) T(item); + new (&data_[nitems_]) T(ke::Forward(item)); nitems_++; } @@ -91,20 +81,13 @@ class Vector : public AllocPolicy // invalid indexes are allowed. // // This is a linear-time operation. - bool insert(size_t at, const T &item) { + template + bool insert(size_t at, U &&item) { if (at == length()) - return append(item); + return append(ke::Forward(item)); if (!moveUp(at)) return false; - new (&data_[at]) T(item); - return true; - } - bool insert(size_t at, Moveable item) { - if (at == length()) - return append(item); - if (!moveUp(at)) - return false; - new (&data_[at]) T(item); + new (&data_[at]) T(ke::Forward(item)); return true; } @@ -112,7 +95,7 @@ class Vector : public AllocPolicy // element. This is a linear-time operation. void remove(size_t at) { for (size_t i = at; i < length() - 1; i++) - data_[i] = Moveable(data_[i + 1]); + data_[i] = ke::Move(data_[i + 1]); pop(); } @@ -156,7 +139,10 @@ class Vector : public AllocPolicy return at(length() - 1); } - T *buffer() const { + T *buffer() { + return data_; + } + const T *buffer() const { return data_; } @@ -180,11 +166,25 @@ class Vector : public AllocPolicy return growIfNeeded(desired - length()); } - Vector &operator =(Moveable > other) { - data_ = other->data_; - nitems_ = other->nitems_; - maxsize_ = other->maxsize_; - other->reset(); + template + bool extend(U &&other) { + if (length() == 0) { + *this = Move(other); + } else { + for (size_t i = 0; i < other.length(); i++) { + if (!append(Move(other[i]))) + return false; + } + } + return true; + } + + Vector &operator =(Vector &&other) { + zap(); + data_ = other.data_; + nitems_ = other.nitems_; + maxsize_ = other.maxsize_; + other.reset(); return *this; } @@ -201,7 +201,7 @@ class Vector : public AllocPolicy this->free(data_); } void reset() { - data_ = NULL; + data_ = nullptr; nitems_ = 0; maxsize_ = 0; } @@ -214,10 +214,10 @@ class Vector : public AllocPolicy // references are taken. if (!growIfNeeded(1)) return false; - new (&data_[nitems_]) T(Moveable(data_[nitems_ - 1])); + new (&data_[nitems_]) T(ke::Move(data_[nitems_ - 1])); nitems_++; for (size_t i = nitems_ - 2; i > at; i--) - data_[i] = Moveable(data_[i - 1]); + data_[i] = ke::Move(data_[i - 1]); return true; } @@ -240,12 +240,9 @@ class Vector : public AllocPolicy } T* newdata = (T*)this->malloc(sizeof(T) * new_maxsize); - if (newdata == NULL) + if (newdata == nullptr) return false; - for (size_t i = 0; i < nitems_; i++) { - new (&newdata[i]) T(Moveable(data_[i])); - data_[i].~T(); - } + MoveRange(newdata, data_, nitems_); this->free(data_); data_ = newdata;