diff --git a/amxmodx/msvc10/amxmodx_mm.vcxproj b/amxmodx/msvc10/amxmodx_mm.vcxproj index 5a2ac486..c7d86407 100644 --- a/amxmodx/msvc10/amxmodx_mm.vcxproj +++ b/amxmodx/msvc10/amxmodx_mm.vcxproj @@ -93,7 +93,7 @@ Disabled ..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;_CRT_SECURE_NO_DEPRECATE;HAVE_STDINT_H;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug 4Bytes @@ -144,7 +144,7 @@ Speed true ..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;_CRT_SECURE_NO_DEPRECATE;HAVE_STDINT_H;%(PreprocessorDefinitions) false true MultiThreaded diff --git a/dlls/hamsandwich/AMBuilder b/dlls/hamsandwich/AMBuilder index b463edec..71736260 100644 --- a/dlls/hamsandwich/AMBuilder +++ b/dlls/hamsandwich/AMBuilder @@ -3,6 +3,10 @@ import os.path binary = AMXX.MetaModule(builder, 'hamsandwich') +binary.compiler.defines += [ + 'HAVE_STDINT_H', +] + binary.sources = [ 'sdk/amxxmodule.cpp', 'amxx_api.cpp', diff --git a/public/amtl/am-fixedarray.h b/public/amtl/am-fixedarray.h new file mode 100644 index 00000000..4681651d --- /dev/null +++ b/public/amtl/am-fixedarray.h @@ -0,0 +1,99 @@ +// 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_fixedarray_h_ +#define _include_amtl_fixedarray_h_ + +#include +#include +#include + +namespace ke { + +template +class FixedArray : public AllocPolicy +{ + public: + FixedArray(size_t length, AllocPolicy = AllocPolicy()) { + length_ = length; + data_ = (T *)this->malloc(sizeof(T) * length_); + if (!data_) + return; + + for (size_t i = 0; i < length_; i++) + new (&data_[i]) T(); + } + ~FixedArray() { + for (size_t i = 0; i < length_; i++) + data_[i].~T(); + this->free(data_); + } + + // This call may be skipped if the allocator policy is infallible. + bool initialize() { + return length_ == 0 || !!data_; + } + + size_t length() const { + return length_; + } + T &operator [](size_t index) { + return at(index); + } + const T &operator [](size_t index) const { + return at(index); + } + T &at(size_t index) { + assert(index < length()); + return data_[index]; + } + const T &at(size_t index) const { + assert(index < length()); + return data_[index]; + } + void set(size_t index, const T &t) { + assert(index < length()); + data_[index] = t; + } + void set(size_t index, ke::Moveable t) { + assert(index < length()); + data_[index] = t; + } + + private: + FixedArray(const FixedArray &other) KE_DELETE; + FixedArray &operator =(const FixedArray &other) KE_DELETE; + + private: + size_t length_; + T *data_; +}; + +} // namespace ke + +#endif // _include_amtl_fixedarray_h_ diff --git a/public/amtl/am-hashmap.h b/public/amtl/am-hashmap.h index 9a2d1ee6..7d5c3839 100644 --- a/public/amtl/am-hashmap.h +++ b/public/amtl/am-hashmap.h @@ -138,19 +138,24 @@ 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) { - return table_.add(i, Entry(key, value)); + Entry entry(key, value); + return table_.add(i, ke::Move(entry)); } bool add(Insert &i, Moveable key, const V &value) { - return table_.add(i, Entry(key, value)); + Entry entry(key, value); + return table_.add(i, ke::Move(entry)); } bool add(Insert &i, const K &key, Moveable value) { - return table_.add(i, Entry(key, value)); + Entry entry(key, value); + return table_.add(i, ke::Move(entry)); } bool add(Insert &i, Moveable key, Moveable value) { - return table_.add(i, Entry(key, value)); + Entry entry(key, value); + return table_.add(i, ke::Move(entry)); } bool add(Insert &i, Moveable key) { - return table_.add(i, Entry(key, V())); + Entry entry(key, V()); + return table_.add(i, ke::Move(entry)); } // This can be used to avoid compiler constructed temporaries, since AMTL diff --git a/public/amtl/am-refcounting-threadsafe.h b/public/amtl/am-refcounting-threadsafe.h index a37b39d4..785ba62b 100644 --- a/public/amtl/am-refcounting-threadsafe.h +++ b/public/amtl/am-refcounting-threadsafe.h @@ -35,12 +35,15 @@ namespace ke { +// See the comment above Refcounted for more information. This class is +// identical, except changing the reference count is guaranteed to be atomic +// with respect to other threads changing the reference count. template class RefcountedThreadsafe { public: RefcountedThreadsafe() - : refcount_(1) + : refcount_(0) { } diff --git a/public/amtl/am-refcounting.h b/public/amtl/am-refcounting.h index 71a6f554..028d6df5 100644 --- a/public/amtl/am-refcounting.h +++ b/public/amtl/am-refcounting.h @@ -37,18 +37,28 @@ namespace ke { template class Ref; -// Holds a refcounted T without addrefing it. This is similar to PassRef<> -// below, but is intended only for freshly allocated objects which start -// with reference count 1, and we don't want to add an extra ref just by -// assigning to PassRef<> or Ref<>. +// Objects in AMTL inheriting from Refcounted will have an initial refcount +// of 0. However, in some systems (such as COM), the initial refcount is 1, +// or functions may return raw pointers that have been AddRef'd. In these +// cases it would be a mistake to use Ref<> or PassRef<>, since the object +// would leak an extra reference. +// +// This container holds a refcounted object without addrefing it. This is +// intended only for interacting with functions which return an object that +// has been manually AddRef'd. Note that this will perform a Release(), so +// so it is necessary to assign it to retain the object. template -class Newborn +class AlreadyRefed { public: - Newborn(T *t) + AlreadyRefed(T *t) : thing_(t) { } + ~AlreadyRefed() { + if (thing_) + thing_->Release(); + } T *release() const { return ReturnAndVoid(thing_); @@ -59,10 +69,10 @@ class Newborn }; template -static inline Newborn -NoAddRef(T *t) +static inline AlreadyRefed +AdoptRef(T *t) { - return Newborn(t); + return AlreadyRefed(t); } // When returning a value, we'd rather not be needlessly changing the refcount, @@ -81,7 +91,14 @@ class PassRef { } - PassRef(const Newborn &other) + PassRef(const AlreadyRefed &other) + : thing_(other.release()) + { + // Don't addref, newborn means already addref'd. + } + + template + PassRef(const AlreadyRefed &other) : thing_(other.release()) { // Don't addref, newborn means already addref'd. @@ -134,7 +151,7 @@ class PassRef private: // Disallowed operators. PassRef &operator =(T *other); - PassRef &operator =(Newborn &other); + PassRef &operator =(AlreadyRefed &other); void AddRef() { if (thing_) @@ -149,13 +166,18 @@ class PassRef mutable T *thing_; }; -// Classes which are refcounted should inherit from this. +// Classes which are refcounted should inherit from this. Note that reference +// counts start at 0 in AMTL, rather than 1. This avoids the complexity of +// having to adopt the initial ref upon allocation. However, this also means +// invoking Release() on a newly allocated object is illegal. Newborn objects +// must either be assigned to a Ref or PassRef (NOT an AdoptRef/AlreadyRefed), +// or must be deleted using |delete|. template class Refcounted { public: Refcounted() - : refcount_(1) + : refcount_(0) { } @@ -217,7 +239,12 @@ class Ref : thing_(other.release()) { } - Ref(const Newborn &other) + Ref(const AlreadyRefed &other) + : thing_(other.release()) + { + } + template + Ref(const AlreadyRefed &other) : thing_(other.release()) { } @@ -255,7 +282,7 @@ class Ref } template - Ref &operator =(const Newborn &other) { + Ref &operator =(const AlreadyRefed &other) { Release(); thing_ = other.release(); return *this; diff --git a/public/amtl/am-string.h b/public/amtl/am-string.h index 074339fa..af875111 100644 --- a/public/amtl/am-string.h +++ b/public/amtl/am-string.h @@ -56,7 +56,7 @@ class AString if (other.length_) set(other.chars_, other.length_); else - length_ = 0; + length_ = 0; } AString(Moveable other) : chars_(other->chars_.take()), @@ -106,17 +106,29 @@ 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 ""; + return isVoid() ? NULL : ""; return chars_; } private: + static const size_t kInvalidLength = (size_t)-1; + void set(const char *str, size_t length) { chars_ = new char[length + 1]; length_ = length; diff --git a/public/amtl/am-threadlocal.h b/public/amtl/am-threadlocal.h new file mode 100644 index 00000000..df4c023c --- /dev/null +++ b/public/amtl/am-threadlocal.h @@ -0,0 +1,175 @@ +// vim: set sts=2 ts=8 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_thread_local_h_ +#define _include_amtl_thread_local_h_ + +#include + +namespace ke { + +// Stores a per-thread value. In single-threaded mode (KE_SINGLE_THREADED), +// this is a no-op container wrapper. +// +// T must be castable to uintptr_t. +// +// When assigning to a ThreadLocal, the assigment will automatically attempt +// to allocate thread-local storage from the operating system. If it fails, it +// will abort the program. If this is undesirable, you may call allocate() +// up-front and handle the error case manually. +// +// The number of thread local slots available to processes is limited (on +// Linux, it is generally 1024). It is best to use ThreadLocal sparingly to +// play nicely with other libraries. +// +// ThreadLocal will free the underlying thread-local storage slot in its +// destructor, but it is not an AutoPtr. It does not delete pointers. Since +// one thread's value is only observable from that thread, make sure to free +// the contained resource (if necessary) before the thread exits. +template +class ThreadLocal +{ + public: + void operator =(const T &other) { + set(other); + } + + T operator *() const { + return get(); + } + T operator ->() const { + return get(); + } + bool operator !() const { + return !get(); + } + bool operator ==(const T &other) const { + return get() == other; + } + bool operator !=(const T &other) const { + return get() != other; + } + + private: + ThreadLocal(const ThreadLocal &other) KE_DELETE; + ThreadLocal &operator =(const ThreadLocal &other) KE_DELETE; + +#if !defined(KE_SINGLE_THREADED) + private: + int allocated_; + + public: + ThreadLocal() { + allocated_ = 0; + } + + T get() const { + if (!allocated_) + return T(); + return internalGet(); + } + void set(const T &t) { + if (!allocated_ && !allocate()) { + fprintf(stderr, "could not allocate thread-local storage\n"); + abort(); + } + internalSet(t); + } + +# if defined(_MSC_VER) + ~ThreadLocal() { + if (allocated_) + TlsFree(key_); + } + + private: + T internalGet() const { + return reinterpret_cast(TlsGetValue(key_)); + } + void internalSet(const T &t) { + TlsSetValue(key_, reinterpret_cast(t)); + } + bool allocate() { + if (InterlockedCompareExchange(&allocated_, 1, 0) == 1) + return true; + key_ = TlsAlloc(); + return key_ != TLS_OUT_OF_INDEXES; + } + + DWORD key_; + +# else + public: + ~ThreadLocal() { + if (allocated_) + pthread_key_delete(key_); + } + + bool allocate() { + if (!__sync_bool_compare_and_swap(&allocated_, 0, 1)) + return true; + return pthread_key_create(&key_, NULL) == 0; + } + + private: + T internalGet() const { + return (T)reinterpret_cast(pthread_getspecific(key_)); + } + void internalSet(const T &t) { + pthread_setspecific(key_, reinterpret_cast(t)); + } + + pthread_key_t key_; +# endif // !_MSC_VER + +#else // KE_SINGLE_THREADED + public: + ThreadLocal() { + t_ = T(); + } + + bool allocate() { + return true; + } + + T get() const { + return t_; + } + void set(const T &t) { + t_ = t; + } + + private: + T t_; +#endif +}; + +} // namespace ke + +#endif // _include_amtl_thread_local_h_ diff --git a/public/amtl/am-utility.h b/public/amtl/am-utility.h index afa0d44a..cbe48167 100644 --- a/public/amtl/am-utility.h +++ b/public/amtl/am-utility.h @@ -33,11 +33,10 @@ #include #include #include -//#include +#include #if defined(_MSC_VER) # include #endif -#include #define KE_32BIT @@ -53,7 +52,7 @@ static const size_t kKB = 1024; static const size_t kMB = 1024 * kKB; static const size_t kGB = 1024 * kMB; -typedef unsigned char * Address; +typedef uint8_t * Address; template T ReturnAndVoid(T &t) @@ -63,18 +62,6 @@ 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 @@ -87,15 +74,10 @@ class AutoPtr : t_(NULL) { } - AutoPtr(T *t) + explicit AutoPtr(T *t) : t_(t) { } - AutoPtr(Moveable > other) - { - t_ = other->t_; - other->t_ = NULL; - } ~AutoPtr() { delete t_; } @@ -111,24 +93,13 @@ class AutoPtr operator T *() const { return t_; } - T *operator =(T *t) { + void operator =(T *t) { delete t_; t_ = t; - 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; }; // Wrapper that automatically deletes its contents. The pointer can be taken @@ -327,6 +298,37 @@ class StorageBuffer }; }; +template +class SaveAndSet +{ + public: + SaveAndSet(T *location, const T &value) + : location_(location), + old_(*location) + { + *location_ = value; + } + ~SaveAndSet() { + *location_ = old_; + } + + private: + T *location_; + T old_; +}; + +#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" #elif defined(__GNUC__) diff --git a/public/amtl/am-vector.h b/public/amtl/am-vector.h index 2b0b8feb..700ec188 100644 --- a/public/amtl/am-vector.h +++ b/public/amtl/am-vector.h @@ -86,22 +86,14 @@ class Vector : public AllocPolicy } // Shift all elements including |at| up by one, and insert |item| at the - // given position. If |at| is one greater than the last usable index, - // i.e. |at == length()|, then this is the same as append(). No other - // invalid indexes are allowed. - // - // This is a linear-time operation. + // given position. This is a linear-time operation. bool insert(size_t at, const T &item) { - if (at == length()) - return append(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); @@ -112,7 +104,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] = T(Moveable(data_[i + 1])); pop(); } @@ -167,6 +159,14 @@ class Vector : public AllocPolicy return growIfNeeded(desired - length()); } + Vector &operator =(Moveable > other) { + data_ = other->data_; + nitems_ = other->nitems_; + maxsize_ = other->maxsize_; + other->reset(); + return *this; + } + private: // These are disallowed because they basically violate the failure handling // model for AllocPolicies and are also likely to have abysmal performance. @@ -186,10 +186,15 @@ class Vector : public AllocPolicy } bool moveUp(size_t at) { - assert(at < nitems_); - if (!append(Moveable(data_[nitems_ - 1]))) + // Note: we don't use append() here. Passing an element as a Moveable into + // insert() or append() can break, since the underlying storage could be + // reallocated, invalidating the Moveable reference. Instead, we inline + // the logic to append() to ensure growIfNeeded occurs before any + // references are taken. + if (!growIfNeeded(1)) return false; - + new (&data_[nitems_]) T(Moveable(data_[nitems_ - 1])); + nitems_++; for (size_t i = nitems_ - 2; i > at; i--) data_[i] = Moveable(data_[i - 1]); return true;