Build the Vulkan server
Bug: 171711491
TODO: Flesh out Framebuffer, ColorBuffer, RenderControl
TODO: Make sure feature control flags are set properly on init.
TODO: Implement various System related things like program and launcher
directory, env variable setting.
Change-Id: I126b9a7c3dcad6318e20e82eed1b6c774827804d
diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt
index 3f20bd2..e0b7bb3 100644
--- a/base/CMakeLists.txt
+++ b/base/CMakeLists.txt
@@ -1,8 +1,26 @@
-add_library(
- gfxstreambase
+set(gfxstream-base-common-sources
MemStream.cpp
+ MessageChannel.cpp
+ PathUtils.cpp
+ SharedLibrary.cpp
Stream.cpp
StreamSerializing.cpp
- SubAllocator.cpp)
+ SubAllocator.cpp
+ System.cpp
+ Tracing.cpp)
+set(gfxstream-base-windows-sources
+ Win32UnicodeString.cpp)
+
+if (WIN32)
+add_library(
+ gfxstream-base
+ ${gfxstream-base-common-sources}
+ ${gfxstream-base-windows-sources})
+else()
+add_library(
+ gfxstream-base
+ ${gfxstream-base-common-sources})
+endif()
+
target_include_directories(
- gfxstreambase PUBLIC ../)
+ gfxstream-base PUBLIC ../)
diff --git a/base/Compiler.h b/base/Compiler.h
new file mode 100644
index 0000000..ddfd992
--- /dev/null
+++ b/base/Compiler.h
@@ -0,0 +1,39 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+#pragma once
+
+// Use this inside a class declaration to ensure that the corresponding objects
+// cannot be copy-constructed or assigned. For example:
+//
+// class Foo {
+// ....
+// DISALLOW_COPY_AND_ASSIGN(Foo)
+// ....
+// };
+//
+// Note: this macro is sometimes defined in 3rd-party libs, so let's check first
+#ifndef DISALLOW_COPY_AND_ASSIGN
+
+#define DISALLOW_COPY_AND_ASSIGN(T) \
+ T(const T& other) = delete; \
+ T& operator=(const T& other) = delete
+
+#endif
+
+#ifndef DISALLOW_COPY_ASSIGN_AND_MOVE
+
+#define DISALLOW_COPY_ASSIGN_AND_MOVE(T) \
+ DISALLOW_COPY_AND_ASSIGN(T); \
+ T(T&&) = delete; \
+ T& operator=(T&&) = delete
+
+#endif
diff --git a/base/ConditionVariable.h b/base/ConditionVariable.h
new file mode 100644
index 0000000..3094a8e
--- /dev/null
+++ b/base/ConditionVariable.h
@@ -0,0 +1,206 @@
+// Copyright (C) 2014 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.
+
+#pragma once
+
+#include "base/Compiler.h"
+#include "base/Lock.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+#include <assert.h>
+
+namespace android {
+namespace base {
+
+// A class that implements a condition variable, which can be used in
+// association with a Lock to blocking-wait for specific conditions.
+// Useful to implement various synchronization data structures.
+class ConditionVariable {
+public:
+ // A set of functions to efficiently unlock the lock used with
+ // the current condition variable and signal or broadcast it.
+ //
+ // The functions are needed because on some platforms (Posix) it's more
+ // efficient to signal the variable before unlocking mutex, while on others
+ // (Windows) it's exactly the opposite. Functions implement the best way
+ // for each platform and abstract it out from the user.
+ void signalAndUnlock(StaticLock* lock);
+ void signalAndUnlock(AutoLock* lock);
+
+ void broadcastAndUnlock(StaticLock* lock);
+ void broadcastAndUnlock(AutoLock* lock);
+
+ void wait(AutoLock* userLock) {
+ assert(userLock->mLocked);
+ wait(&userLock->mLock);
+ }
+
+ //
+ // Convenience functions to get rid of the loop in condition variable usage
+ // Instead of hand-writing a loop, e.g.
+ //
+ // while (mRefCount < 3) {
+ // mCv.wait(&mLock);
+ // }
+ //
+ // use the following two wait() overloads:
+ //
+ // mCv.wait(&mLock, [this]() { return mRefCount >= 3; });
+ //
+ // Parameters:
+ // |lock| - a Lock or AutoLock pointer used with the condition variable.
+ // |pred| - a functor predicate that's compatible with "bool pred()"
+ // signature and returns a condition when one should stop waiting.
+ //
+
+ template <class Predicate>
+ void wait(StaticLock* lock, Predicate pred) {
+ while (!pred()) {
+ this->wait(lock);
+ }
+ }
+
+ template <class Predicate>
+ void wait(AutoLock* lock, Predicate pred) {
+ this->wait(&lock->mLock, pred);
+ }
+
+#ifdef _WIN32
+
+ ConditionVariable() {
+ ::InitializeConditionVariable(&mCond);
+ }
+
+ // There's no special function to destroy CONDITION_VARIABLE in Windows.
+ ~ConditionVariable() = default;
+
+ // Wait until the condition variable is signaled. Note that spurious
+ // wakeups are always a possibility, so always check the condition
+ // in a loop, i.e. do:
+ //
+ // while (!condition) { condVar.wait(&lock); }
+ //
+ // instead of:
+ //
+ // if (!condition) { condVar.wait(&lock); }
+ //
+ void wait(StaticLock* userLock) {
+ ::SleepConditionVariableSRW(&mCond, &userLock->mLock, INFINITE, 0);
+ }
+
+ bool timedWait(StaticLock *userLock, uint64_t waitUntilUs) {
+ const auto now = System::get()->getUnixTimeUs();
+ const auto timeout =
+ std::max<uint64_t>(0, waitUntilUs - now) / 1000;
+ return ::SleepConditionVariableSRW(
+ &mCond, &userLock->mLock, timeout, 0) != 0;
+ }
+
+ // Signal that a condition was reached. This will wake at least (and
+ // preferrably) one waiting thread that is blocked on wait().
+ void signal() {
+ ::WakeConditionVariable(&mCond);
+ }
+
+ // Like signal(), but wakes all of the waiting threads.
+ void broadcast() {
+ ::WakeAllConditionVariable(&mCond);
+ }
+
+private:
+ CONDITION_VARIABLE mCond;
+
+#else // !_WIN32
+
+ // Note: on Posix systems, make it a naive wrapper around pthread_cond_t.
+
+ ConditionVariable() {
+ pthread_cond_init(&mCond, NULL);
+ }
+
+ ~ConditionVariable() {
+ pthread_cond_destroy(&mCond);
+ }
+
+ void wait(StaticLock* userLock) {
+ pthread_cond_wait(&mCond, &userLock->mLock);
+ }
+
+ bool timedWait(StaticLock* userLock, uint64_t waitUntilUs) {
+ timespec abstime;
+ abstime.tv_sec = waitUntilUs / 1000000LL;
+ abstime.tv_nsec = (waitUntilUs % 1000000LL) * 1000;
+ return pthread_cond_timedwait(&mCond, &userLock->mLock, &abstime) == 0;
+ }
+
+ void signal() {
+ pthread_cond_signal(&mCond);
+ }
+
+ void broadcast() {
+ pthread_cond_broadcast(&mCond);
+ }
+
+private:
+ pthread_cond_t mCond;
+
+#endif // !_WIN32
+
+ DISALLOW_COPY_ASSIGN_AND_MOVE(ConditionVariable);
+};
+
+#ifdef _WIN32
+inline void ConditionVariable::signalAndUnlock(StaticLock* lock) {
+ lock->unlock();
+ signal();
+}
+inline void ConditionVariable::signalAndUnlock(AutoLock* lock) {
+ lock->unlock();
+ signal();
+}
+
+inline void ConditionVariable::broadcastAndUnlock(StaticLock* lock) {
+ lock->unlock();
+ broadcast();
+}
+inline void ConditionVariable::broadcastAndUnlock(AutoLock* lock) {
+ lock->unlock();
+ broadcast();
+}
+#else // !_WIN32
+inline void ConditionVariable::signalAndUnlock(StaticLock* lock) {
+ signal();
+ lock->unlock();
+}
+inline void ConditionVariable::signalAndUnlock(AutoLock* lock) {
+ signal();
+ lock->unlock();
+}
+inline void ConditionVariable::broadcastAndUnlock(StaticLock* lock) {
+ broadcast();
+ lock->unlock();
+}
+inline void ConditionVariable::broadcastAndUnlock(AutoLock* lock) {
+ broadcast();
+ lock->unlock();
+}
+#endif // !_WIN32
+
+} // namespace base
+} // namespace android
diff --git a/base/EntityManager.h b/base/EntityManager.h
new file mode 100644
index 0000000..d176114
--- /dev/null
+++ b/base/EntityManager.h
@@ -0,0 +1,568 @@
+// 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.
+#pragma once
+
+#include "base/Lookup.h"
+#include "base/Optional.h"
+
+#include <functional>
+#include <unordered_map>
+#include <vector>
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#define ENTITY_MANAGER_DEBUG 0
+
+#if ENTITY_MANAGER_DEBUG
+
+#define EM_DBG(fmt,...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__);
+
+#else
+#define EM_DBG(...)
+#endif
+
+#define INVALID_ENTITY_HANDLE 0
+#define INVALID_COMPONENT_HANDLE 0
+
+namespace android {
+namespace base {
+
+// EntityManager: A way to represent an abstrat space of objects with handles.
+// Each handle is associated with data of type Item for quick access from handles to data.
+// Otherwise, entity data is spread through ComponentManagers.
+template<size_t indexBits,
+ size_t generationBits,
+ size_t typeBits,
+ class Item>
+class EntityManager {
+public:
+
+ static_assert(64 == (indexBits + generationBits + typeBits),
+ "bits of index, generation, and type must add to 64");
+
+ using EntityHandle = uint64_t;
+ using IteratorFunc = std::function<void(bool live, EntityHandle h, Item& item)>;
+ using ConstIteratorFunc = std::function<void(bool live, EntityHandle h, const Item& item)>;
+
+ static size_t getHandleIndex(EntityHandle h) {
+ return static_cast<size_t>(h & ((1ULL << indexBits) - 1ULL));
+ }
+
+ static size_t getHandleGeneration(EntityHandle h) {
+ return static_cast<size_t>(
+ (h >> indexBits) &
+ ((1ULL << generationBits) - 1ULL));
+ }
+
+ static size_t getHandleType(EntityHandle h) {
+ return static_cast<size_t>(
+ (h >> (indexBits + generationBits)) &
+ ((1ULL << typeBits) - 1ULL));
+ return h & ((1ULL << indexBits) - 1ULL);
+ }
+
+ static EntityHandle makeHandle(
+ size_t index,
+ size_t generation,
+ size_t type) {
+ EntityHandle res = index;
+ res |= generation << indexBits;
+ res |= type << (indexBits + generationBits);
+ return res;
+ }
+
+ static EntityHandle withIndex(EntityHandle h, size_t i) {
+ return makeHandle(i, getHandleGeneration(h), getHandleType(h));
+ }
+
+ static EntityHandle withGeneration(EntityHandle h, size_t nextGen) {
+ return makeHandle(getHandleIndex(h), nextGen, getHandleType(h));
+ }
+
+ static EntityHandle withType(EntityHandle h, size_t newType) {
+ return makeHandle(getHandleIndex(h), getHandleGeneration(h), newType);
+ }
+
+ EntityManager() : EntityManager(0) { }
+
+ ~EntityManager() { clear(); }
+
+ struct EntityEntry {
+ EntityHandle handle = 0;
+ size_t nextFreeIndex = 0;
+ // 0 is a special generation for brand new entries
+ // that are not used yet
+ size_t liveGeneration = 1;
+ Item item;
+ };
+
+ void clear() {
+ mEntries.clear();
+ mFirstFreeIndex = 0;
+ mLiveEntries = 0;
+ }
+
+ EntityHandle add(const Item& item, size_t type) {
+
+ if (!type) return INVALID_ENTITY_HANDLE;
+
+ size_t maxElements = (1ULL << indexBits);
+ if (mLiveEntries == maxElements) return INVALID_ENTITY_HANDLE;
+
+ size_t newIndex = mFirstFreeIndex;
+
+ EM_DBG("newIndex/firstFree: %zu type: %zu", newIndex, type);
+
+ size_t neededCapacity = newIndex + 1;
+ if (maxElements < neededCapacity) return INVALID_ENTITY_HANDLE;
+
+ size_t currentCapacity = mEntries.size();
+ size_t nextCapacity = neededCapacity << 1;
+ if (nextCapacity > maxElements) nextCapacity = maxElements;
+
+ EM_DBG("needed/current/next capacity: %zu %zu %zu",
+ neededCapacity,
+ currentCapacity,
+ nextCapacity);
+
+ if (neededCapacity > mEntries.size()) {
+ mEntries.resize(nextCapacity);
+ for (size_t i = currentCapacity; i < nextCapacity; ++i) {
+ mEntries[i].handle = makeHandle(i, 0, type);
+ mEntries[i].nextFreeIndex = i + 1;
+ EM_DBG("new un-init entry: index %zu nextFree %zu",
+ i, i + 1);
+ }
+ }
+
+ mEntries[newIndex].handle =
+ makeHandle(newIndex, mEntries[newIndex].liveGeneration, type);
+ mEntries[newIndex].item = item;
+
+ mFirstFreeIndex = mEntries[newIndex].nextFreeIndex;
+ EM_DBG("created. new first free: %zu", mFirstFreeIndex);
+
+ ++mLiveEntries;
+
+ EM_DBG("result handle: 0x%llx", (unsigned long long)mEntries[newIndex].handle);
+
+ return mEntries[newIndex].handle;
+ }
+
+ EntityHandle addFixed(EntityHandle fixedHandle, const Item& item, size_t type) {
+ // 3 cases:
+ // 1. handle is not allocated and doesn't correspond to mFirstFreeIndex
+ bool isFreeListNonHead = false;
+ // 2. handle already exists (replace)
+ bool isAlloced = false;
+ // 3. index(handle) == mFirstFreeIndex
+ bool isFreeListHead = false;
+
+ if (!type) return INVALID_ENTITY_HANDLE;
+
+ size_t maxElements = (1ULL << indexBits);
+ if (mLiveEntries == maxElements) return INVALID_ENTITY_HANDLE;
+
+ size_t newIndex = getHandleIndex(fixedHandle);
+
+ EM_DBG("newIndex/firstFree: %zu type: %zu", newIndex, type);
+
+ size_t neededCapacity = newIndex + 1;
+
+ if (maxElements < neededCapacity) return INVALID_ENTITY_HANDLE;
+
+ size_t currentCapacity = mEntries.size();
+ size_t nextCapacity = neededCapacity << 1;
+ if (nextCapacity > maxElements) nextCapacity = maxElements;
+
+ EM_DBG("needed/current/next capacity: %zu %zu %zu",
+ neededCapacity,
+ currentCapacity,
+ nextCapacity);
+
+ if (neededCapacity > mEntries.size()) {
+ mEntries.resize(nextCapacity);
+ for (size_t i = currentCapacity; i < nextCapacity; ++i) {
+ mEntries[i].handle = makeHandle(i, 0, type);
+ mEntries[i].nextFreeIndex = i + 1;
+ EM_DBG("new un-init entry: index %zu nextFree %zu",
+ i, i + 1);
+ }
+ }
+
+ // Now we ensured that there is enough space to talk about the entry of
+ // this |fixedHandle|.
+ if (mFirstFreeIndex == newIndex) {
+ isFreeListHead = true;
+ } else {
+ auto& entry = mEntries[newIndex];
+ if (entry.liveGeneration == getHandleGeneration(entry.handle)) {
+ isAlloced = true;
+ } else {
+ isFreeListNonHead = true;
+ }
+ }
+
+ mEntries[newIndex].handle = fixedHandle;
+ mEntries[newIndex].liveGeneration = getHandleGeneration(fixedHandle);
+ mEntries[newIndex].item = item;
+
+ EM_DBG("new index: %zu", newIndex);
+
+ if (isFreeListHead) {
+
+ EM_DBG("first free index reset from %zu to %zu",
+ mFirstFreeIndex, mEntries[newIndex].nextFreeIndex);
+
+ mFirstFreeIndex = mEntries[newIndex].nextFreeIndex;
+
+ ++mLiveEntries;
+
+ } else if (isAlloced) {
+ // Already replaced whatever is there, and since it's already allocated,
+ // no need to update freelist.
+ EM_DBG("entry at %zu already alloced. replacing.", newIndex);
+ } else if (isFreeListNonHead) {
+ // Go through the freelist and skip over the entry we just added.
+ size_t prevEntryIndex = mFirstFreeIndex;
+
+ EM_DBG("in free list but not head. reorganizing freelist. "
+ "start at %zu -> %zu",
+ mFirstFreeIndex, mEntries[prevEntryIndex].nextFreeIndex);
+
+ while (mEntries[prevEntryIndex].nextFreeIndex != newIndex) {
+ EM_DBG("next: %zu -> %zu",
+ prevEntryIndex,
+ mEntries[prevEntryIndex].nextFreeIndex);
+ prevEntryIndex =
+ mEntries[prevEntryIndex].nextFreeIndex;
+ }
+
+ EM_DBG("finished. set prev entry %zu to new entry's next, %zu",
+ prevEntryIndex, mEntries[newIndex].nextFreeIndex);
+
+ mEntries[prevEntryIndex].nextFreeIndex =
+ mEntries[newIndex].nextFreeIndex;
+
+ ++mLiveEntries;
+ }
+
+ return fixedHandle;
+ }
+ void remove(EntityHandle h) {
+
+ if (get(h) == nullptr) return;
+
+ size_t index = getHandleIndex(h);
+
+ EM_DBG("remove handle: 0x%llx -> index %zu", (unsigned long long)h, index);
+
+ auto& entry = mEntries[index];
+
+ EM_DBG("handle gen: %zu entry gen: %zu", getHandleGeneration(h), entry.liveGeneration);
+
+ ++entry.liveGeneration;
+ if ((entry.liveGeneration == 0) ||
+ (entry.liveGeneration == (1ULL << generationBits))) {
+ entry.liveGeneration = 1;
+ }
+
+ entry.nextFreeIndex = mFirstFreeIndex;
+
+ mFirstFreeIndex = index;
+
+ EM_DBG("new first free: %zu next free: %zu", mFirstFreeIndex, entry.nextFreeIndex);
+
+ --mLiveEntries;
+ }
+
+ Item* get(EntityHandle h) {
+ size_t index = getHandleIndex(h);
+ if (index >= mEntries.size()) return nullptr;
+
+ auto& entry = mEntries[index];
+ if (entry.liveGeneration != getHandleGeneration(h)) return nullptr;
+
+ return &entry.item;
+ }
+
+ bool isLive(EntityHandle h) const {
+ size_t index = getHandleIndex(h);
+ if (index >= mEntries.size()) return false;
+
+ const auto& entry = mEntries[index];
+
+ return (entry.liveGeneration == getHandleGeneration(h));
+ }
+
+ void forEachEntry(IteratorFunc func) {
+ for (auto& entry: mEntries) {
+ auto handle = entry.handle;
+ bool live = isLive(handle);
+ auto& item = entry.item;
+ func(live, handle, item);
+ }
+ }
+
+ void forEachLiveEntry(IteratorFunc func) {
+ for (auto& entry: mEntries) {
+ auto handle = entry.handle;
+ bool live = isLive(handle);
+
+ if (!live) continue;
+
+ auto& item = entry.item;
+ func(live, handle, item);
+ }
+ }
+
+ void forEachLiveEntry_const(ConstIteratorFunc func) const {
+ for (auto& entry: mEntries) {
+ auto handle = entry.handle;
+ bool live = isLive(handle);
+
+ if (!live) continue;
+
+ const auto& item = entry.item;
+ func(live, handle, item);
+ }
+ }
+
+private:
+ EntityManager(size_t initialItems) :
+ mEntries(initialItems),
+ mFirstFreeIndex(0),
+ mLiveEntries(0) { }
+
+ std::vector<EntityEntry> mEntries;
+ size_t mFirstFreeIndex;
+ size_t mLiveEntries;
+};
+
+// Tracks components over a given space of entities.
+// Looking up by entity index is slower, but takes less space overall versus
+// a flat array that parallels the entities.
+template<size_t indexBits,
+ size_t generationBits,
+ size_t typeBits,
+ class Data>
+class ComponentManager {
+public:
+
+ static_assert(64 == (indexBits + generationBits + typeBits),
+ "bits of index, generation, and type must add to 64");
+
+ using ComponentHandle = uint64_t;
+ using EntityHandle = uint64_t;
+ using ComponentIteratorFunc = std::function<void(bool, ComponentHandle componentHandle, EntityHandle entityHandle, Data& data)>;
+ using ConstComponentIteratorFunc = std::function<void(bool, ComponentHandle componentHandle, EntityHandle entityHandle, const Data& data)>;
+
+ // Adds the given |data| and associates it with EntityHandle.
+ // We can also opt-in to immediately tracking the handle in the reverse mapping,
+ // which has an upfront cost in runtime.
+ // Many uses of ComponentManager don't really need to track the associated entity handle,
+ // so it is opt-in.
+
+ ComponentHandle add(
+ EntityHandle h,
+ const Data& data,
+ size_t type,
+ bool tracked = false) {
+
+ InternalItem item = { h, data, tracked };
+ auto res = static_cast<ComponentHandle>(mData.add(item, type));
+
+ if (tracked) {
+ mEntityToComponentMap[h] = res;
+ }
+
+ return res;
+ }
+
+ void clear() {
+ mData.clear();
+ mEntityToComponentMap.clear();
+ }
+
+ // If we didn't explicitly track, just fail.
+ ComponentHandle getComponentHandle(EntityHandle h) const {
+ auto componentHandlePtr = android::base::find(mEntityToComponentMap, h);
+ if (!componentHandlePtr) return INVALID_COMPONENT_HANDLE;
+ return *componentHandlePtr;
+ }
+
+ EntityHandle getEntityHandle(ComponentHandle h) const {
+ return mData.get(h)->entityHandle;
+ }
+
+ void removeByEntity(EntityHandle h) {
+ auto componentHandle = getComponentHandle(h);
+ removeByComponent(componentHandle);
+ }
+
+ void removeByComponent(ComponentHandle h) {
+ auto item = mData.get(h);
+
+ if (!item) return;
+ if (item->tracked) {
+ mEntityToComponentMap.erase(item->entityHandle);
+ }
+
+ mData.remove(h);
+ }
+
+ Data* getByEntity(EntityHandle h) {
+ return getByComponent(getComponentHandle(h));
+ }
+
+ Data* getByComponent(ComponentHandle h) {
+ auto item = mData.get(h);
+ if (!item) return nullptr;
+ return &(item->data);
+ }
+
+ void forEachComponent(ComponentIteratorFunc func) {
+ mData.forEachEntry(
+ [func](bool live, typename InternalEntityManager::EntityHandle componentHandle, InternalItem& item) {
+ func(live, componentHandle, item.entityHandle, item.data);
+ });
+ }
+
+ void forEachLiveComponent(ComponentIteratorFunc func) {
+ mData.forEachLiveEntry(
+ [func](bool live, typename InternalEntityManager::EntityHandle componentHandle, InternalItem& item) {
+ func(live, componentHandle, item.entityHandle, item.data);
+ });
+ }
+
+ void forEachLiveComponent_const(ConstComponentIteratorFunc func) const {
+ mData.forEachLiveEntry_const(
+ [func](bool live, typename InternalEntityManager::EntityHandle componentHandle, const InternalItem& item) {
+ func(live, componentHandle, item.entityHandle, item.data);
+ });
+ }
+
+private:
+ struct InternalItem {
+ EntityHandle entityHandle;
+ Data data;
+ bool tracked;
+ };
+
+ using InternalEntityManager = EntityManager<indexBits, generationBits, typeBits, InternalItem>;
+ using EntityToComponentMap = std::unordered_map<EntityHandle, ComponentHandle>;
+
+ mutable InternalEntityManager mData;
+ EntityToComponentMap mEntityToComponentMap;
+};
+
+// ComponentManager, but unpacked; uses the same index space as the associated
+// entities. Takes more space by default, but not more if all entities have this component.
+template<size_t indexBits,
+ size_t generationBits,
+ size_t typeBits,
+ class Data>
+class UnpackedComponentManager {
+public:
+ using ComponentHandle = uint64_t;
+ using EntityHandle = uint64_t;
+ using ComponentIteratorFunc =
+ std::function<void(bool, ComponentHandle componentHandle, EntityHandle entityHandle, Data& data)>;
+ using ConstComponentIteratorFunc =
+ std::function<void(bool, ComponentHandle componentHandle, EntityHandle entityHandle, const Data& data)>;
+
+ EntityHandle add(EntityHandle h, const Data& data) {
+
+ size_t index = indexOfEntity(h);
+
+ if (index + 1 > mItems.size()) {
+ mItems.resize((index + 1) * 2);
+ }
+
+ mItems[index].live = true;
+ mItems[index].handle = h;
+ mItems[index].data = data;
+
+ return h;
+ }
+
+ void clear() {
+ mItems.clear();
+ }
+
+ void remove(EntityHandle h) {
+ size_t index = indexOfEntity(h);
+ if (index >= mItems.size()) return;
+ mItems[index].live = false;
+ }
+
+ Data* get(EntityHandle h) {
+ size_t index = indexOfEntity(h);
+
+ if (index + 1 > mItems.size()) {
+ mItems.resize((index + 1) * 2);
+ }
+
+ auto item = mItems.data() + index;
+ if (!item->live) return nullptr;
+ return &item->data;
+ }
+
+ const Data* get_const(EntityHandle h) const {
+ size_t index = indexOfEntity(h);
+
+ if (index + 1 > mItems.size()) {
+ return nullptr;
+ }
+
+ auto item = mItems.data() + index;
+ if (!item->live) return nullptr;
+ return &item->data;
+ }
+
+ void forEachComponent(ComponentIteratorFunc func) {
+ for (auto& item : mItems) {
+ func(item.live, item.handle, item.handle, item.data);
+ }
+ }
+
+ void forEachLiveComponent(ComponentIteratorFunc func) {
+ for (auto& item : mItems) {
+ if (item.live) func(item.live, item.handle, item.handle, item.data);
+ }
+ }
+
+ void forEachLiveComponent_const(ConstComponentIteratorFunc func) const {
+ for (auto& item : mItems) {
+ if (item.live) func(item.live, item.handle, item.handle, item.data);
+ }
+ }
+
+private:
+ static size_t indexOfEntity(EntityHandle h) {
+ return EntityManager<indexBits, generationBits, typeBits, int>::getHandleIndex(h);
+ }
+
+ struct InternalItem {
+ bool live = false;
+ EntityHandle handle = 0;
+ Data data;
+ };
+
+ std::vector<InternalItem> mItems;
+};
+
+} // namespace android
+} // namespace base
diff --git a/base/Lock.h b/base/Lock.h
new file mode 100644
index 0000000..56fe7c5
--- /dev/null
+++ b/base/Lock.h
@@ -0,0 +1,230 @@
+// Copyright (C) 2014 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.
+
+#pragma once
+
+#include "base/Compiler.h"
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+#else
+#include <pthread.h>
+#endif
+
+#include <assert.h>
+
+namespace android {
+namespace base {
+
+class AutoLock;
+class AutoWriteLock;
+class AutoReadLock;
+
+// A wrapper class for mutexes only suitable for using in static context,
+// where it's OK to leak the underlying system object. Use Lock for scoped or
+// member locks.
+class StaticLock {
+public:
+ using AutoLock = android::base::AutoLock;
+
+ constexpr StaticLock() = default;
+
+ // Acquire the lock.
+ void lock() {
+#ifdef _WIN32
+ ::AcquireSRWLockExclusive(&mLock);
+#else
+ ::pthread_mutex_lock(&mLock);
+#endif
+ }
+
+ bool tryLock() {
+ bool ret = false;
+#ifdef _WIN32
+ ret = ::TryAcquireSRWLockExclusive(&mLock);
+#else
+ ret = ::pthread_mutex_trylock(&mLock) == 0;
+#endif
+ return ret;
+ }
+
+ // Release the lock.
+ void unlock() {
+#ifdef _WIN32
+ ::ReleaseSRWLockExclusive(&mLock);
+#else
+ ::pthread_mutex_unlock(&mLock);
+#endif
+ }
+
+protected:
+ friend class ConditionVariable;
+
+#ifdef _WIN32
+ // Benchmarks show that on Windows SRWLOCK performs a little bit better than
+ // CRITICAL_SECTION for uncontended mode and much better in case of
+ // contention.
+ SRWLOCK mLock = SRWLOCK_INIT;
+#else
+ pthread_mutex_t mLock = PTHREAD_MUTEX_INITIALIZER;
+#endif
+ // Both POSIX threads and WinAPI don't allow move (undefined behavior).
+ DISALLOW_COPY_ASSIGN_AND_MOVE(StaticLock);
+};
+
+// Simple wrapper class for mutexes used in non-static context.
+class Lock : public StaticLock {
+public:
+ using StaticLock::AutoLock;
+
+ constexpr Lock() = default;
+#ifndef _WIN32
+ // The only difference is that POSIX requires a deallocation function call
+ // for its mutexes.
+ ~Lock() { ::pthread_mutex_destroy(&mLock); }
+#endif
+};
+
+class ReadWriteLock {
+public:
+ using AutoWriteLock = android::base::AutoWriteLock;
+ using AutoReadLock = android::base::AutoReadLock;
+
+#ifdef _WIN32
+ constexpr ReadWriteLock() = default;
+ ~ReadWriteLock() = default;
+ void lockRead() { ::AcquireSRWLockShared(&mLock); }
+ void unlockRead() { ::ReleaseSRWLockShared(&mLock); }
+ void lockWrite() { ::AcquireSRWLockExclusive(&mLock); }
+ void unlockWrite() { ::ReleaseSRWLockExclusive(&mLock); }
+
+private:
+ SRWLOCK mLock = SRWLOCK_INIT;
+#else // !_WIN32
+ ReadWriteLock() { ::pthread_rwlock_init(&mLock, NULL); }
+ ~ReadWriteLock() { ::pthread_rwlock_destroy(&mLock); }
+ void lockRead() { ::pthread_rwlock_rdlock(&mLock); }
+ void unlockRead() { ::pthread_rwlock_unlock(&mLock); }
+ void lockWrite() { ::pthread_rwlock_wrlock(&mLock); }
+ void unlockWrite() { ::pthread_rwlock_unlock(&mLock); }
+
+private:
+ pthread_rwlock_t mLock;
+#endif // !_WIN32
+
+ friend class ConditionVariable;
+ DISALLOW_COPY_ASSIGN_AND_MOVE(ReadWriteLock);
+};
+
+// Helper class to lock / unlock a mutex automatically on scope
+// entry and exit.
+// NB: not thread-safe (as opposed to the Lock class)
+class AutoLock {
+public:
+ AutoLock(StaticLock& lock) : mLock(lock) { mLock.lock(); }
+
+ AutoLock(AutoLock&& other) : mLock(other.mLock), mLocked(other.mLocked) {
+ other.mLocked = false;
+ }
+
+ void lock() {
+ assert(!mLocked);
+ mLock.lock();
+ mLocked = true;
+ }
+
+ void unlock() {
+ assert(mLocked);
+ mLock.unlock();
+ mLocked = false;
+ }
+
+ bool isLocked() const { return mLocked; }
+
+ ~AutoLock() {
+ if (mLocked) {
+ mLock.unlock();
+ }
+ }
+
+private:
+ StaticLock& mLock;
+ bool mLocked = true;
+
+ friend class ConditionVariable;
+ // Don't allow move because this class has a non-movable object.
+ DISALLOW_COPY_AND_ASSIGN(AutoLock);
+};
+
+class AutoWriteLock {
+public:
+ AutoWriteLock(ReadWriteLock& lock) : mLock(lock) { mLock.lockWrite(); }
+
+ void lockWrite() {
+ assert(!mWriteLocked);
+ mLock.lockWrite();
+ mWriteLocked = true;
+ }
+
+ void unlockWrite() {
+ assert(mWriteLocked);
+ mLock.unlockWrite();
+ mWriteLocked = false;
+ }
+
+ ~AutoWriteLock() {
+ if (mWriteLocked) {
+ mLock.unlockWrite();
+ }
+ }
+
+private:
+ ReadWriteLock& mLock;
+ bool mWriteLocked = true;
+ // This class has a non-movable object.
+ DISALLOW_COPY_ASSIGN_AND_MOVE(AutoWriteLock);
+};
+
+class AutoReadLock {
+public:
+ AutoReadLock(ReadWriteLock& lock) : mLock(lock) { mLock.lockRead(); }
+
+ void lockRead() {
+ assert(!mReadLocked);
+ mLock.lockRead();
+ mReadLocked = true;
+ }
+
+ void unlockRead() {
+ assert(mReadLocked);
+ mLock.unlockRead();
+ mReadLocked = false;
+ }
+
+ ~AutoReadLock() {
+ if (mReadLocked) {
+ mLock.unlockRead();
+ }
+ }
+
+private:
+ ReadWriteLock& mLock;
+ bool mReadLocked = true;
+ // This class has a non-movable object.
+ DISALLOW_COPY_ASSIGN_AND_MOVE(AutoReadLock);
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/Lookup.h b/base/Lookup.h
new file mode 100644
index 0000000..a944590
--- /dev/null
+++ b/base/Lookup.h
@@ -0,0 +1,168 @@
+// Copyright 2016 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+#pragma once
+
+#include "base/TypeTraits.h"
+
+#include <initializer_list>
+#include <set>
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
+// A set of convenience functions for map and set lookups. They allow a simpler
+// syntax, e.g.
+// if (auto val = find(map, "key")) {
+// <process the value>
+// }
+// ... or
+// auto value = find(funcThatReturnsMap(), "other_key");
+// if (!value) ...
+//
+// Note: these don't work for multimaps, as there's no single value
+// to return (and, more importantly, as those are completely useless).
+
+namespace android {
+namespace base {
+
+// Helper predicates that check if the template argument is a map / set /
+// a mutlikey collection of any kind.
+// These are used as a constraints for the lookup functions to get better error
+// messages if the arguments don't support the map interface.
+template <class T>
+using is_any_map = std::integral_constant<
+ bool,
+ is_template_instantiation_of<T, std::map>::value ||
+ is_template_instantiation_of<T, std::unordered_map>::value>;
+
+template <class T>
+using is_any_set = std::integral_constant<
+ bool,
+ is_template_instantiation_of<T, std::set>::value ||
+ is_template_instantiation_of<T, std::unordered_set>::value>;
+
+template <class T>
+using is_any_multikey = std::integral_constant<
+ bool,
+ is_template_instantiation_of<T, std::multimap>::value ||
+ is_template_instantiation_of<T, std::unordered_multimap>::value ||
+ is_template_instantiation_of<T, std::multiset>::value ||
+ is_template_instantiation_of<T, std::unordered_multiset>::value>;
+
+template <class T, class = enable_if<is_any_map<T>>>
+const typename T::mapped_type* find(const T& map,
+ const typename T::key_type& key) {
+ const auto it = map.find(key);
+ if (it == map.end()) {
+ return nullptr;
+ }
+
+ return &it->second;
+}
+
+// Version that returns a modifiable value.
+template <class T, class = enable_if<is_any_map<T>>>
+typename T::mapped_type* find(T& map, const typename T::key_type& key) {
+ auto it = map.find(key);
+ if (it == map.end()) {
+ return nullptr;
+ }
+
+ return &it->second;
+}
+
+// Version with a default, returns a _copy_ because of the possible fallback
+// to a default - it might be destroyed after the call.
+template <class T,
+ class U = typename T::mapped_type,
+ class = enable_if_c<
+ is_any_map<T>::value &&
+ std::is_convertible<U, typename T::mapped_type>::value>>
+typename T::mapped_type findOrDefault(const T& map,
+ const typename T::key_type& key,
+ U&& defaultVal = {}) {
+ if (auto valPtr = find(map, key)) {
+ return *valPtr;
+ }
+ return std::forward<U>(defaultVal);
+}
+
+// Version that finds the first of the values passed in |keys| in the order they
+// are passed. E.g., the following code finds '2' as the first value in |keys|:
+// set<int> s = {1, 2, 3};
+// auto val = findFirstOf(s, {2, 1});
+// EXPECT_EQ(2, *val);
+template <class T, class = enable_if<is_any_map<T>>>
+const typename T::mapped_type* findFirstOf(
+ const T& map,
+ std::initializer_list<typename T::key_type> keys) {
+ for (const auto& key : keys) {
+ if (const auto valPtr = find(map, key)) {
+ return valPtr;
+ }
+ }
+ return nullptr;
+}
+
+template <class T, class = enable_if<is_any_map<T>>>
+typename T::mapped_type* findFirstOf(
+ T& map,
+ std::initializer_list<typename T::key_type> keys) {
+ for (const auto& key : keys) {
+ if (const auto valPtr = find(map, key)) {
+ return valPtr;
+ }
+ }
+ return nullptr;
+}
+
+// Version that finds first of the passed |key| values or returns the
+// |defaultVal| if none were found.
+template <class T,
+ class U,
+ class = enable_if_c<
+ is_any_map<T>::value &&
+ std::is_convertible<U, typename T::mapped_type>::value>>
+typename T::mapped_type findFirstOfOrDefault(
+ const T& map,
+ std::initializer_list<typename T::key_type> keys,
+ U&& defaultVal) {
+ if (const auto valPtr = findFirstOf(map, keys)) {
+ return *valPtr;
+ }
+ return std::forward<U>(defaultVal);
+}
+
+template <class T,
+ class = enable_if_c<is_any_map<T>::value || is_any_set<T>::value ||
+ is_any_multikey<T>::value>>
+bool contains(const T& c, const typename T::key_type& key) {
+ const auto it = c.find(key);
+ return it != c.end();
+}
+
+template <class T,
+ class = enable_if_c<is_any_map<T>::value || is_any_set<T>::value ||
+ is_any_multikey<T>::value>>
+bool containsAnyOf(const T& c,
+ std::initializer_list<typename T::key_type> keys) {
+ for (const auto& key : keys) {
+ if (contains(c, key)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/MemStream.cpp b/base/MemStream.cpp
index 6c5dbfb..d6dc641 100644
--- a/base/MemStream.cpp
+++ b/base/MemStream.cpp
@@ -19,6 +19,8 @@
#include <algorithm>
#include <utility>
+#include <string.h>
+
namespace android {
namespace base {
diff --git a/base/MessageChannel.cpp b/base/MessageChannel.cpp
new file mode 100644
index 0000000..ca29e2f
--- /dev/null
+++ b/base/MessageChannel.cpp
@@ -0,0 +1,121 @@
+// Copyright 2014 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 "base/MessageChannel.h"
+
+namespace android {
+namespace base {
+
+MessageChannelBase::MessageChannelBase(size_t capacity) : mCapacity(capacity) {}
+
+size_t MessageChannelBase::size() const {
+ AutoLock lock(mLock);
+ return mCount;
+}
+
+void MessageChannelBase::stop() {
+ android::base::AutoLock lock(mLock);
+ mStopped = true;
+ mCount = 0;
+ mCanRead.broadcast();
+ mCanWrite.broadcastAndUnlock(&lock);
+}
+
+bool MessageChannelBase::isStopped() const {
+ AutoLock lock(mLock);
+ return isStoppedLocked();
+}
+
+void MessageChannelBase::waitForEmpty() {
+ AutoLock lock(mLock);
+ while (mCount > 0) {
+ mCanWrite.wait(&lock);
+ }
+}
+
+size_t MessageChannelBase::beforeWrite() {
+ mLock.lock();
+ while (mCount >= mCapacity && !mStopped) {
+ mCanWrite.wait(&mLock);
+ }
+ // Return value is undefined if stopped, so let's save a branch and skip the
+ // check for it.
+ size_t result = mPos + mCount;
+ if (result >= mCapacity) {
+ result -= mCapacity;
+ }
+ return result;
+}
+
+Optional<size_t> MessageChannelBase::beforeTryWrite() {
+ mLock.lock();
+
+ if (mCount >= mCapacity || mStopped) {
+ return {};
+ }
+ size_t result = mPos + mCount;
+ if (result >= mCapacity) {
+ result -= mCapacity;
+ }
+ return result;
+}
+
+void MessageChannelBase::afterWrite(bool success) {
+ if (success) {
+ ++mCount;
+ }
+ mCanRead.signalAndUnlock(&mLock);
+}
+
+size_t MessageChannelBase::beforeRead() {
+ mLock.lock();
+ while (mCount == 0 && !mStopped) {
+ mCanRead.wait(&mLock);
+ }
+ return mPos; // return value is undefined if stopped, so let's save a branch
+}
+
+Optional<size_t> MessageChannelBase::beforeTryRead() {
+ mLock.lock();
+
+ if (mCount == 0 || mStopped) {
+ return {};
+ }
+ return mPos;
+}
+
+Optional<size_t> MessageChannelBase::beforeTimedRead(
+ uint64_t wallTimeUs) {
+ mLock.lock();
+
+ while (mCount == 0 && !mStopped) {
+ if (!mCanRead.timedWait(&mLock, wallTimeUs)) {
+ return {};
+ }
+ }
+ return mPos;
+}
+
+void MessageChannelBase::afterRead(bool success) {
+ if (success) {
+ if (++mPos == mCapacity) {
+ mPos = 0U;
+ }
+ --mCount;
+ }
+ mCanWrite.signalAndUnlock(&mLock);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/MessageChannel.h b/base/MessageChannel.h
new file mode 100644
index 0000000..22bc648
--- /dev/null
+++ b/base/MessageChannel.h
@@ -0,0 +1,208 @@
+// Copyright 2014 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.
+
+#pragma once
+
+#include "base/Optional.h"
+#include "base/ConditionVariable.h"
+#include "base/Lock.h"
+
+#include <utility>
+#include <stddef.h>
+
+namespace android {
+namespace base {
+
+// Base non-templated class used to reduce the amount of template
+// specialization.
+class MessageChannelBase {
+public:
+ // Get the current channel size
+ size_t size() const;
+
+ // Abort the currently pending operations and don't allow any other ones
+ void stop();
+
+ // Check if the channel is stopped.
+ bool isStopped() const;
+
+ // Block until the channel has no pending messages.
+ void waitForEmpty();
+
+protected:
+ // Constructor. |capacity| is the buffer capacity in messages.
+ MessageChannelBase(size_t capacity);
+
+ // Destructor.
+ ~MessageChannelBase() = default;
+
+ // Call this method in the sender thread before writing a new message.
+ // This returns the position of the available slot in the message array
+ // where to copy the new fixed-size message. After the copy, call
+ // afterWrite().
+ // If the channel is stopped, return value is undefined.
+ size_t beforeWrite();
+
+ // Same as beforeWrite(), but returns an empty optional if there was
+ // no room to write to instead of waiting for it.
+ // One still needs to call afterWrite() anyway.
+ Optional<size_t> beforeTryWrite();
+
+ // To be called after trying to write a new fixed-size message (which should
+ // happen after beforeWrite() or beforeTryWrite()).
+ // |success| must be true to indicate that a new item was added to the
+ // channel, or false otherwise (i.e. if the channel is stopped, or if
+ // beforeTryWrite() returned an empty optional).
+ void afterWrite(bool success);
+
+ // Call this method in the receiver thread before reading a new message.
+ // This returns the position in the message array where the new message
+ // can be read. Caller must process the message, then call afterRead().
+ // If the channel is stopped, return value is undefined.
+ size_t beforeRead();
+
+ // Same as beforeRead(), but returns an empty optional if there was
+ // no data to read instead of waiting for it.
+ // One still needs to call afterWrite() anyway.
+ Optional<size_t> beforeTryRead();
+
+ // Same as beforeRead(), but returns an empty optional if no data arrived
+ // by the |wallTimeUs| absolute time. One still needs to call
+ // afterWrite() anyway.
+ Optional<size_t> beforeTimedRead(uint64_t wallTimeUs);
+
+ // To be called after reading a fixed-size message from the channel (which
+ // must happen after beforeRead() or beforeTryRead()).
+ // |success| must be true to indicate that a message was read, or false
+ // otherwise (i.e. if the channel is stopped or if beforeTryRead() returned
+ // an empty optional).
+ void afterRead(bool success);
+
+ // A version of isStopped() that doesn't lock the channel but expects it
+ // to be locked by the caller.
+ bool isStoppedLocked() const { return mStopped; }
+
+private:
+ size_t mPos = 0;
+ size_t mCapacity;
+ size_t mCount = 0;
+ bool mStopped = false;
+ mutable Lock mLock; // Mutable to allow const members to lock it.
+ ConditionVariable mCanRead;
+ ConditionVariable mCanWrite;
+};
+
+// Helper class used to implement an uni-directional IPC channel between
+// two threads. The channel can be used to send fixed-size messages of type
+// |T|, with an internal buffer size of |CAPACITY| items. All calls are
+// blocking.
+//
+// Usage is pretty straightforward:
+//
+// - From the sender thread, call send(msg);
+// - From the receiver thread, call receive(&msg);
+// - If you want to stop the IPC, call stop();
+template <typename T, size_t CAPACITY>
+class MessageChannel : public MessageChannelBase {
+public:
+ MessageChannel() : MessageChannelBase(CAPACITY) {}
+
+ bool send(const T& msg) {
+ const size_t pos = beforeWrite();
+ const bool res = !isStoppedLocked();
+ if (res) {
+ mItems[pos] = msg;
+ }
+ afterWrite(res);
+ return res;
+ }
+
+ bool send(T&& msg) {
+ const size_t pos = beforeWrite();
+ const bool res = !isStoppedLocked();
+ if (res) {
+ mItems[pos] = std::move(msg);
+ }
+ afterWrite(res);
+ return res;
+ }
+
+ bool trySend(const T& msg) {
+ const auto pos = beforeTryWrite();
+ if (pos) {
+ mItems[*pos] = msg;
+ }
+ afterWrite(pos);
+ return pos;
+ }
+
+ bool trySend(T&& msg) {
+ const auto pos = beforeTryWrite();
+ if (pos) {
+ mItems[*pos] = std::move(msg);
+ }
+ afterWrite(pos);
+ return pos;
+ }
+
+ bool receive(T* msg) {
+ const size_t pos = beforeRead();
+ const bool res = !isStoppedLocked();
+ if (res) {
+ *msg = std::move(mItems[pos]);
+ }
+ afterRead(res);
+ return res;
+ }
+
+ Optional<T> receive() {
+ const size_t pos = beforeRead();
+ if (!isStoppedLocked()) {
+ Optional<T> msg(std::move(mItems[pos]));
+ afterRead(true);
+ return msg;
+ } else {
+ afterRead(false);
+ return {};
+ }
+ }
+
+ bool tryReceive(T* msg) {
+ const auto pos = beforeTryRead();
+ if (pos) {
+ *msg = std::move(mItems[*pos]);
+ }
+ afterRead(pos);
+ return pos;
+ }
+
+ Optional<T> timedReceive(uint64_t wallTimeUs) {
+ const auto pos = beforeTimedRead(wallTimeUs);
+ if (pos && !isStoppedLocked()) {
+ Optional<T> res(std::move(mItems[*pos]));
+ afterRead(true);
+ return res;
+ }
+ afterRead(false);
+ return {};
+ }
+
+ constexpr size_t capacity() const { return CAPACITY; }
+
+private:
+ T mItems[CAPACITY];
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/PathUtils.cpp b/base/PathUtils.cpp
new file mode 100644
index 0000000..50e09b3
--- /dev/null
+++ b/base/PathUtils.cpp
@@ -0,0 +1,340 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+#include "base/PathUtils.h"
+
+#include <string.h> // for size_t, strncmp
+#include <iterator> // for reverse_iterator, operator!=
+#include <numeric> // for accumulate
+#include <type_traits> // for enable_if<>::type
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#ifdef _WIN32
+#include "Win32UnicodeString.h"
+#endif
+
+static inline bool sIsEmpty(const char* str) {
+ return !str || str[0] == '\0';
+}
+
+namespace android {
+namespace base {
+
+const char* const PathUtils::kExeNameSuffixes[kHostTypeCount] = {"", ".exe"};
+
+const char* const PathUtils::kExeNameSuffix =
+ PathUtils::kExeNameSuffixes[PathUtils::HOST_TYPE];
+
+std::string PathUtils::toExecutableName(const char* baseName,
+ HostType hostType) {
+ return static_cast<std::string>(baseName).append(
+ kExeNameSuffixes[hostType]);
+}
+
+// static
+bool PathUtils::isDirSeparator(int ch, HostType hostType) {
+ return (ch == '/') || (hostType == HOST_WIN32 && ch == '\\');
+}
+
+// static
+bool PathUtils::isPathSeparator(int ch, HostType hostType) {
+ return (hostType == HOST_POSIX && ch == ':') ||
+ (hostType == HOST_WIN32 && ch == ';');
+}
+
+// static
+size_t PathUtils::rootPrefixSize(const std::string& path, HostType hostType) {
+ if (path.empty()) return 0;
+
+ if (hostType != HOST_WIN32)
+ return (path[0] == '/') ? 1U : 0U;
+
+ size_t result = 0;
+ if (path[1] == ':') {
+ int ch = path[0];
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
+ result = 2U;
+ } else if (!strncmp(path.c_str(), "\\\\.\\", 4) ||
+ !strncmp(path.c_str(), "\\\\?\\", 4)) {
+ // UNC prefixes.
+ return 4U;
+ } else if (isDirSeparator(path[0], hostType)) {
+ result = 1;
+ if (isDirSeparator(path[1], hostType)) {
+ result = 2;
+ while (path[result] && !isDirSeparator(path[result], HOST_WIN32))
+ result++;
+ }
+ }
+ if (result && path[result] && isDirSeparator(path[result], HOST_WIN32))
+ result++;
+
+ return result;
+}
+
+// static
+bool PathUtils::isAbsolute(const char* path, HostType hostType) {
+ size_t prefixSize = rootPrefixSize(path, hostType);
+ if (!prefixSize) {
+ return false;
+ }
+ if (hostType != HOST_WIN32) {
+ return true;
+ }
+ return isDirSeparator(path[prefixSize - 1], HOST_WIN32);
+}
+
+// static
+std::string PathUtils::removeTrailingDirSeparator(const char* path,
+ HostType hostType) {
+ size_t pathLen = strlen(path);
+ // NOTE: Don't remove initial path separator for absolute paths.
+ while (pathLen > 1U && isDirSeparator(path[pathLen - 1U], hostType)) {
+ pathLen--;
+ }
+ return std::string(path, pathLen);
+}
+
+// static
+std::string PathUtils::addTrailingDirSeparator(const char* path,
+ HostType hostType) {
+ std::string result = path;
+ if (result.size() > 0 && !isDirSeparator(result[result.size() - 1U])) {
+ result += getDirSeparator(hostType);
+ }
+ return result;
+}
+
+// static
+bool PathUtils::split(const char* path,
+ HostType hostType,
+ const char** dirName,
+ const char** baseName) {
+ if (sIsEmpty(path)) {
+ return false;
+ }
+
+ // If there is a trailing directory separator, return an error.
+ size_t end = strlen(path);
+ if (isDirSeparator(path[end - 1U], hostType)) {
+ return false;
+ }
+
+ // Find last separator.
+ size_t prefixLen = rootPrefixSize(path, hostType);
+ size_t pos = end;
+ while (pos > prefixLen && !isDirSeparator(path[pos - 1U], hostType)) {
+ pos--;
+ }
+
+ // Handle common case.
+ if (pos > prefixLen) {
+ if (dirName) {
+ *dirName = path;
+ }
+ if (baseName) {
+ *baseName = path + pos;
+ }
+ return true;
+ }
+
+ // If there is no directory separator, the path is a single file name.
+ if (dirName) {
+ if (!prefixLen) {
+ *dirName = ".";
+ } else {
+ *dirName = path;
+ }
+ }
+ if (baseName) {
+ *baseName = path + prefixLen;
+ }
+ return true;
+}
+
+// static
+std::string PathUtils::join(const std::string& path1,
+ const std::string& path2,
+ HostType hostType) {
+ if (path1.empty()) {
+ return path2;
+ }
+ if (path2.empty()) {
+ return path1;
+ }
+ if (isAbsolute(path2.c_str(), hostType)) {
+ return path2;
+ }
+ size_t prefixLen = rootPrefixSize(path1, hostType);
+ std::string result(path1);
+ size_t end = result.size();
+ if (end > prefixLen && !isDirSeparator(result[end - 1U], hostType)) {
+ result += getDirSeparator(hostType);
+ }
+ result += path2;
+ return result;
+}
+
+// static
+template <class String>
+std::vector<String> PathUtils::decompose(const String& path,
+ HostType hostType) {
+ std::vector<String> result;
+ if (path.empty())
+ return result;
+
+ size_t prefixLen = rootPrefixSize(path, hostType);
+ auto it = path.begin();
+ if (prefixLen) {
+ result.emplace_back(it, it + prefixLen);
+ it += prefixLen;
+ }
+ for (;;) {
+ auto p = it;
+ while (*p && !isDirSeparator(*p, hostType))
+ p++;
+ if (p > it) {
+ result.emplace_back(it, p);
+ }
+ if (!*p) {
+ break;
+ }
+ it = p + 1;
+ }
+ return result;
+}
+
+std::vector<std::string> PathUtils::decompose(std::string&& path,
+ HostType hostType) {
+ return decompose<std::string>(path, hostType);
+}
+
+template <class String>
+std::string PathUtils::recompose(const std::vector<String>& components,
+ HostType hostType) {
+ if (components.empty()) {
+ return {};
+ }
+
+ const char dirSeparator = getDirSeparator(hostType);
+ std::string result;
+ // To reduce memory allocations, compute capacity before doing the
+ // real append.
+ const size_t capacity =
+ components.size() - 1 +
+ std::accumulate(components.begin(), components.end(), size_t(0),
+ [](size_t val, const String& next) {
+ return val + next.size();
+ });
+
+ result.reserve(capacity);
+ bool addSeparator = false;
+ for (size_t n = 0; n < components.size(); ++n) {
+ const auto& component = components[n];
+ if (addSeparator)
+ result += dirSeparator;
+ addSeparator = true;
+ if (n == 0) {
+ size_t prefixLen = rootPrefixSize(component, hostType);
+ if (prefixLen == component.size()) {
+ addSeparator = false;
+ }
+ }
+ result += component;
+ }
+ return result;
+}
+
+// static
+std::string PathUtils::recompose(const std::vector<std::string>& components,
+ HostType hostType) {
+ return recompose<std::string>(components, hostType);
+}
+
+// static
+template <class String>
+void PathUtils::simplifyComponents(std::vector<String>* components) {
+ std::vector<String> stack;
+ for (auto& component : *components) {
+ if (component == ".") {
+ // Ignore any instance of '.' from the list.
+ continue;
+ }
+ if (component == "..") {
+ // Handling of '..' is specific: if there is a item on the
+ // stack that is not '..', then remove it, otherwise push
+ // the '..'.
+ if (!stack.empty() && stack.back() != "..") {
+ stack.pop_back();
+ } else {
+ stack.push_back(std::move(component));
+ }
+ continue;
+ }
+ // If not a '..', just push on the stack.
+ stack.push_back(std::move(component));
+ }
+ if (stack.empty()) {
+ stack.push_back(".");
+ }
+ components->swap(stack);
+}
+
+void PathUtils::simplifyComponents(std::vector<std::string>* components) {
+ simplifyComponents<std::string>(components);
+}
+
+#ifdef _WIN32
+
+// Return |path| as a Unicode string, while discarding trailing separators.
+Win32UnicodeString win32Path(const char* path) {
+ Win32UnicodeString wpath(path);
+ // Get rid of trailing directory separators, Windows doesn't like them.
+ size_t size = wpath.size();
+ while (size > 0U &&
+ (wpath[size - 1U] == L'\\' || wpath[size - 1U] == L'/')) {
+ size--;
+ }
+ if (size < wpath.size()) {
+ wpath.resize(size);
+ }
+ return wpath;
+}
+
+static int GetWin32Mode(int mode) {
+ // Convert |mode| to win32 permission bits.
+ int win32mode = 0x0;
+
+ if ((mode & R_OK) || (mode & X_OK)) {
+ win32mode |= 0x4;
+ }
+ if (mode & W_OK) {
+ win32mode |= 0x2;
+ }
+
+ return win32mode;
+}
+
+#endif
+
+bool pathExists(const char* path) {
+#ifdef _WIN32
+ return _waccess(win32Path(path).c_str(), GetWin32Mode(F_OK));
+#else
+ return 0 == access(path, F_OK);
+#endif
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/PathUtils.h b/base/PathUtils.h
new file mode 100644
index 0000000..1ec3f40
--- /dev/null
+++ b/base/PathUtils.h
@@ -0,0 +1,297 @@
+// Copyright 2020 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.
+
+#pragma once
+
+#include <stddef.h> // for size_t
+#include <string> // for string, basic_string
+#include <utility> // for move, forward
+#include <vector> // for vector
+
+#include "base/Optional.h" // for Optional
+
+#ifdef __APPLE__
+
+#define LIBSUFFIX ".dylib"
+
+#else
+
+#ifdef _WIN32
+#include "base/Win32UnicodeString.h"
+#define LIBSUFFIX ".dll"
+
+#else
+
+#define LIBSUFFIX ".so"
+
+#endif // !_WIN32 (linux)
+
+#endif // !__APPLE__
+
+namespace android {
+namespace base {
+
+// Utility functions to manage file paths. None of these should touch the
+// file system. All methods must be static.
+class PathUtils {
+public:
+ // An enum listing the supported host file system types.
+ // HOST_POSIX means a Posix-like file system.
+ // HOST_WIN32 means a Windows-like file system.
+ // HOST_TYPE means the current host type (one of the above).
+ // NOTE: If you update this list, modify kHostTypeCount below too.
+ enum HostType {
+ HOST_POSIX = 0,
+ HOST_WIN32 = 1,
+#ifdef _WIN32
+ HOST_TYPE = HOST_WIN32,
+#else
+ HOST_TYPE = HOST_POSIX,
+#endif
+ };
+
+ // The number of distinct items in the HostType enumeration above.
+ static const int kHostTypeCount = 2;
+
+ // Suffixes for an executable file (.exe on Windows, empty otherwise)
+ static const char* const kExeNameSuffixes[kHostTypeCount];
+
+ // Suffixe for an executable file on the current platform
+ static const char* const kExeNameSuffix;
+
+ // Returns the executable name for a base name |baseName|
+ static std::string toExecutableName(const char* baseName, HostType hostType);
+
+ static std::string toExecutableName(const char* baseName) {
+ return toExecutableName(baseName, HOST_TYPE);
+ }
+
+ // Return true if |ch| is a directory separator for a given |hostType|.
+ static bool isDirSeparator(int ch, HostType hostType);
+
+ // Return true if |ch| is a directory separator for the current platform.
+ static bool isDirSeparator(int ch) {
+ return isDirSeparator(ch, HOST_TYPE);
+ }
+
+ // Return true if |ch| is a path separator for a given |hostType|.
+ static bool isPathSeparator(int ch, HostType hostType);
+
+ // Return true if |ch| is a path separator for the current platform.
+ static bool isPathSeparator(int ch) {
+ return isPathSeparator(ch, HOST_TYPE);
+ }
+
+ // Return the directory separator character for a given |hostType|
+ static char getDirSeparator(HostType hostType) {
+ return (hostType == HOST_WIN32) ? '\\' : '/';
+ }
+
+ // Remove trailing separators from a |path| string, for a given |hostType|.
+ static std::string removeTrailingDirSeparator(const char* path,
+ HostType hostType);
+
+ // Remove trailing separators from a |path| string for the current host.
+ static std::string removeTrailingDirSeparator(const char* path) {
+ return removeTrailingDirSeparator(path, HOST_TYPE);
+ }
+
+ // Add a trailing separator if needed.
+ static std::string addTrailingDirSeparator(const char* path,
+ HostType hostType);
+
+ // Add a trailing separator if needed.
+ static std::string addTrailingDirSeparator(const char* path) {
+ return addTrailingDirSeparator(path, HOST_TYPE);
+ }
+
+ // If |path| starts with a root prefix, return its size in bytes, or
+ // 0 otherwise. The definition of valid root prefixes depends on the
+ // value of |hostType|. For HOST_POSIX, it's any path that begins
+ // with a slash (/). For HOST_WIN32, the following prefixes are
+ // recognized:
+ // <drive>:
+ // <drive>:<sep>
+ // <sep><sep>volumeName<sep>
+ static size_t rootPrefixSize(const std::string& path, HostType hostType);
+
+ // Return the root prefix for the current platform. See above for
+ // documentation.
+ static size_t rootPrefixSize(const char* path) {
+ return rootPrefixSize(path, HOST_TYPE);
+ }
+
+ // Return true iff |path| is an absolute path for a given |hostType|.
+ static bool isAbsolute(const char* path, HostType hostType);
+
+ // Return true iff |path| is an absolute path for the current host.
+ static bool isAbsolute(const char* path) {
+ return isAbsolute(path, HOST_TYPE);
+ }
+
+ // Split |path| into a directory name and a file name. |dirName| and
+ // |baseName| are optional pointers to strings that will receive the
+ // corresponding components on success. |hostType| is a host type.
+ // Return true on success, or false on failure.
+ // Note that unlike the Unix 'basename' command, the command will fail
+ // if |path| ends with directory separator or is a single root prefix.
+ // Windows root prefixes are fully supported, which means the following:
+ //
+ // / -> error.
+ // /foo -> '/' + 'foo'
+ // foo -> '.' + 'foo'
+ // <drive>: -> error.
+ // <drive>:foo -> '<drive>:' + 'foo'
+ // <drive>:\foo -> '<drive>:\' + 'foo'
+ //
+ static bool split(const char* path,
+ HostType hostType,
+ const char** dirName,
+ const char** baseName);
+
+ // A variant of split() for the current process' host type.
+ static bool split(const char* path,
+ const char** dirName,
+ const char** baseName) {
+ return split(path, HOST_TYPE, dirName, baseName);
+ }
+
+ // Join two path components together. Note that if |path2| is an
+ // absolute path, this function returns a copy of |path2|, otherwise
+ // the result will be the concatenation of |path1| and |path2|, if
+ // |path1| doesn't end with a directory separator, a |hostType| specific
+ // one will be inserted between the two paths in the result.
+ static std::string join(const std::string& path1,
+ const std::string& path2,
+ HostType hostType);
+
+ // A variant of join() for the current process' host type.
+ static std::string join(const std::string& path1, const std::string& path2) {
+ return join(path1, path2, HOST_TYPE);
+ }
+
+ // A convenience function to join a bunch of paths at once
+ template <class... Paths>
+ static std::string join(const std::string& path1,
+ const std::string& path2,
+ Paths&&... paths) {
+ return join(path1, join(path2, std::forward<Paths>(paths)...));
+ }
+
+ // Decompose |path| into individual components. If |path| has a root
+ // prefix, it will always be the first component. I.e. for Posix
+ // systems this will be '/' (for absolute paths). For Win32 systems,
+ // it could be 'C:" (for a path relative to a root volume) or "C:\"
+ // for an absolute path from volume C).,
+ // On success, return true and sets |out| to a vector of strings,
+ // each one being a path component (prefix or subdirectory or file
+ // name). Directory separators do not appear in components, except
+ // for the root prefix, if any.
+ static std::vector<std::string> decompose(std::string&& path,
+ HostType hostType);
+
+ template <class String>
+ static std::vector<String> decompose(const String& path,
+ HostType hostType);
+
+ // Decompose |path| into individual components for the host platform.
+ // See comments above for more details.
+ static std::vector<std::string> decompose(std::string&& path) {
+ return decompose(std::move(path), HOST_TYPE);
+ }
+
+ // Recompose a path from individual components into a file path string.
+ // |components| is a vector of strings, and |hostType| the target
+ // host type to use. Return a new file path string. Note that if the
+ // first component is a root prefix, it will be kept as is, i.e.:
+ // [ 'C:', 'foo' ] -> 'C:foo' on Win32, but not Posix where it will
+ // be 'C:/foo'.
+ static std::string recompose(const std::vector<std::string>& components,
+ HostType hostType);
+ template <class String>
+ static std::string recompose(const std::vector<String>& components,
+ HostType hostType);
+
+ // Recompose a path from individual components into a file path string
+ // for the current host. |components| is a vector os strings.
+ // Returns a new file path string.
+ template <class String>
+ static std::string recompose(const std::vector<String>& components) {
+ return PathUtils::recompose(components, HOST_TYPE);
+ }
+
+ // Given a list of components returned by decompose(), simplify it
+ // by removing instances of '.' and '..' when that makes sense.
+ // Note that it is not possible to simplify initial instances of
+ // '..', i.e. "foo/../../bar" -> "../bar"
+ static void simplifyComponents(std::vector<std::string>* components);
+ template <class String>
+ static void simplifyComponents(std::vector<String>* components);
+
+ // Returns a version of |path| that is relative to |base|.
+ // This can be useful for converting absolute paths to
+ // relative paths given |base|.
+ // Example:
+ // |base|: C:\Users\foo
+ // |path|: C:\Users\foo\AppData\Local\Android\Sdk
+ // would give
+ // AppData\Local\Android\Sdk.
+ // If |base| is not a prefix of |path|, fails by returning
+ // the original |path| unmodified.
+ static std::string relativeTo(const char* base, const char* path, HostType hostType);
+ static std::string relativeTo(const char* base, const char* path) {
+ return relativeTo(base, path, HOST_TYPE);
+ }
+
+ static Optional<std::string> pathWithoutDirs(const char* name);
+ static Optional<std::string> pathToDir(const char* name);
+
+ // Replaces the entries ${xx} with the value of the environment variable
+ // xx if it exists. Returns kNullopt if the environment variable is
+ // not set or empty.
+ static Optional<std::string> pathWithEnvSubstituted(const char* path);
+
+ // Replaces the entries ${xx} with the value of the environment variable
+ // xx if it exists. Returns kNullopt if the environment variable is
+ // not set or empty.
+ static Optional<std::string> pathWithEnvSubstituted(std::vector<std::string> decomposedPath);
+
+#ifdef _WIN32
+ static Win32UnicodeString asUnicodePath(const char* path) { return Win32UnicodeString(path); }
+#else
+ static std::string asUnicodePath(const char* path) { return path; }
+#endif
+};
+
+// Useful shortcuts to avoid too much typing.
+static const PathUtils::HostType kHostPosix = PathUtils::HOST_POSIX;
+static const PathUtils::HostType kHostWin32 = PathUtils::HOST_WIN32;
+static const PathUtils::HostType kHostType = PathUtils::HOST_TYPE;
+
+template <class... Paths>
+std::string pj(const std::string& path1,
+ const std::string& path2,
+ Paths&&... paths) {
+ return PathUtils::join(path1,
+ pj(path2, std::forward<Paths>(paths)...));
+}
+
+std::string pj(const std::string& path1, const std::string& path2);
+
+std::string pj(const std::vector<std::string>& paths);
+
+bool pathExists(const char* path);
+
+} // namespace base
+} // namespace android
diff --git a/base/SharedLibrary.cpp b/base/SharedLibrary.cpp
new file mode 100644
index 0000000..59db81c
--- /dev/null
+++ b/base/SharedLibrary.cpp
@@ -0,0 +1,295 @@
+// Copyright (C) 2014 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 "base/SharedLibrary.h"
+#include "base/PathUtils.h"
+
+#include <functional>
+#include <vector>
+
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef _WIN32
+#include <dlfcn.h>
+#include <stdlib.h>
+#endif
+
+#define GL_LOG(...)
+
+using android::base::PathUtils;
+
+namespace android {
+namespace base {
+
+class LibrarySearchPaths {
+public:
+ LibrarySearchPaths() = default;
+
+ void addPath(const char* path) {
+ mPaths.push_back(path);
+ }
+
+ void forEachPath(std::function<void(const std::string&)> func) {
+ for (const auto& path: mPaths) {
+ func(path);
+ }
+ }
+
+private:
+ std::vector<std::string> mPaths;
+};
+
+LibrarySearchPaths* sSearchPaths() {
+ static LibrarySearchPaths* paths = new LibrarySearchPaths;
+ return paths;
+}
+
+SharedLibrary::LibraryMap SharedLibrary::s_libraryMap = LibraryMap();
+
+// static
+SharedLibrary* SharedLibrary::open(const char* libraryName) {
+ GL_LOG("SharedLibrary::open for [%s]\n", libraryName);
+ char error[1];
+ return open(libraryName, error, sizeof(error));
+}
+
+SharedLibrary* SharedLibrary::open(const char* libraryName,
+ char* error,
+ size_t errorSize) {
+ auto lib = s_libraryMap.find(libraryName);
+
+ if (lib == s_libraryMap.end()) {
+ GL_LOG("SharedLibrary::open for [%s]: not found in map, open for the first time\n", libraryName);
+ SharedLibrary* load = do_open(libraryName, error, errorSize);
+ if (load != nullptr) {
+ s_libraryMap[libraryName] = std::move(
+ std::unique_ptr<SharedLibrary, SharedLibrary::Deleter>(
+ load));
+ }
+ return load;
+ }
+
+ return lib->second.get();
+}
+
+#ifdef _WIN32
+
+// static
+SharedLibrary* SharedLibrary::do_open(const char* libraryName,
+ char* error,
+ size_t errorSize) {
+ GL_LOG("SharedLibrary::open for [%s] (win32): call LoadLibrary\n", libraryName);
+ HMODULE lib = LoadLibrary(libraryName);
+
+ // Try a bit harder to find the shared library if we cannot find it.
+ if (!lib) {
+ GL_LOG("SharedLibrary::open for [%s] can't find in default path. Searching alternatives...\n",
+ libraryName);
+ sSearchPaths()->forEachPath([&lib, libraryName](const std::string& path) {
+ if (!lib) {
+ auto libName = PathUtils::join(path, libraryName);
+ GL_LOG("SharedLibrary::open for [%s]: trying [%s]\n",
+ libraryName, libName.c_str());
+ lib = LoadLibrary(libName.c_str());
+ GL_LOG("SharedLibrary::open for [%s]: trying [%s]. found? %d\n",
+ libraryName, libName.c_str(), lib != nullptr);
+ }
+ });
+ }
+
+ if (lib) {
+ constexpr size_t kMaxPathLength = 2048;
+ char fullPath[kMaxPathLength];
+ GetModuleFileNameA(lib, fullPath, kMaxPathLength);
+ GL_LOG("SharedLibrary::open succeeded for [%s]. File name: [%s]\n",
+ libraryName, fullPath);
+ return new SharedLibrary(lib);
+ }
+
+ if (errorSize == 0) {
+ GL_LOG("SharedLibrary::open for [%s] failed, but no error\n",
+ libraryName);
+ return NULL;
+ }
+
+ // Convert error into human-readable message.
+ DWORD errorCode = ::GetLastError();
+ LPSTR message = NULL;
+ size_t messageLen = FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ errorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR) &message,
+ 0,
+ NULL);
+
+ int ret = snprintf(error, errorSize, "%.*s", (int)messageLen, message);
+ if (ret < 0 || ret == static_cast<int>(errorSize)) {
+ // snprintf() on Windows doesn't behave as expected by C99,
+ // this path is to ensure that the result is always properly
+ // zero-terminated.
+ ret = static_cast<int>(errorSize - 1);
+ error[ret] = '\0';
+ }
+ // Remove any trailing \r\n added by FormatMessage
+ if (ret > 0 && error[ret - 1] == '\n') {
+ error[--ret] = '\0';
+ }
+ if (ret > 0 && error[ret - 1] == '\r') {
+ error[--ret] = '\0';
+ }
+ GL_LOG("Failed to load [%s]. Error string: [%s]\n",
+ libraryName, error);
+
+ return NULL;
+}
+
+SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {}
+
+SharedLibrary::~SharedLibrary() {
+ if (mLib) {
+ // BUG: 66013149
+ // In windows it sometimes hang on exit when destroying s_libraryMap.
+ // Let's skip freeing the library, since pretty much the only situation
+ // we need to do it, is on exit.
+ //FreeLibrary(mLib);
+ }
+}
+
+SharedLibrary::FunctionPtr SharedLibrary::findSymbol(
+ const char* symbolName) const {
+ if (!mLib || !symbolName) {
+ return NULL;
+ }
+ return reinterpret_cast<FunctionPtr>(
+ GetProcAddress(mLib, symbolName));
+}
+
+#else // !_WIN32
+
+// static
+SharedLibrary* SharedLibrary::do_open(const char* libraryName,
+ char* error,
+ size_t errorSize) {
+ GL_LOG("SharedLibrary::open for [%s] (posix): begin\n", libraryName);
+
+ const char* libPath = libraryName;
+ char* path = NULL;
+
+ const char* libBaseName = strrchr(libraryName, '/');
+ if (!libBaseName) {
+ libBaseName = libraryName;
+ }
+
+ if (!strchr(libBaseName, '.')) {
+ // There is no extension in this library name, so append one.
+#ifdef __APPLE__
+ static const char kDllExtension[] = ".dylib";
+#else
+ static const char kDllExtension[] = ".so";
+#endif
+ size_t pathLen = strlen(libraryName) + sizeof(kDllExtension);
+ path = static_cast<char*>(malloc(pathLen));
+ snprintf(path, pathLen, "%s%s", libraryName, kDllExtension);
+ libPath = path;
+ }
+
+ dlerror(); // clear error.
+
+#ifdef __APPLE__
+ // On OSX, some libraries don't include an extension (notably OpenGL)
+ // On OSX we try to open |libraryName| first. If that doesn't exist,
+ // we try |libraryName|.dylib
+ GL_LOG("SharedLibrary::open for [%s] (posix,darwin): call dlopen\n", libraryName);
+ void* lib = dlopen(libraryName, RTLD_NOW);
+ if (lib == NULL) {
+ GL_LOG("SharedLibrary::open for [%s] (posix,darwin): failed, "
+ "try again with [%s]\n", libraryName, libPath);
+ lib = dlopen(libPath, RTLD_NOW);
+
+ sSearchPaths()->forEachPath([&lib, libraryName, libPath](const std::string& path) {
+ if (!lib) {
+ auto libName = PathUtils::join(path, libraryName);
+ GL_LOG("SharedLibrary::open for [%s] (posix,darwin): still failed, "
+ "try [%s]\n", libraryName, libName.c_str());
+ lib = dlopen(libName.c_str(), RTLD_NOW);
+ if (!lib) {
+ auto libPathName = PathUtils::join(path, libPath);
+ GL_LOG("SharedLibrary::open for [%s] (posix,darwin): still failed, "
+ "try [%s]\n", libraryName, libPathName.c_str());
+ lib = dlopen(libPathName.c_str(), RTLD_NOW);
+ }
+ }
+ });
+ }
+#else
+ GL_LOG("SharedLibrary::open for [%s] (posix,linux): call dlopen on [%s]\n",
+ libraryName, libPath);
+ void* lib = dlopen(libPath, RTLD_NOW);
+#endif
+
+ sSearchPaths()->forEachPath([&lib, libPath, libraryName](const std::string& path) {
+ if (!lib) {
+ auto libPathName = PathUtils::join(path, libPath);
+ GL_LOG("SharedLibrary::open for [%s] (posix): try again with %s\n",
+ libraryName,
+ libPathName.c_str());
+ lib = dlopen(libPathName.c_str(), RTLD_NOW);
+ }
+ });
+
+ if (path) {
+ free(path);
+ }
+
+ if (lib) {
+ return new SharedLibrary(lib);
+ }
+
+ snprintf(error, errorSize, "%s", dlerror());
+ GL_LOG("SharedLibrary::open for [%s] failed (posix). dlerror: [%s]\n",
+ libraryName, error);
+ return NULL;
+}
+
+SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {}
+
+SharedLibrary::~SharedLibrary() {
+ if (mLib) {
+ dlclose(mLib);
+ }
+}
+
+SharedLibrary::FunctionPtr SharedLibrary::findSymbol(
+ const char* symbolName) const {
+ if (!mLib || !symbolName) {
+ return NULL;
+ }
+ return reinterpret_cast<FunctionPtr>(dlsym(mLib, symbolName));
+}
+
+#endif // !_WIN32
+
+// static
+void SharedLibrary::addLibrarySearchPath(const char* path) {
+ sSearchPaths()->addPath(path);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/SharedLibrary.h b/base/SharedLibrary.h
new file mode 100644
index 0000000..c9e5c8d
--- /dev/null
+++ b/base/SharedLibrary.h
@@ -0,0 +1,129 @@
+// Copyright (C) 2014 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 EMUGL_COMMON_SHARED_LIBRARY_H
+#define EMUGL_COMMON_SHARED_LIBRARY_H
+
+#include <stddef.h>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#ifdef _MSC_VER
+# ifdef BUILDING_EMUGL_COMMON_SHARED
+# define EMUGL_COMMON_API __declspec(dllexport)
+# else
+# define EMUGL_COMMON_API __declspec(dllimport)
+#endif
+#else
+# define EMUGL_COMMON_API
+#endif
+
+namespace android {
+namespace base {
+
+// A class used to open a platform-specific shared library, and probe
+// it for symbols. Usage is the following:
+//
+// // Open the library.
+// SharedLibrary* library = SharedLibrary::open("libFoo");
+// if (!library) {
+// ... could not find / open library!
+// }
+//
+// //Probe for function symbol.
+// FunctionPtr my_func = library->findSymbol("my_func");
+//
+// A shared library will be unloaded on program exit.
+class EMUGL_COMMON_API SharedLibrary {
+private:
+ struct Deleter {
+ void operator()(SharedLibrary* lib) const { delete lib; }
+ };
+
+public:
+ typedef std::unordered_map<
+ std::string,
+ std::unique_ptr<SharedLibrary, SharedLibrary::Deleter>>
+ LibraryMap;
+
+ // Open a given library. If |libraryName| has no extension, a
+ // platform-appropriate extension is added and that path is opened.
+ // If the |libraryName| has an extension, that form is opened.
+ //
+ // On OSX, some libraries don't include an extension (notably OpenGL)
+ // On OSX we try to open |libraryName| first. If that doesn't exist,
+ // we try |libraryName|.dylib
+ //
+ // On success, returns a new SharedLibrary instance that must be
+ // deleted by the caller.
+ static SharedLibrary* open(const char* libraryName);
+
+ // A variant of open() that can report a human-readable error if loading
+ // the library fails. |error| is a caller-provided buffer of |errorSize|
+ // bytes that will be filled with snprintf() and always zero terminated.
+ //
+ // On success, return a new SharedLibrary instance, and do not touch
+ // the content of |error|. On failure, return NULL, and sets the content
+ // of |error|.
+ static SharedLibrary* open(const char* libraryName,
+ char* error,
+ size_t errorSize);
+
+ // Adds an extra path to search for libraries.
+ static void addLibrarySearchPath(const char* path);
+
+ // Generic function pointer type, for values returned by the
+ // findSymbol() method.
+ typedef void (*FunctionPtr)(void);
+
+ // Probe a given SharedLibrary instance to find a symbol named
+ // |symbolName| in it. Return its address as a FunctionPtr, or
+ // NULL if the symbol is not found.
+ FunctionPtr findSymbol(const char* symbolName) const;
+
+private:
+
+ static LibraryMap s_libraryMap;
+
+ static SharedLibrary* do_open(const char* libraryName,
+ char* error,
+ size_t errorSize);
+#ifdef _WIN32
+ typedef HMODULE HandleType;
+#else
+ typedef void* HandleType;
+#endif
+
+ // Constructor intentionally hidden.
+ SharedLibrary(HandleType);
+
+ // Closes an existing SharedLibrary hidden so nobody
+ // starts accidently cleaning up these libraries.
+ ~SharedLibrary();
+
+
+ HandleType mLib;
+};
+
+# define EMUGL_LIBNAME(name) "lib" name
+
+} // namespace base
+} // namespace android
+
+#endif // EMUGL_COMMON_SHARED_LIBRARY_H
diff --git a/base/StaticMap.h b/base/StaticMap.h
new file mode 100644
index 0000000..a30127f
--- /dev/null
+++ b/base/StaticMap.h
@@ -0,0 +1,83 @@
+// 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.
+
+#include "base/Optional.h"
+#include "base/Lock.h"
+
+#include <functional>
+#include <unordered_map>
+
+namespace android {
+namespace base {
+
+// Static map class for use with LazyInstance or in global structures
+// as a process-wide registry of something. Safe for concurrent accress.
+template <class K, class V>
+class StaticMap {
+public:
+ StaticMap() = default;
+
+ void set(const K& key, const V& value) {
+ AutoLock lock(mLock);
+ mItems.emplace(key, value);
+ }
+
+ void erase(const K& key) {
+ AutoLock lock(mLock);
+ mItems.erase(key);
+ }
+
+ bool isPresent(const K& key) const {
+ AutoLock lock(mLock);
+ auto it = mItems.find(key);
+ return it != mItems.end();
+ }
+
+ android::base::Optional<V> get(const K& key) const {
+ AutoLock lock(mLock);
+ auto it = mItems.find(key);
+ if (it == mItems.end()) {
+ return android::base::kNullopt;
+ }
+ return it->second;
+ }
+
+ using ErasePredicate = std::function<bool(K, V)>;
+
+ void eraseIf(ErasePredicate p) {
+ AutoLock lock(mLock);
+ auto it = mItems.begin();
+ for (; it != mItems.end();) {
+ if (p(it->first, it->second)) {
+ it = mItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ void clear() {
+ AutoLock lock(mLock);
+ mItems.clear();
+ }
+private:
+ using AutoLock = android::base::AutoLock;
+ using Lock = android::base::Lock;
+
+ mutable android::base::Lock mLock;
+ std::unordered_map<K, V> mItems;
+};
+
+} // namespace base
+} // namespace android
diff --git a/base/Stream.cpp b/base/Stream.cpp
index 5415059..6820d3b 100644
--- a/base/Stream.cpp
+++ b/base/Stream.cpp
@@ -103,17 +103,13 @@
return u.f;
}
-void Stream::putString(StringView str) {
- this->putBe32(str.size());
- this->write(str.data(), str.size());
-}
-
void Stream::putString(const char* str) {
- putString(StringView(str));
+ putString(str, strlen(str));
}
void Stream::putString(const char* str, size_t len) {
- putString(StringView(str, len));
+ this->putBe32(len);
+ this->write(str, len);
}
std::string Stream::getString() {
diff --git a/base/Stream.h b/base/Stream.h
index 3451b91..8537d2c 100644
--- a/base/Stream.h
+++ b/base/Stream.h
@@ -14,8 +14,6 @@
#pragma once
-#include "base/StringView.h"
-
#include <string>
#include <inttypes.h>
@@ -76,9 +74,6 @@
// Read a single 32-bit float value from the stream.
float getFloat();
- // Write a string |str| into the stream. Ignore errors.
- void putString(StringView str);
-
// Write a 0-terminated C string |str| into the stream. Ignore error.
void putString(const char* str);
diff --git a/base/StringView.h b/base/StringView.h
deleted file mode 100644
index 4d6a92e..0000000
--- a/base/StringView.h
+++ /dev/null
@@ -1,298 +0,0 @@
-// Copyright 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.
-
-#pragma once
-
-#include "base/Optional.h"
-#include "base/TypeTraits.h"
-
-#include <algorithm>
-#include <cstring>
-#include <string>
-
-namespace android {
-namespace base {
-
-// A StringView is a simple (address, size) pair that points to an
-// existing read-only string. It's a convenience class used to prevent
-// the creation of std::string() objects un-necessarily.
-//
-// Consider the two following functions:
-//
-// size_t count1(const std::string& str) {
-// size_t count = 0;
-// for (size_t n = 0; n < str.size(); ++n) {
-// if (str[n] == '1') {
-// count++;
-// }
-// }
-// return count;
-// }
-//
-// size_t count2(const StringView& str) {
-// size_t count = 0;
-// for (size_t n = 0; n < str.size(); ++n) {
-// if (str[n] == '2') {
-// count++;
-// }
-// }
-// return count;
-// }
-//
-// Then consider the following calls:
-//
-// size_t n1 = count1("There is 1 one in this string!");
-// size_t n2 = count2("I can count 2 too");
-//
-// In the first case, the compiler will silently create a temporary
-// std::string object, copy the input string into it (allocating memory in
-// the heap), call count1() and destroy the std::string upon its return.
-//
-// In the second case, the compiler will create a temporary StringView,
-// initialize it trivially before calling count2(), this results in
-// much less generated code, as well as better performance.
-//
-// Generally speaking, always use a reference or pointer to StringView
-// instead of a std::string if your function or method doesn't need to modify
-// its input.
-//
-class StringView {
-public:
- constexpr StringView() : mString(""), mSize(0U) {}
-
- constexpr StringView(const StringView& other) :
- mString(other.data()), mSize(other.size()) {}
-
- // IMPORTANT: all StringView constructors are intentionally not explict
- // it is needed to allow seamless creation of StringView from all types
- // of strings around - as it's intended to be a generic string wrapper
-
- // A constexpr constructor from a constant buffer, initializing |mSize|
- // as well. This allows one to declare a static const StringView instance
- // and initialize it at compile time, with no runtime overhead:
- //
- // static constexpr StringView message = "blah";
- //
- template <size_t size>
- constexpr StringView(const char (&buf)[size]) :
- mString(buf), mSize(size - 1) {}
-
- // Ctor for non-const arrays, AKA buffers. These usually contain some
- // string formatted at runtime, so call strlen() instead of using the
- // buffer size.
- template <size_t size>
- constexpr StringView(char (&buf)[size]) :
- mString(buf), mSize(strlen(buf)) {}
-
- // Constructor from a const char pointer. It has to be templated to make
- // sure the array-based one is chosen for an array - otherwise non-templated
- // overload always wins
- // Note: the parameter type is a const reference to a const pointer. This
- // is to make this overload a poorer choice for the case of an array. For
- // the 'const char[]' argument both 'reference to an array' and 'pointer'
- // overloads are tied, so compiler can't choose without help
- // Note2: for all constructors and set() calls, |end| must be
- // dereferencable. It is notrequired to be '\0', but there has to be some
- // data there. One may not construct a StringView passing past-the-end
- // iterator as |end|! StringView will try to dereference it.
- template <class Char, class = enable_if<std::is_same<Char, char>>>
- constexpr StringView(const Char* const & string) :
- mString(string ? string : ""), mSize(string ? strlen(string) : 0) {}
-
- StringView(const std::string& str) :
- mString(str.c_str()), mSize(str.size()) {}
-
- constexpr StringView(const char* str, size_t len)
- : mString(str ? str : ""), mSize(len) {}
-
- constexpr StringView(const char* begin, const char* end)
- : mString(begin ? begin : ""), mSize(begin ? end - begin : 0) {}
-
- constexpr StringView(std::nullptr_t) :
- mString(""), mSize(0) {}
-
- std::string str() const { return std::string(mString, mString + mSize); }
- constexpr const char* data() const { return mString; }
- constexpr size_t size() const { return mSize; }
-
- typedef const char* iterator;
- typedef const char* const_iterator;
-
- constexpr const_iterator begin() const { return mString; }
- constexpr const_iterator end() const { return mString + mSize; }
-
- constexpr bool empty() const { return !size(); }
- constexpr bool isNullTerminated() const { return *end() == '\0'; }
-
- void clear() {
- mSize = 0;
- mString = "";
- }
-
- constexpr char operator[](size_t index) const {
- return mString[index];
- }
-
- void set(const char* data, size_t len) {
- mString = data ? data : "";
- mSize = len;
- }
-
- void set(const char* str) {
- mString = str ? str : "";
- mSize = ::strlen(mString);
- }
-
- void set(const StringView& other) {
- mString = other.mString;
- mSize = other.mSize;
- }
-
- // Compare with another StringView.
- int compare(const StringView& other) const;
-
- StringView& operator=(const StringView& other) {
- set(other);
- return *this;
- }
-
- // find() first occurrence of |other| with an initial offset.
- // Returns absolute offset (does not include |off|).
- size_t find(StringView other, size_t off = 0) {
- // Trivial case
- if (!other.mSize) return 0;
-
- size_t safeOff = std::min(off, mSize);
-
- const char* searchStart = mString + safeOff;
- const char* searchEnd = searchStart + mSize - safeOff;
-
- const char* res =
- std::search(searchStart, searchEnd,
- other.mString, other.mString + other.mSize);
- if (res == searchEnd) return std::string::npos;
- return (size_t)((uintptr_t)res - (uintptr_t)mString);
- }
-
- // getSubstr(); returns this string starting at the first place |other|
- // occurs, otherwise a blank string.
- StringView getSubstr(StringView other, size_t off = 0) {
- size_t loc = find(other, off);
- if (loc == std::string::npos) return StringView("");
- return { mString + loc, end() };
- }
-
- // Returns substring starting at |begin| and running for |len|,
- // or the rest of the string if |len| is std::string::npos.
- StringView substr(size_t begin, size_t len = std::string::npos) {
- if (len == std::string::npos) {
- len = mSize - begin;
- }
- size_t safeOff = std::min(begin, mSize);
- size_t safeLen = std::min(len, mSize - safeOff);
- return { mString + safeOff, safeLen };
- }
-
- // Returns substring starting at |begin| ending at |end|,
- // or the rest of the string if |end is std::string::npos.
- StringView substrAbs(size_t begin, size_t end = std::string::npos) {
- if (end == std::string::npos) {
- end = begin + mSize;
- }
- return substr(begin, end - begin);
- }
-
- // Convert to std::string when needed.
- operator std::string() const { return std::string(mString, mSize); }
-
-private:
- const char* mString;
- size_t mSize;
-};
-
-// Comparison operators. Defined as functions to allow automatic type
-// conversions with C strings and std::string objects.
-
-bool operator==(const StringView& x, const StringView& y);
-
-inline bool operator!=(const StringView& x, const StringView& y) {
- return !(x == y);
-}
-
-inline bool operator<(const StringView& x, const StringView& y) {
- return x.compare(y) < 0;
-}
-
-inline bool operator>=(const StringView& x, const StringView& y) {
- return !(x < y);
-}
-
-inline bool operator >(const StringView& x, const StringView& y) {
- return x.compare(y) > 0;
-}
-
-inline bool operator<=(const StringView& x, const StringView& y) {
- return !(x > y);
-}
-
-// Helper to get a null-terminated const char* from a string view.
-// Only allocates if the StringView is not null terminated.
-//
-// Usage:
-//
-// StringView myString = ...;
-// printf("Contents: %s\n", c_str(myString));
-//
-// c_str(...) constructs a temporary object that may allocate memory if the
-// StringView is not null termianted. The lifetime of the temporary object will
-// be until the next sequence point (typically the next semicolon). If the
-// value needs to exist for longer than that, cache the instance.
-//
-// StringView myString = ...;
-// auto myNullTerminatedString = c_str(myString);
-// functionAcceptingConstCharPointer(myNullTerminatedString);
-//
-class CStrWrapper {
-public:
- CStrWrapper(StringView stringView) : mStringView(stringView) {}
-
- // Returns a null-terminated char*, potentially creating a copy to add a
- // null terminator.
- const char* get() {
- if (mStringView.isNullTerminated()) {
- return mStringView.data();
- } else {
- // Create the std::string copy on-demand.
- if (!mStringCopy) {
- mStringCopy.emplace(mStringView.str());
- }
-
- return mStringCopy->c_str();
- }
- }
-
- // Enable casting to const char*
- operator const char*() { return get(); }
-
-private:
- const StringView mStringView;
- Optional<std::string> mStringCopy;
-};
-
-inline CStrWrapper c_str(StringView stringView) {
- return CStrWrapper(stringView);
-}
-
-} // namespace base
-} // namespace android
diff --git a/base/System.cpp b/base/System.cpp
new file mode 100644
index 0000000..6306778
--- /dev/null
+++ b/base/System.cpp
@@ -0,0 +1,18 @@
+#include "base/System.h"
+
+namespace android {
+namespace base {
+
+std::string getEnvironmentVariable(const std::string& key) {
+ return {};
+}
+
+void setEnvironmentVariable(const std::string& key) {
+}
+
+bool isVerboseLogging() {
+ return false;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/System.h b/base/System.h
new file mode 100644
index 0000000..aeed4c9
--- /dev/null
+++ b/base/System.h
@@ -0,0 +1,16 @@
+#include <string>
+
+namespace android {
+namespace base {
+
+std::string getEnvironmentVariable(const std::string& key);
+void setEnvironmentVariable(const std::string& key, const std::string& value);
+bool isVerboseLogging();
+
+uint64_t getUnixTimeUs();
+
+std::string getProgramDirectory();
+std::string getLauncherDirectory();
+
+} // namespace base
+} // namespace android
diff --git a/base/Tracing.cpp b/base/Tracing.cpp
new file mode 100644
index 0000000..fc9ac03
--- /dev/null
+++ b/base/Tracing.cpp
@@ -0,0 +1,85 @@
+// Copyright (C) 2019 The Android Open Source Project
+// Copyright (C) 2019 Google Inc.
+//
+// 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 "base/Tracing.h"
+
+#include <string>
+#include <vector>
+
+#include <fcntl.h>
+
+namespace android {
+namespace base {
+
+const bool* tracingDisabledPtr = nullptr;
+
+void initializeTracing() {
+ // virtualdeviceperfetto::initialize(&tracingDisabledPtr);
+}
+
+void enableTracing() {
+ // if (virtualdeviceperfetto::queryTraceConfig().tracingDisabled) {
+ // virtualdeviceperfetto::enableTracing();
+ // }
+}
+
+void disableTracing() {
+ // if (!virtualdeviceperfetto::queryTraceConfig().tracingDisabled) {
+ // virtualdeviceperfetto::disableTracing();
+ // }
+}
+
+bool shouldEnableTracing() {
+ // return !(virtualdeviceperfetto::queryTraceConfig().tracingDisabled);
+}
+
+#ifdef __cplusplus
+# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
+# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
+#else
+# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
+# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
+#endif
+
+__attribute__((always_inline)) void beginTrace(const char* name) {
+ if (CC_LIKELY(*tracingDisabledPtr)) return;
+ // virtualdeviceperfetto::beginTrace(name);
+}
+
+__attribute__((always_inline)) void endTrace() {
+ if (CC_LIKELY(*tracingDisabledPtr)) return;
+ // virtualdeviceperfetto::endTrace();
+}
+
+__attribute__((always_inline)) void traceCounter(const char* name, int64_t value) {
+ if (CC_LIKELY(*tracingDisabledPtr)) return;
+ // virtualdeviceperfetto::traceCounter(name, value);
+}
+
+ScopedTrace::ScopedTrace(const char* name) {
+ if (CC_LIKELY(*tracingDisabledPtr)) return;
+ // virtualdeviceperfetto::beginTrace(name);
+}
+
+ScopedTrace::~ScopedTrace() {
+ if (CC_LIKELY(*tracingDisabledPtr)) return;
+ // virtualdeviceperfetto::endTrace();
+}
+
+void setGuestTime(uint64_t t) {
+ // virtualdeviceperfetto::setGuestTime(t);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/Tracing.h b/base/Tracing.h
new file mode 100644
index 0000000..93557d0
--- /dev/null
+++ b/base/Tracing.h
@@ -0,0 +1,74 @@
+// Copyright (C) 2019 The Android Open Source Project
+// Copyright (C) 2019 Google Inc.
+//
+// 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.
+#pragma once
+
+#include <inttypes.h>
+
+// Library to perform tracing. Talks to platform-specific
+// tracing libraries.
+namespace android {
+namespace base {
+
+// New tracing API that talks to an underlying tracing library, possibly perfetto.
+//
+// Sets up global state useful for tracing.
+void initializeTracing();
+
+// Enable/disable tracing
+void enableTracing();
+void disableTracing();
+
+// Set the time of traces on the host to be at this guest time.
+// Not needed if we assume timestamps can be transferrable (e.g.,
+// when RDTSC with raw passthrough is used)
+void setGuestTime(uint64_t guestTime);
+
+// Record a counter of some kind.
+void traceCounter(const char* tag, int64_t value);
+
+void beginTrace(const char* name);
+void endTrace();
+
+class ScopedTrace {
+public:
+ ScopedTrace(const char* name);
+ ~ScopedTrace();
+};
+
+class ScopedTraceDerived : public ScopedTrace {
+public:
+ void* member = nullptr;
+};
+
+void setGuestTime(uint64_t t);
+
+void enableTracing();
+void disableTracing();
+
+bool shouldEnableTracing();
+
+void traceCounter(const char* name, int64_t value);
+
+} // namespace base
+} // namespace android
+
+#define __AEMU_GENSYM2(x,y) x##y
+#define __AEMU_GENSYM1(x,y) __AEMU_GENSYM2(x,y)
+#define AEMU_GENSYM(x) __AEMU_GENSYM1(x,__COUNTER__)
+
+#define AEMU_SCOPED_TRACE(tag) __attribute__ ((unused)) android::base::ScopedTrace AEMU_GENSYM(aemuScopedTrace_)(tag)
+#define AEMU_SCOPED_TRACE_CALL() AEMU_SCOPED_TRACE(__func__)
+#define AEMU_SCOPED_THRESHOLD_TRACE_CALL()
+#define AEMU_SCOPED_THRESHOLD_TRACE(...)
diff --git a/base/Win32UnicodeString.cpp b/base/Win32UnicodeString.cpp
new file mode 100644
index 0000000..3658940
--- /dev/null
+++ b/base/Win32UnicodeString.cpp
@@ -0,0 +1,247 @@
+// 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 "base/Win32UnicodeString.h"
+
+#include <algorithm>
+
+#include <windows.h>
+
+#include <string.h>
+
+namespace android {
+namespace base {
+
+Win32UnicodeString::Win32UnicodeString() : mStr(nullptr), mSize(0u) {}
+
+Win32UnicodeString::Win32UnicodeString(const char* str, size_t len)
+ : mStr(nullptr), mSize(0u) {
+ reset(str, strlen(str));
+}
+
+Win32UnicodeString::Win32UnicodeString(const char* str)
+ : mStr(nullptr), mSize(0u) {
+ reset(str);
+}
+
+Win32UnicodeString::Win32UnicodeString(size_t size) : mStr(nullptr), mSize(0u) {
+ resize(size);
+}
+
+Win32UnicodeString::Win32UnicodeString(const wchar_t* str)
+ : mStr(nullptr), mSize(0u) {
+ size_t len = str ? wcslen(str) : 0u;
+ resize(len);
+ ::memcpy(mStr, str ? str : L"", len * sizeof(wchar_t));
+}
+
+Win32UnicodeString::Win32UnicodeString(const Win32UnicodeString& other)
+ : mStr(nullptr), mSize(0u) {
+ resize(other.mSize);
+ ::memcpy(mStr, other.c_str(), other.mSize * sizeof(wchar_t));
+}
+
+Win32UnicodeString::~Win32UnicodeString() {
+ delete[] mStr;
+}
+
+Win32UnicodeString& Win32UnicodeString::operator=(
+ const Win32UnicodeString& other) {
+ resize(other.mSize);
+ ::memcpy(mStr, other.c_str(), other.mSize * sizeof(wchar_t));
+ return *this;
+}
+
+Win32UnicodeString& Win32UnicodeString::operator=(const wchar_t* str) {
+ size_t len = str ? wcslen(str) : 0u;
+ resize(len);
+ ::memcpy(mStr, str ? str : L"", len * sizeof(wchar_t));
+ return *this;
+}
+
+wchar_t* Win32UnicodeString::data() {
+ if (!mStr) {
+ // Ensure the function never returns NULL.
+ // it is safe to const_cast the pointer here - user isn't allowed to
+ // write into it anyway
+ return const_cast<wchar_t*>(L"");
+ }
+ return mStr;
+}
+
+std::string Win32UnicodeString::toString() const {
+ return convertToUtf8(mStr, mSize);
+}
+
+void Win32UnicodeString::reset(const char* str, size_t len) {
+ if (mStr) {
+ delete[] mStr;
+ }
+ const int utf16Len = calcUtf16BufferLength(str, len);
+ mStr = new wchar_t[utf16Len + 1];
+ mSize = static_cast<size_t>(utf16Len);
+ convertFromUtf8(mStr, utf16Len, str, len);
+ mStr[mSize] = L'\0';
+}
+
+void Win32UnicodeString::reset(const char* str) {
+ reset(str, strlen(str));
+}
+
+void Win32UnicodeString::resize(size_t newSize) {
+ if (newSize == 0) {
+ delete [] mStr;
+ mStr = nullptr;
+ mSize = 0;
+ } else if (newSize <= mSize) {
+ mStr[newSize] = 0;
+ mSize = newSize;
+ } else {
+ wchar_t* oldStr = mStr;
+ mStr = new wchar_t[newSize + 1u];
+ size_t copySize = std::min(newSize, mSize);
+ ::memcpy(mStr, oldStr ? oldStr : L"", copySize * sizeof(wchar_t));
+ mStr[copySize] = L'\0';
+ mStr[newSize] = L'\0';
+ mSize = newSize;
+ delete[] oldStr;
+ }
+}
+
+void Win32UnicodeString::append(const wchar_t* str) {
+ append(str, wcslen(str));
+}
+
+void Win32UnicodeString::append(const wchar_t* str, size_t len) {
+ // NOTE: This method should be rarely used, so don't try to optimize
+ // storage with larger capacity values and exponential increments.
+ if (!str || !len) {
+ return;
+ }
+ size_t oldSize = size();
+ resize(oldSize + len);
+ memmove(mStr + oldSize, str, len * sizeof(wchar_t));
+}
+
+void Win32UnicodeString::append(const Win32UnicodeString& other) {
+ append(other.c_str(), other.size());
+}
+
+wchar_t* Win32UnicodeString::release() {
+ wchar_t* result = mStr;
+ mStr = nullptr;
+ mSize = 0u;
+ return result;
+}
+
+// static
+std::string Win32UnicodeString::convertToUtf8(const wchar_t* str, int len) {
+ std::string result;
+ const int utf8Len = calcUtf8BufferLength(str, len);
+ if (utf8Len > 0) {
+ result.resize(static_cast<size_t>(utf8Len));
+ convertToUtf8(&result[0], utf8Len, str, len);
+ if (len == -1) {
+ result.resize(utf8Len - 1); // get rid of the null-terminator
+ }
+ }
+ return result;
+}
+
+// returns the return value of a Win32UnicodeString public conversion function
+// from a WinAPI conversion function returned code
+static int convertRetVal(int winapiResult) {
+ return winapiResult ? winapiResult : -1;
+}
+
+// static
+int Win32UnicodeString::calcUtf8BufferLength(const wchar_t* str, int len) {
+ if (len < 0 && len != -1) {
+ return -1;
+ }
+ if (len == 0) {
+ return 0;
+ }
+ const int utf8Len = WideCharToMultiByte(CP_UTF8, // CodePage
+ 0, // dwFlags
+ str, // lpWideCharStr
+ len, // cchWideChar
+ nullptr, // lpMultiByteStr
+ 0, // cbMultiByte
+ nullptr, // lpDefaultChar
+ nullptr); // lpUsedDefaultChar
+
+ return convertRetVal(utf8Len);
+}
+
+// static
+int Win32UnicodeString::calcUtf16BufferLength(const char* str, int len) {
+ if (len < 0 && len != -1) {
+ return -1;
+ }
+ if (len == 0) {
+ return 0;
+ }
+ const int utf16Len = MultiByteToWideChar(CP_UTF8, // CodePage
+ 0, // dwFlags
+ str, // lpMultiByteStr
+ len, // cbMultiByte
+ nullptr, // lpWideCharStr
+ 0); // cchWideChar
+
+ return convertRetVal(utf16Len);
+}
+
+// static
+int Win32UnicodeString::convertToUtf8(char* outStr, int outLen,
+ const wchar_t* str, int len) {
+ if (!outStr || outLen < 0 || !str || (len < 0 && len != -1)) {
+ return -1;
+ }
+ if (len == 0) {
+ return 0;
+ }
+
+ const int utf8Len = WideCharToMultiByte(CP_UTF8, // CodePage
+ 0, // dwFlags
+ str, // lpWideCharStr
+ len, // cchWideChar
+ outStr, // lpMultiByteStr
+ outLen, // cbMultiByte
+ nullptr, // lpDefaultChar
+ nullptr); // lpUsedDefaultChar
+ return convertRetVal(utf8Len);
+}
+
+// static
+int Win32UnicodeString::convertFromUtf8(wchar_t* outStr, int outLen,
+ const char* str, int len) {
+ if (!outStr || outLen < 0 || !str || (len < 0 && len != -1)) {
+ return -1;
+ }
+ if (len == 0) {
+ return 0;
+ }
+
+ const int utf16Len = MultiByteToWideChar(CP_UTF8, // CodePage
+ 0, // dwFlags
+ str, // lpMultiByteStr
+ len, // cbMultiByte
+ outStr, // lpWideCharStr
+ outLen); // cchWideChar
+ return convertRetVal(utf16Len);
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/Win32UnicodeString.h b/base/Win32UnicodeString.h
new file mode 100644
index 0000000..58eda61
--- /dev/null
+++ b/base/Win32UnicodeString.h
@@ -0,0 +1,154 @@
+// 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.
+
+#pragma once
+
+#ifndef _WIN32
+// this file is just empty on non-Win32
+#else // _WIN32
+
+#include "base/Compiler.h"
+
+#include <string>
+
+#include <wchar.h>
+
+namespace android {
+namespace base {
+
+// Helper class used to model a Windows Unicode string, which stores
+// text and file paths as a zero-terminated array of UTF-16 code points
+// (at least since Windows Vista, before that the encoding was slightly
+// different).
+//
+// This is very intentionally *not* a general purpose class. It should only
+// be used to simplify conversions between the Win32 Unicode API and the
+// rest of android::base which uses UTF-8 for all Unicode text.
+class Win32UnicodeString {
+public:
+ // Default constructor.
+ Win32UnicodeString();
+
+ // Initialize a new instance from UTF-8 text at |str| of |len| bytes.
+ // This doesn't try to validate the input, i.e. on error, the instance's
+ // content is undefined.
+ Win32UnicodeString(const char* str, size_t len);
+
+ // Initialize a new instance from an existing string instance |str|.
+ explicit Win32UnicodeString(const char* str);
+
+ // Initialize by reserving enough room for a string of |size| UTF-16
+ // codepoints.
+ explicit Win32UnicodeString(size_t size);
+
+ // Initialize from a zero-terminated wchar_t array.
+ explicit Win32UnicodeString(const wchar_t* str);
+
+ // Copy-constructor.
+ Win32UnicodeString(const Win32UnicodeString& other);
+
+ // Destructor.
+ ~Win32UnicodeString();
+
+ // Assignment operators.
+ Win32UnicodeString& operator=(const Win32UnicodeString& other);
+ Win32UnicodeString& operator=(const wchar_t* str);
+
+ // Return pointer to first wchar_t in the string.
+ const wchar_t* c_str() const { return mStr ? mStr : L""; }
+
+ // Return pointer to writable wchar_t array. This can never be NULL
+ // but no more than size() items should be accessed.
+ wchar_t* data();
+
+ // Return size of the string, this is the number of UTF-16 code points
+ // stored by the string, which may be larger than the number of actual
+ // Unicode characters in it.
+ size_t size() const { return mSize; }
+
+ // Convert to a string instance holding the corresponding UTF-8 text.
+ std::string toString() const;
+
+ // Return n-th character from string.
+ wchar_t operator[](size_t index) const { return mStr[index]; }
+
+ // Reset content from UTF-8 text at |str| or |len| bytes.
+ void reset(const char* str, size_t len);
+
+ // Reset content from UTF-8 text at |str|.
+ void reset(const char* str);
+
+ // Resize array.
+ void resize(size_t newSize);
+
+ // Append at the end of a Win32UnicodeString.
+ void append(const wchar_t* other);
+ void append(const wchar_t* other, size_t len);
+ void append(const Win32UnicodeString& other);
+
+ // Release the Unicode string array to the caller.
+ wchar_t* release();
+
+ // Directly convert a Unicode string to UTF-8 text and back.
+ // |len| - input length. if set to -1, means the input is null-terminated
+ static std::string convertToUtf8(const wchar_t* str, int len = -1);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Be careful when crossing this line. The following functions work with
+ // raw buffers of static length, and are much harder to use correctly.
+ // You've been warned
+ ////////////////////////////////////////////////////////////////////////////
+
+ // Calculate the needed buffer size (in characters) to convert the |str|
+ // parameter either to or from UTF8
+ // |len| - size of the input. -1 means it is 0-terminated
+ // Return value is either the size of the buffer needed to convert the whole
+ // input of size |len|, or, if |len| is -1, the size needed to convert a
+ // zero-terminated string, including the terminator character, or negative -
+ // if the size can't be calculated
+ static int calcUtf8BufferLength(const wchar_t* str, int len = -1);
+ static int calcUtf16BufferLength(const char* str, int len = -1);
+
+ // The following two functions convert |str| into the output buffer, instead
+ // of dynamically allocated class object.
+ // |str| - source string to convert, must be not null
+ // |len| - length of the source string, in chars; must be positive or -1.
+ // -1 means 'the input is zero-terminated'. In that case output has to
+ // be large enough to hold a zero character as well.
+ // |outStr| - the output buffer, must be not null
+ // |outLen| - the output buffer length, in chars; must be large enough to
+ // hold the converted string. One can calculate the required length using
+ // one of the getUtf<*>BufferLength() functions
+ //
+ // Returns a number of bytes written into the |outBuf|, or -1 on failure
+ //
+ // Note: you can't get the reason of the failure from this function call:
+ // it could be bad input parameter (e.g. null buffer), too short output,
+ // bad characters in the input string, even OS failure. So make sure
+ // you do the call to getUtf<*>BufferLength() and all parameters are
+ // correct - if you need to know the exact reason and not just "can't" one.
+ static int convertToUtf8(char* outStr, int outLen,
+ const wchar_t* str, int len = -1);
+ static int convertFromUtf8(wchar_t* outStr, int outLen,
+ const char* str, int len = -1);
+
+private:
+ wchar_t* mStr;
+ size_t mSize;
+};
+
+} // namespace base
+} // namespace android
+
+#endif // _WIN32
diff --git a/base/Win32UnicodeString_unittest.cpp b/base/Win32UnicodeString_unittest.cpp
new file mode 100644
index 0000000..32e8e9e
--- /dev/null
+++ b/base/Win32UnicodeString_unittest.cpp
@@ -0,0 +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.
+
+#include "android/base/system/Win32UnicodeString.h"
+
+#include <gtest/gtest.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+namespace android {
+namespace base {
+
+TEST(Win32UnicodeString, DefaultConstructor) {
+ Win32UnicodeString str;
+ EXPECT_EQ(0u, str.size());
+ EXPECT_STREQ(L"", str.c_str());
+ EXPECT_TRUE(str.data());
+}
+
+TEST(Win32UnicodeString, Constructors) {
+ static const struct {
+ const char* utf8;
+ const wchar_t* utf16;
+ } kData[] = {
+ {"", L""},
+ {"Hello World!", L"Hello World!"},
+ {"T\xC3\xA9l\xC3\xA9vision", L"T\xE9l\xE9vision"},
+ {"foo\xE1\x80\x80 bar", L"foo\x1000 bar"},
+ };
+ const size_t kDataSize = ARRAY_SIZE(kData);
+
+ for (size_t n = 0; n < kDataSize; ++n) {
+ Win32UnicodeString str1(kData[n].utf8);
+ EXPECT_EQ(wcslen(kData[n].utf16), str1.size());
+ EXPECT_STREQ(kData[n].utf16, str1.c_str());
+
+ Win32UnicodeString str2(kData[n].utf8, strlen(kData[n].utf8));
+ EXPECT_EQ(wcslen(kData[n].utf16), str2.size());
+ EXPECT_STREQ(kData[n].utf16, str2.c_str());
+
+ std::string baseStr(kData[n].utf8);
+ Win32UnicodeString str3(baseStr);
+ EXPECT_EQ(wcslen(kData[n].utf16), str3.size());
+ EXPECT_STREQ(kData[n].utf16, str3.c_str());
+
+ size_t utf16Len = wcslen(kData[n].utf16);
+ Win32UnicodeString str4(kData[n].utf16);
+ EXPECT_EQ(utf16Len, str4.size());
+ EXPECT_STREQ(kData[n].utf16, str4.c_str());
+
+ Win32UnicodeString str5 = str4;
+ EXPECT_EQ(utf16Len, str5.size());
+ EXPECT_STREQ(kData[n].utf16, str5.c_str());
+
+ Win32UnicodeString str6("foo");
+ str6 = str5;
+ EXPECT_EQ(utf16Len, str6.size());
+ EXPECT_STREQ(kData[n].utf16, str6.c_str());
+ }
+}
+
+TEST(Win32UnicodeString, convertToUtf8) {
+ static const struct {
+ const char* utf8;
+ const wchar_t* utf16;
+ } kData[] = {
+ {"", L""},
+ {"Hello World!", L"Hello World!"},
+ {"T\xC3\xA9l\xC3\xA9vision", L"T\xE9l\xE9vision"},
+ {"foo\xE1\x80\x80 bar", L"foo\x1000 bar"},
+ };
+ const size_t kDataSize = ARRAY_SIZE(kData);
+
+ for (size_t n = 0; n < kDataSize; ++n) {
+ std::string str1 = Win32UnicodeString::convertToUtf8(kData[n].utf16);
+ EXPECT_EQ(strlen(kData[n].utf8), str1.size());
+ EXPECT_STREQ(kData[n].utf8, str1.c_str());
+
+ std::string str2 = Win32UnicodeString::convertToUtf8(
+ kData[n].utf16, wcslen(kData[n].utf16));
+ EXPECT_EQ(strlen(kData[n].utf8), str2.size());
+ EXPECT_STREQ(kData[n].utf8, str2.c_str());
+
+ char out[256];
+ int len = Win32UnicodeString::convertToUtf8(out, sizeof(out),
+ kData[n].utf16);
+ EXPECT_EQ(strlen(kData[n].utf8) + 1U, (size_t)len);
+ EXPECT_STREQ(kData[n].utf8, out);
+
+ len = Win32UnicodeString::convertToUtf8(out, sizeof(out),
+ kData[n].utf16,
+ wcslen(kData[n].utf16));
+ EXPECT_EQ((int)strlen(kData[n].utf8), len);
+ out[len] = 0;
+ EXPECT_STREQ(kData[n].utf8, out);
+
+ if (kData[n].utf8[0] != 0) {
+ len = Win32UnicodeString::convertToUtf8(out, 1,
+ kData[n].utf16);
+ EXPECT_EQ(-1, len);
+ }
+
+ len = Win32UnicodeString::convertToUtf8(nullptr, 0,
+ kData[n].utf16);
+ EXPECT_EQ(-1, len);
+
+ len = Win32UnicodeString::convertToUtf8(nullptr, 0,
+ kData[n].utf16,
+ wcslen(kData[n].utf16));
+ EXPECT_EQ(-1, len);
+
+ len = Win32UnicodeString::calcUtf8BufferLength(kData[n].utf16);
+ EXPECT_EQ((int)strlen(kData[n].utf8) + 1, len);
+
+ len = Win32UnicodeString::calcUtf8BufferLength(kData[n].utf16,
+ wcslen(kData[n].utf16));
+ EXPECT_EQ((int)strlen(kData[n].utf8), len);
+ }
+}
+
+TEST(Win32UnicodeString, convertFromUtf8) {
+ static const struct {
+ const char* utf8;
+ const wchar_t* utf16;
+ } kData[] = {
+ {"", L""},
+ {"Hello World!", L"Hello World!"},
+ {"T\xC3\xA9l\xC3\xA9vision", L"T\xE9l\xE9vision"},
+ {"foo\xE1\x80\x80 bar", L"foo\x1000 bar"},
+ };
+ const size_t kDataSize = ARRAY_SIZE(kData);
+
+ for (size_t n = 0; n < kDataSize; ++n) {
+ wchar_t out[256];
+ int len = Win32UnicodeString::convertFromUtf8(out, ARRAY_SIZE(out),
+ kData[n].utf8);
+ EXPECT_EQ((int)wcslen(kData[n].utf16) + 1, len);
+ EXPECT_STREQ(kData[n].utf16, out);
+
+ len = Win32UnicodeString::convertFromUtf8(out, ARRAY_SIZE(out),
+ kData[n].utf8,
+ strlen(kData[n].utf8));
+ EXPECT_EQ((int)wcslen(kData[n].utf16), len);
+ out[len] = 0;
+ EXPECT_STREQ(kData[n].utf16, out);
+
+ if (kData[n].utf16[0] != 0) {
+ len = Win32UnicodeString::convertFromUtf8(out, 1, kData[n].utf8);
+ EXPECT_EQ(-1, len);
+ }
+
+ len = Win32UnicodeString::convertFromUtf8(nullptr, 0, kData[n].utf8);
+ EXPECT_EQ(-1, len);
+
+ len = Win32UnicodeString::convertFromUtf8(nullptr, 0,
+ kData[n].utf8,
+ strlen(kData[n].utf8));
+ EXPECT_EQ(-1, len);
+
+ len = Win32UnicodeString::calcUtf16BufferLength(kData[n].utf8);
+ EXPECT_EQ((int)wcslen(kData[n].utf16) + 1, len);
+
+ len = Win32UnicodeString::calcUtf16BufferLength(kData[n].utf8,
+ strlen(kData[n].utf8));
+ EXPECT_EQ((int)wcslen(kData[n].utf16), len);
+ }
+}
+
+TEST(Win32UnicodeString, appending) {
+ static const struct {
+ const wchar_t* first;
+ const wchar_t* second;
+ const wchar_t* result;
+ } kData[] = {
+ {L"foo", L"bar", L"foobar"},
+ {L"", L"bar", L"bar"},
+ {L"foo", L"", L"foo"},
+ {L"foobar", L" with ice cream", L"foobar with ice cream"},
+ };
+
+ for (const auto& data : kData) {
+ {
+ // Test appending Win32UnicodeString
+ Win32UnicodeString first(data.first);
+ Win32UnicodeString second(data.second);
+
+ first.append(second);
+ EXPECT_EQ(wcslen(data.result), first.size());
+ EXPECT_STREQ(data.result, first.c_str());
+ }
+ {
+ // Test appending wchar_t*
+ Win32UnicodeString str(data.first);
+ str.append(data.second);
+ EXPECT_EQ(wcslen(data.result), str.size());
+ EXPECT_STREQ(data.result, str.c_str());
+ }
+ {
+ // Test appending wchar_t* with length
+ Win32UnicodeString str(data.first);
+ str.append(data.second, wcslen(data.second));
+ EXPECT_EQ(wcslen(data.result), str.size());
+ EXPECT_STREQ(data.result, str.c_str());
+ }
+ if (wcslen(data.second) > 0) {
+ // Test appending with fewer characters
+ Win32UnicodeString str(data.first);
+ str.append(data.second, wcslen(data.second) - 1);
+ EXPECT_EQ(wcslen(data.result) - 1, str.size());
+ std::wstring choppedResult(data.result, wcslen(data.result) - 1);
+ EXPECT_STREQ(choppedResult.c_str(), str.c_str());
+ }
+ }
+}
+
+} // namespace base
+} // namespace android