update external headers

This commit is contained in:
Nick Fisher
2022-02-06 13:28:28 +08:00
parent 6d5a63d398
commit bd3d0d080b
150 changed files with 27445 additions and 14805 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +1,114 @@
/*
* Copyright (C) 2018 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_BINARYTREEARRAY_H
#define TNT_UTILS_BINARYTREEARRAY_H
#include <utils/compiler.h>
#include <type_traits>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
class BinaryTreeArray {
// Simple fixed capacity stack
template<typename TYPE, size_t CAPACITY,
typename = typename std::enable_if<std::is_pod<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(mSize < CAPACITY);
mElements[mSize++] = v;
}
void pop() noexcept {
assert(mSize > 0);
--mSize;
}
const TYPE& back() const noexcept {
return mElements[mSize - 1];
}
};
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)); }
// this builds the depth-first binary tree array top down (post-order)
template<typename Leaf, typename Node>
static void traverse(size_t height, Leaf leaf, Node node) noexcept {
struct TNode {
uint32_t index;
uint32_t col;
uint32_t height;
uint32_t next;
bool isLeaf() const noexcept { return height == 1; }
size_t left() const noexcept { return BinaryTreeArray::left(index, height); }
size_t right() const noexcept { return BinaryTreeArray::right(index, height); }
};
stack<TNode, 16> stack;
stack.push(TNode{ 0, 0, (uint32_t)height, (uint32_t)count(height) });
uint32_t prevLeft = 0;
uint32_t prevRight = 0;
uint32_t prevIndex = 0;
while (!stack.empty()) {
TNode const* const UTILS_RESTRICT curr = &stack.back();
const bool isLeaf = curr->isLeaf();
const uint32_t index = curr->index;
const uint32_t l = (uint32_t)curr->left();
const uint32_t r = (uint32_t)curr->right();
if (prevLeft == index || prevRight == index) {
if (!isLeaf) {
// the 'next' node of our left node's right descendants is our right child
stack.push({ l, 2 * curr->col, curr->height - 1, r });
}
} else if (l == prevIndex) {
if (!isLeaf) {
// the 'next' node of our right child is our own 'next' sibling
stack.push({ r, 2 * curr->col + 1, curr->height - 1, curr->next });
}
} else {
if (!isLeaf) {
node(index, l, r, curr->next);
} else {
leaf(index, curr->col, curr->next);
}
stack.pop();
}
prevLeft = l;
prevRight = r;
prevIndex = index;
}
}
};
} // namespace utils
#endif //TNT_UTILS_BINARYTREEARRAY_H
/*
* Copyright (C) 2018 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_BINARYTREEARRAY_H
#define TNT_UTILS_BINARYTREEARRAY_H
#include <utils/compiler.h>
#include <type_traits>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
class BinaryTreeArray {
// Simple fixed capacity stack
template<typename TYPE, size_t CAPACITY,
typename = typename std::enable_if<std::is_pod<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(mSize < CAPACITY);
mElements[mSize++] = v;
}
void pop() noexcept {
assert(mSize > 0);
--mSize;
}
const TYPE& back() const noexcept {
return mElements[mSize - 1];
}
};
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)); }
// this builds the depth-first binary tree array top down (post-order)
template<typename Leaf, typename Node>
static void traverse(size_t height, Leaf leaf, Node node) noexcept {
struct TNode {
uint32_t index;
uint32_t col;
uint32_t height;
uint32_t next;
bool isLeaf() const noexcept { return height == 1; }
size_t left() const noexcept { return BinaryTreeArray::left(index, height); }
size_t right() const noexcept { return BinaryTreeArray::right(index, height); }
};
stack<TNode, 16> stack;
stack.push(TNode{ 0, 0, (uint32_t)height, (uint32_t)count(height) });
uint32_t prevLeft = 0;
uint32_t prevRight = 0;
uint32_t prevIndex = 0;
while (!stack.empty()) {
TNode const* const UTILS_RESTRICT curr = &stack.back();
const bool isLeaf = curr->isLeaf();
const uint32_t index = curr->index;
const uint32_t l = (uint32_t)curr->left();
const uint32_t r = (uint32_t)curr->right();
if (prevLeft == index || prevRight == index) {
if (!isLeaf) {
// the 'next' node of our left node's right descendants is our right child
stack.push({ l, 2 * curr->col, curr->height - 1, r });
}
} else if (l == prevIndex) {
if (!isLeaf) {
// the 'next' node of our right child is our own 'next' sibling
stack.push({ r, 2 * curr->col + 1, curr->height - 1, curr->next });
}
} else {
if (!isLeaf) {
node(index, l, r, curr->next);
} else {
leaf(index, curr->col, curr->next);
}
stack.pop();
}
prevLeft = l;
prevRight = r;
prevIndex = index;
}
}
};
} // namespace utils
#endif //TNT_UTILS_BINARYTREEARRAY_H

View File

@@ -1,99 +1,99 @@
/*
* Copyright (C) 2019 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_BITMASKENUM_H
#define TNT_UTILS_BITMASKENUM_H
#include <utils/compiler.h>
#include <type_traits> // for std::false_type
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
template<typename Enum>
struct EnableBitMaskOperators : public std::false_type { };
} // namespace utils
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 {
using underlying = std::underlying_type_t<Enum>;
return underlying(rhs) == 0;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator~(Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(~underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator|(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) | underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator&(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) & underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator^(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) ^ underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator|=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs | rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator&=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs & rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator^=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs ^ rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool none(Enum lhs) noexcept {
return !lhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool any(Enum lhs) noexcept {
return !none(lhs);
}
#endif // TNT_UTILS_BITMASKENUM_H
/*
* Copyright (C) 2019 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_BITMASKENUM_H
#define TNT_UTILS_BITMASKENUM_H
#include <utils/compiler.h>
#include <type_traits> // for std::false_type
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
template<typename Enum>
struct EnableBitMaskOperators : public std::false_type { };
} // namespace utils
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 {
using underlying = std::underlying_type_t<Enum>;
return underlying(rhs) == 0;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator~(Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(~underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator|(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) | underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator&(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) & underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator^(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) ^ underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator|=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs | rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator&=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs & rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator^=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs ^ rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool none(Enum lhs) noexcept {
return !lhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool any(Enum lhs) noexcept {
return !none(lhs);
}
#endif // TNT_UTILS_BITMASKENUM_H

View File

@@ -1,393 +1,393 @@
/*
* Copyright (C) 2015 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_FILAMENT_CSTRING_H
#define TNT_FILAMENT_CSTRING_H
// NOTE: this header should not include STL headers
#include <utils/compiler.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
namespace utils {
//! \privatesection
struct hashCStrings {
typedef const char* argument_type;
typedef size_t result_type;
result_type operator()(argument_type cstr) const noexcept {
size_t hash = 5381;
while (int c = *cstr++) {
hash = (hash * 33u) ^ size_t(c);
}
return hash;
}
};
//! \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 = default;
// 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)) {
}
// assignment from a string literal
template<size_t N>
StaticString& operator=(StringLiteral<N> const& other) noexcept {
mString = other;
mLength = size_type(N - 1);
mHash = computeHash(other);
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);
size_type hash = 5381;
while (int c = *literal++) {
hash = (hash * 33u) ^ size_type(c);
}
r.mHash = hash;
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; }
private:
const_pointer mString = nullptr;
size_type mLength = 0;
size_type mHash = 0;
template<size_t N>
static constexpr size_type computeHash(StringLiteral<N> const& s) noexcept {
size_type hash = 5381;
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);
}
};
// ------------------------------------------------------------------------------------------------
class UTILS_PUBLIC CString {
public:
using value_type = char;
using size_type = uint32_t;
using difference_type = int32_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = value_type*;
using const_iterator = const value_type*;
CString() noexcept = default;
// Allocates memory and appends a null. This constructor can be used to hold arbitrary data
// inside the string (i.e. it can contain nulls or non-ASCII encodings).
CString(const char* cstr, 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.
explicit CString(const char* cstr);
template<size_t N>
explicit CString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
: CString(other, N - 1) {
}
CString(StaticString const& s) : CString(s.c_str(), s.size()) {}
CString(const CString& rhs);
CString(CString&& rhs) noexcept {
this->swap(rhs);
}
CString& operator=(const CString& rhs);
CString& operator=(CString&& rhs) noexcept {
this->swap(rhs);
return *this;
}
~CString() noexcept {
if (mData) {
free(mData - 1);
}
}
void swap(CString& other) noexcept {
// don't use std::swap(), we don't want an STL dependency in this file
auto *temp = mCStr;
mCStr = other.mCStr;
other.mCStr = temp;
}
const_pointer c_str() const noexcept { return mCStr; }
pointer c_str() noexcept { return mCStr; }
const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; }
const_pointer data() const noexcept { return c_str(); }
pointer data() noexcept { return c_str(); }
size_type size() const noexcept { return mData ? mData[-1].length : 0; }
size_type length() const noexcept { return size(); }
bool empty() const noexcept { return size() == 0; }
iterator begin() noexcept { return mCStr; }
iterator end() noexcept { return begin() + length(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return begin() + length(); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
CString& replace(size_type pos, size_type len, const CString& str) noexcept;
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
const_reference operator[](size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference operator[](size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference at(size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
reference front() noexcept {
assert(size());
return begin()[0];
}
const_reference front() const noexcept {
assert(size());
return begin()[0];
}
reference back() noexcept {
assert(size());
return begin()[size() - 1];
}
const_reference back() const noexcept {
assert(size());
return begin()[size() - 1];
}
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(size_t size, void* ptr) {
assert(ptr);
return ptr;
}
private:
struct Data {
size_type length;
};
// mCStr points to the C-string or nullptr. if non-null, mCStr is preceded by the string's size
union {
value_type *mCStr = nullptr;
Data* mData; // Data is stored at mData[-1]
};
int compare(const CString& rhs) const noexcept {
size_type lhs_size = size();
size_type rhs_size = rhs.size();
if (lhs_size < rhs_size) return -1;
if (lhs_size > rhs_size) return 1;
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()));
}
friend bool operator!=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs == rhs);
}
friend bool operator<(CString const& lhs, CString const& rhs) noexcept {
return lhs.compare(rhs) < 0;
}
friend bool operator>(CString const& lhs, CString const& rhs) noexcept {
return lhs.compare(rhs) > 0;
}
friend bool operator>=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs < rhs);
}
friend bool operator<=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs > rhs);
}
};
// implement this for your type for automatic conversion to CString. Failing to do so leads
// to a compile time failure.
template<typename T>
CString to_string(T value) noexcept;
} // namespace utils
// FIXME: how could we not include this one?
// needed for std::hash, since implementation is inline, this would not cause
// binaries incompatibilities if another STL version was used.
#include <functional>
namespace std {
//! \privatesection
template<>
struct hash<utils::CString> {
typedef utils::CString argument_type;
typedef size_t result_type;
utils::hashCStrings hasher;
size_t operator()(const utils::CString& s) const noexcept {
return hasher(s.c_str());
}
};
//! \privatesection
template<>
struct hash<utils::StaticString> {
typedef utils::StaticString argument_type;
typedef size_t result_type;
size_t operator()(const utils::StaticString& s) const noexcept {
return s.getHash();
}
};
} // namespace std
#endif // TNT_FILAMENT_CSTRING_H
/*
* Copyright (C) 2015 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_CSTRING_H
#define TNT_UTILS_CSTRING_H
// NOTE: this header should not include STL headers
#include <utils/compiler.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
namespace utils {
//! \privatesection
struct hashCStrings {
typedef const char* argument_type;
typedef size_t result_type;
result_type operator()(argument_type cstr) const noexcept {
size_t hash = 5381;
while (int c = *cstr++) {
hash = (hash * 33u) ^ size_t(c);
}
return hash;
}
};
//! \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 = default;
// 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)) {
}
// assignment from a string literal
template<size_t N>
StaticString& operator=(StringLiteral<N> const& other) noexcept {
mString = other;
mLength = size_type(N - 1);
mHash = computeHash(other);
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);
size_type hash = 5381;
while (int c = *literal++) {
hash = (hash * 33u) ^ size_type(c);
}
r.mHash = hash;
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; }
private:
const_pointer mString = nullptr;
size_type mLength = 0;
size_type mHash = 0;
template<size_t N>
static constexpr size_type computeHash(StringLiteral<N> const& s) noexcept {
size_type hash = 5381;
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);
}
};
// ------------------------------------------------------------------------------------------------
class UTILS_PUBLIC CString {
public:
using value_type = char;
using size_type = uint32_t;
using difference_type = int32_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = value_type*;
using const_iterator = const value_type*;
CString() noexcept = default;
// Allocates memory and appends a null. This constructor can be used to hold arbitrary data
// inside the string (i.e. it can contain nulls or non-ASCII encodings).
CString(const char* cstr, 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.
explicit CString(const char* cstr);
template<size_t N>
explicit CString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
: CString(other, N - 1) {
}
CString(StaticString const& s) : CString(s.c_str(), s.size()) {}
CString(const CString& rhs);
CString(CString&& rhs) noexcept {
this->swap(rhs);
}
CString& operator=(const CString& rhs);
CString& operator=(CString&& rhs) noexcept {
this->swap(rhs);
return *this;
}
~CString() noexcept {
if (mData) {
free(mData - 1);
}
}
void swap(CString& other) noexcept {
// don't use std::swap(), we don't want an STL dependency in this file
auto *temp = mCStr;
mCStr = other.mCStr;
other.mCStr = temp;
}
const_pointer c_str() const noexcept { return mCStr; }
pointer c_str() noexcept { return mCStr; }
const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; }
const_pointer data() const noexcept { return c_str(); }
pointer data() noexcept { return c_str(); }
size_type size() const noexcept { return mData ? mData[-1].length : 0; }
size_type length() const noexcept { return size(); }
bool empty() const noexcept { return size() == 0; }
iterator begin() noexcept { return mCStr; }
iterator end() noexcept { return begin() + length(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return begin() + length(); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
CString& replace(size_type pos, size_type len, const CString& str) noexcept;
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
const_reference operator[](size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference operator[](size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference at(size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
reference front() noexcept {
assert(size());
return begin()[0];
}
const_reference front() const noexcept {
assert(size());
return begin()[0];
}
reference back() noexcept {
assert(size());
return begin()[size() - 1];
}
const_reference back() const noexcept {
assert(size());
return begin()[size() - 1];
}
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(size_t size, void* ptr) {
assert(ptr);
return ptr;
}
private:
struct Data {
size_type length;
};
// mCStr points to the C-string or nullptr. if non-null, mCStr is preceded by the string's size
union {
value_type *mCStr = nullptr;
Data* mData; // Data is stored at mData[-1]
};
int compare(const CString& rhs) const noexcept {
size_type lhs_size = size();
size_type rhs_size = rhs.size();
if (lhs_size < rhs_size) return -1;
if (lhs_size > rhs_size) return 1;
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()));
}
friend bool operator!=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs == rhs);
}
friend bool operator<(CString const& lhs, CString const& rhs) noexcept {
return lhs.compare(rhs) < 0;
}
friend bool operator>(CString const& lhs, CString const& rhs) noexcept {
return lhs.compare(rhs) > 0;
}
friend bool operator>=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs < rhs);
}
friend bool operator<=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs > rhs);
}
};
// implement this for your type for automatic conversion to CString. Failing to do so leads
// to a compile time failure.
template<typename T>
CString to_string(T value) noexcept;
} // namespace utils
// FIXME: how could we not include this one?
// needed for std::hash, since implementation is inline, this would not cause
// binaries incompatibilities if another STL version was used.
#include <functional>
namespace std {
//! \privatesection
template<>
struct hash<utils::CString> {
typedef utils::CString argument_type;
typedef size_t result_type;
utils::hashCStrings hasher;
size_t operator()(const utils::CString& s) const noexcept {
return hasher(s.c_str());
}
};
//! \privatesection
template<>
struct hash<utils::StaticString> {
typedef utils::StaticString argument_type;
typedef size_t result_type;
size_t operator()(const utils::StaticString& s) const noexcept {
return s.getHash();
}
};
} // namespace std
#endif // TNT_UTILS_CSTRING_H

View File

@@ -1,127 +1,127 @@
/*
* Copyright (C) 2015 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 UTILS_CALLSTACK_H_
#define UTILS_CALLSTACK_H_
#include <stddef.h>
#include <stdint.h>
#include <typeinfo>
#include <utils/CString.h>
#include <utils/Log.h>
namespace utils {
/**
* CallStack captures the current's thread call stack.
*/
class CallStack {
public:
/**
* Creates an empty call stack
* @see CallStack::capture()
*/
CallStack() = default;
CallStack(const CallStack&) = default;
~CallStack() = default;
/**
* A convenience method to create and capture the stack trace in one go.
* @param ignore number frames to ignore at the top of the stack.
* @return A CallStack object
*/
static CallStack unwind(size_t ignore = 0) noexcept;
/**
* Capture the current thread's stack and replaces the existing one if any.
* @param ignore number frames to ignore at the top of the stack.
*/
void update(size_t ignore = 0) noexcept;
/**
* Get the number of stack frames this object has recorded.
* @return How many stack frames are accessible through operator[]
*/
size_t getFrameCount() const noexcept;
/**
* Return the program-counter of each stack frame captured
* @param index of the frame between 0 and getFrameCount()-1
* @return the program-counter of the stack-frame recorded at index \p index
* @throw std::out_of_range if the index is out of range
*/
intptr_t operator [](size_t index) const;
/** Demangles a C++ type name */
static utils::CString demangleTypeName(const char* mangled);
template <typename T>
static utils::CString typeName() {
#if UTILS_HAS_RTTI
return demangleTypeName(typeid(T).name());
#else
return CString("<no-rtti>");
#endif
}
/**
* Outputs a CallStack into a stream.
* This will print, when possible, the demangled names of functions corresponding to the
* program-counter recorded.
*/
friend utils::io::ostream& operator <<(utils::io::ostream& stream, const CallStack& callstack);
bool operator <(const CallStack& rhs) const;
inline bool operator >(const CallStack& rhs) const {
return rhs < *this;
}
inline bool operator !=(const CallStack& rhs) const {
return *this < rhs || rhs < *this;
}
inline bool operator >=(const CallStack& rhs) const {
return !operator <(rhs);
}
inline bool operator <=(const CallStack& rhs) const {
return !operator >(rhs);
}
inline bool operator ==(const CallStack& rhs) const {
return !operator !=(rhs);
}
private:
void update_gcc(size_t ignore) noexcept;
static utils::CString demangle(const char* mangled);
static constexpr size_t NUM_FRAMES = 20;
struct StackFrameInfo {
intptr_t pc;
};
size_t m_frame_count = 0;
StackFrameInfo m_stack[NUM_FRAMES];
};
} // namespace utils
#endif // UTILS_CALLSTACK_H_
/*
* Copyright (C) 2015 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 UTILS_CALLSTACK_H
#define UTILS_CALLSTACK_H
#include <stddef.h>
#include <stdint.h>
#include <typeinfo>
#include <utils/CString.h>
#include <utils/Log.h>
namespace utils {
/**
* CallStack captures the current's thread call stack.
*/
class CallStack {
public:
/**
* Creates an empty call stack
* @see CallStack::capture()
*/
CallStack() = default;
CallStack(const CallStack&) = default;
~CallStack() = default;
/**
* A convenience method to create and capture the stack trace in one go.
* @param ignore number frames to ignore at the top of the stack.
* @return A CallStack object
*/
static CallStack unwind(size_t ignore = 0) noexcept;
/**
* Capture the current thread's stack and replaces the existing one if any.
* @param ignore number frames to ignore at the top of the stack.
*/
void update(size_t ignore = 0) noexcept;
/**
* Get the number of stack frames this object has recorded.
* @return How many stack frames are accessible through operator[]
*/
size_t getFrameCount() const noexcept;
/**
* Return the program-counter of each stack frame captured
* @param index of the frame between 0 and getFrameCount()-1
* @return the program-counter of the stack-frame recorded at index \p index
* @throw std::out_of_range if the index is out of range
*/
intptr_t operator [](size_t index) const;
/** Demangles a C++ type name */
static utils::CString demangleTypeName(const char* mangled);
template <typename T>
static utils::CString typeName() {
#if UTILS_HAS_RTTI
return demangleTypeName(typeid(T).name());
#else
return CString("<no-rtti>");
#endif
}
/**
* Outputs a CallStack into a stream.
* This will print, when possible, the demangled names of functions corresponding to the
* program-counter recorded.
*/
friend utils::io::ostream& operator <<(utils::io::ostream& stream, const CallStack& callstack);
bool operator <(const CallStack& rhs) const;
inline bool operator >(const CallStack& rhs) const {
return rhs < *this;
}
inline bool operator !=(const CallStack& rhs) const {
return *this < rhs || rhs < *this;
}
inline bool operator >=(const CallStack& rhs) const {
return !operator <(rhs);
}
inline bool operator <=(const CallStack& rhs) const {
return !operator >(rhs);
}
inline bool operator ==(const CallStack& rhs) const {
return !operator !=(rhs);
}
private:
void update_gcc(size_t ignore) noexcept;
static utils::CString demangle(const char* mangled);
static constexpr size_t NUM_FRAMES = 20;
struct StackFrameInfo {
intptr_t pc;
};
size_t m_frame_count = 0;
StackFrameInfo m_stack[NUM_FRAMES];
};
} // namespace utils
#endif // UTILS_CALLSTACK_H

View File

@@ -1,26 +1,26 @@
/*
* Copyright (C) 2016 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 UTILS_CONDITION_H
#define UTILS_CONDITION_H
#if defined(__linux__) && !defined(__SANITIZE_THREAD__)
#include <utils/linux/Condition.h>
#else
#include <utils/generic/Condition.h>
#endif
#endif // UTILS_CONDITION_H
/*
* Copyright (C) 2016 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_CONDITION_H
#define TNT_UTILS_CONDITION_H
#if defined(__linux__)
#include <utils/linux/Condition.h>
#else
#include <utils/generic/Condition.h>
#endif
#endif // TNT_UTILS_CONDITION_H

View File

@@ -1,91 +1,91 @@
/*
* Copyright (C) 2015 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 UTILS_COUNTDOWNLATCH_H_
#define UTILS_COUNTDOWNLATCH_H_
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A count down latch is used to block one or several threads until the latch is signaled
* a certain number of times.
*
* Threads entering the latch are blocked until the latch is signaled enough times.
*
* @see CyclicBarrier
*/
class CountDownLatch {
public:
/**
* Creates a count down latch with a specified count. The minimum useful value is 1.
* @param count the latch counter initial value
*/
explicit CountDownLatch(size_t count) noexcept;
~CountDownLatch() = default;
/**
* Blocks until latch() is called \p count times.
* @see CountDownLatch(size_t count)
*/
void await() noexcept;
/**
* Releases threads blocked in await() when called \p count times. Calling latch() more than
* \p count times has no effect.
* @see reset()
*/
void latch() noexcept;
/**
* Resets the count-down latch to the given value.
*
* @param new_count New latch count. A value of zero will immediately unblock all waiting
* threads.
*
* @warning Use with caution. It's only safe to reset the latch count when you're sure
* that no threads are waiting in await(). This can be guaranteed in various ways, for
* instance, if you have a single thread calling await(), you could call reset() from that
* thread, or you could use a CyclicBarrier to make sure all threads using the CountDownLatch
* are at a known place (i.e.: not in await()) when reset() is called.
*/
void reset(size_t new_count) noexcept;
/**
* @return the number of times latch() has been called since construction or reset.
* @see reset(), CountDownLatch(size_t count)
*/
size_t getCount() const noexcept;
CountDownLatch() = delete;
CountDownLatch(const CountDownLatch&) = delete;
CountDownLatch& operator=(const CountDownLatch&) = delete;
private:
uint32_t m_initial_count;
uint32_t m_remaining_count;
mutable Mutex m_lock;
mutable Condition m_cv;
};
} // namespace utils
#endif // UTILS_COUNTDOWNLATCH_H_
/*
* Copyright (C) 2015 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_COUNTDOWNLATCH_H
#define TNT_UTILS_COUNTDOWNLATCH_H
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A count down latch is used to block one or several threads until the latch is signaled
* a certain number of times.
*
* Threads entering the latch are blocked until the latch is signaled enough times.
*
* @see CyclicBarrier
*/
class CountDownLatch {
public:
/**
* Creates a count down latch with a specified count. The minimum useful value is 1.
* @param count the latch counter initial value
*/
explicit CountDownLatch(size_t count) noexcept;
~CountDownLatch() = default;
/**
* Blocks until latch() is called \p count times.
* @see CountDownLatch(size_t count)
*/
void await() noexcept;
/**
* Releases threads blocked in await() when called \p count times. Calling latch() more than
* \p count times has no effect.
* @see reset()
*/
void latch() noexcept;
/**
* Resets the count-down latch to the given value.
*
* @param new_count New latch count. A value of zero will immediately unblock all waiting
* threads.
*
* @warning Use with caution. It's only safe to reset the latch count when you're sure
* that no threads are waiting in await(). This can be guaranteed in various ways, for
* instance, if you have a single thread calling await(), you could call reset() from that
* thread, or you could use a CyclicBarrier to make sure all threads using the CountDownLatch
* are at a known place (i.e.: not in await()) when reset() is called.
*/
void reset(size_t new_count) noexcept;
/**
* @return the number of times latch() has been called since construction or reset.
* @see reset(), CountDownLatch(size_t count)
*/
size_t getCount() const noexcept;
CountDownLatch() = delete;
CountDownLatch(const CountDownLatch&) = delete;
CountDownLatch& operator=(const CountDownLatch&) = delete;
private:
uint32_t m_initial_count;
uint32_t m_remaining_count;
mutable Mutex m_lock;
mutable Condition m_cv;
};
} // namespace utils
#endif // TNT_UTILS_COUNTDOWNLATCH_H

View File

@@ -1,84 +1,84 @@
/*
* Copyright (C) 2015 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 UTILS_CYCLIC_BARRIER_H_
#define UTILS_CYCLIC_BARRIER_H_
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A cyclic barrier is used to synchronize several threads to a particular execution point.
*
* Threads entering the barrier are halted until all threads reach the barrier.
*
* @see CountDownLatch
*/
class CyclicBarrier {
public:
/**
* Creates a cyclic barrier with a specified number of threads to synchronize. The minimum
* useful value is 2. A value of 0 is invalid and is silently changed to 1.
* @param num_threads Number of threads to synchronize.
*/
explicit CyclicBarrier(size_t num_threads) noexcept;
/**
* @return The number of thread that are synchronized.
*/
size_t getThreadCount() const noexcept;
/**
* @return Number of threads currently waiting on the barrier.
*/
size_t getWaitingThreadCount() const noexcept;
/**
* Blocks until getThreadCount()-1 other threads reach await().
*/
void await() noexcept;
/**
* Resets the cyclic barrier to its original state and releases all waiting threads.
*/
void reset() noexcept;
CyclicBarrier() = delete;
CyclicBarrier(const CyclicBarrier&) = delete;
CyclicBarrier& operator=(const CyclicBarrier&) = delete;
private:
enum class State {
TRAP, RELEASE
};
const size_t m_num_threads;
mutable Mutex m_lock;
mutable Condition m_cv;
State m_state = State::TRAP;
size_t m_trapped_threads = 0;
size_t m_released_threads = 0;
};
} // namespace utils
#endif // UTILS_CYCLIC_BARRIER_H_
/*
* Copyright (C) 2015 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_CYCLIC_BARRIER_H
#define TNT_UTILS_CYCLIC_BARRIER_H
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A cyclic barrier is used to synchronize several threads to a particular execution point.
*
* Threads entering the barrier are halted until all threads reach the barrier.
*
* @see CountDownLatch
*/
class CyclicBarrier {
public:
/**
* Creates a cyclic barrier with a specified number of threads to synchronize. The minimum
* useful value is 2. A value of 0 is invalid and is silently changed to 1.
* @param num_threads Number of threads to synchronize.
*/
explicit CyclicBarrier(size_t num_threads) noexcept;
/**
* @return The number of thread that are synchronized.
*/
size_t getThreadCount() const noexcept;
/**
* @return Number of threads currently waiting on the barrier.
*/
size_t getWaitingThreadCount() const noexcept;
/**
* Blocks until getThreadCount()-1 other threads reach await().
*/
void await() noexcept;
/**
* Resets the cyclic barrier to its original state and releases all waiting threads.
*/
void reset() noexcept;
CyclicBarrier() = delete;
CyclicBarrier(const CyclicBarrier&) = delete;
CyclicBarrier& operator=(const CyclicBarrier&) = delete;
private:
enum class State {
TRAP, RELEASE
};
const size_t m_num_threads;
mutable Mutex m_lock;
mutable Condition m_cv;
State m_state = State::TRAP;
size_t m_trapped_threads = 0;
size_t m_released_threads = 0;
};
} // namespace utils
#endif // TNT_UTILS_CYCLIC_BARRIER_H

View File

@@ -1,98 +1,98 @@
/*
* Copyright (C) 2017 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_ENTITY_H
#define TNT_UTILS_ENTITY_H
#include <utils/compiler.h>
// FIXME: could we get rid of <functional>
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
namespace utils {
class UTILS_PUBLIC Entity {
public:
// this can be used to create an array of to-be-filled entities (see create())
Entity() noexcept = default;
// Entities can be copied
Entity(const Entity& e) noexcept = default;
Entity(Entity&& e) noexcept = default;
Entity& operator=(const Entity& e) noexcept = default;
Entity& operator=(Entity&& e) noexcept = default;
// Entities can be compared
bool operator==(Entity e) const { return e.mIdentity == mIdentity; }
bool operator!=(Entity e) const { return e.mIdentity != mIdentity; }
// Entities can be sorted
bool operator<(Entity e) const { return e.mIdentity < mIdentity; }
bool isNull() const noexcept {
return mIdentity == 0;
}
// an id that can be used for debugging/printing
uint32_t getId() const noexcept {
return mIdentity;
}
explicit operator bool() const noexcept { return !isNull(); }
void clear() noexcept { mIdentity = 0; }
// Exports an entity to an int32_t which can be used "as is" in the Java programing language.
static int32_t smuggle(Entity entity) noexcept {
return int32_t(entity.getId());
}
// Imports an entity from an int32_t generated by smuggle() above.
static Entity import(int32_t identity) noexcept {
return Entity{ Type(identity) };
}
private:
friend class EntityManager;
friend class EntityManagerImpl;
friend struct std::hash<Entity>;
using Type = uint32_t;
explicit Entity(Type identity) noexcept : mIdentity(identity) { }
Type mIdentity = 0;
};
} // namespace utils
namespace std {
template<>
struct hash<utils::Entity> {
typedef utils::Entity argument_type;
typedef size_t result_type;
result_type operator()(argument_type const& e) const {
return e.getId();
}
};
} // namespace std
#endif // TNT_UTILS_ENTITY_H
/*
* Copyright (C) 2017 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_ENTITY_H
#define TNT_UTILS_ENTITY_H
#include <utils/compiler.h>
// FIXME: could we get rid of <functional>
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
namespace utils {
class UTILS_PUBLIC Entity {
public:
// this can be used to create an array of to-be-filled entities (see create())
Entity() noexcept = default;
// Entities can be copied
Entity(const Entity& e) noexcept = default;
Entity(Entity&& e) noexcept = default;
Entity& operator=(const Entity& e) noexcept = default;
Entity& operator=(Entity&& e) noexcept = default;
// Entities can be compared
bool operator==(Entity e) const { return e.mIdentity == mIdentity; }
bool operator!=(Entity e) const { return e.mIdentity != mIdentity; }
// Entities can be sorted
bool operator<(Entity e) const { return e.mIdentity < mIdentity; }
bool isNull() const noexcept {
return mIdentity == 0;
}
// an id that can be used for debugging/printing
uint32_t getId() const noexcept {
return mIdentity;
}
explicit operator bool() const noexcept { return !isNull(); }
void clear() noexcept { mIdentity = 0; }
// Exports an entity to an int32_t which can be used "as is" in the Java programing language.
static int32_t smuggle(Entity entity) noexcept {
return int32_t(entity.getId());
}
// Imports an entity from an int32_t generated by smuggle() above.
static Entity import(int32_t identity) noexcept {
return Entity{ Type(identity) };
}
private:
friend class EntityManager;
friend class EntityManagerImpl;
friend struct std::hash<Entity>;
using Type = uint32_t;
explicit Entity(Type identity) noexcept : mIdentity(identity) { }
Type mIdentity = 0;
};
} // namespace utils
namespace std {
template<>
struct hash<utils::Entity> {
typedef utils::Entity argument_type;
typedef size_t result_type;
result_type operator()(argument_type const& e) const {
return e.getId();
}
};
} // namespace std
#endif // TNT_UTILS_ENTITY_H

View File

@@ -1,89 +1,89 @@
/*
* Copyright (C) 2017 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_FILAMENT_UTILS_ENTITYINSTANCE_H
#define TNT_FILAMENT_UTILS_ENTITYINSTANCE_H
#include <utils/compiler.h>
#include <type_traits>
#include <stdint.h>
namespace utils {
class UTILS_PUBLIC EntityInstanceBase {
public:
using Type = uint32_t;
protected:
Type mInstance = 0;
};
template <typename T, bool EDIT = false>
class UTILS_PUBLIC EntityInstance : public EntityInstanceBase {
public:
// default Instance is invalid
constexpr EntityInstance() noexcept = default;
// check if this Instance is valid
constexpr bool isValid() const noexcept { return mInstance != 0; }
// Instances of same type can be copied/assigned
constexpr EntityInstance(EntityInstance const& other) noexcept = default;
constexpr EntityInstance& operator=(EntityInstance const& other) noexcept = default;
// EDIT instances can be converted to "read" Instances of same type
template <typename = std::enable_if<!EDIT>>
constexpr explicit EntityInstance(EntityInstance<T, true> const& other) noexcept {
mInstance = other.asValue();
}
template <typename = std::enable_if<!EDIT>>
EntityInstance& operator=(EntityInstance<T, true> const& other) noexcept {
mInstance = other.asValue();
return *this;
}
// Instances can be compared
constexpr bool operator!=(EntityInstance e) const { return mInstance != e.mInstance; }
constexpr bool operator==(EntityInstance e) const { return mInstance == e.mInstance; }
// Instances can be sorted
constexpr bool operator<(EntityInstance e) const { return mInstance < e.mInstance; }
constexpr bool operator<=(EntityInstance e) const { return mInstance <= e.mInstance; }
constexpr bool operator>(EntityInstance e) const { return mInstance > e.mInstance; }
constexpr bool operator>=(EntityInstance e) const { return mInstance >= e.mInstance; }
// and we can iterate
constexpr EntityInstance& operator++() noexcept { ++mInstance; return *this; }
constexpr EntityInstance& operator--() noexcept { --mInstance; return *this; }
constexpr const EntityInstance operator++(int) const noexcept { return EntityInstance{ mInstance + 1 }; }
constexpr const EntityInstance operator--(int) const noexcept { return EntityInstance{ mInstance - 1 }; }
// return a value for this Instance (mostly needed for debugging
constexpr uint32_t asValue() const noexcept { return mInstance; }
// auto convert to Type so it can be used as an index
constexpr operator Type() const noexcept { return mInstance; } // NOLINT(google-explicit-constructor)
// conversion from Type so we can initialize from an index
constexpr EntityInstance(Type value) noexcept { mInstance = value; } // NOLINT(google-explicit-constructor)
};
} // namespace utils
#endif // TNT_FILAMENT_UTILS_ENTITYINSTANCE_H
/*
* Copyright (C) 2017 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_ENTITYINSTANCE_H
#define TNT_UTILS_ENTITYINSTANCE_H
#include <utils/compiler.h>
#include <type_traits>
#include <stdint.h>
namespace utils {
class UTILS_PUBLIC EntityInstanceBase {
public:
using Type = uint32_t;
protected:
Type mInstance = 0;
};
template <typename T, bool EDIT = false>
class UTILS_PUBLIC EntityInstance : public EntityInstanceBase {
public:
// default Instance is invalid
constexpr EntityInstance() noexcept = default;
// check if this Instance is valid
constexpr bool isValid() const noexcept { return mInstance != 0; }
// Instances of same type can be copied/assigned
constexpr EntityInstance(EntityInstance const& other) noexcept = default;
constexpr EntityInstance& operator=(EntityInstance const& other) noexcept = default;
// EDIT instances can be converted to "read" Instances of same type
template <typename = std::enable_if<!EDIT>>
constexpr explicit EntityInstance(EntityInstance<T, true> const& other) noexcept {
mInstance = other.asValue();
}
template <typename = std::enable_if<!EDIT>>
EntityInstance& operator=(EntityInstance<T, true> const& other) noexcept {
mInstance = other.asValue();
return *this;
}
// Instances can be compared
constexpr bool operator!=(EntityInstance e) const { return mInstance != e.mInstance; }
constexpr bool operator==(EntityInstance e) const { return mInstance == e.mInstance; }
// Instances can be sorted
constexpr bool operator<(EntityInstance e) const { return mInstance < e.mInstance; }
constexpr bool operator<=(EntityInstance e) const { return mInstance <= e.mInstance; }
constexpr bool operator>(EntityInstance e) const { return mInstance > e.mInstance; }
constexpr bool operator>=(EntityInstance e) const { return mInstance >= e.mInstance; }
// and we can iterate
constexpr EntityInstance& operator++() noexcept { ++mInstance; return *this; }
constexpr EntityInstance& operator--() noexcept { --mInstance; return *this; }
constexpr const EntityInstance operator++(int) const noexcept { return EntityInstance{ mInstance + 1 }; }
constexpr const EntityInstance operator--(int) const noexcept { return EntityInstance{ mInstance - 1 }; }
// return a value for this Instance (mostly needed for debugging
constexpr uint32_t asValue() const noexcept { return mInstance; }
// auto convert to Type so it can be used as an index
constexpr operator Type() const noexcept { return mInstance; } // NOLINT(google-explicit-constructor)
// conversion from Type so we can initialize from an index
constexpr EntityInstance(Type value) noexcept { mInstance = value; } // NOLINT(google-explicit-constructor)
};
} // namespace utils
#endif // TNT_UTILS_ENTITYINSTANCE_H

View File

@@ -1,133 +1,133 @@
/*
* Copyright (C) 2017 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_ENTITYMANAGER_H
#define TNT_UTILS_ENTITYMANAGER_H
#include <assert.h>
#include <stdint.h>
#include <utils/Entity.h>
#include <utils/compiler.h>
#ifndef FILAMENT_UTILS_TRACK_ENTITIES
#define FILAMENT_UTILS_TRACK_ENTITIES false
#endif
#if FILAMENT_UTILS_TRACK_ENTITIES
#include <utils/ostream.h>
#include <vector>
#endif
namespace utils {
class UTILS_PUBLIC EntityManager {
public:
// Get the global EntityManager. Is is recommended to cache this value.
// Thread Safe.
static EntityManager& get() noexcept;
class Listener {
public:
virtual void onEntitiesDestroyed(size_t n, Entity const* entities) noexcept = 0;
protected:
~Listener() noexcept;
};
// maximum number of entities that can exist at the same time
static size_t getMaxEntityCount() noexcept {
// because index 0 is reserved, we only have 2^GENERATION_SHIFT - 1 valid indices
return RAW_INDEX_COUNT - 1;
}
// create n entities. Thread safe.
void create(size_t n, Entity* entities);
// destroys n entities. Thread safe.
void destroy(size_t n, Entity* entities) noexcept;
// create a new Entity. Thread safe.
// Return Entity.isNull() if the entity cannot be allocated.
Entity create() {
Entity e;
create(1, &e);
return e;
}
// destroys an Entity. Thread safe.
void destroy(Entity e) noexcept {
destroy(1, &e);
}
// return whether the given Entity has been destroyed (false) or not (true).
// Thread safe.
bool isAlive(Entity e) const noexcept {
assert(getIndex(e) < RAW_INDEX_COUNT);
return (!e.isNull()) && (getGeneration(e) == mGens[getIndex(e)]);
}
// registers a listener to be called when an entity is destroyed. thread safe.
// if the listener is already register, this method has no effect.
void registerListener(Listener* l) noexcept;
// unregisters a listener.
void unregisterListener(Listener* l) noexcept;
/* no user serviceable parts below */
// current generation of the given index. Use for debugging and testing.
uint8_t getGenerationForIndex(size_t index) const noexcept {
return mGens[index];
}
// singleton, can't be copied
EntityManager(const EntityManager& rhs) = delete;
EntityManager& operator=(const EntityManager& rhs) = delete;
#if FILAMENT_UTILS_TRACK_ENTITIES
std::vector<Entity> getActiveEntities() const;
void dumpActiveEntities(utils::io::ostream& out) const;
#endif
private:
friend class EntityManagerImpl;
EntityManager();
~EntityManager();
// GENERATION_SHIFT determines how many simultaneous Entities are available, the
// minimum memory requirement is 2^GENERATION_SHIFT bytes.
static constexpr const int GENERATION_SHIFT = 17;
static constexpr const size_t RAW_INDEX_COUNT = (1 << GENERATION_SHIFT);
static constexpr const Entity::Type INDEX_MASK = (1 << GENERATION_SHIFT) - 1u;
static inline Entity::Type getGeneration(Entity e) noexcept {
return e.getId() >> GENERATION_SHIFT;
}
static inline Entity::Type getIndex(Entity e) noexcept {
return e.getId() & INDEX_MASK;
}
static inline Entity::Type makeIdentity(Entity::Type g, Entity::Type i) noexcept {
return (g << GENERATION_SHIFT) | (i & INDEX_MASK);
}
// stores the generation of each index.
uint8_t * const mGens;
};
} // namespace utils
#endif // TNT_UTILS_ENTITYMANAGER_H
/*
* Copyright (C) 2017 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_ENTITYMANAGER_H
#define TNT_UTILS_ENTITYMANAGER_H
#include <assert.h>
#include <stdint.h>
#include <utils/Entity.h>
#include <utils/compiler.h>
#ifndef FILAMENT_UTILS_TRACK_ENTITIES
#define FILAMENT_UTILS_TRACK_ENTITIES false
#endif
#if FILAMENT_UTILS_TRACK_ENTITIES
#include <utils/ostream.h>
#include <vector>
#endif
namespace utils {
class UTILS_PUBLIC EntityManager {
public:
// Get the global EntityManager. Is is recommended to cache this value.
// Thread Safe.
static EntityManager& get() noexcept;
class Listener {
public:
virtual void onEntitiesDestroyed(size_t n, Entity const* entities) noexcept = 0;
protected:
~Listener() noexcept;
};
// maximum number of entities that can exist at the same time
static size_t getMaxEntityCount() noexcept {
// because index 0 is reserved, we only have 2^GENERATION_SHIFT - 1 valid indices
return RAW_INDEX_COUNT - 1;
}
// create n entities. Thread safe.
void create(size_t n, Entity* entities);
// destroys n entities. Thread safe.
void destroy(size_t n, Entity* entities) noexcept;
// create a new Entity. Thread safe.
// Return Entity.isNull() if the entity cannot be allocated.
Entity create() {
Entity e;
create(1, &e);
return e;
}
// destroys an Entity. Thread safe.
void destroy(Entity e) noexcept {
destroy(1, &e);
}
// return whether the given Entity has been destroyed (false) or not (true).
// Thread safe.
bool isAlive(Entity e) const noexcept {
assert(getIndex(e) < RAW_INDEX_COUNT);
return (!e.isNull()) && (getGeneration(e) == mGens[getIndex(e)]);
}
// registers a listener to be called when an entity is destroyed. thread safe.
// if the listener is already register, this method has no effect.
void registerListener(Listener* l) noexcept;
// unregisters a listener.
void unregisterListener(Listener* l) noexcept;
/* no user serviceable parts below */
// current generation of the given index. Use for debugging and testing.
uint8_t getGenerationForIndex(size_t index) const noexcept {
return mGens[index];
}
// singleton, can't be copied
EntityManager(const EntityManager& rhs) = delete;
EntityManager& operator=(const EntityManager& rhs) = delete;
#if FILAMENT_UTILS_TRACK_ENTITIES
std::vector<Entity> getActiveEntities() const;
void dumpActiveEntities(utils::io::ostream& out) const;
#endif
private:
friend class EntityManagerImpl;
EntityManager();
~EntityManager();
// GENERATION_SHIFT determines how many simultaneous Entities are available, the
// minimum memory requirement is 2^GENERATION_SHIFT bytes.
static constexpr const int GENERATION_SHIFT = 17;
static constexpr const size_t RAW_INDEX_COUNT = (1 << GENERATION_SHIFT);
static constexpr const Entity::Type INDEX_MASK = (1 << GENERATION_SHIFT) - 1u;
static inline Entity::Type getGeneration(Entity e) noexcept {
return e.getId() >> GENERATION_SHIFT;
}
static inline Entity::Type getIndex(Entity e) noexcept {
return e.getId() & INDEX_MASK;
}
static inline Entity::Type makeIdentity(Entity::Type g, Entity::Type i) noexcept {
return (g << GENERATION_SHIFT) | (i & INDEX_MASK);
}
// stores the generation of each index.
uint8_t * const mGens;
};
} // namespace utils
#endif // TNT_UTILS_ENTITYMANAGER_H

View File

@@ -1,410 +1,411 @@
/*
* Copyright (C) 2021 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_FIXEDCAPACITYVECTOR_H
#define TNT_UTILS_FIXEDCAPACITYVECTOR_H
#include <utils/compressed_pair.h>
#include <utils/Panic.h>
#include <algorithm>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
#ifndef NDEBUG
#define FILAMENT_FORCE_CAPACITY_CHECK true
#else
#define FILAMENT_FORCE_CAPACITY_CHECK false
#endif
namespace utils {
/**
* FixedCapacityVector is (almost) a drop-in replacement for std::vector<> except it has a
* fixed capacity decided at runtime. The vector storage is never reallocated unless reserve()
* is called. Operations that add elements to the vector can fail if there is not enough
* capacity.
*
* An empty vector with a given capacity is created with
* FixedCapacityVector<T>::with_capacity( capacity );
*
* NOTE: When passing an initial size into the FixedCapacityVector constructor, default construction
* of the elements is skipped when their construction is trivial. This behavior is different from
* std::vector. e.g., std::vector<int>(4) constructs 4 zeros while FixedCapacityVector<int>(4)
* allocates 4 uninitialized values. Note that zero initialization is easily achieved by passing in
* the optional value argument, e.g. FixedCapacityVector<int>(4, 0) or foo.resize(4, 0).
*/
template<typename T, typename A = std::allocator<T>, bool CapacityCheck = true>
class UTILS_PUBLIC FixedCapacityVector {
public:
using allocator_type = A;
using value_type = T;
using reference = T&;
using const_reference = T const&;
using size_type = uint32_t;
using difference_type = int32_t;
using pointer = T*;
using const_pointer = T const*;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
private:
using storage_traits = std::allocator_traits<allocator_type>;
public:
/** returns an empty vector with the specified capacity */
static FixedCapacityVector with_capacity(
size_type capacity, const allocator_type& allocator = allocator_type()) {
return FixedCapacityVector(construct_with_capacity, capacity, allocator);
}
FixedCapacityVector() = default;
explicit FixedCapacityVector(const allocator_type& allocator) noexcept
: mCapacityAllocator({}, allocator) {
}
explicit FixedCapacityVector(size_type size, const allocator_type& allocator = allocator_type())
: mSize(size),
mCapacityAllocator(size, allocator) {
mData = this->allocator().allocate(this->capacity());
construct(begin(), end());
}
FixedCapacityVector(size_type size, const_reference value,
const allocator_type& alloc = allocator_type())
: mSize(size),
mCapacityAllocator(size, alloc) {
mData = this->allocator().allocate(this->capacity());
construct(begin(), end(), value);
}
FixedCapacityVector(FixedCapacityVector const& rhs)
: mSize(rhs.mSize),
mCapacityAllocator(rhs.capacity(),
storage_traits::select_on_container_copy_construction(rhs.allocator())) {
mData = allocator().allocate(capacity());
std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
}
FixedCapacityVector(FixedCapacityVector&& rhs) noexcept {
this->swap(rhs);
}
~FixedCapacityVector() noexcept {
destroy(begin(), end());
allocator().deallocate(data(), capacity());
}
FixedCapacityVector& operator=(FixedCapacityVector const& rhs) {
if (this != &rhs) {
FixedCapacityVector t(rhs);
this->swap(t);
}
return *this;
}
FixedCapacityVector& operator=(FixedCapacityVector&& rhs) noexcept {
this->swap(rhs);
return *this;
}
allocator_type get_allocator() const noexcept {
return mCapacityAllocator.second();
}
// --------------------------------------------------------------------------------------------
iterator begin() noexcept { return data(); }
iterator end() noexcept { return data() + size(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return data() + size(); }
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
const_reverse_iterator crbegin() const noexcept { return rbegin(); }
const_reverse_iterator crend() const noexcept { return rend(); }
// --------------------------------------------------------------------------------------------
size_type size() const noexcept { return mSize; }
size_type capacity() const noexcept { return mCapacityAllocator.first(); }
bool empty() const noexcept { return size() == 0; }
size_type max_size() const noexcept {
return std::min(storage_traits::max_size(allocator()),
std::numeric_limits<size_type>::max());
}
// --------------------------------------------------------------------------------------------
reference operator[](size_type n) noexcept {
assert(n < size());
return *(begin() + n);
}
const_reference operator[](size_type n) const noexcept {
assert(n < size());
return *(begin() + n);
}
reference front() noexcept { return *begin(); }
const_reference front() const noexcept { return *begin(); }
reference back() noexcept { return *(end() - 1); }
const_reference back() const noexcept { return *(end() - 1); }
value_type* data() noexcept { return mData; }
const value_type* data() const noexcept { return mData; }
// --------------------------------------------------------------------------------------------
void push_back(const_reference v) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, v);
}
void push_back(value_type&& v) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, std::move(v));
}
template<typename ... ARGS>
reference emplace_back(ARGS&& ... args) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, std::forward<ARGS>(args)...);
return *pos;
}
void pop_back() {
assert(!empty());
--mSize;
destroy(end(), end() + 1);
}
iterator insert(const_iterator position, const_reference v) {
if (position == end()) {
push_back(v);
} else {
assertCapacityForSize(size() + 1);
pointer p = const_cast<pointer>(position);
move_range(p, end(), p + 1);
++mSize;
// here we handle inserting an element of this vector!
const_pointer pv = std::addressof(v);
if (p <= pv && pv < end()) {
*p = *(pv + 1);
} else {
*p = v;
}
}
return const_cast<iterator>(position);
}
iterator insert(const_iterator position, value_type&& v) {
if (position == end()) {
push_back(std::move(v));
} else {
assertCapacityForSize(size() + 1);
pointer p = const_cast<pointer>(position);
move_range(p, end(), p + 1);
++mSize;
*p = std::move(v);
}
return const_cast<iterator>(position);
}
iterator erase(const_iterator pos) {
assert(pos != end());
return erase(pos, pos + 1);
}
iterator erase(const_iterator first, const_iterator last) {
assert(first <= last);
auto e = std::move(const_cast<iterator>(last), end(), const_cast<iterator>(first));
destroy(e, end());
mSize -= std::distance(first, last);
return const_cast<iterator>(first);
}
void clear() noexcept {
destroy(begin(), end());
mSize = 0;
}
void resize(size_type count) {
assertCapacityForSize(count);
if constexpr(std::is_trivially_constructible_v<value_type> &&
std::is_trivially_destructible_v<value_type>) {
// we check for triviality here so that the implementation could be non-inline
mSize = count;
} else {
resize_non_trivial(count);
}
}
void resize(size_type count, const_reference v) {
assertCapacityForSize(count);
resize_non_trivial(count, v);
}
void swap(FixedCapacityVector& other) {
using std::swap;
swap(mData, other.mData);
swap(mSize, other.mSize);
mCapacityAllocator.swap(other.mCapacityAllocator);
}
UTILS_NOINLINE
void reserve(size_type c) {
if (c > capacity()) {
FixedCapacityVector t(construct_with_capacity, c, allocator());
t.mSize = size();
std::uninitialized_move(begin(), end(), t.begin());
this->swap(t);
}
}
private:
enum construct_with_capacity_tag{ construct_with_capacity };
FixedCapacityVector(construct_with_capacity_tag,
size_type capacity, const allocator_type& allocator = allocator_type())
: mCapacityAllocator(capacity, allocator) {
mData = this->allocator().allocate(this->capacity());
}
allocator_type& allocator() noexcept {
return mCapacityAllocator.second();
}
allocator_type const& allocator() const noexcept {
return mCapacityAllocator.second();
}
iterator assertCapacityForSize(size_type s) {
if constexpr(CapacityCheck || FILAMENT_FORCE_CAPACITY_CHECK) {
ASSERT_PRECONDITION(capacity() >= s,
"capacity exceeded: requested size %lu, available capacity %lu.",
(unsigned long)s, (unsigned long)capacity());
}
return end();
}
inline void construct(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_constructible_v<value_type>) {
construct_non_trivial(first, last);
}
}
void construct(iterator first, iterator last, const_reference proto) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::construct(allocator(), first++, proto);
}
}
// should this be NOINLINE?
void construct_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::construct(allocator(), first++);
}
}
inline void destroy(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_destructible_v<value_type>) {
destroy_non_trivial(first, last);
}
}
// should this be NOINLINE?
void destroy_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::destroy(allocator(), --last);
}
}
// should this be NOINLINE?
void resize_non_trivial(size_type count) {
if (count > size()) {
construct(end(), begin() + count);
} else if (count < size()) {
destroy(begin() + count, end());
}
mSize = count;
}
// should this be NOINLINE?
void resize_non_trivial(size_type count, const_reference v) {
if (count > size()) {
construct(end(), begin() + count, v);
} else if (count < size()) {
destroy(begin() + count, end());
}
mSize = count;
}
// should this be NOINLINE?
void move_range(pointer s, pointer e, pointer to) {
if constexpr(std::is_trivially_copy_assignable_v<value_type>
&& std::is_trivially_destructible_v<value_type>) {
// this generates memmove -- which doesn't happen otherwise
std::move_backward(s, e, to + std::distance(s, e));
} else {
pointer our_end = end();
difference_type n = our_end - to; // nb of elements to move by operator=
pointer i = s + n; // 1st element to move by move ctor
for (pointer d = our_end ; i < our_end ; ++i, ++d) {
storage_traits::construct(allocator(), d, std::move(*i));
}
std::move_backward(s, s + n, our_end);
}
}
template<typename TYPE>
class SizeTypeWrapper {
TYPE value{};
public:
SizeTypeWrapper() noexcept = default;
SizeTypeWrapper(SizeTypeWrapper const& rhs) noexcept = default;
explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { }
SizeTypeWrapper operator=(TYPE rhs) noexcept { value = rhs; return *this; }
operator TYPE() const noexcept { return value; }
};
pointer mData{};
size_type mSize{};
compressed_pair<SizeTypeWrapper<size_type>, allocator_type> mCapacityAllocator{};
};
} // namespace utils
#endif //TNT_UTILS_FIXEDCAPACITYVECTOR_H
/*
* Copyright (C) 2021 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_FIXEDCAPACITYVECTOR_H
#define TNT_UTILS_FIXEDCAPACITYVECTOR_H
#include <utils/compressed_pair.h>
#include <utils/Panic.h>
#include <algorithm>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
#ifndef NDEBUG
#define FILAMENT_FORCE_CAPACITY_CHECK true
#else
#define FILAMENT_FORCE_CAPACITY_CHECK false
#endif
namespace utils {
/**
* FixedCapacityVector is (almost) a drop-in replacement for std::vector<> except it has a
* fixed capacity decided at runtime. The vector storage is never reallocated unless reserve()
* is called. Operations that add elements to the vector can fail if there is not enough
* capacity.
*
* An empty vector with a given capacity is created with
* FixedCapacityVector<T>::with_capacity( capacity );
*
* NOTE: When passing an initial size into the FixedCapacityVector constructor, default construction
* of the elements is skipped when their construction is trivial. This behavior is different from
* std::vector. e.g., std::vector<int>(4) constructs 4 zeros while FixedCapacityVector<int>(4)
* allocates 4 uninitialized values. Note that zero initialization is easily achieved by passing in
* the optional value argument, e.g. FixedCapacityVector<int>(4, 0) or foo.resize(4, 0).
*/
template<typename T, typename A = std::allocator<T>, bool CapacityCheck = true>
class UTILS_PUBLIC FixedCapacityVector {
public:
using allocator_type = A;
using value_type = T;
using reference = T&;
using const_reference = T const&;
using size_type = uint32_t;
using difference_type = int32_t;
using pointer = T*;
using const_pointer = T const*;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
private:
using storage_traits = std::allocator_traits<allocator_type>;
public:
/** returns an empty vector with the specified capacity */
static FixedCapacityVector with_capacity(
size_type capacity, const allocator_type& allocator = allocator_type()) {
return FixedCapacityVector(construct_with_capacity, capacity, allocator);
}
FixedCapacityVector() = default;
explicit FixedCapacityVector(const allocator_type& allocator) noexcept
: mCapacityAllocator({}, allocator) {
}
explicit FixedCapacityVector(size_type size, const allocator_type& allocator = allocator_type())
: mSize(size),
mCapacityAllocator(size, allocator) {
mData = this->allocator().allocate(this->capacity());
construct(begin(), end());
}
FixedCapacityVector(size_type size, const_reference value,
const allocator_type& alloc = allocator_type())
: mSize(size),
mCapacityAllocator(size, alloc) {
mData = this->allocator().allocate(this->capacity());
construct(begin(), end(), value);
}
FixedCapacityVector(FixedCapacityVector const& rhs)
: mSize(rhs.mSize),
mCapacityAllocator(rhs.capacity(),
storage_traits::select_on_container_copy_construction(rhs.allocator())) {
mData = allocator().allocate(capacity());
std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
}
FixedCapacityVector(FixedCapacityVector&& rhs) noexcept {
this->swap(rhs);
}
~FixedCapacityVector() noexcept {
destroy(begin(), end());
allocator().deallocate(data(), capacity());
}
FixedCapacityVector& operator=(FixedCapacityVector const& rhs) {
if (this != &rhs) {
FixedCapacityVector t(rhs);
this->swap(t);
}
return *this;
}
FixedCapacityVector& operator=(FixedCapacityVector&& rhs) noexcept {
this->swap(rhs);
return *this;
}
allocator_type get_allocator() const noexcept {
return mCapacityAllocator.second();
}
// --------------------------------------------------------------------------------------------
iterator begin() noexcept { return data(); }
iterator end() noexcept { return data() + size(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return data() + size(); }
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
const_reverse_iterator crbegin() const noexcept { return rbegin(); }
const_reverse_iterator crend() const noexcept { return rend(); }
// --------------------------------------------------------------------------------------------
size_type size() const noexcept { return mSize; }
size_type capacity() const noexcept { return mCapacityAllocator.first(); }
bool empty() const noexcept { return size() == 0; }
size_type max_size() const noexcept {
return std::min(storage_traits::max_size(allocator()),
std::numeric_limits<size_type>::max());
}
// --------------------------------------------------------------------------------------------
reference operator[](size_type n) noexcept {
assert(n < size());
return *(begin() + n);
}
const_reference operator[](size_type n) const noexcept {
assert(n < size());
return *(begin() + n);
}
reference front() noexcept { return *begin(); }
const_reference front() const noexcept { return *begin(); }
reference back() noexcept { return *(end() - 1); }
const_reference back() const noexcept { return *(end() - 1); }
value_type* data() noexcept { return mData; }
const value_type* data() const noexcept { return mData; }
// --------------------------------------------------------------------------------------------
void push_back(const_reference v) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, v);
}
void push_back(value_type&& v) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, std::move(v));
}
template<typename ... ARGS>
reference emplace_back(ARGS&& ... args) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, std::forward<ARGS>(args)...);
return *pos;
}
void pop_back() {
assert(!empty());
--mSize;
destroy(end(), end() + 1);
}
iterator insert(const_iterator position, const_reference v) {
if (position == end()) {
push_back(v);
} else {
assertCapacityForSize(size() + 1);
pointer p = const_cast<pointer>(position);
move_range(p, end(), p + 1);
++mSize;
// here we handle inserting an element of this vector!
const_pointer pv = std::addressof(v);
if (p <= pv && pv < end()) {
*p = *(pv + 1);
} else {
*p = v;
}
}
return const_cast<iterator>(position);
}
iterator insert(const_iterator position, value_type&& v) {
if (position == end()) {
push_back(std::move(v));
} else {
assertCapacityForSize(size() + 1);
pointer p = const_cast<pointer>(position);
move_range(p, end(), p + 1);
++mSize;
*p = std::move(v);
}
return const_cast<iterator>(position);
}
iterator erase(const_iterator pos) {
assert(pos != end());
return erase(pos, pos + 1);
}
iterator erase(const_iterator first, const_iterator last) {
assert(first <= last);
auto e = std::move(const_cast<iterator>(last), end(), const_cast<iterator>(first));
destroy(e, end());
mSize -= std::distance(first, last);
return const_cast<iterator>(first);
}
void clear() noexcept {
destroy(begin(), end());
mSize = 0;
}
void resize(size_type count) {
assertCapacityForSize(count);
if constexpr(std::is_trivially_constructible_v<value_type> &&
std::is_trivially_destructible_v<value_type>) {
// we check for triviality here so that the implementation could be non-inline
mSize = count;
} else {
resize_non_trivial(count);
}
}
void resize(size_type count, const_reference v) {
assertCapacityForSize(count);
resize_non_trivial(count, v);
}
void swap(FixedCapacityVector& other) {
using std::swap;
swap(mData, other.mData);
swap(mSize, other.mSize);
mCapacityAllocator.swap(other.mCapacityAllocator);
}
UTILS_NOINLINE
void reserve(size_type c) {
if (c > capacity()) {
FixedCapacityVector t(construct_with_capacity, c, allocator());
t.mSize = size();
std::uninitialized_move(begin(), end(), t.begin());
this->swap(t);
}
}
private:
enum construct_with_capacity_tag{ construct_with_capacity };
FixedCapacityVector(construct_with_capacity_tag,
size_type capacity, const allocator_type& allocator = allocator_type())
: mCapacityAllocator(capacity, allocator) {
mData = this->allocator().allocate(this->capacity());
}
allocator_type& allocator() noexcept {
return mCapacityAllocator.second();
}
allocator_type const& allocator() const noexcept {
return mCapacityAllocator.second();
}
iterator assertCapacityForSize(size_type s) {
if constexpr(CapacityCheck || FILAMENT_FORCE_CAPACITY_CHECK) {
ASSERT_PRECONDITION(capacity() >= s,
"capacity exceeded: requested size %lu, available capacity %lu.",
(unsigned long)s, (unsigned long)capacity());
}
return end();
}
inline void construct(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_constructible_v<value_type>) {
construct_non_trivial(first, last);
}
}
void construct(iterator first, iterator last, const_reference proto) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::construct(allocator(), first++, proto);
}
}
// should this be NOINLINE?
void construct_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::construct(allocator(), first++);
}
}
inline void destroy(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_destructible_v<value_type>) {
destroy_non_trivial(first, last);
}
}
// should this be NOINLINE?
void destroy_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::destroy(allocator(), --last);
}
}
// should this be NOINLINE?
void resize_non_trivial(size_type count) {
if (count > size()) {
construct(end(), begin() + count);
} else if (count < size()) {
destroy(begin() + count, end());
}
mSize = count;
}
// should this be NOINLINE?
void resize_non_trivial(size_type count, const_reference v) {
if (count > size()) {
construct(end(), begin() + count, v);
} else if (count < size()) {
destroy(begin() + count, end());
}
mSize = count;
}
// should this be NOINLINE?
void move_range(pointer s, pointer e, pointer to) {
if constexpr(std::is_trivially_copy_assignable_v<value_type>
&& std::is_trivially_destructible_v<value_type>) {
// this generates memmove -- which doesn't happen otherwise
std::move_backward(s, e, to + std::distance(s, e));
} else {
pointer our_end = end();
difference_type n = our_end - to; // nb of elements to move by operator=
pointer i = s + n; // 1st element to move by move ctor
for (pointer d = our_end ; i < our_end ; ++i, ++d) {
storage_traits::construct(allocator(), d, std::move(*i));
}
std::move_backward(s, s + n, our_end);
}
}
template<typename TYPE>
class SizeTypeWrapper {
TYPE value{};
public:
SizeTypeWrapper() noexcept = default;
SizeTypeWrapper(SizeTypeWrapper const& rhs) noexcept = default;
explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { }
SizeTypeWrapper operator=(TYPE rhs) noexcept { value = rhs; return *this; }
operator TYPE() const noexcept { return value; }
};
pointer mData{};
size_type mSize{};
compressed_pair<SizeTypeWrapper<size_type>, allocator_type> mCapacityAllocator{};
};
} // namespace utils
#endif // TNT_UTILS_FIXEDCAPACITYVECTOR_H

View File

@@ -1,74 +1,74 @@
/*
* Copyright (C) 2018 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_HASH_H
#define TNT_UTILS_HASH_H
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
namespace utils {
namespace hash {
inline uint32_t murmur3(const uint32_t* key, size_t wordCount, uint32_t seed) noexcept {
uint32_t h = seed;
size_t i = wordCount;
do {
uint32_t k = *key++;
k *= 0xcc9e2d51u;
k = (k << 15u) | (k >> 17u);
k *= 0x1b873593u;
h ^= k;
h = (h << 13u) | (h >> 19u);
h = (h * 5u) + 0xe6546b64u;
} while (--i);
h ^= wordCount;
h ^= h >> 16u;
h *= 0x85ebca6bu;
h ^= h >> 13u;
h *= 0xc2b2ae35u;
h ^= h >> 16u;
return h;
}
template<typename T>
struct MurmurHashFn {
uint32_t operator()(const T& key) const noexcept {
static_assert(0 == (sizeof(key) & 3u), "Hashing requires a size that is a multiple of 4.");
return murmur3((const uint32_t*) &key, sizeof(key) / 4, 0);
}
};
// combines two hashes together
template<class T>
inline void combine(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
}
// combines two hashes together, faster but less good
template<class T>
inline void combine_fast(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) << 1u;
}
} // namespace hash
} // namespace utils
#endif // TNT_UTILS_HASH_H
/*
* Copyright (C) 2018 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_HASH_H
#define TNT_UTILS_HASH_H
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
namespace utils {
namespace hash {
inline uint32_t murmur3(const uint32_t* key, size_t wordCount, uint32_t seed) noexcept {
uint32_t h = seed;
size_t i = wordCount;
do {
uint32_t k = *key++;
k *= 0xcc9e2d51u;
k = (k << 15u) | (k >> 17u);
k *= 0x1b873593u;
h ^= k;
h = (h << 13u) | (h >> 19u);
h = (h * 5u) + 0xe6546b64u;
} while (--i);
h ^= wordCount;
h ^= h >> 16u;
h *= 0x85ebca6bu;
h ^= h >> 13u;
h *= 0xc2b2ae35u;
h ^= h >> 16u;
return h;
}
template<typename T>
struct MurmurHashFn {
uint32_t operator()(const T& key) const noexcept {
static_assert(0 == (sizeof(key) & 3u), "Hashing requires a size that is a multiple of 4.");
return murmur3((const uint32_t*) &key, sizeof(key) / 4, 0);
}
};
// combines two hashes together
template<class T>
inline void combine(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
}
// combines two hashes together, faster but less good
template<class T>
inline void combine_fast(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) << 1u;
}
} // namespace hash
} // namespace utils
#endif // TNT_UTILS_HASH_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +1,46 @@
/*
* Copyright (C) 2015 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_LOG_H
#define TNT_UTILS_LOG_H
#include <utils/compiler.h>
#include <utils/ostream.h>
namespace utils {
struct UTILS_PUBLIC Loggers {
// DEBUG level logging stream
io::ostream& d;
// ERROR level logging stream
io::ostream& e;
// WARNING level logging stream
io::ostream& w;
// INFORMATION level logging stream
io::ostream& i;
};
extern UTILS_PUBLIC Loggers const slog;
} // namespace utils
#endif // TNT_UTILS_LOG_H
/*
* Copyright (C) 2015 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_LOG_H
#define TNT_UTILS_LOG_H
#include <utils/compiler.h>
#include <utils/ostream.h>
namespace utils {
struct UTILS_PUBLIC Loggers {
// DEBUG level logging stream
io::ostream& d;
// ERROR level logging stream
io::ostream& e;
// WARNING level logging stream
io::ostream& w;
// INFORMATION level logging stream
io::ostream& i;
// VERBOSE level logging stream
io::ostream& v;
};
extern UTILS_PUBLIC Loggers const slog;
} // namespace utils
#endif // TNT_UTILS_LOG_H

View File

@@ -1,26 +1,26 @@
/*
* Copyright (C) 2016 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 UTILS_MUTEX_H
#define UTILS_MUTEX_H
#if defined(__linux__) && !defined(__SANITIZE_THREAD__)
#include <utils/linux/Mutex.h>
#else
#include <utils/generic/Mutex.h>
#endif
#endif // UTILS_MUTEX_H
/*
* Copyright (C) 2016 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_MUTEX_H
#define TNT_UTILS_MUTEX_H
#if defined(__linux__)
#include <utils/linux/Mutex.h>
#else
#include <utils/generic/Mutex.h>
#endif
#endif // TNT_UTILS_MUTEX_H

View File

@@ -1,133 +1,133 @@
/*
* Copyright (C) 2017 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_NAMECOMPONENTMANAGER_H
#define TNT_UTILS_NAMECOMPONENTMANAGER_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/EntityInstance.h>
#include <utils/SingleInstanceComponentManager.h>
#include <functional>
namespace utils {
class EntityManager;
namespace details {
class SafeString {
public:
SafeString() noexcept = default;
explicit SafeString(const char* str) noexcept : mCStr(strdup(str)) { }
SafeString(SafeString&& rhs) noexcept : mCStr(rhs.mCStr) { rhs.mCStr = nullptr; }
SafeString& operator=(SafeString&& rhs) noexcept {
std::swap(mCStr, rhs.mCStr);
return *this;
}
~SafeString() { free((void*)mCStr); }
const char* c_str() const noexcept { return mCStr; }
private:
char const* mCStr = nullptr;
};
} // namespace details
/**
* \class NameComponentManager NameComponentManager.h utils/NameComponentManager.h
* \brief Allows clients to associate string labels with entities.
*
* To access the name of an existing entity, clients should first use NameComponentManager to get a
* temporary handle called an \em instance. Please note that instances are ephemeral; clients should
* store entities, not instances.
*
* Usage example:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* auto names = new NameComponentManager(EntityManager::get());
* names->addComponent(myEntity);
* names->setName(names->getInstance(myEntity), "Jeanne d'Arc");
* ...
* printf("%s\n", names->getName(names->getInstance(myEntity));
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager<details::SafeString> {
public:
using Instance = EntityInstance<NameComponentManager>;
/**
* Creates a new name manager associated with the given entity manager.
*
* Note that multiple string labels could be associated with each entity simply by
* creating multiple instances of NameComponentManager.
*/
explicit NameComponentManager(EntityManager& em);
~NameComponentManager();
/**
* Checks if the given entity already has a name component.
*/
using SingleInstanceComponentManager::hasComponent;
/**
* Gets a temporary handle that can be used to access the name.
*
* @return Non-zero handle if the entity has a name component, 0 otherwise.
*/
Instance getInstance(Entity e) const noexcept {
return Instance(SingleInstanceComponentManager::getInstance(e));
}
/*! \cond PRIVATE */
// these are implemented in SingleInstanceComponentManager<>, but we need to
// reimplement them in each manager, to ensure they are generated in an implementation file
// for backward binary compatibility reasons.
size_t getComponentCount() const noexcept;
Entity const* getEntities() const noexcept;
void gc(const EntityManager& em, size_t ratio = 4) noexcept;
/*! \endcond */
/**
* Adds a name component to the given entity if it doesn't already exist.
*/
void addComponent(Entity e);
/**
* Removes the name component to the given entity if it exists.
*/
void removeComponent(Entity e);
/**
* Stores a copy of the given string and associates it with the given instance.
*/
void setName(Instance instance, const char* name) noexcept;
/**
* Retrieves the string associated with the given instance, or nullptr if none exists.
*
* @return pointer to the copy that was made during setName()
*/
const char* getName(Instance instance) const noexcept;
};
} // namespace utils
#endif // TNT_UTILS_NAMECOMPONENTMANAGER_H
/*
* Copyright (C) 2017 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_NAMECOMPONENTMANAGER_H
#define TNT_UTILS_NAMECOMPONENTMANAGER_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/EntityInstance.h>
#include <utils/SingleInstanceComponentManager.h>
#include <functional>
namespace utils {
class EntityManager;
namespace details {
class SafeString {
public:
SafeString() noexcept = default;
explicit SafeString(const char* str) noexcept : mCStr(strdup(str)) { }
SafeString(SafeString&& rhs) noexcept : mCStr(rhs.mCStr) { rhs.mCStr = nullptr; }
SafeString& operator=(SafeString&& rhs) noexcept {
std::swap(mCStr, rhs.mCStr);
return *this;
}
~SafeString() { free((void*)mCStr); }
const char* c_str() const noexcept { return mCStr; }
private:
char const* mCStr = nullptr;
};
} // namespace details
/**
* \class NameComponentManager NameComponentManager.h utils/NameComponentManager.h
* \brief Allows clients to associate string labels with entities.
*
* To access the name of an existing entity, clients should first use NameComponentManager to get a
* temporary handle called an \em instance. Please note that instances are ephemeral; clients should
* store entities, not instances.
*
* Usage example:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* auto names = new NameComponentManager(EntityManager::get());
* names->addComponent(myEntity);
* names->setName(names->getInstance(myEntity), "Jeanne d'Arc");
* ...
* printf("%s\n", names->getName(names->getInstance(myEntity));
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager<details::SafeString> {
public:
using Instance = EntityInstance<NameComponentManager>;
/**
* Creates a new name manager associated with the given entity manager.
*
* Note that multiple string labels could be associated with each entity simply by
* creating multiple instances of NameComponentManager.
*/
explicit NameComponentManager(EntityManager& em);
~NameComponentManager();
/**
* Checks if the given entity already has a name component.
*/
using SingleInstanceComponentManager::hasComponent;
/**
* Gets a temporary handle that can be used to access the name.
*
* @return Non-zero handle if the entity has a name component, 0 otherwise.
*/
Instance getInstance(Entity e) const noexcept {
return Instance(SingleInstanceComponentManager::getInstance(e));
}
/*! \cond PRIVATE */
// these are implemented in SingleInstanceComponentManager<>, but we need to
// reimplement them in each manager, to ensure they are generated in an implementation file
// for backward binary compatibility reasons.
size_t getComponentCount() const noexcept;
Entity const* getEntities() const noexcept;
void gc(const EntityManager& em, size_t ratio = 4) noexcept;
/*! \endcond */
/**
* Adds a name component to the given entity if it doesn't already exist.
*/
void addComponent(Entity e);
/**
* Removes the name component to the given entity if it exists.
*/
void removeComponent(Entity e);
/**
* Stores a copy of the given string and associates it with the given instance.
*/
void setName(Instance instance, const char* name) noexcept;
/**
* Retrieves the string associated with the given instance, or nullptr if none exists.
*
* @return pointer to the copy that was made during setName()
*/
const char* getName(Instance instance) const noexcept;
};
} // namespace utils
#endif // TNT_UTILS_NAMECOMPONENTMANAGER_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,290 +1,290 @@
/*
* Copyright (C) 2015 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 UTILS_PATH_H_
#define UTILS_PATH_H_
#include <utils/compiler.h>
#include <iosfwd>
#include <string>
#include <vector>
namespace utils {
/**
* An abstract representation of file and directory paths.
*/
class UTILS_PUBLIC Path {
public:
/**
* Creates a new empty path.
*/
Path() = default;
~Path() = default;
/**
* Creates a new path with the specified pathname.
*
* @param pathname a non-null pathname string
*/
Path(const char* pathname);
/**
* Creates a new path with the specified pathname.
*
* @param pathname a pathname string
*/
Path(const std::string& pathname);
/**
* Tests whether the file or directory denoted by this abstract
* pathname exists.
*
* @return true if the file or directory denoted by this
* abstract pathname exists, false otherwise
*/
bool exists() const;
/**
* Tests whether this abstract pathname represents a regular file.
* This method can only return true if the path exists.
*
* @return true if this pathname represents an existing file,
* false if the path doesn't exist or represents something
* else (directory, symlink, etc.)
*/
bool isFile() const;
/**
* Tests whether this abstract pathname represents a directory.
* This method can only return true if the path exists.
*
* @return true if this pathname represents an existing directory,
* false if the path doesn't exist or represents a file
*/
bool isDirectory() const;
/**
* Tests whether this path is empty. An empty path does not
* exist.
*
* @return true if the underlying abstract pathname is empty,
* false otherwise
*/
bool isEmpty() const { return m_path.empty(); }
const char* c_str() const { return m_path.c_str(); }
/**
* Replaces the abstract pathname of this object with the
* specified pathname.
*
* @param pathname a pathname string
*/
void setPath(const std::string& pathname) {
m_path = getCanonicalPath(pathname);
}
/**
* @return the canonical pathname this path represents
*/
const std::string& getPath() const { return m_path; }
/**
* Returns the parent of this path as Path.
* @return a new path containing the parent of this path
*/
Path getParent() const;
/**
* Returns ancestor path where "0" is the immediate parent.
* @return a new path containing the ancestor of this path
*/
Path getAncestor(int n) const;
/**
* Returns the name of the file or directory represented by
* this abstract pathname.
*
* @return the name of the file or directory represented by
* this abstract pathname, or an empty string if
* this path is empty
*/
std::string getName() const;
/**
* Returns the name of the file or directory represented by
* this abstract pathname without its extension.
*
* @return the name of the file or directory represented by
* this abstract pathname, or an empty string if
* this path is empty
*/
std::string getNameWithoutExtension() const;
/**
* Returns the file extension (after the ".") if one is present.
* Returns the empty string if no filename is present or if the
* path is a directory.
*
* @return the file extension (if one is present and
* this is not a directory), else the empty string.
*/
std::string getExtension() const;
/**
* Returns the absolute representation of this path.
* If this path's pathname starts with a leading '/',
* the returned path is equal to this path. Otherwise,
* this path's pathname is concatenated with the current
* working directory and the result is returned.
*
* @return a new path containing the absolute representation
* of this path
*/
Path getAbsolutePath() const;
/**
* @return true if this abstract pathname is not empty
* and starts with a leading '/', false otherwise
*/
bool isAbsolute() const;
/**
* Splits this object's abstract pathname in a vector of file
* and directory name. If the underlying abstract pathname
* starts with a '/', the returned vector's first element
* will be the string "/".
*
* @return a vector of strings, empty if this path is empty
*/
std::vector<std::string> split() const;
/**
* Concatenates the specified path with this path in a new
* path object.
*
* @note if the pathname to concatenate with starts with
* a leading '/' then that pathname is returned without
* being concatenated to this object's pathname.
*
* @param path the path to concatenate with
*
* @return the concatenation of the two paths
*/
Path concat(const Path& path) const;
/**
* Concatenates the specified path with this path and
* stores the result in this path.
*
* @note if the pathname to concatenate with starts with
* a leading '/' then that pathname replaces this object's
* pathname.
*
* @param path the path to concatenate with
*/
void concatToSelf(const Path& path);
operator std::string const&() const { return m_path; }
Path operator+(const Path& rhs) const { return concat(rhs); }
Path& operator+=(const Path& rhs) {
concatToSelf(rhs);
return *this;
}
bool operator==(const Path& rhs) const { return m_path == rhs.m_path; }
bool operator!=(const Path& rhs) const { return m_path != rhs.m_path; }
bool operator<(const Path& rhs) const { return m_path < rhs.m_path; }
bool operator>(const Path& rhs) const { return m_path > rhs.m_path; }
friend std::ostream& operator<<(std::ostream& os, const Path& path);
/**
* Returns a canonical copy of the specified pathname by removing
* unnecessary path segments such as ".", ".." and "/".
*
* @param pathname a pathname string
*
* @return the canonical representation of the specified pathname
*/
static std::string getCanonicalPath(const std::string& pathname);
/**
* This method is equivalent to calling root.concat(leaf).
*/
static Path concat(const std::string& root, const std::string& leaf);
/**
* @return a path representing the current working directory
*/
static Path getCurrentDirectory();
/**
* @return a path representing the current executable
*/
static Path getCurrentExecutable();
/**
* @return a path representing a directory where temporary files can be stored
*/
static Path getTemporaryDirectory();
/**
* Creates a directory denoted by the given path.
* This is not recursive and doesn't create intermediate directories.
*
* @return True if directory was successfully created.
* When false, errno should have details on actual error.
*/
bool mkdir() const;
/**
* Creates a directory denoted by the given path.
* This is recursive and parent directories will be created if they do not
* exist.
*
* @return True if directory was successfully created or already exists.
* When false, errno should have details on actual error.
*/
bool mkdirRecursive() const;
/**
* Deletes this file.
*
* @return True if file was successfully deleted.
* When false, errno should have details on actual error.
*/
bool unlinkFile();
/**
* Lists the contents of this directory, skipping hidden files.
*
* @return A vector of paths of the contents of the directory. If the path points to a file,
* nonexistent directory, or empty directory, an empty vector is returned.
*/
std::vector<Path> listContents() const;
private:
std::string m_path;
};
} // namespace utils
#endif // UTILS_PATH_H_
/*
* Copyright (C) 2015 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_PATH_H
#define TNT_UTILS_PATH_H
#include <utils/compiler.h>
#include <iosfwd>
#include <string>
#include <vector>
namespace utils {
/**
* An abstract representation of file and directory paths.
*/
class UTILS_PUBLIC Path {
public:
/**
* Creates a new empty path.
*/
Path() = default;
~Path() = default;
/**
* Creates a new path with the specified pathname.
*
* @param pathname a non-null pathname string
*/
Path(const char* pathname);
/**
* Creates a new path with the specified pathname.
*
* @param pathname a pathname string
*/
Path(const std::string& pathname);
/**
* Tests whether the file or directory denoted by this abstract
* pathname exists.
*
* @return true if the file or directory denoted by this
* abstract pathname exists, false otherwise
*/
bool exists() const;
/**
* Tests whether this abstract pathname represents a regular file.
* This method can only return true if the path exists.
*
* @return true if this pathname represents an existing file,
* false if the path doesn't exist or represents something
* else (directory, symlink, etc.)
*/
bool isFile() const;
/**
* Tests whether this abstract pathname represents a directory.
* This method can only return true if the path exists.
*
* @return true if this pathname represents an existing directory,
* false if the path doesn't exist or represents a file
*/
bool isDirectory() const;
/**
* Tests whether this path is empty. An empty path does not
* exist.
*
* @return true if the underlying abstract pathname is empty,
* false otherwise
*/
bool isEmpty() const { return m_path.empty(); }
const char* c_str() const { return m_path.c_str(); }
/**
* Replaces the abstract pathname of this object with the
* specified pathname.
*
* @param pathname a pathname string
*/
void setPath(const std::string& pathname) {
m_path = getCanonicalPath(pathname);
}
/**
* @return the canonical pathname this path represents
*/
const std::string& getPath() const { return m_path; }
/**
* Returns the parent of this path as Path.
* @return a new path containing the parent of this path
*/
Path getParent() const;
/**
* Returns ancestor path where "0" is the immediate parent.
* @return a new path containing the ancestor of this path
*/
Path getAncestor(int n) const;
/**
* Returns the name of the file or directory represented by
* this abstract pathname.
*
* @return the name of the file or directory represented by
* this abstract pathname, or an empty string if
* this path is empty
*/
std::string getName() const;
/**
* Returns the name of the file or directory represented by
* this abstract pathname without its extension.
*
* @return the name of the file or directory represented by
* this abstract pathname, or an empty string if
* this path is empty
*/
std::string getNameWithoutExtension() const;
/**
* Returns the file extension (after the ".") if one is present.
* Returns the empty string if no filename is present or if the
* path is a directory.
*
* @return the file extension (if one is present and
* this is not a directory), else the empty string.
*/
std::string getExtension() const;
/**
* Returns the absolute representation of this path.
* If this path's pathname starts with a leading '/',
* the returned path is equal to this path. Otherwise,
* this path's pathname is concatenated with the current
* working directory and the result is returned.
*
* @return a new path containing the absolute representation
* of this path
*/
Path getAbsolutePath() const;
/**
* @return true if this abstract pathname is not empty
* and starts with a leading '/', false otherwise
*/
bool isAbsolute() const;
/**
* Splits this object's abstract pathname in a vector of file
* and directory name. If the underlying abstract pathname
* starts with a '/', the returned vector's first element
* will be the string "/".
*
* @return a vector of strings, empty if this path is empty
*/
std::vector<std::string> split() const;
/**
* Concatenates the specified path with this path in a new
* path object.
*
* @note if the pathname to concatenate with starts with
* a leading '/' then that pathname is returned without
* being concatenated to this object's pathname.
*
* @param path the path to concatenate with
*
* @return the concatenation of the two paths
*/
Path concat(const Path& path) const;
/**
* Concatenates the specified path with this path and
* stores the result in this path.
*
* @note if the pathname to concatenate with starts with
* a leading '/' then that pathname replaces this object's
* pathname.
*
* @param path the path to concatenate with
*/
void concatToSelf(const Path& path);
operator std::string const&() const { return m_path; }
Path operator+(const Path& rhs) const { return concat(rhs); }
Path& operator+=(const Path& rhs) {
concatToSelf(rhs);
return *this;
}
bool operator==(const Path& rhs) const { return m_path == rhs.m_path; }
bool operator!=(const Path& rhs) const { return m_path != rhs.m_path; }
bool operator<(const Path& rhs) const { return m_path < rhs.m_path; }
bool operator>(const Path& rhs) const { return m_path > rhs.m_path; }
friend std::ostream& operator<<(std::ostream& os, const Path& path);
/**
* Returns a canonical copy of the specified pathname by removing
* unnecessary path segments such as ".", ".." and "/".
*
* @param pathname a pathname string
*
* @return the canonical representation of the specified pathname
*/
static std::string getCanonicalPath(const std::string& pathname);
/**
* This method is equivalent to calling root.concat(leaf).
*/
static Path concat(const std::string& root, const std::string& leaf);
/**
* @return a path representing the current working directory
*/
static Path getCurrentDirectory();
/**
* @return a path representing the current executable
*/
static Path getCurrentExecutable();
/**
* @return a path representing a directory where temporary files can be stored
*/
static Path getTemporaryDirectory();
/**
* Creates a directory denoted by the given path.
* This is not recursive and doesn't create intermediate directories.
*
* @return True if directory was successfully created.
* When false, errno should have details on actual error.
*/
bool mkdir() const;
/**
* Creates a directory denoted by the given path.
* This is recursive and parent directories will be created if they do not
* exist.
*
* @return True if directory was successfully created or already exists.
* When false, errno should have details on actual error.
*/
bool mkdirRecursive() const;
/**
* Deletes this file.
*
* @return True if file was successfully deleted.
* When false, errno should have details on actual error.
*/
bool unlinkFile();
/**
* Lists the contents of this directory, skipping hidden files.
*
* @return A vector of paths of the contents of the directory. If the path points to a file,
* nonexistent directory, or empty directory, an empty vector is returned.
*/
std::vector<Path> listContents() const;
private:
std::string m_path;
};
} // namespace utils
#endif // TNT_UTILS_PATH_H

View File

@@ -1,212 +1,212 @@
/*
* Copyright (C) 2016 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_PROFILER_H
#define TNT_UTILS_PROFILER_H
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <chrono> // note: This is safe (only used inline)
#if defined(__linux__)
# include <unistd.h>
# include <sys/ioctl.h>
# include <linux/perf_event.h>
#endif
#include <utils/compiler.h>
namespace utils {
class Profiler {
public:
enum {
INSTRUCTIONS = 0, // must be zero
CPU_CYCLES = 1,
DCACHE_REFS = 2,
DCACHE_MISSES = 3,
BRANCHES = 4,
BRANCH_MISSES = 5,
ICACHE_REFS = 6,
ICACHE_MISSES = 7,
// Must be last one
EVENT_COUNT
};
enum {
EV_CPU_CYCLES = 1u << CPU_CYCLES,
EV_L1D_REFS = 1u << DCACHE_REFS,
EV_L1D_MISSES = 1u << DCACHE_MISSES,
EV_BPU_REFS = 1u << BRANCHES,
EV_BPU_MISSES = 1u << BRANCH_MISSES,
EV_L1I_REFS = 1u << ICACHE_REFS,
EV_L1I_MISSES = 1u << ICACHE_MISSES,
// helpers
EV_L1D_RATES = EV_L1D_REFS | EV_L1D_MISSES,
EV_L1I_RATES = EV_L1I_REFS | EV_L1I_MISSES,
EV_BPU_RATES = EV_BPU_REFS | EV_BPU_MISSES,
};
Profiler() noexcept; // must call resetEvents()
explicit Profiler(uint32_t eventMask) noexcept;
~Profiler() noexcept;
Profiler(const Profiler& rhs) = delete;
Profiler(Profiler&& rhs) = delete;
Profiler& operator=(const Profiler& rhs) = delete;
Profiler& operator=(Profiler&& rhs) = delete;
// selects which events are enabled.
uint32_t resetEvents(uint32_t eventMask) noexcept;
uint32_t getEnabledEvents() const noexcept { return mEnabledEvents; }
// could return false if performance counters are not supported/enabled
bool isValid() const { return mCountersFd[0] >= 0; }
class Counters {
friend class Profiler;
uint64_t nr;
uint64_t time_enabled;
uint64_t time_running;
struct {
uint64_t value;
uint64_t id;
} counters[Profiler::EVENT_COUNT];
friend Counters operator-(Counters lhs, const Counters& rhs) noexcept {
lhs.nr -= rhs.nr;
lhs.time_enabled -= rhs.time_enabled;
lhs.time_running -= rhs.time_running;
for (size_t i = 0; i < EVENT_COUNT; ++i) {
lhs.counters[i].value -= rhs.counters[i].value;
}
return lhs;
}
public:
uint64_t getInstructions() const { return counters[INSTRUCTIONS].value; }
uint64_t getCpuCycles() const { return counters[CPU_CYCLES].value; }
uint64_t getL1DReferences() const { return counters[DCACHE_REFS].value; }
uint64_t getL1DMisses() const { return counters[DCACHE_MISSES].value; }
uint64_t getL1IReferences() const { return counters[ICACHE_REFS].value; }
uint64_t getL1IMisses() const { return counters[ICACHE_MISSES].value; }
uint64_t getBranchInstructions() const { return counters[BRANCHES].value; }
uint64_t getBranchMisses() const { return counters[BRANCH_MISSES].value; }
std::chrono::duration<uint64_t, std::nano> getWallTime() const {
return std::chrono::duration<uint64_t, std::nano>(time_enabled);
}
std::chrono::duration<uint64_t, std::nano> getRunningTime() const {
return std::chrono::duration<uint64_t, std::nano>(time_running);
}
double getIPC() const noexcept {
uint64_t cpuCycles = getCpuCycles();
uint64_t instructions = getInstructions();
return double(instructions) / double(cpuCycles);
}
double getCPI() const noexcept {
uint64_t cpuCycles = getCpuCycles();
uint64_t instructions = getInstructions();
return double(cpuCycles) / double(instructions);
}
double getL1DMissRate() const noexcept {
uint64_t cacheReferences = getL1DReferences();
uint64_t cacheMisses = getL1DMisses();
return double(cacheMisses) / double(cacheReferences);
}
double getL1DHitRate() const noexcept {
return 1.0 - getL1DMissRate();
}
double getL1IMissRate() const noexcept {
uint64_t cacheReferences = getL1IReferences();
uint64_t cacheMisses = getL1IMisses();
return double(cacheMisses) / double(cacheReferences);
}
double getL1IHitRate() const noexcept {
return 1.0 - getL1IMissRate();
}
double getBranchMissRate() const noexcept {
uint64_t branchReferences = getBranchInstructions();
uint64_t branchMisses = getBranchMisses();
return double(branchMisses) / double(branchReferences);
}
double getBranchHitRate() const noexcept {
return 1.0 - getBranchMissRate();
}
double getMPKI(uint64_t misses) const noexcept {
return (misses * 1000.0) / getInstructions();
}
};
#if defined(__linux__)
void reset() noexcept {
int fd = mCountersFd[0];
ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
}
void start() noexcept {
int fd = mCountersFd[0];
ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
}
void stop() noexcept {
int fd = mCountersFd[0];
ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
}
Counters readCounters() noexcept;
#else // !__linux__
void reset() noexcept { }
void start() noexcept { }
void stop() noexcept { }
Counters readCounters() noexcept { return {}; }
#endif // __linux__
bool hasBranchRates() const noexcept {
return (mCountersFd[BRANCHES] >= 0) && (mCountersFd[BRANCH_MISSES] >= 0);
}
bool hasICacheRates() const noexcept {
return (mCountersFd[ICACHE_REFS] >= 0) && (mCountersFd[ICACHE_MISSES] >= 0);
}
private:
UTILS_UNUSED uint8_t mIds[EVENT_COUNT] = {};
int mCountersFd[EVENT_COUNT];
uint32_t mEnabledEvents = 0;
};
} // namespace utils
#endif // TNT_UTILS_PROFILER_H
/*
* Copyright (C) 2016 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_PROFILER_H
#define TNT_UTILS_PROFILER_H
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <chrono> // note: This is safe (only used inline)
#if defined(__linux__)
# include <unistd.h>
# include <sys/ioctl.h>
# include <linux/perf_event.h>
#endif
#include <utils/compiler.h>
namespace utils {
class Profiler {
public:
enum {
INSTRUCTIONS = 0, // must be zero
CPU_CYCLES = 1,
DCACHE_REFS = 2,
DCACHE_MISSES = 3,
BRANCHES = 4,
BRANCH_MISSES = 5,
ICACHE_REFS = 6,
ICACHE_MISSES = 7,
// Must be last one
EVENT_COUNT
};
enum {
EV_CPU_CYCLES = 1u << CPU_CYCLES,
EV_L1D_REFS = 1u << DCACHE_REFS,
EV_L1D_MISSES = 1u << DCACHE_MISSES,
EV_BPU_REFS = 1u << BRANCHES,
EV_BPU_MISSES = 1u << BRANCH_MISSES,
EV_L1I_REFS = 1u << ICACHE_REFS,
EV_L1I_MISSES = 1u << ICACHE_MISSES,
// helpers
EV_L1D_RATES = EV_L1D_REFS | EV_L1D_MISSES,
EV_L1I_RATES = EV_L1I_REFS | EV_L1I_MISSES,
EV_BPU_RATES = EV_BPU_REFS | EV_BPU_MISSES,
};
Profiler() noexcept; // must call resetEvents()
explicit Profiler(uint32_t eventMask) noexcept;
~Profiler() noexcept;
Profiler(const Profiler& rhs) = delete;
Profiler(Profiler&& rhs) = delete;
Profiler& operator=(const Profiler& rhs) = delete;
Profiler& operator=(Profiler&& rhs) = delete;
// selects which events are enabled.
uint32_t resetEvents(uint32_t eventMask) noexcept;
uint32_t getEnabledEvents() const noexcept { return mEnabledEvents; }
// could return false if performance counters are not supported/enabled
bool isValid() const { return mCountersFd[0] >= 0; }
class Counters {
friend class Profiler;
uint64_t nr;
uint64_t time_enabled;
uint64_t time_running;
struct {
uint64_t value;
uint64_t id;
} counters[Profiler::EVENT_COUNT];
friend Counters operator-(Counters lhs, const Counters& rhs) noexcept {
lhs.nr -= rhs.nr;
lhs.time_enabled -= rhs.time_enabled;
lhs.time_running -= rhs.time_running;
for (size_t i = 0; i < EVENT_COUNT; ++i) {
lhs.counters[i].value -= rhs.counters[i].value;
}
return lhs;
}
public:
uint64_t getInstructions() const { return counters[INSTRUCTIONS].value; }
uint64_t getCpuCycles() const { return counters[CPU_CYCLES].value; }
uint64_t getL1DReferences() const { return counters[DCACHE_REFS].value; }
uint64_t getL1DMisses() const { return counters[DCACHE_MISSES].value; }
uint64_t getL1IReferences() const { return counters[ICACHE_REFS].value; }
uint64_t getL1IMisses() const { return counters[ICACHE_MISSES].value; }
uint64_t getBranchInstructions() const { return counters[BRANCHES].value; }
uint64_t getBranchMisses() const { return counters[BRANCH_MISSES].value; }
std::chrono::duration<uint64_t, std::nano> getWallTime() const {
return std::chrono::duration<uint64_t, std::nano>(time_enabled);
}
std::chrono::duration<uint64_t, std::nano> getRunningTime() const {
return std::chrono::duration<uint64_t, std::nano>(time_running);
}
double getIPC() const noexcept {
uint64_t cpuCycles = getCpuCycles();
uint64_t instructions = getInstructions();
return double(instructions) / double(cpuCycles);
}
double getCPI() const noexcept {
uint64_t cpuCycles = getCpuCycles();
uint64_t instructions = getInstructions();
return double(cpuCycles) / double(instructions);
}
double getL1DMissRate() const noexcept {
uint64_t cacheReferences = getL1DReferences();
uint64_t cacheMisses = getL1DMisses();
return double(cacheMisses) / double(cacheReferences);
}
double getL1DHitRate() const noexcept {
return 1.0 - getL1DMissRate();
}
double getL1IMissRate() const noexcept {
uint64_t cacheReferences = getL1IReferences();
uint64_t cacheMisses = getL1IMisses();
return double(cacheMisses) / double(cacheReferences);
}
double getL1IHitRate() const noexcept {
return 1.0 - getL1IMissRate();
}
double getBranchMissRate() const noexcept {
uint64_t branchReferences = getBranchInstructions();
uint64_t branchMisses = getBranchMisses();
return double(branchMisses) / double(branchReferences);
}
double getBranchHitRate() const noexcept {
return 1.0 - getBranchMissRate();
}
double getMPKI(uint64_t misses) const noexcept {
return (misses * 1000.0) / getInstructions();
}
};
#if defined(__linux__)
void reset() noexcept {
int fd = mCountersFd[0];
ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
}
void start() noexcept {
int fd = mCountersFd[0];
ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
}
void stop() noexcept {
int fd = mCountersFd[0];
ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
}
Counters readCounters() noexcept;
#else // !__linux__
void reset() noexcept { }
void start() noexcept { }
void stop() noexcept { }
Counters readCounters() noexcept { return {}; }
#endif // __linux__
bool hasBranchRates() const noexcept {
return (mCountersFd[BRANCHES] >= 0) && (mCountersFd[BRANCH_MISSES] >= 0);
}
bool hasICacheRates() const noexcept {
return (mCountersFd[ICACHE_REFS] >= 0) && (mCountersFd[ICACHE_MISSES] >= 0);
}
private:
UTILS_UNUSED uint8_t mIds[EVENT_COUNT] = {};
int mCountersFd[EVENT_COUNT];
uint32_t mEnabledEvents = 0;
};
} // namespace utils
#endif // TNT_UTILS_PROFILER_H

View File

@@ -1,86 +1,86 @@
/*
* Copyright (C) 2018 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_RANGE_H
#define TNT_UTILS_RANGE_H
#include <algorithm>
#include <assert.h>
#include <stddef.h>
#include <utils/compiler.h>
namespace utils {
template<typename T>
struct Range {
using value_type = T;
T first = 0;
T last = 0;
size_t size() const noexcept { return last - first; }
bool empty() const noexcept { return !size(); }
class const_iterator {
friend struct Range;
T value = {};
public:
const_iterator() noexcept = default;
explicit const_iterator(T value) noexcept : value(value) {}
using value_type = T;
using pointer = value_type*;
using difference_type = ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
const value_type operator*() const { return value; }
const value_type operator[](size_t n) const { return value + n; }
const_iterator& operator++() { ++value; return *this; }
const_iterator& operator--() { --value; return *this; }
const const_iterator operator++(int) { const_iterator t(value); value++; return t; }
const const_iterator operator--(int) { const_iterator t(value); value--; return t; }
const_iterator operator+(size_t rhs) const { return { value + rhs }; }
const_iterator operator+(size_t rhs) { return { value + rhs }; }
const_iterator operator-(size_t rhs) const { return { value - rhs }; }
difference_type operator-(const_iterator const& rhs) const { return value - rhs.value; }
bool operator==(const_iterator const& rhs) const { return (value == rhs.value); }
bool operator!=(const_iterator const& rhs) const { return (value != rhs.value); }
bool operator>=(const_iterator const& rhs) const { return (value >= rhs.value); }
bool operator> (const_iterator const& rhs) const { return (value > rhs.value); }
bool operator<=(const_iterator const& rhs) const { return (value <= rhs.value); }
bool operator< (const_iterator const& rhs) const { return (value < rhs.value); }
};
const_iterator begin() noexcept { return const_iterator{ first }; }
const_iterator end() noexcept { return const_iterator{ last }; }
const_iterator begin() const noexcept { return const_iterator{ first }; }
const_iterator end() const noexcept { return const_iterator{ last }; }
const_iterator front() const noexcept { return const_iterator{ first }; }
const_iterator back() const noexcept { return const_iterator{ last - 1 }; }
};
} // namespace utils
#endif // TNT_UTILS_RANGE_H
/*
* Copyright (C) 2018 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_RANGE_H
#define TNT_UTILS_RANGE_H
#include <algorithm>
#include <assert.h>
#include <stddef.h>
#include <utils/compiler.h>
namespace utils {
template<typename T>
struct Range {
using value_type = T;
T first = 0;
T last = 0;
size_t size() const noexcept { return last - first; }
bool empty() const noexcept { return !size(); }
class const_iterator {
friend struct Range;
T value = {};
public:
const_iterator() noexcept = default;
explicit const_iterator(T value) noexcept : value(value) {}
using value_type = T;
using pointer = value_type*;
using difference_type = ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
const value_type operator*() const { return value; }
const value_type operator[](size_t n) const { return value + n; }
const_iterator& operator++() { ++value; return *this; }
const_iterator& operator--() { --value; return *this; }
const const_iterator operator++(int) { const_iterator t(value); value++; return t; }
const const_iterator operator--(int) { const_iterator t(value); value--; return t; }
const_iterator operator+(size_t rhs) const { return { value + rhs }; }
const_iterator operator+(size_t rhs) { return { value + rhs }; }
const_iterator operator-(size_t rhs) const { return { value - rhs }; }
difference_type operator-(const_iterator const& rhs) const { return value - rhs.value; }
bool operator==(const_iterator const& rhs) const { return (value == rhs.value); }
bool operator!=(const_iterator const& rhs) const { return (value != rhs.value); }
bool operator>=(const_iterator const& rhs) const { return (value >= rhs.value); }
bool operator> (const_iterator const& rhs) const { return (value > rhs.value); }
bool operator<=(const_iterator const& rhs) const { return (value <= rhs.value); }
bool operator< (const_iterator const& rhs) const { return (value < rhs.value); }
};
const_iterator begin() noexcept { return const_iterator{ first }; }
const_iterator end() noexcept { return const_iterator{ last }; }
const_iterator begin() const noexcept { return const_iterator{ first }; }
const_iterator end() const noexcept { return const_iterator{ last }; }
const_iterator front() const noexcept { return const_iterator{ first }; }
const_iterator back() const noexcept { return const_iterator{ last - 1 }; }
};
} // namespace utils
#endif // TNT_UTILS_RANGE_H

View File

@@ -1,314 +1,314 @@
/*
* Copyright (C) 2017 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_SINGLEINSTANCECOMPONENTMANAGER_H
#define TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/EntityManager.h>
#include <utils/StructureOfArrays.h>
#include <tsl/robin_map.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
class EntityManager;
/*
* Helper class to create single instance component managers.
*
* This handles the component's storage as a structure-of-arrays, as well
* as the garbage collection.
*
* This is intended to be used as base class for a real component manager. When doing so,
* and the real component manager is a public API, make sure to forward the public methods
* to the implementation.
*
*/
template <typename ... Elements>
class UTILS_PUBLIC SingleInstanceComponentManager {
private:
// this is just to avoid using std::default_random_engine, since we're in a public header.
class default_random_engine {
uint32_t mState = 1u; // must be 0 < seed < 0x7fffffff
public:
inline uint32_t operator()() noexcept {
return mState = uint32_t((uint64_t(mState) * 48271u) % 0x7fffffffu);
}
};
protected:
static constexpr size_t ENTITY_INDEX = sizeof ... (Elements);
public:
using SoA = StructureOfArrays<Elements ..., Entity>;
using Instance = EntityInstanceBase::Type;
SingleInstanceComponentManager() noexcept {
// We always start with a dummy entry because index=0 is reserved. The component
// at index = 0, is guaranteed to be default-initialized.
// Sub-classes can use this to their advantage.
mData.push_back();
}
SingleInstanceComponentManager(SingleInstanceComponentManager&& rhs) noexcept {/* = default */}
SingleInstanceComponentManager& operator=(SingleInstanceComponentManager&& rhs) noexcept {/* = default */}
~SingleInstanceComponentManager() noexcept = default;
// not copyable
SingleInstanceComponentManager(SingleInstanceComponentManager const& rhs) = delete;
SingleInstanceComponentManager& operator=(SingleInstanceComponentManager const& rhs) = delete;
// returns true if the given Entity has a component of this Manager
bool hasComponent(Entity e) const noexcept {
return getInstance(e) != 0;
}
// Get instance of this Entity to be used to retrieve components
UTILS_NOINLINE
Instance getInstance(Entity e) const noexcept {
auto const& map = mInstanceMap;
// find() generates quite a bit of code
auto pos = map.find(e);
return pos != map.end() ? pos->second : 0;
}
// returns the number of components (i.e. size of each arrays)
size_t getComponentCount() const noexcept {
// The array as an extra dummy component at index 0, so the visible count is 1 less.
return mData.size() - 1;
}
bool empty() const noexcept {
return getComponentCount() == 0;
}
// returns a pointer to the Entity array. This is basically the list
// of entities this component manager handles.
// The pointer becomes invalid when adding or removing a component.
Entity const* getEntities() const noexcept {
return begin<ENTITY_INDEX>();
}
Entity getEntity(Instance i) const noexcept {
return elementAt<ENTITY_INDEX>(i);
}
// Add a component to the given Entity. If the entity already has a component from this
// manager, this function is a no-op.
// This invalidates all pointers components.
inline Instance addComponent(Entity e);
// Removes a component from the given entity.
// This invalidates all pointers components.
inline Instance removeComponent(Entity e);
// trigger one round of garbage collection. this is intended to be called on a regular
// basis. This gc gives up after it cannot randomly free 'ratio' component in a row.
void gc(const EntityManager& em, size_t ratio = 4) noexcept {
gc(em, ratio, [this](Entity e) {
removeComponent(e);
});
}
// return the first instance
Instance begin() const noexcept { return 1u; }
// return the past-the-last instance
Instance end() const noexcept { return Instance(begin() + getComponentCount()); }
// return a pointer to the first element of the ElementIndex'th array
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex>* begin() noexcept {
return mData.template data<ElementIndex>() + 1;
}
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const* begin() const noexcept {
return mData.template data<ElementIndex>() + 1;
}
// return a pointer to the past-the-end element of the ElementIndex'th array
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex>* end() noexcept {
return begin<ElementIndex>() + getComponentCount();
}
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const* end() const noexcept {
return begin<ElementIndex>() + getComponentCount();
}
// return a Slice<>
template<size_t ElementIndex>
Slice<typename SoA::template TypeAt<ElementIndex>> slice() noexcept {
return { begin<ElementIndex>(), end<ElementIndex>() };
}
template<size_t ElementIndex>
Slice<const typename SoA::template TypeAt<ElementIndex>> slice() const noexcept {
return { begin<ElementIndex>(), end<ElementIndex>() };
}
// return a reference to the index'th element of the ElementIndex'th array
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex>& elementAt(Instance index) noexcept {
assert(index);
return data<ElementIndex>()[index];
}
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const& elementAt(Instance index) const noexcept {
assert(index);
return data<ElementIndex>()[index];
}
// returns a pointer to the RAW ARRAY of components including the first dummy component
// Use with caution.
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const* raw_array() const noexcept {
return data<ElementIndex>();
}
// We need our own version of Field because mData is private
template<size_t E>
struct Field : public SoA::template Field<E> {
Field(SingleInstanceComponentManager& soa, EntityInstanceBase::Type i) noexcept
: SoA::template Field<E>{ soa.mData, i } {
}
using SoA::template Field<E>::operator =;
};
protected:
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex>* data() noexcept {
return mData.template data<ElementIndex>();
}
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const* data() const noexcept {
return mData.template data<ElementIndex>();
}
// swap only internals
void swap(Instance i, Instance j) noexcept {
assert(i);
assert(j);
if (i && j) {
// update the index map
auto& map = mInstanceMap;
Entity& ei = elementAt<ENTITY_INDEX>(i);
Entity& ej = elementAt<ENTITY_INDEX>(j);
std::swap(ei, ej);
if (ei) {
map[ei] = i;
}
if (ej) {
map[ej] = j;
}
}
}
template<typename REMOVE>
void gc(const EntityManager& em, size_t ratio,
REMOVE removeComponent) noexcept {
Entity const* entities = getEntities();
size_t count = getComponentCount();
size_t aliveInARow = 0;
default_random_engine& rng = mRng;
#pragma nounroll
while (count && aliveInARow < ratio) {
// note: using the modulo favorizes lower number
size_t i = rng() % count;
if (UTILS_LIKELY(em.isAlive(entities[i]))) {
++aliveInARow;
continue;
}
aliveInARow = 0;
count--;
removeComponent(entities[i]);
}
}
protected:
SoA mData;
private:
// maps an entity to an instance index
tsl::robin_map<Entity, Instance> mInstanceMap;
default_random_engine mRng;
};
// Keep these outside of the class because CLion has trouble parsing them
template<typename ... Elements>
typename SingleInstanceComponentManager<Elements ...>::Instance
SingleInstanceComponentManager<Elements ...>::addComponent(Entity e) {
Instance ci = 0;
if (!e.isNull()) {
if (!hasComponent(e)) {
// this is like a push_back(e);
mData.push_back().template back<ENTITY_INDEX>() = e;
// index 0 is used when the component doesn't exist
ci = Instance(mData.size() - 1);
mInstanceMap[e] = ci;
} else {
// if the entity already has this component, just return its instance
ci = mInstanceMap[e];
}
}
assert(ci != 0);
return ci;
}
// Keep these outside of the class because CLion has trouble parsing them
template <typename ... Elements>
typename SingleInstanceComponentManager<Elements ...>::Instance
SingleInstanceComponentManager<Elements ... >::removeComponent(Entity e) {
auto& map = mInstanceMap;
auto pos = map.find(e);
if (UTILS_LIKELY(pos != map.end())) {
size_t index = pos->second;
assert(index != 0);
size_t last = mData.size() - 1;
if (last != index) {
// move the last item to where we removed this component, as to keep
// the array tightly packed.
mData.forEach([index, last](auto* p) {
p[index] = std::move(p[last]);
});
Entity lastEntity = mData.template elementAt<ENTITY_INDEX>(index);
map[lastEntity] = index;
}
mData.pop_back();
map.erase(pos);
return last;
}
return 0;
}
} // namespace filament
#endif // TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H
/*
* Copyright (C) 2017 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_SINGLEINSTANCECOMPONENTMANAGER_H
#define TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/EntityManager.h>
#include <utils/StructureOfArrays.h>
#include <tsl/robin_map.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
class EntityManager;
/*
* Helper class to create single instance component managers.
*
* This handles the component's storage as a structure-of-arrays, as well
* as the garbage collection.
*
* This is intended to be used as base class for a real component manager. When doing so,
* and the real component manager is a public API, make sure to forward the public methods
* to the implementation.
*
*/
template <typename ... Elements>
class UTILS_PUBLIC SingleInstanceComponentManager {
private:
// this is just to avoid using std::default_random_engine, since we're in a public header.
class default_random_engine {
uint32_t mState = 1u; // must be 0 < seed < 0x7fffffff
public:
inline uint32_t operator()() noexcept {
return mState = uint32_t((uint64_t(mState) * 48271u) % 0x7fffffffu);
}
};
protected:
static constexpr size_t ENTITY_INDEX = sizeof ... (Elements);
public:
using SoA = StructureOfArrays<Elements ..., Entity>;
using Instance = EntityInstanceBase::Type;
SingleInstanceComponentManager() noexcept {
// We always start with a dummy entry because index=0 is reserved. The component
// at index = 0, is guaranteed to be default-initialized.
// Sub-classes can use this to their advantage.
mData.push_back();
}
SingleInstanceComponentManager(SingleInstanceComponentManager&& rhs) noexcept {/* = default */}
SingleInstanceComponentManager& operator=(SingleInstanceComponentManager&& rhs) noexcept {/* = default */}
~SingleInstanceComponentManager() noexcept = default;
// not copyable
SingleInstanceComponentManager(SingleInstanceComponentManager const& rhs) = delete;
SingleInstanceComponentManager& operator=(SingleInstanceComponentManager const& rhs) = delete;
// returns true if the given Entity has a component of this Manager
bool hasComponent(Entity e) const noexcept {
return getInstance(e) != 0;
}
// Get instance of this Entity to be used to retrieve components
UTILS_NOINLINE
Instance getInstance(Entity e) const noexcept {
auto const& map = mInstanceMap;
// find() generates quite a bit of code
auto pos = map.find(e);
return pos != map.end() ? pos->second : 0;
}
// returns the number of components (i.e. size of each arrays)
size_t getComponentCount() const noexcept {
// The array as an extra dummy component at index 0, so the visible count is 1 less.
return mData.size() - 1;
}
bool empty() const noexcept {
return getComponentCount() == 0;
}
// returns a pointer to the Entity array. This is basically the list
// of entities this component manager handles.
// The pointer becomes invalid when adding or removing a component.
Entity const* getEntities() const noexcept {
return begin<ENTITY_INDEX>();
}
Entity getEntity(Instance i) const noexcept {
return elementAt<ENTITY_INDEX>(i);
}
// Add a component to the given Entity. If the entity already has a component from this
// manager, this function is a no-op.
// This invalidates all pointers components.
inline Instance addComponent(Entity e);
// Removes a component from the given entity.
// This invalidates all pointers components.
inline Instance removeComponent(Entity e);
// trigger one round of garbage collection. this is intended to be called on a regular
// basis. This gc gives up after it cannot randomly free 'ratio' component in a row.
void gc(const EntityManager& em, size_t ratio = 4) noexcept {
gc(em, ratio, [this](Entity e) {
removeComponent(e);
});
}
// return the first instance
Instance begin() const noexcept { return 1u; }
// return the past-the-last instance
Instance end() const noexcept { return Instance(begin() + getComponentCount()); }
// return a pointer to the first element of the ElementIndex'th array
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex>* begin() noexcept {
return mData.template data<ElementIndex>() + 1;
}
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const* begin() const noexcept {
return mData.template data<ElementIndex>() + 1;
}
// return a pointer to the past-the-end element of the ElementIndex'th array
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex>* end() noexcept {
return begin<ElementIndex>() + getComponentCount();
}
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const* end() const noexcept {
return begin<ElementIndex>() + getComponentCount();
}
// return a Slice<>
template<size_t ElementIndex>
Slice<typename SoA::template TypeAt<ElementIndex>> slice() noexcept {
return { begin<ElementIndex>(), end<ElementIndex>() };
}
template<size_t ElementIndex>
Slice<const typename SoA::template TypeAt<ElementIndex>> slice() const noexcept {
return { begin<ElementIndex>(), end<ElementIndex>() };
}
// return a reference to the index'th element of the ElementIndex'th array
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex>& elementAt(Instance index) noexcept {
assert(index);
return data<ElementIndex>()[index];
}
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const& elementAt(Instance index) const noexcept {
assert(index);
return data<ElementIndex>()[index];
}
// returns a pointer to the RAW ARRAY of components including the first dummy component
// Use with caution.
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const* raw_array() const noexcept {
return data<ElementIndex>();
}
// We need our own version of Field because mData is private
template<size_t E>
struct Field : public SoA::template Field<E> {
Field(SingleInstanceComponentManager& soa, EntityInstanceBase::Type i) noexcept
: SoA::template Field<E>{ soa.mData, i } {
}
using SoA::template Field<E>::operator =;
};
protected:
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex>* data() noexcept {
return mData.template data<ElementIndex>();
}
template<size_t ElementIndex>
typename SoA::template TypeAt<ElementIndex> const* data() const noexcept {
return mData.template data<ElementIndex>();
}
// swap only internals
void swap(Instance i, Instance j) noexcept {
assert(i);
assert(j);
if (i && j) {
// update the index map
auto& map = mInstanceMap;
Entity& ei = elementAt<ENTITY_INDEX>(i);
Entity& ej = elementAt<ENTITY_INDEX>(j);
std::swap(ei, ej);
if (ei) {
map[ei] = i;
}
if (ej) {
map[ej] = j;
}
}
}
template<typename REMOVE>
void gc(const EntityManager& em, size_t ratio,
REMOVE removeComponent) noexcept {
Entity const* entities = getEntities();
size_t count = getComponentCount();
size_t aliveInARow = 0;
default_random_engine& rng = mRng;
#pragma nounroll
while (count && aliveInARow < ratio) {
// note: using the modulo favorizes lower number
size_t i = rng() % count;
if (UTILS_LIKELY(em.isAlive(entities[i]))) {
++aliveInARow;
continue;
}
aliveInARow = 0;
count--;
removeComponent(entities[i]);
}
}
protected:
SoA mData;
private:
// maps an entity to an instance index
tsl::robin_map<Entity, Instance> mInstanceMap;
default_random_engine mRng;
};
// Keep these outside of the class because CLion has trouble parsing them
template<typename ... Elements>
typename SingleInstanceComponentManager<Elements ...>::Instance
SingleInstanceComponentManager<Elements ...>::addComponent(Entity e) {
Instance ci = 0;
if (!e.isNull()) {
if (!hasComponent(e)) {
// this is like a push_back(e);
mData.push_back().template back<ENTITY_INDEX>() = e;
// index 0 is used when the component doesn't exist
ci = Instance(mData.size() - 1);
mInstanceMap[e] = ci;
} else {
// if the entity already has this component, just return its instance
ci = mInstanceMap[e];
}
}
assert(ci != 0);
return ci;
}
// Keep these outside of the class because CLion has trouble parsing them
template <typename ... Elements>
typename SingleInstanceComponentManager<Elements ...>::Instance
SingleInstanceComponentManager<Elements ... >::removeComponent(Entity e) {
auto& map = mInstanceMap;
auto pos = map.find(e);
if (UTILS_LIKELY(pos != map.end())) {
size_t index = pos->second;
assert(index != 0);
size_t last = mData.size() - 1;
if (last != index) {
// move the last item to where we removed this component, as to keep
// the array tightly packed.
mData.forEach([index, last](auto* p) {
p[index] = std::move(p[last]);
});
Entity lastEntity = mData.template elementAt<ENTITY_INDEX>(index);
map[lastEntity] = index;
}
mData.pop_back();
map.erase(pos);
return last;
}
return 0;
}
} // namespace filament
#endif // TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H

View File

@@ -1,373 +1,373 @@
/*
* Copyright (C) 2015 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_SLICE_H
#define TNT_UTILS_SLICE_H
#include <algorithm>
#include <atomic>
#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>
class Slice {
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;
Slice() noexcept = default;
Slice(const_iterator begin, const_iterator end) noexcept
: mBegin(const_cast<iterator>(begin)), mEndOffset(size_type(end - begin)) {
}
Slice(const_pointer begin, size_type count) noexcept
: mBegin(const_cast<iterator>(begin)), mEndOffset(size_type(count)) {
}
Slice(Slice const& rhs) noexcept = default;
Slice(Slice&& rhs) noexcept = default;
Slice& operator=(Slice const& rhs) noexcept = default;
Slice& operator=(Slice&& rhs) noexcept = default;
void set(pointer begin, size_type count) UTILS_RESTRICT noexcept {
mBegin = begin;
mEndOffset = size_type(count);
}
void set(iterator begin, iterator end) UTILS_RESTRICT noexcept {
mBegin = begin;
mEndOffset = size_type(end - begin);
}
void swap(Slice& rhs) UTILS_RESTRICT noexcept {
std::swap(mBegin, rhs.mBegin);
std::swap(mEndOffset, rhs.mEndOffset);
}
void clear() UTILS_RESTRICT noexcept {
mBegin = nullptr;
mEndOffset = 0;
}
// size
size_t size() const UTILS_RESTRICT noexcept { return mEndOffset; }
size_t sizeInBytes() const UTILS_RESTRICT noexcept { return size() * sizeof(T); }
bool empty() const UTILS_RESTRICT noexcept { return size() == 0; }
// iterators
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]; }
const_iterator cend() const UTILS_RESTRICT noexcept { return this->end(); }
// data access
reference operator[](size_t n) UTILS_RESTRICT noexcept {
assert(n < size());
return mBegin[n];
}
const_reference operator[](size_t n) const UTILS_RESTRICT noexcept {
assert(n < size());
return mBegin[n];
}
reference at(size_t n) UTILS_RESTRICT noexcept {
return operator[](n);
}
const_reference at(size_t n) const UTILS_RESTRICT noexcept {
return operator[](n);
}
reference front() UTILS_RESTRICT noexcept {
assert(!empty());
return *mBegin;
}
const_reference front() const UTILS_RESTRICT noexcept {
assert(!empty());
return *mBegin;
}
reference back() UTILS_RESTRICT noexcept {
assert(!empty());
return *(this->end() - 1);
}
const_reference back() const UTILS_RESTRICT noexcept {
assert(!empty());
return *(this->end() - 1);
}
pointer data() UTILS_RESTRICT noexcept {
return this->begin();
}
const_pointer data() const UTILS_RESTRICT noexcept {
return this->begin();
}
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;
};
} // namespace utils
#endif // TNT_UTILS_SLICE_H
/*
* Copyright (C) 2015 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_SLICE_H
#define TNT_UTILS_SLICE_H
#include <algorithm>
#include <atomic>
#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>
class Slice {
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;
Slice() noexcept = default;
Slice(const_iterator begin, const_iterator end) noexcept
: mBegin(const_cast<iterator>(begin)), mEndOffset(size_type(end - begin)) {
}
Slice(const_pointer begin, size_type count) noexcept
: mBegin(const_cast<iterator>(begin)), mEndOffset(size_type(count)) {
}
Slice(Slice const& rhs) noexcept = default;
Slice(Slice&& rhs) noexcept = default;
Slice& operator=(Slice const& rhs) noexcept = default;
Slice& operator=(Slice&& rhs) noexcept = default;
void set(pointer begin, size_type count) UTILS_RESTRICT noexcept {
mBegin = begin;
mEndOffset = size_type(count);
}
void set(iterator begin, iterator end) UTILS_RESTRICT noexcept {
mBegin = begin;
mEndOffset = size_type(end - begin);
}
void swap(Slice& rhs) UTILS_RESTRICT noexcept {
std::swap(mBegin, rhs.mBegin);
std::swap(mEndOffset, rhs.mEndOffset);
}
void clear() UTILS_RESTRICT noexcept {
mBegin = nullptr;
mEndOffset = 0;
}
// size
size_t size() const UTILS_RESTRICT noexcept { return mEndOffset; }
size_t sizeInBytes() const UTILS_RESTRICT noexcept { return size() * sizeof(T); }
bool empty() const UTILS_RESTRICT noexcept { return size() == 0; }
// iterators
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]; }
const_iterator cend() const UTILS_RESTRICT noexcept { return this->end(); }
// data access
reference operator[](size_t n) UTILS_RESTRICT noexcept {
assert(n < size());
return mBegin[n];
}
const_reference operator[](size_t n) const UTILS_RESTRICT noexcept {
assert(n < size());
return mBegin[n];
}
reference at(size_t n) UTILS_RESTRICT noexcept {
return operator[](n);
}
const_reference at(size_t n) const UTILS_RESTRICT noexcept {
return operator[](n);
}
reference front() UTILS_RESTRICT noexcept {
assert(!empty());
return *mBegin;
}
const_reference front() const UTILS_RESTRICT noexcept {
assert(!empty());
return *mBegin;
}
reference back() UTILS_RESTRICT noexcept {
assert(!empty());
return *(this->end() - 1);
}
const_reference back() const UTILS_RESTRICT noexcept {
assert(!empty());
return *(this->end() - 1);
}
pointer data() UTILS_RESTRICT noexcept {
return this->begin();
}
const_pointer data() const UTILS_RESTRICT noexcept {
return this->begin();
}
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;
};
} // namespace utils
#endif // TNT_UTILS_SLICE_H

View File

@@ -1,90 +1,90 @@
/*
* Copyright (C) 2019 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_SPINLOCK_H
#define TNT_UTILS_SPINLOCK_H
#include <utils/compiler.h>
#include <utils/Mutex.h>
#include <atomic>
#include <type_traits>
#include <assert.h>
#include <stddef.h>
namespace utils {
namespace details {
class SpinLock {
std::atomic_flag mLock = ATOMIC_FLAG_INIT;
public:
void lock() noexcept {
UTILS_PREFETCHW(&mLock);
#ifdef __ARM_ACLE
// we signal an event on this CPU, so that the first yield() will be a no-op,
// and falls through the test_and_set(). This is more efficient than a while { }
// construct.
UTILS_SIGNAL_EVENT();
do {
yield();
} while (mLock.test_and_set(std::memory_order_acquire));
#else
goto start;
do {
yield();
start: ;
} while (mLock.test_and_set(std::memory_order_acquire));
#endif
}
void unlock() noexcept {
mLock.clear(std::memory_order_release);
#ifdef __ARM_ARCH_7A__
// on ARMv7a SEL is needed
UTILS_SIGNAL_EVENT();
// as well as a memory barrier is needed
__dsb(0xA); // ISHST = 0xA (b1010)
#else
// on ARMv8 we could avoid the call to SE, but we'd need to write the
// test_and_set() above by hand, so the WFE only happens without a STRX first.
UTILS_BROADCAST_EVENT();
#endif
}
private:
inline void yield() noexcept {
// on x86 call pause instruction, on ARM call WFE
UTILS_WAIT_FOR_EVENT();
}
};
} // namespace details
#if defined(__SANITIZE_THREAD__)
// Unfortunately TSAN doesn't support homegrown synchronization primitives
using SpinLock = Mutex;
#elif defined(__ARM_ARCH_7A__)
// We've had problems with "wfe" on some ARM-V7 devices, causing spurious SIGILL
using SpinLock = Mutex;
#else
using SpinLock = details::SpinLock;
#endif
} // namespace utils
#endif //TNT_UTILS_SPINLOCK_H
/*
* Copyright (C) 2019 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_SPINLOCK_H
#define TNT_UTILS_SPINLOCK_H
#include <utils/compiler.h>
#include <utils/Mutex.h>
#include <atomic>
#include <type_traits>
#include <assert.h>
#include <stddef.h>
namespace utils {
namespace details {
class SpinLock {
std::atomic_flag mLock = ATOMIC_FLAG_INIT;
public:
void lock() noexcept {
UTILS_PREFETCHW(&mLock);
#ifdef __ARM_ACLE
// we signal an event on this CPU, so that the first yield() will be a no-op,
// and falls through the test_and_set(). This is more efficient than a while { }
// construct.
UTILS_SIGNAL_EVENT();
do {
yield();
} while (mLock.test_and_set(std::memory_order_acquire));
#else
goto start;
do {
yield();
start: ;
} while (mLock.test_and_set(std::memory_order_acquire));
#endif
}
void unlock() noexcept {
mLock.clear(std::memory_order_release);
#ifdef __ARM_ARCH_7A__
// on ARMv7a SEL is needed
UTILS_SIGNAL_EVENT();
// as well as a memory barrier is needed
__dsb(0xA); // ISHST = 0xA (b1010)
#else
// on ARMv8 we could avoid the call to SE, but we'd need to write the
// test_and_set() above by hand, so the WFE only happens without a STRX first.
UTILS_BROADCAST_EVENT();
#endif
}
private:
inline void yield() noexcept {
// on x86 call pause instruction, on ARM call WFE
UTILS_WAIT_FOR_EVENT();
}
};
} // namespace details
#if UTILS_HAS_SANITIZE_THREAD
// Active spins with atomics slow down execution too much under ThreadSanitizer.
using SpinLock = Mutex;
#elif defined(__ARM_ARCH_7A__)
// We've had problems with "wfe" on some ARM-V7 devices, causing spurious SIGILL
using SpinLock = Mutex;
#else
using SpinLock = details::SpinLock;
#endif
} // namespace utils
#endif // TNT_UTILS_SPINLOCK_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,278 +1,278 @@
/*
* Copyright (C) 2016 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_SYSTRACE_H
#define TNT_UTILS_SYSTRACE_H
#define SYSTRACE_TAG_NEVER (0)
#define SYSTRACE_TAG_ALWAYS (1<<0)
#define SYSTRACE_TAG_FILAMENT (1<<1) // don't change, used in makefiles
#define SYSTRACE_TAG_JOBSYSTEM (1<<2)
#if defined(ANDROID)
#include <atomic>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <utils/compiler.h>
/*
* The SYSTRACE_ macros use SYSTRACE_TAG as a the TAG, which should be defined
* before this file is included. If not, the SYSTRACE_TAG_ALWAYS tag will be used.
*/
#ifndef SYSTRACE_TAG
#define SYSTRACE_TAG (SYSTRACE_TAG_ALWAYS)
#endif
// enable tracing
#define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(SYSTRACE_TAG)
// disable tracing
#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG)
/**
* Creates a Systrace context in the current scope. needed for calling all other systrace
* commands below.
*/
#define SYSTRACE_CONTEXT() ::utils::details::Systrace ___tracer(SYSTRACE_TAG)
// SYSTRACE_NAME traces the beginning and end of the current scope. To trace
// the correct start and end times this macro should be declared first in the
// scope body.
// It also automatically creates a Systrace context
#define SYSTRACE_NAME(name) ::utils::details::ScopedTrace ___tracer(SYSTRACE_TAG, name)
// SYSTRACE_CALL is an SYSTRACE_NAME that uses the current function name.
#define SYSTRACE_CALL() SYSTRACE_NAME(__FUNCTION__)
#define SYSTRACE_NAME_BEGIN(name) \
___tracer.traceBegin(SYSTRACE_TAG, name)
#define SYSTRACE_NAME_END() \
___tracer.traceEnd(SYSTRACE_TAG)
/**
* Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END
* contexts, asynchronous events do not need to be nested. The name describes
* the event, and the cookie provides a unique identifier for distinguishing
* simultaneous events. The name and cookie used to begin an event must be
* used to end it.
*/
#define SYSTRACE_ASYNC_BEGIN(name, cookie) \
___tracer.asyncBegin(SYSTRACE_TAG, name, cookie)
/**
* Trace the end of an asynchronous event.
* This should have a corresponding SYSTRACE_ASYNC_BEGIN.
*/
#define SYSTRACE_ASYNC_END(name, cookie) \
___tracer.asyncEnd(SYSTRACE_TAG, name, cookie)
/**
* Traces an integer counter value. name is used to identify the counter.
* This can be used to track how a value changes over time.
*/
#define SYSTRACE_VALUE32(name, val) \
___tracer.value(SYSTRACE_TAG, name, int32_t(val))
#define SYSTRACE_VALUE64(name, val) \
___tracer.value(SYSTRACE_TAG, name, int64_t(val))
// ------------------------------------------------------------------------------------------------
// No user serviceable code below...
// ------------------------------------------------------------------------------------------------
namespace utils {
namespace details {
class Systrace {
public:
enum tags {
NEVER = SYSTRACE_TAG_NEVER,
ALWAYS = SYSTRACE_TAG_ALWAYS,
FILAMENT = SYSTRACE_TAG_FILAMENT,
JOBSYSTEM = SYSTRACE_TAG_JOBSYSTEM
// we could define more TAGS here, as we need them.
};
Systrace(uint32_t tag) noexcept {
if (tag) init(tag);
}
static void enable(uint32_t tags) noexcept;
static void disable(uint32_t tags) noexcept;
inline void traceBegin(uint32_t tag, const char* name) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
beginSection(this, name);
}
}
inline void traceEnd(uint32_t tag) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
endSection(this);
}
}
inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
beginAsyncSection(this, name, cookie);
}
}
inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
endAsyncSection(this, name, cookie);
}
}
inline void value(uint32_t tag, const char* name, int32_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
setCounter(this, name, value);
}
}
inline void value(uint32_t tag, const char* name, int64_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
setCounter(this, name, value);
}
}
private:
friend class ScopedTrace;
// whether tracing is supported at all by the platform
using ATrace_isEnabled_t = bool (*)(void);
using ATrace_beginSection_t = void (*)(const char* sectionName);
using ATrace_endSection_t = void (*)(void);
using ATrace_beginAsyncSection_t = void (*)(const char* sectionName, int32_t cookie);
using ATrace_endAsyncSection_t = void (*)(const char* sectionName, int32_t cookie);
using ATrace_setCounter_t = void (*)(const char* counterName, int64_t counterValue);
struct GlobalState {
bool isTracingAvailable;
std::atomic<uint32_t> isTracingEnabled;
int markerFd;
ATrace_isEnabled_t ATrace_isEnabled;
ATrace_beginSection_t ATrace_beginSection;
ATrace_endSection_t ATrace_endSection;
ATrace_beginAsyncSection_t ATrace_beginAsyncSection;
ATrace_endAsyncSection_t ATrace_endAsyncSection;
ATrace_setCounter_t ATrace_setCounter;
void (*beginSection)(Systrace* that, const char* name);
void (*endSection)(Systrace* that);
void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*setCounter)(Systrace* that, const char* name, int64_t value);
};
static GlobalState sGlobalState;
// per-instance versions for better performance
ATrace_isEnabled_t ATrace_isEnabled;
ATrace_beginSection_t ATrace_beginSection;
ATrace_endSection_t ATrace_endSection;
ATrace_beginAsyncSection_t ATrace_beginAsyncSection;
ATrace_endAsyncSection_t ATrace_endAsyncSection;
ATrace_setCounter_t ATrace_setCounter;
void (*beginSection)(Systrace* that, const char* name);
void (*endSection)(Systrace* that);
void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*setCounter)(Systrace* that, const char* name, int64_t value);
void init(uint32_t tag) noexcept;
// cached values for faster access, no need to be initialized
bool mIsTracingEnabled;
int mMarkerFd = -1;
pid_t mPid;
static void setup() noexcept;
static void init_once() noexcept;
static bool isTracingEnabled(uint32_t tag) noexcept;
static void begin_body(int fd, int pid, const char* name) noexcept;
static void end_body(int fd, int pid) noexcept;
static void async_begin_body(int fd, int pid, const char* name, int32_t cookie) noexcept;
static void async_end_body(int fd, int pid, const char* name, int32_t cookie) noexcept;
static void int64_body(int fd, int pid, const char* name, int64_t value) noexcept;
};
// ------------------------------------------------------------------------------------------------
class ScopedTrace {
public:
// we don't inline this because it's relatively heavy due to a global check
ScopedTrace(uint32_t tag, const char* name) noexcept : mTrace(tag), mTag(tag) {
mTrace.traceBegin(tag, name);
}
inline ~ScopedTrace() noexcept {
mTrace.traceEnd(mTag);
}
inline void value(uint32_t tag, const char* name, int32_t v) noexcept {
mTrace.value(tag, name, v);
}
inline void value(uint32_t tag, const char* name, int64_t v) noexcept {
mTrace.value(tag, name, v);
}
private:
Systrace mTrace;
const uint32_t mTag;
};
} // namespace details
} // namespace utils
// ------------------------------------------------------------------------------------------------
#else // !ANDROID
// ------------------------------------------------------------------------------------------------
#define SYSTRACE_ENABLE()
#define SYSTRACE_DISABLE()
#define SYSTRACE_CONTEXT()
#define SYSTRACE_NAME(name)
#define SYSTRACE_NAME_BEGIN(name)
#define SYSTRACE_NAME_END()
#define SYSTRACE_CALL()
#define SYSTRACE_ASYNC_BEGIN(name, cookie)
#define SYSTRACE_ASYNC_END(name, cookie)
#define SYSTRACE_VALUE32(name, val)
#define SYSTRACE_VALUE64(name, val)
#endif // ANDROID
#endif // TNT_UTILS_SYSTRACE_H
/*
* Copyright (C) 2016 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_SYSTRACE_H
#define TNT_UTILS_SYSTRACE_H
#define SYSTRACE_TAG_NEVER (0)
#define SYSTRACE_TAG_ALWAYS (1<<0)
#define SYSTRACE_TAG_FILAMENT (1<<1) // don't change, used in makefiles
#define SYSTRACE_TAG_JOBSYSTEM (1<<2)
#if defined(__ANDROID__)
#include <atomic>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <utils/compiler.h>
/*
* The SYSTRACE_ macros use SYSTRACE_TAG as a the TAG, which should be defined
* before this file is included. If not, the SYSTRACE_TAG_ALWAYS tag will be used.
*/
#ifndef SYSTRACE_TAG
#define SYSTRACE_TAG (SYSTRACE_TAG_ALWAYS)
#endif
// enable tracing
#define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(SYSTRACE_TAG)
// disable tracing
#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG)
/**
* Creates a Systrace context in the current scope. needed for calling all other systrace
* commands below.
*/
#define SYSTRACE_CONTEXT() ::utils::details::Systrace ___tracer(SYSTRACE_TAG)
// SYSTRACE_NAME traces the beginning and end of the current scope. To trace
// the correct start and end times this macro should be declared first in the
// scope body.
// It also automatically creates a Systrace context
#define SYSTRACE_NAME(name) ::utils::details::ScopedTrace ___tracer(SYSTRACE_TAG, name)
// SYSTRACE_CALL is an SYSTRACE_NAME that uses the current function name.
#define SYSTRACE_CALL() SYSTRACE_NAME(__FUNCTION__)
#define SYSTRACE_NAME_BEGIN(name) \
___tracer.traceBegin(SYSTRACE_TAG, name)
#define SYSTRACE_NAME_END() \
___tracer.traceEnd(SYSTRACE_TAG)
/**
* Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END
* contexts, asynchronous events do not need to be nested. The name describes
* the event, and the cookie provides a unique identifier for distinguishing
* simultaneous events. The name and cookie used to begin an event must be
* used to end it.
*/
#define SYSTRACE_ASYNC_BEGIN(name, cookie) \
___tracer.asyncBegin(SYSTRACE_TAG, name, cookie)
/**
* Trace the end of an asynchronous event.
* This should have a corresponding SYSTRACE_ASYNC_BEGIN.
*/
#define SYSTRACE_ASYNC_END(name, cookie) \
___tracer.asyncEnd(SYSTRACE_TAG, name, cookie)
/**
* Traces an integer counter value. name is used to identify the counter.
* This can be used to track how a value changes over time.
*/
#define SYSTRACE_VALUE32(name, val) \
___tracer.value(SYSTRACE_TAG, name, int32_t(val))
#define SYSTRACE_VALUE64(name, val) \
___tracer.value(SYSTRACE_TAG, name, int64_t(val))
// ------------------------------------------------------------------------------------------------
// No user serviceable code below...
// ------------------------------------------------------------------------------------------------
namespace utils {
namespace details {
class Systrace {
public:
enum tags {
NEVER = SYSTRACE_TAG_NEVER,
ALWAYS = SYSTRACE_TAG_ALWAYS,
FILAMENT = SYSTRACE_TAG_FILAMENT,
JOBSYSTEM = SYSTRACE_TAG_JOBSYSTEM
// we could define more TAGS here, as we need them.
};
Systrace(uint32_t tag) noexcept {
if (tag) init(tag);
}
static void enable(uint32_t tags) noexcept;
static void disable(uint32_t tags) noexcept;
inline void traceBegin(uint32_t tag, const char* name) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
beginSection(this, name);
}
}
inline void traceEnd(uint32_t tag) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
endSection(this);
}
}
inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
beginAsyncSection(this, name, cookie);
}
}
inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
endAsyncSection(this, name, cookie);
}
}
inline void value(uint32_t tag, const char* name, int32_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
setCounter(this, name, value);
}
}
inline void value(uint32_t tag, const char* name, int64_t value) noexcept {
if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) {
setCounter(this, name, value);
}
}
private:
friend class ScopedTrace;
// whether tracing is supported at all by the platform
using ATrace_isEnabled_t = bool (*)(void);
using ATrace_beginSection_t = void (*)(const char* sectionName);
using ATrace_endSection_t = void (*)(void);
using ATrace_beginAsyncSection_t = void (*)(const char* sectionName, int32_t cookie);
using ATrace_endAsyncSection_t = void (*)(const char* sectionName, int32_t cookie);
using ATrace_setCounter_t = void (*)(const char* counterName, int64_t counterValue);
struct GlobalState {
bool isTracingAvailable;
std::atomic<uint32_t> isTracingEnabled;
int markerFd;
ATrace_isEnabled_t ATrace_isEnabled;
ATrace_beginSection_t ATrace_beginSection;
ATrace_endSection_t ATrace_endSection;
ATrace_beginAsyncSection_t ATrace_beginAsyncSection;
ATrace_endAsyncSection_t ATrace_endAsyncSection;
ATrace_setCounter_t ATrace_setCounter;
void (*beginSection)(Systrace* that, const char* name);
void (*endSection)(Systrace* that);
void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*setCounter)(Systrace* that, const char* name, int64_t value);
};
static GlobalState sGlobalState;
// per-instance versions for better performance
ATrace_isEnabled_t ATrace_isEnabled;
ATrace_beginSection_t ATrace_beginSection;
ATrace_endSection_t ATrace_endSection;
ATrace_beginAsyncSection_t ATrace_beginAsyncSection;
ATrace_endAsyncSection_t ATrace_endAsyncSection;
ATrace_setCounter_t ATrace_setCounter;
void (*beginSection)(Systrace* that, const char* name);
void (*endSection)(Systrace* that);
void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie);
void (*setCounter)(Systrace* that, const char* name, int64_t value);
void init(uint32_t tag) noexcept;
// cached values for faster access, no need to be initialized
bool mIsTracingEnabled;
int mMarkerFd = -1;
pid_t mPid;
static void setup() noexcept;
static void init_once() noexcept;
static bool isTracingEnabled(uint32_t tag) noexcept;
static void begin_body(int fd, int pid, const char* name) noexcept;
static void end_body(int fd, int pid) noexcept;
static void async_begin_body(int fd, int pid, const char* name, int32_t cookie) noexcept;
static void async_end_body(int fd, int pid, const char* name, int32_t cookie) noexcept;
static void int64_body(int fd, int pid, const char* name, int64_t value) noexcept;
};
// ------------------------------------------------------------------------------------------------
class ScopedTrace {
public:
// we don't inline this because it's relatively heavy due to a global check
ScopedTrace(uint32_t tag, const char* name) noexcept : mTrace(tag), mTag(tag) {
mTrace.traceBegin(tag, name);
}
inline ~ScopedTrace() noexcept {
mTrace.traceEnd(mTag);
}
inline void value(uint32_t tag, const char* name, int32_t v) noexcept {
mTrace.value(tag, name, v);
}
inline void value(uint32_t tag, const char* name, int64_t v) noexcept {
mTrace.value(tag, name, v);
}
private:
Systrace mTrace;
const uint32_t mTag;
};
} // namespace details
} // namespace utils
// ------------------------------------------------------------------------------------------------
#else // !ANDROID
// ------------------------------------------------------------------------------------------------
#define SYSTRACE_ENABLE()
#define SYSTRACE_DISABLE()
#define SYSTRACE_CONTEXT()
#define SYSTRACE_NAME(name)
#define SYSTRACE_NAME_BEGIN(name)
#define SYSTRACE_NAME_END()
#define SYSTRACE_CALL()
#define SYSTRACE_ASYNC_BEGIN(name, cookie)
#define SYSTRACE_ASYNC_END(name, cookie)
#define SYSTRACE_VALUE32(name, val)
#define SYSTRACE_VALUE64(name, val)
#endif // ANDROID
#endif // TNT_UTILS_SYSTRACE_H

View File

@@ -0,0 +1,26 @@
/*
* 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_THERMALMANAGER_H
#define TNT_UTILS_THERMALMANAGER_H
#if defined(__ANDROID__)
#include <utils/android/ThermalManager.h>
#else
#include <utils/generic/ThermalManager.h>
#endif
#endif // TNT_UTILS_THERMALMANAGER_H

View File

@@ -1,202 +1,202 @@
/*
* Copyright (C) 2016 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_WORKSTEALINGDEQUEUE_H
#define TNT_UTILS_WORKSTEALINGDEQUEUE_H
#include <atomic>
#include <assert.h>
#include <stddef.h>
namespace utils {
/*
* A templated, lockless, fixed-size work-stealing dequeue
*
*
* top bottom
* v v
* |----|----|----|----|----|----|
* steal() push(), pop()
* any thread main thread
*
*
*/
template <typename TYPE, size_t COUNT>
class WorkStealingDequeue {
static_assert(!(COUNT & (COUNT - 1)), "COUNT must be a power of two");
static constexpr size_t MASK = COUNT - 1;
// mTop and mBottom must be signed integers. We use 64-bits atomics so we don't have
// to worry about wrapping around.
using index_t = int64_t;
std::atomic<index_t> mTop = { 0 }; // written/read in pop()/steal()
std::atomic<index_t> mBottom = { 0 }; // written only in pop(), read in push(), steal()
TYPE mItems[COUNT];
// NOTE: it's not safe to return a reference because getItemAt() can be called
// concurrently and the caller could std::move() the item unsafely.
TYPE getItemAt(index_t index) noexcept { return mItems[index & MASK]; }
void setItemAt(index_t index, TYPE item) noexcept { mItems[index & MASK] = item; }
public:
using value_type = TYPE;
inline void push(TYPE item) noexcept;
inline TYPE pop() noexcept;
inline TYPE steal() noexcept;
size_t getSize() const noexcept { return COUNT; }
// for debugging only...
size_t getCount() const noexcept {
index_t bottom = mBottom.load(std::memory_order_relaxed);
index_t top = mTop.load(std::memory_order_relaxed);
return bottom - top;
}
};
/*
* Adds an item at the BOTTOM of the queue.
*
* Must be called from the main thread.
*/
template <typename TYPE, size_t COUNT>
void WorkStealingDequeue<TYPE, COUNT>::push(TYPE item) noexcept {
// std::memory_order_relaxed is sufficient because this load doesn't acquire anything from
// another thread. mBottom is only written in pop() which cannot be concurrent with push()
index_t bottom = mBottom.load(std::memory_order_relaxed);
setItemAt(bottom, item);
// std::memory_order_release is used because we release the item we just pushed to other
// threads which are calling steal().
mBottom.store(bottom + 1, std::memory_order_release);
}
/*
* Removes an item from the BOTTOM of the queue.
*
* Must be called from the main thread.
*/
template <typename TYPE, size_t COUNT>
TYPE WorkStealingDequeue<TYPE, COUNT>::pop() noexcept {
// std::memory_order_seq_cst is needed to guarantee ordering in steal()
// Note however that this is not a typical acquire/release operation:
// - not acquire because mBottom is only written in push() which is not concurrent
// - not release because we're not publishing anything to steal() here
//
// QUESTION: does this prevent mTop load below to be reordered before the "store" part of
// fetch_sub()? Hopefully it does. If not we'd need a full memory barrier.
//
index_t bottom = mBottom.fetch_sub(1, std::memory_order_seq_cst) - 1;
// bottom could be -1 if we tried to pop() from an empty queue. This will be corrected below.
assert( bottom >= -1 );
// std::memory_order_seq_cst is needed to guarantee ordering in steal()
// Note however that this is not a typical acquire operation
// (i.e. other thread's writes of mTop don't publish data)
index_t top = mTop.load(std::memory_order_seq_cst);
if (top < bottom) {
// Queue isn't empty and it's not the last item, just return it, this is the common case.
return getItemAt(bottom);
}
TYPE item{};
if (top == bottom) {
// we just took the last item
item = getItemAt(bottom);
// Because we know we took the last item, we could be racing with steal() -- the last
// item being both at the top and bottom of the queue.
// We resolve this potential race by also stealing that item from ourselves.
if (mTop.compare_exchange_strong(top, top + 1,
std::memory_order_seq_cst,
std::memory_order_relaxed)) {
// success: we stole our last item from ourself, meaning that a concurrent steal()
// would have failed.
// mTop now equals top + 1, we adjust top to make the queue empty.
top++;
} else {
// failure: mTop was not equal to top, which means the item was stolen under our feet.
// top now equals to mTop. Simply discard the item we just popped.
// The queue is now empty.
item = TYPE();
}
} else {
// We could be here if the item was stolen just before we read mTop, we'll adjust
// mBottom below.
assert(top - bottom == 1);
}
// std::memory_order_relaxed used because we're not publishing any data.
// no concurrent writes to mBottom possible, it's always safe to write mBottom.
mBottom.store(top, std::memory_order_relaxed);
return item;
}
/*
* Steals an item from the TOP of another thread's queue.
*
* This can be called concurrently with steal(), push() or pop()
*
* steal() never fails, either there is an item and it atomically takes it, or there isn't and
* it returns an empty item.
*/
template <typename TYPE, size_t COUNT>
TYPE WorkStealingDequeue<TYPE, COUNT>::steal() noexcept {
while (true) {
/*
* Note: A Key component of this algorithm is that mTop is read before mBottom here
* (and observed as such in other threads)
*/
// std::memory_order_seq_cst is needed to guarantee ordering in pop()
// Note however that this is not a typical acquire operation
// (i.e. other thread's writes of mTop don't publish data)
index_t top = mTop.load(std::memory_order_seq_cst);
// std::memory_order_acquire is needed because we're acquiring items published in push().
// std::memory_order_seq_cst is needed to guarantee ordering in pop()
index_t bottom = mBottom.load(std::memory_order_seq_cst);
if (top >= bottom) {
// queue is empty
return TYPE();
}
// The queue isn't empty
TYPE item(getItemAt(top));
if (mTop.compare_exchange_strong(top, top + 1,
std::memory_order_seq_cst,
std::memory_order_relaxed)) {
// success: we stole an item, just return it.
return item;
}
// failure: the item we just tried to steal was pop()'ed under our feet,
// simply discard it; nothing to do -- it's okay to try again.
}
}
} // namespace utils
#endif // TNT_UTILS_WORKSTEALINGDEQUEUE_H
/*
* Copyright (C) 2016 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_WORKSTEALINGDEQUEUE_H
#define TNT_UTILS_WORKSTEALINGDEQUEUE_H
#include <atomic>
#include <assert.h>
#include <stddef.h>
namespace utils {
/*
* A templated, lockless, fixed-size work-stealing dequeue
*
*
* top bottom
* v v
* |----|----|----|----|----|----|
* steal() push(), pop()
* any thread main thread
*
*
*/
template <typename TYPE, size_t COUNT>
class WorkStealingDequeue {
static_assert(!(COUNT & (COUNT - 1)), "COUNT must be a power of two");
static constexpr size_t MASK = COUNT - 1;
// mTop and mBottom must be signed integers. We use 64-bits atomics so we don't have
// to worry about wrapping around.
using index_t = int64_t;
std::atomic<index_t> mTop = { 0 }; // written/read in pop()/steal()
std::atomic<index_t> mBottom = { 0 }; // written only in pop(), read in push(), steal()
TYPE mItems[COUNT];
// NOTE: it's not safe to return a reference because getItemAt() can be called
// concurrently and the caller could std::move() the item unsafely.
TYPE getItemAt(index_t index) noexcept { return mItems[index & MASK]; }
void setItemAt(index_t index, TYPE item) noexcept { mItems[index & MASK] = item; }
public:
using value_type = TYPE;
inline void push(TYPE item) noexcept;
inline TYPE pop() noexcept;
inline TYPE steal() noexcept;
size_t getSize() const noexcept { return COUNT; }
// for debugging only...
size_t getCount() const noexcept {
index_t bottom = mBottom.load(std::memory_order_relaxed);
index_t top = mTop.load(std::memory_order_relaxed);
return bottom - top;
}
};
/*
* Adds an item at the BOTTOM of the queue.
*
* Must be called from the main thread.
*/
template <typename TYPE, size_t COUNT>
void WorkStealingDequeue<TYPE, COUNT>::push(TYPE item) noexcept {
// std::memory_order_relaxed is sufficient because this load doesn't acquire anything from
// another thread. mBottom is only written in pop() which cannot be concurrent with push()
index_t bottom = mBottom.load(std::memory_order_relaxed);
setItemAt(bottom, item);
// std::memory_order_release is used because we release the item we just pushed to other
// threads which are calling steal().
mBottom.store(bottom + 1, std::memory_order_release);
}
/*
* Removes an item from the BOTTOM of the queue.
*
* Must be called from the main thread.
*/
template <typename TYPE, size_t COUNT>
TYPE WorkStealingDequeue<TYPE, COUNT>::pop() noexcept {
// std::memory_order_seq_cst is needed to guarantee ordering in steal()
// Note however that this is not a typical acquire/release operation:
// - not acquire because mBottom is only written in push() which is not concurrent
// - not release because we're not publishing anything to steal() here
//
// QUESTION: does this prevent mTop load below to be reordered before the "store" part of
// fetch_sub()? Hopefully it does. If not we'd need a full memory barrier.
//
index_t bottom = mBottom.fetch_sub(1, std::memory_order_seq_cst) - 1;
// bottom could be -1 if we tried to pop() from an empty queue. This will be corrected below.
assert( bottom >= -1 );
// std::memory_order_seq_cst is needed to guarantee ordering in steal()
// Note however that this is not a typical acquire operation
// (i.e. other thread's writes of mTop don't publish data)
index_t top = mTop.load(std::memory_order_seq_cst);
if (top < bottom) {
// Queue isn't empty and it's not the last item, just return it, this is the common case.
return getItemAt(bottom);
}
TYPE item{};
if (top == bottom) {
// we just took the last item
item = getItemAt(bottom);
// Because we know we took the last item, we could be racing with steal() -- the last
// item being both at the top and bottom of the queue.
// We resolve this potential race by also stealing that item from ourselves.
if (mTop.compare_exchange_strong(top, top + 1,
std::memory_order_seq_cst,
std::memory_order_relaxed)) {
// success: we stole our last item from ourself, meaning that a concurrent steal()
// would have failed.
// mTop now equals top + 1, we adjust top to make the queue empty.
top++;
} else {
// failure: mTop was not equal to top, which means the item was stolen under our feet.
// top now equals to mTop. Simply discard the item we just popped.
// The queue is now empty.
item = TYPE();
}
} else {
// We could be here if the item was stolen just before we read mTop, we'll adjust
// mBottom below.
assert(top - bottom == 1);
}
// std::memory_order_relaxed used because we're not publishing any data.
// no concurrent writes to mBottom possible, it's always safe to write mBottom.
mBottom.store(top, std::memory_order_relaxed);
return item;
}
/*
* Steals an item from the TOP of another thread's queue.
*
* This can be called concurrently with steal(), push() or pop()
*
* steal() never fails, either there is an item and it atomically takes it, or there isn't and
* it returns an empty item.
*/
template <typename TYPE, size_t COUNT>
TYPE WorkStealingDequeue<TYPE, COUNT>::steal() noexcept {
while (true) {
/*
* Note: A Key component of this algorithm is that mTop is read before mBottom here
* (and observed as such in other threads)
*/
// std::memory_order_seq_cst is needed to guarantee ordering in pop()
// Note however that this is not a typical acquire operation
// (i.e. other thread's writes of mTop don't publish data)
index_t top = mTop.load(std::memory_order_seq_cst);
// std::memory_order_acquire is needed because we're acquiring items published in push().
// std::memory_order_seq_cst is needed to guarantee ordering in pop()
index_t bottom = mBottom.load(std::memory_order_seq_cst);
if (top >= bottom) {
// queue is empty
return TYPE();
}
// The queue isn't empty
TYPE item(getItemAt(top));
if (mTop.compare_exchange_strong(top, top + 1,
std::memory_order_seq_cst,
std::memory_order_relaxed)) {
// success: we stole an item, just return it.
return item;
}
// failure: the item we just tried to steal was pop()'ed under our feet,
// simply discard it; nothing to do -- it's okay to try again.
}
}
} // namespace utils
#endif // TNT_UTILS_WORKSTEALINGDEQUEUE_H

View File

@@ -1,122 +1,122 @@
/*
* Copyright (C) 2018 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_ZIP2ITERATOR_H
#define TNT_UTILS_ZIP2ITERATOR_H
#include <iterator>
#include <type_traits>
#include <utils/compiler.h>
namespace utils {
/*
* A random access iterator that wraps two other random access iterators.
* This mostly exists so that one can sort an array using values from another.
*/
template<typename It1, typename It2>
class Zip2Iterator {
std::pair<It1, It2> mIt;
using Ref1 = typename std::iterator_traits<It1>::reference;
using Ref2 = typename std::iterator_traits<It2>::reference;
using Val1 = typename std::iterator_traits<It1>::value_type;
using Val2 = typename std::iterator_traits<It2>::value_type;
public:
struct Ref : public std::pair<Ref1, Ref2> {
using std::pair<Ref1, Ref2>::pair;
using std::pair<Ref1, Ref2>::operator=;
private:
friend void swap(Ref lhs, Ref rhs) {
using std::swap;
swap(lhs.first, rhs.first);
swap(lhs.second, rhs.second);
}
};
using value_type = std::pair<Val1, Val2>;
using reference = Ref;
using pointer = value_type*;
using difference_type = ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
Zip2Iterator() = default;
Zip2Iterator(It1 first, It2 second) : mIt({first, second}) {}
Zip2Iterator(Zip2Iterator const& rhs) noexcept = default;
Zip2Iterator& operator=(Zip2Iterator const& rhs) = default;
reference operator*() const { return { *mIt.first, *mIt.second }; }
reference operator[](size_t n) const { return *(*this + n); }
Zip2Iterator& operator++() {
++mIt.first;
++mIt.second;
return *this;
}
Zip2Iterator& operator--() {
--mIt.first;
--mIt.second;
return *this;
}
// Postfix operator needed by Microsoft C++
const Zip2Iterator operator++(int) {
Zip2Iterator t(*this);
mIt.first++;
mIt.second++;
return t;
}
const Zip2Iterator operator--(int) {
Zip2Iterator t(*this);
mIt.first--;
mIt.second--;
return t;
}
Zip2Iterator& operator+=(size_t v) {
mIt.first += v;
mIt.second += v;
return *this;
}
Zip2Iterator& operator-=(size_t v) {
mIt.first -= v;
mIt.second -= v;
return *this;
}
Zip2Iterator operator+(size_t rhs) const { return { mIt.first + rhs, mIt.second + rhs }; }
Zip2Iterator operator+(size_t rhs) { return { mIt.first + rhs, mIt.second + rhs }; }
Zip2Iterator operator-(size_t rhs) const { return { mIt.first - rhs, mIt.second - rhs }; }
difference_type operator-(Zip2Iterator const& rhs) const { return mIt.first - rhs.mIt.first; }
bool operator==(Zip2Iterator const& rhs) const { return (mIt.first == rhs.mIt.first); }
bool operator!=(Zip2Iterator const& rhs) const { return (mIt.first != rhs.mIt.first); }
bool operator>=(Zip2Iterator const& rhs) const { return (mIt.first >= rhs.mIt.first); }
bool operator> (Zip2Iterator const& rhs) const { return (mIt.first > rhs.mIt.first); }
bool operator<=(Zip2Iterator const& rhs) const { return (mIt.first <= rhs.mIt.first); }
bool operator< (Zip2Iterator const& rhs) const { return (mIt.first < rhs.mIt.first); }
};
} // namespace utils
#endif /* TNT_UTILS_ZIP2ITERATOR_H */
/*
* Copyright (C) 2018 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_ZIP2ITERATOR_H
#define TNT_UTILS_ZIP2ITERATOR_H
#include <iterator>
#include <type_traits>
#include <utils/compiler.h>
namespace utils {
/*
* A random access iterator that wraps two other random access iterators.
* This mostly exists so that one can sort an array using values from another.
*/
template<typename It1, typename It2>
class Zip2Iterator {
std::pair<It1, It2> mIt;
using Ref1 = typename std::iterator_traits<It1>::reference;
using Ref2 = typename std::iterator_traits<It2>::reference;
using Val1 = typename std::iterator_traits<It1>::value_type;
using Val2 = typename std::iterator_traits<It2>::value_type;
public:
struct Ref : public std::pair<Ref1, Ref2> {
using std::pair<Ref1, Ref2>::pair;
using std::pair<Ref1, Ref2>::operator=;
private:
friend void swap(Ref lhs, Ref rhs) {
using std::swap;
swap(lhs.first, rhs.first);
swap(lhs.second, rhs.second);
}
};
using value_type = std::pair<Val1, Val2>;
using reference = Ref;
using pointer = value_type*;
using difference_type = ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
Zip2Iterator() = default;
Zip2Iterator(It1 first, It2 second) : mIt({first, second}) {}
Zip2Iterator(Zip2Iterator const& rhs) noexcept = default;
Zip2Iterator& operator=(Zip2Iterator const& rhs) = default;
reference operator*() const { return { *mIt.first, *mIt.second }; }
reference operator[](size_t n) const { return *(*this + n); }
Zip2Iterator& operator++() {
++mIt.first;
++mIt.second;
return *this;
}
Zip2Iterator& operator--() {
--mIt.first;
--mIt.second;
return *this;
}
// Postfix operator needed by Microsoft C++
const Zip2Iterator operator++(int) {
Zip2Iterator t(*this);
mIt.first++;
mIt.second++;
return t;
}
const Zip2Iterator operator--(int) {
Zip2Iterator t(*this);
mIt.first--;
mIt.second--;
return t;
}
Zip2Iterator& operator+=(size_t v) {
mIt.first += v;
mIt.second += v;
return *this;
}
Zip2Iterator& operator-=(size_t v) {
mIt.first -= v;
mIt.second -= v;
return *this;
}
Zip2Iterator operator+(size_t rhs) const { return { mIt.first + rhs, mIt.second + rhs }; }
Zip2Iterator operator+(size_t rhs) { return { mIt.first + rhs, mIt.second + rhs }; }
Zip2Iterator operator-(size_t rhs) const { return { mIt.first - rhs, mIt.second - rhs }; }
difference_type operator-(Zip2Iterator const& rhs) const { return mIt.first - rhs.mIt.first; }
bool operator==(Zip2Iterator const& rhs) const { return (mIt.first == rhs.mIt.first); }
bool operator!=(Zip2Iterator const& rhs) const { return (mIt.first != rhs.mIt.first); }
bool operator>=(Zip2Iterator const& rhs) const { return (mIt.first >= rhs.mIt.first); }
bool operator> (Zip2Iterator const& rhs) const { return (mIt.first > rhs.mIt.first); }
bool operator<=(Zip2Iterator const& rhs) const { return (mIt.first <= rhs.mIt.first); }
bool operator< (Zip2Iterator const& rhs) const { return (mIt.first < rhs.mIt.first); }
};
} // namespace utils
#endif // TNT_UTILS_ZIP2ITERATOR_H

View File

@@ -1,277 +1,277 @@
/*
* Copyright (C) 2017 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_ALGORITHM_H
#define TNT_UTILS_ALGORITHM_H
#include <utils/compiler.h>
#include <functional> // for std::less
#include <type_traits> // for std::enable_if
#include <limits.h>
#include <stdint.h>
namespace utils {
namespace details {
template<typename T>
constexpr inline T popcount(T v) noexcept {
static_assert(sizeof(T) * CHAR_BIT <= 128, "details::popcount() only support up to 128 bits");
constexpr T ONES = ~T(0);
v = v - ((v >> 1u) & ONES / 3);
v = (v & ONES / 15 * 3) + ((v >> 2u) & ONES / 15 * 3);
v = (v + (v >> 4u)) & ONES / 255 * 15;
return (T) (v * (ONES / 255)) >> (sizeof(T) - 1) * CHAR_BIT;
}
template<typename T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
constexpr inline T clz(T x) noexcept {
static_assert(sizeof(T) * CHAR_BIT <= 128, "details::clz() only support up to 128 bits");
x |= (x >> 1u);
x |= (x >> 2u);
x |= (x >> 4u);
x |= (x >> 8u);
x |= (x >> 16u);
if (sizeof(T) * CHAR_BIT >= 64) { // just to silence compiler warning
x |= (x >> 32u);
}
if (sizeof(T) * CHAR_BIT >= 128) { // just to silence compiler warning
x |= (x >> 64u);
}
return T(sizeof(T) * CHAR_BIT) - details::popcount(x);
}
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 (x) c--;
if (sizeof(T) * CHAR_BIT >= 64) {
if (x & T(0x00000000FFFFFFFF)) c -= 32;
}
if (x & T(0x0000FFFF0000FFFF)) c -= 16;
if (x & T(0x00FF00FF00FF00FF)) c -= 8;
if (x & T(0x0F0F0F0F0F0F0F0F)) c -= 4;
if (x & T(0x3333333333333333)) c -= 2;
if (x & T(0x5555555555555555)) c -= 1;
return c;
}
} // namespace details
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned int UTILS_ALWAYS_INLINE clz(unsigned int x) noexcept {
#if __has_builtin(__builtin_clz)
return __builtin_clz(x);
#else
return details::clz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long UTILS_ALWAYS_INLINE clz(unsigned long x) noexcept {
#if __has_builtin(__builtin_clzl)
return __builtin_clzl(x);
#else
return details::clz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long long UTILS_ALWAYS_INLINE clz(unsigned long long x) noexcept {
#if __has_builtin(__builtin_clzll)
return __builtin_clzll(x);
#else
return details::clz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned int UTILS_ALWAYS_INLINE ctz(unsigned int x) noexcept {
#if __has_builtin(__builtin_ctz)
return __builtin_ctz(x);
#else
return details::ctz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long UTILS_ALWAYS_INLINE ctz(unsigned long x) noexcept {
#if __has_builtin(__builtin_ctzl)
return __builtin_ctzl(x);
#else
return details::ctz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long long UTILS_ALWAYS_INLINE ctz(unsigned long long x) noexcept {
#if __has_builtin(__builtin_ctzll)
return __builtin_ctzll(x);
#else
return details::ctz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned int UTILS_ALWAYS_INLINE popcount(unsigned int x) noexcept {
#if __has_builtin(__builtin_popcount)
return __builtin_popcount(x);
#else
return details::popcount(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long UTILS_ALWAYS_INLINE popcount(unsigned long x) noexcept {
#if __has_builtin(__builtin_popcountl)
return __builtin_popcountl(x);
#else
return details::popcount(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long long UTILS_ALWAYS_INLINE popcount(unsigned long long x) noexcept {
#if __has_builtin(__builtin_popcountll)
return __builtin_popcountll(x);
#else
return details::popcount(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
uint8_t UTILS_ALWAYS_INLINE popcount(uint8_t x) noexcept {
return (uint8_t)popcount((unsigned int)x);
}
template<typename T,
typename = std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value>>
constexpr inline UTILS_PUBLIC UTILS_PURE
T log2i(T x) noexcept {
return (sizeof(x) * 8 - 1u) - clz(x);
}
/*
* branch-less version of std::lower_bound and std::upper_bound.
* These versions are intended to be fully inlined, which only happens when the size
* of the array is known at compile time. This code also performs better if the
* array is a power-of-two in size.
*
* These code works even if the conditions above are not met, and becomes a less-branches
* algorithm instead of a branch-less one!
*/
template<typename RandomAccessIterator, typename T, typename COMPARE = typename std::less<T>>
inline UTILS_PUBLIC
RandomAccessIterator lower_bound(
RandomAccessIterator first, RandomAccessIterator last, const T& value,
COMPARE comp = std::less<T>(),
bool assume_power_of_two = false) {
size_t len = last - first;
if (!assume_power_of_two) {
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
// and gets optimized out if the size is known at compile time.
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
size_t difference = (last - first) - len;
// If len was already a POT, then difference will be 0.
// We need to explicitly check this case to avoid dereferencing past the end of the array
first += !difference || comp(first[len], value) ? difference : 0;
}
while (len) {
// The number of repetitions here doesn't affect the result. We manually unroll the loop
// twice, to guarantee we have at least two iterations without branches (for the case
// where the size is not known at compile time
first += comp(first[len >>= 1u], value) ? len : 0;
first += comp(first[len >>= 1u], value) ? len : 0;
}
first += comp(*first, value);
return first;
}
template<typename RandomAccessIterator, typename T, typename COMPARE = typename std::less<T>>
inline UTILS_PUBLIC
RandomAccessIterator upper_bound(
RandomAccessIterator first, RandomAccessIterator last,
const T& value, COMPARE comp = std::less<T>(),
bool assume_power_of_two = false) {
size_t len = last - first;
if (!assume_power_of_two) {
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
// and gets optimized out if the size is known at compile time.
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
size_t difference = (last - first) - len;
// If len was already a POT, then difference will be 0.
// We need to explicitly check this case to avoid dereferencing past the end of the array
first += !difference || comp(value, first[len]) ? 0 : difference;
}
while (len) {
// The number of repetitions here doesn't affect the result. We manually unroll the loop
// twice, to guarantee we have at least two iterations without branches (for the case
// where the size is not known at compile time
first += !comp(value, first[len >>= 1u]) ? len : 0;
first += !comp(value, first[len >>= 1u]) ? len : 0;
}
first += !comp(value, *first);
return first;
}
template<typename RandomAccessIterator, typename COMPARE>
inline UTILS_PUBLIC
RandomAccessIterator partition_point(
RandomAccessIterator first, RandomAccessIterator last, COMPARE pred,
bool assume_power_of_two = false) {
size_t len = last - first;
if (!assume_power_of_two) {
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
// and gets optimized out if the size is known at compile time.
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
size_t difference = (last - first) - len;
first += !difference || pred(first[len]) ? difference : 0;
}
while (len) {
// The number of repetitions here doesn't affect the result. We manually unroll the loop
// twice, to guarantee we have at least two iterations without branches (for the case
// where the size is not known at compile time
first += pred(first[len>>=1u]) ? len : 0;
first += pred(first[len>>=1u]) ? len : 0;
}
first += pred(*first);
return first;
}
template <class To, class From>
typename std::enable_if_t<
(sizeof(To) == sizeof(From)) &&
std::is_trivially_copyable<From>::value,
To>
// constexpr support needs compiler magic
bit_cast(const From &src) noexcept {
return reinterpret_cast<const To&>(src);
}
} // namespace utils
#endif // TNT_UTILS_ALGORITHM_H
/*
* Copyright (C) 2017 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_ALGORITHM_H
#define TNT_UTILS_ALGORITHM_H
#include <utils/compiler.h>
#include <functional> // for std::less
#include <type_traits> // for std::enable_if
#include <limits.h>
#include <stdint.h>
namespace utils {
namespace details {
template<typename T>
constexpr inline T popcount(T v) noexcept {
static_assert(sizeof(T) * CHAR_BIT <= 128, "details::popcount() only support up to 128 bits");
constexpr T ONES = ~T(0);
v = v - ((v >> 1u) & ONES / 3);
v = (v & ONES / 15 * 3) + ((v >> 2u) & ONES / 15 * 3);
v = (v + (v >> 4u)) & ONES / 255 * 15;
return (T) (v * (ONES / 255)) >> (sizeof(T) - 1) * CHAR_BIT;
}
template<typename T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
constexpr inline T clz(T x) noexcept {
static_assert(sizeof(T) * CHAR_BIT <= 128, "details::clz() only support up to 128 bits");
x |= (x >> 1u);
x |= (x >> 2u);
x |= (x >> 4u);
x |= (x >> 8u);
x |= (x >> 16u);
if (sizeof(T) * CHAR_BIT >= 64) { // just to silence compiler warning
x |= (x >> 32u);
}
if (sizeof(T) * CHAR_BIT >= 128) { // just to silence compiler warning
x |= (x >> 64u);
}
return T(sizeof(T) * CHAR_BIT) - details::popcount(x);
}
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 (x) c--;
if (sizeof(T) * CHAR_BIT >= 64) {
if (x & T(0x00000000FFFFFFFF)) c -= 32;
}
if (x & T(0x0000FFFF0000FFFF)) c -= 16;
if (x & T(0x00FF00FF00FF00FF)) c -= 8;
if (x & T(0x0F0F0F0F0F0F0F0F)) c -= 4;
if (x & T(0x3333333333333333)) c -= 2;
if (x & T(0x5555555555555555)) c -= 1;
return c;
}
} // namespace details
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned int UTILS_ALWAYS_INLINE clz(unsigned int x) noexcept {
#if __has_builtin(__builtin_clz)
return __builtin_clz(x);
#else
return details::clz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long UTILS_ALWAYS_INLINE clz(unsigned long x) noexcept {
#if __has_builtin(__builtin_clzl)
return __builtin_clzl(x);
#else
return details::clz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long long UTILS_ALWAYS_INLINE clz(unsigned long long x) noexcept {
#if __has_builtin(__builtin_clzll)
return __builtin_clzll(x);
#else
return details::clz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned int UTILS_ALWAYS_INLINE ctz(unsigned int x) noexcept {
#if __has_builtin(__builtin_ctz)
return __builtin_ctz(x);
#else
return details::ctz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long UTILS_ALWAYS_INLINE ctz(unsigned long x) noexcept {
#if __has_builtin(__builtin_ctzl)
return __builtin_ctzl(x);
#else
return details::ctz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long long UTILS_ALWAYS_INLINE ctz(unsigned long long x) noexcept {
#if __has_builtin(__builtin_ctzll)
return __builtin_ctzll(x);
#else
return details::ctz(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned int UTILS_ALWAYS_INLINE popcount(unsigned int x) noexcept {
#if __has_builtin(__builtin_popcount)
return __builtin_popcount(x);
#else
return details::popcount(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long UTILS_ALWAYS_INLINE popcount(unsigned long x) noexcept {
#if __has_builtin(__builtin_popcountl)
return __builtin_popcountl(x);
#else
return details::popcount(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
unsigned long long UTILS_ALWAYS_INLINE popcount(unsigned long long x) noexcept {
#if __has_builtin(__builtin_popcountll)
return __builtin_popcountll(x);
#else
return details::popcount(x);
#endif
}
constexpr inline UTILS_PUBLIC UTILS_PURE
uint8_t UTILS_ALWAYS_INLINE popcount(uint8_t x) noexcept {
return (uint8_t)popcount((unsigned int)x);
}
template<typename T,
typename = std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value>>
constexpr inline UTILS_PUBLIC UTILS_PURE
T log2i(T x) noexcept {
return (sizeof(x) * 8 - 1u) - clz(x);
}
/*
* branch-less version of std::lower_bound and std::upper_bound.
* These versions are intended to be fully inlined, which only happens when the size
* of the array is known at compile time. This code also performs better if the
* array is a power-of-two in size.
*
* These code works even if the conditions above are not met, and becomes a less-branches
* algorithm instead of a branch-less one!
*/
template<typename RandomAccessIterator, typename T, typename COMPARE = typename std::less<T>>
inline UTILS_PUBLIC
RandomAccessIterator lower_bound(
RandomAccessIterator first, RandomAccessIterator last, const T& value,
COMPARE comp = std::less<T>(),
bool assume_power_of_two = false) {
size_t len = last - first;
if (!assume_power_of_two) {
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
// and gets optimized out if the size is known at compile time.
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
size_t difference = (last - first) - len;
// If len was already a POT, then difference will be 0.
// We need to explicitly check this case to avoid dereferencing past the end of the array
first += !difference || comp(first[len], value) ? difference : 0;
}
while (len) {
// The number of repetitions here doesn't affect the result. We manually unroll the loop
// twice, to guarantee we have at least two iterations without branches (for the case
// where the size is not known at compile time
first += comp(first[len >>= 1u], value) ? len : 0;
first += comp(first[len >>= 1u], value) ? len : 0;
}
first += comp(*first, value);
return first;
}
template<typename RandomAccessIterator, typename T, typename COMPARE = typename std::less<T>>
inline UTILS_PUBLIC
RandomAccessIterator upper_bound(
RandomAccessIterator first, RandomAccessIterator last,
const T& value, COMPARE comp = std::less<T>(),
bool assume_power_of_two = false) {
size_t len = last - first;
if (!assume_power_of_two) {
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
// and gets optimized out if the size is known at compile time.
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
size_t difference = (last - first) - len;
// If len was already a POT, then difference will be 0.
// We need to explicitly check this case to avoid dereferencing past the end of the array
first += !difference || comp(value, first[len]) ? 0 : difference;
}
while (len) {
// The number of repetitions here doesn't affect the result. We manually unroll the loop
// twice, to guarantee we have at least two iterations without branches (for the case
// where the size is not known at compile time
first += !comp(value, first[len >>= 1u]) ? len : 0;
first += !comp(value, first[len >>= 1u]) ? len : 0;
}
first += !comp(value, *first);
return first;
}
template<typename RandomAccessIterator, typename COMPARE>
inline UTILS_PUBLIC
RandomAccessIterator partition_point(
RandomAccessIterator first, RandomAccessIterator last, COMPARE pred,
bool assume_power_of_two = false) {
size_t len = last - first;
if (!assume_power_of_two) {
// handle non power-of-two sized arrays. If it's POT, the next line is a no-op
// and gets optimized out if the size is known at compile time.
len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2
size_t difference = (last - first) - len;
first += !difference || pred(first[len]) ? difference : 0;
}
while (len) {
// The number of repetitions here doesn't affect the result. We manually unroll the loop
// twice, to guarantee we have at least two iterations without branches (for the case
// where the size is not known at compile time
first += pred(first[len>>=1u]) ? len : 0;
first += pred(first[len>>=1u]) ? len : 0;
}
first += pred(*first);
return first;
}
template <class To, class From>
typename std::enable_if_t<
(sizeof(To) == sizeof(From)) &&
std::is_trivially_copyable<From>::value,
To>
// constexpr support needs compiler magic
bit_cast(const From &src) noexcept {
return reinterpret_cast<const To&>(src);
}
} // namespace utils
#endif // TNT_UTILS_ALGORITHM_H

View File

@@ -0,0 +1,60 @@
/*
* 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_ANDROID_THERMALMANAGER_H
#define TNT_UTILS_ANDROID_THERMALMANAGER_H
#include <utils/compiler.h>
#include <stdint.h>
struct AThermalManager;
namespace utils {
class ThermalManager {
public:
enum class ThermalStatus : int8_t {
ERROR = -1,
NONE,
LIGHT,
MODERATE,
SEVERE,
CRITICAL,
EMERGENCY,
SHUTDOWN
};
ThermalManager();
~ThermalManager();
// Movable
ThermalManager(ThermalManager&& rhs) noexcept;
ThermalManager& operator=(ThermalManager&& rhs) noexcept;
// not copiable
ThermalManager(ThermalManager const& rhs) = delete;
ThermalManager& operator=(ThermalManager const& rhs) = delete;
ThermalStatus getCurrentThermalStatus() const noexcept;
private:
AThermalManager* mThermalManager = nullptr;
};
} // namespace utils
#endif // TNT_UTILS_ANDROID_THERMALMANAGER_H

View File

@@ -1,34 +1,34 @@
/*
* Copyright (C) 2019 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_API_H
#define TNT_UTILS_API_H
#include <utils/compiler.h>
namespace utils {
/**
* Returns this platform's API level. On Android this function will return
* the API level as defined by the SDK API level version. If a platform does
* not have an API level, this function returns 0.
*/
UTILS_PUBLIC
int api_level();
} // namespace utils
#endif // TNT_UTILS_ARCHITECTURE_H
/*
* Copyright (C) 2019 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_APILEVEL_H
#define TNT_UTILS_APILEVEL_H
#include <utils/compiler.h>
namespace utils {
/**
* Returns this platform's API level. On Android this function will return
* the API level as defined by the SDK API level version. If a platform does
* not have an API level, this function returns 0.
*/
UTILS_PUBLIC
int api_level();
} // namespace utils
#endif // TNT_UTILS_APILEVEL_H

View File

@@ -1,28 +1,28 @@
/*
* Copyright (C) 2016 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_ARCHITECTURE_H
#define TNT_UTILS_ARCHITECTURE_H
#include <stddef.h>
namespace utils {
constexpr size_t CACHELINE_SIZE = 64;
} // namespace utils
#endif // TNT_UTILS_ARCHITECTURE_H
/*
* Copyright (C) 2016 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_ARCHITECTURE_H
#define TNT_UTILS_ARCHITECTURE_H
#include <stddef.h>
namespace utils {
constexpr size_t CACHELINE_SIZE = 64;
} // namespace utils
#endif // TNT_UTILS_ARCHITECTURE_H

View File

@@ -1,28 +1,28 @@
/*
* Copyright (C) 2016 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_ASHMEM_H
#define TNT_UTILS_ASHMEM_H
#include <stddef.h>
namespace utils {
int ashmem_create_region(const char *name, size_t size);
} // namespace utils
#endif /* TNT_UTILS_ASHMEM_H */
/*
* Copyright (C) 2016 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_ASHMEM_H
#define TNT_UTILS_ASHMEM_H
#include <stddef.h>
namespace utils {
int ashmem_create_region(const char *name, size_t size);
} // namespace utils
#endif // TNT_UTILS_ASHMEM_H

View File

@@ -1,316 +1,324 @@
/*
* Copyright (C) 2015 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_BITSET_H
#define TNT_UTILS_BITSET_H
#include <utils/algorithm.h>
#include <utils/compiler.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <type_traits>
#if defined(__ARM_NEON)
# if defined(__ARM_ACLE) && defined(__aarch64__)
# include <arm_neon.h>
# define TNT_UTILS_BITSET_USE_NEON 1
# endif
#endif
namespace utils {
/*
* This bitset<> class is different from std::bitset<> in that it allows us to control
* the exact storage size. This is useful for small bitset (e.g. < 64, on 64-bits machines).
* It also allows for lexicographical compares (i.e. sorting).
*/
template<typename T, size_t N = 1,
typename = typename std::enable_if<std::is_integral<T>::value &&
std::is_unsigned<T>::value>::type>
class UTILS_PUBLIC bitset {
T storage[N];
public:
static constexpr T BITS_PER_WORD = sizeof(T) * 8;
static constexpr T BIT_COUNT = BITS_PER_WORD * N;
static constexpr T WORLD_COUNT = N;
using container_type = T;
bitset() noexcept {
std::fill(std::begin(storage), std::end(storage), 0);
}
T getBitsAt(size_t n) const noexcept {
return storage[n];
}
T& getBitsAt(size_t n) noexcept {
return storage[n];
}
T getValue() const noexcept {
static_assert(N == 1, "bitfield must only have one storage word");
return storage[0];
}
void setValue(T value) noexcept {
static_assert(N == 1, "bitfield must only have one storage word");
storage[0] = value;
}
template<typename F>
void forEachSetBit(F exec) const noexcept {
for (size_t i = 0; i < N; i++) {
T v = storage[i];
while (v) {
T k = utils::ctz(v);
v &= ~(T(1) << k);
exec(size_t(k + BITS_PER_WORD * i));
}
}
}
size_t size() const noexcept { return N * BITS_PER_WORD; }
bool test(size_t bit) const noexcept { return operator[](bit); }
void set(size_t b) noexcept {
storage[b / BITS_PER_WORD] |= T(1) << (b % BITS_PER_WORD);
}
void set(size_t b, bool value) noexcept {
storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD));
storage[b / BITS_PER_WORD] |= T(value) << (b % BITS_PER_WORD);
}
void unset(size_t b) noexcept {
storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD));
}
void flip(size_t b) noexcept {
storage[b / BITS_PER_WORD] ^= T(1) << (b % BITS_PER_WORD);
}
void reset() noexcept {
std::fill(std::begin(storage), std::end(storage), 0);
}
bool operator[](size_t b) const noexcept {
return bool(storage[b / BITS_PER_WORD] & (T(1) << (b % BITS_PER_WORD)));
}
size_t count() const noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0 && BIT_COUNT / 128 < 31) {
// Use NEON for bitset multiple of 128 bits.
// The intermediate computation can't handle more than 31*128 bits because
// intermediate counts must be 8 bits.
uint8x16_t const* const p = (uint8x16_t const*) storage;
uint8x16_t counts = vcntq_u8(p[0]);
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
counts += vcntq_u8(p[i]);
}
return vaddlvq_u8(counts);
} else
#endif
{
T r = utils::popcount(storage[0]);
for (size_t i = 1; i < N; ++i) {
r += utils::popcount(storage[i]);
}
return r;
}
}
bool any() const noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint64x2_t const* const p = (uint64x2_t const*) storage;
uint64x2_t r = p[0];
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
r |= p[i];
}
return bool(r[0] | r[1]);
} else
#endif
{
T r = storage[0];
for (size_t i = 1; i < N; ++i) {
r |= storage[i];
}
return bool(r);
}
}
bool none() const noexcept {
return !any();
}
bool all() const noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint64x2_t const* const p = (uint64x2_t const*) storage;
uint64x2_t r = p[0];
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
r &= p[i];
}
return T(~(r[0] & r[1])) == T(0);
} else
#endif
{
T r = storage[0];
for (size_t i = 1; i < N; ++i) {
r &= storage[i];
}
return T(~r) == T(0);
}
}
bool operator!=(const bitset& b) const noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
bitset temp(*this ^ b);
uint64x2_t const* const p = (uint64x2_t const*) temp.storage;
uint64x2_t r = p[0];
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
r |= p[i];
}
return bool(r[0] | r[1]);
} else
#endif
{
T r = storage[0] ^ b.storage[0];
for (size_t i = 1; i < N; ++i) {
r |= storage[i] ^ b.storage[i];
}
return bool(r);
}
}
bool operator==(const bitset& b) const noexcept {
return !operator!=(b);
}
bitset& operator&=(const bitset& b) noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint8x16_t* const p = (uint8x16_t*) storage;
uint8x16_t const* const q = (uint8x16_t const*) b.storage;
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
p[i] &= q[i];
}
} else
#endif
{
for (size_t i = 0; i < N; ++i) {
storage[i] &= b.storage[i];
}
}
return *this;
}
bitset& operator|=(const bitset& b) noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint8x16_t* const p = (uint8x16_t*) storage;
uint8x16_t const* const q = (uint8x16_t const*) b.storage;
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
p[i] |= q[i];
}
} else
#endif
{
for (size_t i = 0; i < N; ++i) {
storage[i] |= b.storage[i];
}
}
return *this;
}
bitset& operator^=(const bitset& b) noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint8x16_t* const p = (uint8x16_t*) storage;
uint8x16_t const* const q = (uint8x16_t const*) b.storage;
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
p[i] ^= q[i];
}
} else
#endif
{
for (size_t i = 0; i < N; ++i) {
storage[i] ^= b.storage[i];
}
}
return *this;
}
bitset operator~() const noexcept {
bitset r;
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint8x16_t* const p = (uint8x16_t*) r.storage;
uint8x16_t const* const q = (uint8x16_t const*) storage;
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
p[i] = ~q[i];
}
} else
#endif
{
for (size_t i = 0; i < N; ++i) {
r.storage[i] = ~storage[i];
}
}
return r;
}
private:
friend bool operator<(bitset const& lhs, bitset const& rhs) noexcept {
return std::lexicographical_compare(
std::begin(lhs.storage), std::end(lhs.storage),
std::begin(rhs.storage), std::end(rhs.storage)
);
}
friend bitset operator&(const bitset& lhs, const bitset& rhs) noexcept {
return bitset(lhs) &= rhs;
}
friend bitset operator|(const bitset& lhs, const bitset& rhs) noexcept {
return bitset(lhs) |= rhs;
}
friend bitset operator^(const bitset& lhs, const bitset& rhs) noexcept {
return bitset(lhs) ^= rhs;
}
};
using bitset8 = bitset<uint8_t>;
using bitset32 = bitset<uint32_t>;
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!");
} // namespace utils
#endif // TNT_UTILS_BITSET_H
/*
* Copyright (C) 2015 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_BITSET_H
#define TNT_UTILS_BITSET_H
#include <utils/algorithm.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <type_traits>
#if defined(__ARM_NEON)
# if defined(__ARM_ACLE) && defined(__aarch64__)
# include <arm_neon.h>
# define TNT_UTILS_BITSET_USE_NEON 1
# endif
#endif
namespace utils {
/*
* This bitset<> class is different from std::bitset<> in that it allows us to control
* the exact storage size. This is useful for small bitset (e.g. < 64, on 64-bits machines).
* It also allows for lexicographical compares (i.e. sorting).
*/
template<typename T, size_t N = 1,
typename = typename std::enable_if<std::is_integral<T>::value &&
std::is_unsigned<T>::value>::type>
class UTILS_PUBLIC bitset {
T storage[N];
public:
static constexpr T BITS_PER_WORD = sizeof(T) * 8;
static constexpr T BIT_COUNT = BITS_PER_WORD * N;
static constexpr T WORLD_COUNT = N;
using container_type = T;
bitset() noexcept {
std::fill(std::begin(storage), std::end(storage), 0);
}
T getBitsAt(size_t n) const noexcept {
assert_invariant(n<N);
return storage[n];
}
T& getBitsAt(size_t n) noexcept {
assert_invariant(n<N);
return storage[n];
}
T getValue() const noexcept {
static_assert(N == 1, "bitfield must only have one storage word");
return storage[0];
}
void setValue(T value) noexcept {
static_assert(N == 1, "bitfield must only have one storage word");
storage[0] = value;
}
template<typename F>
void forEachSetBit(F exec) const noexcept {
for (size_t i = 0; i < N; i++) {
T v = storage[i];
while (v) {
T k = utils::ctz(v);
v &= ~(T(1) << k);
exec(size_t(k + BITS_PER_WORD * i));
}
}
}
size_t size() const noexcept { return N * BITS_PER_WORD; }
bool test(size_t bit) const noexcept { return operator[](bit); }
void set(size_t b) noexcept {
assert_invariant(b / BITS_PER_WORD < N);
storage[b / BITS_PER_WORD] |= T(1) << (b % BITS_PER_WORD);
}
void set(size_t b, bool value) noexcept {
assert_invariant(b / BITS_PER_WORD < N);
storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD));
storage[b / BITS_PER_WORD] |= T(value) << (b % BITS_PER_WORD);
}
void unset(size_t b) noexcept {
assert_invariant(b / BITS_PER_WORD < N);
storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD));
}
void flip(size_t b) noexcept {
assert_invariant(b / BITS_PER_WORD < N);
storage[b / BITS_PER_WORD] ^= T(1) << (b % BITS_PER_WORD);
}
void reset() noexcept {
std::fill(std::begin(storage), std::end(storage), 0);
}
bool operator[](size_t b) const noexcept {
assert_invariant(b / BITS_PER_WORD < N);
return bool(storage[b / BITS_PER_WORD] & (T(1) << (b % BITS_PER_WORD)));
}
size_t count() const noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0 && BIT_COUNT / 128 < 31) {
// Use NEON for bitset multiple of 128 bits.
// The intermediate computation can't handle more than 31*128 bits because
// intermediate counts must be 8 bits.
uint8x16_t const* const p = (uint8x16_t const*) storage;
uint8x16_t counts = vcntq_u8(p[0]);
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
counts += vcntq_u8(p[i]);
}
return vaddlvq_u8(counts);
} else
#endif
{
T r = utils::popcount(storage[0]);
for (size_t i = 1; i < N; ++i) {
r += utils::popcount(storage[i]);
}
return r;
}
}
bool any() const noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint64x2_t const* const p = (uint64x2_t const*) storage;
uint64x2_t r = p[0];
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
r |= p[i];
}
return bool(r[0] | r[1]);
} else
#endif
{
T r = storage[0];
for (size_t i = 1; i < N; ++i) {
r |= storage[i];
}
return bool(r);
}
}
bool none() const noexcept {
return !any();
}
bool all() const noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint64x2_t const* const p = (uint64x2_t const*) storage;
uint64x2_t r = p[0];
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
r &= p[i];
}
return T(~(r[0] & r[1])) == T(0);
} else
#endif
{
T r = storage[0];
for (size_t i = 1; i < N; ++i) {
r &= storage[i];
}
return T(~r) == T(0);
}
}
bool operator!=(const bitset& b) const noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
bitset temp(*this ^ b);
uint64x2_t const* const p = (uint64x2_t const*) temp.storage;
uint64x2_t r = p[0];
for (size_t i = 1; i < BIT_COUNT / 128; ++i) {
r |= p[i];
}
return bool(r[0] | r[1]);
} else
#endif
{
T r = storage[0] ^ b.storage[0];
for (size_t i = 1; i < N; ++i) {
r |= storage[i] ^ b.storage[i];
}
return bool(r);
}
}
bool operator==(const bitset& b) const noexcept {
return !operator!=(b);
}
bitset& operator&=(const bitset& b) noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint8x16_t* const p = (uint8x16_t*) storage;
uint8x16_t const* const q = (uint8x16_t const*) b.storage;
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
p[i] &= q[i];
}
} else
#endif
{
for (size_t i = 0; i < N; ++i) {
storage[i] &= b.storage[i];
}
}
return *this;
}
bitset& operator|=(const bitset& b) noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint8x16_t* const p = (uint8x16_t*) storage;
uint8x16_t const* const q = (uint8x16_t const*) b.storage;
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
p[i] |= q[i];
}
} else
#endif
{
for (size_t i = 0; i < N; ++i) {
storage[i] |= b.storage[i];
}
}
return *this;
}
bitset& operator^=(const bitset& b) noexcept {
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint8x16_t* const p = (uint8x16_t*) storage;
uint8x16_t const* const q = (uint8x16_t const*) b.storage;
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
p[i] ^= q[i];
}
} else
#endif
{
for (size_t i = 0; i < N; ++i) {
storage[i] ^= b.storage[i];
}
}
return *this;
}
bitset operator~() const noexcept {
bitset r;
#if defined(TNT_UTILS_BITSET_USE_NEON)
if (BIT_COUNT % 128 == 0) {
uint8x16_t* const p = (uint8x16_t*) r.storage;
uint8x16_t const* const q = (uint8x16_t const*) storage;
for (size_t i = 0; i < BIT_COUNT / 128; ++i) {
p[i] = ~q[i];
}
} else
#endif
{
for (size_t i = 0; i < N; ++i) {
r.storage[i] = ~storage[i];
}
}
return r;
}
private:
friend bool operator<(bitset const& lhs, bitset const& rhs) noexcept {
return std::lexicographical_compare(
std::begin(lhs.storage), std::end(lhs.storage),
std::begin(rhs.storage), std::end(rhs.storage)
);
}
friend bitset operator&(const bitset& lhs, const bitset& rhs) noexcept {
return bitset(lhs) &= rhs;
}
friend bitset operator|(const bitset& lhs, const bitset& rhs) noexcept {
return bitset(lhs) |= rhs;
}
friend bitset operator^(const bitset& lhs, const bitset& rhs) noexcept {
return bitset(lhs) ^= rhs;
}
};
using bitset8 = bitset<uint8_t>;
using bitset32 = bitset<uint32_t>;
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!");
} // namespace utils
#endif // TNT_UTILS_BITSET_H

View File

@@ -1,226 +1,228 @@
/*
* Copyright (C) 2015 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_COMPILER_H
#define TNT_UTILS_COMPILER_H
// compatibility with non-clang compilers...
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if __has_attribute(visibility)
# define UTILS_PUBLIC __attribute__((visibility("default")))
#else
# define UTILS_PUBLIC
#endif
#if __has_attribute(deprecated)
# define UTILS_DEPRECATED [[deprecated]]
#else
# define UTILS_DEPRECATED
#endif
#if __has_attribute(packed)
# define UTILS_PACKED __attribute__((packed))
#else
# define UTILS_PACKED
#endif
#if __has_attribute(noreturn)
# define UTILS_NORETURN __attribute__((noreturn))
#else
# define UTILS_NORETURN
#endif
#if __has_attribute(visibility)
# ifndef TNT_DEV
# define UTILS_PRIVATE __attribute__((visibility("hidden")))
# else
# define UTILS_PRIVATE
# endif
#else
# define UTILS_PRIVATE
#endif
#define UTILS_NO_SANITIZE_THREAD
#if defined(__has_feature)
# if __has_feature(thread_sanitizer)
# undef UTILS_NO_SANITIZE_THREAD
# define UTILS_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
# endif
#endif
#define UTILS_HAS_SANITIZE_MEMORY 0
#if defined(__has_feature)
# if __has_feature(memory_sanitizer)
# undef UTILS_HAS_SANITIZE_MEMORY
# define UTILS_HAS_SANITIZE_MEMORY 1
# endif
#endif
/*
* helps the compiler's optimizer predicting branches
*/
#if __has_builtin(__builtin_expect)
# ifdef __cplusplus
# define UTILS_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
# define UTILS_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
# else
# define UTILS_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
# define UTILS_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
# endif
#else
# define UTILS_LIKELY( exp ) (!!(exp))
# define UTILS_UNLIKELY( exp ) (!!(exp))
#endif
#if __has_builtin(__builtin_prefetch)
# define UTILS_PREFETCH( exp ) (__builtin_prefetch(exp))
#else
# define UTILS_PREFETCH( exp )
#endif
#if __has_builtin(__builtin_assume)
# define UTILS_ASSUME( exp ) (__builtin_assume(exp))
#else
# define UTILS_ASSUME( exp )
#endif
#if (defined(__i386__) || defined(__x86_64__))
# define UTILS_HAS_HYPER_THREADING 1 // on x86 we assume we have hyper-threading.
#else
# define UTILS_HAS_HYPER_THREADING 0
#endif
#if defined(__EMSCRIPTEN__) || defined(FILAMENT_SINGLE_THREADED)
# define UTILS_HAS_THREADING 0
#else
# define UTILS_HAS_THREADING 1
#endif
#if __has_attribute(noinline)
#define UTILS_NOINLINE __attribute__((noinline))
#else
#define UTILS_NOINLINE
#endif
#if __has_attribute(always_inline)
#define UTILS_ALWAYS_INLINE __attribute__((always_inline))
#else
#define UTILS_ALWAYS_INLINE
#endif
#if __has_attribute(pure)
#define UTILS_PURE __attribute__((pure))
#else
#define UTILS_PURE
#endif
#if __has_attribute(maybe_unused)
#define UTILS_UNUSED [[maybe_unused]]
#define UTILS_UNUSED_IN_RELEASE [[maybe_unused]]
#elif __has_attribute(unused)
#define UTILS_UNUSED __attribute__((unused))
#define UTILS_UNUSED_IN_RELEASE __attribute__((unused))
#else
#define UTILS_UNUSED
#define UTILS_UNUSED_IN_RELEASE
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1900
# define UTILS_RESTRICT __restrict
#elif (defined(__clang__) || defined(__GNUC__))
# define UTILS_RESTRICT __restrict__
#else
# define UTILS_RESTRICT
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1900
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 1
#elif __has_feature(cxx_thread_local)
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 1
#else
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 0
#endif
#if __has_feature(cxx_rtti) || defined(_CPPRTTI)
# define UTILS_HAS_RTTI 1
#else
# define UTILS_HAS_RTTI 0
#endif
#ifdef __ARM_ACLE
# include <arm_acle.h>
# define UTILS_WAIT_FOR_INTERRUPT() __wfi()
# define UTILS_WAIT_FOR_EVENT() __wfe()
# define UTILS_BROADCAST_EVENT() __sev()
# define UTILS_SIGNAL_EVENT() __sevl()
# define UTILS_PAUSE() __yield()
# define UTILS_PREFETCHW(addr) __pldx(1, 0, 0, addr)
#else // !__ARM_ACLE
# if (defined(__i386__) || defined(__x86_64__))
# define UTILS_X86_PAUSE {__asm__ __volatile__( "rep; nop" : : : "memory" );}
# define UTILS_WAIT_FOR_INTERRUPT() UTILS_X86_PAUSE
# define UTILS_WAIT_FOR_EVENT() UTILS_X86_PAUSE
# define UTILS_BROADCAST_EVENT()
# define UTILS_SIGNAL_EVENT()
# define UTILS_PAUSE() UTILS_X86_PAUSE
# define UTILS_PREFETCHW(addr) UTILS_PREFETCH(addr)
# else // !x86
# define UTILS_WAIT_FOR_INTERRUPT()
# define UTILS_WAIT_FOR_EVENT()
# define UTILS_BROADCAST_EVENT()
# define UTILS_SIGNAL_EVENT()
# define UTILS_PAUSE()
# define UTILS_PREFETCHW(addr) UTILS_PREFETCH(addr)
# endif // x86
#endif // __ARM_ACLE
// ssize_t is a POSIX type.
#if defined(WIN32) || defined(_WIN32)
#include <Basetsd.h>
typedef SSIZE_T ssize_t;
#endif
#ifdef _MSC_VER
# define UTILS_EMPTY_BASES __declspec(empty_bases)
#else
# define UTILS_EMPTY_BASES
#endif
#if defined(WIN32) || defined(_WIN32)
#define IMPORTSYMB __declspec(dllimport)
#else
#define IMPORTSYMB
#endif
#if defined(_MSC_VER) && !defined(__PRETTY_FUNCTION__)
# define __PRETTY_FUNCTION__ __FUNCSIG__
#endif
#endif // TNT_UTILS_COMPILER_H
/*
* Copyright (C) 2015 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_COMPILER_H
#define TNT_UTILS_COMPILER_H
// compatibility with non-clang compilers...
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if __has_attribute(visibility)
# define UTILS_PUBLIC __attribute__((visibility("default")))
#else
# define UTILS_PUBLIC
#endif
#if __has_attribute(deprecated)
# define UTILS_DEPRECATED [[deprecated]]
#else
# define UTILS_DEPRECATED
#endif
#if __has_attribute(packed)
# define UTILS_PACKED __attribute__((packed))
#else
# define UTILS_PACKED
#endif
#if __has_attribute(noreturn)
# define UTILS_NORETURN __attribute__((noreturn))
#else
# define UTILS_NORETURN
#endif
#if __has_attribute(visibility)
# ifndef TNT_DEV
# define UTILS_PRIVATE __attribute__((visibility("hidden")))
# else
# define UTILS_PRIVATE
# endif
#else
# define UTILS_PRIVATE
#endif
#define UTILS_NO_SANITIZE_THREAD
#if __has_feature(thread_sanitizer)
#undef UTILS_NO_SANITIZE_THREAD
#define UTILS_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread")))
#endif
#define UTILS_HAS_SANITIZE_THREAD 0
#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)
#undef UTILS_HAS_SANITIZE_THREAD
#define UTILS_HAS_SANITIZE_THREAD 1
#endif
#define UTILS_HAS_SANITIZE_MEMORY 0
#if __has_feature(memory_sanitizer)
#undef UTILS_HAS_SANITIZE_MEMORY
#define UTILS_HAS_SANITIZE_MEMORY 1
#endif
/*
* helps the compiler's optimizer predicting branches
*/
#if __has_builtin(__builtin_expect)
# ifdef __cplusplus
# define UTILS_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
# define UTILS_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
# else
# define UTILS_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
# define UTILS_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
# endif
#else
# define UTILS_LIKELY( exp ) (!!(exp))
# define UTILS_UNLIKELY( exp ) (!!(exp))
#endif
#if __has_builtin(__builtin_prefetch)
# define UTILS_PREFETCH( exp ) (__builtin_prefetch(exp))
#else
# define UTILS_PREFETCH( exp )
#endif
#if __has_builtin(__builtin_assume)
# define UTILS_ASSUME( exp ) (__builtin_assume(exp))
#else
# define UTILS_ASSUME( exp )
#endif
#if (defined(__i386__) || defined(__x86_64__))
# define UTILS_HAS_HYPER_THREADING 1 // on x86 we assume we have hyper-threading.
#else
# define UTILS_HAS_HYPER_THREADING 0
#endif
#if defined(__EMSCRIPTEN__) || defined(FILAMENT_SINGLE_THREADED)
# define UTILS_HAS_THREADING 0
#else
# define UTILS_HAS_THREADING 1
#endif
#if __has_attribute(noinline)
#define UTILS_NOINLINE __attribute__((noinline))
#else
#define UTILS_NOINLINE
#endif
#if __has_attribute(always_inline)
#define UTILS_ALWAYS_INLINE __attribute__((always_inline))
#else
#define UTILS_ALWAYS_INLINE
#endif
#if __has_attribute(pure)
#define UTILS_PURE __attribute__((pure))
#else
#define UTILS_PURE
#endif
#if __has_attribute(maybe_unused)
#define UTILS_UNUSED [[maybe_unused]]
#define UTILS_UNUSED_IN_RELEASE [[maybe_unused]]
#elif __has_attribute(unused)
#define UTILS_UNUSED __attribute__((unused))
#define UTILS_UNUSED_IN_RELEASE __attribute__((unused))
#else
#define UTILS_UNUSED
#define UTILS_UNUSED_IN_RELEASE
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1900
# define UTILS_RESTRICT __restrict
#elif (defined(__clang__) || defined(__GNUC__))
# define UTILS_RESTRICT __restrict__
#else
# define UTILS_RESTRICT
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1900
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 1
#elif __has_feature(cxx_thread_local)
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 1
#else
# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 0
#endif
#if __has_feature(cxx_rtti) || defined(_CPPRTTI)
# define UTILS_HAS_RTTI 1
#else
# define UTILS_HAS_RTTI 0
#endif
#ifdef __ARM_ACLE
# include <arm_acle.h>
# define UTILS_WAIT_FOR_INTERRUPT() __wfi()
# define UTILS_WAIT_FOR_EVENT() __wfe()
# define UTILS_BROADCAST_EVENT() __sev()
# define UTILS_SIGNAL_EVENT() __sevl()
# define UTILS_PAUSE() __yield()
# define UTILS_PREFETCHW(addr) __pldx(1, 0, 0, addr)
#else // !__ARM_ACLE
# if (defined(__i386__) || defined(__x86_64__))
# define UTILS_X86_PAUSE {__asm__ __volatile__( "rep; nop" : : : "memory" );}
# define UTILS_WAIT_FOR_INTERRUPT() UTILS_X86_PAUSE
# define UTILS_WAIT_FOR_EVENT() UTILS_X86_PAUSE
# define UTILS_BROADCAST_EVENT()
# define UTILS_SIGNAL_EVENT()
# define UTILS_PAUSE() UTILS_X86_PAUSE
# define UTILS_PREFETCHW(addr) UTILS_PREFETCH(addr)
# else // !x86
# define UTILS_WAIT_FOR_INTERRUPT()
# define UTILS_WAIT_FOR_EVENT()
# define UTILS_BROADCAST_EVENT()
# define UTILS_SIGNAL_EVENT()
# define UTILS_PAUSE()
# define UTILS_PREFETCHW(addr) UTILS_PREFETCH(addr)
# endif // x86
#endif // __ARM_ACLE
// ssize_t is a POSIX type.
#if defined(WIN32) || defined(_WIN32)
#include <Basetsd.h>
typedef SSIZE_T ssize_t;
#endif
#ifdef _MSC_VER
# define UTILS_EMPTY_BASES __declspec(empty_bases)
#else
# define UTILS_EMPTY_BASES
#endif
#if defined(WIN32) || defined(_WIN32)
#define IMPORTSYMB __declspec(dllimport)
#else
#define IMPORTSYMB
#endif
#if defined(_MSC_VER) && !defined(__PRETTY_FUNCTION__)
# define __PRETTY_FUNCTION__ __FUNCSIG__
#endif
#endif // TNT_UTILS_COMPILER_H

View File

@@ -1,68 +1,68 @@
/*
* Copyright (C) 2021 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_COMPRESSED_PAIR_H
#define TNT_UTILS_COMPRESSED_PAIR_H
#include <type_traits>
#include <utility>
namespace utils {
template<typename T, bool>
struct dependent_type : public T {
};
template<typename T1, typename T2,
std::enable_if_t<!std::is_same_v<T1, T2>, bool> = true>
struct compressed_pair : private T1, private T2 {
template<bool Dummy = true, typename = std::enable_if_t<
dependent_type<std::is_default_constructible<T1>, Dummy>::value &&
dependent_type<std::is_default_constructible<T2>, Dummy>::value>>
compressed_pair() : T1(), T2() {}
template<typename U1, typename U2>
compressed_pair(U1&& other1, U2&& other2)
: T1(std::forward<U1>(other1)),
T2(std::forward<U2>(other2)) {}
T1& first() noexcept {
return static_cast<T1&>(*this);
}
T2& second() noexcept {
return static_cast<T2&>(*this);
}
T1 const& first() const noexcept {
return static_cast<T1 const&>(*this);
}
T2 const& second() const noexcept {
return static_cast<T2 const&>(*this);
}
void swap(compressed_pair& other) noexcept {
using std::swap;
swap(first(), other.first());
swap(second(), other.second());
}
};
} // namespace utils
#endif //TNT_UTILS_COMPRESSED_PAIR_H
/*
* Copyright (C) 2021 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_COMPRESSED_PAIR_H
#define TNT_UTILS_COMPRESSED_PAIR_H
#include <type_traits>
#include <utility>
namespace utils {
template<typename T, bool>
struct dependent_type : public T {
};
template<typename T1, typename T2,
std::enable_if_t<!std::is_same_v<T1, T2>, bool> = true>
struct compressed_pair : private T1, private T2 {
template<bool Dummy = true, typename = std::enable_if_t<
dependent_type<std::is_default_constructible<T1>, Dummy>::value &&
dependent_type<std::is_default_constructible<T2>, Dummy>::value>>
compressed_pair() : T1(), T2() {}
template<typename U1, typename U2>
compressed_pair(U1&& other1, U2&& other2)
: T1(std::forward<U1>(other1)),
T2(std::forward<U2>(other2)) {}
T1& first() noexcept {
return static_cast<T1&>(*this);
}
T2& second() noexcept {
return static_cast<T2&>(*this);
}
T1 const& first() const noexcept {
return static_cast<T1 const&>(*this);
}
T2 const& second() const noexcept {
return static_cast<T2 const&>(*this);
}
void swap(compressed_pair& other) noexcept {
using std::swap;
swap(first(), other.first());
swap(second(), other.second());
}
};
} // namespace utils
#endif // TNT_UTILS_COMPRESSED_PAIR_H

View File

@@ -1,33 +1,33 @@
/*
* Copyright (C) 2021 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_DEBUG_H
#define TNT_UTILS_DEBUG_H
#include <utils/compiler.h>
namespace utils {
void panic(const char *func, const char * file, int line, const char *assertion) noexcept;
} // namespace filament
#ifdef NDEBUG
# define assert_invariant(e) ((void)0)
#else
# define assert_invariant(e) \
(UTILS_LIKELY(e) ? ((void)0) : utils::panic(__func__, __FILE__, __LINE__, #e))
#endif // NDEBUG
#endif //TNT_UTILS_DEBUG_H
/*
* Copyright (C) 2021 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_DEBUG_H
#define TNT_UTILS_DEBUG_H
#include <utils/compiler.h>
namespace utils {
void panic(const char *func, const char * file, int line, const char *assertion) noexcept;
} // namespace filament
#ifdef NDEBUG
# define assert_invariant(e) ((void)0)
#else
# define assert_invariant(e) \
(UTILS_LIKELY(e) ? ((void)0) : utils::panic(__func__, __FILE__, __LINE__, #e))
#endif // NDEBUG
#endif // TNT_UTILS_DEBUG_H

View File

@@ -1,39 +1,39 @@
/*
* Copyright (C) 2016 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 UTILS_GENERIC_CONDITION_H
#define UTILS_GENERIC_CONDITION_H
#include <condition_variable>
namespace utils {
class Condition : public std::condition_variable {
public:
using std::condition_variable::condition_variable;
inline void notify_n(size_t n) noexcept {
if (n == 1) {
notify_one();
} else if (n > 1) {
notify_all();
}
}
};
} // namespace utils
#endif // UTILS_GENERIC_CONDITION_H
/*
* Copyright (C) 2016 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_GENERIC_CONDITION_H
#define TNT_UTILS_GENERIC_CONDITION_H
#include <condition_variable>
namespace utils {
class Condition : public std::condition_variable {
public:
using std::condition_variable::condition_variable;
inline void notify_n(size_t n) noexcept {
if (n == 1) {
notify_one();
} else if (n > 1) {
notify_all();
}
}
};
} // namespace utils
#endif // TNT_UTILS_GENERIC_CONDITION_H

View File

@@ -1,28 +1,28 @@
/*
* Copyright (C) 2016 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 UTILS_GENERIC_MUTEX_H
#define UTILS_GENERIC_MUTEX_H
#include <mutex>
namespace utils {
using Mutex = std::mutex;
} // namespace utils
#endif // UTILS_GENERIC_MUTEX_H
/*
* Copyright (C) 2016 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_GENERIC_MUTEX_H
#define TNT_UTILS_GENERIC_MUTEX_H
#include <mutex>
namespace utils {
using Mutex = std::mutex;
} // namespace utils
#endif // TNT_UTILS_GENERIC_MUTEX_H

View File

@@ -0,0 +1,56 @@
/*
* 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_GENERIC_THERMALMANAGER_H
#define TNT_UTILS_GENERIC_THERMALMANAGER_H
#include <utils/compiler.h>
#include <stdint.h>
namespace utils {
class ThermalManager {
public:
enum class ThermalStatus : int8_t {
ERROR = -1,
NONE,
LIGHT,
MODERATE,
SEVERE,
CRITICAL,
EMERGENCY,
SHUTDOWN
};
ThermalManager() = default;
// Movable
ThermalManager(ThermalManager&& rhs) noexcept = default;
ThermalManager& operator=(ThermalManager&& rhs) noexcept = default;
// not copiable
ThermalManager(ThermalManager const& rhs) = delete;
ThermalManager& operator=(ThermalManager const& rhs) = delete;
ThermalStatus getCurrentThermalStatus() const noexcept {
return ThermalStatus::NONE;
}
};
} // namespace utils
#endif // TNT_UTILS_GENERIC_THERMALMANAGER_H

View File

@@ -1,123 +1,123 @@
/*
* Copyright (C) 2016 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 UTILS_LINUX_CONDITION_H
#define UTILS_LINUX_CONDITION_H
#include <atomic>
#include <chrono>
#include <condition_variable> // for cv_status
#include <limits>
#include <mutex> // for unique_lock
#include <utils/linux/Mutex.h>
#include <time.h>
namespace utils {
/*
* A very simple condition variable class that can be used as an (almost) drop-in replacement
* for std::condition_variable (doesn't have the timed wait() though).
* It is very low overhead as most of it is inlined.
*/
class Condition {
public:
Condition() noexcept = default;
Condition(const Condition&) = delete;
Condition& operator=(const Condition&) = delete;
void notify_all() noexcept {
pulse(std::numeric_limits<int>::max());
}
void notify_one() noexcept {
pulse(1);
}
void notify_n(size_t n) noexcept {
if (n > 0) pulse(n);
}
void wait(std::unique_lock<Mutex>& lock) noexcept {
wait_until(lock.mutex(), false, nullptr);
}
template <class P>
void wait(std::unique_lock<Mutex>& lock, P predicate) {
while (!predicate()) {
wait(lock);
}
}
template<typename D>
std::cv_status wait_until(std::unique_lock<Mutex>& lock,
const std::chrono::time_point<std::chrono::steady_clock, D>& timeout_time) noexcept {
// convert to nanoseconds
uint64_t ns = std::chrono::duration<uint64_t, std::nano>(timeout_time.time_since_epoch()).count();
using sec_t = decltype(timespec::tv_sec);
using nsec_t = decltype(timespec::tv_nsec);
timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) };
return wait_until(lock.mutex(), false, &ts);
}
template<typename D>
std::cv_status wait_until(std::unique_lock<Mutex>& lock,
const std::chrono::time_point<std::chrono::system_clock, D>& timeout_time) noexcept {
// convert to nanoseconds
uint64_t ns = std::chrono::duration<uint64_t, std::nano>(timeout_time.time_since_epoch()).count();
using sec_t = decltype(timespec::tv_sec);
using nsec_t = decltype(timespec::tv_nsec);
timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) };
return wait_until(lock.mutex(), true, &ts);
}
template<typename C, typename D, typename P>
bool wait_until(std::unique_lock<Mutex>& lock,
const std::chrono::time_point<C, D>& timeout_time, P predicate) noexcept {
while (!predicate()) {
if (wait_until(lock, timeout_time) == std::cv_status::timeout) {
return predicate();
}
}
return true;
}
template<typename R, typename Period>
std::cv_status wait_for(std::unique_lock<Mutex>& lock,
const std::chrono::duration<R, Period>& rel_time) noexcept {
return wait_until(lock, std::chrono::steady_clock::now() + rel_time);
}
template<typename R, typename Period, typename P>
bool wait_for(std::unique_lock<Mutex>& lock,
const std::chrono::duration<R, Period>& rel_time, P pred) noexcept {
return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(pred));
}
private:
std::atomic<uint32_t> mState = { 0 };
void pulse(int threadCount) noexcept;
std::cv_status wait_until(Mutex* lock,
bool realtimeClock, timespec* ts) noexcept;
};
} // namespace utils
#endif // UTILS_LINUX_CONDITION_H
/*
* Copyright (C) 2016 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_LINUX_CONDITION_H
#define TNT_UTILS_LINUX_CONDITION_H
#include <atomic>
#include <chrono>
#include <condition_variable> // for cv_status
#include <limits>
#include <mutex> // for unique_lock
#include <utils/linux/Mutex.h>
#include <time.h>
namespace utils {
/*
* A very simple condition variable class that can be used as an (almost) drop-in replacement
* for std::condition_variable (doesn't have the timed wait() though).
* It is very low overhead as most of it is inlined.
*/
class Condition {
public:
Condition() noexcept = default;
Condition(const Condition&) = delete;
Condition& operator=(const Condition&) = delete;
void notify_all() noexcept {
pulse(std::numeric_limits<int>::max());
}
void notify_one() noexcept {
pulse(1);
}
void notify_n(size_t n) noexcept {
if (n > 0) pulse(n);
}
void wait(std::unique_lock<Mutex>& lock) noexcept {
wait_until(lock.mutex(), false, nullptr);
}
template <class P>
void wait(std::unique_lock<Mutex>& lock, P predicate) {
while (!predicate()) {
wait(lock);
}
}
template<typename D>
std::cv_status wait_until(std::unique_lock<Mutex>& lock,
const std::chrono::time_point<std::chrono::steady_clock, D>& timeout_time) noexcept {
// convert to nanoseconds
uint64_t ns = std::chrono::duration<uint64_t, std::nano>(timeout_time.time_since_epoch()).count();
using sec_t = decltype(timespec::tv_sec);
using nsec_t = decltype(timespec::tv_nsec);
timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) };
return wait_until(lock.mutex(), false, &ts);
}
template<typename D>
std::cv_status wait_until(std::unique_lock<Mutex>& lock,
const std::chrono::time_point<std::chrono::system_clock, D>& timeout_time) noexcept {
// convert to nanoseconds
uint64_t ns = std::chrono::duration<uint64_t, std::nano>(timeout_time.time_since_epoch()).count();
using sec_t = decltype(timespec::tv_sec);
using nsec_t = decltype(timespec::tv_nsec);
timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) };
return wait_until(lock.mutex(), true, &ts);
}
template<typename C, typename D, typename P>
bool wait_until(std::unique_lock<Mutex>& lock,
const std::chrono::time_point<C, D>& timeout_time, P predicate) noexcept {
while (!predicate()) {
if (wait_until(lock, timeout_time) == std::cv_status::timeout) {
return predicate();
}
}
return true;
}
template<typename R, typename Period>
std::cv_status wait_for(std::unique_lock<Mutex>& lock,
const std::chrono::duration<R, Period>& rel_time) noexcept {
return wait_until(lock, std::chrono::steady_clock::now() + rel_time);
}
template<typename R, typename Period, typename P>
bool wait_for(std::unique_lock<Mutex>& lock,
const std::chrono::duration<R, Period>& rel_time, P pred) noexcept {
return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(pred));
}
private:
std::atomic<uint32_t> mState = { 0 };
void pulse(int threadCount) noexcept;
std::cv_status wait_until(Mutex* lock,
bool realtimeClock, timespec* ts) noexcept;
};
} // namespace utils
#endif // TNT_UTILS_LINUX_CONDITION_H

View File

@@ -1,64 +1,64 @@
/*
* Copyright (C) 2016 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 UTILS_LINUX_MUTEX_H
#define UTILS_LINUX_MUTEX_H
#include <atomic>
#include <utils/compiler.h>
namespace utils {
/*
* A very simple mutex class that can be used as an (almost) drop-in replacement
* for std::mutex.
* It is very low overhead as most of it is inlined.
*/
class Mutex {
public:
constexpr Mutex() noexcept = default;
Mutex(const Mutex&) = delete;
Mutex& operator=(const Mutex&) = delete;
void lock() noexcept {
uint32_t old_state = UNLOCKED;
if (UTILS_UNLIKELY(!mState.compare_exchange_strong(old_state,
LOCKED, std::memory_order_acquire, std::memory_order_relaxed))) {
wait();
}
}
void unlock() noexcept {
if (UTILS_UNLIKELY(mState.exchange(UNLOCKED, std::memory_order_release) == LOCKED_CONTENDED)) {
wake();
}
}
private:
enum {
UNLOCKED = 0, LOCKED = 1, LOCKED_CONTENDED = 2
};
std::atomic<uint32_t> mState = { UNLOCKED };
void wait() noexcept;
void wake() noexcept;
};
} // namespace utils
#endif // UTILS_LINUX_MUTEX_H
/*
* Copyright (C) 2016 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_LINUX_MUTEX_H
#define TNT_UTILS_LINUX_MUTEX_H
#include <atomic>
#include <utils/compiler.h>
namespace utils {
/*
* A very simple mutex class that can be used as an (almost) drop-in replacement
* for std::mutex.
* It is very low overhead as most of it is inlined.
*/
class Mutex {
public:
constexpr Mutex() noexcept = default;
Mutex(const Mutex&) = delete;
Mutex& operator=(const Mutex&) = delete;
void lock() noexcept {
uint32_t old_state = UNLOCKED;
if (UTILS_UNLIKELY(!mState.compare_exchange_strong(old_state,
LOCKED, std::memory_order_acquire, std::memory_order_relaxed))) {
wait();
}
}
void unlock() noexcept {
if (UTILS_UNLIKELY(mState.exchange(UNLOCKED, std::memory_order_release) == LOCKED_CONTENDED)) {
wake();
}
}
private:
enum {
UNLOCKED = 0, LOCKED = 1, LOCKED_CONTENDED = 2
};
std::atomic<uint32_t> mState = { UNLOCKED };
void wait() noexcept;
void wake() noexcept;
};
} // namespace utils
#endif // TNT_UTILS_LINUX_MUTEX_H

View File

@@ -1,115 +1,115 @@
/*
* Copyright (C) 2018 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_MEMALIGN_H
#define TNT_UTILS_MEMALIGN_H
#include <type_traits>
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#if defined(WIN32)
#include <malloc.h>
#endif
namespace utils {
inline void* aligned_alloc(size_t size, size_t align) noexcept {
assert(align && !(align & align - 1));
void* p = nullptr;
// must be a power of two and >= sizeof(void*)
while (align < sizeof(void*)) {
align <<= 1u;
}
#if defined(WIN32)
p = ::_aligned_malloc(size, align);
#else
::posix_memalign(&p, align, size);
#endif
return p;
}
inline void aligned_free(void* p) noexcept {
#if defined(WIN32)
::_aligned_free(p);
#else
::free(p);
#endif
}
/*
* This allocator can be used with std::vector for instance to ensure all items are aligned
* to their alignof(). e.g.
*
* template<typename T>
* using aligned_vector = std::vector<T, utils::STLAlignedAllocator<T>>;
*
* aligned_vector<Foo> foos;
*
*/
template<typename TYPE>
class STLAlignedAllocator {
static_assert(!(alignof(TYPE) & (alignof(TYPE) - 1)), "alignof(T) must be a power of two");
public:
using value_type = TYPE;
using pointer = TYPE*;
using const_pointer = const TYPE*;
using reference = TYPE&;
using const_reference = const TYPE&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using propagate_on_container_move_assignment = std::true_type;
using is_always_equal = std::true_type;
template<typename T>
struct rebind { using other = STLAlignedAllocator<T>; };
inline STLAlignedAllocator() noexcept = default;
template<typename T>
inline explicit STLAlignedAllocator(const STLAlignedAllocator<T>&) noexcept {}
inline ~STLAlignedAllocator() noexcept = default;
inline pointer allocate(size_type n) noexcept {
return (pointer)aligned_alloc(n * sizeof(value_type), alignof(TYPE));
}
inline void deallocate(pointer p, size_type) {
aligned_free(p);
}
// stateless allocators are always equal
template<typename T>
bool operator==(const STLAlignedAllocator<T>& rhs) const noexcept {
return true;
}
template<typename T>
bool operator!=(const STLAlignedAllocator<T>& rhs) const noexcept {
return false;
}
};
} // namespace utils
#endif // TNT_UTILS_MEMALIGN_H
/*
* Copyright (C) 2018 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_MEMALIGN_H
#define TNT_UTILS_MEMALIGN_H
#include <type_traits>
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#if defined(WIN32)
#include <malloc.h>
#endif
namespace utils {
inline void* aligned_alloc(size_t size, size_t align) noexcept {
assert(align && !(align & align - 1));
void* p = nullptr;
// must be a power of two and >= sizeof(void*)
while (align < sizeof(void*)) {
align <<= 1u;
}
#if defined(WIN32)
p = ::_aligned_malloc(size, align);
#else
::posix_memalign(&p, align, size);
#endif
return p;
}
inline void aligned_free(void* p) noexcept {
#if defined(WIN32)
::_aligned_free(p);
#else
::free(p);
#endif
}
/*
* This allocator can be used with std::vector for instance to ensure all items are aligned
* to their alignof(). e.g.
*
* template<typename T>
* using aligned_vector = std::vector<T, utils::STLAlignedAllocator<T>>;
*
* aligned_vector<Foo> foos;
*
*/
template<typename TYPE>
class STLAlignedAllocator {
static_assert(!(alignof(TYPE) & (alignof(TYPE) - 1)), "alignof(T) must be a power of two");
public:
using value_type = TYPE;
using pointer = TYPE*;
using const_pointer = const TYPE*;
using reference = TYPE&;
using const_reference = const TYPE&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using propagate_on_container_move_assignment = std::true_type;
using is_always_equal = std::true_type;
template<typename T>
struct rebind { using other = STLAlignedAllocator<T>; };
inline STLAlignedAllocator() noexcept = default;
template<typename T>
inline explicit STLAlignedAllocator(const STLAlignedAllocator<T>&) noexcept {}
inline ~STLAlignedAllocator() noexcept = default;
inline pointer allocate(size_type n) noexcept {
return (pointer)aligned_alloc(n * sizeof(value_type), alignof(TYPE));
}
inline void deallocate(pointer p, size_type) {
aligned_free(p);
}
// stateless allocators are always equal
template<typename T>
bool operator==(const STLAlignedAllocator<T>& rhs) const noexcept {
return true;
}
template<typename T>
bool operator!=(const STLAlignedAllocator<T>& rhs) const noexcept {
return false;
}
};
} // namespace utils
#endif // TNT_UTILS_MEMALIGN_H

View File

@@ -1,136 +1,139 @@
/*
* Copyright (C) 2019 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_OSTREAM_H
#define TNT_UTILS_OSTREAM_H
#include <string>
#include <utils/bitset.h>
#include <utils/compiler.h> // ssize_t is a POSIX type.
namespace utils {
namespace io {
class UTILS_PUBLIC ostream {
public:
virtual ~ostream();
ostream& operator<<(short value) noexcept;
ostream& operator<<(unsigned short value) noexcept;
ostream& operator<<(char value) noexcept;
ostream& operator<<(unsigned char value) noexcept;
ostream& operator<<(int value) noexcept;
ostream& operator<<(unsigned int value) noexcept;
ostream& operator<<(long value) noexcept;
ostream& operator<<(unsigned long value) noexcept;
ostream& operator<<(long long value) noexcept;
ostream& operator<<(unsigned long long value) noexcept;
ostream& operator<<(float value) noexcept;
ostream& operator<<(double value) noexcept;
ostream& operator<<(long double value) noexcept;
ostream& operator<<(bool value) noexcept;
ostream& operator<<(const void* value) noexcept;
ostream& operator<<(const char* string) noexcept;
ostream& operator<<(const unsigned char* string) noexcept;
ostream& operator<<(ostream& (* f)(ostream&)) noexcept { return f(*this); }
ostream& dec() noexcept;
ostream& hex() noexcept;
protected:
class Buffer {
public:
Buffer() noexcept;
~Buffer() noexcept;
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
char* buffer = nullptr; // buffer address
char* curr = nullptr; // current pointer
size_t size = 0; // size remaining
size_t capacity = 0; // total capacity of the buffer
const char* get() const noexcept { return buffer; }
void advance(ssize_t n) noexcept;
void reset() noexcept;
void reserve(size_t newSize) noexcept;
};
Buffer mData;
Buffer& getBuffer() noexcept { return mData; }
private:
virtual ostream& flush() noexcept = 0;
friend ostream& hex(ostream& s) noexcept;
friend ostream& dec(ostream& s) noexcept;
friend ostream& endl(ostream& s) noexcept;
friend ostream& flush(ostream& s) noexcept;
enum type {
SHORT, USHORT, CHAR, UCHAR, INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, DOUBLE,
LONG_DOUBLE
};
bool mShowHex = false;
const char* getFormat(type t) const noexcept;
/*
* Checks that the buffer has room for s additional bytes, growing the allocation if necessary.
*/
void growBufferIfNeeded(size_t s) noexcept;
};
// handles std::string
inline ostream& operator << (ostream& o, std::string const& s) noexcept { return o << s.c_str(); }
// handles utils::bitset
inline ostream& operator << (ostream& o, utils::bitset32 const& s) noexcept {
return o << (void*)uintptr_t(s.getValue());
}
// handles vectors from libmath (but we do this generically, without needing a dependency on libmath)
template<template<typename T> class VECTOR, typename T>
inline ostream& operator<<(ostream& stream, const VECTOR<T>& v) {
stream << "< ";
for (size_t i = 0; i < v.size() - 1; i++) {
stream << v[i] << ", ";
}
stream << v[v.size() - 1] << " >";
return stream;
}
inline ostream& hex(ostream& s) noexcept { return s.hex(); }
inline ostream& dec(ostream& s) noexcept { return s.dec(); }
inline ostream& endl(ostream& s) noexcept { s << "\n"; return s.flush(); }
inline ostream& flush(ostream& s) noexcept { return s.flush(); }
} // namespace io
} // namespace utils
#endif // TNT_UTILS_OSTREAM_H
/*
* Copyright (C) 2019 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_OSTREAM_H
#define TNT_UTILS_OSTREAM_H
#include <mutex>
#include <string>
#include <utility>
#include <utils/bitset.h>
#include <utils/compiler.h> // ssize_t is a POSIX type.
namespace utils::io {
class UTILS_PUBLIC ostream {
public:
virtual ~ostream();
ostream& operator<<(short value) noexcept;
ostream& operator<<(unsigned short value) noexcept;
ostream& operator<<(char value) noexcept;
ostream& operator<<(unsigned char value) noexcept;
ostream& operator<<(int value) noexcept;
ostream& operator<<(unsigned int value) noexcept;
ostream& operator<<(long value) noexcept;
ostream& operator<<(unsigned long value) noexcept;
ostream& operator<<(long long value) noexcept;
ostream& operator<<(unsigned long long value) noexcept;
ostream& operator<<(float value) noexcept;
ostream& operator<<(double value) noexcept;
ostream& operator<<(long double value) noexcept;
ostream& operator<<(bool value) noexcept;
ostream& operator<<(const void* value) noexcept;
ostream& operator<<(const char* string) noexcept;
ostream& operator<<(const unsigned char* string) noexcept;
ostream& operator<<(ostream& (* f)(ostream&)) noexcept { return f(*this); }
ostream& dec() noexcept;
ostream& hex() noexcept;
protected:
class Buffer {
public:
Buffer() noexcept;
~Buffer() noexcept;
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
const char* get() const noexcept { return buffer; }
std::pair<char*, size_t> grow(size_t s) noexcept;
void advance(ssize_t n) noexcept;
void reset() noexcept;
private:
void reserve(size_t newSize) noexcept;
char* buffer = nullptr; // buffer address
char* curr = nullptr; // current pointer
size_t size = 0; // size remaining
size_t capacity = 0; // total capacity of the buffer
};
std::mutex mLock;
Buffer mData;
Buffer& getBuffer() noexcept { return mData; }
ostream& print(const char* format, ...) noexcept;
private:
virtual ostream& flush() noexcept = 0;
friend ostream& hex(ostream& s) noexcept;
friend ostream& dec(ostream& s) noexcept;
friend ostream& endl(ostream& s) noexcept;
friend ostream& flush(ostream& s) noexcept;
enum type {
SHORT, USHORT, CHAR, UCHAR, INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, DOUBLE,
LONG_DOUBLE
};
inline const char* getFormat(type t) const noexcept;
bool mShowHex = false;
};
// handles std::string
inline ostream& operator << (ostream& o, std::string const& s) noexcept { return o << s.c_str(); }
// handles utils::bitset
inline ostream& operator << (ostream& o, utils::bitset32 const& s) noexcept {
return o << (void*)uintptr_t(s.getValue());
}
// handles vectors from libmath (but we do this generically, without needing a dependency on libmath)
template<template<typename T> class VECTOR, typename T>
inline ostream& operator<<(ostream& stream, const VECTOR<T>& v) {
stream << "< ";
for (size_t i = 0; i < v.size() - 1; i++) {
stream << v[i] << ", ";
}
stream << v[v.size() - 1] << " >";
return stream;
}
inline ostream& hex(ostream& s) noexcept { return s.hex(); }
inline ostream& dec(ostream& s) noexcept { return s.dec(); }
inline ostream& endl(ostream& s) noexcept { s << "\n"; return s.flush(); }
inline ostream& flush(ostream& s) noexcept { return s.flush(); }
} // namespace utils::io
#endif // TNT_UTILS_OSTREAM_H

View File

@@ -1,37 +1,37 @@
/*
* Copyright (C) 2019 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_SSTREAM_H
#define TNT_SSTREAM_H
#include <utils/ostream.h>
namespace utils {
namespace io {
class sstream : public ostream {
public:
ostream &flush() noexcept override;
const char* c_str() const noexcept;
};
} // namespace io
} // namespace utils
#endif //TNT_SSTREAM_H
/*
* Copyright (C) 2019 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_SSTREAM_H
#define TNT_UTILS_SSTREAM_H
#include <utils/ostream.h>
namespace utils {
namespace io {
class sstream : public ostream {
public:
ostream &flush() noexcept override;
const char* c_str() const noexcept;
};
} // namespace io
} // namespace utils
#endif // TNT_UTILS_SSTREAM_H

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2021 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_STRING_H
#define TNT_UTILS_STRING_H
#include <utils/ostream.h>
namespace utils {
float strtof_c(const char* start, char** end);
} // namespace utils
#endif // TNT_UTILS_STRING_H

View File

@@ -1,40 +1,40 @@
/*
* Copyright (C) 2018 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 UTILS_TRAP_H
#define UTILS_TRAP_H
#include <csignal>
#if defined(WIN32)
#include <Windows.h>
#include <utils/unwindows.h>
#endif
namespace utils {
// This can be used as a programmatic breakpoint.
inline void debug_trap() noexcept {
#if defined(WIN32)
DebugBreak();
#else
std::raise(SIGINT);
#endif
}
} // namespace utils
#endif // UTILS_TRAP_H
/*
* Copyright (C) 2018 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_TRAP_H
#define TNT_UTILS_TRAP_H
#include <csignal>
#if defined(WIN32)
#include <Windows.h>
#include <utils/unwindows.h>
#endif
namespace utils {
// This can be used as a programmatic breakpoint.
inline void debug_trap() noexcept {
#if defined(WIN32)
DebugBreak();
#else
std::raise(SIGINT);
#endif
}
} // namespace utils
#endif // TNT_UTILS_TRAP_H

View File

@@ -1,51 +1,51 @@
/*
* Copyright (C) 2018 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.
*/
#if defined (WIN32)
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#ifdef far
#undef far
#endif
#ifdef near
#undef near
#endif
#ifdef ERROR
#undef ERROR
#endif
#ifdef OPAQUE
#undef OPAQUE
#endif
#ifdef TRANSPARENT
#undef TRANSPARENT
#endif
#ifdef PURE
#undef PURE
#endif
#endif
/*
* Copyright (C) 2018 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.
*/
#if defined (WIN32)
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#ifdef far
#undef far
#endif
#ifdef near
#undef near
#endif
#ifdef ERROR
#undef ERROR
#endif
#ifdef OPAQUE
#undef OPAQUE
#endif
#ifdef TRANSPARENT
#undef TRANSPARENT
#endif
#ifdef PURE
#undef PURE
#endif
#endif

View File

@@ -1,60 +1,60 @@
/*
* Copyright (C) 2015 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.
*/
#include <vector>
#ifndef UTILS_VECTOR_H
#define UTILS_VECTOR_H
namespace utils {
/**
* Inserts the specified item in the vector at its sorted position.
*/
template <typename T>
static inline void insert_sorted(std::vector<T>& v, T item) {
auto pos = std::lower_bound(v.begin(), v.end(), item);
v.insert(pos, std::move(item));
}
/**
* Inserts the specified item in the vector at its sorted position.
* The item type must implement the < operator. If the specified
* item is already present in the vector, this method returns without
* inserting the item again.
*
* @return True if the item was inserted at is sorted position, false
* if the item already exists in the vector.
*/
template <typename T>
static inline bool insert_sorted_unique(std::vector<T>& v, T item) {
if (UTILS_LIKELY(v.size() == 0 || v.back() < item)) {
v.push_back(item);
return true;
}
auto pos = std::lower_bound(v.begin(), v.end(), item);
if (UTILS_LIKELY(pos == v.end() || item < *pos)) {
v.insert(pos, std::move(item));
return true;
}
return false;
}
} // end utils namespace
#endif //UTILS_VECTOR_H
/*
* Copyright (C) 2015 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_VECTOR_H
#define TNT_UTILS_VECTOR_H
#include <vector>
namespace utils {
/**
* Inserts the specified item in the vector at its sorted position.
*/
template <typename T>
static inline void insert_sorted(std::vector<T>& v, T item) {
auto pos = std::lower_bound(v.begin(), v.end(), item);
v.insert(pos, std::move(item));
}
/**
* Inserts the specified item in the vector at its sorted position.
* The item type must implement the < operator. If the specified
* item is already present in the vector, this method returns without
* inserting the item again.
*
* @return True if the item was inserted at is sorted position, false
* if the item already exists in the vector.
*/
template <typename T>
static inline bool insert_sorted_unique(std::vector<T>& v, T item) {
if (UTILS_LIKELY(v.size() == 0 || v.back() < item)) {
v.push_back(item);
return true;
}
auto pos = std::lower_bound(v.begin(), v.end(), item);
if (UTILS_LIKELY(pos == v.end() || item < *pos)) {
v.insert(pos, std::move(item));
return true;
}
return false;
}
} // end utils namespace
#endif // TNT_UTILS_VECTOR_H

View File

@@ -1,33 +1,33 @@
/*
* Copyright (C) 2018 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_WIN32_STDTYPES_H
#define TNT_UTILS_WIN32_STDTYPES_H
#if defined(WIN32)
#include <windows.h>
// Copied from linux libc sys/stat.h:
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define PATH_MAX (MAX_PATH)
// For getcwd
#include <direct.h>
#define getcwd _getcwd
#endif
#endif
/*
* Copyright (C) 2018 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_WIN32_STDTYPES_H
#define TNT_UTILS_WIN32_STDTYPES_H
#if defined(WIN32)
#include <windows.h>
// Copied from linux libc sys/stat.h:
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define PATH_MAX (MAX_PATH)
// For getcwd
#include <direct.h>
#define getcwd _getcwd
#endif
#endif // TNT_UTILS_WIN32_STDTYPES_H