加入5套spine官方运行时

This commit is contained in:
tianmo
2026-09-07 21:40:29 +08:00
parent 98e51a259e
commit 6837ecada9
935 changed files with 204665 additions and 8 deletions
@@ -0,0 +1,400 @@
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "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 ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) 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
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
#ifndef Spine_Map_h
#define Spine_Map_h
#include <spine/Array.h>
#include <spine/Extension.h>
#include <spine/SpineObject.h>
#include <spine/SpineString.h>
#include <assert.h>
#include <stdint.h>
#include <string.h>
namespace spine {
template<typename K>
class MapHash {
public:
size_t operator()(const K &key) const {
return (size_t) key;
}
};
template<typename T>
class MapHash<T *> {
public:
size_t operator()(T *const &key) const {
uintptr_t value = (uintptr_t) key;
return (size_t) (value >> 4);
}
};
template<>
class MapHash<long long> {
public:
size_t operator()(const long long &key) const {
unsigned long long value = (unsigned long long) key;
return (size_t) (value ^ (value >> 32));
}
};
template<>
class MapHash<unsigned long long> {
public:
size_t operator()(const unsigned long long &key) const {
return (size_t) (key ^ (key >> 32));
}
};
template<>
class MapHash<String> {
public:
size_t operator()(const String &key) const {
const unsigned char *buffer = (const unsigned char *) key.buffer();
size_t length = key.length();
uint64_t hash = 14695981039346656037ULL;
for (size_t i = 0; i < length; i++) {
hash ^= buffer[i];
hash *= 1099511628211ULL;
}
return (size_t) (hash ^ (hash >> 32));
}
};
template<typename K>
class MapEquals {
public:
bool operator()(const K &a, const K &b) const {
return a == b;
}
};
/// A compact open-addressed hash map. Unlike HashMap, this implementation does not allocate per entry.
/// Iteration order is unspecified and may change when the map grows or entries are removed.
template<typename K, typename V, typename Hash = MapHash<K>, typename Equals = MapEquals<K>>
class Map : public SpineObject {
private:
enum State {
Empty = 0,
Occupied = 1,
Deleted = 2
};
struct Entry {
K key;
V value;
};
public:
class Pair {
public:
explicit Pair(K &k, V &v) : key(k), value(v) {
}
K &key;
V &value;
};
class Entries {
public:
friend class Map;
bool hasNext() {
while (_index < _map->_capacity) {
if (_map->_states[_index] == Occupied) return true;
_index++;
}
return false;
}
Pair next() {
assert(_index < _map->_capacity);
assert(_map->_states[_index] == Occupied);
Entry &entry = _map->_entries[_index++];
return Pair(entry.key, entry.value);
}
private:
explicit Entries(Map &map) : _map(&map), _index(0) {
}
Map *_map;
size_t _index;
};
Map() : _entries(NULL), _states(NULL), _capacity(0), _size(0), _deleted(0), _hash(), _equals() {
}
explicit Map(size_t capacity) : _entries(NULL), _states(NULL), _capacity(0), _size(0), _deleted(0), _hash(), _equals() {
ensureCapacity(capacity);
}
Map(const Map &other) : _entries(NULL), _states(NULL), _capacity(0), _size(0), _deleted(0), _hash(other._hash), _equals(other._equals) {
ensureCapacity(other._size);
for (size_t i = 0; i < other._capacity; i++) {
if (other._states[i] == Occupied) put(other._entries[i].key, other._entries[i].value);
}
}
Map &operator=(const Map &other) {
if (this == &other) return *this;
clear();
_hash = other._hash;
_equals = other._equals;
ensureCapacity(other._size);
for (size_t i = 0; i < other._capacity; i++) {
if (other._states[i] == Occupied) put(other._entries[i].key, other._entries[i].value);
}
return *this;
}
~Map() {
release();
}
size_t size() const {
return _size;
}
size_t getCapacity() const {
return _capacity;
}
bool isEmpty() const {
return _size == 0;
}
void ensureCapacity(size_t expectedSize) {
size_t needed = capacityFor(expectedSize);
if (_capacity < needed) rehash(needed);
}
void clear() {
for (size_t i = 0; i < _capacity; i++) {
if (_states[i] == Occupied) resetEntry(i);
_states[i] = Empty;
}
_size = 0;
_deleted = 0;
}
void release() {
if (!_entries) return;
clear();
for (size_t i = 0; i < _capacity; i++) _entries[i].~Entry();
SpineExtension::free(_entries, __FILE__, __LINE__);
SpineExtension::free(_states, __FILE__, __LINE__);
_entries = NULL;
_states = NULL;
_capacity = 0;
}
bool containsKey(const K &key) const {
return findIndex(key) >= 0;
}
V *get(const K &key) {
int index = findIndex(key);
return index >= 0 ? &_entries[index].value : NULL;
}
const V *get(const K &key) const {
int index = findIndex(key);
return index >= 0 ? &_entries[index].value : NULL;
}
V operator[](const K &key) const {
const V *value = get(key);
assert(value);
return *value;
}
void put(const K &key, const V &value) {
ensureLoadForOneMore();
bool found = false;
size_t index = findSlot(key, found);
if (found) {
_entries[index].value = value;
return;
}
if (_states[index] == Deleted) _deleted--;
_states[index] = Occupied;
_entries[index].key = key;
_entries[index].value = value;
_size++;
}
/// Adds the key and value only if the key is not already present.
/// @return The existing value, or V() if the key was not present and the new value was added.
V putMissing(const K &key, const V &value) {
ensureLoadForOneMore();
bool found = false;
size_t index = findSlot(key, found);
if (found) return _entries[index].value;
if (_states[index] == Deleted) _deleted--;
_states[index] = Occupied;
_entries[index].key = key;
_entries[index].value = value;
_size++;
return V();
}
bool addAll(Array<K> &keys, const V &value) {
size_t oldSize = _size;
ensureCapacity(_size + keys.size());
for (size_t i = 0; i < keys.size(); i++) put(keys[i], value);
return _size != oldSize;
}
bool remove(const K &key) {
int index = findIndex(key);
if (index < 0) return false;
resetEntry((size_t) index);
_states[index] = Deleted;
_size--;
_deleted++;
if (_size == 0) {
memset(_states, Empty, _capacity);
_deleted = 0;
}
return true;
}
Entries getEntries() {
return Entries(*this);
}
private:
Entry *_entries;
unsigned char *_states;
size_t _capacity;
size_t _size;
size_t _deleted;
Hash _hash;
Equals _equals;
static size_t mix(size_t h) {
h ^= h >> 16;
h *= (size_t) 0x7feb352d;
h ^= h >> 15;
h *= (size_t) 0x846ca68b;
h ^= h >> 16;
return h;
}
static size_t nextPowerOfTwo(size_t value) {
size_t result = 8;
while (result < value) result <<= 1;
return result;
}
static size_t capacityFor(size_t expectedSize) {
if (expectedSize < 6) return 8;
return nextPowerOfTwo((expectedSize * 4 + 2) / 3);
}
void ensureLoadForOneMore() {
if (_capacity == 0) {
rehash(8);
return;
}
if ((_size + _deleted + 1) * 4 > _capacity * 3) {
size_t newCapacity = _deleted > _size ? _capacity : _capacity << 1;
rehash(newCapacity);
}
}
int findIndex(const K &key) const {
if (_capacity == 0) return -1;
size_t mask = _capacity - 1;
size_t index = mix(_hash(key)) & mask;
while (true) {
unsigned char state = _states[index];
if (state == Empty) return -1;
if (state == Occupied && _equals(_entries[index].key, key)) return (int) index;
index = (index + 1) & mask;
}
}
/// Returns an index containing key, an empty slot, or the first deleted slot encountered.
size_t findSlot(const K &key, bool &found) const {
size_t mask = _capacity - 1;
size_t index = mix(_hash(key)) & mask;
size_t firstDeleted = (size_t) -1;
while (true) {
unsigned char state = _states[index];
if (state == Empty) {
found = false;
return firstDeleted != (size_t) -1 ? firstDeleted : index;
}
if (state == Deleted) {
if (firstDeleted == (size_t) -1) firstDeleted = index;
} else if (_equals(_entries[index].key, key)) {
found = true;
return index;
}
index = (index + 1) & mask;
}
}
void rehash(size_t newCapacity) {
newCapacity = nextPowerOfTwo(newCapacity);
Entry *oldEntries = _entries;
unsigned char *oldStates = _states;
size_t oldCapacity = _capacity;
_entries = SpineExtension::alloc<Entry>(newCapacity, __FILE__, __LINE__);
_states = SpineExtension::calloc<unsigned char>(newCapacity, __FILE__, __LINE__);
assert(_entries);
assert(_states);
_capacity = newCapacity;
_size = 0;
_deleted = 0;
for (size_t i = 0; i < _capacity; i++) new (_entries + i) Entry();
if (oldEntries) {
for (size_t i = 0; i < oldCapacity; i++) {
if (oldStates[i] == Occupied) put(oldEntries[i].key, oldEntries[i].value);
oldEntries[i].~Entry();
}
SpineExtension::free(oldEntries, __FILE__, __LINE__);
SpineExtension::free(oldStates, __FILE__, __LINE__);
}
}
void resetEntry(size_t index) {
_entries[index].key = K();
_entries[index].value = V();
}
};
}
#endif /* Spine_Map_h */