Add AMTL files
This commit is contained in:
parent
9c191949d8
commit
f3aee6d766
|
@ -220,6 +220,7 @@ class AMXXConfig(object):
|
||||||
]
|
]
|
||||||
|
|
||||||
cfg.includes += [os.path.join(builder.buildPath, 'includes')]
|
cfg.includes += [os.path.join(builder.buildPath, 'includes')]
|
||||||
|
cfg.includes += [os.path.join(builder.sourcePath, 'public', 'amtl')]
|
||||||
return
|
return
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
###########################################
|
###########################################
|
||||||
|
|
||||||
HLSDK = ../../hlsdk
|
HLSDK = ../../hlsdk
|
||||||
MM_ROOT = ../../metamod/metamod
|
MM_ROOT = ../../metamod-am/metamod
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
### EDIT BELOW FOR OTHER PROJECTS ###
|
### EDIT BELOW FOR OTHER PROJECTS ###
|
||||||
|
@ -36,7 +36,7 @@ CPP_OSX = clang
|
||||||
|
|
||||||
LINK = -Lzlib
|
LINK = -Lzlib
|
||||||
|
|
||||||
INCLUDE = -I. -I$(HLSDK) -I$(HLSDK)/common -I$(HLSDK)/dlls -I$(HLSDK)/engine -I$(HLSDK)/game_shared \
|
INCLUDE = -I. -I../public/amtl -I$(HLSDK) -I$(HLSDK)/common -I$(HLSDK)/dlls -I$(HLSDK)/engine -I$(HLSDK)/game_shared \
|
||||||
-I$(HLSDK)/public -I$(MM_ROOT)
|
-I$(HLSDK)/public -I$(MM_ROOT)
|
||||||
|
|
||||||
################################################
|
################################################
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
</Midl>
|
</Midl>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<AdditionalIncludeDirectories>..\;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
@ -143,7 +143,7 @@
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||||
<OmitFramePointers>true</OmitFramePointers>
|
<OmitFramePointers>true</OmitFramePointers>
|
||||||
<AdditionalIncludeDirectories>..\;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<IgnoreStandardIncludePath>false</IgnoreStandardIncludePath>
|
<IgnoreStandardIncludePath>false</IgnoreStandardIncludePath>
|
||||||
<StringPooling>true</StringPooling>
|
<StringPooling>true</StringPooling>
|
||||||
|
@ -192,7 +192,7 @@
|
||||||
</Midl>
|
</Midl>
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<Optimization>Disabled</Optimization>
|
<Optimization>Disabled</Optimization>
|
||||||
<AdditionalIncludeDirectories>..\;$(METAMOD)\metamod;$(HLSDK)\multiplayer\common;$(HLSDK)\multiplayer\engine;$(HLSDK)\multiplayer\dlls;$(HLSDK)\multiplayer\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\multiplayer\common;$(HLSDK)\multiplayer\engine;$(HLSDK)\multiplayer\dlls;$(HLSDK)\multiplayer\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;BINLOG_ENABLED;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT;BINLOG_ENABLED;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
@ -243,7 +243,7 @@
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||||
<OmitFramePointers>true</OmitFramePointers>
|
<OmitFramePointers>true</OmitFramePointers>
|
||||||
<AdditionalIncludeDirectories>..\;$(METAMOD)\metamod;$(HLSDK)\multiplayer\common;$(HLSDK)\multiplayer\engine;$(HLSDK)\multiplayer\dlls;$(HLSDK)\multiplayer\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\;..\..\public\amtl;$(METAMOD)\metamod;$(HLSDK)\multiplayer\common;$(HLSDK)\multiplayer\engine;$(HLSDK)\multiplayer\dlls;$(HLSDK)\multiplayer\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;BINLOG_ENABLED;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;JIT;ASM32;PAWN_CELL_SIZE=32;BINLOG_ENABLED;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<IgnoreStandardIncludePath>false</IgnoreStandardIncludePath>
|
<IgnoreStandardIncludePath>false</IgnoreStandardIncludePath>
|
||||||
<StringPooling>true</StringPooling>
|
<StringPooling>true</StringPooling>
|
||||||
|
@ -389,6 +389,7 @@
|
||||||
<ClInclude Include="..\sh_list.h" />
|
<ClInclude Include="..\sh_list.h" />
|
||||||
<ClInclude Include="..\sh_stack.h" />
|
<ClInclude Include="..\sh_stack.h" />
|
||||||
<ClInclude Include="..\sh_tinyhash.h" />
|
<ClInclude Include="..\sh_tinyhash.h" />
|
||||||
|
<ClInclude Include="..\sm_stringhashmap.h" />
|
||||||
<ClInclude Include="..\svn_version.h" />
|
<ClInclude Include="..\svn_version.h" />
|
||||||
<ClInclude Include="..\trie_natives.h" />
|
<ClInclude Include="..\trie_natives.h" />
|
||||||
<ClInclude Include="..\zlib\zconf.h" />
|
<ClInclude Include="..\zlib\zconf.h" />
|
||||||
|
|
65
public/amtl/am-allocator-policies.h
Normal file
65
public/amtl/am-allocator-policies.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// 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_allocatorpolicies_h_
|
||||||
|
#define _include_amtl_allocatorpolicies_h_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
// The default system allocator policy will crash on out-of-memory.
|
||||||
|
class SystemAllocatorPolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void reportOutOfMemory() {
|
||||||
|
fprintf(stderr, "OUT OF MEMORY\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
void reportAllocationOverflow() {
|
||||||
|
fprintf(stderr, "OUT OF MEMORY\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void free(void *memory) {
|
||||||
|
::free(memory);
|
||||||
|
}
|
||||||
|
void *malloc(size_t bytes) {
|
||||||
|
void *ptr = ::malloc(bytes);
|
||||||
|
if (!ptr)
|
||||||
|
reportOutOfMemory();
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _include_amtl_allocatorpolicies_h_
|
102
public/amtl/am-atomics.h
Normal file
102
public/amtl/am-atomics.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
// 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_atomics_h_
|
||||||
|
#define _include_amtl_atomics_h_
|
||||||
|
|
||||||
|
#include <am-utility.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
extern "C" {
|
||||||
|
long __cdecl _InterlockedIncrement(long volatile *dest);
|
||||||
|
long __cdecl _InterlockedDecrement(long volatile *dest);
|
||||||
|
}
|
||||||
|
# pragma intrinsic(_InterlockedIncrement)
|
||||||
|
# pragma intrinsic(_InterlockedDecrement)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <size_t Width>
|
||||||
|
struct AtomicOps;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct AtomicOps<4>
|
||||||
|
{
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
typedef long Type;
|
||||||
|
|
||||||
|
static Type Increment(Type *ptr) {
|
||||||
|
return _InterlockedIncrement(ptr);
|
||||||
|
}
|
||||||
|
static Type Decrement(Type *ptr) {
|
||||||
|
return _InterlockedDecrement(ptr);
|
||||||
|
};
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
typedef int Type;
|
||||||
|
|
||||||
|
// x86/x64 notes: When using GCC < 4.8, this will compile to a spinlock.
|
||||||
|
// On 4.8+, or when using Clang, we'll get the more optimal "lock addl"
|
||||||
|
// variant.
|
||||||
|
static Type Increment(Type *ptr) {
|
||||||
|
return __sync_add_and_fetch(ptr, 1);
|
||||||
|
}
|
||||||
|
static Type Decrement(Type *ptr) {
|
||||||
|
return __sync_sub_and_fetch(ptr, 1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class AtomicRefCount
|
||||||
|
{
|
||||||
|
typedef AtomicOps<sizeof(uintptr_t)> Ops;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AtomicRefCount(uintptr_t value)
|
||||||
|
: value_(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void increment() {
|
||||||
|
Ops::Increment(&value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return false if all references are gone.
|
||||||
|
bool decrement() {
|
||||||
|
return Ops::Decrement(&value_) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ops::Type value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _include_amtl_atomics_h_
|
||||||
|
|
196
public/amtl/am-hashmap.h
Normal file
196
public/amtl/am-hashmap.h
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
// 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_hashmap_h_
|
||||||
|
#define _include_amtl_hashmap_h_
|
||||||
|
|
||||||
|
#include <am-hashtable.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
// Template parameters:
|
||||||
|
//
|
||||||
|
// K - Key type.
|
||||||
|
// V - Value type.
|
||||||
|
// HashPolicy - A struct with a hash and comparator function for each lookup type:
|
||||||
|
// static uint32_t hash(const Type &value);
|
||||||
|
// static bool matches(const Type &value, const K &key);
|
||||||
|
//
|
||||||
|
// All types that match a given key, must compute the same hash.
|
||||||
|
//
|
||||||
|
// Note that like HashTable, a HashMap is not usable until init() has been called.
|
||||||
|
template <typename K,
|
||||||
|
typename V,
|
||||||
|
typename HashPolicy,
|
||||||
|
typename AllocPolicy = SystemAllocatorPolicy>
|
||||||
|
class HashMap : public AllocPolicy
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
struct Entry
|
||||||
|
{
|
||||||
|
K key;
|
||||||
|
V value;
|
||||||
|
|
||||||
|
Entry()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Entry(Moveable<Entry> other)
|
||||||
|
: key(Moveable<K>(other->key)),
|
||||||
|
value(Moveable<V>(other->value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry(const K &aKey, const V &aValue)
|
||||||
|
: key(aKey),
|
||||||
|
value(aValue)
|
||||||
|
{ }
|
||||||
|
Entry(const K &aKey, Moveable<V> aValue)
|
||||||
|
: key(aKey),
|
||||||
|
value(aValue)
|
||||||
|
{ }
|
||||||
|
Entry(Moveable<K> aKey, const V &aValue)
|
||||||
|
: key(aKey),
|
||||||
|
value(aValue)
|
||||||
|
{ }
|
||||||
|
Entry(Moveable<K> aKey, Moveable<V> aValue)
|
||||||
|
: key(aKey),
|
||||||
|
value(aValue)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Policy
|
||||||
|
{
|
||||||
|
typedef Entry Payload;
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
static uint32_t hash(const Lookup &key) {
|
||||||
|
return HashPolicy::hash(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
static bool matches(const Lookup &key, const Payload &payload) {
|
||||||
|
return HashPolicy::matches(key, payload.key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef HashTable<Policy, AllocPolicy> Internal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HashMap(AllocPolicy ap = AllocPolicy())
|
||||||
|
: table_(ap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// capacity must be a power of two.
|
||||||
|
bool init(size_t capacity = 16) {
|
||||||
|
return table_.init(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef typename Internal::Result Result;
|
||||||
|
typedef typename Internal::Insert Insert;
|
||||||
|
typedef typename Internal::iterator iterator;
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
Result find(const Lookup &key) {
|
||||||
|
return table_.find(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
Insert findForAdd(const Lookup &key) {
|
||||||
|
return table_.findForAdd(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
void removeIfExists(const Lookup &key) {
|
||||||
|
return table_.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(Result &r) {
|
||||||
|
table_.remove(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
bool add(Insert &i, Moveable<K> key, const V &value) {
|
||||||
|
return table_.add(i, Entry(key, value));
|
||||||
|
}
|
||||||
|
bool add(Insert &i, const K &key, Moveable<V> value) {
|
||||||
|
return table_.add(i, Entry(key, value));
|
||||||
|
}
|
||||||
|
bool add(Insert &i, Moveable<K> key, Moveable<V> value) {
|
||||||
|
return table_.add(i, Entry(key, value));
|
||||||
|
}
|
||||||
|
bool add(Insert &i, Moveable<K> key) {
|
||||||
|
return table_.add(i, Entry(key, V()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can be used to avoid compiler constructed temporaries, since AMTL
|
||||||
|
// does not yet support move semantics. If you use this, the key and value
|
||||||
|
// must be set after.
|
||||||
|
bool add(Insert &i) {
|
||||||
|
return table_.add(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator iter() {
|
||||||
|
return iterator(&table_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
table_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t elements() const {
|
||||||
|
return table_.elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t estimateMemoryUse() const {
|
||||||
|
return table_.estimateMemoryUse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Internal table_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct PointerPolicy
|
||||||
|
{
|
||||||
|
static inline uint32_t hash(T *p) {
|
||||||
|
return HashPointer(p);
|
||||||
|
}
|
||||||
|
static inline bool matches(T *p1, T *p2) {
|
||||||
|
return p1 == p2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _include_amtl_hashmap_h_
|
129
public/amtl/am-hashset.h
Normal file
129
public/amtl/am-hashset.h
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
// 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_hashmap_h_
|
||||||
|
#define _include_amtl_hashmap_h_
|
||||||
|
|
||||||
|
#include <am-hashtable.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
// Template parameters:
|
||||||
|
//
|
||||||
|
// K - Key type.
|
||||||
|
// HashPolicy - A struct with a hash and comparator function for each lookup type:
|
||||||
|
// static uint32_t hash(const Type &value);
|
||||||
|
// static bool matches(const Type &value, const K &key);
|
||||||
|
//
|
||||||
|
// Like HashMap and HashTable, init() must be called to construct the set.
|
||||||
|
template <typename K,
|
||||||
|
typename HashPolicy,
|
||||||
|
typename AllocPolicy = SystemAllocatorPolicy>
|
||||||
|
class HashSet : public AllocPolicy
|
||||||
|
{
|
||||||
|
struct Policy {
|
||||||
|
typedef K Payload;
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
static uint32_t hash(const Lookup &key) {
|
||||||
|
return HashPolicy::hash(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
static bool matches(const Lookup &key, const Payload &payload) {
|
||||||
|
return HashPolicy::matches(key, payload);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef HashTable<Policy, AllocPolicy> Internal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HashSet(AllocPolicy ap = AllocPolicy())
|
||||||
|
: table_(ap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// capacity must be a power of two.
|
||||||
|
bool init(size_t capacity = 16) {
|
||||||
|
return table_.init(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef typename Internal::Result Result;
|
||||||
|
typedef typename Internal::Insert Insert;
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
Result find(const Lookup &key) {
|
||||||
|
return table_.find(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
Insert findForAdd(const Lookup &key) {
|
||||||
|
return table_.findForAdd(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Lookup>
|
||||||
|
void removeIfExists(const Lookup &key) {
|
||||||
|
return table_.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(Result &r) {
|
||||||
|
table_.remove(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<K> key) {
|
||||||
|
return table_.add(i, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can be used to avoid compiler constructed temporaries, since AMTL
|
||||||
|
// does not yet support move semantics. If you use this, the key and value
|
||||||
|
// must be set after.
|
||||||
|
bool add(Insert &i) {
|
||||||
|
return table_.add(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
table_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t estimateMemoryUse() const {
|
||||||
|
return table_.estimateMemoryUse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Internal table_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _include_amtl_hashset_h_
|
631
public/amtl/am-hashtable.h
Normal file
631
public/amtl/am-hashtable.h
Normal file
|
@ -0,0 +1,631 @@
|
||||||
|
// 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_KEIMA_HASHTABLE_H_
|
||||||
|
#define _INCLUDE_KEIMA_HASHTABLE_H_
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "am-allocator-policies.h"
|
||||||
|
#include "am-utility.h"
|
||||||
|
#include "am-moveable.h"
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename T>
|
||||||
|
class HashTableEntry
|
||||||
|
{
|
||||||
|
uint32_t hash_;
|
||||||
|
T t_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const uint32_t kFreeHash = 0;
|
||||||
|
static const uint32_t kRemovedHash = 1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
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> t) {
|
||||||
|
new (&t_) T(t);
|
||||||
|
}
|
||||||
|
uint32_t hash() const {
|
||||||
|
return hash_;
|
||||||
|
}
|
||||||
|
void setRemoved() {
|
||||||
|
destruct();
|
||||||
|
hash_ = kRemovedHash;
|
||||||
|
}
|
||||||
|
void setFree() {
|
||||||
|
destruct();
|
||||||
|
hash_ = kFreeHash;
|
||||||
|
}
|
||||||
|
void initialize() {
|
||||||
|
hash_ = kFreeHash;
|
||||||
|
}
|
||||||
|
void destruct() {
|
||||||
|
if (isLive())
|
||||||
|
t_.~T();
|
||||||
|
}
|
||||||
|
bool removed() const {
|
||||||
|
return hash_ == kRemovedHash;
|
||||||
|
}
|
||||||
|
bool free() const {
|
||||||
|
return hash_ == kFreeHash;
|
||||||
|
}
|
||||||
|
bool isLive() const {
|
||||||
|
return hash_ > kRemovedHash;
|
||||||
|
}
|
||||||
|
T &payload() {
|
||||||
|
assert(isLive());
|
||||||
|
return t_;
|
||||||
|
}
|
||||||
|
bool sameHash(uint32_t hash) const {
|
||||||
|
return hash_ == hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
HashTableEntry(const HashTableEntry &other) KE_DELETE;
|
||||||
|
HashTableEntry &operator =(const HashTableEntry &other) KE_DELETE;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The HashPolicy for the table must have the following members:
|
||||||
|
//
|
||||||
|
// Payload
|
||||||
|
// static uint32_t hash(const LookupType &key);
|
||||||
|
// static bool matches(const LookupType &key, const Payload &other);
|
||||||
|
//
|
||||||
|
// Payload must be a type, and LookupType is any type that lookups will be
|
||||||
|
// performed with (these functions can be overloaded). Example:
|
||||||
|
//
|
||||||
|
// struct Policy {
|
||||||
|
// typedef KeyValuePair Payload;
|
||||||
|
// static uint32 hash(const Key &key) {
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
// static bool matches(const Key &key, const KeyValuePair &pair) {
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// Note that the table is not usable until init() has been called.
|
||||||
|
//
|
||||||
|
template <typename HashPolicy, typename AllocPolicy = SystemAllocatorPolicy>
|
||||||
|
class HashTable : public AllocPolicy
|
||||||
|
{
|
||||||
|
friend class iterator;
|
||||||
|
|
||||||
|
typedef typename HashPolicy::Payload Payload;
|
||||||
|
typedef detail::HashTableEntry<Payload> Entry;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const uint32_t kMinCapacity = 16;
|
||||||
|
static const uint32_t kMaxCapacity = INT_MAX / sizeof(Entry);
|
||||||
|
|
||||||
|
template <typename Key>
|
||||||
|
uint32_t computeHash(const Key &key) {
|
||||||
|
// Multiply by golden ratio.
|
||||||
|
uint32_t hash = HashPolicy::hash(key) * 0x9E3779B9;
|
||||||
|
if (hash == Entry::kFreeHash || hash == Entry::kRemovedHash)
|
||||||
|
hash += 2;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry *createTable(uint32_t capacity) {
|
||||||
|
assert(capacity <= kMaxCapacity);
|
||||||
|
|
||||||
|
Entry *table = (Entry *)this->malloc(capacity * sizeof(Entry));
|
||||||
|
if (!table)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < capacity; i++)
|
||||||
|
table[i].initialize();
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Result
|
||||||
|
{
|
||||||
|
friend class HashTable;
|
||||||
|
|
||||||
|
Entry *entry_;
|
||||||
|
|
||||||
|
Entry &entry() {
|
||||||
|
return *entry_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Result(Entry *entry)
|
||||||
|
: entry_(entry)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Payload * operator ->() {
|
||||||
|
return &entry_->payload();
|
||||||
|
}
|
||||||
|
Payload & operator *() {
|
||||||
|
return entry_->payload();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found() const {
|
||||||
|
return entry_->isLive();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Insert : public Result
|
||||||
|
{
|
||||||
|
uint32_t hash_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Insert(Entry *entry, uint32_t hash)
|
||||||
|
: Result(entry),
|
||||||
|
hash_(hash)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t hash() const {
|
||||||
|
return hash_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Probulator {
|
||||||
|
uint32_t hash_;
|
||||||
|
uint32_t capacity_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Probulator(uint32_t hash, uint32_t capacity)
|
||||||
|
: hash_(hash),
|
||||||
|
capacity_(capacity)
|
||||||
|
{
|
||||||
|
assert(IsPowerOfTwo(capacity_));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t entry() const {
|
||||||
|
return hash_ & (capacity_ - 1);
|
||||||
|
}
|
||||||
|
uint32_t next() {
|
||||||
|
hash_++;
|
||||||
|
return entry();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool underloaded() const {
|
||||||
|
// Check if the table is underloaded: < 25% entries used.
|
||||||
|
return (capacity_ > kMinCapacity) && (nelements_ + ndeleted_ < capacity_ / 4);
|
||||||
|
}
|
||||||
|
bool overloaded() const {
|
||||||
|
// Grow if the table is overloaded: > 75% entries used.
|
||||||
|
return (nelements_ + ndeleted_) > ((capacity_ / 2) + (capacity_ / 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shrink() {
|
||||||
|
if ((capacity_ >> 1) < minCapacity_)
|
||||||
|
return true;
|
||||||
|
return changeCapacity(capacity_ >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool grow() {
|
||||||
|
if (capacity_ >= kMaxCapacity) {
|
||||||
|
this->reportAllocationOverflow();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return changeCapacity(capacity_ << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changeCapacity(uint32_t newCapacity) {
|
||||||
|
assert(newCapacity <= kMaxCapacity);
|
||||||
|
|
||||||
|
Entry *newTable = createTable(newCapacity);
|
||||||
|
if (!newTable)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Entry *oldTable = table_;
|
||||||
|
uint32_t oldCapacity = capacity_;
|
||||||
|
|
||||||
|
table_ = newTable;
|
||||||
|
capacity_ = newCapacity;
|
||||||
|
ndeleted_ = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < oldCapacity; i++) {
|
||||||
|
Entry &oldEntry = oldTable[i];
|
||||||
|
if (oldEntry.isLive()) {
|
||||||
|
Insert p = insertUnique(oldEntry.hash());
|
||||||
|
p.entry().setHash(p.hash());
|
||||||
|
p.entry().construct(Moveable<Payload>(oldEntry.payload()));
|
||||||
|
}
|
||||||
|
oldEntry.destruct();
|
||||||
|
}
|
||||||
|
this->free(oldTable);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For use when the key is known to be unique.
|
||||||
|
Insert insertUnique(uint32_t hash) {
|
||||||
|
Probulator probulator(hash, capacity_);
|
||||||
|
|
||||||
|
Entry *e = &table_[probulator.entry()];
|
||||||
|
for (;;) {
|
||||||
|
if (e->free() || e->removed())
|
||||||
|
break;
|
||||||
|
e = &table_[probulator.next()];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Insert(e, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key>
|
||||||
|
Result lookup(const Key &key) {
|
||||||
|
uint32_t hash = computeHash(key);
|
||||||
|
Probulator probulator(hash, capacity_);
|
||||||
|
|
||||||
|
Entry *e = &table_[probulator.entry()];
|
||||||
|
for (;;) {
|
||||||
|
if (e->free())
|
||||||
|
break;
|
||||||
|
if (e->isLive() &&
|
||||||
|
e->sameHash(hash) &&
|
||||||
|
HashPolicy::matches(key, e->payload()))
|
||||||
|
{
|
||||||
|
return Result(e);
|
||||||
|
}
|
||||||
|
e = &table_[probulator.next()];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key>
|
||||||
|
Insert lookupForAdd(const Key &key) {
|
||||||
|
uint32_t hash = computeHash(key);
|
||||||
|
Probulator probulator(hash, capacity_);
|
||||||
|
|
||||||
|
Entry *e = &table_[probulator.entry()];
|
||||||
|
for (;;) {
|
||||||
|
if (!e->isLive())
|
||||||
|
break;
|
||||||
|
if (e->sameHash(hash) && HashPolicy::matches(key, e->payload()))
|
||||||
|
break;
|
||||||
|
e = &table_[probulator.next()];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Insert(e, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool internalAdd(Insert &i) {
|
||||||
|
assert(!i.found());
|
||||||
|
|
||||||
|
// If the entry is deleted, just re-use the slot.
|
||||||
|
if (i.entry().removed()) {
|
||||||
|
ndeleted_--;
|
||||||
|
} else {
|
||||||
|
// Otherwise, see if we're at max capacity.
|
||||||
|
if (nelements_ == kMaxCapacity) {
|
||||||
|
this->reportAllocationOverflow();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the table is over or underloaded. The table is always at
|
||||||
|
// least 25% free, so this check is enough to guarantee one free slot.
|
||||||
|
// (Without one free slot, insertion search could infinite loop.)
|
||||||
|
uint32_t oldCapacity = capacity_;
|
||||||
|
if (!checkDensity())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If the table changed size, we need to find a new insertion point.
|
||||||
|
// Note that a removed entry is impossible: either we caught it above,
|
||||||
|
// or we just resized and no entries are removed.
|
||||||
|
if (capacity_ != oldCapacity)
|
||||||
|
i = insertUnique(i.hash());
|
||||||
|
}
|
||||||
|
|
||||||
|
nelements_++;
|
||||||
|
i.entry().setHash(i.hash());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeEntry(Entry &e) {
|
||||||
|
assert(e.isLive());
|
||||||
|
e.setRemoved();
|
||||||
|
ndeleted_++;
|
||||||
|
nelements_--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
HashTable(AllocPolicy ap = AllocPolicy())
|
||||||
|
: AllocPolicy(ap),
|
||||||
|
capacity_(0),
|
||||||
|
nelements_(0),
|
||||||
|
ndeleted_(0),
|
||||||
|
table_(NULL),
|
||||||
|
minCapacity_(kMinCapacity)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~HashTable()
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < capacity_; i++)
|
||||||
|
table_[i].destruct();
|
||||||
|
this->free(table_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init(uint32_t capacity = 0) {
|
||||||
|
if (capacity < kMinCapacity) {
|
||||||
|
capacity = kMinCapacity;
|
||||||
|
} else if (capacity > kMaxCapacity) {
|
||||||
|
this->reportAllocationOverflow();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
minCapacity_ = capacity;
|
||||||
|
|
||||||
|
assert(IsPowerOfTwo(capacity));
|
||||||
|
capacity_ = capacity;
|
||||||
|
|
||||||
|
table_ = createTable(capacity_);
|
||||||
|
if (!table_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Result object must not be used past mutating table operations.
|
||||||
|
template <typename Key>
|
||||||
|
Result find(const Key &key) {
|
||||||
|
return lookup(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Insert object must not be used past mutating table operations.
|
||||||
|
template <typename Key>
|
||||||
|
Insert findForAdd(const Key &key) {
|
||||||
|
return lookupForAdd(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key>
|
||||||
|
void removeIfExists(const Key &key) {
|
||||||
|
Result r = find(key);
|
||||||
|
if (!r.found())
|
||||||
|
return;
|
||||||
|
remove(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(Result &r) {
|
||||||
|
assert(r.found());
|
||||||
|
removeEntry(r.entry());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if (!internalAdd(i))
|
||||||
|
return false;
|
||||||
|
i.entry().construct(payload);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool add(Insert &i, Moveable<Payload> payload) {
|
||||||
|
if (!internalAdd(i))
|
||||||
|
return false;
|
||||||
|
i.entry().construct(payload);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool add(Insert &i) {
|
||||||
|
if (!internalAdd(i))
|
||||||
|
return false;
|
||||||
|
i.entry().construct();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkDensity() {
|
||||||
|
if (underloaded())
|
||||||
|
return shrink();
|
||||||
|
if (overloaded())
|
||||||
|
return grow();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
for (size_t i = 0; i < capacity_; i++) {
|
||||||
|
table_[i].setFree();
|
||||||
|
}
|
||||||
|
ndeleted_ = 0;
|
||||||
|
nelements_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t elements() const {
|
||||||
|
return nelements_;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t estimateMemoryUse() const {
|
||||||
|
return sizeof(Entry) * capacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// It is illegal to mutate a HashTable during iteration.
|
||||||
|
class iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
iterator(HashTable *table)
|
||||||
|
: table_(table),
|
||||||
|
i_(table->table_),
|
||||||
|
end_(table->table_ + table->capacity_)
|
||||||
|
{
|
||||||
|
while (i_ < end_ && !i_->isLive())
|
||||||
|
i_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
return i_ == end_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase() {
|
||||||
|
assert(!empty());
|
||||||
|
table_->removeEntry(*i_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Payload *operator ->() const {
|
||||||
|
return &i_->payload();
|
||||||
|
}
|
||||||
|
Payload &operator *() const {
|
||||||
|
return i_->payload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void next() {
|
||||||
|
do {
|
||||||
|
i_++;
|
||||||
|
} while (i_ < end_ && !i_->isLive());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
HashTable *table_;
|
||||||
|
Entry *i_;
|
||||||
|
Entry *end_;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
HashTable(const HashTable &other) KE_DELETE;
|
||||||
|
HashTable &operator =(const HashTable &other) KE_DELETE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t capacity_;
|
||||||
|
uint32_t nelements_;
|
||||||
|
uint32_t ndeleted_;
|
||||||
|
Entry *table_;
|
||||||
|
uint32_t minCapacity_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Bob Jenkin's one-at-a-time hash function[1].
|
||||||
|
//
|
||||||
|
// [1] http://burtleburtle.net/bob/hash/doobs.html
|
||||||
|
class CharacterStreamHasher
|
||||||
|
{
|
||||||
|
uint32_t hash;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CharacterStreamHasher()
|
||||||
|
: hash(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void add(char c) {
|
||||||
|
hash += c;
|
||||||
|
hash += (hash << 10);
|
||||||
|
hash ^= (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(const char *s, size_t length) {
|
||||||
|
for (size_t i = 0; i < length; i++)
|
||||||
|
add(s[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t result() {
|
||||||
|
hash += (hash << 3);
|
||||||
|
hash ^= (hash >> 11);
|
||||||
|
hash += (hash << 15);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
HashCharSequence(const char *s, size_t length)
|
||||||
|
{
|
||||||
|
CharacterStreamHasher hasher;
|
||||||
|
hasher.add(s, length);
|
||||||
|
return hasher.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
FastHashCharSequence(const char *s, size_t length)
|
||||||
|
{
|
||||||
|
uint32_t hash = 0;
|
||||||
|
for (size_t i = 0; i < length; i++)
|
||||||
|
hash = s[i] + (hash << 6) + (hash << 16) - hash;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From http://burtleburtle.net/bob/hash/integer.html
|
||||||
|
static inline uint32_t
|
||||||
|
HashInt32(int32_t a)
|
||||||
|
{
|
||||||
|
a = (a ^ 61) ^ (a >> 16);
|
||||||
|
a = a + (a << 3);
|
||||||
|
a = a ^ (a >> 4);
|
||||||
|
a = a * 0x27d4eb2d;
|
||||||
|
a = a ^ (a >> 15);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From http://www.cris.com/~Ttwang/tech/inthash.htm
|
||||||
|
static inline uint32_t
|
||||||
|
HashInt64(int64_t key)
|
||||||
|
{
|
||||||
|
key = (~key) + (key << 18); // key = (key << 18) - key - 1;
|
||||||
|
key = key ^ (uint64_t(key) >> 31);
|
||||||
|
key = key * 21; // key = (key + (key << 2)) + (key << 4);
|
||||||
|
key = key ^ (uint64_t(key) >> 11);
|
||||||
|
key = key + (key << 6);
|
||||||
|
key = key ^ (uint64_t(key) >> 22);
|
||||||
|
return uint32_t(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t Size>
|
||||||
|
static inline uint32_t
|
||||||
|
HashInteger(uintptr_t value);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline uint32_t
|
||||||
|
HashInteger<4>(uintptr_t value)
|
||||||
|
{
|
||||||
|
return HashInt32(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline uint32_t
|
||||||
|
HashInteger<8>(uintptr_t value)
|
||||||
|
{
|
||||||
|
return HashInt64(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
HashPointer(void *ptr)
|
||||||
|
{
|
||||||
|
return HashInteger<sizeof(ptr)>(reinterpret_cast<uintptr_t>(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ke
|
||||||
|
|
||||||
|
#endif // _INCLUDE_KEIMA_HASHTABLE_H_
|
183
public/amtl/am-inlinelist.h
Normal file
183
public/amtl/am-inlinelist.h
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
// 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_inline_list_h_
|
||||||
|
#define _include_amtl_inline_list_h_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
template <typename T> class InlineList;
|
||||||
|
|
||||||
|
// Objects can recursively inherit from InlineListNode in order to have
|
||||||
|
// membership in an InlineList<T>.
|
||||||
|
template <typename T>
|
||||||
|
class InlineListNode
|
||||||
|
{
|
||||||
|
friend class InlineList<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
InlineListNode()
|
||||||
|
: next_(NULL),
|
||||||
|
prev_(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InlineListNode(InlineListNode *next, InlineListNode *prev)
|
||||||
|
: next_(next),
|
||||||
|
prev_(prev)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
InlineListNode *next_;
|
||||||
|
InlineListNode *prev_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An InlineList is a linked list that threads link pointers through objects,
|
||||||
|
// rather than allocating node memory. A node can be in at most one list at
|
||||||
|
// any time.
|
||||||
|
//
|
||||||
|
// Since InlineLists are designed to be very cheap, there is no requirement
|
||||||
|
// that elements be removed from a list once the list is destructed. However,
|
||||||
|
// for as long as the list is alive, all of its contained nodes must also
|
||||||
|
// be alive.
|
||||||
|
template <typename T>
|
||||||
|
class InlineList
|
||||||
|
{
|
||||||
|
typedef InlineListNode<T> Node;
|
||||||
|
|
||||||
|
Node head_;
|
||||||
|
|
||||||
|
// Work around a clang bug where we can't initialize with &head_ in the ctor.
|
||||||
|
inline Node *head() {
|
||||||
|
return &head_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
InlineList()
|
||||||
|
: head_(head(), head())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~InlineList()
|
||||||
|
{
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
// Remove all items to clear their next/prev fields.
|
||||||
|
while (begin() != end())
|
||||||
|
remove(*begin());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
class iterator
|
||||||
|
{
|
||||||
|
friend class InlineList;
|
||||||
|
Node *iter_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
iterator(Node *iter)
|
||||||
|
: iter_(iter)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator & operator ++() {
|
||||||
|
iter_ = iter_->next;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
iterator operator ++(int) {
|
||||||
|
iterator old(*this);
|
||||||
|
iter_ = iter_->next_;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
T * operator *() {
|
||||||
|
return static_cast<T *>(iter_);
|
||||||
|
}
|
||||||
|
T * operator ->() {
|
||||||
|
return static_cast<T *>(iter_);
|
||||||
|
}
|
||||||
|
bool operator !=(const iterator &where) const {
|
||||||
|
return iter_ != where.iter_;
|
||||||
|
}
|
||||||
|
bool operator ==(const iterator &where) const {
|
||||||
|
return iter_ == where.iter_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() {
|
||||||
|
return iterator(head_.next_);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() {
|
||||||
|
return iterator(&head_);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator erase(iterator &at) {
|
||||||
|
iterator next = at;
|
||||||
|
next++;
|
||||||
|
|
||||||
|
remove(at.iter_);
|
||||||
|
|
||||||
|
// Iterator is no longer valid.
|
||||||
|
at.iter_ = NULL;
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
return head_.next_ == &head_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(Node *t) {
|
||||||
|
t->prev_->next_ = t->next_;
|
||||||
|
t->next_->prev_ = t->prev_;
|
||||||
|
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
t->next_ = NULL;
|
||||||
|
t->prev_ = NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(Node *t) {
|
||||||
|
assert(!t->next_);
|
||||||
|
assert(!t->prev_);
|
||||||
|
|
||||||
|
t->prev_ = head_.prev_;
|
||||||
|
t->next_ = &head_;
|
||||||
|
head_.prev_->next_ = t;
|
||||||
|
head_.prev_ = t;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _include_amtl_inline_list_h_
|
||||||
|
|
309
public/amtl/am-linkedlist.h
Normal file
309
public/amtl/am-linkedlist.h
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
// 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_linkedlist_h_
|
||||||
|
#define _include_amtl_linkedlist_h_
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <am-allocator-policies.h>
|
||||||
|
#include <am-utility.h>
|
||||||
|
#include <am-moveable.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
// LinkedList, analagous to std::list or SourceHook::List. Since it performs a
|
||||||
|
// malloc() and free() on every contained node, it should be avoided unless
|
||||||
|
// absolutely necessary, or for when allocation performance is not a factor. It
|
||||||
|
// is provided here to safely port old AlliedModders code to AMTL.
|
||||||
|
//
|
||||||
|
// In order to use a circular chain, LinkedList's allocation size includes
|
||||||
|
// exactly one T. If T is very large, LinkedList should be allocated on the
|
||||||
|
// heap, to avoid using the stack.
|
||||||
|
template <class T, class AllocPolicy = SystemAllocatorPolicy>
|
||||||
|
class LinkedList : public AllocPolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
friend class iterator;
|
||||||
|
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Node(const T &o)
|
||||||
|
: obj(o)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Node(Moveable<T> o)
|
||||||
|
: obj(o)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
T obj;
|
||||||
|
Node *next;
|
||||||
|
Node *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
LinkedList(AllocPolicy = AllocPolicy())
|
||||||
|
: length_(0)
|
||||||
|
{
|
||||||
|
head()->prev = head();
|
||||||
|
head()->next = head();
|
||||||
|
}
|
||||||
|
~LinkedList() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool append(const T &obj) {
|
||||||
|
return insertBefore(end(), obj) != end();
|
||||||
|
}
|
||||||
|
bool append(Moveable<T> obj) {
|
||||||
|
return insertBefore(end(), obj) != end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool prepend(const T &obj) {
|
||||||
|
return insertBefore(begin(), obj) != begin();
|
||||||
|
}
|
||||||
|
bool prepend(Moveable<T> obj) {
|
||||||
|
return insertBefore(begin(), obj) != begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t length() const {
|
||||||
|
return length_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
Node *node = head()->next;
|
||||||
|
Node *temp;
|
||||||
|
head()->next = head();
|
||||||
|
head()->prev = head();
|
||||||
|
|
||||||
|
// Iterate through the nodes until we find the sentinel again.
|
||||||
|
while (node != head()) {
|
||||||
|
temp = node->next;
|
||||||
|
freeNode(node);
|
||||||
|
node = temp;
|
||||||
|
}
|
||||||
|
length_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
return (length_ == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
T &front() {
|
||||||
|
assert(!empty());
|
||||||
|
return head()->next->obj;
|
||||||
|
}
|
||||||
|
T &back() {
|
||||||
|
assert(!empty());
|
||||||
|
return head()->prev->obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Node *head() const {
|
||||||
|
return sentinel_.address();
|
||||||
|
}
|
||||||
|
Node *head() {
|
||||||
|
return sentinel_.address();
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *allocNode(const T &obj) {
|
||||||
|
Node *node = (Node *)this->malloc(sizeof(Node));
|
||||||
|
if (!node)
|
||||||
|
return NULL;
|
||||||
|
new (node) Node(obj);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
Node *allocNode(Moveable<T> obj) {
|
||||||
|
Node *node = (Node *)this->malloc(sizeof(Node));
|
||||||
|
if (!node)
|
||||||
|
return NULL;
|
||||||
|
new (node) Node(obj);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeNode(Node *node) {
|
||||||
|
node->obj.~T();
|
||||||
|
this->free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
StorageBuffer<Node> sentinel_;
|
||||||
|
size_t length_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class iterator
|
||||||
|
{
|
||||||
|
friend class LinkedList;
|
||||||
|
|
||||||
|
public:
|
||||||
|
iterator()
|
||||||
|
: this_(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
iterator(const LinkedList &src)
|
||||||
|
: this_(src.head())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
iterator(Node *n)
|
||||||
|
: this_(n)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
iterator(const iterator &where)
|
||||||
|
: this_(where.this_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator &operator --() {
|
||||||
|
if (this_)
|
||||||
|
this_ = this_->prev;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
iterator operator --(int) {
|
||||||
|
iterator old(*this);
|
||||||
|
if (this_)
|
||||||
|
this_ = this_->prev;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
iterator &operator ++() {
|
||||||
|
if (this_)
|
||||||
|
this_ = this_->next;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
iterator operator ++(int) {
|
||||||
|
iterator old(*this);
|
||||||
|
if (this_)
|
||||||
|
this_ = this_->next;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &operator * () const {
|
||||||
|
return this_->obj;
|
||||||
|
}
|
||||||
|
T &operator * () {
|
||||||
|
return this_->obj;
|
||||||
|
}
|
||||||
|
T *operator ->() {
|
||||||
|
return &this_->obj;
|
||||||
|
}
|
||||||
|
const T *operator ->() const {
|
||||||
|
return &(this_->obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator !=(const iterator &where) const {
|
||||||
|
return (this_ != where.this_);
|
||||||
|
}
|
||||||
|
bool operator ==(const iterator &where) const {
|
||||||
|
return (this_ == where.this_);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() {
|
||||||
|
return !!this_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Node *this_;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Insert obj right before where.
|
||||||
|
iterator insert(iterator where, Node *node) {
|
||||||
|
if (!node)
|
||||||
|
return where;
|
||||||
|
|
||||||
|
Node *pWhereNode = where.this_;
|
||||||
|
|
||||||
|
pWhereNode->prev->next = node;
|
||||||
|
node->prev = pWhereNode->prev;
|
||||||
|
pWhereNode->prev = node;
|
||||||
|
node->next = pWhereNode;
|
||||||
|
|
||||||
|
length_++;
|
||||||
|
return iterator(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
iterator begin() {
|
||||||
|
return iterator(head()->next);
|
||||||
|
}
|
||||||
|
iterator end() {
|
||||||
|
return iterator(head());
|
||||||
|
}
|
||||||
|
iterator erase(iterator where) {
|
||||||
|
Node *pNode = where.this_;
|
||||||
|
iterator iter(where);
|
||||||
|
iter++;
|
||||||
|
|
||||||
|
pNode->prev->next = pNode->next;
|
||||||
|
pNode->next->prev = pNode->prev;
|
||||||
|
|
||||||
|
freeNode(pNode);
|
||||||
|
length_--;
|
||||||
|
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
iterator insertBefore(iterator where, const T &obj) {
|
||||||
|
return insert(where, allocNode(obj));
|
||||||
|
}
|
||||||
|
iterator insertBefore(iterator where, Moveable<T> obj) {
|
||||||
|
return insert(where, allocNode(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Removes one instance of |obj| from the list, if found.
|
||||||
|
void remove(const T &obj) {
|
||||||
|
for (iterator b = begin(); b != end(); b++) {
|
||||||
|
if (*b == obj) {
|
||||||
|
erase(b);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
iterator find(const U &equ) {
|
||||||
|
for (iterator iter = begin(); iter != end(); iter++) {
|
||||||
|
if (*iter == equ)
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
// These are disallowed because they basically violate the failure handling
|
||||||
|
// model for AllocPolicies and are also likely to have abysmal performance.
|
||||||
|
LinkedList &operator =(const LinkedList<T> &other) KE_DELETE;
|
||||||
|
LinkedList(const LinkedList<T> &other) KE_DELETE;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ke
|
||||||
|
|
||||||
|
#endif //_INCLUDE_CSDM_LIST_H
|
73
public/amtl/am-moveable.h
Normal file
73
public/amtl/am-moveable.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// 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_moveable_h_
|
||||||
|
#define _include_amtl_moveable_h_
|
||||||
|
|
||||||
|
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> 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.
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Moveable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit Moveable(T &t)
|
||||||
|
: t_(t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
T *operator ->() {
|
||||||
|
return &t_;
|
||||||
|
}
|
||||||
|
operator T &() {
|
||||||
|
return t_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T &t_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline Moveable<T>
|
||||||
|
Move(T &t)
|
||||||
|
{
|
||||||
|
return Moveable<T>(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ke
|
||||||
|
|
||||||
|
#endif // _include_amtl_moveable_h_
|
68
public/amtl/am-refcounting-threadsafe.h
Normal file
68
public/amtl/am-refcounting-threadsafe.h
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// 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_ts_refcounting_h_
|
||||||
|
#define _include_amtl_ts_refcounting_h_
|
||||||
|
|
||||||
|
#include <am-refcounting.h>
|
||||||
|
#include <am-atomics.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class RefcountedThreadsafe
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RefcountedThreadsafe()
|
||||||
|
: refcount_(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddRef() {
|
||||||
|
refcount_.increment();
|
||||||
|
}
|
||||||
|
bool Release() {
|
||||||
|
if (!refcount_.decrement()) {
|
||||||
|
delete static_cast<T *>(this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~RefcountedThreadsafe() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AtomicRefCount refcount_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ke
|
||||||
|
|
||||||
|
#endif // _include_amtl_ts_refcounting_h_
|
302
public/amtl/am-refcounting.h
Normal file
302
public/amtl/am-refcounting.h
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
// vim: set sts=8 ts=4 sw=4 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_refcounting_h_
|
||||||
|
#define _include_amtl_refcounting_h_
|
||||||
|
|
||||||
|
#include <am-utility.h>
|
||||||
|
#include <am-moveable.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
template <typename T> 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<>.
|
||||||
|
template <typename T>
|
||||||
|
class Newborn
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Newborn(T *t)
|
||||||
|
: thing_(t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
T *release() const {
|
||||||
|
return ReturnAndVoid(thing_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable T *thing_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static inline Newborn<T>
|
||||||
|
NoAddRef(T *t)
|
||||||
|
{
|
||||||
|
return Newborn<T>(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// When returning a value, we'd rather not be needlessly changing the refcount,
|
||||||
|
// so we have a special type to use for returns.
|
||||||
|
template <typename T>
|
||||||
|
class PassRef
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PassRef(T *thing)
|
||||||
|
: thing_(thing)
|
||||||
|
{
|
||||||
|
AddRef();
|
||||||
|
}
|
||||||
|
PassRef()
|
||||||
|
: thing_(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PassRef(const Newborn<T *> &other)
|
||||||
|
: thing_(other.release())
|
||||||
|
{
|
||||||
|
// Don't addref, newborn means already addref'd.
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
inline PassRef(const Ref<S> &other);
|
||||||
|
|
||||||
|
PassRef(const PassRef &other)
|
||||||
|
: thing_(other.release())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
template <typename S>
|
||||||
|
PassRef(const PassRef<S> &other)
|
||||||
|
: thing_(other.release())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~PassRef()
|
||||||
|
{
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
operator T &() {
|
||||||
|
return *thing_;
|
||||||
|
}
|
||||||
|
operator T *() const {
|
||||||
|
return thing_;
|
||||||
|
}
|
||||||
|
T *operator ->() const {
|
||||||
|
return operator *();
|
||||||
|
}
|
||||||
|
T *operator *() const {
|
||||||
|
return thing_;
|
||||||
|
}
|
||||||
|
bool operator !() const {
|
||||||
|
return !thing_;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *release() const {
|
||||||
|
return ReturnAndVoid(thing_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
PassRef &operator =(const PassRef<S> &other) {
|
||||||
|
Release();
|
||||||
|
thing_ = other.release();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Disallowed operators.
|
||||||
|
PassRef &operator =(T *other);
|
||||||
|
PassRef &operator =(Newborn<T> &other);
|
||||||
|
|
||||||
|
void AddRef() {
|
||||||
|
if (thing_)
|
||||||
|
thing_->AddRef();
|
||||||
|
}
|
||||||
|
void Release() {
|
||||||
|
if (thing_)
|
||||||
|
thing_->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable T *thing_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Classes which are refcounted should inherit from this.
|
||||||
|
template <typename T>
|
||||||
|
class Refcounted
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Refcounted()
|
||||||
|
: refcount_(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddRef() {
|
||||||
|
refcount_++;
|
||||||
|
}
|
||||||
|
void Release() {
|
||||||
|
assert(refcount_ > 0);
|
||||||
|
if (--refcount_ == 0)
|
||||||
|
delete static_cast<T *>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~Refcounted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uintptr_t refcount_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simple class for automatic refcounting.
|
||||||
|
template <typename T>
|
||||||
|
class Ref
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Ref(T *thing)
|
||||||
|
: thing_(thing)
|
||||||
|
{
|
||||||
|
AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref()
|
||||||
|
: thing_(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref(const Ref &other)
|
||||||
|
: thing_(other.thing_)
|
||||||
|
{
|
||||||
|
AddRef();
|
||||||
|
}
|
||||||
|
Ref(Moveable<Ref> other)
|
||||||
|
: thing_(other->thing_)
|
||||||
|
{
|
||||||
|
other->thing_ = NULL;
|
||||||
|
}
|
||||||
|
template <typename S>
|
||||||
|
Ref(const Ref<S> &other)
|
||||||
|
: thing_(*other)
|
||||||
|
{
|
||||||
|
AddRef();
|
||||||
|
}
|
||||||
|
Ref(const PassRef<T> &other)
|
||||||
|
: thing_(other.release())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
template <typename S>
|
||||||
|
Ref(const PassRef<S> &other)
|
||||||
|
: thing_(other.release())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Ref(const Newborn<T> &other)
|
||||||
|
: thing_(other.release())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~Ref()
|
||||||
|
{
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
T *operator ->() const {
|
||||||
|
return operator *();
|
||||||
|
}
|
||||||
|
T *operator *() const {
|
||||||
|
return thing_;
|
||||||
|
}
|
||||||
|
operator T *() {
|
||||||
|
return thing_;
|
||||||
|
}
|
||||||
|
bool operator !() const {
|
||||||
|
return !thing_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
Ref &operator =(S *thing) {
|
||||||
|
Release();
|
||||||
|
thing_ = thing;
|
||||||
|
AddRef();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
Ref &operator =(const PassRef<S> &other) {
|
||||||
|
Release();
|
||||||
|
thing_ = other.release();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S>
|
||||||
|
Ref &operator =(const Newborn<S> &other) {
|
||||||
|
Release();
|
||||||
|
thing_ = other.release();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref &operator =(const Ref &other) {
|
||||||
|
Release();
|
||||||
|
thing_ = other.thing_;
|
||||||
|
AddRef();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref &operator =(Moveable<Ref> other) {
|
||||||
|
Release();
|
||||||
|
thing_ = other->thing_;
|
||||||
|
other->thing_ = NULL;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AddRef() {
|
||||||
|
if (thing_)
|
||||||
|
thing_->AddRef();
|
||||||
|
}
|
||||||
|
void Release() {
|
||||||
|
if (thing_)
|
||||||
|
thing_->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
T *thing_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> template <typename S>
|
||||||
|
PassRef<T>::PassRef(const Ref<S> &other)
|
||||||
|
: thing_(*other)
|
||||||
|
{
|
||||||
|
AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ke
|
||||||
|
|
||||||
|
#endif // _include_amtl_refcounting_h_
|
||||||
|
|
134
public/amtl/am-string.h
Normal file
134
public/amtl/am-string.h
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// 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_string_h_
|
||||||
|
#define _include_amtl_string_h_
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <am-utility.h>
|
||||||
|
#include <am-moveable.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
// ASCII string.
|
||||||
|
class AString
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AString()
|
||||||
|
: length_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit AString(const char *str) {
|
||||||
|
set(str, strlen(str));
|
||||||
|
}
|
||||||
|
AString(const char *str, size_t length) {
|
||||||
|
set(str, length);
|
||||||
|
}
|
||||||
|
AString(const AString &other) {
|
||||||
|
if (other.length_)
|
||||||
|
set(other.chars_, other.length_);
|
||||||
|
else
|
||||||
|
length_ = 0;
|
||||||
|
}
|
||||||
|
AString(Moveable<AString> other)
|
||||||
|
: chars_(other->chars_.take()),
|
||||||
|
length_(other->length_)
|
||||||
|
{
|
||||||
|
other->length_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AString &operator =(const char *str) {
|
||||||
|
if (str && str[0]) {
|
||||||
|
set(str, strlen(str));
|
||||||
|
} else {
|
||||||
|
chars_ = NULL;
|
||||||
|
length_ = 0;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
AString &operator =(const AString &other) {
|
||||||
|
if (other.length_) {
|
||||||
|
set(other.chars_, other.length_);
|
||||||
|
} else {
|
||||||
|
chars_ = NULL;
|
||||||
|
length_ = 0;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
AString &operator =(Moveable<AString> other) {
|
||||||
|
chars_ = other->chars_.take();
|
||||||
|
length_ = other->length_;
|
||||||
|
other->length_ = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compare(const char *str) const {
|
||||||
|
return strcmp(chars(), str);
|
||||||
|
}
|
||||||
|
int compare(const AString &other) const {
|
||||||
|
return strcmp(chars(), other.chars());
|
||||||
|
}
|
||||||
|
bool operator ==(const AString &other) const {
|
||||||
|
return other.length() == length() &&
|
||||||
|
memcmp(other.chars(), chars(), length()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char operator [](size_t index) const {
|
||||||
|
assert(index < length());
|
||||||
|
return chars()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t length() const {
|
||||||
|
return length_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *chars() const {
|
||||||
|
if (!chars_)
|
||||||
|
return "";
|
||||||
|
return chars_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void set(const char *str, size_t length) {
|
||||||
|
chars_ = new char[length + 1];
|
||||||
|
length_ = length;
|
||||||
|
memcpy(chars_, str, length);
|
||||||
|
chars_[length] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoArray<char> chars_;
|
||||||
|
size_t length_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _include_amtl_string_h_
|
213
public/amtl/am-thread-posix.h
Normal file
213
public/amtl/am-thread-posix.h
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
// 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_thread_posix_h_
|
||||||
|
#define _include_amtl_thread_posix_h_
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#if defined(__linux__)
|
||||||
|
# include <sys/prctl.h>
|
||||||
|
#endif
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
# include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
class Mutex : public Lockable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Mutex() {
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
int rv =
|
||||||
|
#endif
|
||||||
|
pthread_mutex_init(&mutex_, NULL);
|
||||||
|
assert(rv == 0);
|
||||||
|
}
|
||||||
|
~Mutex() {
|
||||||
|
pthread_mutex_destroy(&mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DoTryLock() KE_OVERRIDE {
|
||||||
|
return pthread_mutex_trylock(&mutex_) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoLock() KE_OVERRIDE {
|
||||||
|
pthread_mutex_lock(&mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoUnlock() KE_OVERRIDE {
|
||||||
|
pthread_mutex_unlock(&mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_t *raw() {
|
||||||
|
return &mutex_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
pthread_mutex_t mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Currently, this class only supports single-listener CVs.
|
||||||
|
class ConditionVariable : public Lockable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConditionVariable() {
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
int rv =
|
||||||
|
#endif
|
||||||
|
pthread_cond_init(&cv_, NULL);
|
||||||
|
assert(rv == 0);
|
||||||
|
}
|
||||||
|
~ConditionVariable() {
|
||||||
|
pthread_cond_destroy(&cv_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DoTryLock() KE_OVERRIDE {
|
||||||
|
return mutex_.DoTryLock();
|
||||||
|
}
|
||||||
|
void DoLock() KE_OVERRIDE {
|
||||||
|
mutex_.DoLock();
|
||||||
|
}
|
||||||
|
void DoUnlock() KE_OVERRIDE {
|
||||||
|
mutex_.DoUnlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notify() {
|
||||||
|
AssertCurrentThreadOwns();
|
||||||
|
pthread_cond_signal(&cv_);
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitResult Wait(size_t timeout_ms) {
|
||||||
|
AssertCurrentThreadOwns();
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
struct timespec ts;
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
|
||||||
|
return Wait_Error;
|
||||||
|
#else
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
|
||||||
|
struct timespec ts;
|
||||||
|
ts.tv_sec = tv.tv_sec;
|
||||||
|
ts.tv_nsec = tv.tv_usec * 1000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ts.tv_sec += timeout_ms / 1000;
|
||||||
|
ts.tv_nsec += (timeout_ms % 1000) * 1000000;
|
||||||
|
if (ts.tv_nsec >= 1000000000) {
|
||||||
|
ts.tv_sec++;
|
||||||
|
ts.tv_nsec -= 1000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugSetUnlocked();
|
||||||
|
int rv = pthread_cond_timedwait(&cv_, mutex_.raw(), &ts);
|
||||||
|
DebugSetLocked();
|
||||||
|
|
||||||
|
if (rv == ETIMEDOUT)
|
||||||
|
return Wait_Timeout;
|
||||||
|
if (rv == 0)
|
||||||
|
return Wait_Signaled;
|
||||||
|
return Wait_Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitResult Wait() {
|
||||||
|
AssertCurrentThreadOwns();
|
||||||
|
|
||||||
|
DebugSetUnlocked();
|
||||||
|
int rv = pthread_cond_wait(&cv_, mutex_.raw());
|
||||||
|
DebugSetLocked();
|
||||||
|
|
||||||
|
if (rv == 0)
|
||||||
|
return Wait_Signaled;
|
||||||
|
return Wait_Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mutex mutex_;
|
||||||
|
pthread_cond_t cv_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Thread
|
||||||
|
{
|
||||||
|
struct ThreadData {
|
||||||
|
IRunnable *run;
|
||||||
|
char name[17];
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
Thread(IRunnable *run, const char *name = NULL) {
|
||||||
|
ThreadData *data = new ThreadData;
|
||||||
|
data->run = run;
|
||||||
|
snprintf(data->name, sizeof(data->name), "%s", name ? name : "");
|
||||||
|
|
||||||
|
initialized_ = (pthread_create(&thread_, NULL, Main, data) == 0);
|
||||||
|
if (!initialized_)
|
||||||
|
delete data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Succeeded() const {
|
||||||
|
return initialized_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Join() {
|
||||||
|
if (!Succeeded())
|
||||||
|
return;
|
||||||
|
pthread_join(thread_, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void *Main(void *arg) {
|
||||||
|
AutoPtr<ThreadData> data((ThreadData *)arg);
|
||||||
|
|
||||||
|
if (data->name[0]) {
|
||||||
|
#if defined(__linux__)
|
||||||
|
prctl(PR_SET_NAME, (unsigned long)data->name);
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
int (*fn)(const char *) = (int (*)(const char *))dlsym(RTLD_DEFAULT, "pthread_setname_np");
|
||||||
|
if (fn)
|
||||||
|
fn(data->name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
data->run->Run();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool initialized_;
|
||||||
|
pthread_t thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ke
|
||||||
|
|
||||||
|
#endif // _include_amtl_thread_posix_h_
|
||||||
|
|
265
public/amtl/am-thread-utils.h
Normal file
265
public/amtl/am-thread-utils.h
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
// 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_threads_
|
||||||
|
#define _include_amtl_threads_
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# include <windows.h>
|
||||||
|
# include <WinBase.h>
|
||||||
|
#else
|
||||||
|
# include <pthread.h>
|
||||||
|
#endif
|
||||||
|
#include <am-utility.h>
|
||||||
|
|
||||||
|
// Thread primitives for SourcePawn.
|
||||||
|
//
|
||||||
|
// Linking Requirements:
|
||||||
|
//
|
||||||
|
// OS X: None (-lpthread and -ldl are optional)
|
||||||
|
// Windows: None
|
||||||
|
// Linux: -lpthread -lrt required
|
||||||
|
//
|
||||||
|
// -- Mutexes --
|
||||||
|
//
|
||||||
|
// A Lockable is a mutual exclusion primitive. It can be owned by at most one
|
||||||
|
// thread at a time, and ownership blocks any other thread from taking taking
|
||||||
|
// ownership. Ownership must be acquired and released on the same thread.
|
||||||
|
// Lockables are not re-entrant.
|
||||||
|
//
|
||||||
|
// While a few classes support the Lockable interface, the simplest Lockable
|
||||||
|
// object that can be instantiated is a Mutex.
|
||||||
|
//
|
||||||
|
// -- Condition Variables --
|
||||||
|
//
|
||||||
|
// A ConditionVariable provides mutually exclusive access based on a
|
||||||
|
// condition ocurring. CVs provide two capabilities: Wait(), which will block
|
||||||
|
// until the condition is triggered, and Notify(), which signals any blocking
|
||||||
|
// thread that the condition has occurred.
|
||||||
|
//
|
||||||
|
// Condition variables have an underlying mutex lock. This lock must be
|
||||||
|
// acquired before calling Wait() or Notify(). It is automatically released
|
||||||
|
// once Wait begins blocking. This operation is atomic with respect to other
|
||||||
|
// threads and the mutex. For example, it is not possible for the lock to be
|
||||||
|
// acquired by another thread in between unlocking and blocking. Since Notify
|
||||||
|
// also requires the lock to be acquired, there is no risk of an event
|
||||||
|
// accidentally dissipating into thin air because it was sent before the other
|
||||||
|
// thread began blocking.
|
||||||
|
//
|
||||||
|
// When Wait() returns, the lock is automatically re-acquired. This operation
|
||||||
|
// is NOT atomic. In between waking up and re-acquiring the lock, another
|
||||||
|
// thread may steal the lock and issue another event. Applications must
|
||||||
|
// account for this. For example, a message pump should check that there are
|
||||||
|
// no messages left to process before blocking again.
|
||||||
|
//
|
||||||
|
// Likewise, it is also not defined whether a Signal() will have any effect
|
||||||
|
// while a thread is not waiting on the monitor. This is yet another reason
|
||||||
|
// the above paragraph is so important - applications should, under a lock of
|
||||||
|
// the condition variable - check for state changes before waiting.
|
||||||
|
//
|
||||||
|
// -- Threads --
|
||||||
|
//
|
||||||
|
// A Thread object, when created, spawns a new thread with the given callback
|
||||||
|
// (the callbacks must implement IRunnable). Threads have one method of
|
||||||
|
// interest, Join(), which will block until the thread's execution finishes.
|
||||||
|
// Deleting a thread object will free any operating system resources associated
|
||||||
|
// with that thread, if the thread has finished executing.
|
||||||
|
//
|
||||||
|
// Threads can fail to spawn; make sure to check Succeeded().
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
// Abstraction for getting a unique thread identifier. Debug-only.
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
typedef DWORD ThreadId;
|
||||||
|
|
||||||
|
static inline ThreadId GetCurrentThreadId()
|
||||||
|
{
|
||||||
|
return ::GetCurrentThreadId();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
typedef pthread_t ThreadId;
|
||||||
|
|
||||||
|
static inline ThreadId GetCurrentThreadId()
|
||||||
|
{
|
||||||
|
return pthread_self();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Classes which use non-reentrant, same-thread lock/unlock semantics should
|
||||||
|
// inherit from this and implement DoLock/DoUnlock.
|
||||||
|
class Lockable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Lockable()
|
||||||
|
{
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
owner_ = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
virtual ~Lockable() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryLock() {
|
||||||
|
if (DoTryLock()) {
|
||||||
|
DebugSetLocked();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lock() {
|
||||||
|
assert(Owner() != GetCurrentThreadId());
|
||||||
|
DoLock();
|
||||||
|
DebugSetLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unlock() {
|
||||||
|
assert(Owner() == GetCurrentThreadId());
|
||||||
|
DebugSetUnlocked();
|
||||||
|
DoUnlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssertCurrentThreadOwns() const {
|
||||||
|
assert(Owner() == GetCurrentThreadId());
|
||||||
|
}
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
bool Locked() const {
|
||||||
|
return owner_ != 0;
|
||||||
|
}
|
||||||
|
ThreadId Owner() const {
|
||||||
|
return owner_;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
virtual bool DoTryLock() = 0;
|
||||||
|
virtual void DoLock() = 0;
|
||||||
|
virtual void DoUnlock() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void DebugSetUnlocked() {
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
owner_ = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void DebugSetLocked() {
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
owner_ = GetCurrentThreadId();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
ThreadId owner_;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
// RAII for automatically locking and unlocking an object.
|
||||||
|
class AutoLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AutoLock(Lockable *lock)
|
||||||
|
: lock_(lock)
|
||||||
|
{
|
||||||
|
lock_->Lock();
|
||||||
|
}
|
||||||
|
~AutoLock() {
|
||||||
|
lock_->Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Lockable *lock_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoTryLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AutoTryLock(Lockable *lock)
|
||||||
|
{
|
||||||
|
lock_ = lock->TryLock() ? lock : NULL;
|
||||||
|
}
|
||||||
|
~AutoTryLock() {
|
||||||
|
if (lock_)
|
||||||
|
lock_->Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Lockable *lock_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// RAII for automatically unlocking and relocking an object.
|
||||||
|
class AutoUnlock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AutoUnlock(Lockable *lock)
|
||||||
|
: lock_(lock)
|
||||||
|
{
|
||||||
|
lock_->Unlock();
|
||||||
|
}
|
||||||
|
~AutoUnlock() {
|
||||||
|
lock_->Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Lockable *lock_;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WaitResult {
|
||||||
|
// Woke up because something happened.
|
||||||
|
Wait_Signaled,
|
||||||
|
|
||||||
|
// Woke up because nothing happened and a timeout was specified.
|
||||||
|
Wait_Timeout,
|
||||||
|
|
||||||
|
// Woke up, but because of an error.
|
||||||
|
Wait_Error
|
||||||
|
};
|
||||||
|
|
||||||
|
// This must be implemented in order to spawn a new thread.
|
||||||
|
class IRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IRunnable() {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Run() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ke
|
||||||
|
|
||||||
|
// Include the actual thread implementations.
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# include "am-thread-windows.h"
|
||||||
|
#else
|
||||||
|
# include "am-thread-posix.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _include_amtl_threads_
|
161
public/amtl/am-thread-windows.h
Normal file
161
public/amtl/am-thread-windows.h
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
// 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_thread_windows_h_
|
||||||
|
#define _include_amtl_thread_windows_h_
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
class CriticalSection : public Lockable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CriticalSection() {
|
||||||
|
InitializeCriticalSection(&cs_);
|
||||||
|
}
|
||||||
|
~CriticalSection() {
|
||||||
|
DeleteCriticalSection(&cs_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DoTryLock() KE_OVERRIDE {
|
||||||
|
return !!TryEnterCriticalSection(&cs_);
|
||||||
|
}
|
||||||
|
void DoLock() KE_OVERRIDE {
|
||||||
|
EnterCriticalSection(&cs_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoUnlock() KE_OVERRIDE {
|
||||||
|
LeaveCriticalSection(&cs_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CRITICAL_SECTION cs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef CriticalSection Mutex;
|
||||||
|
|
||||||
|
// Currently, this class only supports single-listener CVs.
|
||||||
|
class ConditionVariable : public Lockable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ConditionVariable() {
|
||||||
|
event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
}
|
||||||
|
~ConditionVariable() {
|
||||||
|
CloseHandle(event_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DoTryLock() KE_OVERRIDE {
|
||||||
|
return cs_.DoTryLock();
|
||||||
|
}
|
||||||
|
void DoLock() KE_OVERRIDE {
|
||||||
|
cs_.DoLock();
|
||||||
|
}
|
||||||
|
void DoUnlock() KE_OVERRIDE {
|
||||||
|
cs_.DoUnlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notify() {
|
||||||
|
AssertCurrentThreadOwns();
|
||||||
|
SetEvent(event_);
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitResult Wait(size_t timeout_ms) {
|
||||||
|
// 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);
|
||||||
|
Lock();
|
||||||
|
|
||||||
|
if (rv == WAIT_TIMEOUT)
|
||||||
|
return Wait_Timeout;
|
||||||
|
if (rv == WAIT_FAILED)
|
||||||
|
return Wait_Error;
|
||||||
|
return Wait_Signaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitResult Wait() {
|
||||||
|
return Wait(INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CriticalSection cs_;
|
||||||
|
HANDLE event_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Thread(IRunnable *run, const char *name = NULL) {
|
||||||
|
thread_ = CreateThread(NULL, 0, Main, run, 0, NULL);
|
||||||
|
}
|
||||||
|
~Thread() {
|
||||||
|
if (!thread_)
|
||||||
|
return;
|
||||||
|
CloseHandle(thread_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Succeeded() const {
|
||||||
|
return !!thread_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Join() {
|
||||||
|
if (!Succeeded())
|
||||||
|
return;
|
||||||
|
WaitForSingleObject(thread_, INFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE handle() const {
|
||||||
|
return thread_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static DWORD WINAPI Main(LPVOID arg) {
|
||||||
|
((IRunnable *)arg)->Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma pack(push, 8)
|
||||||
|
struct ThreadNameInfo {
|
||||||
|
DWORD dwType;
|
||||||
|
LPCSTR szName;
|
||||||
|
DWORD dwThreadID;
|
||||||
|
DWORD dwFlags;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
private:
|
||||||
|
HANDLE thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ke
|
||||||
|
|
||||||
|
#endif // _include_amtl_thread_windows_h_
|
346
public/amtl/am-utility.h
Normal file
346
public/amtl/am-utility.h
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
// 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_utility_h_
|
||||||
|
#define _include_amtl_utility_h_
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
//#include <stdint.h>
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# include <intrin.h>
|
||||||
|
#endif
|
||||||
|
#include <am-moveable.h>
|
||||||
|
|
||||||
|
#define KE_32BIT
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# pragma warning(disable:4355)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
static const size_t kMallocAlignment = sizeof(void *) * 2;
|
||||||
|
|
||||||
|
static const size_t kKB = 1024;
|
||||||
|
static const size_t kMB = 1024 * kKB;
|
||||||
|
static const size_t kGB = 1024 * kMB;
|
||||||
|
|
||||||
|
typedef unsigned char * Address;
|
||||||
|
|
||||||
|
template <typename T> T
|
||||||
|
ReturnAndVoid(T &t)
|
||||||
|
{
|
||||||
|
T saved = t;
|
||||||
|
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 <typename T>
|
||||||
|
class AutoPtr
|
||||||
|
{
|
||||||
|
T *t_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoPtr()
|
||||||
|
: t_(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
AutoPtr(T *t)
|
||||||
|
: t_(t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
AutoPtr(Moveable<AutoPtr<T> > 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 *operator =(Moveable<AutoPtr<T> > 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
|
||||||
|
// to avoid destruction.
|
||||||
|
template <typename T>
|
||||||
|
class AutoArray
|
||||||
|
{
|
||||||
|
T *t_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoArray()
|
||||||
|
: t_(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit AutoArray(T *t)
|
||||||
|
: t_(t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~AutoArray() {
|
||||||
|
delete [] t_;
|
||||||
|
}
|
||||||
|
T *take() {
|
||||||
|
return ReturnAndVoid(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_;
|
||||||
|
}
|
||||||
|
void operator =(T *t) {
|
||||||
|
delete [] t_;
|
||||||
|
t_ = t;
|
||||||
|
}
|
||||||
|
bool operator !() const {
|
||||||
|
return !t_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
Log2(size_t number)
|
||||||
|
{
|
||||||
|
assert(number != 0);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
unsigned long rval;
|
||||||
|
# ifdef _M_IX86
|
||||||
|
_BitScanReverse(&rval, number);
|
||||||
|
# elif _M_X64
|
||||||
|
_BitScanReverse64(&rval, number);
|
||||||
|
# endif
|
||||||
|
return rval;
|
||||||
|
#else
|
||||||
|
size_t bit;
|
||||||
|
asm("bsr %1, %0\n"
|
||||||
|
: "=r" (bit)
|
||||||
|
: "rm" (number));
|
||||||
|
return bit;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
FindRightmostBit(size_t number)
|
||||||
|
{
|
||||||
|
assert(number != 0);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
unsigned long rval;
|
||||||
|
# ifdef _M_IX86
|
||||||
|
_BitScanForward(&rval, number);
|
||||||
|
# elif _M_X64
|
||||||
|
_BitScanForward64(&rval, number);
|
||||||
|
# endif
|
||||||
|
return rval;
|
||||||
|
#else
|
||||||
|
size_t bit;
|
||||||
|
asm("bsf %1, %0\n"
|
||||||
|
: "=r" (bit)
|
||||||
|
: "rm" (number));
|
||||||
|
return bit;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsPowerOfTwo(size_t value)
|
||||||
|
{
|
||||||
|
if (value == 0)
|
||||||
|
return false;
|
||||||
|
return !(value & (value - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
Align(size_t count, size_t alignment)
|
||||||
|
{
|
||||||
|
assert(IsPowerOfTwo(alignment));
|
||||||
|
return count + (alignment - (count % alignment)) % alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsUint32AddSafe(unsigned a, unsigned b)
|
||||||
|
{
|
||||||
|
if (!a || !b)
|
||||||
|
return true;
|
||||||
|
size_t log2_a = Log2(a);
|
||||||
|
size_t log2_b = Log2(b);
|
||||||
|
return (log2_a < sizeof(unsigned) * 8) &&
|
||||||
|
(log2_b < sizeof(unsigned) * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsUintPtrAddSafe(size_t a, size_t b)
|
||||||
|
{
|
||||||
|
if (!a || !b)
|
||||||
|
return true;
|
||||||
|
size_t log2_a = Log2(a);
|
||||||
|
size_t log2_b = Log2(b);
|
||||||
|
return (log2_a < sizeof(size_t) * 8) &&
|
||||||
|
(log2_b < sizeof(size_t) * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsUint32MultiplySafe(unsigned a, unsigned b)
|
||||||
|
{
|
||||||
|
if (a <= 1 || b <= 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
size_t log2_a = Log2(a);
|
||||||
|
size_t log2_b = Log2(b);
|
||||||
|
return log2_a + log2_b <= sizeof(unsigned) * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsUintPtrMultiplySafe(size_t a, size_t b)
|
||||||
|
{
|
||||||
|
if (a <= 1 || b <= 1)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
size_t log2_a = Log2(a);
|
||||||
|
size_t log2_b = Log2(b);
|
||||||
|
return log2_a + log2_b <= sizeof(size_t) * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 <typename T>
|
||||||
|
static inline bool
|
||||||
|
IsAligned(T addr, size_t alignment)
|
||||||
|
{
|
||||||
|
assert(IsPowerOfTwo(alignment));
|
||||||
|
return !(uintptr_t(addr) & (alignment - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Address
|
||||||
|
AlignedBase(Address addr, size_t alignment)
|
||||||
|
{
|
||||||
|
assert(IsPowerOfTwo(alignment));
|
||||||
|
return Address(uintptr_t(addr) & ~(alignment - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> static inline T
|
||||||
|
Min(const T &t1, const T &t2)
|
||||||
|
{
|
||||||
|
return t1 < t2 ? t1 : t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> static inline T
|
||||||
|
Max(const T &t1, const T &t2)
|
||||||
|
{
|
||||||
|
return t1 > t2 ? t1 : t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class StorageBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
T *address() {
|
||||||
|
return reinterpret_cast<T *>(buffer_);
|
||||||
|
}
|
||||||
|
const T *address() const {
|
||||||
|
return reinterpret_cast<const T *>(buffer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
union {
|
||||||
|
char buffer_[sizeof(T)];
|
||||||
|
uint64_t aligned_;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
# define KE_SIZET_FMT "%Iu"
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# define KE_SIZET_FMT "%zu"
|
||||||
|
#else
|
||||||
|
# error "Implement format specifier string"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
# define KE_CRITICAL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||||
|
#else
|
||||||
|
# define KE_CRITICAL_LIKELY(x) x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // _include_amtl_utility_h_
|
239
public/amtl/am-vector.h
Normal file
239
public/amtl/am-vector.h
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
// 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_KEIMA_TPL_CPP_VECTOR_H_
|
||||||
|
#define _INCLUDE_KEIMA_TPL_CPP_VECTOR_H_
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <am-allocator-policies.h>
|
||||||
|
#include <am-utility.h>
|
||||||
|
#include <am-moveable.h>
|
||||||
|
|
||||||
|
namespace ke {
|
||||||
|
|
||||||
|
template <typename T, typename AllocPolicy = SystemAllocatorPolicy>
|
||||||
|
class Vector : public AllocPolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Vector(AllocPolicy = AllocPolicy())
|
||||||
|
: data_(NULL),
|
||||||
|
nitems_(0),
|
||||||
|
maxsize_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector(Moveable<Vector<T, AllocPolicy> > other) {
|
||||||
|
data_ = other->data_;
|
||||||
|
nitems_ = other->nitems_;
|
||||||
|
maxsize_ = other->maxsize_;
|
||||||
|
other->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Vector() {
|
||||||
|
zap();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool append(const T &item) {
|
||||||
|
if (!growIfNeeded(1))
|
||||||
|
return false;
|
||||||
|
new (&data_[nitems_]) T(item);
|
||||||
|
nitems_++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool append(Moveable<T> item) {
|
||||||
|
if (!growIfNeeded(1))
|
||||||
|
return false;
|
||||||
|
new (&data_[nitems_]) T(item);
|
||||||
|
nitems_++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void infallibleAppend(const T &item) {
|
||||||
|
assert(growIfNeeded(1));
|
||||||
|
new (&data_[nitems_]) T(item);
|
||||||
|
nitems_++;
|
||||||
|
}
|
||||||
|
void infallibleAppend(Moveable<T> item) {
|
||||||
|
assert(growIfNeeded(1));
|
||||||
|
new (&data_[nitems_]) T(item);
|
||||||
|
nitems_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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<T> item) {
|
||||||
|
if (at == length())
|
||||||
|
return append(item);
|
||||||
|
if (!moveUp(at))
|
||||||
|
return false;
|
||||||
|
new (&data_[at]) T(item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift all elements at the given position down, removing the given
|
||||||
|
// element. This is a linear-time operation.
|
||||||
|
void remove(size_t at) {
|
||||||
|
for (size_t i = at; i < length() - 1; i++)
|
||||||
|
data_[i] = Moveable<T>(data_[i + 1]);
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
T popCopy() {
|
||||||
|
T t = at(length() - 1);
|
||||||
|
pop();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
void pop() {
|
||||||
|
assert(nitems_);
|
||||||
|
data_[nitems_ - 1].~T();
|
||||||
|
nitems_--;
|
||||||
|
}
|
||||||
|
bool empty() const {
|
||||||
|
return length() == 0;
|
||||||
|
}
|
||||||
|
size_t length() const {
|
||||||
|
return nitems_;
|
||||||
|
}
|
||||||
|
T& at(size_t i) {
|
||||||
|
assert(i < length());
|
||||||
|
return data_[i];
|
||||||
|
}
|
||||||
|
const T& at(size_t i) const {
|
||||||
|
assert(i < length());
|
||||||
|
return data_[i];
|
||||||
|
}
|
||||||
|
T& operator [](size_t i) {
|
||||||
|
return at(i);
|
||||||
|
}
|
||||||
|
const T& operator [](size_t i) const {
|
||||||
|
return at(i);
|
||||||
|
}
|
||||||
|
void clear() {
|
||||||
|
nitems_ = 0;
|
||||||
|
}
|
||||||
|
const T &back() const {
|
||||||
|
return at(length() - 1);
|
||||||
|
}
|
||||||
|
T &back() {
|
||||||
|
return at(length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
T *buffer() const {
|
||||||
|
return data_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ensure(size_t desired) {
|
||||||
|
if (desired <= length())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return growIfNeeded(desired - length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// These are disallowed because they basically violate the failure handling
|
||||||
|
// model for AllocPolicies and are also likely to have abysmal performance.
|
||||||
|
Vector(const Vector<T> &other) KE_DELETE;
|
||||||
|
Vector &operator =(const Vector<T> &other) KE_DELETE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void zap() {
|
||||||
|
for (size_t i = 0; i < nitems_; i++)
|
||||||
|
data_[i].~T();
|
||||||
|
this->free(data_);
|
||||||
|
}
|
||||||
|
void reset() {
|
||||||
|
data_ = NULL;
|
||||||
|
nitems_ = 0;
|
||||||
|
maxsize_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool moveUp(size_t at) {
|
||||||
|
assert(at < nitems_);
|
||||||
|
if (!append(Moveable<T>(data_[nitems_ - 1])))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t i = nitems_ - 2; i > at; i--)
|
||||||
|
data_[i] = Moveable<T>(data_[i - 1]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool growIfNeeded(size_t needed)
|
||||||
|
{
|
||||||
|
if (!IsUintPtrAddSafe(nitems_, needed)) {
|
||||||
|
this->reportAllocationOverflow();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (nitems_ + needed < maxsize_)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
size_t new_maxsize = maxsize_ ? maxsize_ : 8;
|
||||||
|
while (nitems_ + needed > new_maxsize) {
|
||||||
|
if (!IsUintPtrMultiplySafe(new_maxsize, 2)) {
|
||||||
|
this->reportAllocationOverflow();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
new_maxsize *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* newdata = (T*)this->malloc(sizeof(T) * new_maxsize);
|
||||||
|
if (newdata == NULL)
|
||||||
|
return false;
|
||||||
|
for (size_t i = 0; i < nitems_; i++) {
|
||||||
|
new (&newdata[i]) T(Moveable<T>(data_[i]));
|
||||||
|
data_[i].~T();
|
||||||
|
}
|
||||||
|
this->free(data_);
|
||||||
|
|
||||||
|
data_ = newdata;
|
||||||
|
maxsize_ = new_maxsize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* data_;
|
||||||
|
size_t nitems_;
|
||||||
|
size_t maxsize_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _INCLUDE_KEIMA_TPL_CPP_VECTOR_H_ */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user