From 90a2d1bb3905a37238ba74719355a2c2335cfb73 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 23 Aug 2013 22:29:44 -0700 Subject: [PATCH] Update AMTL with a number of changes. - Introduce HashMap, a key-value map based on HashTable. - Introduce LinkedList, to port from SourceHook::List. - Introduce AString, to port from SourceHook::String. - Introduce KE_OVERRIDE and KE_DELETE helpers for C++11. - HashTable now constructs/destructs only live items. - Fix insert-on-removed-item bug in HashTable. - Fix Vector keeping a new maxsize if allocation fails. - Renamed am-inline-list.h to am-inlinelist.h. --HG-- rename : public/amtl/am-inline-list.h => public/amtl/am-inlinelist.h --- public/amtl/am-allocator-policies.h | 2 +- public/amtl/am-hashmap.h | 133 ++++ public/amtl/am-hashtable.h | 586 ++++++++++++++++++ .../{am-inline-list.h => am-inlinelist.h} | 0 public/amtl/am-linkedlist.h | 290 +++++++++ public/amtl/am-string.h | 101 +++ public/amtl/am-thread-posix.h | 12 +- public/amtl/am-thread-windows.h | 12 +- public/amtl/am-utility.h | 131 ++-- public/amtl/am-vector.h | 26 +- sourcepawn/jit/BaseRuntime.h | 2 +- 11 files changed, 1206 insertions(+), 89 deletions(-) create mode 100644 public/amtl/am-hashmap.h create mode 100644 public/amtl/am-hashtable.h rename public/amtl/{am-inline-list.h => am-inlinelist.h} (100%) create mode 100644 public/amtl/am-linkedlist.h create mode 100644 public/amtl/am-string.h diff --git a/public/amtl/am-allocator-policies.h b/public/amtl/am-allocator-policies.h index 8a74adc5..5f9712bf 100644 --- a/public/amtl/am-allocator-policies.h +++ b/public/amtl/am-allocator-policies.h @@ -38,7 +38,7 @@ namespace ke { // The default system allocator policy will crash on out-of-memory. class SystemAllocatorPolicy { - protected: + public: void reportOutOfMemory() { fprintf(stderr, "OUT OF MEMORY\n"); abort(); diff --git a/public/amtl/am-hashmap.h b/public/amtl/am-hashmap.h new file mode 100644 index 00000000..3ddd5278 --- /dev/null +++ b/public/amtl/am-hashmap.h @@ -0,0 +1,133 @@ +// 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 + +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 +class HashMap : public AllocPolicy +{ + private: + struct Entry + { + K key; + V value; + + Entry(const K &aKey, const V &aValue) + : key(aKey), + value(aValue) + { + } + }; + + struct Policy + { + typedef Entry Payload; + + template + static uint32_t hash(const Lookup &key) { + return HashPolicy::hash(key); + } + + template + static bool matches(const Lookup &key, const Payload &payload) { + return HashPolicy::matches(key, payload.key); + } + }; + + typedef HashTable 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; + + template + Result find(const Lookup &key) { + return table_.find(key); + } + + template + Insert findForAdd(const Lookup &key) { + return table_.findForAdd(key); + } + + template + 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)); + } + + size_t estimateMemoryUse() const { + return table_.estimateMemoryUse(); + } + + private: + Internal table_; +}; + +} + +#endif // _include_amtl_hashmap_h_ diff --git a/public/amtl/am-hashtable.h b/public/amtl/am-hashtable.h new file mode 100644 index 00000000..de0a7259 --- /dev/null +++ b/public/amtl/am-hashtable.h @@ -0,0 +1,586 @@ +// 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 +#include +#include +#include "am-utility.h" + +namespace ke { + +namespace detail { + template + 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, const T &t) { + hash_ = hash; + new (&t_) T(t); + } + uint32_t hash() const { + return hash_; + } + void setRemoved() { + destruct(); + hash_ = kRemovedHash; + } + 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 +class HashTable : public AllocPolicy +{ + friend class iterator; + + typedef typename HashPolicy::Payload Payload; + typedef detail::HashTableEntry Entry; + + private: + static const uint32_t kMinCapacity = 16; + static const uint32_t kMaxCapacity = INT_MAX / sizeof(Entry); + + template + 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(), 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 + 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 + 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, const Payload &payload) { + 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()); + } + + i.entry().setHash(i.hash(), payload); + nelements_++; + return true; + } + + void removeEntry(Entry &e) { + assert(e.isLive()); + e.setRemoved(); + ndeleted_++; + nelements_--; + } + + public: + HashTable(AllocPolicy ap) + : 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 + Result find(const Key &key) { + return lookup(key); + } + + // The Insert object must not be used past mutating table operations. + template + Insert findForAdd(const Key &key) { + return lookupForAdd(key); + } + + template + 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) { + return internalAdd(i, payload); + } + + bool checkDensity() { + if (underloaded()) + return shrink(); + if (overloaded()) + return grow(); + return true; + } + + 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_); + } + + const 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(key) >> 31); + key = key * 21; // key = (key + (key << 2)) + (key << 4); + key = key ^ (uint64(key) >> 11); + key = key + (key << 6); + key = key ^ (uint64(key) >> 22); + return uint32(key); +} + +template +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(reinterpret_cast(ptr)); +} + +} // namespace ke + +#endif // _INCLUDE_KEIMA_HASHTABLE_H_ diff --git a/public/amtl/am-inline-list.h b/public/amtl/am-inlinelist.h similarity index 100% rename from public/amtl/am-inline-list.h rename to public/amtl/am-inlinelist.h diff --git a/public/amtl/am-linkedlist.h b/public/amtl/am-linkedlist.h new file mode 100644 index 00000000..5c06930b --- /dev/null +++ b/public/amtl/am-linkedlist.h @@ -0,0 +1,290 @@ +// 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 +#include +#include +#include + +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 LinkedList : public AllocPolicy +{ + public: + friend class iterator; + + class Node + { + public: + Node(const T &o) + : obj(o) + { + } + + T obj; + Node *next; + Node *prev; + }; + +public: + LinkedList(AllocPolicy = AllocPolicy()) + : length_(0) + { + } + ~LinkedList() { + clear(); + } + + bool append(const T &obj) { + Node *node = allocNode(obj); + if (!node) + return false; + + node->prev = head()->prev; + node->next = head(); + head()->prev->next = node; + head()->prev = node; + + length_++; + } + + bool prepend(const T &obj) { + return insert(begin(), obj); + } + + size_t size() 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; + } + + void freeNode(Node *node) { + node->obj.~T(); + this->free(node); + } + + private: + StorageBuffer 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_; + }; + + public: + iterator begin() const { + return iterator(head()->next); + } + iterator end() const { + 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 insert(iterator where, const T &obj) { + // Insert obj right before where + Node *node = allocNode(obj); + 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: + void remove(const T &obj) { + for (iterator b = begin(); b != end(); b++) { + if (*b == obj) { + erase(b); + break; + } + } + } + + template + iterator find(const U &equ) const { + 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 &other) KE_DELETE; + LinkedList(const LinkedList &other) KE_DELETE; +}; + +} // namespace ke + +#endif //_INCLUDE_CSDM_LIST_H diff --git a/public/amtl/am-string.h b/public/amtl/am-string.h new file mode 100644 index 00000000..d91e2542 --- /dev/null +++ b/public/amtl/am-string.h @@ -0,0 +1,101 @@ +// 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 +#include +#include + +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) { + set(other.chars_, other.length_); + } + + AString &operator =(const char *str) { + set(str, strlen(str)); + return *this; + } + AString &operator =(const AString &other) { + set(other.chars_, other.length_); + return *this; + } + + 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 chars_; + size_t length_; +}; + +} + +#endif // _include_amtl_string_h_ diff --git a/public/amtl/am-thread-posix.h b/public/amtl/am-thread-posix.h index d103389e..bc654261 100644 --- a/public/amtl/am-thread-posix.h +++ b/public/amtl/am-thread-posix.h @@ -57,15 +57,15 @@ class Mutex : public Lockable pthread_mutex_destroy(&mutex_); } - bool DoTryLock() { + bool DoTryLock() KE_OVERRIDE { return pthread_mutex_trylock(&mutex_) == 0; } - void DoLock() { + void DoLock() KE_OVERRIDE { pthread_mutex_lock(&mutex_); } - void DoUnlock() { + void DoUnlock() KE_OVERRIDE { pthread_mutex_unlock(&mutex_); } @@ -92,13 +92,13 @@ class ConditionVariable : public Lockable pthread_cond_destroy(&cv_); } - bool DoTryLock() { + bool DoTryLock() KE_OVERRIDE { return mutex_.DoTryLock(); } - void DoLock() { + void DoLock() KE_OVERRIDE { mutex_.DoLock(); } - void DoUnlock() { + void DoUnlock() KE_OVERRIDE { mutex_.DoUnlock(); } diff --git a/public/amtl/am-thread-windows.h b/public/amtl/am-thread-windows.h index 5e6970c2..c4e34ed2 100644 --- a/public/amtl/am-thread-windows.h +++ b/public/amtl/am-thread-windows.h @@ -44,14 +44,14 @@ class CriticalSection : public Lockable DeleteCriticalSection(&cs_); } - bool DoTryLock() { + bool DoTryLock() KE_OVERRIDE { return !!TryEnterCriticalSection(&cs_); } - void DoLock() { + void DoLock() KE_OVERRIDE { EnterCriticalSection(&cs_); } - void DoUnlock() { + void DoUnlock() KE_OVERRIDE { LeaveCriticalSection(&cs_); } @@ -72,13 +72,13 @@ class ConditionVariable : public Lockable CloseHandle(event_); } - bool DoTryLock() { + bool DoTryLock() KE_OVERRIDE { return cs_.DoTryLock(); } - void DoLock() { + void DoLock() KE_OVERRIDE { cs_.DoLock(); } - void DoUnlock() { + void DoUnlock() KE_OVERRIDE { cs_.DoUnlock(); } diff --git a/public/amtl/am-utility.h b/public/amtl/am-utility.h index cdbf61f5..83281cb9 100644 --- a/public/amtl/am-utility.h +++ b/public/amtl/am-utility.h @@ -108,80 +108,49 @@ class AutoPtr } }; -// Bob Jenkin's one-at-a-time hash function[1]. -// -// [1] http://burtleburtle.net/bob/hash/doobs.html -class CharacterStreamHasher +// Wrapper that automatically deletes its contents. The pointer can be taken +// to avoid destruction. +template +class AutoArray { - uint32 hash; + T *t_; public: - CharacterStreamHasher() - : hash(0) - { } - - void add(char c) { - hash += c; - hash += (hash << 10); - hash ^= (hash >> 6); + AutoArray() + : t_(NULL) + { } - - void add(const char *s, size_t length) { - for (size_t i = 0; i < length; i++) - add(s[i]); + explicit AutoArray(T *t) + : t_(t) + { } - - uint32 result() { - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - return hash; + ~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 uint32 -HashCharSequence(const char *s, size_t length) -{ - CharacterStreamHasher hasher; - hasher.add(s, length); - return hasher.result(); -} - -// From http://burtleburtle.net/bob/hash/integer.html -static inline uint32 -HashInt32(int32 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 -HashInt64(int64 key) -{ - key = (~key) + (key << 18); // key = (key << 18) - key - 1; - key = key ^ (uint64(key) >> 31); - key = key * 21; // key = (key + (key << 2)) + (key << 4); - key = key ^ (uint64(key) >> 11); - key = key + (key << 6); - key = key ^ (uint64(key) >> 22); - return uint32(key); -} - -static inline uint32 -HashPointer(void *p) -{ -#if defined(KE_32BIT) - return HashInt32(reinterpret_cast(p)); -#elif defined(KE_64BIT) - return HashInt64(reinterpret_cast(p)); -#endif -} - static inline size_t Log2(size_t number) { @@ -317,6 +286,36 @@ Max(const T &t1, const T &t2) return t1 > t2 ? t1 : t2; } +template +class StorageBuffer +{ + public: + T *address() { + return reinterpret_cast(buffer_); + } + const T *address() const { + return reinterpret_cast(buffer_); + } + + private: + union { + char buffer_[sizeof(T)]; + uint64_t aligned_; + }; +}; + +#if __cplusplus >= 201103L +# define KE_CXX11 +#endif + +#if defined(KE_CXX11) +# define KE_DELETE delete +# define KE_OVERRIDE override +#else +# define KE_DELETE +# define KE_OVERRIDE +#endif + #if defined(_MSC_VER) # define KE_SIZET_FMT "%Iu" #elif defined(__GNUC__) diff --git a/public/amtl/am-vector.h b/public/amtl/am-vector.h index c70b2281..5facd517 100644 --- a/public/amtl/am-vector.h +++ b/public/amtl/am-vector.h @@ -48,8 +48,7 @@ class Vector : public AllocPolicy { } - ~Vector() - { + ~Vector() { zap(); } @@ -61,7 +60,7 @@ class Vector : public AllocPolicy other.reset(); } - bool append(const T& item) { + bool append(const T &item) { if (!growIfNeeded(1)) return false; new (&data_[nitems_]) T(item); @@ -124,6 +123,12 @@ class Vector : public AllocPolicy 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 &other) KE_DELETE; + Vector &operator =(const Vector &other) KE_DELETE; + private: void zap() { for (size_t i = 0; i < nitems_; i++) @@ -144,16 +149,17 @@ class Vector : public AllocPolicy } if (nitems_ + needed < maxsize_) return true; - if (maxsize_ == 0) - maxsize_ = 8; - while (nitems_ + needed > maxsize_) { - if (!IsUintPtrMultiplySafe(maxsize_, 2)) { + + size_t new_maxsize = maxsize_ ? maxsize_ : 8; + while (nitems_ + needed > new_maxsize) { + if (!IsUintPtrMultiplySafe(new_maxsize, 2)) { this->reportAllocationOverflow(); return false; } - maxsize_ *= 2; + new_maxsize *= 2; } - T* newdata = (T*)this->malloc(sizeof(T) * maxsize_); + + T* newdata = (T*)this->malloc(sizeof(T) * new_maxsize); if (newdata == NULL) return false; for (size_t i = 0; i < nitems_; i++) { @@ -161,7 +167,9 @@ class Vector : public AllocPolicy data_[i].~T(); } this->free(data_); + data_ = newdata; + maxsize_ = new_maxsize; return true; } diff --git a/sourcepawn/jit/BaseRuntime.h b/sourcepawn/jit/BaseRuntime.h index 04f3560c..3ccd5c9c 100644 --- a/sourcepawn/jit/BaseRuntime.h +++ b/sourcepawn/jit/BaseRuntime.h @@ -4,7 +4,7 @@ #include #include -#include +#include #include "jit_shared.h" #include "sp_vm_function.h"