update headers

This commit is contained in:
Nick Fisher
2022-12-05 17:51:44 +08:00
parent 8d562f1742
commit dd100653dc
234 changed files with 62619 additions and 9800 deletions

View File

@@ -51,7 +51,7 @@ static inline P* align(P* p, size_t alignment) noexcept {
template <typename P>
static inline P* align(P* p, size_t alignment, size_t offset) noexcept {
P* const r = align(add(p, offset), alignment);
assert(pointermath::add(r, -offset) >= p);
assert(r >= add(p, offset));
return r;
}
@@ -129,7 +129,9 @@ public:
private:
void* end() UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mSize); }
void* current() UTILS_RESTRICT noexcept { return pointermath::add(mBegin, mCur); }
void set_current(void* p) UTILS_RESTRICT noexcept { mCur = uintptr_t(p) - uintptr_t(mBegin); }
void set_current(void* p) UTILS_RESTRICT noexcept {
mCur = uint32_t(uintptr_t(p) - uintptr_t(mBegin));
}
void* mBegin = nullptr;
uint32_t mSize = 0;
@@ -167,7 +169,7 @@ public:
~HeapAllocator() noexcept = default;
void swap(HeapAllocator& rhs) noexcept { }
void swap(HeapAllocator&) noexcept { }
};
// ------------------------------------------------------------------------------------------------
@@ -487,11 +489,15 @@ namespace TrackingPolicy {
// default no-op tracker
struct Untracked {
Untracked() noexcept = default;
Untracked(const char* name, void* base, size_t size) noexcept { }
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept { }
void onFree(void* p, size_t = 0) noexcept { }
Untracked(const char* name, void* base, size_t size) noexcept {
(void)name, void(base), (void)size;
}
void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept {
(void)p, (void)size, (void)alignment, (void)extra;
}
void onFree(void* p, size_t = 0) noexcept { (void)p; }
void onReset() noexcept { }
void onRewind(void* addr) noexcept { }
void onRewind(void* addr) noexcept { (void)addr; }
};
// This just track the max memory usage and logs it in the destructor

View File

@@ -31,7 +31,7 @@ class BinaryTreeArray {
// Simple fixed capacity stack
template<typename TYPE, size_t CAPACITY,
typename = typename std::enable_if<std::is_pod<TYPE>::value>::type>
typename = typename std::enable_if<std::is_trivial<TYPE>::value>::type>
class stack {
TYPE mElements[CAPACITY];
size_t mSize = 0;
@@ -52,8 +52,10 @@ class BinaryTreeArray {
public:
static size_t count(size_t height) noexcept { return (1u << height) - 1; }
static size_t left(size_t i, size_t height) noexcept { return i + 1; }
static size_t right(size_t i, size_t height) noexcept { return i + (1u << (height - 1)); }
static size_t left(size_t i, size_t /*height*/) noexcept { return i + 1; }
static size_t right(size_t i, size_t height) noexcept {
return i + (size_t(1) << (height - 1));
}
// this builds the depth-first binary tree array top down (post-order)
template<typename Leaf, typename Node>

View File

@@ -26,10 +26,56 @@
#include <stdint.h>
namespace utils {
template<typename Enum>
struct EnableBitMaskOperators : public std::false_type { };
template<typename Enum>
struct EnableIntegerOperators : public std::false_type { };
namespace Enum {
template<typename Enum>
size_t count();
} // namespace enum
} // namespace utils
// ------------------------------------------------------------------------------------------------
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
inline constexpr int operator+(Enum value) noexcept {
return int(value);
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
inline constexpr bool operator==(Enum lhs, size_t rhs) noexcept {
using underlying_t = std::underlying_type_t<Enum>;
return underlying_t(lhs) == rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
inline constexpr bool operator==(size_t lhs, Enum rhs) noexcept {
return rhs == lhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
inline constexpr bool operator!=(Enum lhs, size_t rhs) noexcept {
return !(rhs == lhs);
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableIntegerOperators<Enum>::value, int> = 0>
inline constexpr bool operator!=(size_t lhs, Enum rhs) noexcept {
return rhs != lhs;
}
// ------------------------------------------------------------------------------------------------
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool operator!(Enum rhs) noexcept {

View File

@@ -42,157 +42,9 @@ struct hashCStrings {
}
};
//! \privatesection
struct equalCStrings {
typedef const char* first_argument_type;
typedef const char* second_argument_type;
typedef bool result_type;
bool operator()(const char* lhs, const char* rhs) const noexcept {
return !strcmp(lhs, rhs);
}
};
//! \privatesection
struct lessCStrings {
typedef const char* first_argument_type;
typedef const char* second_argument_type;
typedef bool result_type;
result_type operator()(first_argument_type lhs, second_argument_type rhs) const noexcept {
return strcmp(lhs, rhs) < 0;
}
};
// This can be used to creates a string from a string literal -- w/o underlying allocations.
// e.g.:
// StaticString s("Hello World!");
//
template <size_t N>
using StringLiteral = const char[N];
//! \publicsection
class UTILS_PUBLIC StaticString {
public:
using value_type = char;
using size_type = uint32_t;
using difference_type = int32_t;
using const_reference = const value_type&;
using const_pointer = const value_type*;
using const_iterator = const value_type*;
constexpr StaticString() noexcept {} // NOLINT(modernize-use-equals-default), Ubuntu compiler bug
// initialization from a string literal
template<size_t N>
constexpr StaticString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
: mString(other),
mLength(size_type(N - 1)),
mHash(computeHash(other, N - 1)) {
// we rely on inlining for computeHash. It would be nice to do this with constexpr
// instead, but unfortunately 'other' is not constexpr once a parameter.
}
// assignment from a string literal
template<size_t N>
StaticString& operator=(StringLiteral<N> const& other) noexcept {
mString = other;
mLength = size_type(N - 1);
// we rely on inlining for computeHash. It would be nice to do this with constexpr
// instead, but unfortunately 'other' is not constexpr once a parameter.
mHash = computeHash(other, N - 1);
return *this;
}
// helper to make a StaticString from a C string that is known to be a string literal
static constexpr StaticString make(const_pointer literal, size_t length) noexcept {
StaticString r;
r.mString = literal;
r.mLength = size_type(length);
r.mHash = computeHash(literal, length);
return r;
}
static StaticString make(const_pointer literal) noexcept {
return make(literal, strlen(literal));
}
const_pointer c_str() const noexcept { return mString; }
const_pointer data() const noexcept { return mString; }
size_type size() const noexcept { return mLength; }
size_type length() const noexcept { return mLength; }
bool empty() const noexcept { return size() == 0; }
void clear() noexcept { mString = nullptr; mLength = 0; }
const_iterator begin() const noexcept { return mString; }
const_iterator end() const noexcept { return mString + mLength; }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
const_reference operator[](size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference front() const noexcept {
assert(size());
return begin()[0];
}
const_reference back() const noexcept {
assert(size());
return begin()[size() - 1];
}
size_type getHash() const noexcept { return mHash; }
struct Hasher {
typedef StaticString argument_type;
typedef size_t result_type;
result_type operator()(const argument_type& s) const noexcept {
return s.getHash();
}
};
private:
const_pointer mString = nullptr;
size_type mLength = 0;
size_type mHash = 0;
static constexpr size_type computeHash(const char* s, const size_t N) noexcept {
size_type hash = 5381;
UTILS_NOUNROLL
for (size_t i = 0; i < N - 1; i++) {
hash = (hash * 33u) ^ size_type(s[i]);
}
return hash;
}
int compare(const StaticString& rhs) const noexcept;
friend bool operator==(StaticString const& lhs, StaticString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
}
friend bool operator!=(StaticString const& lhs, StaticString const& rhs) noexcept {
return !(lhs == rhs);
}
friend bool operator<(StaticString const& lhs, StaticString const& rhs) noexcept {
return lhs.compare(rhs) < 0;
}
friend bool operator>(StaticString const& lhs, StaticString const& rhs) noexcept {
return lhs.compare(rhs) > 0;
}
friend bool operator>=(StaticString const& lhs, StaticString const& rhs) noexcept {
return !(lhs < rhs);
}
friend bool operator<=(StaticString const& lhs, StaticString const& rhs) noexcept {
return !(lhs > rhs);
}
};
// ------------------------------------------------------------------------------------------------
@@ -220,7 +72,7 @@ public:
explicit CString(size_t length);
// Allocates memory and copies traditional C string content. Unlike the above constructor, this
// does not alllow embedded nulls. This is explicit because this operation is costly.
// does not allow embedded nulls. This is explicit because this operation is costly.
explicit CString(const char* cstr);
template<size_t N>
@@ -228,8 +80,6 @@ public:
: CString(other, N - 1) {
}
CString(StaticString const& s) : CString(s.c_str(), s.size()) {} // NOLINT(google-explicit-constructor)
CString(const CString& rhs);
CString(CString&& rhs) noexcept {
@@ -349,10 +199,6 @@ private:
return strncmp(data(), rhs.data(), size());
}
friend bool operator==(CString const& lhs, StaticString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
}
friend bool operator==(CString const& lhs, CString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
@@ -375,7 +221,7 @@ private:
};
// implement this for your type for automatic conversion to CString. Failing to do so leads
// to a compile time failure.
// to a compile-time failure.
template<typename T>
CString to_string(T value) noexcept;

View File

@@ -17,7 +17,7 @@
#ifndef TNT_UTILS_CONDITION_H
#define TNT_UTILS_CONDITION_H
#if defined(__linux__)
#if defined(__ANDROID__)
#include <utils/linux/Condition.h>
#else
#include <utils/generic/Condition.h>

View File

@@ -20,6 +20,7 @@
#include <utils/compressed_pair.h>
#include <utils/Panic.h>
#include <initializer_list>
#include <limits>
#include <memory>
#include <type_traits>
@@ -91,6 +92,14 @@ public:
construct(begin(), end());
}
FixedCapacityVector(std::initializer_list<T> list,
const allocator_type& alloc = allocator_type())
: mSize(list.size()),
mCapacityAllocator(list.size(), alloc) {
mData = this->allocator().allocate(this->capacity());
std::uninitialized_copy(list.begin(), list.end(), begin());
}
FixedCapacityVector(size_type size, const_reference value,
const allocator_type& alloc = allocator_type())
: mSize(size),
@@ -322,7 +331,7 @@ private:
}
void construct(iterator first, iterator last, const_reference proto) noexcept {
#pragma nounroll
UTILS_NOUNROLL
while (first != last) {
storage_traits::construct(allocator(), first++, proto);
}
@@ -330,7 +339,7 @@ private:
// should this be NOINLINE?
void construct_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
UTILS_NOUNROLL
while (first != last) {
storage_traits::construct(allocator(), first++);
}
@@ -346,7 +355,7 @@ private:
// should this be NOINLINE?
void destroy_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
UTILS_NOUNROLL
while (first != last) {
storage_traits::destroy(allocator(), --last);
}

View File

@@ -303,6 +303,8 @@ public:
return mParallelSplitCount;
}
size_t getThreadCount() const { return mThreadCount; }
private:
// this is just to avoid using std::default_random_engine, since we're in a public header.
class default_random_engine {

View File

@@ -17,7 +17,7 @@
#ifndef TNT_UTILS_MUTEX_H
#define TNT_UTILS_MUTEX_H
#if defined(__linux__)
#if defined(__ANDROID__)
#include <utils/linux/Mutex.h>
#else
#include <utils/generic/Mutex.h>

View File

@@ -0,0 +1,180 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_QUADTREE_H
#define TNT_UTILS_QUADTREE_H
#include <utils/compiler.h>
#include <utils/debug.h>
#include <array>
#include <type_traits>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
namespace QuadTreeUtils {
/**
* 16-bits morton encoding
* @param x 8-bits horizontal coordinate
* @param y 8-bits vertical coordinate
* @return morton encoding of (x,y)
*/
static inline constexpr uint16_t morton(uint8_t x, uint8_t y) noexcept {
uint32_t r = x | (uint32_t(y) << 16);
r = (r | (r << 4u)) & 0x0f0f0f0fu;
r = (r | (r << 2u)) & 0x33333333u;
r = (r | (r << 1u)) & 0x55555555u;
return uint16_t(r | (r >> 15u));
}
/**
* size of a quad-tree of height `height`
* @param height height of the Quad-tree
* @return the number of elements in the tree
*/
static inline constexpr size_t size(size_t height) noexcept {
return QuadTreeUtils::morton(uint8_t((1u << height) - 1u), 0u);
}
/**
* Index in the QuadTreeArray of a Quad-tree node referenced by its height and code
* @param l height of the node
* @param code morton code of the node
* @return index in the QuadTreeArray of this node
*/
static inline constexpr size_t index(size_t l, size_t code) noexcept {
return size(l) + code;
}
/**
* Index in the QuadTreeArray of the parent of the specified node
* @param l height of the node
* @param code morton code of the node
* @return index in the QuadTreeArray of this node's parent
*/
static inline constexpr size_t parent(size_t l, size_t code) noexcept {
assert_invariant(l > 0);
return index(l - 1u, code >> 2u);
}
// integrated unit-tests!
static_assert(size(0) == 0);
static_assert(size(1) == 1);
static_assert(size(2) == 5);
static_assert(size(3) == 21);
static_assert(size(4) == 85);
} // namespace QuadTreeUtils
/**
* A Quad-tree encoded as an array.
* @tparam T Type of the quad-tree nodes
* @tparam HEIGHT Height of the quad-tree, muse be <= 4
*/
template<typename T, size_t HEIGHT>
class QuadTreeArray : public std::array<T, QuadTreeUtils::size(HEIGHT)> {
static_assert(HEIGHT <= 4, "QuadTreeArray height must be <= 4");
// Simple fixed capacity stack
template<typename TYPE, size_t CAPACITY,
typename = typename std::enable_if<std::is_trivial<TYPE>::value>::type>
class stack {
TYPE mElements[CAPACITY];
size_t mSize = 0;
public:
bool empty() const noexcept { return mSize == 0; }
void push(TYPE const& v) noexcept {
assert_invariant(mSize < CAPACITY);
mElements[mSize++] = v;
}
void pop() noexcept {
assert_invariant(mSize > 0);
--mSize;
}
const TYPE& back() const noexcept {
return mElements[mSize - 1];
}
};
public:
using code_t = uint8_t;
struct NodeId {
int8_t l; // height of the node or -1 if invalid
code_t code; // morton code of the node
};
enum class TraversalResult {
EXIT, // end traversal
RECURSE, // proceed with the children
SKIP_CHILDREN // skip children
};
static inline constexpr size_t height() noexcept {
return HEIGHT;
}
/**
* non-recursive depth-first traversal
*
* @tparam Process closure to process each visited node
* @param l height of the node to start with
* @param code code of the node to start with
* @param h maximum height to visit, must be < height()
* @param process closure to process each visited node
*/
template<typename Process,
typename = typename std::enable_if<
std::is_invocable_r_v<TraversalResult, Process, NodeId>>::type>
static void traverse(int8_t l, code_t code, size_t maxHeight, Process&& process) noexcept {
assert_invariant(maxHeight < height());
const int8_t h = int8_t(maxHeight);
stack<NodeId, 4 * height()> stack;
stack.push({ l, code });
while (!stack.empty()) {
NodeId curr = stack.back();
stack.pop();
TraversalResult r = process(curr);
if (r == TraversalResult::EXIT) {
break;
}
if (r == TraversalResult::RECURSE && curr.l < h) {
for (size_t c = 0; c < 4; c++) {
stack.push({
int8_t(curr.l + 1u),
code_t((curr.code << 2u) | (3u - c))
});
}
}
}
}
template<typename Node,
typename = typename std::enable_if<
std::is_invocable_r_v<TraversalResult, Node, NodeId>>::type>
static void traverse(int8_t l, code_t code, Node&& process) noexcept {
traverse(l, code, height() - 1, std::forward<Node>(process));
}
};
} // namespace utils
#endif //TNT_UTILS_QUADTREE_H

View File

@@ -19,6 +19,7 @@
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/EntityInstance.h>
#include <utils/EntityManager.h>
#include <utils/StructureOfArrays.h>
@@ -71,8 +72,8 @@ public:
mData.push_back();
}
SingleInstanceComponentManager(SingleInstanceComponentManager&& rhs) noexcept {/* = default */}
SingleInstanceComponentManager& operator=(SingleInstanceComponentManager&& rhs) noexcept {/* = default */}
SingleInstanceComponentManager(SingleInstanceComponentManager&&) noexcept {/* = default */}
SingleInstanceComponentManager& operator=(SingleInstanceComponentManager&&) noexcept {/* = default */}
~SingleInstanceComponentManager() noexcept = default;
// not copyable

View File

@@ -17,20 +17,19 @@
#ifndef TNT_UTILS_SLICE_H
#define TNT_UTILS_SLICE_H
#include <atomic>
#include <utils/compiler.h>
#include <utility>
#include <assert.h>
#include <stddef.h>
#include <utils/compiler.h>
namespace utils {
/*
* A fixed-size slice of a container
*/
template <typename T, typename SIZE_TYPE = uint32_t>
template <typename T>
class Slice {
public:
using iterator = T*;
@@ -40,16 +39,18 @@ public:
using const_reference = T const&;
using pointer = T*;
using const_pointer = T const*;
using size_type = SIZE_TYPE;
using size_type = size_t;
Slice() noexcept = default;
Slice(const_iterator begin, const_iterator end) noexcept
: mBegin(const_cast<iterator>(begin)), mEndOffset(size_type(end - begin)) {
: mBegin(const_cast<iterator>(begin)),
mEnd(const_cast<iterator>(end)) {
}
Slice(const_pointer begin, size_type count) noexcept
: mBegin(const_cast<iterator>(begin)), mEndOffset(size_type(count)) {
: mBegin(const_cast<iterator>(begin)),
mEnd(mBegin + count) {
}
Slice(Slice const& rhs) noexcept = default;
@@ -59,26 +60,26 @@ public:
void set(pointer begin, size_type count) UTILS_RESTRICT noexcept {
mBegin = begin;
mEndOffset = size_type(count);
mEnd = begin + count;
}
void set(iterator begin, iterator end) UTILS_RESTRICT noexcept {
mBegin = begin;
mEndOffset = size_type(end - begin);
mEnd = end;
}
void swap(Slice& rhs) UTILS_RESTRICT noexcept {
std::swap(mBegin, rhs.mBegin);
std::swap(mEndOffset, rhs.mEndOffset);
std::swap(mEnd, rhs.mEnd);
}
void clear() UTILS_RESTRICT noexcept {
mBegin = nullptr;
mEndOffset = 0;
mEnd = nullptr;
}
// size
size_t size() const UTILS_RESTRICT noexcept { return mEndOffset; }
size_t size() const UTILS_RESTRICT noexcept { return mEnd - mBegin; }
size_t sizeInBytes() const UTILS_RESTRICT noexcept { return size() * sizeof(T); }
bool empty() const UTILS_RESTRICT noexcept { return size() == 0; }
@@ -86,8 +87,8 @@ public:
iterator begin() UTILS_RESTRICT noexcept { return mBegin; }
const_iterator begin() const UTILS_RESTRICT noexcept { return mBegin; }
const_iterator cbegin() const UTILS_RESTRICT noexcept { return this->begin(); }
iterator end() UTILS_RESTRICT noexcept { return &mBegin[mEndOffset]; }
const_iterator end() const UTILS_RESTRICT noexcept { return &mBegin[mEndOffset]; }
iterator end() UTILS_RESTRICT noexcept { return mEnd; }
const_iterator end() const UTILS_RESTRICT noexcept { return mEnd; }
const_iterator cend() const UTILS_RESTRICT noexcept { return this->end(); }
// data access
@@ -139,233 +140,7 @@ public:
protected:
iterator mBegin = nullptr;
size_type mEndOffset = 0;
};
/*
* A fixed-capacity (but growable) slice of a container
*/
template<typename T, typename SIZE_TYPE = uint32_t>
class UTILS_PRIVATE GrowingSlice : public Slice<T, SIZE_TYPE> {
public:
using iterator = typename Slice<T, SIZE_TYPE>::iterator;
using const_iterator = typename Slice<T, SIZE_TYPE>::const_iterator;
using value_type = typename Slice<T, SIZE_TYPE>::value_type;
using reference = typename Slice<T, SIZE_TYPE>::reference;
using const_reference = typename Slice<T, SIZE_TYPE>::const_reference;
using pointer = typename Slice<T, SIZE_TYPE>::pointer;
using const_pointer = typename Slice<T, SIZE_TYPE>::const_pointer;
using size_type = typename Slice<T, SIZE_TYPE>::size_type;
GrowingSlice() noexcept = default;
GrowingSlice(GrowingSlice const& rhs) noexcept = default;
GrowingSlice(GrowingSlice&& rhs) noexcept = default;
template<typename Iter>
GrowingSlice(Iter begin, size_type count) noexcept
: Slice<T, SIZE_TYPE>(begin, size_type(0)),
mCapOffset(count) {
}
GrowingSlice& operator=(GrowingSlice const& rhs) noexcept = default;
GrowingSlice& operator=(GrowingSlice&& rhs) noexcept = default;
// size
size_t remain() const UTILS_RESTRICT noexcept { return mCapOffset - this->mEndOffset; }
size_t capacity() const UTILS_RESTRICT noexcept { return mCapOffset; }
template<typename Iter>
void set(Iter begin, size_type count) UTILS_RESTRICT noexcept {
this->Slice<T, SIZE_TYPE>::set(begin, count);
mCapOffset = count;
}
template<typename Iter>
void set(Iter begin, Iter end) UTILS_RESTRICT noexcept {
this->Slice<T, SIZE_TYPE>::set(begin, end);
mCapOffset = size_type(end - begin);
}
void swap(GrowingSlice& rhs) UTILS_RESTRICT noexcept {
Slice<T, SIZE_TYPE>::swap(rhs);
std::swap(mCapOffset, rhs.mCapOffset);
}
void clear() UTILS_RESTRICT noexcept {
this->mEndOffset = 0;
}
void resize(size_type count) UTILS_RESTRICT noexcept {
assert(count < mCapOffset);
this->mEndOffset = size_type(count);
}
T* grow(size_type count) UTILS_RESTRICT noexcept {
assert(this->size() + count <= mCapOffset);
size_t offset = this->mEndOffset;
this->mEndOffset += count;
return this->mBegin + offset;
}
// data access
void push_back(T const& item) UTILS_RESTRICT noexcept {
T* const p = this->grow(1);
*p = item;
}
void push_back(T&& item) UTILS_RESTRICT noexcept {
T* const p = this->grow(1);
*p = std::move(item);
}
template<typename ... ARGS>
void emplace_back(ARGS&& ... args) UTILS_RESTRICT noexcept {
T* const p = this->grow(1);
new(p) T(std::forward<ARGS>(args)...);
}
private:
// we use size_type == uint32_t to reduce the size on 64-bits machines
size_type mCapOffset = 0;
};
// ------------------------------------------------------------------------------------------------
/*
* A fixed-capacity (but atomically growable) slice of a container
*/
template <typename T, typename SIZE_TYPE = uint32_t>
class AtomicGrowingSlice {
public:
using iterator = T*;
using const_iterator = T const*;
using value_type = T;
using reference = T&;
using const_reference = T const&;
using pointer = T*;
using const_pointer = T const*;
using size_type = SIZE_TYPE;
AtomicGrowingSlice() noexcept = default;
template<typename Iter>
AtomicGrowingSlice(Iter begin, Iter end) noexcept
: mBegin(iterator(begin)),
mEndOffset(0),
mCapOffset(size_type(iterator(end) - iterator(begin))) {
}
template<typename Iter>
AtomicGrowingSlice(Iter begin, size_type count) noexcept
: mBegin(iterator(begin)), mEndOffset(0), mCapOffset(size_type(count)) {
}
template<typename Iter>
void set(Iter begin, size_type count) noexcept {
assert(mBegin == nullptr);
mBegin = iterator(begin);
mEndOffset.store(0, std::memory_order_relaxed);
mCapOffset = count;
}
// clear
void clear() noexcept {
mEndOffset.store(0, std::memory_order_relaxed);
}
// size
size_type size() const noexcept { return mEndOffset.load(std::memory_order_relaxed); }
bool empty() const noexcept { return size() == 0; }
size_type remain() const noexcept { return mCapOffset - size(); }
size_type capacity() const noexcept { return mCapOffset; }
// iterators
iterator begin() noexcept { return mBegin; }
const_iterator begin() const noexcept { return mBegin; }
const_iterator cbegin() const noexcept { return begin(); }
iterator end() noexcept { return &mBegin[size()]; }
const_iterator end() const noexcept { return &mBegin[size()]; }
const_iterator cend() const noexcept { return end(); }
// data access
reference operator[](size_type n) noexcept {
assert(n < size());
return mBegin[n];
}
const_reference operator[](size_type n) const noexcept {
assert(n < size());
return mBegin[n];
}
reference at(size_type n) noexcept {
return operator[](n);
}
const_reference at(size_type n) const noexcept {
return operator[](n);
}
reference front() noexcept {
assert(!empty());
return *mBegin;
}
const_reference front() const noexcept {
assert(!empty());
return *mBegin;
}
reference back() noexcept {
assert(!empty());
return *(end() - 1);
}
const_reference back() const noexcept {
assert(!empty());
return *(end() - 1);
}
pointer data() noexcept {
return begin();
}
const_pointer data() const noexcept {
return begin();
}
T* grow(size_type count) noexcept {
size_type offset = this->mEndOffset.load(std::memory_order_relaxed);
do {
if (UTILS_UNLIKELY(offset + count > mCapOffset)) {
return nullptr;
}
} while (UTILS_UNLIKELY(!this->mEndOffset.compare_exchange_weak(offset, offset + count,
std::memory_order_relaxed, std::memory_order_relaxed)));
return this->mBegin + offset;
}
// data access
void push_back(T const& item) noexcept {
T* const p = this->grow(1);
*p = item;
}
void push_back(T&& item) noexcept {
T* const p = this->grow(1);
*p = std::move(item);
}
template<typename ... ARGS>
void emplace_back(ARGS&& ... args) noexcept {
T* const p = this->grow(1);
new(p) T(std::forward<ARGS>(args)...);
}
private:
iterator mBegin = nullptr;
std::atomic<size_type> mEndOffset = ATOMIC_VAR_INIT(0);
size_type mCapOffset = 0;
iterator mEnd = nullptr;
};
} // namespace utils

View File

@@ -17,19 +17,18 @@
#ifndef TNT_UTILS_STRUCTUREOFARRAYS_H
#define TNT_UTILS_STRUCTUREOFARRAYS_H
#include <array> // note: this is safe, see how std::array is used below (inline / private)
#include <cstddef>
#include <utility>
#include <utils/Allocator.h>
#include <utils/compiler.h>
#include <utils/Slice.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <utils/Allocator.h>
#include <utils/compiler.h>
#include <utils/EntityInstance.h>
#include <utils/Slice.h>
#include <array> // note: this is safe, see how std::array is used below (inline / private)
#include <cstddef>
#include <utility>
namespace utils {
@@ -268,8 +267,10 @@ public:
// allocate enough space for "capacity" elements of each array
// capacity cannot change when optional storage is specified
if (capacity >= mSize) {
// TODO: not entirely sure if "max" of all alignments is always correct
constexpr size_t align = std::max({ std::max(alignof(std::max_align_t), alignof(Elements))... });
const size_t sizeNeeded = getNeededSize(capacity);
void* buffer = mAllocator.alloc(sizeNeeded);
void* buffer = mAllocator.alloc(sizeNeeded, align);
// move all the items (one array at a time) from the old allocation to the new
// this also update the array pointers
@@ -427,10 +428,10 @@ public:
return data<ElementIndex>()[size() - 1];
}
template <size_t E>
template <size_t E, typename IndexType = uint32_t>
struct Field {
SoA& soa;
EntityInstanceBase::Type i;
IndexType i;
using Type = typename SoA::template TypeAt<E>;
UTILS_ALWAYS_INLINE Field& operator = (Field&& rhs) noexcept {
@@ -518,17 +519,18 @@ private:
// compute the required size of each array
const size_t sizes[] = { (sizeof(Elements) * capacity)... };
// we align each array to the same alignment guaranteed by malloc
const size_t align = alignof(std::max_align_t);
// we align each array to at least the same alignment guaranteed by malloc
constexpr size_t const alignments[] = { std::max(alignof(std::max_align_t), alignof(Elements))... };
// hopefully most of this gets unrolled and inlined
std::array<size_t, kArrayCount> offsets;
offsets[0] = 0;
#pragma unroll
UTILS_UNROLL
for (size_t i = 1; i < kArrayCount; i++) {
size_t unalignment = sizes[i - 1] % align;
size_t alignment = unalignment ? (align - unalignment) : 0;
size_t unalignment = (offsets[i - 1] + sizes[i - 1]) % alignments[i];
size_t alignment = unalignment ? (alignments[i] - unalignment) : 0;
offsets[i] = offsets[i - 1] + (sizes[i - 1] + alignment);
assert_invariant(offsets[i] % alignments[i] == 0);
}
return offsets;
}

View File

@@ -46,10 +46,10 @@ constexpr inline T clz(T x) noexcept {
x |= (x >> 4u);
x |= (x >> 8u);
x |= (x >> 16u);
if (sizeof(T) * CHAR_BIT >= 64) { // just to silence compiler warning
if constexpr (sizeof(T) * CHAR_BIT >= 64) { // just to silence compiler warning
x |= (x >> 32u);
}
if (sizeof(T) * CHAR_BIT >= 128) { // just to silence compiler warning
if constexpr (sizeof(T) * CHAR_BIT >= 128) { // just to silence compiler warning
x |= (x >> 64u);
}
return T(sizeof(T) * CHAR_BIT) - details::popcount(x);
@@ -59,7 +59,13 @@ template<typename T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
constexpr inline T ctz(T x) noexcept {
static_assert(sizeof(T) * CHAR_BIT <= 64, "details::ctz() only support up to 64 bits");
T c = sizeof(T) * CHAR_BIT;
x &= -x; // equivalent to x & (~x + 1)
#if defined(_MSC_VER)
// equivalent to x & -x, but MSVC yield a warning for using unary minus operator on unsigned types
x &= (~x + 1);
#else
// equivalent to x & (~x + 1), but some compilers generate a better sequence on ARM
x &= -x;
#endif
if (x) c--;
if (sizeof(T) * CHAR_BIT >= 64) {
if (x & T(0x00000000FFFFFFFF)) c -= 32;

View File

@@ -315,11 +315,14 @@ private:
using bitset8 = bitset<uint8_t>;
using bitset32 = bitset<uint32_t>;
using bitset64 = bitset<uint64_t>;
using bitset128 = bitset<uint64_t, 2>;
using bitset256 = bitset<uint64_t, 4>;
static_assert(sizeof(bitset8) == sizeof(uint8_t), "bitset8 isn't 8 bits!");
static_assert(sizeof(bitset32) == sizeof(uint32_t), "bitset32 isn't 32 bits!");
static_assert(sizeof(bitset64) == sizeof(uint64_t), "bitset64 isn't 64 bits!");
static_assert(sizeof(bitset8) == 1, "bitset8 isn't 8 bits!");
static_assert(sizeof(bitset32) == 4, "bitset32 isn't 32 bits!");
static_assert(sizeof(bitset64) == 8, "bitset64 isn't 64 bits!");
static_assert(sizeof(bitset128) == 16, "bitset128 isn't 128 bits!");
static_assert(sizeof(bitset256) == 32, "bitset256 isn't 256 bits!");
} // namespace utils

View File

@@ -116,8 +116,14 @@
# define UTILS_HAS_HYPER_THREADING 0
#endif
#if defined(__EMSCRIPTEN__) || defined(FILAMENT_SINGLE_THREADED)
#if defined(FILAMENT_SINGLE_THREADED)
# define UTILS_HAS_THREADING 0
#elif defined(__EMSCRIPTEN__)
# if defined(__EMSCRIPTEN_PTHREADS__) && defined(FILAMENT_WASM_THREADS)
# define UTILS_HAS_THREADING 1
# else
# define UTILS_HAS_THREADING 0
# endif
#else
# define UTILS_HAS_THREADING 1
#endif
@@ -140,7 +146,7 @@
#define UTILS_PURE
#endif
#if __has_attribute(maybe_unused)
#if __has_attribute(maybe_unused) || (defined(_MSC_VER) && _MSC_VER >= 1911)
#define UTILS_UNUSED [[maybe_unused]]
#define UTILS_UNUSED_IN_RELEASE [[maybe_unused]]
#elif __has_attribute(unused)
@@ -169,9 +175,11 @@
#if defined(_MSC_VER)
// MSVC does not support loop unrolling hints
# define UTILS_UNROLL
# define UTILS_NOUNROLL
#else
// C++11 allows pragmas to be specified as part of defines using the _Pragma syntax.
# define UTILS_UNROLL _Pragma("unroll")
# define UTILS_NOUNROLL _Pragma("nounroll")
#endif
@@ -232,5 +240,18 @@ typedef SSIZE_T ssize_t;
#endif
#if defined(_MSC_VER)
# define UTILS_WARNING_PUSH _Pragma("warning( push )")
# define UTILS_WARNING_POP _Pragma("warning( pop )")
# define UTILS_WARNING_ENABLE_PADDED _Pragma("warning(1: 4324)")
#elif defined(__clang__)
# define UTILS_WARNING_PUSH _Pragma("clang diagnostic push")
# define UTILS_WARNING_POP _Pragma("clang diagnostic pop")
# define UTILS_WARNING_ENABLE_PADDED _Pragma("clang diagnostic warning \"-Wpadded\"")
#else
# define UTILS_WARNING_PUSH
# define UTILS_WARNING_POP
# define UTILS_WARNING_ENABLE_PADDED
#endif
#endif // TNT_UTILS_COMPILER_H

View File

@@ -98,12 +98,12 @@ public:
// stateless allocators are always equal
template<typename T>
bool operator==(const STLAlignedAllocator<T>& rhs) const noexcept {
bool operator==(const STLAlignedAllocator<T>&) const noexcept {
return true;
}
template<typename T>
bool operator!=(const STLAlignedAllocator<T>& rhs) const noexcept {
bool operator!=(const STLAlignedAllocator<T>&) const noexcept {
return false;
}
};