This commit is contained in:
jeanlemotan
2024-07-02 18:10:39 +02:00
commit 48ab06b1d9
733 changed files with 321088 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
Language : Cpp
BasedOnStyle : Google
Standard : Auto
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
AccessModifierOffset : -4
AlignTrailingComments : true
AllowAllParametersOfDeclarationOnNextLine : false
AllowShortBlocksOnASingleLine : true
AllowShortFunctionsOnASingleLine : true
AllowShortIfStatementsOnASingleLine : false
AllowShortLoopsOnASingleLine : false
BinPackParameters : false
BreakBeforeBraces : Allman
BreakBeforeTernaryOperators : false
BreakConstructorInitializersBeforeComma : true
ColumnLimit : 120
Cpp11BracedListStyle : true
DerivePointerAlignment : true
DerivePointerBinding : false
IndentWidth : 4
KeepEmptyLinesAtTheStartOfBlocks : true
MaxEmptyLinesToKeep : 2
NamespaceIndentation : All
PointerBindsToType : true
SpacesBeforeTrailingComments : 1
SpacesInAngles : false
SpacesInSquareBrackets : false
TabWidth : 4
UseTab : ForIndentation
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-
+6
View File
@@ -0,0 +1,6 @@
# Auto detect text files and perform LF normalization
# http://git-scm.com/docs/gitattributes
* text=auto
.appveyor.yml -text eol=crlf
.appveyor-mingw.yml -text eol=crlf
ci-*.cmd -text eol=crlf
+50
View File
@@ -0,0 +1,50 @@
tags
cscope.out
**/*.swp
**/*.swo
.swp
*.swp
.swo
.TMP
-.d
eastl_build_out
build_bench
bench.bat
build.bat
.p4config
## CMake generated files
CMakeCache.txt
cmake_install.cmake
## Patch files
*.patch
## For Visual Studio Generated projects
*.sln
**/*.vcxproj
**/*.vcxproj.filters
*.VC.opendb
*.sdf
**/*.suo
**/*.user
.vs/*
**/Debug/*
CMakeFiles/*
EASTL.dir/**
RelWithDebInfo/*
Release/*
Win32/*
x64/*
MinSizeRel/*
build*/*
Testing/*
%ALLUSERSPROFILE%/*
# Buck
/buck-out/
/.buckd/
/buckaroo/
.buckconfig.local
BUCKAROO_DEPS
.vscode/settings.json
+18
View File
@@ -0,0 +1,18 @@
[submodule "test/packages/EABase"]
path = test/packages/EABase
url = ../EABase.git
[submodule "test/packages/EAAssert"]
path = test/packages/EAAssert
url = ../EAAssert.git
[submodule "test/packages/EAMain"]
path = test/packages/EAMain
url = ../EAMain.git
[submodule "test/packages/EAStdC"]
path = test/packages/EAStdC
url = ../EAStdC.git
[submodule "test/packages/EATest"]
path = test/packages/EATest
url = ../EATest.git
[submodule "test/packages/EAThread"]
path = test/packages/EAThread
url = ../EAThread.git
+4
View File
@@ -0,0 +1,4 @@
/.git/
tags
.gitignore
cscope.out
+88
View File
@@ -0,0 +1,88 @@
dist: xenial
language: cpp
cache:
- ccache: true
os:
- linux
- osx
- windows
compiler:
- gcc
- clang
- msvc
env:
- EASTL_CONFIG=Debug
- EASTL_CONFIG=Release
addons:
apt:
update: true
sources:
- george-edison55-precise-backports
- sourceline: 'ppa:ubuntu-toolchain-r/test'
- sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-11 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
packages:
- cmake
- cmake-data
- g++-9
- clang-11
matrix:
include:
- compiler: clang "release build with clang to trigger MOJI check"
env: EASTL_CONFIG=Release USE_MOJI_CHECK=yes
os: linux
- compiler: msvc
env: EASTL_CONFIG=Release CXXFLAGS="/std:c++latest /Zc:char8_t"
os: windows
exclude:
- os: osx
compiler: gcc
- os: osx
compiler: msvc
- os: linux
compiler: msvc
- os: windows
compiler: clang
- os: windows
compiler: gcc
# Handle git submodules yourself
git:
submodules: false
before_install:
- git submodule update --init
- if [[ "$CXX" == "g++" ]]; then export CC="gcc-9" ;fi
- if [[ "$CXX" == "g++" ]]; then export CXX="g++-9" ;fi
- if [[ "$CXX" == "clang++" && "${TRAVIS_OS_NAME}" != "osx" ]]; then export CC="clang-11" ;fi
- if [[ "$CXX" == "clang++" && "${TRAVIS_OS_NAME}" != "osx" ]]; then export CXX="clang++-11" ;fi
- if [[ "$CXX" == "g++-9" && "${TRAVIS_OS_NAME}" != "windows" ]]; then g++-9 -v ;fi
- if [[ "$CXX" == "clang++-11" && "${TRAVIS_OS_NAME}" != "windows" ]]; then clang++-11 -v ;fi
install:
# MOJI check; exit 1 if non-ascii characters detected in C++
- if [[ -n "$USE_MOJI_CHECK" && -n `git grep -P "[^[:ascii:]]" source test` ]]; then echo "Moji Detected" && exit 1 ;fi
- if [[ -n "$USE_MOJI_CHECK" ]]; then exit 0 ;fi
before_script:
- mkdir build_$EASTL_CONFIG
- cd build_$EASTL_CONFIG
- cmake .. -DEASTL_BUILD_BENCHMARK:BOOL=ON -DEASTL_BUILD_TESTS:BOOL=ON
- cmake --build . --config $EASTL_CONFIG
script:
# Run Tests
- cd $TRAVIS_BUILD_DIR/build_$EASTL_CONFIG/test
- ctest -C $EASTL_CONFIG -V || exit 1
# Run Benchmarks
- cd $TRAVIS_BUILD_DIR/build_$EASTL_CONFIG/benchmark
- ctest -C $EASTL_CONFIG -V || exit 1
+110
View File
@@ -0,0 +1,110 @@
Additional licenses also apply to this software package as detailed below.
HP STL comes with the following license:
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1994
// Hewlett-Packard Company
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
// provided that the above copyright notice appear in all copies and
// that both that copyright notice and this permission notice appear
// in supporting documentation. Hewlett-Packard Company makes no
// representations about the suitability of this software for any
// purpose. It is provided "as is" without express or implied warranty.
///////////////////////////////////////////////////////////////////////////////
libc++ comes with the following license:
==============================================================================
libc++ License
==============================================================================
The libc++ library is dual licensed under both the University of Illinois
"BSD-Like" license and the MIT license. As a user of this code you may choose
to use it under either license. As a contributor, you agree to allow your code
to be used under both.
Full text of the relevant licenses is included below.
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2009-2015 by the contributors listed at
http://llvm.org/svn/llvm-project/libcxx/trunk/CREDITS.TXT
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
==============================================================================
Copyright (c) 2009-2014 by the contributors listed at
http://llvm.org/svn/llvm-project/libcxx/trunk/CREDITS.TXT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
==============================================================================
*No express or implied license to use PlayStation®4 libraries included.
PlayStation®4 development tools and libraries are subject to separate license
with Sony Interactive Entertainment LLC.
==============================================================================
+80
View File
@@ -0,0 +1,80 @@
#-------------------------------------------------------------------------------------------
# Copyright (C) Electronic Arts Inc. All rights reserved.
#-------------------------------------------------------------------------------------------
cmake_minimum_required(VERSION 3.1)
project(EASTL CXX)
#-------------------------------------------------------------------------------------------
# Options
#-------------------------------------------------------------------------------------------
option(EASTL_BUILD_BENCHMARK "Enable generation of build files for benchmark" OFF)
option(EASTL_BUILD_TESTS "Enable generation of build files for tests" OFF)
option(EASTL_STD_ITERATOR_CATEGORY_ENABLED "Enable compatibility with std:: iterator categories" OFF)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
#-------------------------------------------------------------------------------------------
# Compiler Flags
#-------------------------------------------------------------------------------------------
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/scripts/CMake")
include(CommonCppFlags)
#-------------------------------------------------------------------------------------------
# Library definition
#-------------------------------------------------------------------------------------------
file(GLOB EASTL_SOURCES "source/*.cpp")
add_library(EASTL ${EASTL_SOURCES})
if (MSVC)
set(EASTL_NATVIS_DIR "doc")
set(EASTL_NATVIS_FILE "${EASTL_NATVIS_DIR}/EASTL.natvis")
target_sources(EASTL INTERFACE
$<INSTALL_INTERFACE:${EASTL_NATVIS_FILE}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${EASTL_NATVIS_FILE}>
)
endif()
if(EASTL_BUILD_BENCHMARK)
add_subdirectory(benchmark)
endif()
if(EASTL_BUILD_TESTS)
add_subdirectory(test)
endif()
#-------------------------------------------------------------------------------------------
# Defines
#-------------------------------------------------------------------------------------------
add_definitions(-D_CHAR16T)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
add_definitions(-DEASTL_OPENSOURCE=1)
if (EASTL_STD_ITERATOR_CATEGORY_ENABLED)
add_definitions(-DEASTL_STD_ITERATOR_CATEGORY_ENABLED=1)
endif()
#-------------------------------------------------------------------------------------------
# Include dirs
#-------------------------------------------------------------------------------------------
target_include_directories(EASTL PUBLIC include)
#-------------------------------------------------------------------------------------------
# Dependencies
#-------------------------------------------------------------------------------------------
if (NOT TARGET EABase)
add_subdirectory(test/packages/EABase)
endif()
target_link_libraries(EASTL EABase)
#-------------------------------------------------------------------------------------------
# Installation
#-------------------------------------------------------------------------------------------
install(TARGETS EASTL DESTINATION lib)
install(DIRECTORY include/EASTL DESTINATION include)
if (MSVC)
install(FILES ${EASTL_NATVIS_FILE} DESTINATION ${EASTL_NATVIS_DIR})
endif()
+90
View File
@@ -0,0 +1,90 @@
## Contributing
Before you can contribute, EA must have a Contributor License Agreement (CLA) on file that has been signed by each contributor.
You can sign here: [Go to CLA](https://electronicarts.na1.echosign.com/public/esignWidget?wid=CBFCIBAA3AAABLblqZhByHRvZqmltGtliuExmuV-WNzlaJGPhbSRg2ufuPsM3P0QmILZjLpkGslg24-UJtek*)
If you want to be recognized for your contributions to EASTL or have a project using EASTL be recognized; you can submit a pull request to the appropriate sections in [README.md](README.md).
Some examples of what the format and information will look like is as follows.
* John Smith - jsmith@domain.com
* John Smith
* Frostbite - Electronic Arts
* My Project - [link to said project]
### Pull Request Policy
All code contributions to EASTL are submitted as [Github pull requests](https://help.github.com/articles/using-pull-requests/). All pull requests will be reviewed by an EASTL maintainer according to the guidelines found in the next section.
Your pull request should:
* merge cleanly
* come with tests
* tests should be minimal and stable
* fail before your fix is applied
* pass the test suite
* code formatting is encoded in clang format
* limit using clang format on new code
* do not deviate from style already established in the files
### Getting the Repository
EASTL uses git submodules for its dependencies as they are seperate git repositories. Recursive clones will continue until HD space is exhausted unless they are manually limited.
It is recommended to use the following to get the source:
```bash
git clone https://github.com/electronicarts/EASTL
cd EASTL
git submodule update --init
```
### Running the Unit Tests
EASTL uses CMake as its build system.
* Create and navigate to "your_build_folder":
* mkdir your_build_folder && cd your_build_folder
* Generate build scripts:
* cmake eastl_source_folder -DEASTL_BUILD_TESTS:BOOL=ON
* Build unit tests for "your_config":
* cmake --build . --config your_config
* Run the unit tests for "your_config" from the test folder:
* cd test && ctest -C your_config
Here is an example batch file.
```batch
set build_folder=out
mkdir %build_folder%
pushd %build_folder%
call cmake .. -DEASTL_BUILD_TESTS:BOOL=ON -DEASTL_BUILD_BENCHMARK:BOOL=OFF
call cmake --build . --config Release
call cmake --build . --config Debug
call cmake --build . --config RelWithDebInfo
call cmake --build . --config MinSizeRel
pushd test
call ctest -C Release
call ctest -C Debug
call ctest -C RelWithDebInfo
call ctest -C MinSizeRel
popd
popd
```
Here is an example bash file
```bash
build_folder=out
mkdir $build_folder
pushd $build_folder
cmake .. -DEASTL_BUILD_TESTS:BOOL=ON -DEASTL_BUILD_BENCHMARK:BOOL=OFF
cmake --build . --config Release
cmake --build . --config Debug
cmake --build . --config RelWithDebInfo
cmake --build . --config MinSizeRel
pushd test
ctest -C Release
ctest -C Debug
ctest -C RelWithDebInfo
ctest -C MinSizeRel
popd
popd
```
The value of EASTL_BUILD_BENCHMARK can be toggled to `ON` in order to build projects that include the benchmark program.
+29
View File
@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2019, Electronic Arts
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+73
View File
@@ -0,0 +1,73 @@
# EA Standard Template Library
[![Build Status](https://travis-ci.org/electronicarts/EASTL.svg?branch=master)](https://travis-ci.org/electronicarts/EASTL)
EASTL stands for Electronic Arts Standard Template Library. It is a C++ template library of containers, algorithms, and iterators useful for runtime and tool development across multiple platforms. It is a fairly extensive and robust implementation of such a library and has an emphasis on high performance above all other considerations.
## Usage
If you are familiar with the C++ STL or have worked with other templated container/algorithm libraries, you probably don't need to read this. If you have no familiarity with C++ templates at all, then you probably will need more than this document to get you up to speed. In this case, you need to understand that templates, when used properly, are powerful vehicles for the ease of creation of optimized C++ code. A description of C++ templates is outside the scope of this documentation, but there is plenty of such documentation on the Internet.
EASTL is suitable for any tools and shipping applications where the functionality of EASTL is useful. Modern compilers are capable of producing good code with templates and many people are using them in both current generation and future generation applications on multiple platforms from embedded systems to servers and mainframes.
## Package Managers
You can download and install EASTL using the [Conan](https://github.com/conan-io/conan) package manager:
conan install eastl/3.15.00@
The EASTL package in conan is kept up to date by Conan team members and community contributors. If the version is out-of-date, please [create an issue or pull request](https://github.com/conan-io/conan-center-index) on the Conan Center Index repository.
You can download and install EASTL using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
vcpkg install eastl
The EASTL port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
## Documentation
Please see [EASTL Introduction](doc/Introduction.md).
## Compiling sources
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on compiling and testing the source.
## Credits And Maintainers
EASTL was created by Paul Pedriana and he maintained the project for roughly 10 years.
EASTL was subsequently maintained by Roberto Parolin for more than 8 years.
He was the driver and proponent for getting EASTL opensourced.
Rob was a mentor to all members of the team and taught us everything we ever wanted to know about C++ spookyness.
After Rob, maintenance of EASTL passed to Max Winkler for roughly a year, until landing with its current maintainer Liam Mitchell.
Significant EASTL contributions were made by (in alphabetical order):
* Avery Lee
* Colin Andrews
* JP Flouret
* Liam Mitchell
* Matt Newport
* Max Winkler
* Paul Pedriana
* Roberto Parolin
* Simon Everett
## Contributors
## Projects And Products Using EASTL
* Frostbite - Electronic Arts - [https://www.ea.com/frostbite]
## License
Modified BSD License (3-Clause BSD license) see the file LICENSE in the project root.
+1
View File
@@ -0,0 +1 @@
theme: jekyll-theme-minimal
+96
View File
@@ -0,0 +1,96 @@
#-------------------------------------------------------------------------------------------
# Copyright (C) Electronic Arts Inc. All rights reserved.
#-------------------------------------------------------------------------------------------
#-------------------------------------------------------------------------------------------
# CMake info
#-------------------------------------------------------------------------------------------
cmake_minimum_required(VERSION 3.1)
project(EASTLBenchmarks CXX)
include(CTest)
#-------------------------------------------------------------------------------------------
# Defines
#-------------------------------------------------------------------------------------------
add_definitions(-D_CHAR16T)
#-------------------------------------------------------------------------------------------
# Include directories
#-------------------------------------------------------------------------------------------
include_directories(source)
include_directories(../test/source)
#-------------------------------------------------------------------------------------------
# Compiler Flags
#-------------------------------------------------------------------------------------------
set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/../scripts/CMake")
include(CommonCppFlags)
# Libstdc++ calls new internally, since DLLs have no weak symbols, runtime symbol resolution fails and EASTL's new is not called.
# Linking against static libstdc++ fixes this.
# See https://github.com/electronicarts/EASTL/issues/40 for more info.
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND MINGW)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -static-libstdc++")
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} -static-libstdc++")
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} -static-libstdc++")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_BUILD_TYPE MATCHES "MinSizeRel" AND MINGW)
message(FATAL_ERROR "FIXME: MinSizeRel on MingW-w64's Clang fails to link.")
endif()
# The benchmark suite fails to compile if char8_t is enabled, so disable it.
if (EASTL_NO_CHAR8T_FLAG)
add_compile_options(${EASTL_NO_CHAR8T_FLAG})
endif()
#-------------------------------------------------------------------------------------------
# Source files
#-------------------------------------------------------------------------------------------
file(GLOB EASTLBENCHMARK_SOURCES "source/*.cpp" "../test/source/EASTLTestAllocator.cpp" "../test/source/EASTLTest.cpp")
set(SOURCES ${EASTLBENCHMARK_SOURCES})
#-------------------------------------------------------------------------------------------
# Defines
#-------------------------------------------------------------------------------------------
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
add_definitions(-DEASTL_THREAD_SUPPORT_AVAILABLE=0)
add_definitions(-DEASTL_OPENSOURCE=1)
add_definitions(-D_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS) # silence std::hash_map deprecation warnings
if (EASTL_STD_ITERATOR_CATEGORY_ENABLED)
add_definitions(-DEASTL_STD_ITERATOR_CATEGORY_ENABLED=1)
endif()
if(NOT EASTL_BUILD_TESTS)
add_subdirectory(../test/packages/EAStdC ../test/EAStdC)
add_subdirectory(../test/packages/EAAssert ../test/EAAssert)
add_subdirectory(../test/packages/EAThread ../test/EAThread)
add_subdirectory(../test/packages/EATest ../test/EATest)
add_subdirectory(../test/packages/EAMain ../test/EAMain)
endif()
#-------------------------------------------------------------------------------------------
# Executable definition
#-------------------------------------------------------------------------------------------
add_executable(EASTLBenchmarks ${EASTLBENCHMARK_SOURCES})
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
set(EASTLBenchmark_Libraries
EABase
EAAssert
EAMain
EAThread
EAStdC
EASTL
EATest)
target_link_libraries(EASTLBenchmarks ${EASTLBenchmark_Libraries} Threads::Threads)
#-------------------------------------------------------------------------------------------
# Run Unit tests and verify the results.
#-------------------------------------------------------------------------------------------
add_test(EASTLBenchmarkRuns EASTLBenchmarks)
set_tests_properties (EASTLBenchmarkRuns PROPERTIES PASS_REGULAR_EXPRESSION "RETURNCODE=0")
File diff suppressed because it is too large Load Diff
+366
View File
@@ -0,0 +1,366 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#ifdef _MSC_VER
// Microsoft STL generates warnings.
#pragma warning(disable: 4267) // 'initializing' : conversion from 'size_t' to 'const int', possible loss of data
#endif
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/bitset.h>
EA_DISABLE_ALL_VC_WARNINGS()
#include <bitset>
EA_RESTORE_ALL_VC_WARNINGS()
using namespace EA;
namespace
{
template <typename Bitset>
void TestSet(EA::StdC::Stopwatch& stopwatch, Bitset& b)
{
stopwatch.Restart();
for(int i = 0; i < 100000; i++)
{
b.set();
Benchmark::DoNothing(&b);
}
stopwatch.Stop();
}
template <typename Bitset>
void TestSetIndex(EA::StdC::Stopwatch& stopwatch, Bitset& b, size_t index)
{
stopwatch.Restart();
for(int i = 0; i < 100000; i++)
{
b.set(index);
Benchmark::DoNothing(&b);
}
stopwatch.Stop();
}
template <typename Bitset>
void TestReset(EA::StdC::Stopwatch& stopwatch, Bitset& b)
{
stopwatch.Restart();
for(int i = 0; i < 100000; i++)
{
b.reset();
Benchmark::DoNothing(&b);
}
stopwatch.Stop();
}
template <typename Bitset>
void TestFlip(EA::StdC::Stopwatch& stopwatch, Bitset& b)
{
stopwatch.Restart();
for(int i = 0; i < 100000; i++)
{
b.flip();
Benchmark::DoNothing(&b);
}
stopwatch.Stop();
}
template <typename Bitset>
void TestTest(EA::StdC::Stopwatch& stopwatch, Bitset& b, unsigned nANDValue)
{
stopwatch.Restart();
for(unsigned i = 0; i < 100000; i++)
Benchmark::DoNothing(b.test(i & nANDValue)); // We use & instead of % because the former is always fast due to forced power of 2.
stopwatch.Stop();
}
template <typename Bitset>
void TestCount(EA::StdC::Stopwatch& stopwatch, Bitset& b)
{
size_t temp = 0;
stopwatch.Restart();
for(int i = 0; i < 100000; i++)
{
temp += b.count();
Benchmark::DoNothing(&temp);
}
stopwatch.Stop();
}
template <typename Bitset>
void TestRightShift(EA::StdC::Stopwatch& stopwatch, Bitset& b, size_t n)
{
size_t temp = 0;
stopwatch.Restart();
for(int i = 0; i < 100000; i++)
{
b >>= n;
Benchmark::DoNothing(&temp);
}
stopwatch.Stop();
}
} // namespace
void BenchmarkBitset()
{
EASTLTest_Printf("Bitset\n");
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
{
std::bitset<15> stdBitset15;
eastl::bitset<15> eaBitset15;
std::bitset<35> stdBitset35;
eastl::bitset<35> eaBitset35;
std::bitset<75> stdBitset75;
eastl::bitset<75> eaBitset75;
std::bitset<1500> stdBitset1500;
eastl::bitset<1500> eaBitset1500;
for(int i = 0; i < 2; i++)
{
///////////////////////////////
// Test set()
///////////////////////////////
TestSet(stopwatch1, stdBitset15);
TestSet(stopwatch2, eaBitset15);
if(i == 1)
Benchmark::AddResult("bitset<15>/set()", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSet(stopwatch1, stdBitset35);
TestSet(stopwatch2, eaBitset35);
if(i == 1)
Benchmark::AddResult("bitset<35>/set()", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSet(stopwatch1, stdBitset75);
TestSet(stopwatch2, eaBitset75);
if(i == 1)
Benchmark::AddResult("bitset<75>/set()", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSet(stopwatch1, stdBitset1500);
TestSet(stopwatch2, eaBitset1500);
if(i == 1)
Benchmark::AddResult("bitset<1500>/set()", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test set(index)
///////////////////////////////
TestSetIndex(stopwatch1, stdBitset15, 13);
TestSetIndex(stopwatch2, eaBitset15, 13);
if(i == 1)
Benchmark::AddResult("bitset<15>/set(i)", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSetIndex(stopwatch1, stdBitset35, 33);
TestSetIndex(stopwatch2, eaBitset35, 33);
if(i == 1)
Benchmark::AddResult("bitset<35>/set(i)", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSetIndex(stopwatch1, stdBitset75, 73);
TestSetIndex(stopwatch2, eaBitset75, 73);
if(i == 1)
Benchmark::AddResult("bitset<75>/set(i)", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSetIndex(stopwatch1, stdBitset1500, 730);
TestSetIndex(stopwatch2, eaBitset1500, 730);
if(i == 1)
Benchmark::AddResult("bitset<1500>/set(i)", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test reset()
///////////////////////////////
TestReset(stopwatch1, stdBitset15);
TestReset(stopwatch2, eaBitset15);
if(i == 1)
Benchmark::AddResult("bitset<15>/reset", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestReset(stopwatch1, stdBitset35);
TestReset(stopwatch2, eaBitset35);
if(i == 1)
Benchmark::AddResult("bitset<35>/reset", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestReset(stopwatch1, stdBitset75);
TestReset(stopwatch2, eaBitset75);
if(i == 1)
Benchmark::AddResult("bitset<75>/reset", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestReset(stopwatch1, stdBitset1500);
TestReset(stopwatch2, eaBitset1500);
if(i == 1)
Benchmark::AddResult("bitset<1500>/reset", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test flip
///////////////////////////////
TestFlip(stopwatch1, stdBitset15);
TestFlip(stopwatch2, eaBitset15);
if(i == 1)
Benchmark::AddResult("bitset<15>/flip", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestFlip(stopwatch1, stdBitset35);
TestFlip(stopwatch2, eaBitset35);
if(i == 1)
Benchmark::AddResult("bitset<35>/flip", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestFlip(stopwatch1, stdBitset75);
TestFlip(stopwatch2, eaBitset75);
if(i == 1)
Benchmark::AddResult("bitset<75>/flip", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestFlip(stopwatch1, stdBitset1500);
TestFlip(stopwatch2, eaBitset1500);
if(i == 1)
Benchmark::AddResult("bitset<1500>/flip", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test test
///////////////////////////////
TestTest(stopwatch1, stdBitset15, 7);
TestTest(stopwatch2, eaBitset15, 7);
if(i == 1)
Benchmark::AddResult("bitset<15>/test", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestTest(stopwatch1, stdBitset35, 31);
TestTest(stopwatch2, eaBitset35, 31);
if(i == 1)
Benchmark::AddResult("bitset<35>/test", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestTest(stopwatch1, stdBitset75, 63);
TestTest(stopwatch2, eaBitset75, 63);
if(i == 1)
Benchmark::AddResult("bitset<75>/test", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestTest(stopwatch1, stdBitset1500, 1023);
TestTest(stopwatch2, eaBitset1500, 1023);
if(i == 1)
Benchmark::AddResult("bitset<1500>/test", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test count
///////////////////////////////
TestCount(stopwatch1, stdBitset15);
TestCount(stopwatch2, eaBitset15);
if(i == 1)
Benchmark::AddResult("bitset<15>/count", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestCount(stopwatch1, stdBitset35);
TestCount(stopwatch2, eaBitset35);
if(i == 1)
Benchmark::AddResult("bitset<35>/count", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestCount(stopwatch1, stdBitset75);
TestCount(stopwatch2, eaBitset75);
if(i == 1)
Benchmark::AddResult("bitset<75>/count", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestCount(stopwatch1, stdBitset1500);
TestCount(stopwatch2, eaBitset1500);
if(i == 1)
Benchmark::AddResult("bitset<1500>/count", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test >>=
///////////////////////////////
TestRightShift(stopwatch1, stdBitset15, 1);
TestRightShift(stopwatch2, eaBitset15, 1);
if(i == 1)
Benchmark::AddResult("bitset<15>/>>=/1", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime(),
GetStdSTLType() == kSTLPort ? "STLPort is broken, neglects wraparound check." : NULL);
TestRightShift(stopwatch1, stdBitset35, 1);
TestRightShift(stopwatch2, eaBitset35, 1);
if(i == 1)
Benchmark::AddResult("bitset<35>/>>=/1", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime(),
GetStdSTLType() == kSTLPort ? "STLPort is broken, neglects wraparound check." : NULL);
TestRightShift(stopwatch1, stdBitset75, 1);
TestRightShift(stopwatch2, eaBitset75, 1);
if(i == 1)
Benchmark::AddResult("bitset<75>/>>=/1", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime(),
GetStdSTLType() == kSTLPort ? "STLPort is broken, neglects wraparound check." : NULL);
TestRightShift(stopwatch1, stdBitset1500, 1);
TestRightShift(stopwatch2, eaBitset1500, 1);
if(i == 1)
Benchmark::AddResult("bitset<1500>/>>=/1", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime(),
GetStdSTLType() == kSTLPort ? "STLPort is broken, neglects wraparound check." : NULL);
}
}
}
+342
View File
@@ -0,0 +1,342 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/algorithm.h>
#include <EASTL/deque.h>
#include <EASTL/vector.h>
#include <EASTL/sort.h>
#ifdef _MSC_VER
#pragma warning(push, 0)
#pragma warning(disable: 4350) // behavior change: X called instead of Y
#endif
#include <algorithm>
#include <vector>
#include <deque>
#include <stdio.h>
#include <stdlib.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
using namespace EA;
namespace
{
struct ValuePair
{
uint32_t key;
uint32_t v;
};
struct VPCompare
{
bool operator()(const ValuePair& vp1, const ValuePair& vp2) const
{
return (vp1.key == vp2.key) ? (vp1.v < vp2.v) : (vp1.key < vp2.key);
}
};
bool operator<(const ValuePair& vp1, const ValuePair& vp2)
{
return (vp1.key == vp2.key) ? (vp1.v < vp2.v) : (vp1.key < vp2.key);
}
bool operator==(const ValuePair& vp1, const ValuePair& vp2)
{
return (vp1.key == vp2.key) && (vp1.v == vp2.v);
}
}
EASTL_DECLARE_POD(ValuePair)
EASTL_DECLARE_TRIVIAL_CONSTRUCTOR(ValuePair)
EASTL_DECLARE_TRIVIAL_COPY(ValuePair)
EASTL_DECLARE_TRIVIAL_ASSIGN(ValuePair)
EASTL_DECLARE_TRIVIAL_DESTRUCTOR(ValuePair)
EASTL_DECLARE_TRIVIAL_RELOCATE(ValuePair)
typedef std::deque<ValuePair> StdDeque;
typedef eastl::deque<ValuePair, EASTLAllocatorType, 128> EaDeque; // What value do we pick for the subarray size to make the comparison fair? Using the default isn't ideal because it results in this test measuring speed efficiency and ignoring memory efficiency.
namespace
{
template <typename Container>
void TestPushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector<uint32_t>& intVector)
{
stopwatch.Restart();
for(eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++)
{
const ValuePair vp = { intVector[j], intVector[j] };
c.push_back(vp);
}
stopwatch.Stop();
}
template <typename Container>
void TestPushFront(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector<uint32_t>& intVector)
{
stopwatch.Restart();
for(eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++)
{
const ValuePair vp = { intVector[j], intVector[j] };
c.push_front(vp);
}
stopwatch.Stop();
}
template <typename Container>
void TestBracket(EA::StdC::Stopwatch& stopwatch, Container& c)
{
uint64_t temp = 0;
stopwatch.Restart();
for(typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++)
temp += c[j].key;
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff));
}
template <typename Container>
void TestIteration(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::iterator it = c.begin(), itEnd = c.end();
stopwatch.Restart();
while(it != itEnd)
++it;
stopwatch.Stop();
if(it != c.end())
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(*it).key);
/* Alternative way to measure:
const eastl_size_t n = c.size();
stopwatch.Restart();
for(eastl_size_t i = 0; i < n; ++i)
++it;
stopwatch.Stop();
if(it != c.end())
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(*it).key);
*/
}
template <typename Container>
void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c)
{
// Intentionally use eastl find in order to measure just
// vector access speed and not be polluted by sort speed.
const ValuePair vp = { 0xffffffff, 0 };
stopwatch.Restart();
typename Container::iterator it = eastl::find(c.begin(), c.end(), vp);
stopwatch.Stop();
if(it != c.end())
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(*it).key);
}
template <typename Container>
void TestSort(EA::StdC::Stopwatch& stopwatch, Container& c)
{
// Intentionally use eastl sort in order to measure just
// vector access speed and not be polluted by sort speed.
VPCompare vpCompare;
stopwatch.Restart();
eastl::quick_sort(c.begin(), c.end(), vpCompare);
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c[0].key);
}
template <typename Container>
void TestInsert(EA::StdC::Stopwatch& stopwatch, Container& c)
{
const ValuePair vp = { 0xffffffff, 0 };
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for(j = 0, jEnd = 2000, it = c.begin(); j < jEnd; ++j)
{
it = c.insert(it, vp);
if(it == c.end()) // Try to safely increment the iterator three times.
it = c.begin();
if(++it == c.end())
it = c.begin();
if(++it == c.end())
it = c.begin();
}
stopwatch.Stop();
}
template <typename Container>
void TestErase(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for(j = 0, jEnd = 2000, it = c.begin(); j < jEnd; ++j)
{
it = c.erase(it);
if(it == c.end()) // Try to safely increment the iterator three times.
it = c.begin();
if(++it == c.end())
it = c.begin();
if(++it == c.end())
it = c.begin();
}
stopwatch.Stop();
}
} // namespace
void BenchmarkDeque()
{
EASTLTest_Printf("Deque\n");
EA::UnitTest::RandGenT<uint32_t> rng(EA::UnitTest::GetRandSeed());
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
{ // Exercise some declarations
int nErrorCount = 0;
ValuePair vp1 = { 0, 0 }, vp2 = { 0, 0 };
VPCompare c1, c2;
VERIFY(c1.operator()(vp1, vp2) == c2.operator()(vp1, vp2));
VERIFY((vp1 < vp2) || (vp1 == vp2) || !(vp1 == vp2));
}
{
eastl::vector<uint32_t> intVector(100000);
eastl::generate(intVector.begin(), intVector.end(), rng);
for(int i = 0; i < 2; i++)
{
StdDeque stdDeque;
EaDeque eaDeque;
///////////////////////////////
// Test push_back
///////////////////////////////
TestPushBack(stopwatch1, stdDeque, intVector);
TestPushBack(stopwatch2, eaDeque, intVector);
if(i == 1)
Benchmark::AddResult("deque<ValuePair>/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test push_front
///////////////////////////////
TestPushFront(stopwatch1, stdDeque, intVector);
TestPushFront(stopwatch2, eaDeque, intVector);
if(i == 1)
Benchmark::AddResult("deque<ValuePair>/push_front", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test operator[]
///////////////////////////////
TestBracket(stopwatch1, stdDeque);
TestBracket(stopwatch2, eaDeque);
if(i == 1)
Benchmark::AddResult("deque<ValuePair>/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test iteration
///////////////////////////////
TestIteration(stopwatch1, stdDeque);
TestIteration(stopwatch2, eaDeque);
if(i == 1)
Benchmark::AddResult("deque<ValuePair>/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test find()
///////////////////////////////
TestFind(stopwatch1, stdDeque);
TestFind(stopwatch2, eaDeque);
if(i == 1)
Benchmark::AddResult("deque<ValuePair>/find", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test sort
///////////////////////////////
// Currently VC++ complains about our sort function decrementing std::iterator that is already at begin(). In the strictest sense,
// that's a valid complaint, but we aren't testing std STL here. We will want to revise our sort function eventually.
#if !defined(_MSC_VER) || !defined(_ITERATOR_DEBUG_LEVEL) || (_ITERATOR_DEBUG_LEVEL < 2)
TestSort(stopwatch1, stdDeque);
TestSort(stopwatch2, eaDeque);
if(i == 1)
Benchmark::AddResult("deque<ValuePair>/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
#endif
///////////////////////////////
// Test insert
///////////////////////////////
TestInsert(stopwatch1, stdDeque);
TestInsert(stopwatch2, eaDeque);
if(i == 1)
Benchmark::AddResult("deque<ValuePair>/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase
///////////////////////////////
TestErase(stopwatch1, stdDeque);
TestErase(stopwatch2, eaDeque);
if(i == 1)
Benchmark::AddResult("deque<ValuePair>/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
}
}
}
+469
View File
@@ -0,0 +1,469 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/vector.h>
#include <EASTL/hash_map.h>
#include <EASTL/string.h>
#include <EASTL/algorithm.h>
EA_DISABLE_ALL_VC_WARNINGS()
#include <unordered_map>
#include <string>
#include <algorithm>
#include <stdio.h>
EA_RESTORE_ALL_VC_WARNINGS()
using namespace EA;
// HashString8
//
// We define a string
//
template <typename String>
struct HashString8
{
// Defined for EASTL, STLPort, SGI, etc. and Metrowerks-related hash tables:
size_t operator()(const String& s) const
{
const uint8_t* p = (const uint8_t*) s.c_str();
uint32_t c, stringHash = UINT32_C(2166136261);
while((c = *p++) != 0)
stringHash = (stringHash * 16777619) ^ c;
return stringHash;
}
// Defined for Dinkumware-related (e.g. MS STL) hash tables:
bool operator()(const String& s1, const String& s2) const
{
return s1 < s2;
}
// Defined for Dinkumware-related (e.g. MS STL) hash tables:
enum {
bucket_size = 7,
min_buckets = 8
};
};
using StdMapUint32TO = std::unordered_map<uint32_t, TestObject>;
using StdMapStrUint32 = std::unordered_map<std::string, uint32_t, HashString8<std::string>>;
using EaMapUint32TO = eastl::hash_map<uint32_t, TestObject>;
using EaMapStrUint32 = eastl::hash_map<eastl::string, uint32_t, HashString8<eastl::string>>;
namespace
{
template <typename Container, typename Value>
void TestInsert(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
c.insert(pArrayBegin, pArrayEnd);
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestIteration(EA::StdC::Stopwatch& stopwatch, const Container& c, const Value& findValue)
{
stopwatch.Restart();
typename Container::const_iterator it = eastl::find(c.begin(), c.end(), findValue); // It shouldn't matter what find implementation we use here, as it merely iterates values.
stopwatch.Stop();
if(it != c.end())
sprintf(Benchmark::gScratchBuffer, "%p", &*it);
}
template <typename Container, typename Value>
void TestBracket(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
Benchmark::DoNothing(&c[pArrayBegin->first]);
++pArrayBegin;
}
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
typename Container::iterator it = c.find(pArrayBegin->first);
Benchmark::DoNothing(&it);
++pArrayBegin;
}
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestFindAsStd(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
typename Container::iterator it = c.find(pArrayBegin->first.c_str());
Benchmark::DoNothing(&it);
++pArrayBegin;
}
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestFindAsEa(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
typename Container::iterator it = c.find_as(pArrayBegin->first.c_str());
Benchmark::DoNothing(&it);
++pArrayBegin;
}
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestCount(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
typename Container::size_type temp = 0;
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
temp += c.count(pArrayBegin->first);
++pArrayBegin;
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp);
}
template <typename Container, typename Value>
void TestEraseValue(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
c.erase(pArrayBegin->first);
++pArrayBegin;
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
}
template <typename Container>
void TestErasePosition(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for(j = 0, jEnd = c.size() / 3, it = c.begin(); j < jEnd; ++j)
{
// The erase fucntion is supposed to return an iterator, but the C++ standard was
// not initially clear about it and some STL implementations don't do it correctly.
#if (defined(_MSC_VER) || defined(_CPPLIB_VER)) // _CPPLIB_VER is something defined by Dinkumware STL.
it = c.erase(it);
#else
// This pathway may execute at a slightly different speed than the
// standard behaviour, but that's fine for the benchmark because the
// benchmark is measuring the speed of erasing while iterating, and
// however it needs to get done by the given STL is how it is measured.
const typename Container::iterator itErase(it++);
c.erase(itErase);
#endif
++it;
++it;
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%p %p", &c, &it);
}
template <typename Container>
void TestEraseRange(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it1 = c.begin();
typename Container::iterator it2 = c.begin();
for(j = 0, jEnd = c.size() / 3; j < jEnd; ++j)
++it2;
stopwatch.Restart();
c.erase(it1, it2);
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%p %p %p", &c, &it1, &it2);
}
template <typename Container>
void TestClear(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
c.clear();
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
}
} // namespace
void BenchmarkHash()
{
EASTLTest_Printf("HashMap\n");
EA::UnitTest::Rand rng(EA::UnitTest::GetRandSeed());
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
{
eastl::vector< std::pair<uint32_t, TestObject> > stdVectorUT(10000);
eastl::vector< eastl::pair<uint32_t, TestObject> > eaVectorUT(10000);
eastl::vector< std::pair< std::string, uint32_t> > stdVectorSU(10000);
eastl::vector< eastl::pair<eastl::string, uint32_t> > eaVectorSU(10000);
for(eastl_size_t i = 0, iEnd = stdVectorUT.size(); i < iEnd; i++)
{
const uint32_t n1 = rng.RandLimit((uint32_t)(iEnd / 2));
const uint32_t n2 = rng.RandValue();
stdVectorUT[i] = std::pair<uint32_t, TestObject>(n1, TestObject(n2));
eaVectorUT[i] = eastl::pair<uint32_t, TestObject>(n1, TestObject(n2));
char str_n1[32];
sprintf(str_n1, "%u", (unsigned)n1);
stdVectorSU[i] = std::pair< std::string, uint32_t>( std::string(str_n1), n2);
eaVectorSU[i] = eastl::pair<eastl::string, uint32_t>(eastl::string(str_n1), n2);
}
for(int i = 0; i < 2; i++)
{
StdMapUint32TO stdMapUint32TO;
EaMapUint32TO eaMapUint32TO;
StdMapStrUint32 stdMapStrUint32;
EaMapStrUint32 eaMapStrUint32;
///////////////////////////////
// Test insert(const value_type&)
///////////////////////////////
TestInsert(stopwatch1, stdMapUint32TO, stdVectorUT.data(), stdVectorUT.data() + stdVectorUT.size());
TestInsert(stopwatch2, eaMapUint32TO, eaVectorUT.data(), eaVectorUT.data() + eaVectorUT.size());
if(i == 1)
Benchmark::AddResult("hash_map<uint32_t, TestObject>/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestInsert(stopwatch1, stdMapStrUint32, stdVectorSU.data(), stdVectorSU.data() + stdVectorSU.size());
TestInsert(stopwatch2, eaMapStrUint32, eaVectorSU.data(), eaVectorSU.data() + eaVectorSU.size());
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test iteration
///////////////////////////////
TestIteration(stopwatch1, stdMapUint32TO, StdMapUint32TO::value_type(9999999, TestObject(9999999)));
TestIteration(stopwatch2, eaMapUint32TO, EaMapUint32TO::value_type(9999999, TestObject(9999999)));
if(i == 1)
Benchmark::AddResult("hash_map<uint32_t, TestObject>/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestIteration(stopwatch1, stdMapStrUint32, StdMapStrUint32::value_type( std::string("9999999"), 9999999));
TestIteration(stopwatch2, eaMapStrUint32, EaMapStrUint32::value_type(eastl::string("9999999"), 9999999));
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test operator[]
///////////////////////////////
TestBracket(stopwatch1, stdMapUint32TO, stdVectorUT.data(), stdVectorUT.data() + stdVectorUT.size());
TestBracket(stopwatch2, eaMapUint32TO, eaVectorUT.data(), eaVectorUT.data() + eaVectorUT.size());
if(i == 1)
Benchmark::AddResult("hash_map<uint32_t, TestObject>/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestBracket(stopwatch1, stdMapStrUint32, stdVectorSU.data(), stdVectorSU.data() + stdVectorSU.size());
TestBracket(stopwatch2, eaMapStrUint32, eaVectorSU.data(), eaVectorSU.data() + eaVectorSU.size());
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test find
///////////////////////////////
TestFind(stopwatch1, stdMapUint32TO, stdVectorUT.data(), stdVectorUT.data() + stdVectorUT.size());
TestFind(stopwatch2, eaMapUint32TO, eaVectorUT.data(), eaVectorUT.data() + eaVectorUT.size());
if(i == 1)
Benchmark::AddResult("hash_map<uint32_t, TestObject>/find", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestFind(stopwatch1, stdMapStrUint32, stdVectorSU.data(), stdVectorSU.data() + stdVectorSU.size());
TestFind(stopwatch2, eaMapStrUint32, eaVectorSU.data(), eaVectorSU.data() + eaVectorSU.size());
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/find", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test find_as
///////////////////////////////
TestFindAsStd(stopwatch1, stdMapStrUint32, stdVectorSU.data(), stdVectorSU.data() + stdVectorSU.size());
TestFindAsEa(stopwatch2, eaMapStrUint32, eaVectorSU.data(), eaVectorSU.data() + eaVectorSU.size());
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/find_as/char*", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test count
///////////////////////////////
TestCount(stopwatch1, stdMapUint32TO, stdVectorUT.data(), stdVectorUT.data() + stdVectorUT.size());
TestCount(stopwatch2, eaMapUint32TO, eaVectorUT.data(), eaVectorUT.data() + eaVectorUT.size());
if(i == 1)
Benchmark::AddResult("hash_map<uint32_t, TestObject>/count", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestCount(stopwatch1, stdMapStrUint32, stdVectorSU.data(), stdVectorSU.data() + stdVectorSU.size());
TestCount(stopwatch2, eaMapStrUint32, eaVectorSU.data(), eaVectorSU.data() + eaVectorSU.size());
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/count", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase(const key_type& key)
///////////////////////////////
TestEraseValue(stopwatch1, stdMapUint32TO, stdVectorUT.data(), stdVectorUT.data() + (stdVectorUT.size() / 2));
TestEraseValue(stopwatch2, eaMapUint32TO, eaVectorUT.data(), eaVectorUT.data() + (eaVectorUT.size() / 2));
if(i == 1)
Benchmark::AddResult("hash_map<uint32_t, TestObject>/erase val", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestEraseValue(stopwatch1, stdMapStrUint32, stdVectorSU.data(), stdVectorSU.data() + (stdVectorSU.size() / 2));
TestEraseValue(stopwatch2, eaMapStrUint32, eaVectorSU.data(), eaVectorSU.data() + (eaVectorSU.size() / 2));
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/erase val", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase(iterator position)
///////////////////////////////
TestErasePosition(stopwatch1, stdMapUint32TO);
TestErasePosition(stopwatch2, eaMapUint32TO);
if(i == 1)
Benchmark::AddResult("hash_map<uint32_t, TestObject>/erase pos", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestErasePosition(stopwatch1, stdMapStrUint32);
TestErasePosition(stopwatch2, eaMapStrUint32);
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/erase pos", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase(iterator first, iterator last)
///////////////////////////////
TestEraseRange(stopwatch1, stdMapUint32TO);
TestEraseRange(stopwatch2, eaMapUint32TO);
if(i == 1)
Benchmark::AddResult("hash_map<uint32_t, TestObject>/erase range", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestEraseRange(stopwatch1, stdMapStrUint32);
TestEraseRange(stopwatch2, eaMapStrUint32);
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/erase range", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test clear()
///////////////////////////////
// Clear the containers of whatever they happen to have. We want the containers to have full data.
TestClear(stopwatch1, stdMapUint32TO);
TestClear(stopwatch2, eaMapUint32TO);
TestClear(stopwatch1, stdMapStrUint32);
TestClear(stopwatch2, eaMapStrUint32);
// Re-set the containers with full data.
TestInsert(stopwatch1, stdMapUint32TO, stdVectorUT.data(), stdVectorUT.data() + stdVectorUT.size());
TestInsert(stopwatch2, eaMapUint32TO, eaVectorUT.data(), eaVectorUT.data() + eaVectorUT.size());
TestInsert(stopwatch1, stdMapStrUint32, stdVectorSU.data(), stdVectorSU.data() + stdVectorSU.size());
TestInsert(stopwatch2, eaMapStrUint32, eaVectorSU.data(), eaVectorSU.data() + eaVectorSU.size());
// Now clear the data again, this time measuring it.
TestClear(stopwatch1, stdMapUint32TO);
TestClear(stopwatch2, eaMapUint32TO);
if(i == 1)
Benchmark::AddResult("hash_map<uint32_t, TestObject>/clear", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestClear(stopwatch1, stdMapStrUint32);
TestClear(stopwatch2, eaMapStrUint32);
if(i == 1)
Benchmark::AddResult("hash_map<string, uint32_t>/clear", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
}
}
}
+238
View File
@@ -0,0 +1,238 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/heap.h>
#include <EASTL/vector.h>
#include <EASTL/algorithm.h>
#ifdef _MSC_VER
#pragma warning(push, 0)
#pragma warning(disable: 4350) // behavior change: X called instead of Y
#endif
#include <algorithm>
#include <vector>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
using namespace EA;
namespace
{
template <typename Iterator>
void TestMakeHeapStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last)
{
stopwatch.Restart();
std::make_heap(first, last);
stopwatch.Stop();
}
template <typename Iterator>
void TestMakeHeapEa(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last)
{
stopwatch.Restart();
eastl::make_heap(first, last);
stopwatch.Stop();
}
template <typename Iterator1, typename Iterator2>
void TestPushHeapStd(EA::StdC::Stopwatch& stopwatch, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
{
stopwatch.Restart();
while(first2 != last2)
{
*last1++ = *first2++;
std::push_heap(first1, last1);
}
stopwatch.Stop();
}
template <typename Iterator1, typename Iterator2>
void TestPushHeapEa(EA::StdC::Stopwatch& stopwatch, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
{
stopwatch.Restart();
while(first2 != last2)
{
*last1++ = *first2++;
eastl::push_heap(first1, last1);
}
stopwatch.Stop();
}
template <typename Iterator>
void TestPopHeapStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last, Iterator popEnd)
{
stopwatch.Restart();
while(last != popEnd)
std::pop_heap(first, last--);
stopwatch.Stop();
}
template <typename Iterator>
void TestPopHeapEa(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last, Iterator popEnd)
{
stopwatch.Restart();
while(last != popEnd)
eastl::pop_heap(first, last--);
stopwatch.Stop();
}
template <typename Iterator>
void TestSortHeapStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last)
{
stopwatch.Restart();
std::sort_heap(first, last);
stopwatch.Stop();
}
template <typename Iterator>
void TestSortHeapEa(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last)
{
stopwatch.Restart();
eastl::sort_heap(first, last);
stopwatch.Stop();
}
} // namespace
void BenchmarkHeap()
{
EASTLTest_Printf("Heap (Priority Queue)\n");
EA::UnitTest::RandGenT<uint32_t> rng(EA::UnitTest::GetRandSeed());
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
{
const int kArraySize = 100000;
// uint32[]
uint32_t* const pIntArrayS = new uint32_t[kArraySize * 2]; // * 2 because we will be adding more items via push_heap.
uint32_t* const pIntArrayE = new uint32_t[kArraySize * 2]; // S means Std; E means EA.
uint32_t* const pIntArray2 = new uint32_t[kArraySize]; // This will be used for pop_heap.
eastl::generate(pIntArrayS, pIntArrayS + kArraySize, rng);
eastl::copy(pIntArrayS, pIntArrayS + kArraySize, pIntArrayE);
eastl::copy(pIntArrayS, pIntArrayS + kArraySize, pIntArray2);
// vector<TestObject>
std::vector<TestObject> stdVectorTO(kArraySize * 2);
std::vector<TestObject> stdVectorTO2(kArraySize);
eastl::vector<TestObject> eaVectorTO(kArraySize * 2);
eastl::vector<TestObject> eaVectorTO2(kArraySize);
for(int k = 0; k < kArraySize; k++)
{
stdVectorTO[k] = TestObject(pIntArrayS[k]);
stdVectorTO2[k] = TestObject(pIntArrayS[k]);
eaVectorTO[k] = TestObject(pIntArrayS[k]);
eaVectorTO2[k] = TestObject(pIntArrayS[k]);
}
for(int i = 0; i < 2; i++)
{
///////////////////////////////
// Test make_heap
///////////////////////////////
TestMakeHeapStd(stopwatch1, pIntArrayS, pIntArrayS + kArraySize);
TestMakeHeapEa (stopwatch2, pIntArrayE, pIntArrayE + kArraySize);
if(i == 1)
Benchmark::AddResult("heap (uint32_t[])/make_heap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestMakeHeapStd(stopwatch1, stdVectorTO.begin(), stdVectorTO.begin() + kArraySize);
TestMakeHeapEa (stopwatch2, eaVectorTO.begin(), eaVectorTO.begin() + kArraySize);
if(i == 1)
Benchmark::AddResult("heap (vector<TestObject>)/make_heap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test push_heap
///////////////////////////////
TestPushHeapStd(stopwatch1, pIntArrayS, pIntArrayS + kArraySize, pIntArray2, pIntArray2 + kArraySize);
TestPushHeapEa (stopwatch2, pIntArrayE, pIntArrayE + kArraySize, pIntArray2, pIntArray2 + kArraySize);
if(i == 1)
Benchmark::AddResult("heap (uint32_t[])/push_heap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestPushHeapStd(stopwatch1, stdVectorTO.begin(), stdVectorTO.begin() + kArraySize, stdVectorTO2.begin(), stdVectorTO2.begin() + kArraySize);
TestPushHeapEa (stopwatch2, eaVectorTO.begin(), eaVectorTO.begin() + kArraySize, eaVectorTO2.begin(), eaVectorTO2.begin() + kArraySize);
if(i == 1)
Benchmark::AddResult("heap (vector<TestObject>)/push_heap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test pop_heap
///////////////////////////////
TestPopHeapStd(stopwatch1, pIntArrayS, pIntArrayS + (kArraySize * 2), pIntArrayS + kArraySize); // * 2 because we used push_heap above to add more items.
TestPopHeapEa (stopwatch2, pIntArrayE, pIntArrayE + (kArraySize * 2), pIntArrayE + kArraySize);
if(i == 1)
Benchmark::AddResult("heap (uint32_t[])/pop_heap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestPopHeapStd(stopwatch1, stdVectorTO.begin(), stdVectorTO.begin() + (kArraySize * 2), stdVectorTO.begin() + kArraySize); // * 2 because we used push_heap above to add more items.
TestPopHeapEa (stopwatch2, eaVectorTO.begin(), eaVectorTO.begin() + (kArraySize * 2), eaVectorTO.begin() + kArraySize);
if(i == 1)
Benchmark::AddResult("heap (vector<TestObject>)/pop_heap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test sort_heap
///////////////////////////////
TestSortHeapStd(stopwatch1, pIntArrayS, pIntArrayS + kArraySize);
TestSortHeapEa (stopwatch2, pIntArrayE, pIntArrayE + kArraySize);
if(i == 1)
Benchmark::AddResult("heap (uint32_t[])/sort_heap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSortHeapStd(stopwatch1, stdVectorTO.begin(), stdVectorTO.begin() + kArraySize);
TestSortHeapEa (stopwatch2, eaVectorTO.begin(), eaVectorTO.begin() + kArraySize);
if(i == 1)
Benchmark::AddResult("heap (vector<TestObject>)/sort_heap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
}
delete[] pIntArrayS;
delete[] pIntArrayE;
delete[] pIntArray2;
}
}
+382
View File
@@ -0,0 +1,382 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/list.h>
#include <EASTL/vector.h>
#include <EASTL/algorithm.h>
#include <EASTL/random.h>
#ifdef _MSC_VER
#pragma warning(push, 0)
#pragma warning(disable: 4555) // expression has no effect; expected expression with side-effect
#pragma warning(disable: 4350) // behavior change: X called instead of Y
#endif
#include <list>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
using namespace EA;
using namespace eastl;
typedef std::list<TestObject> StdListTO;
typedef eastl::list<TestObject> EaListTO;
namespace
{
void DoNothing(void*)
{
// Empty
}
template <typename ContainerSource, typename Container>
void TestCtorIterator(EA::StdC::Stopwatch& stopwatch, const ContainerSource& cs, Container*) // Dummy Container argument because of GCC 2.X limitations.
{
stopwatch.Restart();
Container c(cs.begin(), cs.end());
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX);
}
template <typename Container>
void TestCtorN(EA::StdC::Stopwatch& stopwatch, Container*) // Dummy Container argument because of GCC 2.X limitations.
{
stopwatch.Restart();
Container c(10000);
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX);
}
template <typename Container>
void TestPushBack(EA::StdC::Stopwatch& stopwatch, Container& c, const TestObject* pTOBegin, const TestObject* const pTOEnd)
{
stopwatch.Restart();
while(pTOBegin != pTOEnd)
c.push_back(*pTOBegin++);
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX);
}
template <typename Container>
void TestInsert(EA::StdC::Stopwatch& stopwatch, Container& c, const TestObject* pTOBegin, const TestObject* const pTOEnd)
{
typename Container::iterator it = c.begin();
stopwatch.Restart();
while(pTOBegin != pTOEnd)
{
it = c.insert(it, *pTOBegin++);
if(++it == c.end()) // Try to safely increment the iterator a couple times
it = c.begin();
if(++it == c.end())
it = c.begin();
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX);
}
template <typename Container>
void TestSize(EA::StdC::Stopwatch& stopwatch, Container& c, void (*pFunction)(...))
{
stopwatch.Restart();
for(int i = 0; (i < 10000) && c.size(); i++)
(*pFunction)(&c);
stopwatch.Stop();
}
template <typename Container>
void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c, const TestObject& to)
{
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
stopwatch.Restart();
typename Container::iterator it = eastl::find(c.begin(), c.end(), to);
stopwatch.Stop();
if(it != c.end())
sprintf(Benchmark::gScratchBuffer, "%d", (*it).mX);
}
template <typename Container>
void TestReverse(EA::StdC::Stopwatch& stopwatch, Container& c)
{
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
stopwatch.Restart();
c.reverse();
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX);
}
template <typename Container>
void TestRemove(EA::StdC::Stopwatch& stopwatch, Container& c, const TestObject* pTOBegin, const TestObject* const pTOEnd)
{
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
stopwatch.Restart();
while(pTOBegin != pTOEnd)
c.remove(*pTOBegin++);
stopwatch.Stop();
if(!c.empty())
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX);
}
template <typename Container>
void TestSplice(EA::StdC::Stopwatch& stopwatch, Container& c, Container& cSource)
{
typename Container::iterator it = c.begin();
int i = 0, iEnd = (int)cSource.size() - 5;
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
stopwatch.Restart();
while(i++ != iEnd)
c.splice(it, cSource, cSource.begin());
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX);
}
template <typename Container>
void TestErase(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::iterator it = c.begin();
int i = 0, iEnd = (int)c.size() - 5;
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
stopwatch.Restart();
while(i++ != iEnd)
{
it = c.erase(it);
if(it == c.end()) // Try to safely increment the iterator a couple times
it = c.begin();
if(++it == c.end())
it = c.begin();
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.back().mX);
}
} // namespace
void BenchmarkList()
{
EASTLTest_Printf("List\n");
EASTLTest_Rand rng(EA::UnitTest::GetRandSeed());
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
EaListTO eaListTO_1(1);
EaListTO eaListTO_10(10);
EaListTO eaListTO_100(100);
StdListTO stdListTO_1(1);
StdListTO stdListTO_10(10);
StdListTO stdListTO_100(100);
{
char buffer[32];
sprintf(buffer, "%p", &DoNothing);
}
{
eastl::vector<TestObject> toVector(100000);
for(eastl_size_t i = 0, iEnd = toVector.size(); i < iEnd; ++i)
toVector[i] = TestObject((int)i);
random_shuffle(toVector.begin(), toVector.end(), rng);
for(int i = 0; i < 2; i++)
{
StdListTO stdListTO;
EaListTO eaListTO;
///////////////////////////////
// Test list(InputIterator first, InputIterator last)
///////////////////////////////
TestCtorIterator(stopwatch1, toVector, &stdListTO);
TestCtorIterator(stopwatch2, toVector, &eaListTO);
if(i == 1)
Benchmark::AddResult("list<TestObject>/ctor(it)", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test list(size_type n)
///////////////////////////////
TestCtorN(stopwatch1, &stdListTO);
TestCtorN(stopwatch2, &eaListTO);
if(i == 1)
Benchmark::AddResult("list<TestObject>/ctor(n)", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test push_back()
///////////////////////////////
TestPushBack(stopwatch1, stdListTO, toVector.data(), toVector.data() + toVector.size());
TestPushBack(stopwatch2, eaListTO, toVector.data(), toVector.data() + toVector.size());
if(i == 1)
Benchmark::AddResult("list<TestObject>/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test insert()
///////////////////////////////
TestInsert(stopwatch1, stdListTO, toVector.data(), toVector.data() + toVector.size());
TestInsert(stopwatch2, eaListTO, toVector.data(), toVector.data() + toVector.size());
if(i == 1)
Benchmark::AddResult("list<TestObject>/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test size()
///////////////////////////////
TestSize(stopwatch1, stdListTO_1, Benchmark::DoNothing);
TestSize(stopwatch2, eaListTO_1, Benchmark::DoNothing);
if(i == 1)
Benchmark::AddResult("list<TestObject>/size/1", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSize(stopwatch1, stdListTO_10, Benchmark::DoNothing);
TestSize(stopwatch2, eaListTO_10, Benchmark::DoNothing);
if(i == 1)
Benchmark::AddResult("list<TestObject>/size/10", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()
#if !EASTL_LIST_SIZE_CACHE
, "EASTL is configured to not cache the list size."
#endif
);
TestSize(stopwatch1, stdListTO_100, Benchmark::DoNothing);
TestSize(stopwatch2, eaListTO_100, Benchmark::DoNothing);
if(i == 1)
Benchmark::AddResult("list<TestObject>/size/100", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()
#if !EASTL_LIST_SIZE_CACHE
, "EASTL is configured to not cache the list size."
#endif
);
///////////////////////////////
// Test find()
///////////////////////////////
TestFind(stopwatch1, stdListTO, TestObject(99999999));
TestFind(stopwatch2, eaListTO, TestObject(99999999));
if(i == 1)
Benchmark::AddResult("list<TestObject>/find", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test reverse()
///////////////////////////////
TestReverse(stopwatch1, stdListTO);
TestReverse(stopwatch2, eaListTO);
if(i == 1)
Benchmark::AddResult("list<TestObject>/reverse", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test remove()
///////////////////////////////
random_shuffle(toVector.begin(), toVector.end(), rng);
TestRemove(stopwatch1, stdListTO, &toVector[0], &toVector[20]);
TestRemove(stopwatch2, eaListTO, &toVector[0], &toVector[20]);
if(i == 1)
Benchmark::AddResult("list<TestObject>/remove", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test splice()
///////////////////////////////
StdListTO listCopyStd(stdListTO);
EaListTO listCopyEa(eaListTO);
TestSplice(stopwatch1, stdListTO, listCopyStd);
TestSplice(stopwatch2, eaListTO, listCopyEa);
if(i == 1)
Benchmark::AddResult("list<TestObject>/splice", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase()
///////////////////////////////
TestErase(stopwatch1, stdListTO);
TestErase(stopwatch2, eaListTO);
if(i == 1)
Benchmark::AddResult("list<TestObject>/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
}
}
}
+382
View File
@@ -0,0 +1,382 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/map.h>
#include <EASTL/vector.h>
#include <EASTL/algorithm.h>
EA_DISABLE_ALL_VC_WARNINGS()
#include <map>
#include <algorithm>
EA_RESTORE_ALL_VC_WARNINGS()
using namespace EA;
typedef std::map<TestObject, uint32_t> StdMapTOUint32;
typedef eastl::map<TestObject, uint32_t> EaMapTOUint32;
namespace
{
template <typename Container, typename Value>
void TestInsert(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd, const Value& highValue)
{
stopwatch.Restart();
c.insert(pArrayBegin, pArrayEnd);
stopwatch.Stop();
c.insert(highValue);
}
template <typename Container, typename Value>
void TestIteration(EA::StdC::Stopwatch& stopwatch, const Container& c, const Value& findValue)
{
stopwatch.Restart();
typename Container::const_iterator it = eastl::find(c.begin(), c.end(), findValue); // It shouldn't matter what find implementation we use here, as it merely iterates values.
stopwatch.Stop();
if(it != c.end())
sprintf(Benchmark::gScratchBuffer, "%p", &*it);
}
template <typename Container, typename Value>
void TestBracket(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
Benchmark::DoNothing(c[pArrayBegin->first]);
++pArrayBegin;
}
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
Benchmark::DoNothing(c.find(pArrayBegin->first)->second);
++pArrayBegin;
}
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestCount(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
typename Container::size_type temp = 0;
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
temp += c.count(pArrayBegin->first);
++pArrayBegin;
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp);
}
template <typename Container, typename Value>
void TestLowerBound(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
Benchmark::DoNothing(c.lower_bound(pArrayBegin->first)->second);
++pArrayBegin;
}
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestUpperBound(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
Benchmark::DoNothing(c.upper_bound(pArrayBegin->first)->second);
++pArrayBegin;
}
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestEqualRange(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
Benchmark::DoNothing(c.equal_range(pArrayBegin->first).second->second);
++pArrayBegin;
}
stopwatch.Stop();
}
template <typename Container, typename Value>
void TestEraseValue(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
c.erase(pArrayBegin->first);
++pArrayBegin;
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
}
template <typename Container>
void TestErasePosition(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for(j = 0, jEnd = c.size() / 3, it = c.begin(); j < jEnd; ++j)
{
// The erase fucntion is supposed to return an iterator, but the C++ standard was
// not initially clear about it and some STL implementations don't do it correctly.
#if (((defined(_MSC_VER) || defined(_CPPLIB_VER)) && !defined(_HAS_STRICT_CONFORMANCE))) // _CPPLIB_VER is something defined by Dinkumware STL.
it = c.erase(it); // Standard behavior.
#else
// This pathway may execute at a slightly different speed than the
// standard behaviour, but that's fine for the benchmark because the
// benchmark is measuring the speed of erasing while iterating, and
// however it needs to get done by the given STL is how it is measured.
const typename Container::iterator itErase(it++);
c.erase(itErase);
#endif
++it;
++it;
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%p %p", &c, &it);
}
template <typename Container>
void TestEraseRange(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it1 = c.begin();
typename Container::iterator it2 = c.begin();
for(j = 0, jEnd = c.size() / 3; j < jEnd; ++j)
++it2;
stopwatch.Restart();
c.erase(it1, it2);
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%p %p %p", &c, &it1, &it2);
}
template <typename Container>
void TestClear(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
c.clear();
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
}
} // namespace
void BenchmarkMap()
{
EASTLTest_Printf("Map\n");
EA::UnitTest::Rand rng(EA::UnitTest::GetRandSeed());
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
{
eastl::vector< std::pair<TestObject, uint32_t> > stdVector(10000);
eastl::vector< eastl::pair<TestObject, uint32_t> > eaVector(10000);
for(eastl_size_t i = 0, iEnd = stdVector.size(); i < iEnd; i++)
{
const uint32_t n1 = rng.RandLimit(((uint32_t)iEnd / 2));
const uint32_t n2 = rng.RandValue();
stdVector[i] = std::pair<TestObject, uint32_t>(TestObject(n1), n2);
eaVector[i] = eastl::pair<TestObject, uint32_t>(TestObject(n1), n2);
}
for(int i = 0; i < 2; i++)
{
StdMapTOUint32 stdMapTOUint32;
EaMapTOUint32 eaMapTOUint32;
///////////////////////////////
// Test insert(const value_type&)
///////////////////////////////
const std::pair<TestObject, uint32_t> stdHighValue(TestObject(0x7fffffff), 0x7fffffff);
const eastl::pair<TestObject, uint32_t> eaHighValue(TestObject(0x7fffffff), 0x7fffffff);
TestInsert(stopwatch1, stdMapTOUint32, stdVector.data(), stdVector.data() + stdVector.size(), stdHighValue);
TestInsert(stopwatch2, eaMapTOUint32, eaVector.data(), eaVector.data() + eaVector.size(), eaHighValue);
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test iteration
///////////////////////////////
TestIteration(stopwatch1, stdMapTOUint32, StdMapTOUint32::value_type(TestObject(9999999), 9999999));
TestIteration(stopwatch2, eaMapTOUint32, EaMapTOUint32::value_type(TestObject(9999999), 9999999));
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test operator[]
///////////////////////////////
TestBracket(stopwatch1, stdMapTOUint32, stdVector.data(), stdVector.data() + stdVector.size());
TestBracket(stopwatch2, eaMapTOUint32, eaVector.data(), eaVector.data() + eaVector.size());
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test find
///////////////////////////////
TestFind(stopwatch1, stdMapTOUint32, stdVector.data(), stdVector.data() + stdVector.size());
TestFind(stopwatch2, eaMapTOUint32, eaVector.data(), eaVector.data() + eaVector.size());
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/find", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test count
///////////////////////////////
TestCount(stopwatch1, stdMapTOUint32, stdVector.data(), stdVector.data() + stdVector.size());
TestCount(stopwatch2, eaMapTOUint32, eaVector.data(), eaVector.data() + eaVector.size());
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/count", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test lower_bound
///////////////////////////////
TestLowerBound(stopwatch1, stdMapTOUint32, stdVector.data(), stdVector.data() + stdVector.size());
TestLowerBound(stopwatch2, eaMapTOUint32, eaVector.data(), eaVector.data() + eaVector.size());
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/lower_bound", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test upper_bound
///////////////////////////////
TestUpperBound(stopwatch1, stdMapTOUint32, stdVector.data(), stdVector.data() + stdVector.size());
TestUpperBound(stopwatch2, eaMapTOUint32, eaVector.data(), eaVector.data() + eaVector.size());
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/upper_bound", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test equal_range
///////////////////////////////
TestEqualRange(stopwatch1, stdMapTOUint32, stdVector.data(), stdVector.data() + stdVector.size());
TestEqualRange(stopwatch2, eaMapTOUint32, eaVector.data(), eaVector.data() + eaVector.size());
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/equal_range", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase(const key_type& key)
///////////////////////////////
TestEraseValue(stopwatch1, stdMapTOUint32, stdVector.data(), stdVector.data() + (stdVector.size() / 2));
TestEraseValue(stopwatch2, eaMapTOUint32, eaVector.data(), eaVector.data() + (eaVector.size() / 2));
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/erase/key", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase(iterator position)
///////////////////////////////
TestErasePosition(stopwatch1, stdMapTOUint32);
TestErasePosition(stopwatch2, eaMapTOUint32);
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/erase/pos", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime(),
GetStdSTLType() == kSTLMS ? "MS uses a code bloating implementation of erase." : NULL);
///////////////////////////////
// Test erase(iterator first, iterator last)
///////////////////////////////
TestEraseRange(stopwatch1, stdMapTOUint32);
TestEraseRange(stopwatch2, eaMapTOUint32);
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/erase/range", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test clear()
///////////////////////////////
TestClear(stopwatch1, stdMapTOUint32);
TestClear(stopwatch2, eaMapTOUint32);
if(i == 1)
Benchmark::AddResult("map<TestObject, uint32_t>/clear", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
}
}
}
+353
View File
@@ -0,0 +1,353 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/set.h>
#include <EASTL/vector.h>
#include <EASTL/algorithm.h>
EA_DISABLE_ALL_VC_WARNINGS()
#include <set>
#include <algorithm>
EA_RESTORE_ALL_VC_WARNINGS()
using namespace EA;
typedef std::set<uint32_t> StdSetUint32;
typedef eastl::set<uint32_t> EaSetUint32;
namespace
{
template <typename Container>
void TestInsert(EA::StdC::Stopwatch& stopwatch, Container& c, const uint32_t* pArrayBegin, const uint32_t* pArrayEnd)
{
stopwatch.Restart();
c.insert(pArrayBegin, pArrayEnd);
stopwatch.Stop();
// Intentionally push back a high uint32_t value. We do this so that
// later upper_bound, lower_bound and equal_range never return end().
c.insert(0xffffffff);
}
template <typename Container>
void TestIteration(EA::StdC::Stopwatch& stopwatch, const Container& c)
{
stopwatch.Restart();
typename Container::const_iterator it = eastl::find(c.begin(), c.end(), uint32_t(9999999));
stopwatch.Stop();
if(it != c.end())
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)*it);
}
template <typename Container>
void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c, const uint32_t* pArrayBegin, const uint32_t* pArrayEnd)
{
uint32_t temp = 0;
typename Container::iterator it;
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
it = c.find(*pArrayBegin++);
temp += *it;
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp);
}
template <typename Container>
void TestCount(EA::StdC::Stopwatch& stopwatch, Container& c, const uint32_t* pArrayBegin, const uint32_t* pArrayEnd)
{
typename Container::size_type temp = 0;
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
temp += c.count(*pArrayBegin++);
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp);
}
template <typename Container>
void TestLowerBound(EA::StdC::Stopwatch& stopwatch, Container& c, const uint32_t* pArrayBegin, const uint32_t* pArrayEnd)
{
uint32_t temp = 0;
typename Container::iterator it;
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
it = c.lower_bound(*pArrayBegin++);
temp += *it; // We know that it != end because earlier we inserted 0xffffffff.
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp);
}
template <typename Container>
void TestUpperBound(EA::StdC::Stopwatch& stopwatch, Container& c, const uint32_t* pArrayBegin, const uint32_t* pArrayEnd)
{
uint32_t temp = 0;
typename Container::iterator it;
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
it = c.upper_bound(*pArrayBegin++);
temp += *it; // We know that it != end because earlier we inserted 0xffffffff.
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp);
}
template <typename Container>
void TestEqualRange(EA::StdC::Stopwatch& stopwatch, Container& c, const uint32_t* pArrayBegin, const uint32_t* pArrayEnd)
{
uint32_t temp = 0;
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
{
temp += *(c.equal_range(*pArrayBegin++).first); // We know that it != end because earlier we inserted 0xffffffff.
}
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp);
}
template <typename Container>
void TestEraseValue(EA::StdC::Stopwatch& stopwatch, Container& c, const uint32_t* pArrayBegin, const uint32_t* pArrayEnd)
{
stopwatch.Restart();
while(pArrayBegin != pArrayEnd)
c.erase(*pArrayBegin++);
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
}
template <typename Container>
void TestErasePosition(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for(j = 0, jEnd = c.size() / 3, it = c.begin(); j < jEnd; ++j)
{
// The erase fucntion is supposed to return an iterator, but the C++ standard was
// not initially clear about it and some STL implementations don't do it correctly.
#if (((defined(_MSC_VER) || defined(_CPPLIB_VER)) && !defined(_HAS_STRICT_CONFORMANCE))) // _CPPLIB_VER is something defined by Dinkumware STL.
it = c.erase(it);
#else
// This pathway may execute at a slightly different speed than the
// standard behaviour, but that's fine for the benchmark because the
// benchmark is measuring the speed of erasing while iterating, and
// however it needs to get done by the given STL is how it is measured.
const typename Container::iterator itErase(it++);
c.erase(itErase);
#endif
++it;
++it;
}
stopwatch.Stop();
}
template <typename Container>
void TestEraseRange(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it1 = c.begin();
typename Container::iterator it2 = c.begin();
for(j = 0, jEnd = c.size() / 3; j < jEnd; ++j)
++it2;
stopwatch.Restart();
c.erase(it1, it2);
stopwatch.Stop();
}
template <typename Container>
void TestClear(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
c.clear();
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)c.size());
}
} // namespace
void BenchmarkSet()
{
EASTLTest_Printf("Set\n");
EA::UnitTest::Rand rng(EA::UnitTest::GetRandSeed());
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
{
eastl::vector<uint32_t> intVector(10000);
for(eastl_size_t i = 0, iEnd = intVector.size(); i < iEnd; i++)
intVector[i] = (uint32_t)rng.RandLimit(((uint32_t)iEnd / 2)); // This will result in duplicates and even a few triplicates.
for(int i = 0; i < 2; i++)
{
StdSetUint32 stdSetUint32;
EaSetUint32 eaSetUint32;
///////////////////////////////
// Test insert(const value_type&)
///////////////////////////////
TestInsert(stopwatch1, stdSetUint32, intVector.data(), intVector.data() + intVector.size());
TestInsert(stopwatch2, eaSetUint32, intVector.data(), intVector.data() + intVector.size());
if(i == 1)
Benchmark::AddResult("set<uint32_t>/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test iteration
///////////////////////////////
TestIteration(stopwatch1, stdSetUint32);
TestIteration(stopwatch2, eaSetUint32);
if(i == 1)
Benchmark::AddResult("set<uint32_t>/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test find
///////////////////////////////
TestFind(stopwatch1, stdSetUint32, intVector.data(), intVector.data() + intVector.size());
TestFind(stopwatch2, eaSetUint32, intVector.data(), intVector.data() + intVector.size());
if(i == 1)
Benchmark::AddResult("set<uint32_t>/find", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test count
///////////////////////////////
TestCount(stopwatch1, stdSetUint32, intVector.data(), intVector.data() + intVector.size());
TestCount(stopwatch2, eaSetUint32, intVector.data(), intVector.data() + intVector.size());
if(i == 1)
Benchmark::AddResult("set<uint32_t>/count", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test lower_bound
///////////////////////////////
TestLowerBound(stopwatch1, stdSetUint32, intVector.data(), intVector.data() + intVector.size());
TestLowerBound(stopwatch2, eaSetUint32, intVector.data(), intVector.data() + intVector.size());
if(i == 1)
Benchmark::AddResult("set<uint32_t>/lower_bound", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test upper_bound
///////////////////////////////
TestUpperBound(stopwatch1, stdSetUint32, intVector.data(), intVector.data() + intVector.size());
TestUpperBound(stopwatch2, eaSetUint32, intVector.data(), intVector.data() + intVector.size());
if(i == 1)
Benchmark::AddResult("set<uint32_t>/upper_bound", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test equal_range
///////////////////////////////
TestEqualRange(stopwatch1, stdSetUint32, intVector.data(), intVector.data() + intVector.size());
TestEqualRange(stopwatch2, eaSetUint32, intVector.data(), intVector.data() + intVector.size());
if(i == 1)
Benchmark::AddResult("set<uint32_t>/equal_range", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase(const key_type& key)
///////////////////////////////
TestEraseValue(stopwatch1, stdSetUint32, &intVector[0], &intVector[intVector.size() / 2]);
TestEraseValue(stopwatch2, eaSetUint32, &intVector[0], &intVector[intVector.size() / 2]);
if(i == 1)
Benchmark::AddResult("set<uint32_t>/erase/val", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase(iterator position)
///////////////////////////////
TestErasePosition(stopwatch1, stdSetUint32);
TestErasePosition(stopwatch2, eaSetUint32);
if(i == 1)
Benchmark::AddResult("set<uint32_t>/erase/pos", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime(),
GetStdSTLType() == kSTLMS ? "MS uses a code bloating implementation of erase." : NULL);
///////////////////////////////
// Test erase(iterator first, iterator last)
///////////////////////////////
TestEraseRange(stopwatch1, stdSetUint32);
TestEraseRange(stopwatch2, eaSetUint32);
if(i == 1)
Benchmark::AddResult("set<uint32_t>/erase range", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test clear()
///////////////////////////////
TestClear(stopwatch1, stdSetUint32);
TestClear(stopwatch2, eaSetUint32);
if(i == 1)
Benchmark::AddResult("set<uint32_t>/clear", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
}
}
}
File diff suppressed because it is too large Load Diff
+531
View File
@@ -0,0 +1,531 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/algorithm.h>
#include <EASTL/string.h>
#include <EASTL/sort.h>
EA_DISABLE_ALL_VC_WARNINGS()
#include <algorithm>
#include <string>
#include <stdio.h>
#include <stdlib.h>
EA_RESTORE_ALL_VC_WARNINGS()
using namespace EA;
namespace
{
template <typename Container>
void TestPushBack(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
for(int i = 0; i < 100000; i++)
c.push_back((typename Container::value_type)(i & ((typename Container::value_type)~0)));
stopwatch.Stop();
}
template <typename Container, typename T>
void TestInsert1(EA::StdC::Stopwatch& stopwatch, Container& c, T* p)
{
const typename Container::size_type s = c.size();
stopwatch.Restart();
for(int i = 0; i < 100; i++)
c.insert(s - (typename Container::size_type)(i * 317), p);
stopwatch.Stop();
}
template <typename Container>
void TestErase1(EA::StdC::Stopwatch& stopwatch, Container& c)
{
const typename Container::size_type s = c.size();
stopwatch.Restart();
for(int i = 0; i < 100; i++)
c.erase(s - (typename Container::size_type)(i * 339), 7);
stopwatch.Stop();
}
template <typename Container, typename T>
void TestReplace1(EA::StdC::Stopwatch& stopwatch, Container& c, T* p, int n)
{
const typename Container::size_type s = c.size();
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
c.replace(s - (typename Container::size_type)(i * 5), ((n - 2) + (i & 3)), p, n); // The second argument rotates through n-2, n-1, n, n+1, n-2, etc.
stopwatch.Stop();
}
template <typename Container>
void TestReserve(EA::StdC::Stopwatch& stopwatch, Container& c)
{
const typename Container::size_type s = c.capacity();
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
c.reserve((s - 2) + (i & 3)); // The second argument rotates through n-2, n-1, n, n+1, n-2, etc.
stopwatch.Stop();
}
template <typename Container>
void TestSize(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
Benchmark::DoNothing(&c, c.size());
stopwatch.Stop();
}
template <typename Container>
void TestBracket(EA::StdC::Stopwatch& stopwatch, Container& c)
{
int32_t temp = 0;
stopwatch.Restart();
for(typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++)
temp += c[j];
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)temp);
}
template <typename Container>
void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
Benchmark::DoNothing(&c, *eastl::find(c.begin(), c.end(), (typename Container::value_type)~0));
stopwatch.Stop();
}
template <typename Container, typename T>
void TestFind1(EA::StdC::Stopwatch& stopwatch, Container& c, T* p, int pos, int n)
{
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
Benchmark::DoNothing(&c, c.find(p, (typename Container::size_type)pos, (typename Container::size_type)n));
stopwatch.Stop();
}
template <typename Container, typename T>
void TestRfind1(EA::StdC::Stopwatch& stopwatch, Container& c, T* p, int pos, int n)
{
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
Benchmark::DoNothing(&c, c.rfind(p, (typename Container::size_type)pos, (typename Container::size_type)n));
stopwatch.Stop();
}
template <typename Container, typename T>
void TestFirstOf1(EA::StdC::Stopwatch& stopwatch, Container& c, T* p, int pos, int n)
{
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
Benchmark::DoNothing(&c, c.find_first_of(p, (typename Container::size_type)pos, (typename Container::size_type)n));
stopwatch.Stop();
}
template <typename Container, typename T>
void TestLastOf1(EA::StdC::Stopwatch& stopwatch, Container& c, T* p, int pos, int n)
{
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
Benchmark::DoNothing(&c, c.find_last_of(p, (typename Container::size_type)pos, (typename Container::size_type)n));
stopwatch.Stop();
}
template <typename Container, typename T>
void TestFirstNotOf1(EA::StdC::Stopwatch& stopwatch, Container& c, T* p, int pos, int n)
{
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
Benchmark::DoNothing(&c, c.find_first_not_of(p, (typename Container::size_type)pos, (typename Container::size_type)n));
stopwatch.Stop();
}
template <typename Container, typename T>
void TestLastNotOf1(EA::StdC::Stopwatch& stopwatch, Container& c, T* p, int pos, int n)
{
stopwatch.Restart();
for(int i = 0; i < 1000; i++)
Benchmark::DoNothing(&c, c.find_last_not_of(p, (typename Container::size_type)pos, (typename Container::size_type)n));
stopwatch.Stop();
}
template <typename Container>
void TestCompare(EA::StdC::Stopwatch& stopwatch, Container& c1, Container& c2) // size()
{
stopwatch.Restart();
for(int i = 0; i < 500; i++)
Benchmark::DoNothing(&c1, c1.compare(c2));
stopwatch.Stop();
}
template <typename Container>
void TestSwap(EA::StdC::Stopwatch& stopwatch, Container& c1, Container& c2) // size()
{
stopwatch.Restart();
for(int i = 0; i < 10000; i++) // Make sure this is an even count so that when done things haven't changed.
{
c1.swap(c2);
Benchmark::DoNothing(&c1);
}
stopwatch.Stop();
}
} // namespace
void BenchmarkString()
{
EASTLTest_Printf("String\n");
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
{
for(int i = 0; i < 2; i++)
{
std::basic_string<char8_t> ss8(16, 0); // We initialize to size of 16 because different implementations may make
eastl::basic_string<char8_t> es8(16, 0); // different tradeoffs related to startup size. Initial operations are faster
// when strings start with a higher reserve, but they use more memory.
std::basic_string<char16_t> ss16(16, 0); // We try to nullify this tradeoff for the tests below by starting all at
eastl::basic_string<char16_t> es16(16, 0); // the same baseline allocation.
///////////////////////////////
// Test push_back
///////////////////////////////
TestPushBack(stopwatch1, ss8);
TestPushBack(stopwatch2, es8);
if(i == 1)
Benchmark::AddResult("string<char8_t>/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestPushBack(stopwatch1, ss16);
TestPushBack(stopwatch2, es16);
if(i == 1)
Benchmark::AddResult("string<char16_t>/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test insert(size_type position, const value_type* p)
///////////////////////////////
const char8_t pInsert1_8[] = { 'a', 0 };
TestInsert1(stopwatch1, ss8, pInsert1_8);
TestInsert1(stopwatch2, es8, pInsert1_8);
if(i == 1)
Benchmark::AddResult("string<char8_t>/insert/pos,p", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
const char16_t pInsert1_16[] = { 'a', 0 };
TestInsert1(stopwatch1, ss16, pInsert1_16);
TestInsert1(stopwatch2, es16, pInsert1_16);
if(i == 1)
Benchmark::AddResult("string<char16_t>/insert/pos,p", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase(size_type position, size_type n)
///////////////////////////////
TestErase1(stopwatch1, ss8);
TestErase1(stopwatch2, es8);
if(i == 1)
Benchmark::AddResult("string<char8_t>/erase/pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestErase1(stopwatch1, ss16);
TestErase1(stopwatch2, es16);
if(i == 1)
Benchmark::AddResult("string<char16_t>/erase/pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test replace(size_type position, size_type n1, const value_type* p, size_type n2)
///////////////////////////////
const int kReplace1Size = 8;
const char8_t pReplace1_8[kReplace1Size] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
TestReplace1(stopwatch1, ss8, pReplace1_8, kReplace1Size);
TestReplace1(stopwatch2, es8, pReplace1_8, kReplace1Size);
if(i == 1)
Benchmark::AddResult("string<char8_t>/replace/pos,n,p,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
const char16_t pReplace1_16[kReplace1Size] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
TestReplace1(stopwatch1, ss16, pReplace1_16, kReplace1Size);
TestReplace1(stopwatch2, es16, pReplace1_16, kReplace1Size);
if(i == 1)
Benchmark::AddResult("string<char16_t>/replace/pos,n,p,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test reserve(size_type)
///////////////////////////////
TestReserve(stopwatch1, ss8);
TestReserve(stopwatch2, es8);
if(i == 1)
Benchmark::AddResult("string<char8_t>/reserve", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestReserve(stopwatch1, ss16);
TestReserve(stopwatch2, es16);
if(i == 1)
Benchmark::AddResult("string<char16_t>/reserve", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test size()
///////////////////////////////
TestSize(stopwatch1, ss8);
TestSize(stopwatch2, es8);
if(i == 1)
Benchmark::AddResult("string<char8_t>/size", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSize(stopwatch1, ss16);
TestSize(stopwatch2, es16);
if(i == 1)
Benchmark::AddResult("string<char16_t>/size", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test operator[].
///////////////////////////////
TestBracket(stopwatch1, ss8);
TestBracket(stopwatch2, es8);
if(i == 1)
Benchmark::AddResult("string<char8_t>/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestBracket(stopwatch1, ss16);
TestBracket(stopwatch2, es16);
if(i == 1)
Benchmark::AddResult("string<char16_t>/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test iteration via find().
///////////////////////////////
TestFind(stopwatch1, ss8);
TestFind(stopwatch2, es8);
if(i == 1)
Benchmark::AddResult("string<char8_t>/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestFind(stopwatch1, ss16);
TestFind(stopwatch2, es16);
if(i == 1)
Benchmark::AddResult("string<char16_t>/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test find(const value_type* p, size_type position, size_type n)
///////////////////////////////
const int kFind1Size = 7;
const char8_t pFind1_8[kFind1Size] = { 'p', 'a', 't', 't', 'e', 'r', 'n' };
ss8.insert(ss8.size() / 2, pFind1_8);
es8.insert(es8.size() / 2, pFind1_8);
TestFind1(stopwatch1, ss8, pFind1_8, 15, kFind1Size);
TestFind1(stopwatch2, es8, pFind1_8, 15, kFind1Size);
if(i == 1)
Benchmark::AddResult("string<char8_t>/find/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
const char16_t pFind1_16[kFind1Size] = { 'p', 'a', 't', 't', 'e', 'r', 'n' };
#if !defined(EA_PLATFORM_IPHONE) && (!defined(EA_COMPILER_CLANG) && defined(EA_PLATFORM_MINGW)) // Crashes on iPhone.
ss16.insert(ss8.size() / 2, pFind1_16);
#endif
es16.insert(es8.size() / 2, pFind1_16);
TestFind1(stopwatch1, ss16, pFind1_16, 15, kFind1Size);
TestFind1(stopwatch2, es16, pFind1_16, 15, kFind1Size);
if(i == 1)
Benchmark::AddResult("string<char16_t>/find/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test rfind(const value_type* p, size_type position, size_type n)
///////////////////////////////
TestRfind1(stopwatch1, ss8, pFind1_8, 15, kFind1Size);
TestRfind1(stopwatch2, es8, pFind1_8, 15, kFind1Size);
if(i == 1)
Benchmark::AddResult("string<char8_t>/rfind/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestRfind1(stopwatch1, ss16, pFind1_16, 15, kFind1Size);
TestRfind1(stopwatch2, es16, pFind1_16, 15, kFind1Size);
if(i == 1)
Benchmark::AddResult("string<char16_t>/rfind/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
//NOTICE (RASHIN):
//FindFirstOf variants are incredibly slow on palm pixi debug builds.
//Disabling for now...
#if !defined(EA_DEBUG)
///////////////////////////////
// Test find_first_of(const value_type* p, size_type position, size_type n
///////////////////////////////
const int kFindOf1Size = 7;
const char8_t pFindOf1_8[kFindOf1Size] = { '~', '~', '~', '~', '~', '~', '~' };
TestFirstOf1(stopwatch1, ss8, pFindOf1_8, 15, kFindOf1Size);
TestFirstOf1(stopwatch2, es8, pFindOf1_8, 15, kFindOf1Size);
if(i == 1)
Benchmark::AddResult("string<char8_t>/find_first_of/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
const char16_t pFindOf1_16[kFindOf1Size] = { '~', '~', '~', '~', '~', '~', '~' };
TestFirstOf1(stopwatch1, ss16, pFindOf1_16, 15, kFindOf1Size);
TestFirstOf1(stopwatch2, es16, pFindOf1_16, 15, kFindOf1Size);
if(i == 1)
Benchmark::AddResult("string<char16_t>/find_first_of/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test find_last_of(const value_type* p, size_type position, size_type n
///////////////////////////////
TestLastOf1(stopwatch1, ss8, pFindOf1_8, 15, kFindOf1Size);
TestLastOf1(stopwatch2, es8, pFindOf1_8, 15, kFindOf1Size);
if(i == 1)
Benchmark::AddResult("string<char8_t>/find_last_of/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestLastOf1(stopwatch1, ss16, pFindOf1_16, 15, kFindOf1Size);
TestLastOf1(stopwatch2, es16, pFindOf1_16, 15, kFindOf1Size);
if(i == 1)
Benchmark::AddResult("string<char16_t>/find_last_of/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test find_first_not_of(const value_type* p, size_type position, size_type n
///////////////////////////////
TestFirstNotOf1(stopwatch1, ss8, pFind1_8, 15, kFind1Size);
TestFirstNotOf1(stopwatch2, es8, pFind1_8, 15, kFind1Size);
if(i == 1)
Benchmark::AddResult("string<char8_t>/find_first_not_of/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestFirstNotOf1(stopwatch1, ss16, pFind1_16, 15, kFind1Size);
TestFirstNotOf1(stopwatch2, es16, pFind1_16, 15, kFind1Size);
if(i == 1)
Benchmark::AddResult("string<char16_t>/find_first_not_of/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test find_last_of(const value_type* p, size_type position, size_type n
///////////////////////////////
TestLastNotOf1(stopwatch1, ss8, pFind1_8, 15, kFind1Size);
TestLastNotOf1(stopwatch2, es8, pFind1_8, 15, kFind1Size);
if(i == 1)
Benchmark::AddResult("string<char8_t>/find_last_of/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestLastNotOf1(stopwatch1, ss16, pFind1_16, 15, kFind1Size);
TestLastNotOf1(stopwatch2, es16, pFind1_16, 15, kFind1Size);
if(i == 1)
Benchmark::AddResult("string<char16_t>/find_last_of/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
#endif
///////////////////////////////
// Test compare()
///////////////////////////////
std::basic_string<char8_t> ss8X(ss8);
eastl::basic_string<char8_t> es8X(es8);
std::basic_string<char16_t> ss16X(ss16);
eastl::basic_string<char16_t> es16X(es16);
TestCompare(stopwatch1, ss8, ss8X);
TestCompare(stopwatch2, es8, es8X);
if(i == 1)
Benchmark::AddResult("string<char8_t>/compare", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestCompare(stopwatch1, ss16, ss16X);
TestCompare(stopwatch2, es16, es16X);
if(i == 1)
Benchmark::AddResult("string<char16_t>/compare", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test swap()
///////////////////////////////
TestSwap(stopwatch1, ss8, ss8X);
TestSwap(stopwatch2, es8, es8X);
if(i == 1)
Benchmark::AddResult("string<char8_t>/swap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestSwap(stopwatch1, ss16, ss16X);
TestSwap(stopwatch2, es16, es16X);
if(i == 1)
Benchmark::AddResult("string<char16_t>/swap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
}
}
}
+667
View File
@@ -0,0 +1,667 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/algorithm.h>
#include <EASTL/bonus/tuple_vector.h>
#include <EASTL/sort.h>
#ifdef _MSC_VER
#pragma warning(push, 0)
#pragma warning(disable: 4350)
#endif
#include <algorithm>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
using namespace EA;
typedef std::vector<uint64_t> StdVectorUint64;
typedef eastl::tuple_vector<uint64_t> EaTupleVectorUint64;
struct PaddingStruct
{
char padding[56] = { 0 };
};
static const PaddingStruct DefaultPadding;
typedef eastl::tuple<uint64_t, PaddingStruct> PaddedTuple;
typedef std::vector<PaddedTuple> StdVectorUint64Padded;
typedef eastl::tuple_vector<uint64_t, PaddingStruct> EaTupleVectorUint64Padded;
namespace
{
//////////////////////////////////////////////////////////////////////////////
// MovableType
//
struct MovableType
{
int8_t* mpData;
enum { kDataSize = 128 };
MovableType() : mpData(new int8_t[kDataSize])
{ memset(mpData, 0, kDataSize); }
MovableType(const MovableType& x) : mpData(new int8_t[kDataSize])
{ memcpy(mpData, x.mpData, kDataSize); }
MovableType& operator=(const MovableType& x)
{
if(!mpData)
mpData = new int8_t[kDataSize];
memcpy(mpData, x.mpData, kDataSize);
return *this;
}
#if EASTL_MOVE_SEMANTICS_ENABLED
MovableType(MovableType&& x) EA_NOEXCEPT : mpData(x.mpData)
{ x.mpData = NULL; }
MovableType& operator=(MovableType&& x)
{
eastl::swap(mpData, x.mpData); // In practice it may not be right to do a swap, depending on the case.
return *this;
}
#endif
~MovableType()
{ delete[] mpData; }
};
//////////////////////////////////////////////////////////////////////////////
// AutoRefCount
//
// Basic ref-counted object.
//
template <typename T>
class AutoRefCount
{
public:
T* mpObject;
public:
AutoRefCount() EA_NOEXCEPT : mpObject(NULL)
{}
AutoRefCount(T* pObject) EA_NOEXCEPT : mpObject(pObject)
{
if(mpObject)
mpObject->AddRef();
}
AutoRefCount(T* pObject, int) EA_NOEXCEPT : mpObject(pObject)
{
// Inherit the existing refcount.
}
AutoRefCount(const AutoRefCount& x) EA_NOEXCEPT : mpObject(x.mpObject)
{
if(mpObject)
mpObject->AddRef();
}
AutoRefCount& operator=(const AutoRefCount& x)
{
return operator=(x.mpObject);
}
AutoRefCount& operator=(T* pObject)
{
if(pObject != mpObject)
{
T* const pTemp = mpObject; // Create temporary to prevent possible problems with re-entrancy.
if(pObject)
pObject->AddRef();
mpObject = pObject;
if(pTemp)
pTemp->Release();
}
return *this;
}
#if EASTL_MOVE_SEMANTICS_ENABLED
AutoRefCount(AutoRefCount&& x) EA_NOEXCEPT : mpObject(x.mpObject)
{
x.mpObject = NULL;
}
AutoRefCount& operator=(AutoRefCount&& x)
{
if(mpObject)
mpObject->Release();
mpObject = x.mpObject;
x.mpObject = NULL;
return *this;
}
#endif
~AutoRefCount()
{
if(mpObject)
mpObject->Release();
}
T& operator *() const EA_NOEXCEPT
{ return *mpObject; }
T* operator ->() const EA_NOEXCEPT
{ return mpObject; }
operator T*() const EA_NOEXCEPT
{ return mpObject; }
}; // class AutoRefCount
struct RefCounted
{
int mRefCount;
static int msAddRefCount;
static int msReleaseCount;
RefCounted() : mRefCount(1) {}
int AddRef()
{ ++msAddRefCount; return ++mRefCount; }
int Release()
{
++msReleaseCount;
if(mRefCount > 1)
return --mRefCount;
delete this;
return 0;
}
};
int RefCounted::msAddRefCount = 0;
int RefCounted::msReleaseCount = 0;
} // namespace
namespace
{
template <typename Container>
void TestPushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector<uint32_t>& intVector)
{
stopwatch.Restart();
for(eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++)
c.push_back((uint64_t)intVector[j]);
stopwatch.Stop();
}
template <typename Container>
void TestBracket(EA::StdC::Stopwatch& stopwatch, Container& c)
{
uint64_t temp = 0;
stopwatch.Restart();
for(typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++)
temp += c[j];
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff));
}
void TestBracket(EA::StdC::Stopwatch& stopwatch, EaTupleVectorUint64& c)
{
uint64_t temp = 0;
stopwatch.Restart();
for (typename EaTupleVectorUint64::size_type j = 0, jEnd = c.size(); j < jEnd; j++)
temp += eastl::get<0>(c[j]);
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff));
}
template <typename Container>
void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
typedef typename Container::iterator iterator_t; // This typedef is required to get this code to compile on RVCT
iterator_t it = eastl::find(c.begin(), c.end(), UINT64_C(0xffffffffffff));
stopwatch.Stop();
if(it != c.end())
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)*it);
}
void TestFind(EA::StdC::Stopwatch& stopwatch, EaTupleVectorUint64& c)
{
eastl::tuple<uint64_t> val(0xffffffffffff);
stopwatch.Restart();
EaTupleVectorUint64::iterator it = eastl::find(c.begin(), c.end(), val);
stopwatch.Stop();
if (it != c.end())
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eastl::get<0>(*it));
}
template <typename Container>
void TestSort(EA::StdC::Stopwatch& stopwatch, Container& c)
{
// Intentionally use eastl sort in order to measure just
// vector access speed and not be polluted by sort speed.
stopwatch.Restart();
eastl::quick_sort(c.begin(), c.end());
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(c[0] & 0xffffffff));
}
void TestSort(EA::StdC::Stopwatch& stopwatch, EaTupleVectorUint64& c)
{
// Intentionally use eastl sort in order to measure just
// vector access speed and not be polluted by sort speed.
stopwatch.Restart();
eastl::quick_sort(c.begin(), c.end());
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(eastl::get<0>(c[0]) & 0xffffffff));
}
template <typename Container>
void TestInsert(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for(j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j)
{
it = c.insert(it, UINT64_C(0xffffffffffff));
if(it == c.end()) // Try to safely increment the iterator three times.
it = c.begin();
if(++it == c.end())
it = c.begin();
if(++it == c.end())
it = c.begin();
}
stopwatch.Stop();
}
template <typename Container>
void TestErase(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for(j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j)
{
it = c.erase(it);
if(it == c.end()) // Try to safely increment the iterator three times.
it = c.begin();
if(++it == c.end())
it = c.begin();
if(++it == c.end())
it = c.begin();
}
stopwatch.Stop();
}
template <typename Container>
void TestMoveReallocate(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
while(c.size() < 8192)
c.resize(c.capacity() + 1);
stopwatch.Stop();
}
template <typename Container>
void TestMoveErase(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
while(!c.empty())
c.erase(c.begin());
stopwatch.Stop();
}
//////////////////////////////////////////////////////////////////////////
// Variations of test functions for the Padded structures
template <typename Container>
void TestTuplePushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector<uint32_t>& intVector)
{
stopwatch.Restart();
for (eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++)
{
PaddedTuple tup((uint64_t)intVector[j], DefaultPadding);
c.push_back(tup);
}
stopwatch.Stop();
}
template <typename Container>
void TestTupleBracket(EA::StdC::Stopwatch& stopwatch, Container& c)
{
uint64_t temp = 0;
stopwatch.Restart();
for (typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++)
temp += eastl::get<0>(c[j]);
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff));
}
template <typename Container>
void TestTupleFind(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
typedef typename Container::iterator iterator_t; // This typedef is required to get this code to compile on RVCT
iterator_t it = eastl::find_if(c.begin(), c.end(), [](auto tup) { return eastl::get<0>(tup) == 0xFFFFFFFF; });
stopwatch.Stop();
if (it != c.end())
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eastl::get<0>(*it));
}
template <typename Container>
void TestTupleSort(EA::StdC::Stopwatch& stopwatch, Container& c)
{
// Intentionally use eastl sort in order to measure just
// vector access speed and not be polluted by sort speed.
stopwatch.Restart();
eastl::quick_sort(c.begin(), c.end(), [](auto a, auto b) { return eastl::get<0>(a) < eastl::get<0>(b); });
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(eastl::get<0>(c[0]) & 0xffffffff));
}
template <typename Container>
void TestTupleInsert(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
PaddedTuple tup(0xFFFFFFFF, DefaultPadding);
stopwatch.Restart();
for (j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j)
{
it = c.insert(it, tup);
if (it == c.end()) // Try to safely increment the iterator three times.
it = c.begin();
if (++it == c.end())
it = c.begin();
if (++it == c.end())
it = c.begin();
}
stopwatch.Stop();
}
template <typename Container>
void TestTupleErase(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for (j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j)
{
it = c.erase(it);
if (it == c.end()) // Try to safely increment the iterator three times.
it = c.begin();
if (++it == c.end())
it = c.begin();
if (++it == c.end())
it = c.begin();
}
stopwatch.Stop();
}
} // namespace
void BenchmarkTupleVector()
{
EASTLTest_Printf("TupleVector\n");
EA::UnitTest::RandGenT<uint32_t> rng(EA::UnitTest::GetRandSeed());
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
{
eastl::vector<uint32_t> intVector(100000);
eastl::generate(intVector.begin(), intVector.end(), rng);
for(int i = 0; i < 2; i++)
{
StdVectorUint64 stdVectorUint64;
EaTupleVectorUint64 eaTupleVectorUint64;
///////////////////////////////
// Test push_back
///////////////////////////////
TestPushBack(stopwatch1, stdVectorUint64, intVector);
TestPushBack(stopwatch2, eaTupleVectorUint64, intVector);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64>/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test operator[].
///////////////////////////////
TestBracket(stopwatch1, stdVectorUint64);
TestBracket(stopwatch2, eaTupleVectorUint64);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64>/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test iteration via find().
///////////////////////////////
TestFind(stopwatch1, stdVectorUint64);
TestFind(stopwatch2, eaTupleVectorUint64);
TestFind(stopwatch1, stdVectorUint64);
TestFind(stopwatch2, eaTupleVectorUint64);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64>/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test sort
///////////////////////////////
// Currently VC++ complains about our sort function decrementing std::iterator that is already at begin(). In the strictest sense,
// that's a valid complaint, but we aren't testing std STL here. We will want to revise our sort function eventually.
#if !defined(_MSC_VER) || !defined(_ITERATOR_DEBUG_LEVEL) || (_ITERATOR_DEBUG_LEVEL < 2)
TestSort(stopwatch1, stdVectorUint64);
TestSort(stopwatch2, eaTupleVectorUint64);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64>/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
#endif
///////////////////////////////
// Test insert
///////////////////////////////
TestInsert(stopwatch1, stdVectorUint64);
TestInsert(stopwatch2, eaTupleVectorUint64);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64>/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase
///////////////////////////////
TestErase(stopwatch1, stdVectorUint64);
TestErase(stopwatch2, eaTupleVectorUint64);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64>/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////////////////
// Test move of MovableType
// Should be much faster with C++11 move.
///////////////////////////////////////////
std::vector<MovableType> stdVectorMovableType;
eastl::tuple_vector<MovableType> eaTupleVectorMovableType;
TestMoveReallocate(stopwatch1, stdVectorMovableType);
TestMoveReallocate(stopwatch2, eaTupleVectorMovableType);
if(i == 1)
Benchmark::AddResult("tuple_vector<MovableType>/reallocate", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestMoveErase(stopwatch1, stdVectorMovableType);
TestMoveErase(stopwatch2, eaTupleVectorMovableType);
if(i == 1)
Benchmark::AddResult("tuple_vector<MovableType>/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////////////////
// Test move of AutoRefCount
// Should be much faster with C++11 move.
///////////////////////////////////////////
std::vector<AutoRefCount<RefCounted> > stdVectorAutoRefCount;
eastl::tuple_vector<AutoRefCount<RefCounted> > eaTupleVectorAutoRefCount;
for(size_t a = 0; a < 2048; a++)
{
stdVectorAutoRefCount.push_back(AutoRefCount<RefCounted>(new RefCounted));
eaTupleVectorAutoRefCount.push_back(AutoRefCount<RefCounted>(new RefCounted));
}
RefCounted::msAddRefCount = 0;
RefCounted::msReleaseCount = 0;
TestMoveErase(stopwatch1, stdVectorAutoRefCount);
//EASTLTest_Printf("tuple_vector<AutoRefCount>/erase std counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount);
RefCounted::msAddRefCount = 0;
RefCounted::msReleaseCount = 0;
TestMoveErase(stopwatch2, eaTupleVectorAutoRefCount);
//EASTLTest_Printf("tuple_vector<AutoRefCount>/erase EA counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount);
if(i == 1)
Benchmark::AddResult("tuple_vector<AutoRefCount>/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
//////////////////////////////////////////////////////////////////////////
// Test various operations with "padded" data, to demonstrate access/modification of sparse data
StdVectorUint64Padded stdVectorUint64Padded;
EaTupleVectorUint64Padded eaTupleVectorUint64Padded;
///////////////////////////////
// Test push_back
///////////////////////////////
TestTuplePushBack(stopwatch1, stdVectorUint64Padded, intVector);
TestTuplePushBack(stopwatch2, eaTupleVectorUint64Padded, intVector);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64,Padding>/push_back", stopwatch1.GetUnits(),
stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test operator[].
///////////////////////////////
TestTupleBracket(stopwatch1, stdVectorUint64Padded);
TestTupleBracket(stopwatch2, eaTupleVectorUint64Padded);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64,Padding>/operator[]", stopwatch1.GetUnits(),
stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test iteration via find().
///////////////////////////////
TestTupleFind(stopwatch1, stdVectorUint64Padded);
TestTupleFind(stopwatch2, eaTupleVectorUint64Padded);
TestTupleFind(stopwatch1, stdVectorUint64Padded);
TestTupleFind(stopwatch2, eaTupleVectorUint64Padded);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64,Padding>/iteration", stopwatch1.GetUnits(),
stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test sort
///////////////////////////////
// Currently VC++ complains about our sort function decrementing std::iterator that is already at
// begin(). In the strictest sense, that's a valid complaint, but we aren't testing std STL here. We
// will want to revise our sort function eventually.
#if !defined(_MSC_VER) || !defined(_ITERATOR_DEBUG_LEVEL) || (_ITERATOR_DEBUG_LEVEL < 2)
TestTupleSort(stopwatch1, stdVectorUint64Padded);
TestTupleSort(stopwatch2, eaTupleVectorUint64Padded);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64,Padding>/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(),
stopwatch2.GetElapsedTime());
#endif
///////////////////////////////
// Test insert
///////////////////////////////
TestTupleInsert(stopwatch1, stdVectorUint64Padded);
TestTupleInsert(stopwatch2, eaTupleVectorUint64Padded);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64,Padding>/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(),
stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase
///////////////////////////////
TestTupleErase(stopwatch1, stdVectorUint64Padded);
TestTupleErase(stopwatch2, eaTupleVectorUint64Padded);
if(i == 1)
Benchmark::AddResult("tuple_vector<uint64,Padding>/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(),
stopwatch2.GetElapsedTime());
}
}
}
+452
View File
@@ -0,0 +1,452 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EAStdC/EAStopwatch.h>
#include <EASTL/algorithm.h>
#include <EASTL/vector.h>
#include <EASTL/sort.h>
#ifdef _MSC_VER
#pragma warning(push, 0)
#pragma warning(disable: 4350)
#endif
#include <algorithm>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
using namespace EA;
typedef std::vector<uint64_t> StdVectorUint64;
typedef eastl::vector<uint64_t> EaVectorUint64;
namespace
{
//////////////////////////////////////////////////////////////////////////////
// MovableType
//
struct MovableType
{
int8_t* mpData;
enum { kDataSize = 128 };
MovableType() : mpData(new int8_t[kDataSize])
{ memset(mpData, 0, kDataSize); }
MovableType(const MovableType& x) : mpData(new int8_t[kDataSize])
{ memcpy(mpData, x.mpData, kDataSize); }
MovableType& operator=(const MovableType& x)
{
if(!mpData)
mpData = new int8_t[kDataSize];
memcpy(mpData, x.mpData, kDataSize);
return *this;
}
MovableType(MovableType&& x) EA_NOEXCEPT : mpData(x.mpData)
{ x.mpData = NULL; }
MovableType& operator=(MovableType&& x)
{
eastl::swap(mpData, x.mpData); // In practice it may not be right to do a swap, depending on the case.
return *this;
}
~MovableType()
{ delete[] mpData; }
};
//////////////////////////////////////////////////////////////////////////////
// AutoRefCount
//
// Basic ref-counted object.
//
template <typename T>
class AutoRefCount
{
public:
T* mpObject;
public:
AutoRefCount() EA_NOEXCEPT : mpObject(NULL)
{}
AutoRefCount(T* pObject) EA_NOEXCEPT : mpObject(pObject)
{
if(mpObject)
mpObject->AddRef();
}
AutoRefCount(T* pObject, int) EA_NOEXCEPT : mpObject(pObject)
{
// Inherit the existing refcount.
}
AutoRefCount(const AutoRefCount& x) EA_NOEXCEPT : mpObject(x.mpObject)
{
if(mpObject)
mpObject->AddRef();
}
AutoRefCount& operator=(const AutoRefCount& x)
{
return operator=(x.mpObject);
}
AutoRefCount& operator=(T* pObject)
{
if(pObject != mpObject)
{
T* const pTemp = mpObject; // Create temporary to prevent possible problems with re-entrancy.
if(pObject)
pObject->AddRef();
mpObject = pObject;
if(pTemp)
pTemp->Release();
}
return *this;
}
AutoRefCount(AutoRefCount&& x) EA_NOEXCEPT : mpObject(x.mpObject)
{
x.mpObject = NULL;
}
AutoRefCount& operator=(AutoRefCount&& x)
{
if(mpObject)
mpObject->Release();
mpObject = x.mpObject;
x.mpObject = NULL;
return *this;
}
~AutoRefCount()
{
if(mpObject)
mpObject->Release();
}
T& operator *() const EA_NOEXCEPT
{ return *mpObject; }
T* operator ->() const EA_NOEXCEPT
{ return mpObject; }
operator T*() const EA_NOEXCEPT
{ return mpObject; }
}; // class AutoRefCount
struct RefCounted
{
int mRefCount;
static int msAddRefCount;
static int msReleaseCount;
RefCounted() : mRefCount(1) {}
int AddRef()
{ ++msAddRefCount; return ++mRefCount; }
int Release()
{
++msReleaseCount;
if(mRefCount > 1)
return --mRefCount;
delete this;
return 0;
}
};
int RefCounted::msAddRefCount = 0;
int RefCounted::msReleaseCount = 0;
} // namespace
namespace
{
template <typename Container>
void TestPushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector<uint32_t>& intVector)
{
stopwatch.Restart();
for(eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++)
c.push_back((uint64_t)intVector[j]);
stopwatch.Stop();
}
template <typename Container>
void TestBracket(EA::StdC::Stopwatch& stopwatch, Container& c)
{
uint64_t temp = 0;
stopwatch.Restart();
for(typename Container::size_type j = 0, jEnd = c.size(); j < jEnd; j++)
temp += c[j];
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(temp & 0xffffffff));
}
template <typename Container>
void TestFind(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
typedef typename Container::iterator iterator_t; // This typedef is required to get this code to compile on RVCT
iterator_t it = eastl::find(c.begin(), c.end(), UINT64_C(0xffffffffffff));
stopwatch.Stop();
if(it != c.end())
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)*it);
}
template <typename Container>
void TestSort(EA::StdC::Stopwatch& stopwatch, Container& c)
{
// Intentionally use eastl sort in order to measure just
// vector access speed and not be polluted by sort speed.
stopwatch.Restart();
eastl::quick_sort(c.begin(), c.end());
stopwatch.Stop();
sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)(c[0] & 0xffffffff));
}
template <typename Container>
void TestInsert(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for(j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j)
{
it = c.insert(it, UINT64_C(0xffffffffffff));
if(it == c.end()) // Try to safely increment the iterator three times.
it = c.begin();
if(++it == c.end())
it = c.begin();
if(++it == c.end())
it = c.begin();
}
stopwatch.Stop();
}
template <typename Container>
void TestErase(EA::StdC::Stopwatch& stopwatch, Container& c)
{
typename Container::size_type j, jEnd;
typename Container::iterator it;
stopwatch.Restart();
for(j = 0, jEnd = 100, it = c.begin(); j < jEnd; ++j)
{
it = c.erase(it);
if(it == c.end()) // Try to safely increment the iterator three times.
it = c.begin();
if(++it == c.end())
it = c.begin();
if(++it == c.end())
it = c.begin();
}
stopwatch.Stop();
}
template <typename Container>
void TestMoveReallocate(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
while(c.size() < 8192)
c.resize(c.capacity() + 1);
stopwatch.Stop();
}
template <typename Container>
void TestMoveErase(EA::StdC::Stopwatch& stopwatch, Container& c)
{
stopwatch.Restart();
while(!c.empty())
c.erase(c.begin());
stopwatch.Stop();
}
} // namespace
void BenchmarkVector()
{
EASTLTest_Printf("Vector\n");
EA::UnitTest::RandGenT<uint32_t> rng(EA::UnitTest::GetRandSeed());
EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles);
EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles);
{
eastl::vector<uint32_t> intVector(100000);
eastl::generate(intVector.begin(), intVector.end(), rng);
for(int i = 0; i < 2; i++)
{
StdVectorUint64 stdVectorUint64;
EaVectorUint64 eaVectorUint64;
///////////////////////////////
// Test push_back
///////////////////////////////
TestPushBack(stopwatch1, stdVectorUint64, intVector);
TestPushBack(stopwatch2, eaVectorUint64, intVector);
if(i == 1)
Benchmark::AddResult("vector<uint64>/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test operator[].
///////////////////////////////
TestBracket(stopwatch1, stdVectorUint64);
TestBracket(stopwatch2, eaVectorUint64);
if(i == 1)
Benchmark::AddResult("vector<uint64>/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test iteration via find().
///////////////////////////////
TestFind(stopwatch1, stdVectorUint64);
TestFind(stopwatch2, eaVectorUint64);
TestFind(stopwatch1, stdVectorUint64);
TestFind(stopwatch2, eaVectorUint64);
if(i == 1)
Benchmark::AddResult("vector<uint64>/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test sort
///////////////////////////////
// Currently VC++ complains about our sort function decrementing std::iterator that is already at begin(). In the strictest sense,
// that's a valid complaint, but we aren't testing std STL here. We will want to revise our sort function eventually.
#if !defined(_MSC_VER) || !defined(_ITERATOR_DEBUG_LEVEL) || (_ITERATOR_DEBUG_LEVEL < 2)
TestSort(stopwatch1, stdVectorUint64);
TestSort(stopwatch2, eaVectorUint64);
if(i == 1)
Benchmark::AddResult("vector<uint64>/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
#endif
///////////////////////////////
// Test insert
///////////////////////////////
TestInsert(stopwatch1, stdVectorUint64);
TestInsert(stopwatch2, eaVectorUint64);
if(i == 1)
Benchmark::AddResult("vector<uint64>/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////
// Test erase
///////////////////////////////
TestErase(stopwatch1, stdVectorUint64);
TestErase(stopwatch2, eaVectorUint64);
if(i == 1)
Benchmark::AddResult("vector<uint64>/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////////////////
// Test move of MovableType
// Should be much faster with C++11 move.
///////////////////////////////////////////
std::vector<MovableType> stdVectorMovableType;
eastl::vector<MovableType> eaVectorMovableType;
TestMoveReallocate(stopwatch1, stdVectorMovableType);
TestMoveReallocate(stopwatch2, eaVectorMovableType);
if(i == 1)
Benchmark::AddResult("vector<MovableType>/reallocate", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
TestMoveErase(stopwatch1, stdVectorMovableType);
TestMoveErase(stopwatch2, eaVectorMovableType);
if(i == 1)
Benchmark::AddResult("vector<MovableType>/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
///////////////////////////////////////////
// Test move of AutoRefCount
// Should be much faster with C++11 move.
///////////////////////////////////////////
std::vector<AutoRefCount<RefCounted> > stdVectorAutoRefCount;
eastl::vector<AutoRefCount<RefCounted> > eaVectorAutoRefCount;
for(size_t a = 0; a < 2048; a++)
{
stdVectorAutoRefCount.push_back(AutoRefCount<RefCounted>(new RefCounted));
eaVectorAutoRefCount.push_back(AutoRefCount<RefCounted>(new RefCounted));
}
RefCounted::msAddRefCount = 0;
RefCounted::msReleaseCount = 0;
TestMoveErase(stopwatch1, stdVectorAutoRefCount);
EASTLTest_Printf("vector<AutoRefCount>/erase std counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount);
RefCounted::msAddRefCount = 0;
RefCounted::msReleaseCount = 0;
TestMoveErase(stopwatch2, eaVectorAutoRefCount);
EASTLTest_Printf("vector<AutoRefCount>/erase EA counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount);
if(i == 1)
Benchmark::AddResult("vector<AutoRefCount>/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime());
}
}
}
+291
View File
@@ -0,0 +1,291 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#include <EASTL/string.h>
#include <EAMain/EAMain.h>
#ifdef _MSC_VER
#pragma warning(push, 0)
#endif
#include <stdio.h>
#include <math.h>
#include <float.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
namespace Benchmark
{
static int64_t ConvertStopwatchUnits(EA::StdC::Stopwatch::Units unitsSource, int64_t valueSource, EA::StdC::Stopwatch::Units unitsDest)
{
using namespace EA::StdC;
int64_t valueDest = valueSource;
if(unitsSource != unitsDest)
{
double sourceMultiplier;
switch (unitsSource)
{
case Stopwatch::kUnitsCPUCycles:
sourceMultiplier = Stopwatch::GetUnitsPerCPUCycle(unitsDest); // This will typically be a number less than 1.
valueDest = (int64_t)(valueSource * sourceMultiplier);
break;
case Stopwatch::kUnitsCycles:
sourceMultiplier = Stopwatch::GetUnitsPerStopwatchCycle(unitsDest); // This will typically be a number less than 1.
valueDest = (int64_t)(valueSource * sourceMultiplier);
break;
case Stopwatch::kUnitsNanoseconds:
case Stopwatch::kUnitsMicroseconds:
case Stopwatch::kUnitsMilliseconds:
case Stopwatch::kUnitsSeconds:
case Stopwatch::kUnitsMinutes:
case Stopwatch::kUnitsUserDefined:
// To do. Also, handle the case of unitsDest being Cycles or CPUCycles and unitsSource being a time.
break;
}
}
return valueDest;
}
void WriteTime(int64_t timeNS, eastl::string& sTime)
{
if(timeNS > 1000000000)
sTime.sprintf(" %6.2f s", (double)timeNS / 1000000000);
else if(timeNS > 1000000)
sTime.sprintf("%6.1f ms", (double)timeNS / 1000000);
else if(timeNS > 1000)
sTime.sprintf("%6.1f us", (double)timeNS / 1000);
else
sTime.sprintf("%6.1f ns", (double)timeNS / 1);
}
Environment gEnvironment;
Environment& GetEnvironment()
{
return gEnvironment;
}
ResultSet gResultSet;
ResultSet& GetResultSet()
{
return gResultSet;
}
// Scratch sprintf buffer
char gScratchBuffer[1024];
void DoNothing(...)
{
// Intentionally nothing.
}
void AddResult(const char* pName, int units, int64_t nTime1, int64_t nTime2, const char* pNotes)
{
Result result;
result.msName = pName;
result.mUnits = units;
result.mTime1 = nTime1;
result.mTime1NS = ConvertStopwatchUnits((EA::StdC::Stopwatch::Units)units, nTime1, EA::StdC::Stopwatch::kUnitsNanoseconds);
result.mTime2 = nTime2;
result.mTime2NS = ConvertStopwatchUnits((EA::StdC::Stopwatch::Units)units, nTime2, EA::StdC::Stopwatch::kUnitsNanoseconds);
if(pNotes)
result.msNotes = pNotes;
gResultSet.insert(result);
}
void PrintResultLine(const Result& result)
{
const double fRatio = (double)result.mTime1 / (double)result.mTime2;
const double fRatioPrinted = (fRatio > 100) ? 100 : fRatio;
const double fPercentChange = fabs(((double)result.mTime1 - (double)result.mTime2) / (((double)result.mTime1 + (double)result.mTime2) / 2));
const bool bDifference = (result.mTime1 > 10) && (result.mTime2 > 10) && (fPercentChange > 0.25);
const char* pDifference = (bDifference ? (result.mTime1 < result.mTime2 ? "-" : "+") : "");
eastl::string sClockTime1, sClockTime2;
WriteTime(result.mTime1NS, sClockTime1); // This converts an integer in nanoseconds (e.g. 23400000) to a string (e.g. "23.4 ms")
WriteTime(result.mTime2NS, sClockTime2);
EA::UnitTest::Report("%-43s | %13" PRIu64 " %s | %13" PRIu64 " %s | %10.2f%10s", result.msName.c_str(), result.mTime1, sClockTime1.c_str(), result.mTime2, sClockTime2.c_str(), fRatioPrinted, pDifference);
if(result.msNotes.length()) // If there are any notes...
EA::UnitTest::Report(" %s", result.msNotes.c_str());
EA::UnitTest::Report("\n");
}
#if defined(EASTL_BENCHMARK_WRITE_FILE) && EASTL_BENCHMARK_WRITE_FILE
#if !defined(EASTL_BENCHMARK_WRITE_FILE_PATH)
#define EASTL_BENCHMARK_WRITE_FILE_PATH "BenchmarkResults.txt"
#endif
struct FileWriter
{
FILE* mpReportFile;
EA::EAMain::ReportFunction mpSavedReportFunction;
static FileWriter* gpFileWriter;
static void StaticPrintfReportFunction(const char8_t* pText)
{
if(gpFileWriter)
gpFileWriter->PrintfReportFunction(pText);
}
void PrintfReportFunction(const char8_t* pText)
{
fwrite(pText, strlen(pText), 1, mpReportFile);
EA::EAMain::ReportFunction gpReportFunction = EA::EAMain::GetDefaultReportFunction();
gpReportFunction(pText);
}
FileWriter() : mpReportFile(NULL), mpSavedReportFunction(NULL)
{
mpReportFile = fopen(EASTL_BENCHMARK_WRITE_FILE_PATH, "w+");
if(mpReportFile)
{
gpFileWriter = this;
mpSavedReportFunction = EA::EAMain::GetDefaultReportFunction();
EA::EAMain::SetReportFunction(StaticPrintfReportFunction);
}
}
~FileWriter()
{
if(mpReportFile)
{
gpFileWriter = NULL;
EA::EAMain::SetReportFunction(mpSavedReportFunction);
fclose(mpReportFile);
}
}
};
FileWriter* FileWriter::gpFileWriter = NULL;
#endif
void PrintResults()
{
#if defined(EASTL_BENCHMARK_WRITE_FILE) && EASTL_BENCHMARK_WRITE_FILE
FileWriter fileWriter; // This will auto-execute.
#endif
// Print the results
EA::UnitTest::Report("\n");
EA::UnitTest::Report("****************************************************************************************\n");
EA::UnitTest::Report("EASTL Benchmark test results\n");
EA::UnitTest::Report("****************************************************************************************\n");
EA::UnitTest::Report("\n");
EA::UnitTest::Report("EASTL version: %s\n", EASTL_VERSION);
EA::UnitTest::Report("Platform: %s\n", gEnvironment.msPlatform.c_str());
EA::UnitTest::Report("Compiler: %s\n", EA_COMPILER_STRING);
#if defined(EA_DEBUG) || defined(_DEBUG)
EA::UnitTest::Report("Allocator: PPMalloc::GeneralAllocatorDebug. Thread safety enabled.\n");
EA::UnitTest::Report("Build: Debug. Inlining disabled. STL debug features disabled.\n");
#else
EA::UnitTest::Report("Allocator: PPMalloc::GeneralAllocator. Thread safety enabled.\n");
EA::UnitTest::Report("Build: Full optimization. Inlining enabled.\n");
#endif
EA::UnitTest::Report("\n");
EA::UnitTest::Report("Values are ticks and time to complete tests; smaller values are better.\n");
EA::UnitTest::Report("\n");
EA::UnitTest::Report("%-43s%26s%26s%13s%13s\n", "Test", gEnvironment.msSTLName1.c_str(), gEnvironment.msSTLName2.c_str(), "Ratio", "Difference?");
EA::UnitTest::Report("---------------------------------------------------------------------------------------------------------------------\n");
eastl::string sTestTypeLast;
eastl::string sTestTypeTemp;
for(ResultSet::iterator it = gResultSet.begin(); it != gResultSet.end(); ++it)
{
const Result& result = *it;
eastl_size_t n = result.msName.find('/');
if(n == eastl::string::npos)
n = result.msName.length();
sTestTypeTemp.assign(result.msName, 0, n);
if(sTestTypeTemp != sTestTypeLast) // If it looks like we are changing to a new test type... add an empty line to help readability.
{
if(it != gResultSet.begin())
EA::UnitTest::Report("\n");
sTestTypeLast = sTestTypeTemp;
}
PrintResultLine(result);
}
// We will print out a final line that has the sum of the rows printed above.
Result resultSum;
resultSum.msName = "sum";
for(ResultSet::iterator its = gResultSet.begin(); its != gResultSet.end(); ++its)
{
const Result& resultTemp = *its;
EASTL_ASSERT(resultTemp.mUnits == EA::StdC::Stopwatch::kUnitsCPUCycles); // Our ConvertStopwatchUnits call below assumes that every measured time is CPUCycles.
resultSum.mTime1 += resultTemp.mTime1;
resultSum.mTime2 += resultTemp.mTime2;
}
// We do this convert as a final step instead of the loop in order to avoid loss of precision.
resultSum.mTime1NS = ConvertStopwatchUnits(EA::StdC::Stopwatch::kUnitsCPUCycles, resultSum.mTime1, EA::StdC::Stopwatch::kUnitsNanoseconds);
resultSum.mTime2NS = ConvertStopwatchUnits(EA::StdC::Stopwatch::kUnitsCPUCycles, resultSum.mTime2, EA::StdC::Stopwatch::kUnitsNanoseconds);
EA::UnitTest::Report("\n");
PrintResultLine(resultSum);
EA::UnitTest::Report("\n");
EA::UnitTest::Report("****************************************************************************************\n");
EA::UnitTest::Report("\n");
// Clear the results
gResultSet.clear();
gEnvironment.clear();
}
} // namespace Benchmark
+228
View File
@@ -0,0 +1,228 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#ifndef EASTLBENCHMARK_H
#define EASTLBENCHMARK_H
// Intrinsic control
//
// Our benchmark results are being skewed by inconsistent decisions by the
// VC++ compiler to use intrinsic functions. Additionally, many of our
// benchmarks work on large blocks of elements, whereas intrinsics often
// are an improvement only over small blocks of elements. As a result,
// enabling of intrinsics is often resulting in poor benchmark results for
// code that gets an intrinsic enabled for it, even though it will often
// happen in real code to be the opposite case. The disabling of intrinsics
// here often results in EASTL performance being lower than it would be in
// real-world situations.
//
#include <string.h>
#ifdef _MSC_VER
#pragma function(strlen, strcmp, strcpy, strcat, memcpy, memcmp, memset)
#endif
#include <EASTL/set.h>
#include <EASTL/string.h>
#include <EAStdC/EAStopwatch.h>
#include <stdlib.h>
#include <string.h>
void BenchmarkSort();
void BenchmarkList();
void BenchmarkString();
void BenchmarkVector();
void BenchmarkDeque();
void BenchmarkSet();
void BenchmarkMap();
void BenchmarkHash();
void BenchmarkAlgorithm();
void BenchmarkHeap();
void BenchmarkBitset();
void BenchmarkTupleVector();
namespace Benchmark
{
// Environment
//
// The environment for this benchmark test.
//
struct Environment
{
eastl::string8 msPlatform; // Name of test platform (e.g. "Windows")
eastl::string8 msSTLName1; // Name of competitor #1 (e.g. "EASTL").
eastl::string8 msSTLName2; // Name of competitor #2 (e.g. "MS STL").
void clear() { msPlatform.set_capacity(0); msSTLName1.set_capacity(0); msSTLName2.set_capacity(0); }
};
Environment& GetEnvironment();
// Result
//
// An individual benchmark result.
//
struct Result
{
eastl::string8 msName; // Test name (e.g. "vector/insert").
int mUnits; // Timing units (e.g. EA::StdC::Stopwatch::kUnitsSeconds).
int64_t mTime1; // Time of competitor #1.
uint64_t mTime1NS; // Nanoseconds.
int64_t mTime2; // Time of competitor #2.
int64_t mTime2NS; // Nanoseconds.
eastl::string8 msNotes; // Any comments to attach to this result.
Result() : msName(), mUnits(EA::StdC::Stopwatch::kUnitsCPUCycles),
mTime1(0), mTime1NS(0), mTime2(0), mTime2NS(0), msNotes() { }
};
inline bool operator<(const Result& r1, const Result& r2)
{ return r1.msName < r2.msName; }
typedef eastl::set<Result> ResultSet;
ResultSet& GetResultSet();
// Scratch sprintf buffer
extern char gScratchBuffer[1024];
// Utility functions
//
void DoNothing(...);
void AddResult(const char* pName, int units, int64_t nTime1, int64_t nTime2, const char* pNotes = NULL);
void PrintResults();
void WriteTime(int64_t timeNS, eastl::string& sTime);
} // namespace Benchmark
///////////////////////////////////////////////////////////////////////////////
/// LargePOD
///
/// Implements a structure which is essentially a largish POD. Useful for testing
/// containers and algorithms for their ability to efficiently work with PODs.
/// This class isn't strictly a POD by the definition of the C++ standard,
/// but it suffices for our interests.
///
struct LargeObject
{
int32_t mData[2048];
};
struct LargePOD
{
LargeObject mLargeObject1;
LargeObject mLargeObject2;
const char* mpName1;
const char* mpName2;
explicit LargePOD(int32_t x = 0) // A true POD doesn't have a non-trivial constructor.
{
memset(mLargeObject1.mData, 0, sizeof(mLargeObject1.mData));
memset(mLargeObject2.mData, 0, sizeof(mLargeObject2.mData));
mLargeObject1.mData[0] = x;
mpName1 = "LargePOD1";
mpName2 = "LargePOD2";
}
LargePOD(const LargePOD& largePOD) // A true POD doesn't have a non-trivial copy-constructor.
: mLargeObject1(largePOD.mLargeObject1),
mLargeObject2(largePOD.mLargeObject2),
mpName1(largePOD.mpName1),
mpName2(largePOD.mpName2)
{
}
virtual ~LargePOD() { }
LargePOD& operator=(const LargePOD& largePOD) // A true POD doesn't have a non-trivial assignment operator.
{
if(&largePOD != this)
{
mLargeObject1 = largePOD.mLargeObject1;
mLargeObject2 = largePOD.mLargeObject2;
mpName1 = largePOD.mpName1;
mpName2 = largePOD.mpName2;
}
return *this;
}
virtual void DoSomething() // Note that by declaring this virtual, this class is not truly a POD.
{ // But it acts like a POD for the purposes of EASTL algorithms.
mLargeObject1.mData[1]++;
}
operator int()
{
return (int)mLargeObject1.mData[0];
}
};
//EASTL_DECLARE_POD(LargePOD);
//EASTL_DECLARE_TRIVIAL_CONSTRUCTOR(LargePOD);
//EASTL_DECLARE_TRIVIAL_COPY(LargePOD);
//EASTL_DECLARE_TRIVIAL_ASSIGN(LargePOD);
//EASTL_DECLARE_TRIVIAL_DESTRUCTOR(LargePOD);
//EASTL_DECLARE_TRIVIAL_RELOCATE(LargePOD);
// Operators
// We specifically define only == and <, in order to verify that
// our containers and algorithms are not mistakenly expecting other
// operators for the contained and manipulated classes.
inline bool operator==(const LargePOD& t1, const LargePOD& t2)
{
return (memcmp(&t1.mLargeObject1, &t2.mLargeObject1, sizeof(t1.mLargeObject1)) == 0) &&
(memcmp(&t1.mLargeObject2, &t2.mLargeObject2, sizeof(t1.mLargeObject2)) == 0) &&
(strcmp(t1.mpName1, t2.mpName1) == 0) &&
(strcmp(t1.mpName2, t2.mpName2) == 0);
}
inline bool operator<(const LargePOD& t1, const LargePOD& t2)
{
return (memcmp(&t1.mLargeObject1, &t2.mLargeObject1, sizeof(t1.mLargeObject1)) < 0) &&
(memcmp(&t1.mLargeObject2, &t2.mLargeObject2, sizeof(t1.mLargeObject2)) < 0) &&
(strcmp(t1.mpName1, t2.mpName1) < 0) &&
(strcmp(t1.mpName2, t2.mpName2) < 0);
}
#endif // Header sentry
+194
View File
@@ -0,0 +1,194 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#include "EASTLBenchmark.h"
#include "EASTLTest.h"
#if !EASTL_OPENSOURCE
#include <PPMalloc/EAGeneralAllocatorDebug.h>
#endif
#include <EAStdC/EASprintf.h>
#include <EAStdC/EAStopwatch.h>
#include <EAStdC/EAString.h>
#include <EASTL/internal/config.h>
#include <string.h>
#include <stdio.h>
EA_DISABLE_VC_WARNING(4946)
#include "EAMain/EAEntryPointMain.inl"
#include "EASTLTestAllocator.h"
///////////////////////////////////////////////////////////////////////////////
// gpEAGeneralAllocator / gpEAGeneralAllocatorDebug
//
#if !EASTL_OPENSOURCE
namespace EA
{
namespace Allocator
{
#ifdef EA_DEBUG
extern GeneralAllocatorDebug gGeneralAllocator;
extern PPM_API GeneralAllocatorDebug* gpEAGeneralAllocatorDebug;
#else
extern GeneralAllocator gGeneralAllocator;
extern PPM_API GeneralAllocator* gpEAGeneralAllocator;
#endif
}
}
#endif
///////////////////////////////////////////////////////////////////////////////
// Required by EASTL.
//
#if !defined(EASTL_EASTDC_VSNPRINTF) || !EASTL_EASTDC_VSNPRINTF
int Vsnprintf8(char8_t* pDestination, size_t n, const char8_t* pFormat, va_list arguments)
{
return EA::StdC::Vsnprintf(pDestination, n, pFormat, arguments);
}
int Vsnprintf16(char16_t* pDestination, size_t n, const char16_t* pFormat, va_list arguments)
{
return EA::StdC::Vsnprintf(pDestination, n, pFormat, arguments);
}
#if (EASTDC_VERSION_N >= 10600)
int Vsnprintf32(char32_t* pDestination, size_t n, const char32_t* pFormat, va_list arguments)
{
return EA::StdC::Vsnprintf(pDestination, n, pFormat, arguments);
}
#endif
#endif
///////////////////////////////////////////////////////////////////////////////
// main
//
int EAMain(int argc, char* argv[])
{
bool bWaitAtEnd = false;
bool bPrintHelp = false;
int nOptionCount = 0;
int nErrorCount = 0;
EA::EAMain::PlatformStartup();
EA::EAMain::SetVerbosity(2); // Default value.
// Set up debug parameters.
#ifdef EA_DEBUG
// Only enable this temporarily to help find any problems you might find.
// EA::Allocator::gpEAGeneralAllocatorDebug->SetAutoHeapValidation(EA::Allocator::GeneralAllocator::kHeapValidationLevelBasic, 16);
#endif
// Parse command line arguments
for(int i = 1; i < argc; i++)
{
if(strstr(argv[i], "-w") == argv[i])
{
bWaitAtEnd = true;
nOptionCount++;
}
else if(strstr(argv[i], "-v") == argv[i])
{
uint32_t verbosity = EA::StdC::AtoU32(argv[i] + 3);
EA::EAMain::SetVerbosity(verbosity);
nOptionCount++;
}
else if(strstr(argv[i], "-l:") == argv[i])
{
gEASTL_TestLevel = atoi(argv[i] + 3);
if(gEASTL_TestLevel < kEASTL_TestLevelLow)
gEASTL_TestLevel = kEASTL_TestLevelLow;
else if(gEASTL_TestLevel > kEASTL_TestLevelHigh)
gEASTL_TestLevel = kEASTL_TestLevelHigh;
nOptionCount++;
}
else if(strstr(argv[i], "-s:") == argv[i])
{
uint32_t seed = (eastl_size_t)atoi(argv[i] + 3);
EA::UnitTest::SetRandSeed(seed);
nOptionCount++;
}
else if((strstr(argv[i], "-?") == argv[i]) || (strstr(argv[i], "-h") == argv[i]))
{
bPrintHelp = true;
nOptionCount++;
}
}
// Print user help.
if(!bPrintHelp)
bPrintHelp = (nOptionCount == 0);
if(bPrintHelp)
{
EASTLTest_Printf("Options\n");
EASTLTest_Printf(" -w Wait at end.\n");
EASTLTest_Printf(" -l:N Test level in range of [1, 10]. 10 means maximum testing.\n");
EASTLTest_Printf(" -s:N Specify a randomization seed. 0 is default and means use clock.\n");
EASTLTest_Printf(" -? Show help.\n");
}
// Set up test information
Benchmark::Environment& environment = Benchmark::GetEnvironment();
environment.msPlatform = EA_PLATFORM_DESCRIPTION;
environment.msSTLName1 = GetStdSTLName();
environment.msSTLName2 = "EASTL";
// Run tests
#ifndef EA_DEBUG
EA::UnitTest::SetHighThreadPriority();
#endif
EA::StdC::Stopwatch stopwatch(EA::StdC::Stopwatch::kUnitsSeconds, true); // Measure seconds, start the counting immediately.
BenchmarkAlgorithm();
BenchmarkList();
BenchmarkString();
BenchmarkVector();
BenchmarkDeque();
BenchmarkSet();
BenchmarkMap();
BenchmarkHash();
BenchmarkHeap();
BenchmarkBitset();
BenchmarkSort();
BenchmarkTupleVector();
stopwatch.Stop();
#ifndef EA_DEBUG
EA::UnitTest::SetNormalThreadPriority();
#endif
Benchmark::PrintResults();
eastl::string sClockTime;
Benchmark::WriteTime(stopwatch.GetElapsedTime(), sClockTime);
EASTLTest_Printf("Time to complete all tests: %s.\n", sClockTime.c_str());
// Done
if(bWaitAtEnd)
{
EASTLTest_Printf("\nPress any key to exit.\n");
getchar(); // Wait for the user and shutdown
}
EA::EAMain::PlatformShutdown(nErrorCount);
return 0;
}
+851
View File
@@ -0,0 +1,851 @@
# EASTL Benchmarks
## Introduction
This document provides a number of benchmark results of EASTL. Where possible, these benchmarks are implemented as comparisons with equivalent functionality found in other libraries such as compiler STL libraries or other well-known libraries. These comparison benchmarks concentrate on highlighting the differences between implementations rather than the similarities. In many mundane cases -- such as accessing a vector element via operator [] -- virtually all vector/array implementations you are likely to run into will have identical performance.
It's also important to note that the platform you run on can make a significant difference in the results. On a modern 3+GHz Windows PC many operations are fast due to large memory caches, intelligent branch prediction, and parallel instruction execution. However, on embedded or console systems none of these may be the case.
While EASTL generally outperforms std STL, there are some benchmarks here in which EASTL is slower than std STL. There are three primary explanations of this:
1. EASTL is making some kind of speed, memory, or design tradeoff that results in the given speed difference. In may such cases, EASTL goes slower on one benchmark in order to go faster on another benchmark deemed more important. This explanation constitutes about 60% of the cases.
2. Compiler optimizations and resulting code generation is coincidencally favoring one kind of implementation over another, often when they are visually virtually identical. This explantation constitutes about 30% of the cases.
3. EASTL is simply not yet as optimized as it could be. This explanation constitutes about 10% of the cases (as of this writing there are about three such functions throughout EASTL).
## Benchmarks
Below is a table of links to detailed benchmark results derived from the Benchmark test present in the EASTL package. The detailed results are present below the table. Additional platforms will be added as results become available for those platforms. Debug benchmarks are present because (lack of) debug performance can be significant for highly templated libraries. EASTL has specific optimizations to enhance debug performance relative to other standard libraries; in some cases it is 10x or more faster than alternatives (though there are exceptions where EASTL is slower). Feel free to submit results for additional compilers/platforms.
| Platform | Compiler | STL type | Build | Results |
|------|------|------|------|------|
| Win32 | VC++ 7.1 | Microsoft (Dinkumware) | Debug | [Detail]() |
| Win32 | VC++ 7.1 | Microsoft (Dinkumware) | Release | [Detail]() |
| Win32 | VC++ 7.1 | STLPort | Debug | [Detail]() |
| Win32 | VC++ 7.1 | STLPort | Release | [Detail]() |
### Win32.VC71.MS.Debug
```
EASTL version: 0.96.00
Platform: Windows on X86
Compiler: Microsoft Visual C++ compiler, version 1310
Allocator: PPMalloc::GeneralAllocatorDebug. Thread safety enabled.
Build: Debug. Inlining disabled. STL debug features disabled.
Values are times to complete tests; smaller values are better.
Alarm indicates a greater than 10% difference.
Test VC++ EASTL Ratio Alarm
----------------------------------------------------------------------------------------
algorithm/adj_find/vector<TestObject> 33061345 6497757 5.09 *
algorithm/copy/vector<LargePOD> 5844906 4876076 1.20 *
algorithm/copy/vector<uint32_t> 1634346 166065 9.84 *
algorithm/copy_backward/vector<LargePOD> 4515974 4638892 0.97
algorithm/copy_backward/vector<uint32_t> 1821168 121746 14.96 *
algorithm/count/vector<uint64_t> 17048884 2720766 6.27 *
algorithm/equal_range/vector<uint64_t> 1111147812 448756888 2.48 *
algorithm/fill/bool[] 1728722 91936 18.80 *
algorithm/fill/char[]/'d' 1299200 33745 38.50 *
algorithm/fill/vector<char>/'d' 10205092 33796 100.00 *
algorithm/fill/vector<char>/0 10200748 33805 100.00 *
algorithm/fill/vector<uint64_t> 10416538 1399687 7.44 *
algorithm/fill/vector<void*> 10221837 1307700 7.82 *
algorithm/fill_n/bool[] 1399033 34196 40.91 *
algorithm/fill_n/char[] 1299225 33754 38.49 *
algorithm/fill_n/vector<uint64_t> 5961637 1371900 4.35 *
algorithm/find_end/string/end 16569373 2657372 6.24 *
algorithm/find_end/string/middle 16558638 20242410 0.82 *
algorithm/find_end/string/none 16811207 40480468 0.42 *
algorithm/lex_cmp/schar[] 1749674 194429 9.00 *
algorithm/lex_cmp/vector<TestObject> 32824195 5253587 6.25 *
algorithm/lex_cmp/vector<uchar> 29852034 202658 100.00 *
algorithm/lower_bound/vector<TestObject> 798624462 350027935 2.28 *
algorithm/min_element/vector<TestObject> 21675298 5314676 4.08 *
algorithm/rand_shuffle/vector<uint64_t> 84236190 43677506 1.93 *
algorithm/reverse/list<TestObject> 3007292 2105799 1.43 *
algorithm/reverse/vector<TestObject> 2974618 2124796 1.40 *
algorithm/search/string<char> 16228158 3594268 4.52 *
algorithm/search_n/string<char> 16926985 1522096 11.12 *
algorithm/unique/vector<TestObject> 54206243 9988002 5.43 *
algorithm/unique/vector<uint32_t> 26940079 1741991 15.47 *
algorithm/unique/vector<uint64_t> 47621344 5213127 9.13 *
algorithm/upper_bound/vector<uint32_t> 372381295 137901552 2.70 *
bitset<1500>/>>=/1 90196544 92539832 0.97
bitset<1500>/count 50753832 53742117 0.94
bitset<1500>/flip 86935875 85121117 1.02
bitset<1500>/reset 78153837 79922611 0.98
bitset<1500>/set() 79214968 79360658 1.00
bitset<1500>/set(i) 11300589 12199651 0.93
bitset<1500>/test 11282679 13186450 0.86 *
bitset<15>/>>=/1 10500577 6000559 1.75 *
bitset<15>/count 4000356 6399753 0.63 *
bitset<15>/flip 7268877 5647944 1.29 *
bitset<15>/reset 8564235 5800163 1.48 *
bitset<15>/set() 9935523 5914012 1.68 *
bitset<15>/set(i) 11199703 12503637 0.90 *
bitset<15>/test 10600623 12899592 0.82 *
bitset<35>/>>=/1 13076052 6599834 1.98 *
bitset<35>/count 4800384 11500330 0.42 *
bitset<35>/flip 7915439 5816313 1.36 *
bitset<35>/reset 9400049 5803180 1.62 *
bitset<35>/set() 10701152 5840316 1.83 *
bitset<35>/set(i) 11342936 12271128 0.92
bitset<35>/test 10670799 13099682 0.81 *
bitset<75>/>>=/1 14198834 17151088 0.83 *
bitset<75>/count 5795530 8576373 0.68 *
bitset<75>/flip 8516703 8922995 0.95
bitset<75>/reset 9999970 8526095 1.17 *
bitset<75>/set() 11124877 9009686 1.23 *
bitset<75>/set(i) 11300563 12531618 0.90 *
bitset<75>/test 11031913 13100523 0.84 *
deque<ValuePair>/erase 743801706 335646802 2.22 *
deque<ValuePair>/insert 742331809 341912866 2.17 *
deque<ValuePair>/iteration 29097030 16315827 1.78 *
deque<ValuePair>/operator[] 49859598 24026313 2.08 *
deque<ValuePair>/push_back 424807033 34497608 12.31 *
deque<ValuePair>/push_front 402313373 38006322 10.59 *
deque<ValuePair>/sort 725101017 581796551 1.25 *
hash_map<string, uint32_t>/clear 559462 961019 0.58 *
hash_map<string, uint32_t>/count 53377807 8091448 6.60 *
hash_map<string, uint32_t>/erase pos 613573 858084 0.72 *
hash_map<string, uint32_t>/erase range 5488748 461134 11.90 *
hash_map<string, uint32_t>/erase val 35760096 16379858 2.18 *
hash_map<string, uint32_t>/find 43490335 10324823 4.21 *
hash_map<string, uint32_t>/find_as/char* 49343818 8617139 5.73 *
hash_map<string, uint32_t>/insert 107420281 168690439 0.64 *
hash_map<string, uint32_t>/iteration 2456356 1255153 1.96 *
hash_map<string, uint32_t>/operator[] 47209502 12581624 3.75 *
hash_map<uint32_t, TestObject>/clear 533172 546449 0.98
hash_map<uint32_t, TestObject>/count 28667432 2899997 9.89 *
hash_map<uint32_t, TestObject>/erase pos 683239 538289 1.27 *
hash_map<uint32_t, TestObject>/erase range 9632676 253037 38.07 *
hash_map<uint32_t, TestObject>/erase val 25466026 7752188 3.29 *
hash_map<uint32_t, TestObject>/find 20048253 4678502 4.29 *
hash_map<uint32_t, TestObject>/insert 71085798 37686187 1.89 *
hash_map<uint32_t, TestObject>/iteration 1460318 1338317 1.09
hash_map<uint32_t, TestObject>/operator[] 23226692 7888748 2.94 *
heap (uint32_t[])/make_heap 5399966 6961305 0.78 *
heap (uint32_t[])/pop_heap 108060534 103511318 1.04
heap (uint32_t[])/push_heap 22595661 16640688 1.36 *
heap (uint32_t[])/sort_heap 93559424 83076731 1.13 *
heap (vector<TestObject>)/make_heap 91770743 21724870 4.22 *
heap (vector<TestObject>)/pop_heap 1175599317 284007398 4.14 *
heap (vector<TestObject>)/push_heap 207804541 45918046 4.53 *
heap (vector<TestObject>)/sort_heap 970394145 208321477 4.66 *
list<TestObject>/ctor(it) 805539509 760938607 1.06
list<TestObject>/ctor(n) 80959236 75106995 1.08
list<TestObject>/erase 1052543704 1044976137 1.01
list<TestObject>/find 97785267 75970884 1.29 *
list<TestObject>/insert 873895175 807051107 1.08
list<TestObject>/push_back 812797710 780742425 1.04
list<TestObject>/remove 1850600714 1436980599 1.29 *
list<TestObject>/reverse 180270465 80466636 2.24 *
list<TestObject>/size/1 440148 599642 0.73 *
list<TestObject>/size/10 439433 1329817 0.33 * EASTL intentionally implements list::size as O(n).
list<TestObject>/size/100 439595 11030060 0.04 * EASTL intentionally implements list::size as O(n).
list<TestObject>/splice 177106094 69383027 2.55 *
map<TestObject, uint32_t>/clear 508283 470807 1.08
map<TestObject, uint32_t>/count 43145354 14280357 3.02 *
map<TestObject, uint32_t>/equal_range 38594004 16520447 2.34 *
map<TestObject, uint32_t>/erase/key 33948082 16123175 2.11 *
map<TestObject, uint32_t>/erase/pos 578332 455201 1.27 * MS uses a code bloating implementation of erase.
map<TestObject, uint32_t>/erase/range 387345 284538 1.36 *
map<TestObject, uint32_t>/find 22897224 12766100 1.79 *
map<TestObject, uint32_t>/insert 61665800 47286928 1.30 *
map<TestObject, uint32_t>/iteration 1977202 745391 2.65 *
map<TestObject, uint32_t>/lower_bound 19892941 12260928 1.62 *
map<TestObject, uint32_t>/operator[] 24199084 15429634 1.57 *
map<TestObject, uint32_t>/upper_bound 19842409 12064441 1.64 *
set<uint32_t>/clear 1027625 1000901 1.03
set<uint32_t>/count 39730182 13329565 2.98 *
set<uint32_t>/equal_range 34681649 14768827 2.35 *
set<uint32_t>/erase range 841458 602030 1.40 *
set<uint32_t>/erase/pos 1380485 1084303 1.27 * MS uses a code bloating implementation of erase.
set<uint32_t>/erase/val 31617425 13344023 2.37 *
set<uint32_t>/find 19582428 10788864 1.82 *
set<uint32_t>/insert 61434014 48232086 1.27 *
set<uint32_t>/iteration 1512057 667820 2.26 *
set<uint32_t>/lower_bound 18394885 10402785 1.77 *
set<uint32_t>/upper_bound 17189083 10554425 1.63 *
sort/q_sort/TestObject[] 87088799 15037988 5.79 *
sort/q_sort/TestObject[]/sorted 21502892 3284299 6.55 *
sort/q_sort/vector<TestObject> 87962047 15004677 5.86 *
sort/q_sort/vector<TestObject>/sorted 21396523 3341163 6.40 *
sort/q_sort/vector<ValuePair> 80334589 10429161 7.70 *
sort/q_sort/vector<ValuePair>/sorted 22133295 3230553 6.85 *
sort/q_sort/vector<uint32> 72195388 5940302 12.15 *
sort/q_sort/vector<uint32>/sorted 19635171 995495 19.72 *
string<char16_t>/compare 523013373 534722089 0.98
string<char16_t>/erase/pos,n 3446597 3439492 1.00
string<char16_t>/find/p,pos,n 383873158 441902786 0.87 *
string<char16_t>/find_first_not_of/p,pos,n 174157 134131 1.30 *
string<char16_t>/find_first_of/p,pos,n 11715423 8520944 1.37 *
string<char16_t>/find_last_of/p,pos,n 1871556 1226457 1.53 *
string<char16_t>/insert/pos,p 3624877 3357058 1.08
string<char16_t>/iteration 6766787933 581916665 11.63 *
string<char16_t>/operator[] 4820827 2335579 2.06 *
string<char16_t>/push_back 59812962 6757466 8.85 *
string<char16_t>/replace/pos,n,p,n 4371279 4459713 0.98
string<char16_t>/reserve 2307530 1919386 1.20 *
string<char16_t>/rfind/p,pos,n 734826 372615 1.97 *
string<char16_t>/size 41608 28866 1.44 *
string<char16_t>/swap 1033932 1490994 0.69 *
string<char8_t>/compare 63086797 64194771 0.98
string<char8_t>/erase/pos,n 2045687 1960270 1.04
string<char8_t>/find/p,pos,n 123872549 471364764 0.26 *
string<char8_t>/find_first_not_of/p,pos,n 140013 130271 1.07
string<char8_t>/find_first_of/p,pos,n 8051906 8749994 0.92
string<char8_t>/find_last_of/p,pos,n 1318835 1230715 1.07
string<char8_t>/insert/pos,p 1770610 1724234 1.03
string<char8_t>/iteration 28112136 2544475 11.05 *
string<char8_t>/operator[] 4810525 2255841 2.13 *
string<char8_t>/push_back 54869634 6127447 8.95 *
string<char8_t>/replace/pos,n,p,n 2737578 2847900 0.96
string<char8_t>/reserve 1123395 394902 2.84 *
string<char8_t>/rfind/p,pos,n 737299 368518 2.00 *
string<char8_t>/size 42245 26801 1.58 *
string<char8_t>/swap 1036142 1491028 0.69 *
vector<uint64>/erase 56417135 55770251 1.01
vector<uint64>/insert 56617761 56100468 1.01
vector<uint64>/iteration 10413895 1291269 8.06 *
vector<uint64>/operator[] 23507193 3479390 6.76 *
vector<uint64>/push_back 34687939 13806627 2.51 *
vector<uint64>/sort 256886550 84669657 3.03 *
```
### Win32.VC71.MS.Release
```
EASTL version: 0.96.00
Platform: Windows on X86
Compiler: Microsoft Visual C++ compiler, version 1310
Allocator: PPMalloc::GeneralAllocator. Thread safety enabled.
Build: Full optimization. Inlining enabled.
Values are times to complete tests; smaller values are better.
Alarm indicates a greater than 10% difference.
Test VC++ EASTL Ratio Alarm
----------------------------------------------------------------------------------------
algorithm/adj_find/vector<TestObject> 2783546 2750660 1.01
algorithm/copy/vector<LargePOD> 6474025 4972738 1.30 *
algorithm/copy/vector<uint32_t> 157267 173162 0.91
algorithm/copy_backward/vector<LargePOD> 4836406 4374780 1.11 *
algorithm/copy_backward/vector<uint32_t> 104780 120912 0.87 *
algorithm/count/vector<uint64_t> 1368440 1368696 1.00
algorithm/equal_range/vector<uint64_t> 114199387 102783938 1.11 *
algorithm/fill/bool[] 253215 27353 9.26 *
algorithm/fill/char[]/'d' 253164 27404 9.24 *
algorithm/fill/vector<char>/'d' 253105 27362 9.25 *
algorithm/fill/vector<char>/0 253275 27353 9.26 *
algorithm/fill/vector<uint64_t> 397001 394323 1.01
algorithm/fill/vector<void*> 547196 642362 0.85 *
algorithm/fill_n/bool[] 229177 27361 8.38 *
algorithm/fill_n/char[] 228845 27404 8.35 *
algorithm/fill_n/vector<uint64_t> 565233 1376822 0.41 *
algorithm/find_end/string/end 2107116 82356 25.59 *
algorithm/find_end/string/middle 2111672 664283 3.18 *
algorithm/find_end/string/none 2110423 1519596 1.39 *
algorithm/lex_cmp/schar[] 741021 176162 4.21 *
algorithm/lex_cmp/vector<TestObject> 2610494 2642183 0.99
algorithm/lex_cmp/vector<uchar> 697595 167866 4.16 *
algorithm/lower_bound/vector<TestObject> 62462233 58146664 1.07
algorithm/min_element/vector<TestObject> 4350385 2671227 1.63 *
algorithm/rand_shuffle/vector<uint64_t> 10868261 11300818 0.96
algorithm/reverse/list<TestObject> 483718 470024 1.03
algorithm/reverse/vector<TestObject> 476739 484322 0.98
algorithm/search/string<char> 2560387 1259496 2.03 *
algorithm/search_n/string<char> 2770991 458524 6.04 *
algorithm/unique/vector<TestObject> 4194520 4658910 0.90 *
algorithm/unique/vector<uint32_t> 538730 787924 0.68 *
algorithm/unique/vector<uint64_t> 3169829 2575636 1.23 *
algorithm/upper_bound/vector<uint32_t> 27495562 25321593 1.09
bitset<1500>/>>=/1 33464228 33469719 1.00
bitset<1500>/count 18736116 18814903 1.00
bitset<1500>/flip 19299309 18605438 1.04
bitset<1500>/reset 22200487 15262847 1.45 *
bitset<1500>/set() 14418193 17557319 0.82 *
bitset<1500>/set(i) 1599250 1599199 1.00
bitset<1500>/test 1599241 1599233 1.00
bitset<15>/>>=/1 2199222 2264442 0.97
bitset<15>/count 1399406 1399193 1.00
bitset<15>/flip 1266712 1199197 1.06
bitset<15>/reset 1399364 1399109 1.00
bitset<15>/set() 1199197 999201 1.20 *
bitset<15>/set(i) 1599258 1462952 1.09
bitset<15>/test 1599275 1599224 1.00
bitset<35>/>>=/1 2599266 1933376 1.34 *
bitset<35>/count 2599240 2592559 1.00
bitset<35>/flip 1693124 1199188 1.41 *
bitset<35>/reset 1399406 999201 1.40 *
bitset<35>/set() 1599403 1199205 1.33 *
bitset<35>/set(i) 1599241 1599190 1.00
bitset<35>/test 1599250 1599232 1.00
bitset<75>/>>=/1 4199332 4199213 1.00
bitset<75>/count 2999497 2199341 1.36 *
bitset<75>/flip 2399499 1830178 1.31 *
bitset<75>/reset 2199468 1199197 1.83 *
bitset<75>/set() 1999387 1199851 1.67 *
bitset<75>/set(i) 1599266 1599198 1.00
bitset<75>/test 1599241 1662651 0.96
deque<ValuePair>/erase 90444165 37113253 2.44 *
deque<ValuePair>/insert 93299349 36175167 2.58 *
deque<ValuePair>/iteration 2756414 2122076 1.30 *
deque<ValuePair>/operator[] 5117969 4632075 1.10
deque<ValuePair>/push_back 30300757 3060357 9.90 *
deque<ValuePair>/push_front 25498529 2808392 9.08 *
deque<ValuePair>/sort 142283047 111292464 1.28 *
hash_map<string, uint32_t>/clear 146769 389699 0.38 *
hash_map<string, uint32_t>/count 13059434 3460324 3.77 *
hash_map<string, uint32_t>/erase pos 184246 331925 0.56 *
hash_map<string, uint32_t>/erase range 382432 167237 2.29 *
hash_map<string, uint32_t>/erase val 6187898 3302114 1.87 *
hash_map<string, uint32_t>/find 11289369 3459024 3.26 *
hash_map<string, uint32_t>/find_as/char* 13559192 3662387 3.70 *
hash_map<string, uint32_t>/insert 17514012 14095176 1.24 *
hash_map<string, uint32_t>/iteration 801014 218450 3.67 *
hash_map<string, uint32_t>/operator[] 11457065 3690385 3.10 *
hash_map<uint32_t, TestObject>/clear 141865 265379 0.53 *
hash_map<uint32_t, TestObject>/count 1766045 703613 2.51 *
hash_map<uint32_t, TestObject>/erase pos 172337 218458 0.79 *
hash_map<uint32_t, TestObject>/erase range 537846 102340 5.26 *
hash_map<uint32_t, TestObject>/erase val 2220132 1441787 1.54 *
hash_map<uint32_t, TestObject>/find 1612994 1043953 1.55 *
hash_map<uint32_t, TestObject>/insert 7141547 4348056 1.64 *
hash_map<uint32_t, TestObject>/iteration 199512 169328 1.18 *
hash_map<uint32_t, TestObject>/operator[] 1831733 1519707 1.21 *
heap (uint32_t[])/make_heap 3366247 1949093 1.73 *
heap (uint32_t[])/pop_heap 57280514 53779440 1.07
heap (uint32_t[])/push_heap 9700217 7582935 1.28 *
heap (uint32_t[])/sort_heap 47227751 46131948 1.02
heap (vector<TestObject>)/make_heap 11458442 11510819 1.00
heap (vector<TestObject>)/pop_heap 122897267 119061132 1.03
heap (vector<TestObject>)/push_heap 21688481 21176220 1.02
heap (vector<TestObject>)/sort_heap 90867380 88869523 1.02
list<TestObject>/ctor(it) 74591104 69845817 1.07
list<TestObject>/ctor(n) 6243998 5838582 1.07
list<TestObject>/erase 299509298 206013676 1.45 *
list<TestObject>/find 40927185 14514243 2.82 *
list<TestObject>/insert 71277251 47234534 1.51 *
list<TestObject>/push_back 73780527 44116725 1.67 *
list<TestObject>/remove 786197776 326434612 2.41 *
list<TestObject>/reverse 49283128 25029678 1.97 *
list<TestObject>/size/1 159741 139400 1.15 *
list<TestObject>/size/10 159324 346579 0.46 * EASTL intentionally implements list::size as O(n).
list<TestObject>/size/100 159188 97235419 0.00 * EASTL intentionally implements list::size as O(n).
list<TestObject>/splice 63548584 19322931 3.29 *
map<TestObject, uint32_t>/clear 167408 170501 0.98
map<TestObject, uint32_t>/count 10213685 4748346 2.15 *
map<TestObject, uint32_t>/equal_range 9515053 5677558 1.68 *
map<TestObject, uint32_t>/erase/key 6646260 4302300 1.54 *
map<TestObject, uint32_t>/erase/pos 297135 327938 0.91 MS uses a code bloating implementation of erase.
map<TestObject, uint32_t>/erase/range 148614 163702 0.91
map<TestObject, uint32_t>/find 5637531 4767055 1.18 *
map<TestObject, uint32_t>/insert 9591128 9030349 1.06
map<TestObject, uint32_t>/iteration 323595 325261 0.99
map<TestObject, uint32_t>/lower_bound 5398239 4784089 1.13 *
map<TestObject, uint32_t>/operator[] 5631250 5141166 1.10
map<TestObject, uint32_t>/upper_bound 5436336 4762431 1.14 *
set<uint32_t>/clear 155983 156026 1.00
set<uint32_t>/count 9635965 4392146 2.19 *
set<uint32_t>/equal_range 8504157 5247832 1.62 *
set<uint32_t>/erase range 140488 119408 1.18 *
set<uint32_t>/erase/pos 260678 286697 0.91 MS uses a code bloating implementation of erase.
set<uint32_t>/erase/val 6008225 4012825 1.50 *
set<uint32_t>/find 5145432 4381945 1.17 *
set<uint32_t>/insert 8087129 8697251 0.93
set<uint32_t>/iteration 271507 304538 0.89 *
set<uint32_t>/lower_bound 4666228 4404250 1.06
set<uint32_t>/upper_bound 4623600 4402974 1.05
sort/q_sort/TestObject[] 9596169 5578652 1.72 *
sort/q_sort/TestObject[]/sorted 602463 1016132 0.59 *
sort/q_sort/vector<TestObject> 9674828 5430199 1.78 *
sort/q_sort/vector<TestObject>/sorted 606908 1111647 0.55 *
sort/q_sort/vector<ValuePair> 6284194 3423452 1.84 *
sort/q_sort/vector<ValuePair>/sorted 711629 569364 1.25 *
sort/q_sort/vector<uint32> 5453379 2916146 1.87 *
sort/q_sort/vector<uint32>/sorted 537047 419144 1.28 *
string<char16_t>/compare 435083295 251985824 1.73 *
string<char16_t>/erase/pos,n 3454842 3451858 1.00
string<char16_t>/find/p,pos,n 401954723 165298157 2.43 *
string<char16_t>/find_first_not_of/p,pos,n 131452 65374 2.01 *
string<char16_t>/find_first_of/p,pos,n 11657444 4144515 2.81 *
string<char16_t>/find_last_of/p,pos,n 1604248 567571 2.83 *
string<char16_t>/insert/pos,p 3398734 3355460 1.01
string<char16_t>/iteration 218856504 218771844 1.00
string<char16_t>/operator[] 714161 240023 2.98 *
string<char16_t>/push_back 34968235 2444897 14.30 *
string<char16_t>/replace/pos,n,p,n 4226693 4198498 1.01
string<char16_t>/reserve 1901765 390805 4.87 *
string<char16_t>/rfind/p,pos,n 195483 150985 1.29 *
string<char16_t>/size 11169 11245 0.99
string<char16_t>/swap 1459280 419807 3.48 *
string<char8_t>/compare 63071275 77209580 0.82 *
string<char8_t>/erase/pos,n 2008652 1944494 1.03
string<char8_t>/find/p,pos,n 123201023 167536164 0.74 *
string<char8_t>/find_first_not_of/p,pos,n 93372 67864 1.38 *
string<char8_t>/find_first_of/p,pos,n 7542492 3375758 2.23 *
string<char8_t>/find_last_of/p,pos,n 933972 583576 1.60 *
string<char8_t>/insert/pos,p 1737213 1750847 0.99
string<char8_t>/iteration 893834 899130 0.99
string<char8_t>/operator[] 817879 313437 2.61 *
string<char8_t>/push_back 20857734 2004410 10.41 *
string<char8_t>/replace/pos,n,p,n 2578696 2607655 0.99
string<char8_t>/reserve 915127 85289 10.73 *
string<char8_t>/rfind/p,pos,n 196103 148894 1.32 *
string<char8_t>/size 11619 11220 1.04
string<char8_t>/swap 1461056 419874 3.48 *
vector<uint64>/erase 55235116 55284587 1.00
vector<uint64>/insert 55166046 55142755 1.00
vector<uint64>/iteration 553954 509719 1.09
vector<uint64>/operator[] 1284239 798516 1.61 *
vector<uint64>/push_back 5399549 3867959 1.40 *
vector<uint64>/sort 43636314 42619952 1.02
```
### Win32.VC71.STLPort.Debug
```
EASTL version: 0.96.00
Platform: Windows on X86
Compiler: Microsoft Visual C++ compiler, version 1310
Allocator: PPMalloc::GeneralAllocatorDebug. Thread safety enabled.
Build: Debug. Inlining disabled. STL debug features disabled.
Values are times to complete tests; smaller values are better.
Alarm indicates a greater than 10% difference.
Test STLPort EASTL Ratio Alarm
----------------------------------------------------------------------------------------
algorithm/adj_find/vector<TestObject> 5661170 5689517 1.00
algorithm/copy/vector<LargePOD> 5573815 5124428 1.09
algorithm/copy/vector<uint32_t> 148273 125782 1.18 *
algorithm/copy_backward/vector<LargePOD> 5429791 4834510 1.12 *
algorithm/copy_backward/vector<uint32_t> 156765 163038 0.96
algorithm/count/vector<uint64_t> 2730922 2730072 1.00
algorithm/equal_range/vector<uint64_t> 639366489 452896251 1.41 *
algorithm/fill/bool[] 1299326 27361 47.49 *
algorithm/fill/char[]/'d' 27378 27361 1.00
algorithm/fill/vector<char>/'d' 34459 27361 1.26 *
algorithm/fill/vector<char>/0 1299224 27361 47.48 *
algorithm/fill/vector<uint64_t> 1400647 1400145 1.00
algorithm/fill/vector<void*> 1308779 1309085 1.00
algorithm/fill_n/bool[] 1299156 27352 47.50 *
algorithm/fill_n/char[] 1299258 27369 47.47 *
algorithm/fill_n/vector<uint64_t> 1451162 1313632 1.10
algorithm/find_end/string/end 13089999 2526412 5.18 *
algorithm/find_end/string/middle 12627412 20190101 0.63 *
algorithm/find_end/string/none 12704185 40728803 0.31 *
algorithm/lex_cmp/schar[] 1749844 195806 8.94 *
algorithm/lex_cmp/vector<TestObject> 5060968 4799882 1.05
algorithm/lex_cmp/vector<uchar> 1668354 189490 8.80 *
algorithm/lower_bound/vector<TestObject> 450240945 353437573 1.27 *
algorithm/min_element/vector<TestObject> 5861744 5326371 1.10
algorithm/rand_shuffle/vector<uint64_t> 40780449 45780090 0.89 *
algorithm/reverse/list<TestObject> 2657678 2130627 1.25 *
algorithm/reverse/vector<TestObject> 2666424 2124889 1.25 *
algorithm/search/string<char> 3110379 3613460 0.86 *
algorithm/search_n/string<char> 3061665 1521261 2.01 *
algorithm/unique/vector<TestObject> 12423684 9485439 1.31 *
algorithm/unique/vector<uint32_t> 3718699 1726596 2.15 *
algorithm/unique/vector<uint64_t> 6205110 4591631 1.35 *
algorithm/upper_bound/vector<uint32_t> 185391094 139336317 1.33 *
bitset<1500>/>>=/1 120666960 92449816 1.31 * STLPort is broken, neglects wraparound check.
bitset<1500>/count 201709793 52874726 3.81 *
bitset<1500>/flip 87360297 81737071 1.07
bitset<1500>/reset 23950178 77390323 0.31 *
bitset<1500>/set() 84608107 76912011 1.10
bitset<1500>/set(i) 18023620 12229604 1.47 *
bitset<1500>/test 18006553 13276396 1.36 *
bitset<15>/>>=/1 11935904 6012695 1.99 * STLPort is broken, neglects wraparound check.
bitset<15>/count 9368581 6022742 1.56 *
bitset<15>/flip 11600706 6533635 1.78 *
bitset<15>/reset 5830957 5874690 0.99
bitset<15>/set() 11695328 5701621 2.05 *
bitset<15>/set(i) 16363205 12570216 1.30 *
bitset<15>/test 16743172 13201452 1.27 *
bitset<35>/>>=/1 22950918 6774457 3.39 * STLPort is broken, neglects wraparound check.
bitset<35>/count 12655309 11736256 1.08
bitset<35>/flip 13738575 5800042 2.37 *
bitset<35>/reset 15561434 5800510 2.68 *
bitset<35>/set() 13564283 5600709 2.42 *
bitset<35>/set(i) 18519689 12199973 1.52 *
bitset<35>/test 18000569 13103566 1.37 *
bitset<75>/>>=/1 25579525 16669664 1.53 * STLPort is broken, neglects wraparound check.
bitset<75>/count 18740698 8480492 2.21 *
bitset<75>/flip 13555630 8300335 1.63 *
bitset<75>/reset 15200133 8200000 1.85 *
bitset<75>/set() 14408112 8001959 1.80 *
bitset<75>/set(i) 18137741 12374257 1.47 *
bitset<75>/test 18422135 13100038 1.41 *
deque<ValuePair>/erase 651933790 326443043 2.00 *
deque<ValuePair>/insert 659786183 333304660 1.98 *
deque<ValuePair>/iteration 23734592 16173706 1.47 *
deque<ValuePair>/operator[] 59126816 23911774 2.47 *
deque<ValuePair>/push_back 58056988 31859266 1.82 *
deque<ValuePair>/push_front 57780891 31743199 1.82 *
deque<ValuePair>/sort 818414195 596568113 1.37 *
hash_map<string, uint32_t>/clear 3422133 2204517 1.55 *
hash_map<string, uint32_t>/count 9869545 8624924 1.14 *
hash_map<string, uint32_t>/erase pos 3256350 2069299 1.57 *
hash_map<string, uint32_t>/erase range 3230203 1151392 2.81 *
hash_map<string, uint32_t>/erase val 16860362 15939778 1.06
hash_map<string, uint32_t>/find 10286971 9920910 1.04
hash_map<string, uint32_t>/find_as/char* 118136025 9458468 12.49 *
hash_map<string, uint32_t>/insert 188948336 174490082 1.08
hash_map<string, uint32_t>/iteration 4037049 2021036 2.00 *
hash_map<string, uint32_t>/operator[] 11472127 12887699 0.89 *
hash_map<uint32_t, TestObject>/clear 2522264 1331848 1.89 *
hash_map<uint32_t, TestObject>/count 3210739 2897063 1.11 *
hash_map<uint32_t, TestObject>/erase pos 1862281 1304783 1.43 *
hash_map<uint32_t, TestObject>/erase range 698079 579606 1.20 *
hash_map<uint32_t, TestObject>/erase val 8806722 7041298 1.25 *
hash_map<uint32_t, TestObject>/find 3604875 4709645 0.77 *
hash_map<uint32_t, TestObject>/insert 40785711 40376342 1.01
hash_map<uint32_t, TestObject>/iteration 3064088 1508834 2.03 *
hash_map<uint32_t, TestObject>/operator[] 6053742 8176906 0.74 *
heap (uint32_t[])/make_heap 5799813 5738596 1.01
heap (uint32_t[])/pop_heap 113775168 102076134 1.11 *
heap (uint32_t[])/push_heap 21649151 16854845 1.28 *
heap (uint32_t[])/sort_heap 97535213 83290735 1.17 *
heap (vector<TestObject>)/make_heap 22215557 22277063 1.00
heap (vector<TestObject>)/pop_heap 275392171 277340039 0.99
heap (vector<TestObject>)/push_heap 51479442 47342577 1.09
heap (vector<TestObject>)/sort_heap 214474736 218497540 0.98
list<TestObject>/ctor(it) 767753795 753421427 1.02
list<TestObject>/ctor(n) 74185322 73386245 1.01
list<TestObject>/erase 1021003824 1033873589 0.99
list<TestObject>/find 77666072 74917622 1.04
list<TestObject>/insert 788071150 774188737 1.02
list<TestObject>/push_back 760490154 737327348 1.03
list<TestObject>/remove 1682511938 1434771006 1.17 *
list<TestObject>/reverse 87237327 80394623 1.09
list<TestObject>/size/1 3828111 599530 6.39 *
list<TestObject>/size/10 9600605 1329535 7.22 * EASTL intentionally implements list::size as O(n).
list<TestObject>/size/100 62952334 15022551 4.19 * EASTL intentionally implements list::size as O(n).
list<TestObject>/splice 96536412 60804817 1.59 *
map<TestObject, uint32_t>/clear 1142127 1099066 1.04
map<TestObject, uint32_t>/count 19659726 14647548 1.34 *
map<TestObject, uint32_t>/equal_range 36680687 18219086 2.01 *
map<TestObject, uint32_t>/erase/key 28892154 16037774 1.80 *
map<TestObject, uint32_t>/erase/pos 1209643 1185495 1.02
map<TestObject, uint32_t>/erase/range 715402 670539 1.07
map<TestObject, uint32_t>/find 21020992 13429575 1.57 *
map<TestObject, uint32_t>/insert 59530871 51120640 1.16 *
map<TestObject, uint32_t>/iteration 972825 1191946 0.82 *
map<TestObject, uint32_t>/lower_bound 18852651 12495034 1.51 *
map<TestObject, uint32_t>/operator[] 22889573 16676736 1.37 *
map<TestObject, uint32_t>/upper_bound 18603584 12406922 1.50 *
set<uint32_t>/clear 919555 882988 1.04
set<uint32_t>/count 17561110 12461084 1.41 *
set<uint32_t>/equal_range 31522488 15230282 2.07 *
set<uint32_t>/erase range 687582 564765 1.22 *
set<uint32_t>/erase/pos 1044352 1045355 1.00
set<uint32_t>/erase/val 25525304 12940774 1.97 *
set<uint32_t>/find 17140751 10704866 1.60 *
set<uint32_t>/insert 56035051 45555664 1.23 *
set<uint32_t>/iteration 682669 640831 1.07
set<uint32_t>/lower_bound 16339932 10475740 1.56 *
set<uint32_t>/upper_bound 17779424 10652599 1.67 *
sort/q_sort/TestObject[] 17000866 14823515 1.15 *
sort/q_sort/TestObject[]/sorted 6658559 3263328 2.04 *
sort/q_sort/vector<TestObject> 17476629 14953285 1.17 *
sort/q_sort/vector<TestObject>/sorted 6667034 3327435 2.00 *
sort/q_sort/vector<ValuePair> 15391357 10820848 1.42 *
sort/q_sort/vector<ValuePair>/sorted 6617122 3232949 2.05 *
sort/q_sort/vector<uint32> 8343906 6014846 1.39 *
sort/q_sort/vector<uint32>/sorted 3039430 1003127 3.03 *
string<char16_t>/compare 1489709846 532664000 2.80 *
string<char16_t>/erase/pos,n 3528690 3439864 1.03
string<char16_t>/find/p,pos,n 2521448321 443752189 5.68 *
string<char16_t>/find_first_not_of/p,pos,n 661206 137419 4.81 *
string<char16_t>/find_first_of/p,pos,n 54746434 8521335 6.42 *
string<char16_t>/find_last_of/p,pos,n 10607778 1212414 8.75 *
string<char16_t>/insert/pos,p 3445016 3360126 1.03
string<char16_t>/iteration 580955636 579452556 1.00
string<char16_t>/operator[] 2206353 1987809 1.11 *
string<char16_t>/push_back 22421368 6007808 3.73 *
string<char16_t>/replace/pos,n,p,n 5138454 4464786 1.15 *
string<char16_t>/reserve 4922413418 335622 100.00 *
string<char16_t>/rfind/p,pos,n 1440308 380578 3.78 *
string<char16_t>/size 25355 25398 1.00
string<char16_t>/swap 2122704 1490823 1.42 *
string<char8_t>/compare 77222134 77443134 1.00
string<char8_t>/erase/pos,n 1965344 1956521 1.00
string<char8_t>/find/p,pos,n 2468091951 474205522 5.20 *
string<char8_t>/find_first_not_of/p,pos,n 660960 130211 5.08 *
string<char8_t>/find_first_of/p,pos,n 55020899 9240171 5.95 *
string<char8_t>/find_last_of/p,pos,n 10576210 1239053 8.54 *
string<char8_t>/insert/pos,p 1822756 1750880 1.04
string<char8_t>/iteration 2617889 2540148 1.03
string<char8_t>/operator[] 2254794 2256443 1.00
string<char8_t>/push_back 12463022 5210321 2.39 *
string<char8_t>/replace/pos,n,p,n 3744862 2855260 1.31 *
string<char8_t>/reserve 1372046888 218815 100.00 *
string<char8_t>/rfind/p,pos,n 1446232 366902 3.94 *
string<char8_t>/size 26859 25431 1.06
string<char8_t>/swap 2123350 1490509 1.42 *
vector<uint64>/erase 55164013 56417449 0.98
vector<uint64>/insert 55872973 56432664 0.99
vector<uint64>/iteration 1329102 1324623 1.00
vector<uint64>/operator[] 5264738 3136746 1.68 *
vector<uint64>/push_back 14903245 13171175 1.13 *
vector<uint64>/sort 88429095 88542171 1.00
```
### Win32.VC71.STLPort.Release
```
EASTL version: 0.96.00
Platform: Windows on X86
Compiler: Microsoft Visual C++ compiler, version 1310
Allocator: PPMalloc::GeneralAllocator. Thread safety enabled.
Build: Full optimization. Inlining enabled.
Values are times to complete tests; smaller values are better.
Alarm indicates a greater than 10% difference.
Test STLPort EASTL Ratio Alarm
----------------------------------------------------------------------------------------
algorithm/adj_find/vector<TestObject> 2741046 2731441 1.00
algorithm/copy/vector<LargePOD> 6065923 5085142 1.19 *
algorithm/copy/vector<uint32_t> 158304 165555 0.96
algorithm/copy_backward/vector<LargePOD> 4710258 4896476 0.96
algorithm/copy_backward/vector<uint32_t> 146030 142630 1.02
algorithm/count/vector<uint64_t> 1395921 1406334 0.99
algorithm/equal_range/vector<uint64_t> 211692764 118969493 1.78 *
algorithm/fill/bool[] 366078 33737 10.85 *
algorithm/fill/char[]/'d' 33736 33771 1.00
algorithm/fill/vector<char>/'d' 28466 33720 0.84 *
algorithm/fill/vector<char>/0 366086 33728 10.85 *
algorithm/fill/vector<uint64_t> 466250 401591 1.16 *
algorithm/fill/vector<void*> 521603 693481 0.75 *
algorithm/fill_n/bool[] 599709 33762 17.76 *
algorithm/fill_n/char[] 599573 33711 17.79 *
algorithm/fill_n/vector<uint64_t> 434971 1374084 0.32 *
algorithm/find_end/string/end 1494742 85349 17.51 *
algorithm/find_end/string/middle 1480700 687208 2.15 *
algorithm/find_end/string/none 1540540 1546431 1.00
algorithm/lex_cmp/schar[] 921638 178797 5.15 *
algorithm/lex_cmp/vector<TestObject> 2623559 2643551 0.99
algorithm/lex_cmp/vector<uchar> 960899 183608 5.23 *
algorithm/lower_bound/vector<TestObject> 60630534 56531528 1.07
algorithm/min_element/vector<TestObject> 4209022 2768527 1.52 *
algorithm/rand_shuffle/vector<uint64_t> 13762010 15969052 0.86 *
algorithm/reverse/list<TestObject> 673387 731825 0.92
algorithm/reverse/vector<TestObject> 634576 754511 0.84 *
algorithm/search/string<char> 1262599 1387608 0.91
algorithm/search_n/string<char> 1166242 458592 2.54 *
algorithm/unique/vector<TestObject> 4912193 5336317 0.92
algorithm/unique/vector<uint32_t> 809387 809081 1.00
algorithm/unique/vector<uint64_t> 4371814 2414255 1.81 *
algorithm/upper_bound/vector<uint32_t> 31899081 29555596 1.08
bitset<1500>/>>=/1 63308136 40553560 1.56 * STLPort is broken, neglects wraparound check.
bitset<1500>/count 62523178 22799473 2.74 *
bitset<1500>/flip 20302845 19919232 1.02
bitset<1500>/reset 18892015 15403148 1.23 *
bitset<1500>/set() 15803302 17322192 0.91
bitset<1500>/set(i) 2799271 2999310 0.93
bitset<1500>/test 2999293 2799262 1.07
bitset<15>/>>=/1 1199239 3199256 0.37 * STLPort is broken, neglects wraparound check.
bitset<15>/count 3599461 2199231 1.64 *
bitset<15>/flip 1199231 1199188 1.00
bitset<15>/reset 1199188 1199180 1.00
bitset<15>/set() 1199214 1199180 1.00
bitset<15>/set(i) 2599257 1399262 1.86 *
bitset<15>/test 2599274 2599283 1.00
bitset<35>/>>=/1 6643974 4599239 1.44 * STLPort is broken, neglects wraparound check.
bitset<35>/count 5151331 5399438 0.95
bitset<35>/flip 1999404 1199273 1.67 *
bitset<35>/reset 9805285 1399313 7.01 *
bitset<35>/set() 2799279 1199248 2.33 *
bitset<35>/set(i) 2799246 1599241 1.75 *
bitset<35>/test 2999234 2999251 1.00
bitset<75>/>>=/1 7002045 6999333 1.00 STLPort is broken, neglects wraparound check.
bitset<75>/count 5999351 3002259 2.00 *
bitset<75>/flip 3599334 3599163 1.00
bitset<75>/reset 9799344 3399218 2.88 *
bitset<75>/set() 3599232 3599062 1.00
bitset<75>/set(i) 2799228 1599284 1.75 *
bitset<75>/test 2999250 2799339 1.07
deque<ValuePair>/erase 127108651 115258113 1.10
deque<ValuePair>/insert 137727889 116552332 1.18 *
deque<ValuePair>/iteration 7144182 6009899 1.19 *
deque<ValuePair>/operator[] 34241222 20535039 1.67 *
deque<ValuePair>/push_back 6585800 3932126 1.67 *
deque<ValuePair>/push_front 6805865 3993513 1.70 *
deque<ValuePair>/sort 395352323 348778188 1.13 *
hash_map<string, uint32_t>/clear 426640 447015 0.95
hash_map<string, uint32_t>/count 4359344 3883089 1.12 *
hash_map<string, uint32_t>/erase pos 584392 458142 1.28 *
hash_map<string, uint32_t>/erase range 221034 196078 1.13 *
hash_map<string, uint32_t>/erase val 3539867 3790813 0.93
hash_map<string, uint32_t>/find 3966831 3811910 1.04
hash_map<string, uint32_t>/find_as/char* 11591612 4243710 2.73 *
hash_map<string, uint32_t>/insert 16763887 16719194 1.00
hash_map<string, uint32_t>/iteration 909968 478609 1.90 *
hash_map<string, uint32_t>/operator[] 4360041 4108313 1.06
hash_map<uint32_t, TestObject>/clear 302634 283722 1.07
hash_map<uint32_t, TestObject>/count 916487 907426 1.01
hash_map<uint32_t, TestObject>/erase pos 388042 321385 1.21 *
hash_map<uint32_t, TestObject>/erase range 122680 116280 1.06
hash_map<uint32_t, TestObject>/erase val 1710931 1729529 0.99
hash_map<uint32_t, TestObject>/find 1089462 1346527 0.81 *
hash_map<uint32_t, TestObject>/insert 4560310 5072350 0.90 *
hash_map<uint32_t, TestObject>/iteration 960117 495354 1.94 *
hash_map<uint32_t, TestObject>/operator[] 1872830 1890595 0.99
heap (uint32_t[])/make_heap 3528418 3327257 1.06
heap (uint32_t[])/pop_heap 63243859 61011853 1.04
heap (uint32_t[])/push_heap 11602424 10045869 1.15 *
heap (uint32_t[])/sort_heap 52965362 48744729 1.09
heap (vector<TestObject>)/make_heap 13191456 13089711 1.01
heap (vector<TestObject>)/pop_heap 148555656 144787742 1.03
heap (vector<TestObject>)/push_heap 28696689 26618830 1.08
heap (vector<TestObject>)/sort_heap 112473989 114018643 0.99
list<TestObject>/ctor(it) 80186731 74006287 1.08
list<TestObject>/ctor(n) 6232311 6128007 1.02
list<TestObject>/erase 344556374 212877808 1.62 *
list<TestObject>/find 39859075 14591347 2.73 *
list<TestObject>/insert 86935153 56138233 1.55 *
list<TestObject>/push_back 79569180 46700641 1.70 *
list<TestObject>/remove 785786758 324201016 2.42 *
list<TestObject>/reverse 45248186 24852759 1.82 *
list<TestObject>/size/1 219844 219496 1.00
list<TestObject>/size/10 519563 519579 1.00 EASTL intentionally implements list::size as O(n).
list<TestObject>/size/100 4567194 101230266 0.05 * EASTL intentionally implements list::size as O(n).
list<TestObject>/splice 68321087 23601687 2.89 *
map<TestObject, uint32_t>/clear 168011 180540 0.93
map<TestObject, uint32_t>/count 4830439 5139287 0.94
map<TestObject, uint32_t>/equal_range 8700090 6158531 1.41 *
map<TestObject, uint32_t>/erase/key 6696776 4617038 1.45 *
map<TestObject, uint32_t>/erase/pos 309273 333183 0.93
map<TestObject, uint32_t>/erase/range 137419 136068 1.01
map<TestObject, uint32_t>/find 4773498 4931352 0.97
map<TestObject, uint32_t>/insert 9651877 9311699 1.04
map<TestObject, uint32_t>/iteration 372946 416364 0.90 *
map<TestObject, uint32_t>/lower_bound 4784234 4915797 0.97
map<TestObject, uint32_t>/operator[] 5040254 5183147 0.97
map<TestObject, uint32_t>/upper_bound 4724292 4915984 0.96
set<uint32_t>/clear 165300 173289 0.95
set<uint32_t>/count 4958654 4885086 1.02
set<uint32_t>/equal_range 8434134 5698681 1.48 *
set<uint32_t>/erase range 145554 133960 1.09
set<uint32_t>/erase/pos 299914 324760 0.92
set<uint32_t>/erase/val 6506155 4335034 1.50 *
set<uint32_t>/find 4866879 4556043 1.07
set<uint32_t>/insert 8340523 8957257 0.93
set<uint32_t>/iteration 294465 343442 0.86 *
set<uint32_t>/lower_bound 4548095 4756498 0.96
set<uint32_t>/upper_bound 4559196 4521498 1.01
sort/q_sort/TestObject[] 7316766 7013894 1.04
sort/q_sort/TestObject[]/sorted 1668439 1332885 1.25 *
sort/q_sort/vector<TestObject> 7331530 7017260 1.04
sort/q_sort/vector<TestObject>/sorted 1601629 1247120 1.28 *
sort/q_sort/vector<ValuePair> 7071643 7067869 1.00
sort/q_sort/vector<ValuePair>/sorted 2136390 1703799 1.25 *
sort/q_sort/vector<uint32> 3292891 2943627 1.12 *
sort/q_sort/vector<uint32>/sorted 653693 473612 1.38 *
string<char16_t>/compare 356579259 432760228 0.82 *
string<char16_t>/erase/pos,n 3430422 3428645 1.00
string<char16_t>/find/p,pos,n 229263402 225830975 1.02
string<char16_t>/find_first_not_of/p,pos,n 187391 81404 2.30 *
string<char16_t>/find_first_of/p,pos,n 4411831 4413532 1.00
string<char16_t>/find_last_of/p,pos,n 731655 726155 1.01
string<char16_t>/insert/pos,p 3408628 3319726 1.03
string<char16_t>/iteration 309993861 310333547 1.00
string<char16_t>/operator[] 580839 579904 1.00
string<char16_t>/push_back 3983338 2975553 1.34 *
string<char16_t>/replace/pos,n,p,n 4361095 4211504 1.04
string<char16_t>/reserve 935141729 247010 100.00 *
string<char16_t>/rfind/p,pos,n 248956 223397 1.11 *
string<char16_t>/size 13311 13107 1.02
string<char16_t>/swap 519129 579445 0.90 *
string<char8_t>/compare 76695559 76828015 1.00
string<char8_t>/erase/pos,n 1951566 1947282 1.00
string<char8_t>/find/p,pos,n 185878944 185605039 1.00
string<char8_t>/find_first_not_of/p,pos,n 196877 81600 2.41 *
string<char8_t>/find_first_of/p,pos,n 4147685 4145356 1.00
string<char8_t>/find_last_of/p,pos,n 605897 598222 1.01
string<char8_t>/insert/pos,p 1781592 1768264 1.01
string<char8_t>/iteration 921502 921272 1.00
string<char8_t>/operator[] 361250 359873 1.00
string<char8_t>/push_back 3363288 2530493 1.33 *
string<char8_t>/replace/pos,n,p,n 2682600 2633130 1.02
string<char8_t>/reserve 672517501 78387 100.00 *
string<char8_t>/rfind/p,pos,n 226202 200013 1.13 *
string<char8_t>/size 11280 11109 1.02
string<char8_t>/swap 519393 559759 0.93
vector<uint64>/erase 55184856 55192217 1.00
vector<uint64>/insert 56764267 55682726 1.02
vector<uint64>/iteration 423122 424039 1.00
vector<uint64>/operator[] 1189397 860991 1.38 *
vector<uint64>/push_back 5626609 4027317 1.40 *
vector<uint64>/sort 49227036 49231362 1.00
```
----------------------------------------------
End of document
+749
View File
@@ -0,0 +1,749 @@
# EASTL Best Practices
In this document we discuss best practices for using EASTL. The primary emphasis is on performance with a secondary emphasis on correctness and maintainability. Some best practices apply only to some situations, and these will be pointed out as we go along. In order to be easily digestible, we present these practices as a list of items in the tone of the Effective C++ series of books.
## Summary
The descriptions here are intentionally terse; this is to make them easier to visually scan.
1. [Consider intrusive containers.](#consider-intrusive-containers)
2. [Consider fixed-size containers.](#consider-fixed-size-containers)
3. [Consider custom allocators.](#consider-custom-allocators)
4. [Consider hash tables instead of maps.](#consider-hash-tables-instead-of-maps)
5. [Consider a vector_map (a.k.a. sorted vector) for unchanging data.](#consider-a-vector_map-aka-sorted-vector-for-unchanging-data)
6. [Consider slist instead of list.](#consider-slist-instead-of-list)
7. [Avoid redundant end() and size() in loops.](#avoid-redundant-end-and-size-in-loops)
8. [Iterate containers instead of using operator\[\].](#iterate-containers-instead-of-using-operator)
9. [Learn to use the string class appropriately.](#learn-to-use-the-string-class-appropriately)
10. [Cache list size if you want size() to be O(1).](#cache-list-size-if-you-want-listsize-to-be-o1)
11. [Use empty() instead of size() when possible.](#use-empty-instead-of-size-when-possible)
12. [Know your container efficiencies.](#know-your-container-efficiencies)
13. [Use vector::reserve.](#use-vectorreserve)
14. [Use vector::set_capacity to trim memory usage.](#use-vectorset_capacity-to-trim-memory-usage)
15. [Use swap() instead of a manually implemented version.](#use-swap-instead-of-a-manually-implemented-version)
16. [Consider storing pointers instead of objects.](#consider-storing-pointers-instead-of-objects)
17. [Consider smart pointers instead of raw pointers.](#consider-smart-pointers-instead-of-raw-pointers)
18. [Use iterator pre-increment instead of post-increment.](#use-iterator-pre-increment-instead-of-post-increment)
19. [Make temporary references so the code can be traced/debugged.](#make-temporary-references-so-the-code-can-be-traceddebugged)
20. [Consider bitvector or bitset instead of vector\<bool>.](#consider-bitvector-or-bitset-instead-of-vector)
21. [Vectors can be treated as contiguous memory.](#vectors-can-be-treated-as-contiguous-memory)
22. [Search hash_map\<string> via find_as() instead of find().](#search-hash_map-via-find_as-instead-of-find)
23. [Take advantage of type_traits (e.g. EASTL_DECLARE_TRIVIAL_RELOCATE).](#take-advantage-of-type_traits-eg-eastl_declare_trivial_relocate)
24. [Name containers to track memory usage.](#name-containers-to-track-memory-usage)
25. [Learn the algorithms.](#learn-the-algorithms)
26. [Pass and return containers by reference instead of value.](#pass-and-return-containers-by-reference-instead-of-value)
27. [Consider using reset() for fast container teardown.](#consider-using-reset-for-fast-container-teardown)
28. [Consider using fixed_substring instead of copying strings.](#consider-using-fixed_substring-instead-of-copying-strings)
29. [Consider using vector::push_back(void).](#consider-using-vectorpush_backvoid)
## Detail
### Consider intrusive containers.
Intrusive containers (such as intrusive_list) differ from regular containers (such as list) in that they use the stored objects to manage the linked list instead of using nodes allocated from a memory heap. The result is better usage of memory. Additionally intrusive_list objects can be removed from their list without knowing what list they belong to. To make an intrusive_list of Widgets, you have Widget inherit from intrusive_list_node or simply have mpPrev/mpNext member variables.
To create an intrusive_list container, you can use the following code:
```cpp
class Widget : public intrusive_list_node
{ };
intrusive_list<Widget> widgetList;
widgetList.push_back(someWidget);
```
### Consider fixed-size containers.
Fixed-size containers (such as fixed_list) are variations of regular containers (such as list) in that they allocate from a fixed block of local memory instead of allocating from a generic heap. The result is better usage of memory due to reduced fragmentation, better cache behavior, and faster allocation/deallocation. The presence of fixed-size containers negate the most common complaint that people have about STL: that it fragments the heap or "allocates all over the place."
EASTL fixed containers include:
* fixed_list
* fixed_slist
* fixed_vector
* fixed_string
* fixed_map
* fixed_multimap
* fixed_set
* fixed_multiset
* fixed_hash_map
* fixed_hash_multimap
* fixed_hash_set
* fixed_hash_multiset
To create a fixed_set, you can use the following code:
```cpp
fixed_set<int, 25> intSet; // Create a set capable of holding 25 elements.
intSet.push_back(37);
```
### Consider custom allocators.
While EASTL provides fixed-size containers in order to control container memory usage, EASTL lets you assign a custom allocator to any container. This lets you define your own memory pool. EASTL has a more flexible and powerful mechanism of doing this that standard STL, as EASTL understands object alignment requirements, allows for debug naming, allows for sharing allocators across containers, and allows dynamic allocator assignment.
To create a list container that uses your custom allocator and uses block naming, you can use the following code:
```cpp
list<int> intList(pSomeAllocator, "graphics/intList");
intList.push_back(37);
```
### Consider hash tables instead of maps.
Hash containers (such as hash_map) provide the same interface as associative containers (such as map) but have faster lookup and use less memory. The primary disadvantage relative to associative containers is that hash containers are not sorted.
To make a hash_map (dictionary) of integers to strings, you can use the following code:
```cpp
hash_map<int, const char*> stringTable;
stringTable[37] = "hello";
```
### Consider a vector_map (a.k.a. sorted vector) for unchanging data.
You can improve speed, memory usage, and cache behavior by using a vector_map instead of a map (or vector_set instead of set, etc.). The primary disadvantage of vector_map is that insertions and removal of elements is O(n) instead of O(1). However, if your associative container is not going to be changing much or at all, you can benefit from using a vector_map. Consider calling reserve on the vector_map in order to set the desired capacity up front.
To make a vector_set, you can use the following code:
```cpp
vector_set<int> intSet(16); // Create a vector_set with an initial capacity of 16.
intSet.insert(37);
```
Note that you can use containers other than vector to implement vector_set. Here's how you do it with deque:
```cpp
vector_set<int, less<int>, EASTLAllocatorType, deque<int> > intSet;
intSet.insert(37);
```
### Consider slist instead of list.
An slist is a singly-linked list; it is much like a list except that it can only be traversed in a forward direction and not a backward direction. The benefit is that each node is 4 bytes instead of 8 bytes. This is a small improvement, but if you don't need reverse iteration then it can be an improvement. There's also intrusive_slist as an option.
To make an slist, you can use the following code:
```cpp
slist<int> intSlist;
intSlist.push_front(37);
```
### Avoid redundant end() and size() in loops.
Instead of writing code like this:
```cpp
for(deque<int>::iterator it = d.begin(); it != d.end(); ++it)
...
```
write code like this:
```cpp
for(deque<int>::iterator it = d.begin(), itEnd = d.end(); it != itEnd; ++it)
...
```
The latter avoids a function call and return of an object (which in deque's case happens to be more than just a pointer). The above only works when the container is unchanged or for containers that have a constant end value. But "constant end value" we mean containers which can be modified but end always remains the same.
| Constant begin | Non-constant begin | Constant end | Non-constant end |
|------|------|------|------|
| array<sup>1</sup> | string<br> vector<br> deque<br> intrusive_list<br> intrusive_slist<br> vector_map<br> vector_multimap<br> vector_set<br> vector_multiset<br> bit_vector<br> hash_map<br> hash_multimap<br> hash_set<br> hash_multiset<br> intrusive_hash_map<br> intrusive_hash_multimap<br> intrusive_hash_set<br> intrusive_hash_multiset | array<br> list<br> slist<br> intrusive_list<br> intrusive_slist<br> map<br> multimap<br> set<br> multiset<br> hash_map<sup>2</sup><br> hash_multimap<sup>2</sup><br> hash_set<sup>2</sup><br> hash_multiset<sup>2</sup><br> intrusive_hash_map<br> intrusive_hash_multimap<br> intrusive_hash_set<br> intrusive_hash_multiset | string<br> vector<br> deque<br> vector_map<br> vector_multimap<br> vector_set<br> vector_multiset<br> bit_vector |
* <sup>1</sup> Arrays can be neither resized nor reallocated.
* <sup>2</sup> Constant end if the hashtable can't/won't re-hash. Non-constant if it can re-hash.
### Iterate containers instead of using operator[].
It's faster to iterate random access containers via iterators than via operator[], though operator[] usage may look simpler.
Instead of doing this:
```cpp
for(unsigned i = 0, iEnd = intVector.size(); i != iEnd; ++i)
intVector[i] = 37;
```
you can execute more efficiently by doing this:
```cpp
for(vector<int>::iterator it = intVector.begin(), itEnd = intVector.end(); it != itEnd; ++it)
*it = 37;
```
### Learn to use the string class appropriately.
Oddly enough, the most mis-used STL container is easily the string class. The tales of string abuse could rival the 1001 Arabian Nights. Most of the abuses involve doing things in a harder way than need be. In examining the historical mis-uses of string, it is clear that many of the problems stem from the user thinking in terms of C-style string operations instead of object-oriented strings. This explains why statements such as strlen(s.c_str()) are so common, whereas the user could just use s.length() instead and be both clearer and more efficient.
Here we provide a table of actual collected examples of things done and how they could have been done instead.
| What was written | What could have been written |
|------|------|
| `s = s.Left(i) + '+' + s.Right(s.length() - i - 1);` | `s[i] = '+';` |
| `string s(""); // This is the most commonly found misuse.` | `string s;` |
| `s = "";` | `s.clear();` |
| `s.c_str()[0] = 'u';` | `s[0] = 'u';` |
| `len = strlen(s.c_str());` | `len = s.length();` |
| `s = string("u");` | `s = "u";` |
| `puts(s + string("u"));` | `puts(s + "u");` |
| `string s(" ");`<br> `puts(s.c_str());` | `puts(" ");` |
| `s.sprintf("u");` | s = "u";` |
| `char array[32];`<br> `sprintf(array, "%d", 10);`<br> `s = string(array);` | `s.sprintf("%d", 10);` |
The chances are that if you want to do something with a string, there is a very basic way to do it. You don't want your code to appear in a future version of the above table.
### Cache list size if you want list::size() to be O(1).
EASTL's list, slist, intrusive_list, and intrusive_slist containers have a size() implementation which is O(n). That is, these containers don't keep a count (cache) of the current list size and when you call the size() function they iterate the list. This is by design and the reasoning behind it has been deeply debated and considered (and is discussed in the FAQ and the list header file). In summary, list doesn't cache its size because the only function that would benefit is the size function while many others would be negatively impacted and the memory footprint would be negatively impacted, yet list::size is not a very frequently called function in well-designed code. At the same time, nothing prevents the user from caching the size himself, though admittedly it adds some tedium and risk to the code writing process.
Here's an example of caching the list size manually:
```cpp
list<int> intList;
size_t n = 0;
intList.push_back(37);
++n;
intList.pop_front();
--n;
```
### Use empty() instead of size() when possible.
All conventional containers have both an empty function and a size function. For all containers empty() executes with O(1) (constant time) efficiency. However, this is not so for size(), as some containers need to calculate the size and others need to do pointer subtraction (which may involve integer division) to find the size.
### Know your container efficiencies.
The above two practices lead us to this practice, which is a generalization of the above. We present a table of basic information for the conventional EASTL containers. The values are described at the bottom.
| Container | empty() efficiency | size() efficiency | operator[] efficiency | insert() efficiency | erase() efficiency | find() efficiency | sort efficiency |
|------|------|------|------|------|------|------|------|
| slist | 1 | O(n) | - | O(1) | O(1) | O(n) | O(n+) |
| list | 1 | n | - | 1 | 1 | n | n log(n) |
| intrusive_slist | 1 | n | - | 1 | 1 | 1 | n+ |
| intrusive_list | 1 | n | - | 1 | 1 | 1 | n log(n) |
| array | 1 | 1 | 1 | - | - | n | n log(n) |
| vector | 1 | 1<sup>a</sup> | 1 | 1 at end, else n | 1 at end, else n | n | n log(n) |
| vector_set | 1 | 1<sup>a</sup> | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 |
| vector_multiset | 1 | 1<sup>a</sup> | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 |
| vector_map | 1 | 1<sup>a</sup> | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 |
| vector_multimap | 1 | 1<sup>a</sup> | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 |
| deque | 1 | 1<sup>a</sup> | 1 | 1 at begin or end, else n / 2 | 1 at begin or end, else n / 2 | n | n log(n) |
| bit_vector | 1 | 1<sup>a</sup> | 1 | 1 at end, else n | 1 at end, else n | n | n log(n) |
| string, cow_string | 1 | 1<sup>a</sup> | 1 | 1 at end, else n | 1 at end, else n | n | n log(n) |
| set | 1 | 1 | - | log(n) | log(n) | log(n) | 1 |
| multiset | 1 | 1 | - | log(n) | log(n) | log(n) | 1 |
| map | 1 | 1 | log(n) | log(n) | log(n) | log(n) | 1 |
| multimap | 1 | 1 | - | log(n) | log(n) | log(n) | 1 |
| hash_set | 1 | 1 | - | 1 | 1 | 1 | - |
| hash_multiset | 1 | 1 | - | 1 | 1 | 1 | - |
| hash_map | 1 | 1 | - | 1 | 1 | 1 | - |
| hash_multimap | 1 | 1 | - | 1 | 1 | 1 | - |
| intrusive_hash_set | 1 | 1 | - | 1 | 1 | 1 | - |
| intrusive_hash_multiset | 1 | 1 | - | 1 | 1 | 1 | - |
| intrusive_hash_map | 1 | 1 | - | 1 | 1 | 1 | - |
| intrusive_hash_multimap | 1 | 1 | - | 1 | 1 | 1 | - |
Notes:
* \- means that the operation does not exist.
* 1 means amortized constant time. Also known as O(1)
* n means time proportional to the container size. Also known as O(n)
* log(n) means time proportional to the natural logarithm of the container size. Also known as O(log(n))
* n log(n) means time proportional to log(n) times the size of the container. Also known as O(n log(n))
* n+ means that the time is at least n, and possibly higher.
* Inserting at the end of a vector may cause the vector to be resized; resizing a vector is O(n). However, the amortized time complexity for vector insertions at the end is constant.
* Sort assumes the usage of the best possible sort for a large container of random data. Some sort algorithms (e.g. quick_sort) require random access iterators and so the sorting of some containers requires a different sort algorithm. We do not include bucket or radix sorts, as they are always O(n).
* <sup>a</sup> vector, deque, string size is O(1) but involves pointer subtraction and thus integer division and so is not as efficient as containers that store the size directly.
### Use vector::reserve.
You can prevent vectors (and strings) from reallocating as you add items by specifying up front how many items you will be requiring. You can do this in the constructor or by calling the reserve function at any time. The capacity function returns the amount of space which is currently reserved.
Here's how you could specify reserved capacity in a vector:
```cpp
vector<Widget> v(37); // Reserve space to hold up to 37 items.
or
vector<Widget> v; // This empty construction causes to memory to be allocated or reserved.
v.reserve(37);
```
The EASTL vector (and string) implementation looks like this:
```cpp
template <typename T>
class vector {
T* mpBegin; // Beginning of used element memory.
T* mpEnd; // End of used element memory.
T* mpCapacity; // End of storage capacity. Is >= mpEnd
}
```
Another approach to being efficient with vector memory usage is to use fixed_vector.
### Use vector::set_capacity to trim memory usage.
A commonly asked question about vectors and strings is, "How do I reduce the capacity of a vector?" The conventional solution for std STL is to use the somewhat non-obvious trick of using vector<Widget>(v).swap(v). EASTL provides the same functionality via a member function called set_capacity() which is present in both the vector and string classes.
An example of reducing a vector is the following:
```cpp
vector<Widget> v;
...
v.set_capacity();
```
An example of resizing to zero and completely freeing the memory of a vector is the following:
```cpp
vector<Widget> v;
...
v.set_capacity(0);
```
### Use swap() instead of a manually implemented version.
The generic swap algorithm provides a basic version for any kind of object. However, each EASTL container provides a specialization of swap which is optimized for that container. For example, the list container implements swap by simply swapping the internal member pointers and not by moving individual elements.
### Consider storing pointers instead of objects.
There are times when storing pointers to objects is more efficient or useful than storing objects directly in containers. It can be more efficient to store pointers when the objects are big and the container may need to construct, copy, and destruct objects during sorting or resizing. Moving pointers is usually faster than moving objects. It can be useful to store pointers instead of objects when somebody else owns the objects or the objects are in another container. It might be useful for a Widget to be in a list and in a hash table at the same time.
### Consider smart pointers instead of raw pointers.
If you take the above recommendation and store objects as pointers instead of as objects, you may want to consider storing them as smart pointers instead of as regular pointers. This is particularly useful for when you want to delete the object when it is removed from the container. Smart pointers will automatically delete the pointed-to object when the smart pointer is destroyed. Otherwise, you will have to be careful about how you work with the list so that you don't generate memory leaks. Smart pointers implement a shared reference count on the stored pointer, as so any operation you do on a smart pointer container will do the right thing. Any pointer can be stored in a smart pointer, and custom new/delete mechanisms can work with smart pointers. The primary smart pointer is shared_ptr.
Here is an example of creating and using a shared_ptr:
```cpp
typedef shared_ptr<Widget> WPtr;
list<WPtr> wList;
wList.push_back(WPtr(new Widget)); // The user may have operator new/delete overrides.
wList.pop_back(); // Implicitly deletes the Widget.
```
Here is an example of creating and using a shared_ptr that uses a custom allocation and deallocation mechanism:
```cpp
typedef shared_ptr<Widget, EASTLAllocatorType, WidgetDelete> WPtr; // WidgetDelete is a custom destroyer.
list<WPtr> wList;
wList.push_back(WPtr(WidgetCreate(Widget))); // WidgetCreate is a custom allocator.
wList.pop_back(); // Implicitly calls WidgetDelete.
```
### Use iterator pre-increment instead of post-increment.
Pre-increment (e.g. ++x) of iterators is better than post-increment (x++) when the latter is not specifically needed. It is common to find code that uses post-incrementing when it could instead use pre-incrementing; presumably this is due to post-increment looking a little better visually. The problem is that the latter constructs a temporary object before doing the increment. With built-in types such as pointers and integers, the compiler will recognize that the object is a trivial built-in type and that the temporary is not needed, but the compiler cannot do this for other types, even if the compiler sees that the temporary is not used; this is because the constructor may have important side effects and the compiler would be broken if it didn't construct the temporary object.
EASTL iterators are usually not trivial types and so it's best not to hope the compiler will do the best thing. Thus you should always play it safe an use pre-increment of iterators whenever post-increment is not required.
Here is an example of using iterator pre-increment; for loops like this should always use pre-increment:
```cpp
for(set<int>::iterator it(intSet.begin()), itEnd(intSet.end()); it != itEnd; ++it)
*it = 37;
```
### Make temporary references so the code can be traced/debugged.
Users want to be able to inspect or modify variables which are referenced by iterators. While EASTL containers and iterators are designed to make this easier than other STL implementations, it makes things very easy if the code explicitly declares a reference to the iterated element. In addition to making the variable easier to debug, it also makes code easier to read and makes the debug (and possibly release) version of the application run more efficiently.
Instead of doing this:
```cpp
for(list<Widget>::iterator it = wl.begin(), itEnd = wl.end(); it != itEnd; ++it) {
(*it).x = 37;
(*it).y = 38;
(*it).z = 39;
}
```
Consider doing this:
```cpp
for(list<Widget>::iterator it = wl.begin(), itEnd = wl.end(); it != itEnd; ++it) {
Widget& w = *it; // The user can easily inspect or modify w here.
w.x = 37;
w.y = 38;
w.z = 39;
}
```
### Consider bitvector or bitset instead of vector<bool>.
In EASTL, a vector of bool is exactly that. It intentionally does not attempt to make a specialization which implements a packed bit array. The bitvector class is specifically designed for this purpose. There are arguments either way, but if vector<bool> were allowed to be something other than an array of bool, it would go against user expectations and prevent users from making a true array of bool. There's a mechanism for specifically getting the bit packing, and it is bitvector.
Additionally there is bitset, which is not a conventional iterateable container but instead acts like bit flags. bitset may better suit your needs than bitvector if you need to do flag/bit operations instead of array operations. bitset does have an operator[], though.
### Vectors can be treated as contiguous memory.
EASTL vectors (and strings) guarantee that elements are present in a linear contiguous array. This means that you can use a vector as you would a C-style array by using the vector data() member function or by using &v[0].
To use a vector as a pointer to an array, you can use the following code:
```cpp
struct Widget {
uint32_t x;
uint32_t y;
};
vector<Widget> v;
quick_sort((uint64_t*)v.data(), (uint64_t*)(v.data() + v.size()));
```
### Search hash_map<string> via find_as() instead of find().
EASTL hash tables offer a bonus function called find_as when lets you search a hash table by something other than the container type. This is particularly useful for hash tables of string objects that you want to search for by string literals (e.g. "hello") or char pointers. If you search for a string via the find function, your string literal will necessarily be converted to a temporary string object, which is inefficient.
To use find_as, you can use the following code:
```cpp
hash_map<string, int> hashMap;
hash_map<string, int>::iterator it = hashMap.find_as("hello"); // Using default hash and compare.
```
### Take advantage of type_traits (e.g. EASTL_DECLARE_TRIVIAL_RELOCATE).
EASTL includes a fairly serious type traits library that is on par with the one found in Boost but offers some additional performance-enhancing help as well. The type_traits library provides information about class *types*, as opposed to class instances. For example, the is_integral type trait tells if a type is one of int, short, long, char, uint64_t, etc.
There are three primary uses of type traits:
* Allowing for optimized operations on some data types.
* Allowing for different logic pathways based on data types.
* Allowing for compile-type assertions about data type expectations.
Most of the type traits are automatically detected and implemented by the compiler. However, EASTL allows for the user to explicitly give the compiler hints about type traits that the compiler cannot know, via the EASTL_DECLARE declarations. If the user has a class that is relocatable (i.e. can safely use memcpy to copy values), the user can use the EASTL_DECLARE_TRIVIAL_RELOCATE declaration to tell the compiler that the class can be copied via memcpy. This will automatically significantly speed up some containers and algorithms that use that class.
Here is an example of using type traits to tell if a value is a floating point value or not:
```cpp
template <typename T>
DoSomething(T t) {
assert(is_floating_point<T>::value);
}
```
Here is an example of declaring a class as relocatable and using it in a vector.
```cpp
EASTL_DECLARE_TRIVIAL_RELOCATE(Widget); // Usually you put this at the Widget class declaration.
vector<Widget> wVector;
wVector.erase(wVector.begin()); // This operation will be optimized via using memcpy.
```
The following is a full list of the currently recognized type traits. Most of these are implemented as of this writing, but if there is one that is missing, feel free to contact the maintainer of this library and request that it be completed.
* is_void
* is_integral
* is_floating_point
* is_arithmetic
* is_fundamental
* is_const
* is_volatile
* is_abstract
* is_signed
* is_unsigned
* is_array
* is_pointer
* is_reference
* is_member_object_pointer
* is_member_function_pointer
* is_member_pointer
* is_enum
* is_union
* is_class
* is_polymorphic
* is_function
* is_object
* is_scalar
* is_compound
* is_same
* is_convertible
* is_base_of
* is_empty
* is_pod
* is_aligned
* has_trivial_constructor
* has_trivial_copy
* has_trivial_assign
* has_trivial_destructor
* has_trivial_relocate1
* has_nothrow_constructor
* has_nothrow_copy
* has_nothrow_assign
* has_virtual_destructor
* alignment_of
* rank
* extent
*
<sup>1</sup> has_trivial_relocate is not found in Boost nor the C++ standard update proposal. However, it is very useful in allowing for the generation of optimized object moving operations. It is similar to the is_pod type trait, but goes further and allows non-pod classes to be categorized as relocatable. Such categorization is something that no compiler can do, as only the user can know if it is such. Thus EASTL_DECLARE_TRIVIAL_RELOCATE is provided to allow the user to give the compiler a hint.
### Name containers to track memory usage.
All EASTL containers which allocate memory have a built-in function called set_name and have a constructor argument that lets you specify the container name. This name is used in memory tracking and allows for the categorization and measurement of memory usage. You merely need to supply a name for your containers to use and it does the rest.
Here is an example of creating a list and naming it "collision list":
`list<CollisionData> collisionList(allocator("collision list"));`
or
```cpp
list<CollisionData> collisionList;
collisionList.get_allocator().set_name("collision list");
```
Note that EASTL containers do not copy the name contents but merely copy the name pointer. This is done for simplicity and efficiency. A user can get around this limitation by creating a persistently present string table. Additionally, the user can get around this by declaring static but non-const strings and modifying them at runtime.
### Learn the algorithms.
EASTL algorithms provide a variety of optimized implementations of fundamental algorithms. Many of the EASTL algorithms are the same as the STL algorithm set, though EASTL adds additional algorithms and additional optimizations not found in STL implementations such as Microsoft's. The copy algorithm, for example, will memcpy data types that have the has_trivial_relocate type trait instead of doing an element-by-element copy.
The classifications we use here are not exactly the same as found in the C++ standard; they have been modified to be a little more intuitive. Not all the functions listed here may be yet available in EASTL as you read this. If you want some function then send a request to the maintainer. Detailed documentation for each algorithm is found in algorithm.h or the otherwise corresponding header file for the algorithm.
**Search**
* find, find_if
* find_end
* find_first_of
* adjacent_find
* binary_search
* search, search_n
* lower_bound
* upper_bound
* equal_range
**Sort**
* is_sorted
* quick_sort
* insertion_sort
* shell_sort
* heap_sort
* merge_sort, merge_sort_buffer
* merge
* inplace_merge
* partial_sort
* stable_sort
* partial_sort_copy
* <other sort functions found in the EASTL bonus directories>
**Modifying**
* fill, fill_n
* generate, generate_n
* random_shuffle
* swap
* iter_swap
* swap_ranges
* remove, remove_if
* remove_copy, remove_copy_if
* replace, replace_if
* replace_copy, replace_copy_if
* reverse
* reverse_copy
* rotate
* rotate_copy
* partition
* stable_partition
* transform
* next_permutation
* prev_permutation
* unique
* unique_copy
**Non-Modifying**
* for_each
* copy
* copy_backward
* count, count_if
* equal
* mismatch
* min
* max
* min_element
* max_element
* lexicographical_compare
* nth_element
**Heap**
* is_heap
* make_heap
* push_heap
* pop_heap
* change_heap
* sort_heap
* remove_heap
**Set**
* includes
* set_difference
* set_symmetric_difference
* set_intersection
* set_union
### Pass and return containers by reference instead of value.
If you aren't paying attention you might accidentally write code like this:
```cpp
void DoSomething(list<Widget> widgetList) {
...
}
```
The problem with the above is that widgetList is passed by value and not by reference. Thus the a copy of the container is made and passed instead of a reference of the container being passed. This may seem obvious to some but this happens periodically and the compiler gives no warning and the code will often execute properly, but inefficiently. Of course there are some occasions where you really do want to pass values instead of references.
### Consider using reset() for fast container teardown.
EASTL containers have a reset function which unilaterally resets the container to a newly constructed state. The contents of the container are forgotten; no destructors are called and no memory is freed. This is a risky but power function for the purpose of implementing very fast temporary containers. There are numerous cases in high performance programming when you want to create a temporary container out of a scratch buffer area, use the container, and then just "vaporize" it, as it would be waste of time to go through the trouble of clearing the container and destroying and freeing the objects. Such functionality is often used with hash tables or maps and with a stack allocator (a.k.a. linear allocator).
Here's an example of usage of the reset function and a PPMalloc-like StackAllocator:
```cpp
pStackAllocator->push_bookmark();
hash_set<Widget, less<Widget>, StackAllocator> wSet(pStackAllocator);
<use wSet>
wSet.reset();
pStackAllocator->pop_bookmark();
```
### Consider using fixed_substring instead of copying strings.
EASTL provides a fixed_substring class which uses a reference to a character segment instead of allocating its own string memory. This can be a more efficient way to work with strings under some circumstances.
Here's an example of usage of fixed_substring:
```cpp
basic_string<char> str("hello world");
fixed_substring<char> sub(str, 6, 5); // sub == "world"
fixed_substring can refer to any character array and not just one that derives from a string object.
```
### Consider using vector::push_back(void).
EASTL provides an alternative way to insert elements into containers that avoids copy construction and/or the creation of temporaries. Consider the following code:
```cpp
vector<Widget> widgetArray;
widgetArray.push_back(Widget());
```
The standard vector push_back function requires you to supply an object to copy from. This incurs the cost of the creation of a temporary and for some types of classes or situations this cost may be undesirable. It additionally requires that your contained class support copy-construction whereas you may not be able to support copy construction. As an alternative, EASTL provides a push_back(void) function which requires nothing to copy from but instead constructs the object in place in the container. So you can do this:
```cpp
vector<Widget> widgetArray;
widgetArray.push_back();
widgetArray.back().x = 0; // Example of how to reference the new object.
```
Other containers with such copy-less functions include:
```cpp
vector::push_back()
deque::push_back()
deque::push_front()
list::push_back()
list::push_front()
slist::push_front()
map::insert(const key_type& key)
multimap::insert(const key_type& key)
hash_map::insert(const key_type& key)
hash_multimap::insert(const key_type& key)
```
Note that the map functions above allow you to insert a default value specified by key alone and not a value_type like with the other map insert functions.
----------------------------------------------
End of document
+416
View File
@@ -0,0 +1,416 @@
## Introduction to tuple_vector
`tuple_vector` is a data container that is designed to abstract and simplify
the handling of a "structure of arrays" layout of data in memory. In
particular, it mimics the interface of `vector`, including functionality to do
inserts, erases, push_backs, and random-access. It also provides a
`RandomAccessIterator` and corresponding functionality, making it compatible
with most STL (and STL-esque) algorithms such as ranged-for loops, `find_if`,
`remove_if`, or `sort`.
When used or applied properly, this container can improve performance of
some algorithms through cache-coherent data accesses or allowing for
sensible SIMD programming, while keeping the structure of a single
container, to permit a developer to continue to use existing algorithms in
STL and the like.
## Review of "Structure of arrays" data layouts
When trying to improve the performance of some code, it can sometimes be
desirable to transform how some data is stored in memory to be laid out not as
an "array of structures", but as a "structure of arrays". That is, instead of
storing a series of objects as a single contiguous chunk of memory, one or
more data members are instead stored as separate chunks of memory that are
handled and accessed in parallel to each other.
This can be beneficial in two primary respects:
1) To improve the cache coherency of the data accesses, e.g. by utilizing more
data that is loaded per cache line loaded from memory, and thereby reducing
the amount of time waiting on memory accesses from off-CPU memory.
This presentation from Mike Acton touches on this, among other things:
https://www.youtube.com/watch?v=rX0ItVEVjHc
2) To allow the data to be more easily loaded and utilized by SIMD kernels,
by being able to load memory directly into a SIMD register.
This is touched on in this presentation from Andreas Fredriksson for writing
code with SIMD intrinsics:
http://www.gdcvault.com/play/1022249/SIMD-at-Insomniac-Games-How
...and as well in this guide for writing performant ISPC kernels:
https://ispc.github.io/perfguide.html
## How TupleVecImpl works
`tuple_vector` inherits from `TupleVecImpl`, which
provides the bulk of the functionality for those data containers. It manages
the memory allocated, marshals data members to each array of memory, generates
the necessary iterators, and so on.
When a `tuple_vector` is declared, it is alongside a list of types, or "tuple
elements", indicating what data to store in the container, similar to how `tuple`
operates. `TupleVecImpl` uses this list of tuple elements to then inherit from a series of
`TupleVecLeaf` structures, which each have their own pointer to an array of their
corresponding type in memory. When dereferencing the container, either to fetch a
tuple of references or just fetching pointers to the memory, it is these pointers
that are utilized or fetched.
While each `TupleVecLeaf` contains a pointer to its own block of memory, they
are not individual memory allocations. When `TupleVecImpl` needs to grow its
capacity, it calculates the total size needed for a single allocation, taking
into account the number of objects for the container, the size of each tuple
element's type, and the alignment requirements for each type. Pointers into the
allocation for each tuple element are also determined at the same time, which
are passed to each `TupleVecLeaf`. From there, many of the interactions with
`TupleVecImpl`, to modify or access members of the container, then reference
each `TupleVecLeaf`'s data pointer in series, using parameter packs to repeat
each operation for each parent `TupleVecLeaf`.
## How tuple_vector's iterator works
`TupleVecImpl` provides a definition to an iterator type, `TupleVecIter`.
As mentioned above, `TupleVecIter` provides all of the functionality to operate
as a `RandomAccessIterator`. When it is dereferenced, it provides a tuple of
references, similar to `at()` or `operator[]` on `TupleVecImpl`, as opposed to
a reference of some other type. As well, a customization of `move_iterator` for
`TupleVecIter` is provided, which will return a tuple of rvalue-references.
The way that `TupleVecIter` operates internally is to track an index into the
container, as well as a copy of all of the `TupleVecImpl`'s `TupleVecLeaf`
pointers at the time of the iterator's construction. As a result, modifying the
iterator involves just changing the index, and dereferencing the iterator into
the tuple of references involves dereferencing each pointer with an offset
specified by that index.
Of the various ways of handling the multitude of references, this tended to
provide the best code-generation. For example, having a tuple of pointers that
are collectively modified with each iterator modification resulted in the compiler
not being able to accurately determine which pointers were relevant to the final
output of some function, creating many redundant operations. Similarly, having
the iterator refer to the source `TupleVecImpl` for the series of pointers
often resulted in extra, unnecessary, data hops to the `TupleVecImpl` to repeatedly
fetch data that was not practically mutable, but theoretically mutable. While this
solution is the heaviest in terms of storage, the resulted assembly tends to be
competitive with traditional structure-of-arrays setups.
## How to work with tuple_vector, and where to use it
Put simply, `tuple_vector` can be used as a replacement for `vector`. For example,
instead of declaring a structure and vector as:
```
struct Entity
{
bool active;
float lifetime;
Vec3 position;
}
vector<Entity> entityVec;
```
...the `tuple_vector` equivalent of this can be defined as:
```
tuple_vector<bool, float, Vec3> entityVec;
```
In terms of how `tuple_vector` is modified and accessed, it has a similar
featureset as `vector`, except where `vector` would accept or return a single
value, it instead accepts or returns a tuple of values or unstructured series
of equivalent arguments.
For example, the following functions can be used to access the data, either by
fetching a tuple of references to a series of specific values, or the data
pointers to the tuple elements:
```
tuple<bool&, float&, Vec3&> operator[](size_type)
tuple<bool&, float&, Vec3&> at(size_type)
tuple<bool&, float&, Vec3&> iterator::operator*()
tuple<bool&&, float&&, Vec3&&> move_iterator::operator*()
tuple<bool*, float*, Vec3*> data()
// extract the Ith tuple element pointer from the tuple_vector
template<size_type I>
T* get<I>()
// e.g. bool* get<0>(), float* get<1>(), and Vec3* get<2>()
// extract the tuple element pointer of type T from the tuple_vector
// note that this function can only be used if there is one instance
// of type T in the tuple_vector's elements
template<typename T>
T* get<T>()
// e.g. bool* get<bool>(), float* get<float>(), and Vec3* get<Vec3>()
```
And `push_back(...)` has the following overloads, accepting either values or tuples as needed.
```
tuple<bool&, float&, Vec3&> push_back()
push_back(const bool&, const float&, const Vec3&)
push_back(tuple<const bool&, const float&,const Vec3&>)
push_back(bool&&, float&&, Vec3&&)
push_back(tuple<bool&&, float&&, Vec3&&>)
```
...and so on, and so forth, for others like the constructor, `insert(...)`,
`emplace(...)`, `emplace_back(...)`, `assign(...)`, and `resize(...)`.
As well, note that the tuple types that are accepted or returned for
`tuple_vector<Ts...>` have typedefs available in the case of not wanting to use
automatic type deduction:
```
typedef eastl::tuple<Ts...> value_tuple;
typedef eastl::tuple<Ts&...> reference_tuple;
typedef eastl::tuple<const Ts&...> const_reference_tuple;
typedef eastl::tuple<Ts*...> ptr_tuple;
typedef eastl::tuple<const Ts*...> const_ptr_tuple;
typedef eastl::tuple<Ts&&...> rvalue_tuple;
```
With this, and the fact that the iterator type satisfies
the `RandomAccessIterator` requirements, it is possible to use `tuple_vector` in
most ways and manners that `vector` was previously used, with few structural
differences.
However, even if not using it strictly as a replacement for `vector`, it is
still useful as a tool for simplifying management of a traditional structure of
arrays. That is, it is possible to use `tuple_vector` to just perform a single
large memory allocation instead of a series of smaller memory allocations,
by sizing the `tuple_vector` as needed, fetching the necessary pointers with
`data()` or `get<...>()`, and carrying on normally.
One example where this can be utilized is with ISPC integration. Given the
following ISPC function definition:
export void simple(uniform float vin[], uniform float vfactors[], uniform float vout[], uniform int size);
...which generates the following function prototype for C/C++ usage:
extern void simple(float* vin, float* vfactors, float* vout, int32_t size);
...this can be utilized with some raw float arrays:
```
float* vin = new float[NumElements];
float* vfactors = new float[NumElements];
float* vout = new float[NumElements];
// Initialize input buffer
for (int i = 0; i < NumElements; ++i)
{
vin[i] = (float)i;
vfactors[i] = (float)i / 2.0f;
}
// Call simple() function from simple.ispc file
simple(vin, vfactors, vout, NumElements);
delete vin;
delete vfactors;
delete vout;
```
or, with `tuple_vector`:
```
tuple_vector<float, float, float> simpleData(NumElements);
float* vin = simpleData.get<0>();
float* vfactors = simpleData.get<1>();
float* vout = simpleData.get<2>();
// Initialize input buffer
for (int i = 0; i < NumElements; ++i)
{
vin[i] = (float)i;
vfactors[i] = (float)i / 2.0f;
}
// Call simple() function from simple.ispc file
simple(vin, vfactors, vout, NumElements);
```
`simpleData` here only has a single memory allocation during its construction,
instead of the three in the first example, and also automatically releases the
memory when it falls out of scope.
It is possible to also skip a memory allocation entirely, in some circumstances.
EASTL provides "fixed" counterparts of many data containers which allows for a
data container to have an inlined buffer of memory. For example,
`eastl::vector<typename T>` has the following counterpart:
eastl::fixed_vector<typename T, size_type nodeCount, bool enableOverflow = true>
This buffer allows for enough space to hold a `nodeCount` number of `T` objects,
skipping any memory allocation at all, until the requested size becomes
greater than `nodeCount` - assuming `enableOverflow` is True.
There is a similar counterpart to `eastl::tuple_vector<typename... Ts>` available as well:
eastl::fixed_tuple_vector<size_type nodeCount, bool enableOverflow, typename... Ts>
This does the similar legwork in creating an inlined buffer, and all of the
functionality of `tuple_vector` otherwise is supported. Note the slight
difference in declaration, though: `nodeCount` and `enableOverflow` are defined
first, and `enableOverflow` is not a default parameter. This change arises out
of restrictions surrounding variadic templates, in that they must be declared
last, and cannot be mixed with default template parameters.
Lastly, `eastl::vector` and other EASTL data containers support custom Memory Allocator
types, through their template parameters. For example, `eastl::vector`'s full declaration
is actually:
eastl::vector<typename T, typename AllocatorType = EASTLAllocatorType>
However, because such a default template parameter cannot be used with
variadic templates, a separate type for `tuple_vector` is required for such a
definition:
eastl::tuple_vector_alloc<typename AllocatorType, typename... Ts>
Note that `tuple_vector` uses EASTLAllocatorType as the allocator.
## Performance comparisons/discussion
A small benchmark suite for `tuple_vector` is included when running the
EASTLBenchmarks project. It provides the following output on a Core i7 3770k
(Skylake) at 3.5GHz, with DDR3-1600 memory.
The `tuple_vector` benchmark cases compare total execution time of similar
algorithms run against `eastl::tuple_vector` and `std::vector`, such as
erasing or inserting elements, iterating through the array to find a specific
element, sum all of the elements together via operator[] access, or just
running `eastl::sort` on the data containers. More information about the
EASTLBenchmarks suite can be found in EASTL/doc/EASTL Benchmarks.html
Benchmark | STD execution time | EASTL execution time | Ratio
--------- | -------- | ---------- | -----
`tuple_vector<AutoRefCount>/erase ` | 1.7 ms | 1.7 ms | 1.00
`tuple_vector<MovableType>/erase ` | 104.6 ms | 106.3 ms | 0.98
`tuple_vector<MovableType>/reallocate ` | 1.3 ms | 1.7 ms | 0.77 -
| | |
`tuple_vector<uint64>/erase ` | 3.4 ms | 3.5 ms | 0.98
`tuple_vector<uint64>/insert ` | 3.4 ms | 3.4 ms | 0.99
`tuple_vector<uint64>/iteration ` | 56.3 us | 81.4 us | 0.69 -
`tuple_vector<uint64>/operator[] ` | 67.4 us | 61.8 us | 1.09
`tuple_vector<uint64>/push_back ` | 1.3 ms | 818.3 us | 1.53 +
`tuple_vector<uint64>/sort ` | 5.8 ms | 7.3 ms | 0.80
| | |
`tuple_vector<uint64,Padding>/erase ` | 34.7 ms | 32.9 ms | 1.05
`tuple_vector<uint64,Padding>/insert ` | 41.0 ms | 32.6 ms | 1.26
`tuple_vector<uint64,Padding>/iteration ` | 247.1 us | 80.5 us | 3.07 +
`tuple_vector<uint64,Padding>/operator[]` | 695.7 us | 81.1 us | 8.58 +
`tuple_vector<uint64,Padding>/push_back ` | 10.0 ms | 6.0 ms | 1.67 +
`tuple_vector<uint64,Padding>/sort ` | 8.2 ms | 10.1 ms | 0.81
| | |
`vector<AutoRefCount>/erase ` | 1.3 ms | 1.2 ms | 1.05
`vector<MovableType>/erase ` | 104.4 ms | 109.4 ms | 0.95
`vector<MovableType>/reallocate ` | 1.5 ms | 1.5 ms | 0.95
| | |
`vector<uint64>/erase ` | 4.3 ms | 3.6 ms | 1.20
`vector<uint64>/insert ` | 4.8 ms | 4.8 ms | 1.01
`vector<uint64>/iteration ` | 71.5 us | 77.3 us | 0.92
`vector<uint64>/operator[] ` | 90.7 us | 87.2 us | 1.04
`vector<uint64>/push_back ` | 1.6 ms | 1.2 ms | 1.38 +
`vector<uint64>/sort ` | 7.7 ms | 8.2 ms | 0.93
First off, `tuple_vector<uint64>`'s performance versus `std::vector<uint64>` is
comparable, as expected, as the `tuple_vector`'s management for one type
becomes very similar to just a regular vector. The major notable exception is
the iteration case, which runs `eastl::find_if`. This
performance differences is a consequence of the iterator design, and how
it works with indices, not a direct pointer, so the code generation suffers slightly
in this compute-bound scenario. This is worth noting as a demonstration of a
case where falling back to pointer-based iteration by fetching the `begin` and
`end` pointers of that tuple element may be preferable, instead of using the
iterator constructs.
The set of `tuple_vector<uint64,Padding>` tests are more interesting.
This is a comparison between a single `std::vector` with a
structure containing a `uint64` and 56 bytes of padding, and a `tuple_vector` with
two elements: one for `uint64` and one for 56 bytes of padding. The erase,
insert, push_back, and sort cases all perform at a similar relative rate as
they did in the `tuple_vector<uint64>` tests - demonstrating that operations
that have to touch all of elements do not have a significant change in
performance.
However, iteration and operator[] are very different, because
those only access the `uint64` member of both `vector` and `tuple_vector` to run
some operation. The iteration test now runs 3x faster whereas before it ran
0.7x as fast, and operator[] runs 8.5x faster, instead of 1.1x. This
demonstrates some of the utility of `tuple_vector`, in that these algorithms end
up being limited by the CPU's compute capabilities, as opposed to being
limited by how fast they can load memory in from DRAM.
In a series of other tests, generally speaking, `tuple_vector` tends to perform
on par with manual management of multiple arrays in many algorithms and
operations, often even generating the same code. It should be noted that
significant degrees of inlining and optimization are required to get the most out
of `tuple_vector`. Compared to accessing a series of arrays or vectors,
`tuple_vector` does perform a multitude of extra trivial function calls internally
in order to manage the various elements, or interact with `eastl::tuple` through
its interface, so running in debug configurations can run significantly slower
in some cases, e.g. sometimes running at 0.2x the speed compared to vector.
## The problem of referencing tuple elements
This will be experienced shortly after using `tuple_vector` in most capacities,
but it should be noted that the most significant drawback is that there is no
way to **symbolically** reference each tuple element of the `tuple_vector` - much
in the same way as `tuple`. For example, if translating a struct such as...
```
struct Entity
{
float x, y, z;
float lifetime;
};
```
...to `tuple_vector`, it will exist as:
```
tuple_vector<float, float, float, float> entityVec;
```
...and can only be accessed in a manner like `entityVec.get<3>()` to refer to
the `lifetime` member. With existing tools, the only good alternatives are to
encapsulate each float as a separate struct to give it unique typenames...
```
struct entityX { float val; };
struct entityY { float val; };
struct entityZ { float val; };
struct entityLifetime { float val; };
tuple_vector<entityX, entityY, entityZ, entityLifetime> entityVec;
```
...and then access each tuple element by typename like
`entityVec.get<entityLifetime>()`; or, creating an enumerated value to replace
the indices...
```
enum EntityTypeEnum
{
entityX = 0,
entityY = 1,
entityZ = 2,
entityLifetime = 3
};
tuple_vector<float, float, float, float> entityVec;
```
...and then access each tuple element by the enumerated value:
`entityVec.get<entityLifetime>()`.
Either way, there is a fairly significant maintenance and readability issue
around this. This is arguably more severe than with `tuple` on its own
because that is generally not intended for structures with long lifetime.
Ideally, if the language could be mutated to accommodate such a thing, it would
be good to have some combination of typenames and symbolic names in the
declaration, e.g. something like
```
tuple_vector<float x, float y, float z, float lifetime> entityVec;
```
and be able to reference the tuple elements not just by typename or index, but
through their corresponding symbol, like `entityVec.get<lifetime>()`. Or, it may
be interesting if the necessary `get` functions could be even automatically
generated through a reflection system, e.g. `entityVec.get_lifetime()`.
All of this remains a pipe dream for now.
+93
View File
@@ -0,0 +1,93 @@
## Using EASTL in your own projects
This page describes the steps needed to use EASTL in your own projects
## Setting up your project
### Using CMake
Add to your CMakeLists.txt:
```cmake
set(EASTL_ROOT_DIR C:/EASTL)
include_directories (${EASTL_ROOT_DIR}/include)
include_directories (${EASTL_ROOT_DIR}/test/packages/EAAssert/include)
include_directories (${EASTL_ROOT_DIR}/test/packages/EABase/include/Common)
include_directories (${EASTL_ROOT_DIR}/test/packages/EAMain/include)
include_directories (${EASTL_ROOT_DIR}/test/packages/EAStdC/include)
include_directories (${EASTL_ROOT_DIR}/test/packages/EATest/include)
include_directories (${EASTL_ROOT_DIR}/test/packages/EAThread/include)
set(EASTL_LIBRARY debug ${EASTL_ROOT_DIR}/build/Debug/EASTL.lib optimized ${EASTL_ROOT_DIR}/build/Release/EASTL.lib)
add_custom_target(NatVis SOURCES ${EASTL_ROOT_DIR}/doc/EASTL.natvis)
```
And then add the library into the linker
```
target_link_libraries(... ${EASTL_LIBRARY})
```
### Using Visual Studio
Using Visual Studio projecs directly you will need do the following steps:
- Add the include paths
- Add the library path
- Add the library dependency
- Add natvis (optional)
> Note that in the examples below ${EASTL_ROOT_DIR} is the folder in which you stored EASTL. You could create an environment variable for this.
#### Add the include paths
Add the following paths to your C/C++ -> General -> Additional include directories:
```
${EASTL_ROOT_DIR}/include
${EASTL_ROOT_DIR}/test/packages/EAAssert/include
${EASTL_ROOT_DIR}/test/packages/EABase/include/Common
${EASTL_ROOT_DIR}/test/packages/EAMain/include)
${EASTL_ROOT_DIR}/test/packages/EAStdC/include)
${EASTL_ROOT_DIR}/test/packages/EATest/include)
${EASTL_ROOT_DIR}/test/packages/EAThread/include)
```
#### Add the library path
Add the following library path to your Linker -> General -> Additional Library Directories:
```
${EASTL_ROOT_DIR}/build/$(Configuration)
```
#### Add the library dependency
Either add the following library to your Linker -> Input -> Additional Dependencies
```
EASTL.lib
```
Or in code use the following:
```
#pragma comment(lib, "EASTL.lib")
```
#### Add natvis (optional)
> Adding the natvis file to your project allows the debugger to use custom visualizers for the eastl data types. This greatly enhances the debugging experience.
Add the natvis file anywhere in your solution:
```
Right-click your project: Add -> Existing item and then add the following file:
${EASTL_ROOT_DIR}/doc/EASTL.natvis
```
## Setting up your code
### Overloading operator new[]
EASTL requires you to have an overload for the operator new[], here is an example that just forwards to global new[]:
```c
void* __cdecl operator new[](size_t size, const char* name, int flags, unsigned debugFlags, const char* file, int line)
{
return new uint8_t[size];
}
```
+374
View File
@@ -0,0 +1,374 @@
# EASTL Design
## Introduction
EASTL (EA Standard Template Library) is designed to be a template library which encompasses and extends the functionality of standard C++ STL while improving it in various ways useful to game development. Much of EASTL's design is identical to standard STL, as the large majority of the STL is well-designed for many uses. The primary areas where EASTL deviates from standard STL implementations are essentially the following:
* EASTL has a simplified and more flexible custom allocation scheme.
* EASTL has significantly easier to read code.
* EASTL has extension containers and algorithms.
* EASTL has optimizations designed for game development.
Of the above items, the only one which is an incompatible difference with STL is the case of memory allocation. The method for defining a custom allocator for EASTL is slightly different than that of standard STL, though they are 90% similar. The 10% difference, however, is what makes EASTL generally easier and more powerful to work with than standard STL. Containers without custom allocators act identically between EASTL and standard STL.
## Motivations
Our motifications for making EASTL drive the design of EASTL. As identified in the EASTL RFC (Request for Comment), the primary reasons for implementing a custom version of the STL are:
* <span class="458151900-03082005"><font><font>Some STL implementations (especially Microsoft STL) have inferior performance characteristics that make them unsuitable for game development. EASTL is faster than all existing STL implementations.</font></font></span>
* The STL is sometimes hard to debug, as most STL implementations use cryptic variable names and unusual data structures.
* STL allocators are sometimes painful to work with, as they have many requirements and cannot be modified once bound to a container.
* The STL includes excess functionality that can lead to larger code than desirable. It's not very easy to tell programmers they shouldn't use that functionality.
* The STL is implemented with very deep function calls. This results is unacceptable performance in non-optimized builds and sometimes in optimized builds as well.
* The STL doesn't support alignment of contained objects.
* STL containers won't let you insert an entry into a container without supplying an entry to copy from. This can be inefficient.
* Useful STL extensions (e.g. slist, hash_map, shared_ptr) found in existing STL implementations such as STLPort are not portable because they don't exist in other versions of STL or aren't consistent between STL versions.
* The STL lacks useful extensions that game programmers find useful (e.g. intrusive_list) but which could be best optimized in a portable STL environment.
* The STL has specifications that limit our ability to use it efficiently. For example, STL vectors are not guaranteed to use contiguous memory and so cannot be safely used as an array.
* The STL puts an emphasis on correctness before performance, whereas sometimes you can get significant performance gains by making things less academcially pure.
* STL containers have private implementations that don't allow you to work with their data in a portable way, yet sometimes this is an important thing to be able to do (e.g. node pools).
* All existing versions of STL allocate memory in empty versions of at least some of their containers. This is not ideal and prevents optimizations such as container memory resets that can greatly increase performance in some situations.
* The STL is slow to compile, as most modern STL implementations are very large.
* There are legal issues that make it hard for us to freely use portable STL implementations such as STLPort.
* We have no say in the design and implementation of the STL and so are unable to change it to work for our needs.
## Prime Directives
The implementation of EASTL is guided foremost by the following directives which are listed in order of importance.
1. Efficiency (speed and memory usage)
2. Correctness
3. Portability
4. Readability
Note that unlike commercial STL implementations which must put correctness above all, we put a higher value on efficiency. As a result, some functionality may have some usage limitation that is not present in other similar systems but which allows for more efficient operation, especially on the platforms of significance to us.
Portability is significant, but not critical. Yes, EASTL must compile and run on all platforms that we will ship games for. But we don't take that to mean under all compilers that could be conceivably used for such platforms. For example, Microsoft VC6 can be used to compile Windows programs, but VC6's C++ support is too weak for EASTL and so you simply cannot use EASTL under VC6.
Readability is something that EASTL achieves better than many other templated libraries, particularly Microsoft STL and STLPort. We make every attempt to make EASTL code clean and sensible. Sometimes our need to provide optimizations (particularly related to type_traits and iterator types) results in less simple code, but efficiency happens to be our prime directive and so it overrides all other considerations.
## Thread Safety
It's not simple enough to simply say that EASTL is thread-safe or thread-unsafe. However, we can say that with respect to thread safety that EASTL does the right thing.
Individual EASTL containers are not thread-safe. That is, access to an instance of a container from multiple threads at the same time is unsafe if any of those accesses are modifying operations. A given container can be read from multiple threads simultaneously as well as any other standalone data structure. If a user wants to be able to have modifying access an instance of a container from multiple threads, it is up to the user to ensure that proper thread synchronization occurs. This usually means using a mutex.
EASTL classes other than containers are the same as containers with respect to thread safety. EASTL functions (e.g. algorithms) are inherently thread-safe as they have no instance data and operate entirely on the stack. As of this writing, no EASTL function allocates memory and thus doesn't bring thread safety issues via that means.
The user may well need to be concerned about thread safety with respect to memory allocation. If the user modifies containers from multiple threads, then allocators are going to be accessed from multiple threads. If an allocator is shared across multiple container instances (of the same type of container or not), then mutexes (as discussed above) the user uses to protect access to indivudual instances will not suffice to provide thread safety for allocators used across multiple instances. The conventional solution here is to use a mutex within the allocator if it is exected to be used by multiple threads.
EASTL uses neither static nor global variables and thus there are no inter-instance dependencies that would make thread safety difficult for the user to implement.
## Container Design
All EASTL containers follow a set of consistent conventions. Here we define the prototypical container which has the minimal functionality that all (non-adapter) containers must have. Some containers (e.g. stack) are explicitly adapter containers and thus wrap or inherit the properties of the wrapped container in a way that is implementation specific.
```cpp
template <class T, class Allocator =
EASTLAllocator>
class container
{
public:
typedef container<T, Allocator> this_type;
typedef
T
value_type;
typedef T*
pointer;
typedef const T*
const_pointer;
typedef
T& reference;
typedef const
T& const_reference;
typedef
ptrdiff_t difference_type;
typedef
impl_defined size_type;
typedef impl-defined
iterator;
typedef impl-defined
const_iterator;
typedef reverse_iterator<iterator> reverse_iterator;
typedef reverse_iterator<const_iterator> reverse_const_iterator;
typedef Allocator
allocator_type;
public:
container(const
allocator_type& allocator = allocator_type());
container(const this_type&
x);
this_type&
operator=(this_type& x);
void swap(this_type& x);
void reset();
allocator_type& get_allocator();
void set_allocator(allocator_type& allocator);
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
bool validate() const;
int validate_iterator(const_iterator i)
const;
protected:
allocator_type mAllocator;
};
template <class T, class
Allocator>
bool operator==(const container<T, Allocator>& a, const container<T, Allocator>& b);
template <class T, class
Allocator>
bool operator!=(const container<T, Allocator>& a, const
container<T, Allocator>&
b);
```
Notes:
* Swapped containers do not swap their allocators.
* Newly constructed empty containers do no memory allocation. Some STL and other container libraries allocate an initial node from the class memory allocator. EASTL containers by design never do this. If a container needs an initial node, that node should be made part of the container itself or be a static empty node object.
* Empty containers (new or otherwise) contain no constructed objects, including those that might be in an 'end' node. Similarly, no user object (e.g. of type T) should be constructed unless required by the design and unless documented in the cotainer/algorithm contract. 
* The reset function is a special extension function which unilaterally resets the container to an empty state without freeing the memory of the contained objects. This is useful for very quickly tearing down a container built into scratch memory. No memory is allocated by reset, and the container has no allocatedmemory after the reset is executed.
* The validate and validate_iterator functions provide explicit container and iterator validation. EASTL provides an option to do implicit automatic iterator and container validation, but full validation (which can be potentially extensive) has too much of a performance cost to execute implicitly, even in a debug build. So EASTL provides these explicit functions which can be called by the user at the appropriate time and in optimized builds as well as debug builds.
## Allocator Design
The most significant difference between EASTL and standard C++ STL is that standard STL containers are templated on an allocator class with the interface defined in std::allocator. std::allocator is defined in the C++ standard as this:
```cpp
// Standard C++ allocator
template <class T>
class allocator
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T&
reference;
typedef const
T& const_reference;
typedef T value_type;
template <class U>
struct rebind { typedef allocator<U> other; };
allocator() throw();
allocator(const allocator&) throw();
template <class U>
allocator(const allocator<U>&) throw();
~allocator()
throw();
pointer
address(reference x) const;
const_pointer address(const_reference x)
const;
pointer allocate(size_type, typename
allocator<void>::const_pointer hint = 0);
void deallocate(pointer p,
size_type n);
size_type max_size() const
throw();
void construct(pointer p,
const T& val);
void destroy(pointer
p);
};
```
Each STL container needs to have an allocator templated on container type T associated with it. The problem with this is that allocators for containers are defined at the class level and not the instance level. This makes it painful to define custom allocators for containers and adds to code bloat. Also, it turns out that the containers don't actually use allocator<T> but instead use allocator\<T>::rebind\<U>::other. Lastly, you cannot access this allocator after the container is constructed. There are some good academic reasons why the C++ standard works this way, but it results in a lot of unnecessary pain and makes concepts like memory tracking much harder to implement.
What EASTL does is use a more familiar memory allocation pattern whereby there is only one allocator class interface and it is used by all containers. Additionally EASTL containers let you access their allocators and query them, name them, change them, etc.
EASTL has chosen to make allocators not be copied between containers during container swap and assign operations. This means that if container A swaps its contents with container B, both containers retain their original allocators. Similarly, assigning container A to container B causes container B to retain its original allocator. Containers that are equivalent should report so via operator==; EASTL will do a smart swap if allocators are equal, and a brute-force swap otherwise.
```cpp
// EASTL allocator
class allocator
{
public:
    allocator(const char* pName = NULL);
    void* allocate(size_t n, int flags = 0);
    void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0);
    void  deallocate(void* p, size_t n);
    const char* get_name() const;
    void        set_name(const char* pName);
};
allocator* GetDefaultAllocator();
```
## Fixed Size Container Design
EASTL supplies a set of fixed-size containers that the user can use, though the user can also implement their own versions. So in addition to class list there is class fixed_list. The fixed_list class implements a linked list via a fixed-size pool of contiguous memory which has no space overhead (unlike with a regular heap), doesn't cause fragmentation, and allocates very quickly.
EASTL implements fixed containers via subclasses of regular containers which set the regular container's allocator to point to themselves. Thus the implementation for fixed_list is very tiny and consists of little more than constructor and allocator functions. This design has some advantages but has one small disadvantage. The primary advantages are primarily that code bloat is reduced and that the implementation is simple and the user can easily extend it. The primary disadvantage is that the parent list class ends up with a pointer to itself and thus has 4 bytes that could arguably be saved if system was designed differently. That different design would be to make the list class have a policy template parameter which specifies that it is a fixed pool container. EASTL chose not to follow the policy design because it would complicate the implementation, make it harder for the user to extend the container, and would potentially waste more memory due to code bloat than it would save due to the 4 byte savings it achieves in container instances.
## Algorithm Design
EASTL algorithms very much follow the philosophy of standard C++ algorithms, as this philosophy is sound and efficient. One of the primary aspects of algorithms is that they work on iterators and not containers. You will note for example that the find algorithm takes a first and last iterator as arguments and not a container. This has two primary benefits: it allows the user to specify a subrange of the container to search within and it allows the user to apply the find algorithm to sequences that aren't containers (e.g. a C array).
EASTL algorithms are optimized at least as well as the best STL algorithms found in commercial libraries and are significantly optimized over the algorithms that come with the first-party STLs that come with compilers. Most significantly, EASTL algorithms take advantage of type traits of contained classes and take advantage of iterator types to optimize code generation. For example, if you resize an array of integers (or other "pod" type), EASTL will detect that this can be done with a memcpy instead of a slow object-by-object move as would Micrsoft STL.
The optimizations found in EASTL algorithms and the supporting code in EASTL type traits consists of some fairly tricky advanced C++ and while it is fairly easy to read, it requires a C++ expert (language lawyer, really) to implement confidently. The result of this is that it takes more effort to develop and maintain EASTL than it would to maintain a simpler library. However, the performance advantages have been deemed worth the tradeoff.
## Smart Pointer Design
EASTL implements the following smart pointer types:
* shared_ptr
* shared_array
* weak_ptr
* instrusive_ptr
* scoped_ptr
* scoped_array
* linked_ptr
* linked_array
All but linked_ptr/linked_array are well-known smart pointers from the Boost library. The behaviour of these smart pointers is very similar to those from Boost with two exceptions:
* EASTL smart pointers allow you to assign an allocator to them.
* EASTL shared_ptr implements deletion via a templated parameter instead of a dynamically allocated virtual member object interface.
With respect to assigning an allocator, this gives EASTL more control over memory allocation and tracking, as Boost smart pointers unilaterally use global operator new to allocate memory from the global heap.
With respect to shared_ptr deletion, EASTL's current design of using a templated parameter is questionable, but does have some reason. The advantage is that EASTL avoids a heap allocation, avoids virtual function calls, and avoids templated class proliferation. The disadvantage is that EASTL shared_ptr containers which hold void pointers can't call the destructors of their contained objects unless the user manually specifies a custom deleter template parameter. This is case whereby EASTL is more efficient but less safe. We can revisit this topic in the future if it becomes an issue.
## list::size is O(n)
As of this writing, EASTL has three linked list classes: list, slist, and intrusive_list. In each of these classes, the size of the list is not cached in a member size variable. The result of this is that getting the size of a list is not a fast operation, as it requires traversing the list and counting the nodes. We could make the list::size function be fast by having a member mSize variable which tracks the size as we insert and delete items. There are reasons for having such functionality and reasons for not having such functionality. We currently choose to not have a member mSize variable as it would add four bytes to the class, add a tiny amount of processing to functions such as insert and erase, and would only serve to improve the size function, but no others. In the case of intrusive_list, it would do additional harm. The alternative argument is that the C++ standard states that std::list should be an O(1) operation (i.e. have a member size variable), that many C++ standard library list implementations do so, that the size is but an integer which is quick to update, and that many users expect to have a fast size function. In the final analysis, we are developing a library for game development and performance is paramount, so we choose to not cache the list size. The user can always implement a size cache himself.
## basic_string doesn't use copy-on-write
The primary benefit of CoW is that it allows for the sharing of string data between two string objects. Thus if you say this:
```cpp
string a("hello");
string b(a);
```
the "hello" will be shared between a and b. If you then say this:
```cpp
a = "world";
```
then `a` will release its reference to "hello" and leave b with the only reference to it. Normally this functionality is accomplished via reference counting and with atomic operations or mutexes.
The C++ standard does not say anything about basic_string and CoW. However, for a basic_string implementation to be standards-conforming, a number of issues arise which dictate some things about how one would have to implement a CoW string. The discussion of these issues will not be rehashed here, as you can read the references below for better detail than can be provided in the space we have here. However, we can say that the C++ standard is sensible and that anything we try to do here to allow for an efficient CoW implementation would result in a generally unacceptable string interface.
The disadvantages of CoW strings are:
* A reference count needs to exist with the string, which increases string memory usage.
* With thread safety, atomic operations and mutex locks are expensive, especially on weaker memory systems such as console gaming platforms.
* All non-const string accessor functions need to do a sharing check the the first such check needs to detach the string. Similarly, all string assignments need to do a sharing check as well. If you access the string before doing an assignment, the assignment doesn't result in a shared string, because the string has already been detached.
* String sharing doesn't happen the large majority of the time. In some cases, the total sum of the reference count memory can exceed any memory savings gained by the strings that share representations. 
The addition of a cow_string class is under consideration for EASTL. There are conceivably some systems which have string usage patterns which would benefit from CoW sharing. Such functionality is best saved for a separate string implementation so that the other string uses aren't penalized.
This is a good starting HTML reference on the topic:
> [http://www.gotw.ca/publications/optimizations.htm](http://www.gotw.ca/publications/optimizations.htm)
Here is a well-known Usenet discussion on the topic:
> [http://groups-beta.google.com/group/comp.lang.c++.moderated/browse_thread/thread/3dc6af5198d0bf7/886c8642cb06e03d](http://groups-beta.google.com/group/comp.lang.c++.moderated/browse_thread/thread/3dc6af5198d0bf7/886c8642cb06e03d)
----------------------------------------------
End of document
Binary file not shown.
+731
View File
@@ -0,0 +1,731 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This is a Microsoft natvis file, which allows visualization of complex variables in the
Microsoft debugger starting with VS2012. It's a successor to the AutoExp.dat file format.
This file needs to go into your C:\Users\<user>\Documents\Visual Studio 2011\Visualizers\
folder. Microsoft documentation states that it should go into a 2012 folder, but testing
in June of 2013 suggests that it still needs to be the 2011 folder.
You don't need to restart Visual Studio to use it, you just need to restart the debug
session. You can have multiple .natvis files and they will all be used.
VS2017 natvis documentation:
https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="eastl::unique_ptr&lt;*&gt;">
<DisplayString Condition="mPair.mFirst != nullptr">({(void*)mPair.mFirst} = {*mPair.mFirst})</DisplayString>
<DisplayString Condition="mPair.mFirst == nullptr">({nullptr})</DisplayString>
<Expand>
<Item Name="[pointer]">(void*)mPair.mFirst</Item>
<Item Name="[value]">*mPair.mFirst</Item>
</Expand>
</Type>
<Type Name="eastl::shared_ptr&lt;*&gt;">
<DisplayString Condition="mpValue != nullptr">({(void*)mpValue} = {*mpValue})</DisplayString>
<DisplayString Condition="mpValue == nullptr">({nullptr})</DisplayString>
<Expand>
<Item Name="[pointer]">(void*)mpValue</Item>
<Item Name="[value]">*mpValue</Item>
<Item Name="[reference count]">mpRefCount->mRefCount</Item>
<Item Name="[weak reference count]">mpRefCount->mWeakRefCount</Item>
</Expand>
</Type>
<Type Name="eastl::weak_ptr&lt;*&gt;">
<DisplayString>{((mpRefCount &amp;&amp; mpRefCount-&gt;mRefCount) ? mpValue : nullptr)}</DisplayString>
<Expand>
<ExpandedItem>mpRefCount &amp;&amp; mpRefCount-&gt;mRefCount ? mpValue : nullptr</ExpandedItem>
</Expand>
</Type>
<Type Name="eastl::array&lt;*,*&gt;">
<DisplayString Condition="$T2 == 0">[{$T2}] {{}}</DisplayString>
<DisplayString Condition="$T2 == 1">[{$T2}] {{ {*mValue} }}</DisplayString>
<DisplayString Condition="$T2 == 2">[{$T2}] {{ {*mValue}, {*(mValue+1)} }}</DisplayString>
<DisplayString Condition="$T2 == 3">[{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)} }}</DisplayString>
<DisplayString Condition="$T2 == 4">[{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)}, {*(mValue+3)} }}</DisplayString>
<DisplayString Condition="$T2 == 5">[{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)}, {*(mValue+3)}, {*(mValue+4)} }}</DisplayString>
<DisplayString Condition="$T2 == 6">[{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)}, {*(mValue+3)}, {*(mValue+4)}, {*(mValue+5)} }}</DisplayString>
<DisplayString Condition="$T2 &gt; 6">[{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)}, {*(mValue+3)}, {*(mValue+4)}, {*(mValue+5)}, ... }}</DisplayString>
<Expand>
<Item Name="[size]">$T2</Item>
<ArrayItems>
<Size>$T2</Size>
<ValuePointer>mValue</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="eastl::basic_string&lt;*&gt;">
<DisplayString Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">"{mPair.mFirst.heap.mpBegin,sb}"</DisplayString>
<DisplayString Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">"{mPair.mFirst.sso.mData,sb}"</DisplayString>
<Expand>
<Item Name="[length]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">mPair.mFirst.heap.mnSize</Item>
<Item Name="[capacity]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">(mPair.mFirst.heap.mnCapacity &amp; ~kHeapMask)</Item>
<Item Name="[value]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">mPair.mFirst.heap.mpBegin,sb</Item>
<Item Name="[length]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">(SSOLayout::SSO_CAPACITY - mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize)</Item>
<Item Name="[capacity]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">SSOLayout::SSO_CAPACITY</Item>
<Item Name="[value]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">mPair.mFirst.sso.mData,sb</Item>
<Item Name="[uses heap]">!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)</Item>
</Expand>
</Type>
<Type Name="eastl::basic_string&lt;wchar_t,*&gt;">
<DisplayString Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">{mPair.mFirst.heap.mpBegin,su}</DisplayString>
<DisplayString Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">{mPair.mFirst.sso.mData,su}</DisplayString>
<Expand>
<Item Name="[length]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">mPair.mFirst.heap.mnSize</Item>
<Item Name="[capacity]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">(mPair.mFirst.heap.mnCapacity &amp; ~kHeapMask)</Item>
<Item Name="[value]" Condition="!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">mPair.mFirst.heap.mpBegin,su</Item>
<Item Name="[length]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">(SSOLayout::SSO_CAPACITY - mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize)</Item>
<Item Name="[capacity]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">SSOLayout::SSO_CAPACITY</Item>
<Item Name="[value]" Condition="!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)">mPair.mFirst.sso.mData,su</Item>
<Item Name="[uses heap]">!!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize &amp; kSSOMask)</Item>
</Expand>
</Type>
<Type Name="eastl::pair&lt;*&gt;">
<DisplayString>({first}, {second})</DisplayString>
<Expand>
<Item Name="first">first</Item>
<Item Name="second">second</Item>
</Expand>
</Type>
<Type Name="eastl::span&lt;*&gt;">
<DisplayString Condition="mnSize == 0">[{mnSize}] {{}}</DisplayString>
<DisplayString Condition="mnSize == 1">[{mnSize}] {{ {*mpData} }}</DisplayString>
<DisplayString Condition="mnSize == 2">[{mnSize}] {{ {*mpData}, {*(mpData+1)} }}</DisplayString>
<DisplayString Condition="mnSize == 3">[{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)} }}</DisplayString>
<DisplayString Condition="mnSize == 4">[{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)}, {*(mpData+3)} }}</DisplayString>
<DisplayString Condition="mnSize == 5">[{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)}, {*(mpData+3)}, {*(mpData+4)} }}</DisplayString>
<DisplayString Condition="mnSize == 6">[{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)}, {*(mpData+3)}, {*(mpData+4)}, {*(mpData+5)} }}</DisplayString>
<DisplayString Condition="mnSize &gt; 6">[{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)}, {*(mpData+3)}, {*(mpData+4)}, {*(mpData+5)}, ... }}</DisplayString>
<Expand>
<Item Name="[size]">mnSize</Item>
<ArrayItems>
<Size>mnSize</Size>
<ValuePointer>mpData</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="eastl::VectorBase&lt;*&gt;">
<DisplayString Condition="mpEnd == mpBegin">[{mpEnd - mpBegin}] {{}}</DisplayString>
<DisplayString Condition="mpEnd - mpBegin == 1">[{mpEnd - mpBegin}] {{ {*mpBegin} }}</DisplayString>
<DisplayString Condition="mpEnd - mpBegin == 2">[{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)} }}</DisplayString>
<DisplayString Condition="mpEnd - mpBegin == 3">[{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)} }}</DisplayString>
<DisplayString Condition="mpEnd - mpBegin == 4">[{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)}, {*(mpBegin+3)} }}</DisplayString>
<DisplayString Condition="mpEnd - mpBegin == 5">[{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)}, {*(mpBegin+3)}, {*(mpBegin+4)} }}</DisplayString>
<DisplayString Condition="mpEnd - mpBegin == 6">[{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)}, {*(mpBegin+3)}, {*(mpBegin+4)}, {*(mpBegin+5)} }}</DisplayString>
<DisplayString Condition="mpEnd - mpBegin &gt; 6">[{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)}, {*(mpBegin+3)}, {*(mpBegin+4)}, {*(mpBegin+5)}, ... }}</DisplayString>
<Expand>
<Item Name="[size]">mpEnd - mpBegin</Item>
<Item Name="[capacity]">mCapacityAllocator.mFirst - mpBegin</Item>
<ArrayItems>
<Size>mpEnd - mpBegin</Size>
<ValuePointer>mpBegin</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="eastl::DequeBase&lt;*,*,*&gt;">
<DisplayString Condition="mItBegin.mpCurrent == mItEnd.mpCurrent">
[0] {{}}
</DisplayString>
<DisplayString Condition="(mItEnd.mpCurrentArrayPtr - mItBegin.mpCurrentArrayPtr) * $T3 + (mItEnd.mpCurrent-mItEnd.mpBegin) - (mItBegin.mpCurrent-mItBegin.mpBegin) == 1">
[1] {{ {*mItBegin.mpCurrent} }}
</DisplayString>
<DisplayString Condition="(mItEnd.mpCurrentArrayPtr - mItBegin.mpCurrentArrayPtr) * $T3 + (mItEnd.mpCurrent-mItEnd.mpBegin) - (mItBegin.mpCurrent-mItBegin.mpBegin) != 0">
[{(mItEnd.mpCurrentArrayPtr - mItBegin.mpCurrentArrayPtr) * $T3 + (mItEnd.mpCurrent-mItEnd.mpBegin) - (mItBegin.mpCurrent-mItBegin.mpBegin)}]
{{
{*mItBegin.mpCurrent},
...
}}
</DisplayString>
<Expand>
<Item Name="[size]">(mItEnd.mpCurrentArrayPtr - mItBegin.mpCurrentArrayPtr) * $T3 + (mItEnd.mpCurrent-mItEnd.mpBegin) - (mItBegin.mpCurrent-mItBegin.mpBegin)</Item>
<IndexListItems>
<Size>(mItEnd.mpCurrentArrayPtr - mItBegin.mpCurrentArrayPtr) * $T3 + (mItEnd.mpCurrent-mItEnd.mpBegin) - (mItBegin.mpCurrent-mItBegin.mpBegin)</Size>
<ValueNode>mItBegin.mpCurrentArrayPtr[(mItBegin.mpCurrent-mItBegin.mpBegin + $i) / $T3][(mItBegin.mpCurrent-mItBegin.mpBegin + $i) % $T3]</ValueNode>
</IndexListItems>
</Expand>
</Type>
<Type Name="eastl::DequeIterator&lt;*&gt;">
<DisplayString>{*mpCurrent}</DisplayString>
<Expand>
<Item Name="Value">*mpCurrent</Item>
<Item Name="Previous" Condition="mpCurrent == mpBegin">*(*(mpCurrentArrayPtr-1) + (mpEnd-mpBegin) - 1)</Item>
<Item Name="Previous" Condition="mpCurrent != mpBegin">*(mpCurrent-1)</Item>
<Item Name="Next" Condition="mpCurrent+1 == mpEnd">**(mpCurrentArrayPtr+1)</Item>
<Item Name="Next" Condition="mpCurrent+1 != mpEnd">*(mpCurrent+1)</Item>
<Item Name="Begin">mpCurrent == mpBegin</Item>
<Item Name="End">mpCurrent+1 == mpEnd</Item>
</Expand>
</Type>
<Type Name="eastl::queue&lt;*&gt;">
<AlternativeType Name="eastl::priority_queue&lt;*&gt;" />
<AlternativeType Name="eastl::stack&lt;*&gt;" />
<DisplayString>{c}</DisplayString>
<Expand>
<ExpandedItem>c</ExpandedItem>
</Expand>
</Type>
<Type Name="eastl::ListBase&lt;*&gt;">
<DisplayString Condition="mNodeAllocator.mFirst.mpNext == &amp;mNodeAllocator.mFirst">
[0] {{}}
</DisplayString>
<DisplayString Condition="mNodeAllocator.mFirst.mpNext != &amp;mNodeAllocator.mFirst &amp;&amp; mNodeAllocator.mFirst.mpNext-&gt;mpNext == &amp;mNodeAllocator.mFirst">
[1] {{ {((eastl::ListNode&lt;$T1&gt;*)mNodeAllocator.mFirst.mpNext)-&gt;mValue} }}
</DisplayString>
<DisplayString Condition="mNodeAllocator.mFirst.mpNext != &amp;mNodeAllocator.mFirst &amp;&amp; mNodeAllocator.mFirst.mpNext-&gt;mpNext != &amp;mNodeAllocator.mFirst &amp;&amp; mNodeAllocator.mFirst.mpNext-&gt;mpNext-&gt;mpNext == &amp;mNodeAllocator.mFirst">
[2]
{{
{((eastl::ListNode&lt;$T1&gt;*)mNodeAllocator.mFirst.mpNext)-&gt;mValue},
{((eastl::ListNode&lt;$T1&gt;*)mNodeAllocator.mFirst.mpNext-&gt;mpNext)-&gt;mValue}
}}
</DisplayString>
<DisplayString Condition="mNodeAllocator.mFirst.mpNext != &amp;mNodeAllocator.mFirst &amp;&amp; mNodeAllocator.mFirst.mpNext-&gt;mpNext != &amp;mNodeAllocator.mFirst &amp;&amp; mNodeAllocator.mFirst.mpNext-&gt;mpNext-&gt;mpNext != &amp;mNodeAllocator.mFirst">
[?]
{{
{((eastl::ListNode&lt;$T1&gt;*)mNodeAllocator.mFirst.mpNext)-&gt;mValue},
{((eastl::ListNode&lt;$T1&gt;*)mNodeAllocator.mFirst.mpNext-&gt;mpNext)-&gt;mValue},
...
}}
</DisplayString>
<Expand>
<Synthetic Name="NOTE!">
<DisplayString>Content of lists will repeat indefinitely. Keep that in mind!</DisplayString>
</Synthetic>
<LinkedListItems>
<HeadPointer>mNodeAllocator.mFirst.mpNext</HeadPointer>
<NextPointer>mpNext</NextPointer>
<ValueNode>((eastl::ListNode&lt;$T1&gt;*)this)-&gt;mValue</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="eastl::ListNode&lt;*&gt;">
<DisplayString>{mValue}</DisplayString>
<Expand>
<Item Name="Value">mValue</Item>
<Item Name="Next">*(eastl::ListNode&lt;$T1&gt;*)mpNext</Item>
<Item Name="Previous">*(eastl::ListNode&lt;$T1&gt;*)mpPrev</Item>
<Synthetic Name="NOTE!">
<DisplayString>Content of lists will repeat indefinitely. Keep that in mind!</DisplayString>
</Synthetic>
<Synthetic Name="List">
<DisplayString>The rest of the list follows:</DisplayString>
</Synthetic>
<LinkedListItems>
<HeadPointer>(eastl::ListNode&lt;$T1&gt;*)mpNext-&gt;mpNext</HeadPointer>
<NextPointer>(eastl::ListNode&lt;$T1&gt;*)mpNext</NextPointer>
<ValueNode>mValue</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="eastl::ListIterator&lt;*&gt;">
<DisplayString>{*mpNode}</DisplayString>
<Expand>
<ExpandedItem>mpNode</ExpandedItem>
</Expand>
</Type>
<Type Name="eastl::SListBase&lt;*&gt;">
<DisplayString Condition="mNode.mpNext == 0">
[0] {{}}
</DisplayString>
<DisplayString Condition="mNode.mpNext != 0 &amp;&amp; mNode.mpNext-&gt;mpNext == 0">
[1]
{{
{((eastl::SListNode&lt;$T1&gt;*)mNode.mpNext)-&gt;mValue}
}}
</DisplayString>
<DisplayString Condition="mNode.mpNext != 0 &amp;&amp; mNode.mpNext-&gt;mpNext != 0 &amp;&amp; mNode.mpNext-&gt;mpNext-&gt;mpNext == 0">
[2]
{{
{((eastl::SListNode&lt;$T1&gt;*)mNode.mpNext)-&gt;mValue},
{((eastl::SListNode&lt;$T1&gt;*)mNode.mpNext-&gt;mpNext)-&gt;mValue}
}}
</DisplayString>
<DisplayString Condition="mNode.mpNext != 0 &amp;&amp; mNode.mpNext-&gt;mpNext != 0 &amp;&amp; mNode.mpNext-&gt;mpNext-&gt;mpNext != 0">
[?]
{{
{((eastl::SListNode&lt;$T1&gt;*)mNode.mpNext)-&gt;mValue},
{((eastl::SListNode&lt;$T1&gt;*)mNode.mpNext-&gt;mpNext)-&gt;mValue},
...
}}
</DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>mNode.mpNext</HeadPointer>
<NextPointer>mpNext</NextPointer>
<ValueNode>((eastl::SListNode&lt;$T1&gt;*)this)-&gt;mValue</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="eastl::SListNode&lt;*&gt;">
<DisplayString>{mValue}</DisplayString>
<Expand>
<Item Name="Value">mValue</Item>
<Item Name="Next">*(eastl::SListNode&lt;$T1&gt;*)mpNext</Item>
<Synthetic Name="List">
<DisplayString>The rest of the list follows:</DisplayString>
</Synthetic>
<LinkedListItems>
<HeadPointer>mpNext == nullptr ? nullptr : (eastl::SListNode&lt;$T1&gt;*)mpNext-&gt;mpNext</HeadPointer>
<NextPointer>(eastl::SListNode&lt;$T1&gt;*)mpNext</NextPointer>
<ValueNode>mValue</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="eastl::SListIterator&lt;*&gt;">
<DisplayString>{*mpNode}</DisplayString>
<Expand>
<ExpandedItem>*mpNode</ExpandedItem>
</Expand>
</Type>
<Type Name="eastl::intrusive_list_base">
<DisplayString Condition="mAnchor.mpNext == &amp;mAnchor">[0] {{}}</DisplayString>
<DisplayString Condition="mAnchor.mpNext != &amp;mAnchor &amp;&amp; mAnchor.mpNext-&gt;mpNext == &amp;mAnchor">[1] {{ {mAnchor.mpNext} }}</DisplayString>
<DisplayString Condition="mAnchor.mpNext != &amp;mAnchor &amp;&amp; mAnchor.mpNext-&gt;mpNext != &amp;mAnchor">[?] {{ {mAnchor.mpNext}, ... }}</DisplayString>
<Expand>
<Synthetic Name="NOTE!">
<DisplayString>Content of intrusive lists will repeat indefinitely. Keep that in mind!</DisplayString>
</Synthetic>
<LinkedListItems>
<HeadPointer>mAnchor.mpNext</HeadPointer>
<NextPointer>mpNext</NextPointer>
<ValueNode>*this</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="eastl::intrusive_list_iterator&lt;*&gt;">
<DisplayString>{*mpNode}</DisplayString>
<Expand>
<ExpandedItem>*mpNode</ExpandedItem>
</Expand>
</Type>
<Type Name="eastl::set&lt;*&gt;">
<AlternativeType Name="eastl::multiset&lt;*&gt;" />
<DisplayString Condition="mnSize == 0">
[0] {{}}
</DisplayString>
<DisplayString Condition="mnSize == 1">
[1]
{{
{((eastl::rbtree_node&lt;$T1&gt;*)mAnchor.mpNodeLeft)-&gt;mValue}
}}
</DisplayString>
<DisplayString Condition="mnSize &gt; 1">
[{mnSize}]
{{
{((eastl::rbtree_node&lt;$T1&gt;*)mAnchor.mpNodeLeft)-&gt;mValue},
...
}}
</DisplayString>
<Expand>
<Item Name="[size]">mnSize</Item>
<TreeItems>
<Size>mnSize</Size>
<HeadPointer>mAnchor.mpNodeParent</HeadPointer>
<LeftPointer>mpNodeLeft</LeftPointer>
<RightPointer>mpNodeRight</RightPointer>
<ValueNode>((eastl::rbtree_node&lt;$T1&gt;*)this)-&gt;mValue</ValueNode>
</TreeItems>
</Expand>
</Type>
<Type Name="eastl::rbtree&lt;*,*&gt;">
<DisplayString Condition="mnSize == 0">
[0] {{}}
</DisplayString>
<DisplayString Condition="mnSize == 1">
[1]
{{
{((eastl::rbtree_node&lt;$T2&gt;*)mAnchor.mpNodeLeft)-&gt;mValue}
}}
</DisplayString>
<DisplayString Condition="mnSize &gt; 1">
[{mnSize}]
{{
{((eastl::rbtree_node&lt;$T2&gt;*)mAnchor.mpNodeLeft)-&gt;mValue},
...
}}
</DisplayString>
<Expand>
<Item Name="[size]">mnSize</Item>
<TreeItems>
<Size>mnSize</Size>
<HeadPointer>mAnchor.mpNodeParent</HeadPointer>
<LeftPointer>mpNodeLeft</LeftPointer>
<RightPointer>mpNodeRight</RightPointer>
<ValueNode>((eastl::rbtree_node&lt;$T2&gt;*)this)-&gt;mValue</ValueNode>
</TreeItems>
</Expand>
</Type>
<Type Name="eastl::rbtree_node&lt;*&gt;">
<DisplayString>{mValue}</DisplayString>
<Expand>
<Item Name="Value">mValue</Item>
<Synthetic Name="NOTE!">
<DisplayString>It is possible to expand parents that do not exist.</DisplayString>
</Synthetic>
<Item Name="Parent">*(eastl::rbtree_node&lt;$T1&gt;*)mpNodeParent</Item>
<Item Name="Left">*(eastl::rbtree_node&lt;$T1&gt;*)mpNodeLeft</Item>
<Item Name="Right">*(eastl::rbtree_node&lt;$T1&gt;*)mpNodeRight</Item>
</Expand>
</Type>
<Type Name="eastl::rbtree_iterator&lt;*&gt;">
<DisplayString>{*mpNode}</DisplayString>
<Expand>
<ExpandedItem>mpNode</ExpandedItem>
</Expand>
</Type>
<Type Name="eastl::hashtable&lt;*&gt;">
<DisplayString Condition="mnElementCount == 0">[{mnElementCount}] {{}}</DisplayString>
<DisplayString Condition="mnElementCount != 0">[{mnElementCount}] {{ ... }}</DisplayString>
<Expand>
<ArrayItems IncludeView="detailed">
<Size>mnBucketCount</Size>
<ValuePointer>mpBucketArray</ValuePointer>
</ArrayItems>
<CustomListItems ExcludeView="detailed">
<Variable Name="bucketIndex" InitialValue="0"/>
<Variable Name="entry" InitialValue ="mpBucketArray[bucketIndex]"/>
<Loop>
<Item Condition="entry != nullptr">entry->mValue</Item>
<If Condition="entry != nullptr">
<Exec>entry = entry->mpNext</Exec>
</If>
<If Condition="entry == nullptr">
<Exec>bucketIndex++</Exec>
<Break Condition="bucketIndex == mnBucketCount"/>
<Exec>entry = mpBucketArray[bucketIndex]</Exec>
</If>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="eastl::hash_node&lt;*&gt;">
<DisplayString Condition="this != 0 &amp;&amp; mpNext != 0"> {mValue}, {*mpNext}</DisplayString>
<DisplayString Condition="this != 0 &amp;&amp; mpNext == 0"> {mValue}</DisplayString>
<DisplayString Condition="this == 0"></DisplayString>
<Expand>
<LinkedListItems>
<HeadPointer>this</HeadPointer>
<NextPointer>mpNext</NextPointer>
<ValueNode>mValue</ValueNode>
</LinkedListItems>
</Expand>
</Type>
<Type Name="eastl::hashtable_iterator_base&lt;*&gt;">
<DisplayString>{mpNode-&gt;mValue}</DisplayString>
<Expand>
<ExpandedItem>mpNode-&gt;mValue</ExpandedItem>
</Expand>
</Type>
<Type Name="eastl::reverse_iterator&lt;*&gt;">
<DisplayString>{*(mIterator-1)}</DisplayString>
<Expand>
<ExpandedItem>mIterator-1</ExpandedItem>
</Expand>
</Type>
<Type Name="eastl::bitset&lt;*&gt;">
<DisplayString>{{count = {kSize}}}</DisplayString>
<Expand>
<Item Name="[count]">kSize</Item>
<CustomListItems>
<Variable Name="iWord" InitialValue="0" />
<Variable Name="iBitInWord" InitialValue="0" />
<Variable Name="bBitValue" InitialValue="false" />
<Size>kSize</Size>
<Loop>
<Exec>bBitValue = ((mWord[iWord] >> iBitInWord) % 2) != 0 ? true : false</Exec>
<Item>bBitValue</Item>
<Exec>iBitInWord++</Exec>
<If Condition="iBitInWord == kBitsPerWord">
<Exec>iWord++</Exec>
<Exec>iBitInWord = 0</Exec>
</If>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="eastl::ring_buffer&lt;*,*,*&gt;">
<DisplayString>{c}</DisplayString>
<Expand>
<ExpandedItem>c</ExpandedItem>
</Expand>
</Type>
<Type Name="eastl::basic_string_view&lt;*&gt;">
<DisplayString>{mpBegin,[mnCount]}</DisplayString>
<StringView>mpBegin,[mnCount]</StringView>
</Type>
<Type Name="eastl::compressed_pair_imp&lt;*&gt;">
<DisplayString Condition="($T3) == 0" Optional="true">({mFirst}, {mSecond})</DisplayString>
<DisplayString Condition="($T3) == 1" Optional="true">({mSecond})</DisplayString>
<DisplayString Condition="($T3) == 2" Optional="true">({mFirst})</DisplayString>
<DisplayString Condition="($T3) == 3" Optional="true">(empty)</DisplayString>
<DisplayString Condition="($T3) == 4" Optional="true">(empty)</DisplayString>
<DisplayString Condition="($T3) == 5" Optional="true">({mFirst}, {mSecond})</DisplayString>
</Type>
<Type Name="eastl::optional&lt;*&gt;">
<Intrinsic Name="value" Expression="*($T1*)&amp;val"/>
<DisplayString Condition="!engaged">nullopt</DisplayString>
<DisplayString Condition="engaged">{value()}</DisplayString>
<Expand>
<Item Condition="engaged" Name="value">value()</Item>
</Expand>
</Type>
<Type Name="eastl::ratio&lt;*&gt;">
<DisplayString>{$T1} to {$T2}}</DisplayString>
</Type>
<Type Name="eastl::chrono::duration&lt;*,eastl::ratio&lt;1,1000000000&gt; &gt;">
<DisplayString>{mRep} nanoseconds</DisplayString>
</Type>
<Type Name="eastl::chrono::duration&lt;*,eastl::ratio&lt;1,1000000&gt; &gt;">
<DisplayString>{mRep} microseconds</DisplayString>
</Type>
<Type Name="eastl::chrono::duration&lt;*,eastl::ratio&lt;1,1000&gt; &gt;">
<DisplayString>{mRep} milliseconds</DisplayString>
</Type>
<Type Name="eastl::chrono::duration&lt;*,eastl::ratio&lt;1,1&gt; &gt;">
<DisplayString>{mRep} seconds</DisplayString>
</Type>
<Type Name="eastl::chrono::duration&lt;*,eastl::ratio&lt;60,1&gt; &gt;">
<DisplayString>{mRep} minutes</DisplayString>
</Type>
<Type Name="eastl::chrono::duration&lt;*,eastl::ratio&lt;3600,1&gt; &gt;">
<DisplayString>{mRep} hours</DisplayString>
</Type>
<Type Name="eastl::chrono::duration&lt;*,eastl::ratio&lt;*,*&gt; &gt;">
<DisplayString>{mRep} duration with ratio = [{$T2} : {$T3}] </DisplayString>
</Type>
<Type Name="eastl::function&lt;*&gt;">
<DisplayString Condition="mInvokeFuncPtr == nullptr">empty</DisplayString>
<DisplayString>{mInvokeFuncPtr}</DisplayString>
</Type>
<Type Name="eastl::reference_wrapper&lt;*&gt;">
<DisplayString>{*val}</DisplayString>
</Type>
<!--
This implementation isn't ideal because it can't switch between showing inline value vs values stored in a heap allocation.
We are hitting the limit of natvis scripting that we can't workaround unless we change the implementation of eastl::any.
-->
<Type Name="eastl::any">
<DisplayString Condition="m_handler == nullptr">empty</DisplayString>
<DisplayString Condition="m_handler != nullptr">{m_storage.external_storage}</DisplayString>
</Type>
<Type Name="eastl::atomic_flag">
<DisplayString>{mFlag.mAtomic}</DisplayString>
</Type>
<Type Name="eastl::variant&lt;*&gt;">
<Intrinsic Name="index" Expression="(int)mIndex"/>
<DisplayString Condition="index() == size_t(-1)">[valueless_by_exception]</DisplayString>
<DisplayString Condition="index() == 0" Optional="true">{{ index=0, value={($T1*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 1" Optional="true">{{ index=1, value={($T2*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 2" Optional="true">{{ index=2, value={($T3*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 3" Optional="true">{{ index=3, value={($T4*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 4" Optional="true">{{ index=4, value={($T5*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 5" Optional="true">{{ index=5, value={($T6*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 6" Optional="true">{{ index=6, value={($T7*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 7" Optional="true">{{ index=7, value={($T8*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 8" Optional="true">{{ index=8, value={($T9*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 9" Optional="true">{{ index=9, value={($T10*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 10" Optional="true">{{ index=10, value={($T11*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 11" Optional="true">{{ index=11, value={($T12*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 12" Optional="true">{{ index=12, value={($T13*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 13" Optional="true">{{ index=13, value={($T14*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 14" Optional="true">{{ index=14, value={($T15*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 15" Optional="true">{{ index=15, value={($T16*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 16" Optional="true">{{ index=16, value={($T17*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 17" Optional="true">{{ index=17, value={($T18*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 18" Optional="true">{{ index=18, value={($T19*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 19" Optional="true">{{ index=19, value={($T20*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 20" Optional="true">{{ index=20, value={($T21*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 21" Optional="true">{{ index=21, value={($T22*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 22" Optional="true">{{ index=22, value={($T23*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 23" Optional="true">{{ index=23, value={($T24*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 24" Optional="true">{{ index=24, value={($T25*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 25" Optional="true">{{ index=25, value={($T26*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 26" Optional="true">{{ index=26, value={($T27*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 27" Optional="true">{{ index=27, value={($T28*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 28" Optional="true">{{ index=28, value={($T29*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 29" Optional="true">{{ index=29, value={($T30*)mStorage.mBuffer.mCharData}}</DisplayString>
<DisplayString Condition="index() == 30" Optional="true">{{ index=30, value={($T31*)mStorage.mBuffer.mCharData}}</DisplayString>
<Expand>
<Item Name="index">index()</Item>
<Item Name="[value]" Condition="index() == 0" Optional="true">($T1*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 1" Optional="true">($T2*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 2" Optional="true">($T3*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 3" Optional="true">($T4*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 4" Optional="true">($T5*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 5" Optional="true">($T6*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 6" Optional="true">($T7*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 7" Optional="true">($T8*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 8" Optional="true">($T9*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 9" Optional="true">($T10*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 10" Optional="true">($T11*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 11" Optional="true">($T12*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 12" Optional="true">($T13*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 13" Optional="true">($T14*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 14" Optional="true">($T15*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 15" Optional="true">($T16*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 16" Optional="true">($T17*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 17" Optional="true">($T18*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 18" Optional="true">($T19*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 19" Optional="true">($T20*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 20" Optional="true">($T21*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 21" Optional="true">($T22*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 22" Optional="true">($T23*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 23" Optional="true">($T24*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 24" Optional="true">($T25*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 25" Optional="true">($T26*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 26" Optional="true">($T27*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 27" Optional="true">($T28*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 28" Optional="true">($T29*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 29" Optional="true">($T30*)mStorage.mBuffer.mCharData</Item>
<Item Name="[value]" Condition="index() == 30" Optional="true">($T31*)mStorage.mBuffer.mCharData</Item>
</Expand>
</Type>
<Type Name="eastl::tuple&lt;&gt;">
<DisplayString IncludeView="noparens"></DisplayString>
<DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString>
<Expand/>
</Type>
<Type Name="eastl::tuple&lt;*&gt;">
<DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue}</DisplayString>
<DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString>
<Expand>
<Item Name="[0]">(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue</Item>
</Expand>
</Type>
<Type Name="eastl::tuple&lt;*,*&gt;">
<DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue}</DisplayString>
<DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString>
<Expand>
<Item Name="[0]">(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[1]">(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue</Item>
</Expand>
</Type>
<Type Name="eastl::tuple&lt;*,*,*&gt;">
<DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue}</DisplayString>
<DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString>
<Expand>
<Item Name="[0]">(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[1]">(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[2]">(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue</Item>
</Expand>
</Type>
<Type Name="eastl::tuple&lt;*,*,*,*&gt;">
<DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;3,$T4,0&gt;*)&amp;mImpl)).mValue}</DisplayString>
<DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString>
<Expand>
<Item Name="[0]">(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[1]">(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[2]">(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[3]">(*((eastl::Internal::TupleLeaf&lt;3,$T4,0&gt;*)&amp;mImpl)).mValue</Item>
</Expand>
</Type>
<Type Name="eastl::tuple&lt;*,*,*,*,*&gt;">
<DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;3,$T4,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;4,$T5,0&gt;*)&amp;mImpl)).mValue}</DisplayString>
<DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString>
<Expand>
<Item Name="[0]">(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[1]">(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[2]">(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[3]">(*((eastl::Internal::TupleLeaf&lt;3,$T4,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[4]">(*((eastl::Internal::TupleLeaf&lt;4,$T5,0&gt;*)&amp;mImpl)).mValue</Item>
</Expand>
</Type>
<Type Name="eastl::tuple&lt;*,*,*,*,*,*&gt;">
<DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;3,$T4,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;4,$T5,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;5,$T6,0&gt;*)&amp;mImpl)).mValue}</DisplayString>
<DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString>
<Expand>
<Item Name="[0]">(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[1]">(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[2]">(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[3]">(*((eastl::Internal::TupleLeaf&lt;3,$T4,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[4]">(*((eastl::Internal::TupleLeaf&lt;4,$T5,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[5]">(*((eastl::Internal::TupleLeaf&lt;5,$T6,0&gt;*)&amp;mImpl)).mValue</Item>
</Expand>
</Type>
<Type Name="eastl::tuple&lt;*,*,*,*,*,*,*&gt;">
<DisplayString IncludeView="noparens">{(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;3,$T4,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;4,$T5,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;5,$T6,0&gt;*)&amp;mImpl)).mValue}, {(*((eastl::Internal::TupleLeaf&lt;6,$T7,0&gt;*)&amp;mImpl)).mValue}</DisplayString>
<DisplayString ExcludeView="noparens">({*this,view(noparens)})</DisplayString>
<Expand>
<Item Name="[0]">(*((eastl::Internal::TupleLeaf&lt;0,$T1,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[1]">(*((eastl::Internal::TupleLeaf&lt;1,$T2,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[2]">(*((eastl::Internal::TupleLeaf&lt;2,$T3,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[3]">(*((eastl::Internal::TupleLeaf&lt;3,$T4,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[4]">(*((eastl::Internal::TupleLeaf&lt;4,$T5,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[5]">(*((eastl::Internal::TupleLeaf&lt;5,$T6,0&gt;*)&amp;mImpl)).mValue</Item>
<Item Name="[6]">(*((eastl::Internal::TupleLeaf&lt;6,$T7,0&gt;*)&amp;mImpl)).mValue</Item>
</Expand>
</Type>
</AutoVisualizer>
+2290
View File
File diff suppressed because it is too large Load Diff
+93
View File
@@ -0,0 +1,93 @@
# EASTL Glossary
This document provides definitions to various terms related to EASTL. Items that are capitalized are items that are used as template parameters.
| | |
|------|------|
| adapter | An adapter is something that encapsulates a component to provide another interface, such as a C++ class which makes a stack out of a list. |
| algorithm | Algorithms are standalone functions which manipulate data which usually but not necessarily comes from a container. Some algorithms change the data while others don't. Examples are reverse, sort, find, and remove. |
| associative container | An associative container is a variable-sized container that supports efficient retrieval of elements (values) based on keys. It supports insertion and removal of elements, but differs from a sequence in that it does not provide a mechanism for inserting an element at a specific position. Associative containers include map, multimap, set, multiset, hash_map, hash_multimap, hash_set, hash_multiset. |
| array | An array is a C++ container which directly implements a C-style fixed array but which adds STL container semantics to it. |
| basic_string | A templated string class which is usually used to store char or wchar_t strings. |
| begin | The function used by all conventional containers to return the first item in the container. |
| BidirectionalIterator | An input iterator which is like ForwardIterator except it can be read in a backward direction as well. |
| BinaryOperation  | A function which takes two arguments and returns a value (which will usually be assigned to a third object). |
| BinaryPredicate | A function which takes two arguments and returns true if some criteria is met (e.g. they are equal). |
| binder1st, binder2nd | These are function objects which convert one function object into another.  In particular, they implement a binary function whereby you can specify one of the arguments.This is a somewhat abstract concept but has its uses. |
| bit vector | A specialized container that acts like vector<bool> but is implemented via one bit per entry. STL vector<bool> is usually implemented as a bit vector but EASTL avoids this in favor of a specific bit vector container. |
| bitset | An extensible yet efficient implementation of bit flags. Not strictly a conventional STL container and not the same thing as vector<bool> or a bit_vector, both of which are formal iterate-able containers. |
| capacity | Refers to the amount of total storage available in an array-based container such as vector, string, and array. Capacity is always >= container size and is > size in order to provide extra space for a container to grow into. |
| const_iterator | An iterator whose iterated items are cannot be modified. A const_iterator is akin to a const pointer such as 'const char*'. |
| container | A container is an object that stores other objects (its elements), and that has methods for accessing its elements. In particular, every type that is a model of container has an associated iterator type that can be used to iterate through the container's elements. |
| copy constructor | A constructor for a type which takes another object of that type as its argument. For a hypothetical Widget class, the copy constructor is of the form Widget(const Widget& src); |
| Compare | A function which takes two arguments and returns the lesser of the two. |
| deque | The name deque is pronounced "deck" and stands for "double-ended queue."<br><br> A deque is very much like a vector: like vector, it is a sequence that supports random access to elements, constant time insertion and removal of elements at the end of the sequence, and linear time insertion and removal of elements in the middle.<br><br> The main way in which deque differs from vector is that deque also supports constant time insertion and removal of elements at the beginning of the sequence. Additionally, deque does not have any member functions analogous to vector's capacity() and reserve(), and does not provide the guarantees on iterator validity that are associated with those member functions. |
| difference_type | The typedef'd type used by all conventional containers and iterators to define the distance between two iterators. It is usually the same thing as the C/C++ ptrdiff_t data type. |
| empty | The function used by all conventional containers to tell if a container has a size of zero. In many cases empty is more efficient than checking for size() == 0. |
| element | An element refers to a member of a container. |
| end | The function used by all conventional containers to return one-past the last item in the container. |
| equal_range | equal_range is a version of binary search: it attempts to find the element value in an ordered range [first, last). The value returned by equal_range is essentially a combination of the values returned by lower_bound and upper_bound: it returns a pair of iterators i and j such that i is the first position where value could be inserted without violating the ordering and j is the last position where value could be inserted without violating the ordering. It follows that every element in the range [i, j) is equivalent to value, and that [i, j) is the largest subrange of [first, last) that has this property. |
| explicit instantiation | Explicit instantiation lets you create an instantiation of a templated class or function without actually using it in your code. Since this is useful when you are creating library files that use templates for distribution, uninstantiated template definitions are not put into object files. An example of the syntax for explicit instantiation is:<br> `template class vector<char>;`<br> `template void min<int>(int, int);`<br> `template void min(int, int);` |
| ForwardIterator | An input iterator which is like InputIterator except it can be reset back to the beginning. |
| Function | A function which takes one argument and applies some operation to the target. |
| function object, functor | A function object or functor is a class that has the function-call operator (<tt>operator()</tt>) defined. |
| Generator | A function which takes no arguments and returns a value (which will usually be assigned to an object). |
| hash_map, hash_multimap, hash_set, hash_multiset | The hash containers are implementations of map, multimap, set, and multiset via a hashtable instead of via a tree. Searches are O(1) (fast) but the container is not sorted. |
| heap | A heap is a data structure which is not necessarily sorted but is organized such that the highest priority item is at the top. A heap is synonymous with a priority queue and has numerous applications in computer science. |
| InputIterator | An input iterator (iterator you read from) which allows reading each element only once and only in a forward direction. |
| intrusive_list, intrusive_hash_map, etc. | Intrusive containers are containers which don't allocate memory but instead use their contained object to manage the container's memory. While list allocates nodes (with mpPrev/mpNext pointers) that contain the list items, intrusive_list doesn't allocate nodes but instead the container items have the mpPrev/mpNext pointers. |
| intrusive_ptr | intrusive_ptr is a smart pointer which doesn't allocate memory but instead uses the contained object to manage lifetime via addref and release functions. |
| iterator | An iterator is the fundamental entity of reading and enumerating values in a container. Much like a pointer can be used to walk through a character array, an iterator is used to walk through a linked list. |
| iterator category | An iterator category defines the functionality the iterator provides. The conventional iterator categories are InputIterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator, and OutputIterator. See the definitions of each of these for more information.Iterator category is synonymous with <span style="font-style: italic;">iterator_tag</span>. |
| iterator_tag | See <span style="font-style: italic;">iterator category</span>. |
| key_type, Key | A Key or key_type is the identifier used by associative (a.k.a. dictionary) containers (e.g. map, hash_map) to identify the type used to index the mapped_type. If you have a dictionary of strings that you access by an integer id, the ids are the keys and the strings are the mapped types. |
| lexicographical compare | A lexicographical compare is a comparison of two containers that compares them element by element, much like the C strcmp function compares two strings. |
| linked_ptr | A linked_ptr is a shared smart pointer which implements object lifetime via a linked list of all linked_ptrs that are referencing the object. linked_ptr, like intrusive_ptr, is a non-memory-allocating alternative to shared_ptr. |
| list | A list is a doubly linked list. It is a sequence that supports both forward and backward traversal, and (amortized) constant time insertion and removal of elements at the beginning or the end, or in the middle. Lists have the important property that insertion and splicing do not invalidate iterators to list elements, and that even removal invalidates only the iterators that point to the elements that are removed. The ordering of iterators may be changed (that is, list<T>::iterator might have a different predecessor or successor after a list operation than it did before), but the iterators themselves will not be invalidated or made to point to different elements unless that invalidation or mutation is explicit. |
| lower_bound | lower_bound is a version of binary search: it attempts to find the element value in an ordered range [first, last). Specifically, it returns the first position where value could be inserted without violating the ordering. |
| map | Map is a sorted associative container that associates objects of type Key with objects of type T. Map is a pair associative container, meaning that its value type is pair<const Key, T>. It is also a unique associative container, meaning that no two elements have the same key. It is implemented with a tree structure. |
| mapped_type | A mapped_type is a typedef used by associative containers to identify the container object which is accessed by a key. If you have a dictionary of strings that you access by an integer id, the ids are the keys and the strings are the mapped types. |
| member template | A member template is a templated function of a templated class. Thus with a member template function there are two levels of templating -- the class and the function. |
| multimap,  | Multimap is a sorted associative container that associates objects of type Key with objects of type T. multimap is a pair associative container, meaning that its value type is pair<const Key, T>. It is also a multiple associative container, meaning that there is no limit on the number of elements with the same key.It is implemented with a tree structure. |
| multiset | Multiset is a sorted associative container that stores objects of type Key. Its value type, as well as its key type, is Key. It is also a multiple associative container, meaning that two or more elements may be identical. It is implemented with a tree structure. |
| node | A node is a little holder class used by many containers to hold the contained items. A linked-list, for example, defines a node which has three members: mpPrev, mpNext, and T (the contained object). |
| npos | npos is used by the string class to identify a non-existent index. Some string functions return npos to indicate that the function failed. |
| rel_ops | rel_ops refers to "relational operators" and is a set of templated functions which provide operator!= for classes that  have only operator== and provide operator > for classes that have only operator <, etc. Unfortunately, rel_ops have a habit of polluting the global operator space and creating conflicts. They must be used with discretion. |
| reverse_iterator | A reverse_iterator is an iterator which wraps a bidirectional or random access iterator and allows the iterator to be read in reverse direction. The difference between using reverse_iterators and just decrementing regular iterators is that reverse_iterators use operator++ to move backwards and thus work in any algorithm that calls ++ to move through a container. |
| OutputIterator | An output iterator (iterator you write to) which allows writing each element only once in only in a forward direction. |
| POD | POD means Plain Old Data. It refers to C++ classes which act like built-in types and C structs. These are useful to distinguish because some algorithms can be made more efficient when they can detect that they are working with PODs instead of regular classes.  |
| Predicate | A function which takes one argument returns true if the argument meets some criteria. |
| priority_queue | A priority_queue is an adapter container which implements a heap via a random access container such as vector or deque. |
| queue | A queue is an adapter container which implements a FIFO (first-in, first-out) container with which you can add items to the back and get items from the front. |
| RandomAccessIterator | An input iterator which can be addressed like an array. It is a superset of all other input iterators. |
| red-black tree | A red-black tree is a binary tree which has the property of being always balanced. The colors red and black are somewhat arbitrarily named monikers for nodes used to measure the balance of the tree. Red-black trees are considered the best all-around data structure for sorted containers. |
| scalar | A scalar is a data type which is implemented via a numerical value. In C++ this means integers, floating point values, enumerations, and pointers.  |
| scoped_ptr | A scoped_ptr is a smart pointer which is the same as C++ auto_ptr except that it cannot be copied. |
| set | Set is a sorted associative container that stores objects of type Key. Its value type, as well as its key type, is Key. It is also a unique associative container, meaning that no two elements are the same.It is implemented with a tree structure. |
| sequence | A sequence is a variable-sized container whose elements are arranged in a strict linear (though not necessarily contiguous) order. It supports insertion and removal of elements. Sequence containers include vector, deque, array, list, slist. |
| size | All conventional containers have a size member function which returns the count of elements in the container. The efficiency of the size function differs between containers. |
| size_type | The type that a container uses to define its size and counts. This is similar to the C/C++ size_t type but may be specialized for the container. |
| skip list | A skip-list is a type of container which is an alternative to a binary tree for finding data. |
| shared_ptr | A shared_ptr is a smart pointer which allows multiple references (via multiple shared_ptrs) to the same object. When the last shared_ptr goes away, the pointer is freed. shared_ptr is implemented via a shared count between all instances. |
| slist | An slist is like a list but is singly-linked instead of doubly-linked. It can only be iterated in a forward-direction. |
| smart pointer | Smart pointer is a term that identifies a family of utility classes which store pointers and free them when the class instance goes out of scope. Examples of smart pointers are shared_ptr, linked_ptr, intrusive_ptr, and scoped_ptr. |
| splice | Splicing refers to the moving of a subsequence of one Sequence into another Sequence. |
| stack | A stack is a adapter container which implements LIFO (last-in, first, out) access via another container such as a list or deque. |
| STL | Standard Template Library.  |
| StrictWeakOrdering | A BinaryPredicate that compares two objects, returning true if the first precedes the second. Like Compare but has additional requirements. Used for sorting routines.<br><br> This predicate must satisfy the standard mathematical definition of a strict weak ordering. A StrictWeakOrdering has to behave the way that "less than" behaves: if a is less than b then b is not less than a, if a is less than b and b is less than c then a is less than c, and so on. |
| string | See basic_string. |
| T | T is the template parameter name used by most containers to identify the contained element type.  |
| template parameter | A template parameter is the templated type used to define a template function or class. In the declaration 'template <typename T> class vector{ },'  T is a template parameter. |
| template specialization | A template specialization is a custom version of a template which overrides the default version and provides alternative functionality, often for the purpose of providing improved or specialized functionality. |
| treap | A tree-like structure implemented via a heap. This is an alternative to a binary tree (e.g. red-black tree), skip-list, and sorted array as a mechanism for a fast-access sorted container. |
| type traits | Type traits are properties of types. If you have a templated type T and you want to know if it is a pointer, you would use the is_pointer type trait. If you want to know if the type is a POD, you would use the is_pod type trait. Type traits are very useful for allowing the implementation of optimized generic algorithms and for asserting that types have properties expected by the function or class contract. For example, you can use type_traits to tell if a type can be copied via memcpy instead of a slower element-by-element copy. |
| typename | Typename is a C++ keyword used in templated function implementations which identifies to the compiler that the following expression is a type and not a value. It is used extensively in EASTL, particularly in the algorithms. |
| UnaryOperation | A function which takes one argument and returns a value (which will usually be assigned to second object). |
| upper_bound | upper_bound is a version of binary search: it attempts to find the element value in an ordered range [first, last). Specifically, it returns the last position where value could be inserted without violating the ordering. |
| value_type, Value | A value_type is a typedef used by all containers to identify the elements they contain. In most cases value_type is simply the same thing as the user-supplied T template parameter. The primary exception is the associative containers whereby value_type is the pair of key_type and mapped_type. |
| vector | A vector is a Sequence that supports random access to elements, constant time insertion and removal of elements at the end, and linear time insertion and removal of elements at the beginning or in the middle. The number of elements in a vector may vary dynamically; memory management is automatic. Vector is the simplest of the container classes, and in many cases the most efficient. |
| vector_map, vector_multimap, vector_set, vector_multiset | These are containers that implement the functionality of map, multimap, set, and multiset via a vector or deque instead of a tree. They use less memory and find items faster, but are slower to modify and modification invalidates iterators. |
| weak_ptr | A weak_ptr is an adjunct to shared_ptr which doesn't increment the reference on the contained object but can safely tell you if the object still exists and access it if so. It has uses in preventing circular references in shared_ptrs. |
----------------------------------------------
End of document
+134
View File
@@ -0,0 +1,134 @@
# EASTL Gotchas
There are some cases where the EASTL design results in "gotchas" or behavior that isn't necessarily what the new user would expect. These are all situations in which this behavior may be undesirable. One might ask, "Why not change EASTL to make these gotchas go away?" The answer is that in each case making the gotchas go away would either be impossible or would compromise the functionality of the library.
## Summary
The descriptions here are intentionally terse; this is to make them easier to visually scan.
1. [map::operator[] can create elements.](#mapoperator-can-create-elements)
2. [char* converts to string silently.](#char-converts-to-string-silently)
3. [char* is compared by ptr and not by contents.](#char-is-compared-by-ptr-and-not-by-contents)
4. [Iterators can be invalidated by container mutations.](#iterators-can-be-invalidated-by-container-mutations)
5. [Vector resizing may cause ctor/dtor cascades.](#vector-resizing-may-cause-ctordtor-cascades)
6. [Vector and string insert/push_back/resize can reallocate.](#vector-and-string-insertpush_backresize-can-reallocate)
7. [Deriving from containers may not work.](#deriving-from-containers-may-not-work)
8. [set::iterator is const_iterator.](#setiterator-is-const_iterator)
9. [Inserting elements means copying by value.](#inserting-elements-means-copying-by-value)
10. [Containers of pointers can leak if you aren't careful.](#containers-of-pointers-can-leak-if-you-arent-careful)
11. [Containers of auto_ptrs can crash.](#containers-of-auto_ptrs-can-crash)
12. [Remove algorithms don't actually remove elements.](#remove-algorithms-dont-actually-remove-elements)
13. [list::size() is O(n).](#listsize-is-on)
14. [vector and deque::size() may incur integer division.](#vector-and-dequesize-may-incur-integer-division)
15. [Be careful making custom Compare functions.](#be-careful-making-custom-compare-functions)
16. [Comparisons involving floating point are dangerous.](#comparisons-involving-floating-point-are-dangerous)
17. [Writing beyond string::size and vector::size is dangerous.](#writing-beyond-stringsize-and-vectorsize-is-dangerous)
18. [Container operator=() doesn't copy allocators.](#container-operator-doesnt-copy-allocators)
## Detail
### map::operator[] can create elements.
By design, map operator[] creates a value for you if it isn't already present. The reason for this is that the alternative behavior would be to throw an exception, and such behavior isn't desirable. The resolution is to simply use the map::find function instead of operator[].
### char* converts to string silently.
The string class has a non-explicit constructor that takes char* as an argument. Thus if you pass char* to a function that takes a string object, a temporary string will be created. In some cases this is undesirable behavior but the user may not notice it right away, as the compiler gives no warnings. The reason that the string constructor from char* is not declared explicit is that doing so would prevent the user from expressions such as: string s = "hello". In this example, no temporary string object is created, but the syntax is not possible if the char* constructor is declared explicit. Thus a decision to make the string char* constructor explicit involves tradeoffs.
There is an EASTL configuration option called EASTL_STRING_EXPLICIT which makes the string char* ctor explicit and avoids the behaviour described above.
### char* is compared by ptr and not by contents.
If you have a set of strings declared as set<char*>, the find function will compare via the pointer value and not the string contents. The workaround is to make a set of string objects or, better, to supply a custom string comparison function to the set. The workaround is not to declare a global operator< for type char*, as that could cause other systems to break.
### Iterators can be invalidated by container mutations
With some containers, modifications of them may invalidate iterators into them. With other containers, modifications of them only an iterator if the modification involves the element that iterator refers to. Containers in the former category include vector, deque, basic_string (string), vector_map, vector_multimap, vector_set, and vector_multiset. Containers in the latter category include list, slist, map, multimap, multiset, all hash containers, and all intrusive containers.
### Vector resizing may cause ctor/dtor cascades.
If elements are inserted into a vector in middle of the sequence, the elements from the insertion point to the end will be copied upward. This will necessarily cause a series of element constructions and destructions as the elements are copied upward. Similarly, if an element is appended to a vector but the vector capacity is exhausted and needs to be reallocated, the entire vector will undergo a construction and destruction pass as the values are copied to the new storage. This issue exists for deque as well, though to a lesser degree. For vector, the resolution is to reserve enough space in your vector to prevent such reallocation. For deque the resolution is to set its subarray size to enough to prevent such reallocation. Another solution that can often be used is to take advantage of the has_trivial_relocate type trait, which can cause such moves to happen via memcpy instead of via ctor/dtor calls. If your class can be safely memcpy'd, you can use EASTL_DECLARE_TRIVIAL_RELOCATE to tell the compiler it can be memcpy'd. Note that built-in scalars (e.g. int) already are automatically memcpy'd by EASTL.
### Vector and string insert/push_back/resize can reallocate.
If you create an empty vector and use push_back to insert 100 elements, the vector will reallocate itself at least three or four times during the operation. This can be an undesirable thing. The best thing to do if possible is to reserve the size you will need up front in the vector constructor or before you add any elements.
### Deriving from containers may not work.
EASTL containers are not designed with the guarantee that they can be arbitrarily subclassed. This is by design and is done for performance reasons, as such guarantees would likely involve making containers use virtual functions. However, some types of subclassing can be successful and EASTL does such subclassing internally to its advantage. The primary problem with subclassing results when a parent class function calls a function that the user wants to override. The parent class cannot see the overridden function and silent unpredictable behavior will likely occur. If your derived container acts strictly as a wrapper for the container then you will likely be able to successfully subclass it.
### set::iterator is const_iterator.
The reason this is so is that a set is an ordered container and changing the value referred to by an iterator could make the set be out of order. Thus, set and multiset iterators are always const_iterators. If you need to change the value and are sure the change will not alter the container order, use const_cast or declare mutable member variables for your contained object. This resolution is the one blessed by the C++ standardization committee. This issue is addressed in more detail in the EASTL FAQ.
### Inserting elements means copying by value.
When you insert an element into a (non-intrusive) container, the container makes a copy of the element. There is no provision to take over ownership of an object from the user. The exception to this is of course when you use a container of pointers instead of a container of values. See the entry below regarding containers of pointers. Intrusive containers (e.g. intrusive_list) do in fact take over the user-provided value, and thus provide another advantage over regular containers in addition to avoiding memory allocation.
### Containers of pointers can leak if you aren't careful.
Containers of points don't know or care about the possibility that the pointer may have been allocated and need to be freed. Thus if you erase such elements from a container they are not freed. The resolution is to manually free the pointers when removing them or to instead use a container of smart pointers (shared smart pointers, in particular). This issue is addressed in more detail in the EASTL FAQ and the auto_ptr-related entry below.
### Containers of auto_ptrs can crash
We suggested above that the user can use a container of smart pointers to automatically manage contained pointers. However, you don't want to use auto_ptr, as auto_ptrs cannot be safely assigned to each other; doing so results in a stale pointer and most likely a crash.
### Remove algorithms don't actually remove elements.
Algorithms such as remove, remove_if, remove_heap, and unique do not erase elements from the sequences they work on. Instead, they return an iterator to the new end of the sequence and the user must call erase with that iterator in order to actually remove the elements from the container. This behavior exists because algorithms work on sequences via iterators and don't know how to work with containers. Only the container can know how to best erase its own elements. In each case, the documentation for the algorithm reminds the user of this behavior. Similarly, the copy algorithm copies elements from one sequence to another and doesn't modify the size of the destination sequence. So the destination must hold at least as many items as the source, and if it holds more items, you may want to erase the items at the end after the copy.
### list::size() is O(n).
By this we mean that calling size() on a list will iterate the list and add the size as it goes. Thus, getting the size of a list is not a fast operation, as it requires traversing the list and counting the nodes. We could make list::size() be fast by having a member mSize variable. There are reasons for having such functionality and reasons for not having such functionality. We currently choose to not have a member mSize variable as it would add four bytes to the class, add processing to functions such as insert and erase, and would only serve to improve the size function, but no other function. The alternative argument is that the C++ standard states that std::list should be an O(1) operation (i.e. have a member size variable), most C++ standard library list implementations do so, the size is but an integer which is quick to update, and many users expect to have a fast size function. All of this applies to slist and intrusive_list as well.
Note that EASTL's config.h file has an option in it to cause list and slist to cache their size with an mSize variable and thus make size() O(1). This option is disabled by default.
### vector and deque::size() may incur integer division.
Some containers (vector and deque in particular) calculate their size by pointer subtraction. For example, the implementation of vector::size() is 'return mpEnd - mpBegin'. This looks like a harmless subtraction, but if the size of the contained object is not an even power of two then the compiler will likely need to do an integer division to calculate the value of the subtracted pointers. One might suggest that vector use mpBegin and mnSize as member variables instead of mpBegin and mpEnd, but that would incur costs in other vector operations. The suggested workaround is to iterate a vector instead of using a for loop and operator[] and for those cases where you do use a for loop and operator[], get the size once at the beginning of the loop instead of repeatedly during the condition test.
### Be careful making custom Compare functions.
A Compare function compares two values and returns true if the first is less than the second. This is easy to understand for integers and strings, but harder to get right for more complex structures. Many a time have people decided to come up with a fancy mechanism for comparing values and made mistakes. The FAQ has a couple entries related to this. See http://blogs.msdn.com/oldnewthing/archive/2003/10/23/55408.aspx for a story about how this can go wrong by being overly clever.
### Comparisons involving floating point are dangerous.
Floating point comparisons between two values that are very nearly equal can result in inconsistent results. Similarly, floating point comparisons between NaN values will always generate inconsistent results, as NaNs by definition always compare as non-equal. You thus need to be careful when using comparison functions that work with floating point values. Conversions to integral values may help the problem, but not necessarily.
### Writing beyond string::size and vector::size is dangerous.
A trick that often comes to mind when working with strings is to set the string capacity to some maximum value, strcpy data into it, and then resize the string when done. This can be done with EASTL, but only if you resize the string to the maximum value and not reserve the string to the maximum value. The reason is that when you resize a string from size (n) to size (n + count), the count characters are zeroed and overwrite the characters that you strcpyd.
The following code is broken:
```cpp
string mDataDir;
mDataDir.reserve(kMaxPathLength); // reserve
strcpy(&mDataDir[0], "blah/blah/blah");
mDataDir.resize(strlen(&mDataDir[0])); // Overwrites your blah/... with 00000...
```
This following code is OK:
```cpp
string mDataDir;
mDataDir.resize(kMaxPathLength); // resize
strcpy(&mDataDir[0], "blah/blah/blah");
mDataDir.resize(strlen(&mDataDir[0]));
```
### Container operator=() doesn't copy allocators.
EASTL container assignment (e.g. vector::operator=(const vector&)) doesn't copy the allocator. There are good and bad reasons for doing this, but that's how it acts. So you need to beware that you need to assign the allocator separately or make a container subclass which overrides opeator=() and does this.
----------------------------------------------
End of document
+18
View File
@@ -0,0 +1,18 @@
# EASTL Introduction
EASTL stands for Electronic Arts Standard Template Library. It is a C++ template library of containers, algorithms, and iterators useful for runtime and tool development across multiple platforms. It is a fairly extensive and robust implementation of such a library and has an emphasis on high performance above all other considerations.
## Intended Audience
This is a short document intended to provide a basic introduction to EASTL for those new to the concept of EASTL or STL. If you are familiar with the C++ STL or have worked with other templated container/algorithm libraries, you probably don't need to read this. If you have no familiarity with C++ templates at all, then you probably will need more than this document to get you up to speed. In this case you need to understand that templates, when used properly, are powerful vehicles for the ease of creation of optimized C++ code. A description of C++ templates is outside the scope of this documentation, but there is plenty of such documentation on the Internet. See the EASTL FAQ.html document for links to information related to learning templates and STL.
## EASTL Modules
EASTL consists primarily of containers, algorithms, and iterators. An example of a container is a linked list, while an example of an algorithm is a sort function; iterators are the entities of traversal for containers and algorithms. EASTL containers a fairly large number of containers and algorithms, each of which is a very clean, efficient, and unit-tested implementation. We can say with some confidence that you are not likely to find better implementations of these (commercial or otherwise), as these are the result of years of wisdom and diligent work. For a detailed list of EASTL modules, see EASTL Modules.html.
## EASTL Suitability
What uses are EASTL suitable for? Essentially any situation in tools and shipping applications where the functionality of EASTL is useful. Modern compilers are capable of producing good code with templates and many people are using them in both current generation and future generation applications on multiple platforms from embedded systems to servers and mainframes.
----------------------------------------------
End of document
+195
View File
@@ -0,0 +1,195 @@
# EASTL Maintenance
## Introduction
The purpose of this document is to provide some necessary background for anybody who might do work on EASTL. Writing generic templated systems like EASTL can be surprisingly tricky. There are numerous details of the C++ language that you need to understand which don't usually come into play during the day-to-day C++ coding that many people do. It is easy to make a change to some function that seems proper and works for your test case but either violates the design expectations or simply breaks under other circumstances.
It may be useful to start with an example. Here we provide an implementation of the count algorithm which is seems simple enough. Except it is wrong and while it will compile in some cases it won't compile in others:
```cpp
int count(InputIterator first, InputIterator last, const T& value)
{
    int result = 0;
    for(; first < last; ++first){
        if(*first == value)
            ++result;
    }
    return result;
}
```
The problem is with the comparison 'first < last'. The count algorithm takes an InputIterator and operator< is not guaranteed to exist for any given InputIterator (and indeed while operator< exists for vector::iterator, it doesn't exist for list::iterator). The comparison in the above algorithm must instead be implemented as 'first != last'. If we were working with a RandomAccessIterator then 'first < last' would be valid.
In the following sections we cover various topics of interest regarding the development and maintentance of EASTL. Unfortunately, this document can't cover every aspect of EASTL maintenance issues, but at least it should give you a sense of the kinds of issues.
## C++ Language Standard
First and foremost, you need to be familiar with the C++ standard. In particular, the sections of the standard related to containers, algorithms, and iterators are of prime significance. We'll talk about some of this in more detail below. Similarly, a strong understanding of the basic data types is required. What is the difference between ptrdiff_t and intptr_t; unsigned int and size_t; char and signed char?
In addition to the C++ language standard, you'll want to be familiar with the C++ Defect Report. This is a continuously updated document which lists flaws in the original C++ language specification and the current thinking as the resolutions of those flaws. You will notice various references to the Defect Report in EASTL source code.
Additionally, you will want to be familiar with the C++ Technical Report 1 (as of this writing there is only one). This document is the evolving addendum to the C++ standard based on both the Defect Report and based on desired additions to the C++ language and standard library.
Additionally, you will probably want to have some familiarity with Boost. It also helps to keep an eye on comp.std.c++ Usenet discussions. However, watch out for what people say on Usenet. They tend to defend GCC, Unix, std STL, and C++ to a sometimes unreasonable degree. Many discussions ignore performance implications and concentrate only on correctness and sometimes academic correctness above usability.
## Language Use
Macros are (almost) not allowed in EASTL. A prime directive of EASTL is to be easier to read by users and most of the time macros are an impedence to this. So we avoid macros at all costs, even if it ends up making our development and maintenance more difficult. That being said, you will notice that the EASTL config.h file uses macros to control various options. This is an exception to the rule; when we talk about not using macros, we mean with the EASTL implementation itself.
EASTL assumes a compliant and intelligent C++ compiler, and thus all language facilities are usable. However, we nevertheless choose to stay away from some language functionality. The primary language features we avoid are:
* RTTI (run-time-type-identification) (this is deemed too costly)
* Template export (few compilers support this)
* Exception specifications (most compilers ignore them)
Use of per-platform or per-compiler code should be avoided when possible but where there is a significant advantage to be gained it can and indeed should be used. An example of this is the GCC __builtin_expect feature, which allows the user to give the compiler a hint about whether an expression is true or false. This allows for the generation of code that executes faster due to more intelligent branch prediction.
## Prime Directives
The implementation of EASTL is guided foremost by the following directives which are listed in order of importance.
1. Efficiency (speed and memory usage)
2. Correctness (doesn't have bugs)
3. Portability (works on all required platforms with minimal specialized code)
4. Readability (code is legible and comments are present and useful)
Note that unlike commercial STL implementations which must put correctness above all, we put a higher value on efficiency. As a result, some functionality may have some usage limitation that is not present in other similar systems but which allows for more efficient operation, especially on the platforms of significance to us.
Portability is significant, but not critical. Yes, EASTL must compile and run on all platforms that we will ship games for. But we don't take that to mean under all compilers that could be conceivably used for such platforms. For example, Microsoft VC6 can be used to compile Windows programs, but VC6's C++ support is too weak for EASTL and so you simply cannot use EASTL under VC6.
Readability is something that EASTL achieves better than many other templated libraries, particularly Microsoft STL and STLPort. We make every attempt to make EASTL code clean and sensible. Sometimes our need to provide optimizations (particularly related to type_traits and iterator types) results in less simple code, but efficiency happens to be our prime directive and so it overrides all other considerations.
## Coding Conventions
Here we provide a list of coding conventions to follow when maintaining or adding to EASTL, starting with the three language use items from above:
* No RTTI use.
* No use of exception specifications (e.g. appending the 'throw' declarator to a function).
* No use of exception handling itself except where explicitly required by the implementation (e.g. vector::at).
* Exception use needs to savvy to EASTL_EXCEPTIONS_ENABLED.
* No use of macros (outside of config.h). Macros make things more difficult for the user.
* No use of static or global variables.
* No use of global new, delete, malloc, or free. All memory must be user-specifyable via an Allocator parameter (default-specified or explicitly specified).
* Containers use protected member data and functions as opposed to private. This is because doing so allows subclasses to extend the container without the creation of intermediary functions. Recall from our [prime directives](#Prime_Directives) above that performance and simplicity overrule all.
* No use of multithreading primitives. 
* No use of the export keyword.
* We don't have a rule about C-style casts vs. C++ static_cast<>, etc. We would always use static_cast except that debuggers can't evaluate them and so in practice they can get in the way of debugging and tracing. However, if the cast is one that users don't tend to need to view in a debugger, C++ casts are preferred.
* No external library dependencies whatsoever, including standard STL. EASTL is dependent on only EABase and the C++ compiler. 
* All code must be const-correct. This isn't just for readability -- compilation can fail unless const-ness is used correctly everywhere. 
* Algorithms do not refer to containers; they refer only to iterators.
* Algorithms in general do not allocate memory. If such a situation arises, there should be a version of the algorithm which allows the user to provide the allocator.
* No inferior implementations. No facility should be added to EASTL unless it is of professional quality.
* The maintainer should emulate the EASTL style of code layout, regardless of the maintainer's personal preferences. When in Rome, do as the Romans do. EASTL uses 4 spaces for indents, which is how the large majority of code within EA is written.
* No major changes should be done without consulting a peer group.
## Compiler Issues
Historically, templates are the feature of C++ that has given C++ compilers the most fits. We are still working with compilers that don't completely and properly support templates. Luckily, most compilers are now good enough to handle what EASTL requires. Nevertheless, there are precautions we must take.
It turns out that the biggest problem in writing portable EASTL code is that VC++ allows you to make illegal statements which are not allowed by other compilers. For example, VC++ will allow you to neglect using the typename keyword in template references, whereas GCC (especially 3.4+) requires it.
In order to feel comfortable that your EASTL code is C++ correct and is portable, you must do at least these two things:
* Test under at least VS2005, GCC 3.4+, GCC 4.4+, EDG, and clang.
* Test all functions that you write, as compilers will often skip the compilation of a template function if it isn't used.
The two biggest issues to watch out for are 'typename' and a concept called "dependent names". In both cases VC++ will accept non-conforming syntax whereas most other compilers will not. Whenever you reference a templated type (and not a templated value) in a template, you need to prefix it by 'typename'. Whenever your class function refers to a base class member (data or function), you need to refer to it by "this->", "base_type::", or by placing a "using" statement in your class to declare that you will be referencing the given base class member.
## Iterator Issues
The most important thing to understand about iterators is the concept of iterator types and their designated properties. In particular, we need to understand the difference between InputIterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator, and OutputIterator. These differences dictate both how we implement our algorithms and how we implement our optimizations. Please read the C++ standard for a reasonably well-implemented description of these iterator types.
Here's an example from EASTL/algorithm.h which demonstrates how we use iterator types to optimize the reverse algorithm based on the kind of iterator passed to it:
```cpp
template <class BidirectionalIterator>
inline void reverse_impl(BidirectionalIterator first, BidirectionalIterator last, bidirectional_iterator_tag)
{
for(; (first != last) && (first != --last); ++first) // We are not allowed to use operator <, <=, >, >= with
iter_swap(first, last); // a generic (bidirectional or otherwise) iterator.
}
template <typename RandomAccessIterator>
inline void reverse_impl(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag)
{
for(; first < --last; ++first) // With a random access iterator, we can use operator < to more efficiently implement
iter_swap(first, last); // this algorithm. A generic iterator doesn't necessarily have an operator < defined.
}
template <class BidirectionalIterator>
inline void reverse(BidirectionalIterator first, BidirectionalIterator last)
{
typedef typename iterator_traits<BidirectionalIterator>::iterator_category IC;
reverse_impl(first, last, IC());
}
```
## Exception Handling
You will notice that EASTL uses try/catch in some places (particularly in containers) and uses the EASTL_EXCEPTIONS_ENABLED define. For starters, any EASTL code that uses try/catch should always be wrapped within #if EASTL_EXCEPTIONS_ENABLED (note: #if, not #ifdef).
This is simple enough, but what you may be wondering is how it is that EASTL decides to use try/catch for some sections of code and not for others. EASTL follows the C++ standard library conventions with respect to exception handling, and you will see similar exception handling in standard STL. The code that you need to wrap in try/catch is code that can throw a C++ exception (not to be confused with CPU exception) and needs to have something unwound (or fixed) as a result. The important thing is that the container be in a valid state after encountering such exceptions. In general the kinds of things that require such try/catch are:
* Memory allocation failures (which throw exceptions)
* Constructor exceptions
Take a look at the cases in EASTL where try/catch is used and see what it is doing.
## Type Traits
EASTL provides a facility called type_traits which is very similar to the type_traits being proposed by the C++ TR1 (see above). type_traits are useful because they tell you about properties of types at compile time. This allows you to do things such as assert that a data type is scalar or that a data type is const. The way we put them to use in EASTL is to take advantage of them to implement different pathways for functions based on types. For example, we can copy a contiguous array of scalars much faster via memcpy than we can via a for loop, though we could not safely employ the for loop for a non-trivial C++ class.
As mentioned in the GeneralOptimizations section below, EASTL should take advantage of type_traits information to the extent possible to achive maximum effiiciency.
## General Optimizations
One of the primary goals of EASTL is to achieve the highest possible efficiency. In cases where EASTL functionality overlaps standard C++ STL functionality, standard STL implementations provided by compiler vendors are a benchmark upon which EASTL strives to beat. Indeed EASTL is more efficient than all other current STL implementations (with some exception in the case of some Metrowerks STL facilities). Here we list some of the things to look for when considering optimization of EASTL code These items can be considered general optimization suggestions for any code, but this particular list applies to EASTL:
* Take advantage of type_traits to the extent possible (e.g. to use memcpy to move data instead of a for loop when possible).
* Take advantage of iterator types to the extent possible.
* Take advantage of the compiler's expectation that if statements are expected to evaluate as true and for loop conditions are expected to evaluate as false.
* Make inline-friendly code. This often means avoiding temporaries to the extent possible.
* Minimize branching (i.e. minimize 'if' statements). Where branching is used, make it so that 'if' statements execute as true.
* Use EASTL_LIKELY/EASTL_UNLIKELY to give branch hints to the compiler when you are confident it will be beneficial.
* Use restricted pointers (EABase's EA_RESTRICT or various compiler-specific versions of __restrict).
* Compare unsigned values to < max instead of comparing signed values to >= 0 && < max.
* Employ power of 2 integer math instead of math with any kind of integer.
* Use template specialization where possible to implement improved functionality.
* Avoid function calls when the call does something trivial. This improves debug build speed (which matters) and sometimes release build speed as well, though sometimes makes the code intent less clear. A comment next to the code saying what call it is replacing makes the intent clear without sacrificing performance.
## Unit Tests
Writing robust templated containers and algorithms is difficult or impossible without a heavy unit test suite in place. EASTL has a pretty extensive set of unit tests for all containers and algorithms. While the successful automated unit testing of shipping application programs may be a difficult thing to pull off, unit testing of libraries such as this is of huge importance and cannot be understated.
* When making a new unit test, start by copying one of the existing unit tests and follow its conventions.
* Test containers of both scalars and classes.
* Test algorithms on both container iterators (e.g. vector.begin()) and pointer iterators (e.g. int*).
* Make sure that algorithm or container member functions which take iterators work with the type of iterator they claim to (InputIterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator). 
* Test for const-correctness. If a user is allowed to modify something that is supposed to be const, silent errors can go undetected.
* Make sure that unit tests cover all functions and all pathways of the tested code. This means that in writing the unit test you need to look at the source code to understand all the pathways.
* Consider using a random number generator (one is provided in the test library) to do 'monkey' testing whereby unexpected input is given to a module being tested. When doing so, make sure you seed the generator in a way that problems can be reproduced.
* While we avoid macros in EASTL user code, macros to assist in unit tests aren't considered a problem. However, consider that a number of macros could be replaced by templated functions and thus be easier to work with.
* Unit tests don't need to be efficient; feel free to take up all the CPU power and time you need to test a module sufficiently.
* EASTL containers are not thread-safe, by design. Thus there is no need to do multithreading tests as long as you stay away from the usage of static and global variables.
* Unit tests must succeed with no memory leaks and of course no memory corruption. The heap system should be configured to test for this, and heap validation functions are available to the unit tests while in the middle of runs.
## Things to Keep in Mind
* When referring to EASTL functions and types from EASTL code, make sure to preface the type with the EASTL namespace. If you don't do this you can get collisions due to the compiler not knowing if it should use the EASTL namespace or the namespace of the templated type for the function or type.
* Newly constructed empty containers do no memory allocation. Some STL and other container libraries allocate an initial node from the class memory allocator. EASTL containers by design never do this. If a container needs an initial node, that node should be made part of the container itself or be a static empty node object.
* Empty containers (new or otherwise) contain no constructed objects, including those that might be in an 'end' node. Similarly, no user object (e.g. of type T) should be constructed unless required by the design and unless documented in the cotainer/algorithm contract. 
* When creating a new container class, it's best to copy from an existing similar class to the extent possible. This helps keep the library consistent and resolves subtle problems that can happen in the construction of containers.
* Be very careful about tweaking the code. It's easy to think (for example) that a > could be switch to a >= where instead it is a big deal. Just about every line of code in EASTL has been thought through and has a purpose. Unit tests may or may not currently test every bit of EASTL, so you can't necessarily rely on them to give you 100% confidence in changes. If you are not sure about something, contact the original author and he will tell you for sure.
* Algorithm templates always work with iterators and not containers. A given container may of course implement an optimized form or an algorithm itself.
* Make sure everything is heavily unit tested. If somebody finds a bug, fix the bug and make a unit test to make sure the bug doesn't happen again.
* It's easy to get iterator categories confused or forgotten while implementing algorithms and containers.
* Watch out for the strictness of GCC 3.4+. There is a bit of syntax — especially related to templates — that other compilers accept but GCC 3.4+ will not.
* Don't forget to update the config.h EASTL_VERSION define before publishing.
* The vector and string classes define iterator to be T*. We want to always leave this so — at least in release builds — as this gives some algorithms an advantage that optimizers cannot get around.
----------------------------------------------
End of document
+89
View File
@@ -0,0 +1,89 @@
# EASTL Modules
## Introduction
We provide here a list of all top-level modules present or planned for future presence in EASTL. In some cases (e.g. algorithm), the module consists of many smaller submodules which are not described in detail here. In those cases you should consult the source code for those modules or consult the detailed documentation for those modules. This document is a high level overview and not a detailed document.
## Module List
| Module | Description |
|------|------|
| config | Configuration header. Allows for changing some compile-time options. |
| slist <br>fixed_slist | Singly-linked list.<br> fixed_slist is a version which is implemented via a fixed block of contiguous memory.|
| list<br> fixed_list | Doubly-linked list. |
| intrusive_list<br> intrusive_slist | List whereby the contained item provides the node implementation. |
| array | Wrapper for a C-style array which extends it to act like an STL container. |
| vector<br> fixed_vector | Resizable array container.
| vector_set<br> vector_multiset | Set implemented via a vector instead of a tree. Speed and memory use is improved but resizing is slower. |
| vector_map<br> vector_multimap | Map implemented via a vector instead of a tree. Speed and memory use is improved but resizing is slower. |
| deque | Double-ended queue, but also with random access. Acts like a vector but insertions and removals are efficient. |
| bit_vector | Implements a vector of bool, but the actual storage is done with one bit per bool. Not the same thing as a bitset. |
| bitset | Implements an efficient arbitrarily-sized bitfield. Note that this is not strictly the same thing as a vector of bool (bit_vector), as it is optimized to act like an arbitrary set of flags and not to be a generic container which can be iterated, inserted, removed, etc. |
| set<br> multiset<br> fixed_set<br> fixed_multiset | A set is a sorted unique collection, multiset is sorted but non-unique collection. |
| map<br> multimap<br> fixed_map<br> fixed_multimap | A map is a sorted associative collection implemented via a tree. It is also known as dictionary. |
| hash_map<br> hash_multimap<br> fixed_hash_map<br> fixed_hash_multimap | Map implemented via a hash table. |
| intrusive_hash_map<br> intrusive_hash_multimap<br> intrusive_hash_set<br> intrusive_hash_multiset | hash_map whereby the contained item provides the node implementation, much like intrusive_list. |
| hash_set<br> hash_multiset<br> fixed_hash_set<br> fixed_hash_map | Set implemented via a hash table.
| basic_string<br> fixed_string<br> fixed_substring | basic_string is a character string/array.<br> fixed_substring is a string which is a reference to a range within another string or character array.<br> cow_string is a string which implements copy-on-write. |
| algorithm | min/max, find, binary_search, random_shuffle, reverse, etc. |
| sort | Sorting functionality, including functionality not in STL. quick_sort, heap_sort, merge_sort, shell_sort, insertion_sort, etc. |
| numeric | Numeric algorithms: accumulate, inner_product, partial_sum, adjacent_difference, etc. |
| heap | Heap structure functionality: make_heap, push_heap, pop_heap, sort_heap, is_heap, remove_heap, etc. |
| stack | Adapts any container into a stack. |
| queue | Adapts any container into a queue. |
| priority_queue | Implements a conventional priority queue via a heap structure. |
| type_traits | Type information, useful for writing optimized and robust code. Also used for implementing optimized containers and algorithms. |
| utility | pair, make_pair, rel_ops, etc. |
| functional | Function objects. |
| iterator | Iteration for containers and algorithms. |
| smart_ptr | Smart pointers: shared_ptr, shared_array, weak_ptr, scoped_ptr, scoped_array, linked_ptr, linked_array, intrusive_ptr. |
 
## Module Behaviour
The overhead sizes listed here refer to an optimized release build; debug builds may add some additional overhead. Some of the overhead sizes may be off by a little bit (usually at most 4 bytes). This is because the values reported here are those that refer to when EASTL's container optimizations have been complete. These optimizations may not have been completed as you are reading this.
| Container |Stores | Container Overhead (32 bit) | Container Overhead (64 bit) | Node Overhead (32 bit) | Node Overhead (64 bit) | Iterator category | size() efficiency | operator[] efficiency | Insert efficiency | Erase via Iterator efficiency | Find efficiency | Sort efficiency |
|------|------|------|------|------|------|------|------|------|------|------|------|------|
| slist | T | 8 | 16 | 4 | 8 | f | n | - | 1 | 1 | n | n+ |
| list | T | 12 | 24 | 8 | 16 | b | n | - | 1 | 1 | n | n log(n) |
| intrusive_slist | T | 4 | 8 | 4 | 8 | f | n | - | 1 | 1 | 1 | n+ |
| intrusive_list | T | 8 | 16 | 8 | 16 | b | n | - | 1 | 1 | 1 | n log(n) |
| array | T | 0 | 0 | 0 | 0 | r | 1 | 1 | - | - | n | n log(n) |
| vector | T | 16 | 32 | 0 | 0 | r | 1 | 1 | 1 at end, else n | 1 at end, else n | n | n log(n) |
| vector_set | T | 16 | 32 | 0 | 0 | r | 1 | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 |
| vector_multiset | T | 16 | 32 | 0 | 0 | r | 1 | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 |
| vector_map | Key, T | 16 | 32 | 0 | 0 | r | 1 | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 |
| vector_multimap | Key, T | 16 | 32 | 0 | 0 | r | 1 | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 |
| deque | T | 44 | 84 | 0 | 0 | r | 1 | 1 | 1 at begin or end, else n / 2 | 1 at begin or end, else n / 2 | n | n log(n) |
| bit_vector | bool | 8 | 16 | 0 | 0 | r | 1 | 1 | 1 at end, else n | 1 at end, else n | n | n log(n) |
| string (all types) | T | 16 | 32 | 0 | 0 | r | 1 | 1 | 1 at end, else n | 1 at end, else n | n | n log(n) |
| set | T | 24 | 44 | 16 | 28 | b | 1 | - | log(n) | log(n) | log(n) | 1 |
| multiset | T | 24 | 44 | 16 | 28 | b | 1 | - | log(n) | log(n) | log(n) | 1 |
| map | Key, T | 24 | 44 | 16 | 28 | b | 1 | log(n) | log(n) | log(n) | log(n) | 1 |
| multimap | Key, T | 24 | 44 | 16 | 28 | b | 1 | - | log(n) | log(n) | log(n) | 1 |
| hash_set | T | 16 | 20 | 4 | 8 | b | 1 | - | 1 | 1 | 1 | - |
| hash_multiset | T | 16 | 20 | 4 | 8 | b | 1 | - | 1 | 1 | 1 | - |
| hash_map | Key, T | 16 | 20 | 4 | 8 | b | 1 | - | 1 | 1 | 1 | - |
| hash_multimap | Key, T | 16 | 20 | 4 | 8 | b | 1 | - | 1 | 1 | 1 | - |
| intrusive_hash_set | T | 16 | 20 | 4 | 8 | b | 1 | - | 1 | 1 | 1 | - |
| intrusive_hash_multiset | T | 16 | 20 | 4 | 8 | b | 1 | - | 1 | 1 | 1 | - |
| intrusive_hash_map | T <small>(Key == T)</small> | 16 | 20 | 4 | 8 | b | 1 | - | 1 | 1 | 1 | - |
| intrusive_hash_multimap | T <small>(Key == T) </small> | 16 | 20 | 4 | 8 | b | 1 | - | 1 | 1 | 1 | - |
* \- means that the operation does not exist.
* 1 means amortized constant time. Also known as O(1)
* n means time proportional to the container size. Also known as O(n)
* log(n) means time proportional to the natural logarithm of the container size. Also known as O(log(n))
* n log(n) means time proportional to log(n) times the size of the container. Also known as O(n log(n))
* n+ means that the time is at least n, and possibly higher.
* Iterator meanings are: f = forward iterator; b = bidirectional iterator, r = random iterator.
* Overhead indicates approximate per-element overhead memory required in bytes. Overhead doesn't include possible additional overhead that may be imposed by the memory heap used to allocate nodes. General heaps tend to have between 4 and 16 bytes of overhead per allocation, depending on the heap.
* Some overhead values are dependent on the structure alignment characteristics in effect. The values reported here are those that would be in effect for a system that requires pointers to be aligned on boundaries of their size and allocations with a minimum of 4 bytes (thus one byte values get rounded up to 4).
* Some overhead values are dependent on the size_type used by containers. We assume a size_type of 4 bytes, even for 64 bit machines, as this is the EASTL default.
* Inserting at the end of a vector may cause the vector to be resized; resizing a vector is O(n). However, the amortized time complexity for vector insertions at the end is constant.
* Sort assumes the usage of the best possible sort for a large container of random data. Some sort algorithms (e.g. quick_sort) require random access iterators and so the sorting of some containers requires a different sort algorithm. We do not include bucket or radix sorts, as they are always O(n).
* Some containers (e.g. deque, hash*) have unusual data structures that make per-container and per-node overhead calculations not quite account for all memory.
----------------------------------------------
End of document
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+424
View File
@@ -0,0 +1,424 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>EASTL Design</title>
<meta content="text/html; charset=us-ascii" http-equiv="content-type">
<meta name="author" content="Paul Pedriana">
<meta name="description" content="Discusses various design aspects of EASTL.">
<link type="text/css" rel="stylesheet" href="EASTLDoc.css">
</head>
<body>
<h1>EASTL Design</h1>
<h2> Introduction</h2>
<p>EASTL (EA Standard Template Library) is designed to be a template library which encompasses and extends the
functionality of standard C++ STL while improving it in various ways useful to game development. Much of EASTL's design
is identical to standard STL, as the large majority of the STL is well-designed for many uses. The primary areas where
EASTL deviates from standard STL implementations are essentially the following:</p>
<ul>
<li>EASTL has a simplified and more flexible custom allocation scheme.</li>
<li>EASTL has significantly easier to read code.</li>
<li>EASTL has extension containers and algorithms.</li>
<li>EASTL has optimizations designed for game development.</li>
</ul>
<p>Of the above items, the only one which is an incompatible difference with STL is the case of memory allocation. The
method for defining a custom allocator for EASTL is slightly different than that of standard STL, though they are 90%
similar. The 10% difference, however, is what makes EASTL generally easier and more powerful to work with than standard
STL. Containers without custom allocators act identically between EASTL and standard STL.</p>
<h2>Motivations</h2>
<p>Our motifications for making EASTL drive the design of EASTL. As identified in the EASTL RFC (Request for Comment), the
primary reasons for implementing a custom version of the STL are:
</p>
<ul>
<li><span class="458151900-03082005"><font><font>Some STL implementations (especially Microsoft STL) have inferior
performance characteristics that make them unsuitable for game development. EASTL is faster than all existing STL
implementations.</font></font></span></li>
<li>The STL is sometimes hard to debug, as most STL implementations use cryptic variable names and unusual data
structures.</li>
<li>STL allocators are sometimes painful to work with, as they have many requirements and cannot be modified once bound
to a container.</li>
<li>The STL includes excess functionality that can lead to larger code than desirable. It's not very easy to tell
programmers they shouldn't use that functionality.</li>
<li>The STL is implemented with very deep function calls. This results is unacceptable performance in non-optimized
builds and sometimes in optimized builds as well.</li>
<li>The STL doesn't support alignment of contained objects.</li>
<li>STL containers won't let you insert an entry into a container without supplying an entry to copy from. This can be
inefficient.</li>
<li>Useful STL extensions (e.g. slist, hash_map, shared_ptr) found in existing STL implementations such as STLPort are
not portable because they don't exist in other versions of STL or aren't consistent between STL versions.<br></li>
<li>The STL lacks useful extensions that game programmers find useful (e.g. intrusive_list) but which could be best
optimized in a portable STL environment.</li>
<li>The STL has specifications that limit our ability to use it efficiently. For example, STL vectors are not
guaranteed to use contiguous memory and so cannot be safely used as an array.</li>
<li>The STL puts an emphasis on correctness before performance, whereas sometimes you can get significant performance
gains by making things less academcially pure.</li>
<li>STL containers have private implementations that don't allow you to work with their data in a portable way, yet
sometimes this is an important thing to be able to do (e.g. node pools).</li>
<li>All existing versions of STL allocate memory in empty versions of at least some of their containers. This is not
ideal and prevents optimizations such as container memory resets that can greatly increase performance in some
situations.</li>
<li>The STL is slow to compile, as most modern STL implementations are very large.<br></li>
<li>There are legal issues that make it hard for us to freely use portable STL implementations such as STLPort.</li>
<li>We have no say in the design and implementation of the STL and so are unable to change it to work for our
needs.</li>
</ul>
<h2>Prime Directives</h2>
<p>The implementation of EASTL is guided foremost by the
following directives which are listed in order of importance.</p>
<ol>
<li>Efficiency (speed and memory usage)</li>
<li>Correctness</li>
<li>Portability</li>
<li>Readability</li>
</ol>
<p>Note that unlike commercial STL implementations which must put correctness above all, we put a higher value on
efficiency. As a result, some functionality may have some usage limitation that is not present in other similar systems
but which allows for more efficient operation, especially on the platforms of significance to us.</p>
<p>Portability is significant, but not critical. Yes, EASTL must compile and run on all platforms that we will ship games
for. But we don't take that to mean under all compilers that could be conceivably used for such platforms. For example,
Microsoft VC6 can be used to compile Windows programs, but VC6's C++ support is too weak for EASTL and so you simply
cannot use EASTL under VC6.</p>
<p>Readability is something that EASTL achieves better than many other templated libraries, particularly Microsoft STL and
STLPort. We make every attempt to make EASTL code clean and sensible. Sometimes our need to provide optimizations
(particularly related to type_traits and iterator types) results in less simple code, but efficiency happens to be our
prime directive and so it overrides all other considerations.</p>
<h2> Thread Safety</h2>
<p>It's not simple enough to simply say that EASTL is thread-safe or thread-unsafe. However, we can say that with respect
to thread safety that EASTL does the right thing.</p>
<p>Individual EASTL containers are not thread-safe. That is,&nbsp;access to an instance of a container from multiple
threads at the same time is unsafe if any of those accesses are modifying operations. A given container can be read
from multiple threads simultaneously as well as any other standalone data structure. If a user wants to be able to have
modifying access an instance of a container from multiple threads, it is up to the user to ensure that proper thread
synchronization occurs. This usually means using a mutex.</p>
<p>EASTL classes other than containers are the same as containers with respect to thread safety. EASTL functions (e.g.
algorithms) are inherently thread-safe as they have no instance data and operate entirely on the stack. As of this
writing, no EASTL function allocates memory and thus doesn't bring thread safety issues via that means.</p>
<p>The user may well need to be concerned about thread safety with respect to memory allocation. If the user modifies
containers from multiple threads, then allocators are going to be accessed from multiple threads. If an allocator is
shared across multiple container instances (of the same type of container or not), then mutexes (as discussed above)
the user uses to protect access to indivudual instances will not suffice to provide thread safety for allocators used
across multiple instances. The conventional solution here is to use a mutex within the allocator if it is exected to be
used by multiple threads.</p>
<p>EASTL&nbsp;uses neither static nor global variables and thus there are no inter-instance dependencies that would make
thread safety difficult for the user to implement.</p>
<h2> Container Design</h2>
<p>All EASTL containers follow a set of consistent conventions. Here we define the prototypical container which has the
minimal functionality that all (non-adapter) containers must have. Some containers (e.g. stack) are explicitly adapter
containers and thus wrap or inherit the properties of the wrapped container in a way that is implementation
specific.<br>
</p>
<div class="code-example" style="margin-left: 40px;"><small><span style="font-family: Courier New;">template &lt;class T, class Allocator =
EASTLAllocator&gt;<br>
class container<br>
{<br>
public:<br>
&nbsp; &nbsp; typedef container&lt;T, Allocator&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this_type;<br>
&nbsp;&nbsp;&nbsp; typedef
T&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; value_type;<br>
&nbsp;&nbsp;&nbsp; typedef T*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; pointer;<br>
&nbsp;&nbsp;&nbsp; typedef const T*&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp;const_pointer;<br>
&nbsp;&nbsp;&nbsp;&nbsp;typedef
T&amp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;reference;<br>
&nbsp;&nbsp;&nbsp;&nbsp;typedef const
T&amp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const_reference;<br>
&nbsp;&nbsp;&nbsp;&nbsp;typedef
ptrdiff_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;difference_type;<br>
&nbsp;&nbsp;&nbsp;&nbsp;typedef
impl_defined&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size_type;<br>
&nbsp;&nbsp;&nbsp; typedef impl-defined&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; iterator;<br>
&nbsp;&nbsp;&nbsp; typedef impl-defined&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; const_iterator;<br>
&nbsp;&nbsp;&nbsp; typedef reverse_iterator&lt;iterator&gt; &nbsp; &nbsp; &nbsp; &nbsp; reverse_iterator;<br>
&nbsp;&nbsp;&nbsp; typedef reverse_iterator&lt;const_iterator&gt; &nbsp; reverse_const_iterator;<br>
&nbsp;&nbsp;&nbsp; typedef Allocator&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; allocator_type;<br>
<br>
public:<br>
&nbsp;&nbsp;&nbsp; container(</span></small><small><span style="font-family: Courier New;">const</span></small>
<small><span style="font-family: Courier New;">allocator_type&amp; allocator = allocator_type());<br>
&nbsp;&nbsp;&nbsp; container(const</span></small> <small><span style=
"font-family: Courier New;">this_type</span></small><small><span style="font-family: Courier New;">&amp;
x</span></small><small><span style="font-family: Courier New;">);<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;</span></small><small><span style=
"font-family: Courier New;">this_type</span></small><small><span style="font-family: Courier New;">&amp;
operator=(</span></small><small><span style="font-family: Courier New;">this_type</span></small><small><span style=
"font-family: Courier New;">&amp; x);<br>
&nbsp; &nbsp; void swap(</span></small><small><span style=
"font-family: Courier New;">this_type</span></small><small><span style="font-family: Courier New;">&amp; x);<br>
&nbsp;&nbsp;&nbsp; void reset();<br>
<br>
&nbsp;&nbsp;&nbsp; allocator_type&amp; get_allocator();<br>
&nbsp;&nbsp;&nbsp; void &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;set_allocator(allocator_type&amp; allocator);<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;iterator&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin();<br>
&nbsp;&nbsp;&nbsp;&nbsp;const_iterator begin() const;<br>
&nbsp;&nbsp;&nbsp;&nbsp;iterator&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end();<br>
&nbsp;&nbsp;&nbsp;&nbsp;const_iterator end() const;<br>
<br>
&nbsp;&nbsp;&nbsp; bool validate() const;<br></span></small> <small><span style=
"font-family: Courier New;">&nbsp;&nbsp;&nbsp; int&nbsp; validate_iterator(const_iterator i)
const;<br></span></small><br>
<small><span style="font-family: Courier New;">protected:<br>
&nbsp;&nbsp;&nbsp; allocator_type mAllocator;<br>
};<br>
<br>
template &lt;class T,</span></small> <small><span style="font-family: Courier New;">class
Allocator</span></small><small><span style="font-family: Courier New;">&gt;<br>
bool operator==(const container&lt;T, Allocator&gt;&amp; a, const container&lt;T,</span></small> <small><span style=
"font-family: Courier New;">Allocator</span></small><small><span style="font-family: Courier New;">&gt;&amp; b);<br>
<br>
template &lt;class T,</span></small> <small><span style="font-family: Courier New;">class
Allocator</span></small><small><span style="font-family: Courier New;">&gt;<br>
bool operator!=(const container&lt;T,</span></small> <small><span style=
"font-family: Courier New;">Allocator</span></small><small><span style="font-family: Courier New;">&gt;&amp; a, const
container&lt;T,</span></small> <small><span style=
"font-family: Courier New;">Allocator</span></small><small><span style="font-family: Courier New;">&gt;&amp;
b);</span></small></div>
<br>
Notes:
<ul>
<li>Swapped containers do not swap their allocators.</li>
<li>Newly constructed empty containers do no memory allocation. Some STL and other container libraries allocate an
initial node from the class memory allocator. EASTL containers by design never do this. If a container needs an initial
node, that node should be made part of the container itself or be a static empty node object.</li>
<li>Empty containers (new or otherwise) contain no constructed objects, including those that might be in an 'end' node.
Similarly, no user object (e.g. of type T) should be constructed unless required by the design and unless documented in
the cotainer/algorithm contract.&nbsp;</li>
<li>The reset function is a special extension function which unilaterally resets the container to an empty state
without freeing the memory of the contained objects. This is useful for very quickly tearing down a container built
into scratch memory. No memory is allocated by reset, and the container has no allocatedmemory after the reset is
executed.</li>
<li>The validate and validate_iterator functions provide explicit container and iterator validation. EASTL provides an option to do implicit automatic iterator and container validation, but full validation (which can be potentially extensive) has too much of a performance cost to execute implicitly, even in a debug build. So EASTL provides these explicit functions which can be called by the user at the appropriate time and in optimized builds as well as debug builds. </li>
</ul>
<h2>Allocator Design</h2>
<p>The most significant difference between EASTL and standard C++ STL is that standard STL containers are templated on an
allocator class with the interface defined in std::allocator. std::allocator is defined in the C++ standard as
this:<br>
</p>
<div class="code-example" style="margin-left: 40px;"><small><span style="font-family: Courier New;">// Standard C++ allocator<br>
<br>
template &lt;class T&gt;<br>
class allocator</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">{</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">public:</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; typedef size_t &nbsp; &nbsp;size_type;</span><br style=
"font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; typedef ptrdiff_t difference_type;</span><br style=
"font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; typedef T* &nbsp; &nbsp; &nbsp;&nbsp; pointer;</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; typedef const T* &nbsp;const_pointer;</span><br style=
"font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; typedef T&amp; &nbsp; &nbsp; &nbsp;
&nbsp;reference;</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; typedef const
T&amp;&nbsp;&nbsp;const_reference;</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; typedef T &nbsp; &nbsp; &nbsp; &nbsp; value_type;</span><br style="font-family: Courier New;">
<br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; template &lt;class U&gt;<br>
&nbsp; &nbsp;&nbsp;struct rebind { typedef allocator&lt;U&gt; other; };</span><br style="font-family: Courier New;">
<br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; allocator() throw();</span><br style=
"font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; allocator(const allocator&amp;) throw();</span><br style=
"font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; template &lt;class U&gt;<br>
&nbsp; &nbsp; allocator(const allocator&lt;U&gt;&amp;) throw();<br>
<br style="font-family: Courier New;">
</span> <span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp;~allocator()
throw();<br>
<br style="font-family: Courier New;">
</span> <span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; pointer &nbsp;
&nbsp; &nbsp; address(reference x) const;</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; const_pointer address(const_reference x)
const;</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; pointer &nbsp; &nbsp; &nbsp; allocate(size_type, typename
allocator&lt;void&gt;::const_pointer hint = 0);</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; void &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;deallocate(pointer p,
size_type n);</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; size_type &nbsp; &nbsp; max_size() const
throw();</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; void &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;construct(pointer p,
const T&amp; val);</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">&nbsp;&nbsp;&nbsp; void &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;destroy(pointer
p);</span><br style="font-family: Courier New;">
<span style="font-family: Courier New;">};</span></small></div>
<p> Each STL container needs to have an allocator templated on container type T associated with it. The problem with this
is that allocators for containers are defined at the class level and not the instance level. This makes it painful to
define custom allocators for containers and adds to code bloat. Also, it turns out that the containers don't actually
use allocator&lt;T&gt; but instead use allocator&lt;T&gt;::rebind&lt;U&gt;::other. Lastly, you cannot access this
allocator after the container is constructed. There are some good academic reasons why the C++ standard works this way,
but it results in a lot of unnecessary pain and makes concepts like memory tracking much harder to implement.</p>
<p>What EASTL does is use a more familiar memory allocation pattern whereby there is only one allocator class interface
and it is used by all containers. Additionally EASTL containers let you access their allocators and query them, name
them, change them, etc.</p>
<p>EASTL has chosen to make allocators not be copied between containers during container swap and assign operations. This
means that if container A swaps its contents with container B, both containers retain their original allocators.
Similarly, assigning container A to container B causes container B to retain its original allocator. Containers that
are equivalent should report so via operator==; EASTL will do a smart swap if allocators are equal, and a brute-force
swap otherwise.<br>
</p>
<div class="code-example" style="margin-left: 40px;"><small><span style="font-family: Courier New;">// EASTL allocator<br>
<br>
class allocator<br>
{<br>
public:<br>
&nbsp;&nbsp;&nbsp; allocator(const char* pName = NULL);<br>
<br>
&nbsp;&nbsp;&nbsp; void* allocate(size_t n, int flags = 0);<br>
&nbsp;&nbsp;&nbsp; void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0);<br>
&nbsp;&nbsp;&nbsp; void&nbsp; deallocate(void* p, size_t n);<br>
<br>
&nbsp;&nbsp;&nbsp; const char* get_name() const;<br>
&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; set_name(const char* pName);<br>
};<br>
<br>
allocator* GetDefaultAllocator();</span></small></div>
<h2>Fixed Size Container Design</h2>
<p>EASTL supplies a set of&nbsp;fixed-size containers that the user can use, though the user can also implement their own
versions. So in addition to class list there is class fixed_list. The fixed_list class implements a linked list via a
fixed-size pool of contiguous memory which has no space overhead (unlike with a regular heap), doesn't cause
fragmentation, and allocates very quickly.</p>
<p>EASTL implements fixed containers via subclasses of regular containers which set the regular container's allocator to
point to themselves. Thus the implementation for fixed_list is very tiny and consists of little more
than&nbsp;constructor and allocator functions. This design has some advantages but has one small disadvantage. The
primary advantages are primarily that code bloat is reduced and that the implementation is simple and the user can
easily extend it. The primary disadvantage is that the parent list class ends up with a pointer to itself and thus has
4 bytes that could arguably be saved if system was designed differently. That different design would be to make the
list class have a policy template parameter which specifies that it is a fixed pool container. EASTL chose not to
follow the policy design because it would complicate the implementation, make it harder for the user to extend the
container, and would potentially waste more memory due to code bloat than it would save due to the 4 byte savings it
achieves in container instances.</p>
<h2>Algorithm Design</h2>
<p>EASTL algorithms very much follow the philosophy of standard C++ algorithms, as this philosophy is sound and efficient.
One of the primary aspects of algorithms is that they work on iterators and not containers. You will note for example
that the find algorithm takes a first and last iterator as arguments and not a container. This has two primary
benefits: it allows the user to specify a subrange of the container to search within and it allows the user to apply
the find algorithm to sequences that aren't containers (e.g. a C array).</p>
<p>EASTL algorithms are optimized at least as well as the best STL algorithms found in commercial libraries and are
significantly optimized over the algorithms that come with the first-party STLs that come with compilers. Most significantly, EASTL algorithms take advantage of type traits of contained classes and
take advantage of iterator types to optimize code generation. For example, if you resize an array of integers (or other "pod" type), EASTL will detect that this can be done with a memcpy instead of a slow object-by-object move as would
Micrsoft STL.</p>
<p>The optimizations found in EASTL algorithms and the supporting code in EASTL type traits consistts of some fairly
tricky advanced C++ and while it is fairly easy to read, it requires a C++ expert (language lawyer, really) to
implement confidently. The result of this is that it takes more effort to develop and maintain EASTL than it would to
maintain a simpler library. However, the performance advantages have been deemed worth the tradeoff.</p>
<h2>Smart Pointer Design</h2>
<p>EASTL implements the following smart pointer types:</p>
<ul>
<li>shared_ptr</li>
<li>shared_array</li>
<li>weak_ptr</li>
<li>instrusive_ptr</li>
<li>scoped_ptr</li>
<li>scoped_array</li>
<li>linked_ptr</li>
<li>linked_array</li>
</ul>
All but linked_ptr/linked_array are well-known smart pointers from the Boost library. The behaviour of these smart
pointers is very similar to those from Boost with two exceptions:
<ul>
<li>EASTL smart pointers allow you to assign an allocator to them.</li>
<li>EASTL shared_ptr implements deletion via a templated parameter instead of a dynamically allocated&nbsp;virtual
member object interface.</li>
</ul>
<p>With respect to assigning an allocator, this gives EASTL more control over memory allocation and tracking, as Boost
smart pointers unilaterally use global operator new to allocate memory from the global heap.</p>
<p>With respect to shared_ptr deletion, EASTL's current design of using a templated parameter is questionable, but does
have some reason. The advantage is that EASTL avoids a heap allocation, avoids virtual function calls, and avoids
templated class proliferation. The disadvantage is that EASTL shared_ptr containers which hold void pointers can't call
the destructors of their contained objects unless the user manually specifies a custom deleter template parameter. This
is case whereby EASTL is more efficient but less safe. We can revisit this topic in the future if it becomes an
issue.</p>
<h2>list::size is O(n)</h2>
<p>As of this writing, EASTL has three linked list classes: list, slist, and intrusive_list. In each of these classes, the
size of the list is not cached in a member size variable. The result of this is that getting the size of a list is not
a fast operation, as it requires traversing the list and counting the nodes. We could make the list::size function be
fast by having a member mSize variable which tracks the size as we insert and delete items. There are reasons for
having such functionality and reasons for not having such functionality. We currently choose to not have a member mSize
variable as it would add four bytes to the class, add a tiny amount of processing to functions such as insert and
erase, and would only serve to improve the size function, but no others. In the case of intrusive_list, it would do
additional harm. The alternative&nbsp;argument is that the C++ standard states that std::list should be an O(1)
operation (i.e. have a member size variable), that many C++ standard library list&nbsp;implementations do so, that the
size is but an integer which is quick to update, and that many users expect to have a fast size function. In the final
analysis, we are developing a library for game development and performance is paramount, so we choose to not cache the
list size. The user can always implement a size cache himself.</p>
<h2>basic_string doesn't use copy-on-write</h2>
<p>The primary benefit of CoW is that it allows for the sharing of string data between two string objects. Thus if you say
this:</p>
<p class="code-example"> string a("hello");<br style=
"font-family: Courier New;">
string b(a);</p>
<p>the "hello" will be shared between a and b. If you then say this:</p>
<p class="code-example"> a = "world";</p>
<p>then <span style="font-family: Courier New;">a</span> will release its reference to "hello" and leave b with the only
reference to it. Normally this functionality is accomplished via reference counting and with atomic operations or
mutexes.</p>
<p> The C++ standard does not say anything about basic_string and CoW. However, for a basic_string implementation to be
standards-conforming, a number of issues arise which dictate some things about how one would have to implement a CoW
string. The discussion of these issues will not be rehashed here, as you can read the references below for better
detail than can be provided in the&nbsp;space we have here. However, we can say that the C++ standard is sensible
and&nbsp;that anything we try to do here to allow for an efficient CoW implementation would result in a generally
unacceptable string interface.</p>
<p>The disadvantages of CoW strings are:</p>
<ul>
<li>A reference count needs to exist with the string, which increases string memory usage.</li>
<li>With thread safety, atomic operations and mutex locks are expensive, especially&nbsp;on weaker memory systems such
as console gaming platforms.</li>
<li>All non-const string accessor functions need to do a sharing check the the first such check needs to detach the
string. Similarly, all string assignments need to do a sharing check as well. If you access the string before doing an
assignment, the assignment doesn't result in a shared string, because the string has already been detached.</li>
<li>String sharing doesn't happen the large majority of the time. In some cases,&nbsp;the total sum of the reference
count memory can exceed any memory savings gained by the strings that share representations.&nbsp;</li>
</ul>
<p>The addition of a cow_string class is under consideration for EASTL. There are conceivably some systems which have
string usage patterns which would benefit from CoW sharing. Such functionality is best saved for a separate
string&nbsp;implementation so that the other string uses aren't penalized.</p>
<p>This is a good starting HTML reference on the topic:</p>
<blockquote>
<p>
<a href=
"http://www.gotw.ca/publications/optimizations.htm">http://www.gotw.ca/publications/optimizations.htm</a></p>
</blockquote>
<p>Here is a well-known Usenet discussion on the topic:</p>
<blockquote>
<p><a href=
"http://groups-beta.google.com/group/comp.lang.c++.moderated/browse_thread/thread/3dc6af5198d0bf7/886c8642cb06e03d">http://groups-beta.google.com/group/comp.lang.c++.moderated/browse_thread/thread/3dc6af5198d0bf7/886c8642cb06e03d</a></p>
</blockquote>
<hr style="width: 100%; height: 2px;">
End of document<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</body>
</html>
File diff suppressed because it is too large Load Diff
+490
View File
@@ -0,0 +1,490 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>EASTL Glossary</title>
<meta content="text/html; charset=us-ascii" http-equiv="content-type">
<meta name="author" content="Paul Pedriana">
<meta name="description" content="Definitions of common terms related to EASTL.">
<link type="text/css" rel="stylesheet" href="EASTLDoc.css">
</head>
<body>
<h1>EASTL Glossary</h1>
<p>This document provides definitions to various terms related to EASTL. Items that are capitalized are items that are
used as template parameters.</p>
<table style="width: 100%; text-align: left;" border="1" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td>adapter</td>
<td>An adapter is something that encapsulates a component to provide another interface, such as a C++ class which makes
a stack out of a list.</td>
</tr>
<tr>
<td style="width: 150px; vertical-align: top;">algorithm<br></td>
<td style="vertical-align: top;">Algorithms are standalone functions which manipulate data which usually but not
necessarily comes from a container. Some algorithms change the data while others don't. Examples are reverse, sort,
find, and remove.<br></td>
</tr>
<tr>
<td>associative container</td>
<td>An associative container is a variable-sized container that supports efficient retrieval of elements (values) based
on keys. It supports insertion and removal of elements, but differs from a sequence in that it does not provide a
mechanism for inserting an element at a specific position. Associative containers include map, multimap, set, multiset,
hash_map, hash_multimap, hash_set, hash_multiset.</td>
</tr>
<tr>
<td>array</td>
<td>An array is a C++ container which directly implements a C-style fixed array but which adds STL container semantics
to it.</td>
</tr>
<tr>
<td>basic_string</td>
<td>A templated string class which is usually used to store char or wchar_t strings.</td>
</tr>
<tr>
<td>begin</td>
<td>The function used by all conventional containers to return the first item in the container.</td>
</tr>
<tr>
<td>BidirectionalIterator</td>
<td>An input iterator which is like ForwardIterator except it can be read in a backward direction as well.</td>
</tr>
<tr>
<td>BinaryOperation&nbsp;</td>
<td>A function which takes two arguments and returns a value (which will usually be assigned to a third object).</td>
</tr>
<tr>
<td>BinaryPredicate</td>
<td>A function which takes two arguments and returns true if some criteria is met (e.g. they are equal).</td>
</tr>
<tr>
<td>binder1st, binder2nd</td>
<td>These are function objects which convert one function object into another. &nbsp;In particular, they implement a
binary function whereby you can specify one of the arguments.This is a somewhat abstract concept but has its uses.</td>
</tr>
<tr>
<td>bit vector</td>
<td>A specialized container that acts like vector&lt;bool&gt; but is implemented via one bit per entry. STL
vector&lt;bool&gt; is usually implemented as a bit vector but EASTL avoids this in favor of a specific bit vector
container.</td>
</tr>
<tr>
<td>bitset</td>
<td>An extensible yet efficient implementation of bit flags. Not strictly a conventional STL container and not the same
thing as vector&lt;bool&gt; or a bit_vector, both of which are formal iterate-able containers.</td>
</tr>
<tr>
<td>capacity</td>
<td>Refers to the amount of total storage available in an array-based container such as vector, string, and array.
Capacity is always &gt;= container size and is &gt; size in order to provide extra space for a container to grow
into.</td>
</tr>
<tr>
<td>const_iterator</td>
<td>An iterator whose iterated items are cannot be modified. A const_iterator is akin to a const pointer such as 'const
char*'.</td>
</tr>
<tr>
<td>container</td>
<td>A container is an object that stores other objects (its elements), and that has methods for accessing its elements.
In particular, every type that is a model of container has an associated iterator type that can be used to iterate
through the container's elements.</td>
</tr>
<tr>
<td>copy constructor</td>
<td>A constructor for a type which takes another object of that type as its argument. For a hypothetical Widget class,
the copy constructor is of the form Widget(const Widget&amp; src);</td>
</tr>
<tr>
<td>Compare</td>
<td>A function which takes two arguments and returns the lesser of the two.</td>
</tr>
<tr>
<td>deque</td>
<td>The name deque is pronounced "deck" and stands for "double-ended queue."<br>
<br>
A deque is very much like a vector: like vector, it is a sequence that supports random access to elements, constant
time insertion and removal of elements at the end of the sequence, and linear time insertion and removal of elements in
the middle.<br>
<br>
The main way in which deque differs from vector is that deque also supports constant time insertion and removal of
elements at the beginning of the sequence. Additionally, deque does not have any member functions analogous to vector's
capacity() and reserve(), and does not provide the guarantees on iterator validity that are associated with those
member functions.</td>
</tr>
<tr>
<td>difference_type</td>
<td>The typedef'd type used by all conventional containers and iterators to define the distance between two iterators.
It is usually the same thing as the C/C++ ptrdiff_t data type.</td>
</tr>
<tr>
<td>empty</td>
<td>The function used by all conventional containers to tell if a container has a size of zero. In many cases empty is
more efficient than checking for size() == 0.</td>
</tr>
<tr>
<td>element</td>
<td>An element refers to a member of a container.</td>
</tr>
<tr>
<td>end</td>
<td>The function used by all conventional containers to return one-past the last item in the container.</td>
</tr>
<tr>
<td>equal_range</td>
<td>equal_range is a version of binary search: it attempts to find the element value in an ordered range [first, last).
The value returned by equal_range is essentially a combination of the values returned by lower_bound and upper_bound:
it returns a pair of iterators i and j such that i is the first position where value could be inserted without
violating the ordering and j is the last position where value could be inserted without violating the ordering. It
follows that every element in the range [i, j) is equivalent to value, and that [i, j) is the largest subrange of
[first, last) that has this property.</td>
</tr>
<tr>
<td>explicit instantiation</td>
<td>Explicit instantiation lets you create an instantiation of a templated class or function without actually using it
in your code. Since this is useful when you are creating library files that use templates for distribution,
uninstantiated template definitions are not put into object files.&nbsp;An example of the syntax for explicit
instantiation is:<br>
<small><span style="font-family: Courier New;">&nbsp; &nbsp;</span></small> <small><span style=
"font-family: Courier New;">template class vector&lt;char&gt;;<br>
&nbsp; &nbsp; template void min&lt;int&gt;(int, int);<br>
&nbsp; &nbsp; template void min(int, int);</span></small></td>
</tr>
<tr>
<td>ForwardIterator</td>
<td>An input iterator which is like InputIterator except it can be reset back to the beginning.</td>
</tr>
<tr>
<td>Function</td>
<td>A function which takes one argument and applies some operation to the target.</td>
</tr>
<tr>
<td>function object, functor</td>
<td>A function object or functor is a&nbsp;class that has the function-call operator (<tt>operator()</tt>)
defined.</td>
</tr>
<tr>
<td>Generator</td>
<td>A function which takes no arguments and returns a value (which will usually be assigned to an object).</td>
</tr>
<tr>
<td>hash_map, hash_multimap, hash_set, hash_multiset</td>
<td>The hash containers are implementations of map, multimap, set, and multiset via a hashtable instead of via a tree.
Searches are O(1) (fast) but the container is not sorted.</td>
</tr>
<tr>
<td>heap</td>
<td>A heap is a data structure which is not necessarily sorted but is organized such that the highest priority item is
at the top. A heap is synonymous with a priority queue and has numerous applications in computer science.</td>
</tr>
<tr>
<td>InputIterator</td>
<td>An input iterator (iterator you read from) which allows reading each element only once and only in a forward
direction.</td>
</tr>
<tr>
<td>intrusive_list, intrusive_hash_map, etc.</td>
<td>Intrusive containers are containers which don't allocate memory but instead use their contained object to manage
the container's memory. While list allocates nodes (with mpPrev/mpNext pointers) that contain the list items,
intrusive_list doesn't allocate nodes but instead the container items have the mpPrev/mpNext pointers.</td>
</tr>
<tr>
<td>intrusive_ptr</td>
<td>intrusive_ptr is a smart pointer which doesn't allocate memory but instead uses the contained object to manage
lifetime via addref and release functions.</td>
</tr>
<tr>
<td>iterator</td>
<td>An iterator is the fundamental entity of reading and enumerating values in a&nbsp;container. Much like a pointer
can be used to walk through a character array, an iterator is used to walk through a linked list.</td>
</tr>
<tr>
<td>iterator category</td>
<td>An iterator category defines the functionality the iterator provides. The conventional iterator categories are
InputIterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator, and OutputIterator. See the definitions of
each of these for more information.Iterator category is synonymous with <span style=
"font-style: italic;">iterator_tag</span>.</td>
</tr>
<tr>
<td>iterator_tag</td>
<td>See <span style="font-style: italic;">iterator category</span>.</td>
</tr>
<tr>
<td>key_type, Key</td>
<td>A Key or key_type is the identifier used by associative (a.k.a. dictionary) containers (e.g. map, hash_map) to
identify the type used to index the mapped_type. If you have a dictionary of strings that you access by an integer id,
the ids are the keys and the strings are the mapped types.</td>
</tr>
<tr>
<td>lexicographical compare</td>
<td>A lexicographical compare is a comparison of two containers that compares them element by element, much like the C
strcmp function compares two strings.</td>
</tr>
<tr>
<td>linked_ptr</td>
<td>A linked_ptr is a shared smart pointer which implements object lifetime via a linked list of all linked_ptrs that
are referencing the object. linked_ptr, like intrusive_ptr, is a non-memory-allocating alternative to shared_ptr.</td>
</tr>
<tr>
<td>list</td>
<td>A list is a doubly linked list. It is a sequence that supports both forward and backward traversal, and (amortized)
constant time insertion and removal of elements at the beginning or the end, or in the middle. Lists have the important
property that insertion and splicing do not invalidate iterators to list elements, and that even removal invalidates
only the iterators that point to the elements that are removed. The ordering of iterators may be changed (that is,
list&lt;T&gt;::iterator might have a different predecessor or successor after a list operation than it did before), but
the iterators themselves will not be invalidated or made to point to different elements unless that invalidation or
mutation is explicit.</td>
</tr>
<tr>
<td>lower_bound</td>
<td>lower_bound is a version of binary search: it attempts to find the element value in an ordered range [first, last).
Specifically, it returns the first position where value could be inserted without violating the ordering.</td>
</tr>
<tr>
<td>map</td>
<td>Map is a sorted associative container that associates objects of type Key with objects of type T. Map is a pair
associative container, meaning that its value type is pair&lt;const Key, T&gt;. It is also a unique associative
container, meaning that no two elements have the same key. It is implemented with a tree structure.</td>
</tr>
<tr>
<td>mapped_type</td>
<td>A mapped_type is a typedef used by associative containers to identify the container object which is accessed by a
key. If you have a dictionary of strings that you access by an integer id, the ids are the keys and the strings are the
mapped types.</td>
</tr>
<tr>
<td>member template</td>
<td>A member template is a templated function of a templated class. Thus with a member template function there are two
levels of templating -- the class and the function.</td>
</tr>
<tr>
<td>multimap,&nbsp;</td>
<td>Multimap is a sorted associative&nbsp;container that associates objects of type Key with objects of type T.
multimap is a pair associative container, meaning that its value type is pair&lt;const Key, T&gt;. It is also a
multiple associative container, meaning that there is no limit on the number of elements with the same key.It is
implemented with a tree structure.</td>
</tr>
<tr>
<td>multiset</td>
<td>Multiset is a sorted associative container that stores objects of type Key. Its value type, as well as its key
type, is Key. It is also a multiple associative container, meaning that two or more elements may be identical.&nbsp;It
is implemented with a tree structure.</td>
</tr>
<tr>
<td>node</td>
<td>A node is a little holder class used by many containers to hold the contained items. A linked-list, for example,
defines a node which has three members: mpPrev, mpNext, and T (the contained object).</td>
</tr>
<tr>
<td>npos</td>
<td>npos is used by the string class to identify a non-existent index. Some string functions return npos to indicate
that the function failed.</td>
</tr>
<tr>
<td>rel_ops</td>
<td>rel_ops refers to "relational operators" and is a set of templated functions which provide operator!= for classes
that&nbsp; have only operator== and provide operator &gt; for classes that have only operator &lt;, etc. Unfortunately,
rel_ops have a habit of polluting the global operator space and creating conflicts. They must be used with
discretion.</td>
</tr>
<tr>
<td>reverse_iterator</td>
<td>A reverse_iterator is an iterator which wraps a bidirectional or random access iterator and allows the iterator to
be read in reverse direction. The difference between using reverse_iterators and just decrementing regular iterators is
that reverse_iterators use operator++ to move backwards and thus work in any algorithm that calls ++ to move through a
container.</td>
</tr>
<tr>
<td>OutputIterator</td>
<td>An output iterator (iterator you write to) which allows writing each element only once in only in a forward
direction.</td>
</tr>
<tr>
<td>POD</td>
<td>POD means Plain Old Data. It refers to C++ classes which act like built-in types and C structs. These are useful to
distinguish because some algorithms can be made more efficient when they can detect that they are working with PODs
instead of regular classes.&nbsp;</td>
</tr>
<tr>
<td>Predicate</td>
<td>A function which takes one argument returns true if the argument meets some criteria.</td>
</tr>
<tr>
<td>priority_queue</td>
<td>A priority_queue is an adapter container which implements a heap via a random access container such as vector or
deque.</td>
</tr>
<tr>
<td>queue</td>
<td>A queue is an adapter container which implements a FIFO (first-in, first-out) container with which you can add
items to the back and get items from the front.</td>
</tr>
<tr>
<td>RandomAccessIterator</td>
<td>An input iterator which can be addressed like an array. It is a superset of all other input iterators.</td>
</tr>
<tr>
<td>red-black tree</td>
<td>A red-black tree is a binary tree which has the property of being always balanced. The colors red and black are
somewhat arbitrarily named monikers for nodes used to measure the balance of the tree. Red-black trees are considered
the best all-around data structure for sorted containers.</td>
</tr>
<tr>
<td>scalar</td>
<td>A scalar is a data type which is implemented via a numerical value. In C++ this means integers, floating point
values, enumerations, and pointers.&nbsp;</td>
</tr>
<tr>
<td>scoped_ptr</td>
<td>A scoped_ptr is a smart pointer which is the same as C++ auto_ptr except that it cannot be copied.</td>
</tr>
<tr>
<td>set</td>
<td>Set is a sorted associative container that stores objects of type Key. Its value type, as well as its key type, is
Key. It is also a unique associative container, meaning that no two elements are the same.It is implemented with a tree
structure.</td>
</tr>
<tr>
<td>sequence</td>
<td>A sequence is a variable-sized container whose elements are arranged in a strict linear (though not necessarily
contiguous) order. It supports insertion and removal of elements. Sequence containers include vector, deque, array,
list, slist.</td>
</tr>
<tr>
<td>size</td>
<td>All conventional containers have a size member function which returns the count of elements in the container. The
efficiency of the size function differs between containers.</td>
</tr>
<tr>
<td>size_type</td>
<td>The type that a container uses to define its size and counts. This is similar to the C/C++ size_t type but may be
specialized for the container. It defaults to size_t, but it is possible to force it to be 4 bytes for 64 bit machines by defining EASTL_SIZE_T_32BIT.</td>
</tr>
<tr>
<td>skip list</td>
<td>A skip-list is a type of container which is an alternative to a binary tree for finding data.</td>
</tr>
<tr>
<td>shared_ptr</td>
<td>A shared_ptr is a smart pointer which allows multiple references (via multiple shared_ptrs) to the same object.
When the last shared_ptr goes away, the pointer is freed. shared_ptr is implemented via a shared count between all
instances.</td>
</tr>
<tr>
<td>slist</td>
<td>An slist is like a list but is singly-linked instead of doubly-linked. It can only be iterated in a
forward-direction.</td>
</tr>
<tr>
<td>smart pointer</td>
<td>Smart pointer is a term that identifies a family of utility classes which store pointers and free them when the
class instance goes out of scope. Examples of smart pointers are shared_ptr, linked_ptr, intrusive_ptr, and
scoped_ptr.</td>
</tr>
<tr>
<td>splice</td>
<td>Splicing refers to the moving of a subsequence of one Sequence into another Sequence.</td>
</tr>
<tr>
<td>stack</td>
<td>A stack is a adapter container which implements LIFO (last-in, first, out) access via another container such as a
list or deque.</td>
</tr>
<tr>
<td>STL</td>
<td>Standard Template Library.&nbsp;</td>
</tr>
<tr>
<td>StrictWeakOrdering</td>
<td>A BinaryPredicate that compares two objects, returning true if the first precedes the second. Like Compare but has
additional requirements. Used for sorting routines.<br>
<br>
This predicate must satisfy the standard mathematical definition of a strict weak ordering. A StrictWeakOrdering has to
behave the way that "less than" behaves: if a is less than b then b is not less than a, if a is less than b and b is
less than c then a is less than c, and so on.</td>
</tr>
<tr>
<td>string</td>
<td>See basic_string.</td>
</tr>
<tr>
<td>T</td>
<td>T is the template parameter name used by most containers to identify the contained element type.&nbsp;</td>
</tr>
<tr>
<td>template parameter</td>
<td>A template parameter is the templated type used to define a template function or class. In the declaration
'template &lt;typename T&gt; class vector{ },' &nbsp;T is a template parameter.</td>
</tr>
<tr>
<td>template specialization</td>
<td>A template specialization is a custom version of a template which overrides the default version and provides
alternative functionality, often for the purpose of providing improved or specialized functionality.</td>
</tr>
<tr>
<td>treap</td>
<td>A tree-like structure implemented via a heap. This is an alternative to a binary tree (e.g. red-black tree),
skip-list, and sorted array as a mechanism for a fast-access sorted container.</td>
</tr>
<tr>
<td>type traits</td>
<td>Type traits are properties of types. If you have a templated type T and you want to know if it is a pointer, you
would use the is_pointer type trait. If you want to know if the type is a POD, you would use the is_pod type trait.
Type traits are very useful for allowing the implementation of optimized generic algorithms and for asserting that
types have properties expected by the function or class contract. For example, you can use type_traits to tell if a
type can be copied via memcpy instead of a slower element-by-element copy.</td>
</tr>
<tr>
<td>typename</td>
<td>Typename is a C++ keyword used in templated function implementations which identifies to the compiler that the
following expression is a type and not a value. It is used extensively in EASTL, particularly in the algorithms.</td>
</tr>
<tr>
<td>UnaryOperation</td>
<td>A function which takes one argument and returns a value (which will usually be assigned to second object).</td>
</tr>
<tr>
<td>upper_bound</td>
<td>upper_bound is a version of binary search: it attempts to find the element value in an ordered range [first, last).
Specifically, it returns the last position where value could be inserted without violating the ordering.</td>
</tr>
<tr>
<td>value_type, Value</td>
<td>A value_type is a typedef used by all containers to identify the elements they contain. In most cases value_type is
simply the same thing as the user-supplied T template parameter. The primary exception is the associative containers
whereby value_type is the pair of key_type and mapped_type.</td>
</tr>
<tr>
<td>vector</td>
<td>A vector is a Sequence that supports random access to elements, constant time insertion and removal of elements at
the end, and linear time insertion and removal of elements at the beginning or in the middle. The number of elements in
a vector may vary dynamically; memory management is automatic. Vector is the simplest of the container classes, and in
many cases the most efficient.</td>
</tr>
<tr>
<td>vector_map,&nbsp;vector_multimap,&nbsp;vector_set,&nbsp;vector_multiset</td>
<td>These are containers that implement the functionality of map, multimap, set, and multiset via a vector or deque
instead of a tree. They use less memory and find items faster, but are slower to modify and modification invalidates
iterators.</td>
</tr>
<tr>
<td>weak_ptr</td>
<td>A weak_ptr is an adjunct to shared_ptr which doesn't increment the reference on the contained object but can safely
tell you if the object still exists and access it if so. It has uses in preventing circular references in
shared_ptrs.</td>
</tr>
</tbody>
</table>
<br>
<hr style="width: 100%; height: 2px;">
End of document<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</body>
</html>
+175
View File
@@ -0,0 +1,175 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>EASTL Gotchas</title>
<meta content="text/html; charset=us-ascii" http-equiv="content-type">
<meta name="author" content="Paul Pedriana">
<meta name="description" content="Desciptions of potential pitfalls that exist in EASTL.">
<link type="text/css" rel="stylesheet" href="EASTLDoc.css">
<style type="text/css">
<!--
.style1 {color: #FF0000}
.style2 {color: #009933}
-->
</style>
</head>
<body>
<h1>EASTL Gotchas</h1>
<p> There are some cases where the EASTL design results in "gotchas" or behavior that isn't necessarily what the new user
would expect. These are all situations in which this behavior may be undesirable. One might ask, "Why not change EASTL
to make these gotchas go away?" The answer is that in each case making the gotchas go away would either be impossible
or would compromise the functionality of the library.</p>
<h2>Summary</h2>
<p>The descriptions here are intentionally terse; this is to make them easier to visually scan.</p>
<table style="text-align: left; width: 100%;" border="0" cellpadding="1" cellspacing="1">
<tbody>
<tr>
<td style="width: 28px;">1</td>
<td><a href="#Gotchas.1">map::operator[] can create elements.</a></td>
</tr>
<tr>
<td style="width: 28px;">2</td>
<td><a href="#Gotchas.2">char* converts to string silently.</a></td>
</tr>
<tr>
<td style="width: 28px;">3</td>
<td><a href="#Gotchas.3">char* is compared by ptr and not by contents.</a></td>
</tr>
<tr>
<td style="width: 28px;">4</td>
<td><a href="#Gotchas.4">Iterators can be invalidated by container mutations.</a></td>
</tr>
<tr>
<td style="width: 28px;">5</td>
<td><a href="#Gotchas.5">Vector resizing may cause ctor/dtor cascades.</a></td>
</tr>
<tr>
<td style="width: 28px;">6</td>
<td><a href="#Gotchas.6">Vector and string insert/push_back/resize can reallocate.</a></td>
</tr>
<tr>
<td style="width: 28px;">7</td>
<td><a href="#Gotchas.7">Deriving from containers may not work.</a></td>
</tr>
<tr>
<td style="width: 28px;">8</td>
<td><a href="#Gotchas.8">set::iterator is const_iterator.</a></td>
</tr>
<tr>
<td style="width: 28px;">9</td>
<td><a href="#Gotchas.9">Inserting elements means copying by value.</a></td>
</tr>
<tr>
<td style="width: 28px;">10</td>
<td><a href="#Gotchas.10">Containers of pointers can leak if you aren't careful.</a></td>
</tr>
<tr>
<td style="width: 28px;">11</td>
<td><a href="#Gotchas.11">Containers of auto_ptrs can crash.</a></td>
</tr>
<tr>
<td style="width: 28px;">12</td>
<td><a href="#Gotchas.12">Remove algorithms don't actually remove elements.</a></td>
</tr>
<tr>
<td style="width: 28px;">13</td>
<td><a href="#Gotchas.13">list::size() is O(n).</a></td>
</tr>
<tr>
<td style="width: 28px;">14</td>
<td><a href="#Gotchas.14">vector and deque::size() may incur integer division.</a></td>
</tr>
<tr>
<td style="width: 28px;">15</td>
<td><a href="#Gotchas.15">Be careful making custom Compare functions.</a></td>
</tr>
<tr>
<td style="width: 28px;">16</td>
<td><a href="#Gotchas.16">Comparisons involving floating point are dangerous.</a></td>
</tr>
<tr>
<td style="width: 28px;">17</td>
<td><a href="#Gotchas.17">Writing beyond string::size and vector::size is dangerous. </a></td>
</tr>
<tr>
<td style="width: 28px;">18</td>
<td><a href="#Gotchas.18">Container operator=() doesn't copy allocators. </a></td>
</tr>
</tbody>
</table>
<h2> Detail</h2>
<p class="faq-question"><a name="Gotchas.1"></a>1
map::operator[] can create elements.</p>
<p class="faq-answer">By design, map operator[] creates a value for you if it isn't already present. The reason for this is that the alternative behavior would be to throw an exception, and such behavior isn't desirable. The resolution is to simply use the map::find function instead of operator[].</p>
<p class="faq-question"><a name="Gotchas.2"></a>2
char* converts to string silently.</p>
<p class="faq-answer">The string class has a non-explicit constructor that takes char* as an argument. Thus if you pass char* to a function that takes a string object, a temporary string will be created. In some cases this is undesirable behavior but the user may not notice it right away, as the compiler gives no warnings. The reason that the string constructor from char* is not declared explicit is that doing so would prevent the user from expressions such as: string s = &quot;hello&quot;. In this example, no temporary string object is created, but the syntax is not possible if the char* constructor is declared explicit. Thus a decision to make the string char* constructor explicit involves tradeoffs.</p>
<p class="faq-answer">There is an EASTL configuration option called EASTL_STRING_EXPLICIT which makes the string char* ctor explicit and avoids the behaviour described above.</p>
<p class="faq-question"><a name="Gotchas.3"></a>3
char* is compared by ptr and not by contents.</p>
<p class="faq-answer">If you have a set of strings declared as set&lt;char*&gt;, the find function will compare via the pointer value and not the string contents. The workaround is to make a set of string objects or, better, to supply a custom string comparison function to the set. The workaround is not to declare a global operator&lt; for type char*, as that could cause other systems to break.</p>
<p class="faq-question"><a name="Gotchas.4"></a>4 Iterators can be invalidated by container mutations</p>
<p class="faq-answer">With some containers, modifications of them may invalidate iterators into them. With other containers, modifications of them only an iterator if the modification involves the element that iterator refers to. Containers in the former category include vector, deque, basic_string (string), vector_map, vector_multimap, vector_set, and vector_multiset. Containers in the latter category include list, slist, map, multimap, multiset, all hash containers, and all intrusive containers.</p>
<p class="faq-question"><a name="Gotchas.5"></a>5 Vector resizing may cause ctor/dtor cascades.</p>
<p>If elements are inserted into a vector in middle of the sequence, the elements from the insertion point to the end will be copied upward. This will necessarily cause a series of element constructions and destructions as the elements are copied upward. Similarly, if an element is appended to a vector but the vector capacity is exhausted and needs to be reallocated, the entire vector will undergo a construction and destruction pass as the values are copied to the new storage. This issue exists for deque as well, though to a lesser degree. For vector, the resolution is to reserve enough space in your vector to prevent such reallocation. For deque the resolution is to set its subarray size to enough to prevent such reallocation. Another solution that can often be used is to take advantage of the has_trivial_relocate type trait, which can cause such moves to happen via memcpy instead of via ctor/dtor calls. If your class can be safely memcpy'd, you can use EASTL_DECLARE_TRIVIAL_RELOCATE to tell the compiler it can be memcpy'd. Note that built-in scalars (e.g. int) already are automatically memcpy'd by EASTL.</p>
<p class="faq-question"><a name="Gotchas.6"></a>6
Vector and string insert/push_back/resize can reallocate.</p>
<p>If you create an empty vector and use push_back to insert 100 elements, the vector will reallocate itself at least three or four times during the operation. This can be an undesirable thing. The best thing to do if possible is to reserve the size you will need up front in the vector constructor or before you add any elements.</p>
<p class="faq-question"><a name="Gotchas.7"></a>7
Deriving from containers may not work.</p>
<p>EASTL containers are not designed with the guarantee that they can be arbitrarily subclassed. This is by design and is done for performance reasons, as such guarantees would likely involve making containers use virtual functions. However, some types of subclassing can be successful and EASTL does such subclassing internally to its advantage. The primary problem with subclassing results when a parent class function calls a function that the user wants to override. The parent class cannot see the overridden function and silent unpredictable behavior will likely occur. If your derived container acts strictly as a wrapper for the container then you will likely be able to successfully subclass it.</p>
<p class="faq-question"><a name="Gotchas.8"></a>8
set::iterator is const_iterator.</p>
<p class="faq-answer">The reason this is so is that a set is an ordered container and changing the value referred to by an iterator could make the set be out of order. Thus, set and multiset iterators are always const_iterators. If you need to change the value and are sure the change will not alter the container order, use const_cast or declare mutable member variables for your contained object. This resolution is the one blessed by the C++ standardization committee. This issue is addressed in more detail in the EASTL FAQ.</p>
<p class="faq-question"><a name="Gotchas.9"></a>9
Inserting elements means copying by value.</p>
<p class="faq-answer">When you insert an element into a (non-intrusive) container, the container makes a copy of the element. There is no provision to take over ownership of an object from the user. The exception to this is of course when you use a container of pointers instead of a container of values. See the entry below regarding containers of pointers. Intrusive containers (e.g. intrusive_list) do in fact take over the user-provided value, and thus provide another advantage over regular containers in addition to avoiding memory allocation.</p>
<p class="faq-question"><a name="Gotchas.10"></a>10
Containers of pointers can leak if you aren't careful.</p>
<p class="faq-answer">Containers of points don't know or care about the possibility that the pointer may have been allocated and need to be freed. Thus if you erase such elements from a container they are not freed. The resolution is to manually free the pointers when removing them or to instead use a container of smart pointers (shared smart pointers, in particular). This issue is addressed in more detail in the EASTL FAQ and the auto_ptr-related entry below.</p>
<p class="faq-question"><a name="Gotchas.11"></a>11
Containers of auto_ptrs can crash</p>
<p class="faq-answer">We suggested above that the user can use a container of smart pointers to automatically manage contained pointers. However, you don't want to use auto_ptr, as auto_ptrs cannot be safely assigned to each other; doing so results in a stale pointer and most likely a crash.</p>
<p class="faq-question"><a name="Gotchas.12"></a>12
Remove algorithms don't actually remove elements.</p>
<p class="faq-answer">Algorithms such as remove, remove_if, remove_heap, and unique do not erase elements from the sequences they work on. Instead, they return an iterator to the new end of the sequence and the user must call erase with that iterator in order to actually remove the elements from the container. This behavior exists because algorithms work on sequences via iterators and don't know how to work with containers. Only the container can know how to best erase its own elements. In each case, the documentation for the algorithm reminds the user of this behavior. Similarly, the copy algorithm copies elements from one sequence to another and doesn't modify the size of the destination sequence. So the destination must hold at least as many items as the source, and if it holds more items, you may want to erase the items at the end after the copy.</p>
<p class="faq-question"><a name="Gotchas.13"></a>13
list::size() is O(n).</p>
<p class="faq-answer">By this we mean that calling size() on a list will iterate the list and add the size as it goes. Thus, getting the size of a list is not a fast operation, as it requires traversing the list and counting the nodes. We could make list::size() be fast by having a member mSize variable. There are reasons for having such functionality and reasons for not having such functionality. We currently choose to not have a member mSize variable as it would add four bytes to the class, add&nbsp;processing to functions such as insert and erase, and would only serve to improve the size function, but no other function. The alternative&nbsp;argument is that the C++ standard states that std::list&nbsp;should be an O(1) operation (i.e. have a member size variable), most C++ standard library list implementations do so, the size is but an integer which is quick to update, and many users expect to have a fast size function. All of this applies to slist and intrusive_list as well.</p>
<p class="faq-answer">Note that EASTL's config.h file has an option in it to cause list and slist to cache their size with an mSize variable and thus make size() O(1). This option is disabled by default.</p>
<p class="faq-question"> <a name="Gotchas.14"></a>14
vector and deque::size() may incur integer division.</p>
<p class="faq-answer">Some containers (vector and deque in particular) calculate their size by pointer subtraction. For example, the implementation of vector::size() is 'return mpEnd - mpBegin'. This looks like a harmless subtraction, but if the size of the contained object is not an even power of two then the compiler will likely need to do an integer division to calculate the value of the subtracted pointers. One might suggest that vector use mpBegin and mnSize as member variables instead of mpBegin and mpEnd, but that would incur costs in other vector operations. The suggested workaround is to iterate a vector instead of using a for loop and operator[] and for those cases where you do use a for loop and operator[], get the size once at the beginning of the loop instead of repeatedly during the condition test.</p>
<p class="faq-question"><a name="Gotchas.15"></a>15
Be careful making custom Compare functions.
</p>
<p class="faq-answer">A Compare function compares two values and returns true if the first is less than the second. This is easy to understand for integers and strings, but harder to get right for more complex structures. Many a time have people decided to come up with a fancy mechanism for comparing values and made mistakes. The FAQ has a couple entries related to this. See http://blogs.msdn.com/oldnewthing/archive/2003/10/23/55408.aspx for a story about how this can go wrong by being overly clever.</p>
<p class="faq-question"> <a name="Gotchas.16"></a>16
Comparisons involving floating point are dangerous.</p>
<p class="faq-answer">Floating point comparisons between two values that are very nearly equal can result in inconsistent results. Similarly, floating point comparisons between NaN values will always generate inconsistent results, as NaNs by definition always compare as non-equal. You thus need to be careful when using comparison functions that work with floating point values. Conversions to integral values may help the problem, but not necessarily.</p>
<p class="faq-question"><a name="Gotchas.17" id="Gotchas.17"></a>17 Writing beyond string::size and vector::size is dangerous.</p>
<p>A trick that often comes to mind when working with strings is to set the string capacity to some maximum value, strcpy data into it, and then resize the string when done. This can be done with EASTL, but only if you resize the string to the maximum value and not reserve the string to the maximum value. The reason is that when you resize a string from size (n) to size (n + count), the count characters are zeroed and overwrite the characters that you strcpyd. </p>
<p class="faq-answer">The following code is broken: </p>
<p class="code-example">string mDataDir;<br>
<br>
mDataDir.<span class="style1">reserve</span>(kMaxPathLength);<br>
strcpy(&amp;mDataDir[0], &quot;blah/blah/blah&quot;);<br>
mDataDir.resize(strlen(&amp;mDataDir[0])); // Overwrites your blah/... with 00000...</p>
<p class="faq-answer">This following code is OK: </p>
<p class="code-example">string mDataDir;<br>
<br>
mDataDir.<span class="style2">resize</span>(kMaxPathLength);<br>
strcpy(&amp;mDataDir[0], &quot;blah/blah/blah&quot;);<br>
mDataDir.resize(strlen(&amp;mDataDir[0]));</p>
<p class="faq-question"><a name="Gotchas.18" id="Gotchas.18"></a>18 Container operator=() doesn't copy allocators.
</p>
<p class="faq-answer">EASTL container assignment (e.g. vector::operator=(const vector&amp;)) doesn't copy the allocator. There are good and bad reasons for doing this, but that's how it acts. So you need to beware that you need to assign the allocator separately or make a container subclass which overrides opeator=() and does this. </p>
<br>
<hr style="width: 100%; height: 2px;">
End of document<br>
<br>
<br>
<br>
<br>
</body>
</html>
+47
View File
@@ -0,0 +1,47 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>EASTL Introduction</title>
<meta content="text/html; charset=us-ascii" http-equiv="content-type">
<meta name="author" content="Paul Pedriana">
<meta name="description" content="Basic introduction to EASTL.">
<link type="text/css" rel="stylesheet" href="EASTLDoc.css">
</head>
<body>
<h1>EASTL Introduction</h1>
<p>EASTL stands for Electronic Arts Standard Template Library. It is a C++ template library of containers, algorithms, and
iterators useful for runtime and tool development across multiple platforms. It is a fairly extensive and robust
implementation of such a library and has an emphasis on high performance above all other considerations.</p>
<h2>Intended Audience</h2>
<p>This is a short document intended to provide a basic introduction to EASTL for
those new to the concept of EASTL or STL. If you are familiar with the C++ STL
or have worked with other templated container/algorithm libraries, you probably
don't need to read this. If you have no familiarity with C++ templates at all,
then you probably will need more than this document to get you up to speed. In
this case you need to understand that templates, when used properly, are powerful
vehicles for the ease of creation of optimized C++ code. A description of C++
templates is outside the scope of this documentation, but there is plenty of such
documentation on the Internet. See the <a href="EASTL%20FAQ.html">EASTL FAQ.html</a>
document for links to information related to learning templates and STL.</p>
<h2>EASTL Modules</h2>
<p>EASTL consists primarily of containers, algorithms, and iterators. An example of a container is a linked list, while an
example of an algorithm is a sort function; iterators are the entities of traversal for containers and algorithms.
EASTL containers a fairly large number of containers and algorithms, each of which is a very clean, efficient, and
unit-tested implementation. We can say with some confidence that you are not likely to find better implementations of
these (commercial or otherwise), as these are the result of years of wisdom and diligent work. For a detailed list of
EASTL modules, see <a href="EASTL%20Modules.html">EASTL Modules.html</a>.</p>
<h2>EASTL Suitability</h2>
<p>What uses are EASTL suitable for? Essentially any situation in tools and shipping applications where the functionality
of EASTL is useful. Modern compilers are capable of producing good code with templates and many people are using them
in both current generation and future generation applications on multiple platforms from embedded systems to servers
and mainframes.</p>
<hr style="width: 100%; height: 2px;">
End of document<br>
<br>
<br>
<br>
<br>
<br>
<br>
</body>
</html>
+292
View File
@@ -0,0 +1,292 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>EASTL Maintenance</title>
<meta content="text/html; charset=us-ascii" http-equiv="content-type">
<meta name="author" content="Paul Pedriana">
<meta name="description" content="Information for the EASTL maintainer.">
<link type="text/css" rel="stylesheet" href="EASTLDoc.css">
</head>
<body>
<h1>EASTL Maintenance</h1>
<h2><span style="font-style: italic;"><a name="Introduction" id="Introduction"></a></span>Introduction</h2>
<p>The purpose of this document is to provide some necessary background for anybody who might do work on EASTL. Writing
generic templated systems like EASTL can be surprisingly tricky. There are numerous details of the C++ language that
you need to understand which don't usually come into play during the day-to-day C++ coding that many people do. It is
easy to make a change to some function that seems proper and works for your test case but either violates the design
expectations or simply breaks under other circumstances.<br>
<br>
It may be useful to start with an example. Here we provide an implementation of the count algorithm which is seems
simple enough. Except it is wrong and while it&nbsp;will compile in some cases it won't compile in others:</p>
<pre class="code-example">template &lt;class InputIterator, class T&gt;
int count(InputIterator first, InputIterator last, const T&amp; value)
{
&nbsp;&nbsp;&nbsp; int result = 0;
&nbsp;&nbsp;&nbsp; for(; first &lt; last; ++first){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(*first == value)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++result;
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; return result;
} </pre>
<p>The problem is with the comparison 'first &lt; last'. The count algorithm takes an InputIterator and operator&lt; is
not guaranteed to exist for any given InputIterator (and indeed while operator&lt; exists for vector::iterator, it
doesn't exist for list::iterator). The comparison in the above algorithm must instead be implemented as 'first !=
last'. If we were working with a RandomAccessIterator then 'first &lt; last' would be valid.</p>
<p>In the following sections we cover various topics of interest regarding the development and maintentance of EASTL.
Unfortunately, this document can't cover every aspect of EASTL maintenance issues, but at least it should give you a
sense of the kinds of issues.</p>
<h2> <a name="Language_Standard" id="Language_Standard"></a>C++ Language Standard</h2>
<p>First and foremost, you need to be familiar with the C++ standard. In particular, the sections of the standard related
to containers, algorithms, and iterators are of prime significance. We'll talk about some of this in more detail below.
Similarly, a strong understanding of the basic data types is required. What is the difference between ptrdiff_t and
intptr_t; unsigned int and size_t; char and signed char?</p>
<p>In addition to the C++ language standard, you'll want to be familiar with the C++ Defect Report. This is a continuously
updated document which lists flaws in the original C++ language specification and the current thinking as the
resolutions of those flaws. You will notice various references to the Defect Report in EASTL source code.</p>
<p>Additionally, you will want to be familiar with the C++ Technical Report 1 (as of this writing there is only one). This
document is the evolving addendum to the C++ standard based on both the Defect Report and based on desired additions to
the C++ language and standard library.</p>
<p>Additionally, you will probably want to have some familiarity with Boost. It also helps to&nbsp;keep an eye on
comp.std.c++ Usenet discussions. However, watch out for what people say on Usenet. They tend to defend GCC, Unix, std
STL, and C++&nbsp;to a sometimes&nbsp;unreasonable degree. Many discussions ignore performance implications and
concentrate only on correctness and sometimes academic correctness above usability.</p>
<h2> <a name="Langauge_Use" id="Langauge_Use"></a>Language Use</h2>
<p>Macros are (almost) not allowed in EASTL.&nbsp;A prime directive of EASTL is to be easier to read by users and most of
the time macros are an impedence to this. So we avoid macros at all costs, even if it ends up making our development
and maintenance more difficult. That being said, you will notice that the EASTL config.h file uses macros to control
various options. This is an exception to the rule; when we talk about not using macros, we mean with the EASTL
implementation itself.</p>
<p>EASTL assumes a compliant and intelligent C++ compiler, and thus all language facilities are usable. However, we
nevertheless choose to stay away from some language functionality. The primary language features we avoid are:</p>
<ul>
<li>RTTI (run-time-type-identification) (this is deemed too costly)</li>
<li>Template export (few compilers support this)</li>
<li>Exception specifications (most compilers ignore them)</li>
</ul>
<p>Use of per-platform or per-compiler code should be avoided when possible but where there is a significant advantage to
be gained it can and indeed should be used. An example of this is the GCC __builtin_expect feature, which allows the
user to give the compiler a hint about whether an expression is true or false. This allows for the generation of code
that executes faster due to more intelligent branch prediction.</p>
<h2> <a name="Prime_Directives" id="Prime_Directives"></a>Prime Directives</h2>
<p>The
implementation of EASTL is guided foremost by the following directives which are listed in order of importance.</p>
<ol>
<li>Efficiency (speed and memory usage)</li>
<li>Correctness (doesn't have bugs)</li>
<li>Portability (works on all required platforms with minimal specialized code)</li>
<li>Readability (code is legible and comments are present and useful)</li>
</ol>
<p>Note that unlike commercial STL implementations which must put correctness above all, we put a higher value on
efficiency. As a result, some functionality may have some usage limitation that is not present in other similar systems
but which allows for more efficient operation, especially on the platforms of significance to us.</p>
<p>Portability is significant, but not critical. Yes, EASTL must compile and run on all platforms that we will ship games
for. But we don't take that to mean under all compilers that could be conceivably used for such platforms. For example,
Microsoft VC6 can be used to compile Windows programs, but VC6's C++ support is too weak for EASTL and so you simply
cannot use EASTL under VC6.</p>
<p>Readability is something that EASTL achieves better than many other templated libraries, particularly Microsoft STL and
STLPort. We make every attempt to make EASTL code clean and sensible. Sometimes our need to provide optimizations
(particularly related to type_traits and iterator types) results in less simple code, but efficiency happens to be our
prime directive and so it overrides all other considerations.</p>
<h2> <a name="Coding_Conventions" id="Coding_Conventions"></a>Coding Conventions</h2>
<p>Here we provide a list of coding conventions to follow when maintaining or adding to EASTL, starting with the three
language use items from above:</p>
<ul>
<li>No RTTI use.</li>
<li>No use of exception specifications (e.g. appending the 'throw' declarator to a function).</li>
<li>No use of exception handling itself except where explicitly required by the implementation (e.g. vector::at).</li>
<li>Exception use needs to savvy to EASTL_EXCEPTIONS_ENABLED.</li>
<li>No use of macros (outside of config.h). Macros make things more difficult for the user.</li>
<li>No use of static or global variables.</li>
<li>No use of global new, delete, malloc, or free. All memory must be user-specifyable via an Allocator parameter
(default-specified or explicitly specified).</li>
<li>Containers use protected member data and functions as opposed to private. This is because doing so allows
subclasses to extend the container without the creation of intermediary functions. Recall from our <a href="#Prime_Directives">prime directives</a> above that performance and simplicity overrule all.</li>
<li>No use of multithreading primitives.&nbsp;</li>
<li>No use of the export keyword.</li>
<li>We don't have a rule about C-style casts vs. C++ static_cast&lt;&gt;, etc. We would always use static_cast except
that debuggers can't evaluate them and so in practice they can get in the way of debugging and tracing. However, if the
cast is one that users don't tend to need to view in a debugger, C++ casts are preferred.</li>
<li>No external library dependencies whatsoever, including standard STL. EASTL is dependent&nbsp;on only EABase and the
C++ compiler.&nbsp;</li>
<li>All code must be const-correct. This isn't just for readability -- compilation can fail unless const-ness is used
correctly everywhere.&nbsp;</li>
<li>Algorithms do not refer to containers; they refer only to iterators.</li>
<li>Algorithms in general do not allocate memory. If such a situation arises, there should be a version of the
algorithm which allows the user to provide the allocator.</li>
<li>No inferior implementations. No facility should be added to EASTL unless it is of professional
quality.</li>
<li>The maintainer should emulate the EASTL style of code layout, regardless of the maintainer's personal preferences.
When in Rome, do as the Romans do. EASTL uses 4 spaces for indents, which is how the large majority of code within EA
is written.</li>
<li>No major changes should be done without consulting a peer group.</li>
</ul>
<h2><a name="Compiler_Issues" id="Compiler_Issues"></a>Compiler Issues</h2>
<p>Historically, templates are the feature of C++ that has given C++ compilers the most fits. We are still working with
compilers that don't completely and properly support templates. Luckily, most compilers are now good enough to handle
what EASTL requires. Nevertheless, there are precautions we must take.</p>
<p>It turns out that the biggest problem in writing portable EASTL code is that VC++ allows you to make illegal statements
which are not allowed by other compilers. For example, VC++ will allow you to neglect using the typename keyword in
template references, whereas GCC (especially 3.4+) requires it.</p>
<p>In order to feel comfortable that your EASTL code is C++ correct and is portable, you must do at least these two
things:</p>
<ul>
<li>Test under at least VS2005, GCC 3.4+, GCC 4.4+, EDG, and clang. </li>
<li>Test all functions that you write, as compilers will often skip the compilation of a template function if it isn't
used.</li>
</ul>
<p>The two biggest issues to watch out for are 'typename' and a concept called "dependent names". In both cases VC++ will
accept non-conforming syntax whereas most other compilers will not. Whenever you reference a templated type (and not a templated
value) in a template, you need to prefix it by 'typename'. Whenever your class function refers to a base class member (data or
function), you need to refer to it by "this-&gt;", "base_type::", or by placing a "using" statement in your class to
declare that you will be referencing the given base class member.</p>
<h2> <a name="Iterator_Issues" id="Iterator_Issues"></a>Iterator Issues</h2>
<p>The most important thing to understand about iterators is the concept of iterator types and their designated
properties. In particular, we need to understand the difference between InputIterator, ForwardIterator,
BidirectionalIterator, RandomAccessIterator, and OutputIterator. These differences dictate both how we implement our
algorithms and how we implement our optimizations. Please read the C++ standard for a reasonably well-implemented
description of these iterator types.</p>
<p>Here's an example from EASTL/algorithm.h which demonstrates how we use iterator types to optimize the reverse algorithm
based on the kind of iterator passed to it:</p>
<pre class="code-example">template &lt;class BidirectionalIterator&gt;
inline void reverse_impl(BidirectionalIterator first, BidirectionalIterator last, bidirectional_iterator_tag)<br>{
&nbsp;&nbsp;&nbsp; for(; (first != last) &amp;&amp; (first != --last); ++first) <span class="code-example-comment">// We are not allowed to use operator &lt;, &lt;=, &gt;, &gt;= with</span>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; iter_swap(first, last);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="code-example-comment">// a generic (bidirectional or otherwise) iterator.</span>
}<br>
template &lt;typename RandomAccessIterator&gt;
inline void reverse_impl(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag)
{
&nbsp;&nbsp;&nbsp; for(; first &lt; --last; ++first) <span class="code-example-comment">// With a random access iterator, we can use operator &lt; to more efficiently implement</span>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; iter_swap(first, last);&nbsp;&nbsp;&nbsp; <span class="code-example-comment">// this algorithm. A generic iterator doesn't necessarily have an operator &lt; defined.</span>
}<br><br>
template &lt;class BidirectionalIterator&gt;
inline void reverse(BidirectionalIterator first, BidirectionalIterator last)
{
&nbsp;&nbsp;&nbsp; typedef typename iterator_traits&lt;BidirectionalIterator&gt;::iterator_category IC;
&nbsp;&nbsp;&nbsp; reverse_impl(first, last, IC());
}</pre>
<h2> <a name="Exception_Handling" id="Exception_Handling"></a>Exception Handling</h2>
<p>You will notice that EASTL uses try/catch in some places (particularly in containers) and uses
the&nbsp;EASTL_EXCEPTIONS_ENABLED define. For starters, any EASTL code that uses try/catch should always be wrapped
within #if EASTL_EXCEPTIONS_ENABLED (note: #if, not #ifdef).</p>
<p>This is simple enough, but what you may be wondering is how it is that EASTL decides to use try/catch for some sections
of code and not for others. EASTL follows the C++ standard library conventions with respect to exception handling, and
you will see similar exception handling in standard STL. The code that you need to wrap in try/catch is code that can
throw a C++ exception (not to be confused with CPU exception) and needs to have something unwound (or fixed) as a
result. The important thing is that the container be in a valid state after encountering such exceptions. In general
the kinds of things that require such try/catch are:</p>
<ul>
<li>Memory allocation failures (which throw exceptions)</li>
<li>Constructor exceptions</li>
</ul>
<p>Take a look at the cases in EASTL where try/catch is used and see what it is doing.</p>
<h2> <a name="Type_Traits" id="Type_Traits"></a>Type Traits </h2>
<p>EASTL provides a facility called type_traits which is very similar to the type_traits being proposed by the C++ TR1
(see above). type_traits are useful because they tell you about properties of types at compile time. This allows you to
do things such as assert that a data type is scalar or that a data type is const. The way we put them to use in EASTL
is to take advantage of them to implement different pathways for functions based on types. For example, we can copy a
contiguous array of scalars much faster via memcpy than we can via a for loop, though we could not safely employ the
for loop for a non-trivial C++ class.</p>
<p>As mentioned in the GeneralOptimizations section below, EASTL should take advantage of type_traits information to the
extent possible to achive maximum effiiciency.</p>
<h2> <a name="General_Optimizations" id="General_Optimizations"></a>General
Optimizations</h2>
<p>One of the primary goals of EASTL is to achieve the highest possible efficiency. In cases where EASTL functionality
overlaps standard C++ STL functionality, standard STL implementations provided by compiler vendors are a benchmark upon
which EASTL strives to beat. Indeed EASTL is more efficient than all other current STL implementations&nbsp;(with some
exception in the case of some Metrowerks STL facilities). Here we list some of the things to look for when considering
optimization of EASTL code These items can be considered general optimization suggestions for any code, but this
particular list applies to EASTL:</p>
<ul>
<li>Take advantage of type_traits to the extent possible (e.g. to use memcpy to move data instead of a for loop when
possible).</li>
<li>Take advantage of iterator types to the extent possible.</li>
<li>Take advantage of the compiler's expectation that if statements are expected to evaluate as true and for loop
conditions are expected to evaluate as false.</li>
<li>Make inline-friendly code. This often means avoiding temporaries to the extent possible.</li>
<li>Minimize branching (i.e. minimize 'if' statements). Where branching is used, make it so that 'if' statements
execute as true.</li>
<li>Use EASTL_LIKELY/EASTL_UNLIKELY to give branch hints to the compiler when you are confident it will be
beneficial.</li>
<li>Use&nbsp;restricted pointers (EABase's EA_RESTRICT or various compiler-specific versions of __restrict).</li>
<li>Compare unsigned values to &lt; max instead of comparing signed values to &gt;= 0 &amp;&amp; &lt; max.</li>
<li>Employ power of 2 integer math instead of math with any kind of integer.</li>
<li>Use template specialization where possible to implement improved functionality.</li>
<li>Avoid function calls when the call does something trivial. This improves debug build speed (which matters) and
sometimes release build speed as well, though sometimes makes the code intent less clear. A comment next to the code
saying what call it is replacing makes the intent clear without sacrificing performance.</li>
</ul>
<h2><a name="Unit_Tests" id="Unit_Tests"></a>Unit Tests</h2>
<p>Writing robust templated containers and algorithms is difficult or impossible without a heavy unit test suite in place.
EASTL has a pretty extensive set of unit tests for all containers and algorithms. While the successful automated unit
testing of shipping application programs may be a difficult thing to pull off, unit testing of libraries such as this
is of huge importance and cannot be understated. </p>
<ul>
<li>When making a new unit test, start by copying one of the existing unit tests and follow its conventions.</li>
<li>Test containers of both scalars and classes.</li>
<li>Test algorithms on both container iterators (e.g. vector.begin()) and pointer iterators (e.g. int*).</li>
<li>Make sure that algorithm or container member functions which take iterators work with the type of iterator they
claim to (InputIterator, ForwardIterator, BidirectionalIterator, RandomAccessIterator).&nbsp;</li>
<li>Test for const-correctness. If a user is allowed to modify something that is supposed to be const, silent errors
can go undetected.</li>
<li>Make sure that unit tests cover all functions and all pathways of the tested code. This means that in writing the
unit test you need to look at the source code to understand all the pathways.</li>
<li>Consider using a random number generator (one is provided in the test library) to do 'monkey' testing whereby
unexpected input is given to a module being tested. When doing so, make sure you seed the generator in a way that
problems can be reproduced.</li>
<li>While we avoid macros in EASTL user code, macros to assist in unit tests aren't considered a problem. However,
consider that a number of macros could be replaced by templated functions and thus be easier to work with.</li>
<li>Unit tests don't need to be efficient; feel free to take up all the CPU power and time you need to test a module
sufficiently.</li>
<li>EASTL containers are not thread-safe, by design. Thus there is no need to do multithreading tests as long as you
stay away from the usage of static and global variables.</li>
<li>Unit tests must succeed with no memory leaks and of course no memory corruption. The heap system should be
configured to test for this, and heap validation functions are available to the unit tests while in the middle of
runs.</li>
</ul>
<h2><a name="Things_to_Keep_in_Mind" id="Things_to_Keep_in_Mind"></a>Things to Keep in Mind</h2>
<ul>
<li>When referring to EASTL functions and types from EASTL code, make sure to preface the type with the EASTL
namespace. If you don't do this you can get collisions due to the compiler not knowing if it should use the EASTL
namespace or the namespace of the templated type for the function or type.</li>
<li>Newly constructed empty containers do no memory allocation. Some STL and other container libraries allocate an
initial node from the class memory allocator. EASTL containers by design never do this.&nbsp;If a container needs an
initial node, that node should be made part of the container itself or be a static empty node object.</li>
<li>Empty containers (new or otherwise) contain no constructed objects, including those that might be in an 'end' node.
Similarly, no user object (e.g. of type T) should be constructed unless required by the design and unless documented in
the cotainer/algorithm contract.&nbsp;</li>
<li>When creating a new container class, it's best to copy from an existing similar class to the extent possible. This
helps keep the library consistent and resolves subtle problems that can happen in the construction of containers.</li>
<li>Be very careful about tweaking the code. It's easy to think (for example) that a &gt; could be switch to a &gt;=
where instead it is a big deal. Just about every line of code in EASTL has been thought through and has a purpose. Unit
tests may or may not currently test every bit of EASTL, so you can't necessarily rely on them to give you 100%
confidence in changes. If you are not sure about something, contact the original author and he will tell you for
sure.</li>
<li>Algorithm templates always work with iterators and not containers. A given container may of course implement an
optimized form or an algorithm itself.</li>
<li>Make sure everything is heavily unit tested. If somebody finds a bug, fix the bug and make a unit test to make sure
the bug doesn't happen again.</li>
<li>It's easy to get iterator categories confused or forgotten while implementing algorithms and containers.</li>
<li>Watch out for the strictness of GCC 3.4+. There is a bit of syntax &#8212; especially related to templates &#8212; that other
compilers accept but GCC 3.4+ will not.</li>
<li>Don't forget to update the config.h EASTL_VERSION define before publishing.</li>
<li>The vector and string classes define iterator to be T*. We want to always leave this so &#8212; at least in release
builds &#8212; as this gives some algorithms an advantage that optimizers cannot get around.</li>
</ul>
<hr style="width: 100%; height: 2px;">
<br>
<br>
<br>
<br>
<br>
</body>
</html>
+666
View File
@@ -0,0 +1,666 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>EASTL Modules</title>
<meta content="text/html; charset=us-ascii" http-equiv="content-type">
<meta name="author" content="Paul Pedriana">
<meta name="description" content="Lists the top-level modules present in EASTL.">
<link type="text/css" rel="stylesheet" href="EASTLDoc.css">
<style type="text/css">
<!--
.style1 {font-size: 10pt}
-->
</style>
</head>
<body>
<h1><font size="+3">EASTL Modules</font></h1>
<h2> Introduction</h2>
<p>We provide here a list of all top-level modules present or planned for future presence in EASTL. In some cases (e.g.
algorithm), the module consists of many smaller submodules which are not described in detail here. In those cases you
should consult the source code for those modules or consult the detailed documentation for those modules. This document
is a high level overview and not a detailed document.</p>
<h2>Module List</h2>
<table style="text-align: left; width: 100%;" border="1" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="font-weight: bold;">&nbsp;Module</td>
<td style="font-weight: bold;">Description</td>
</tr>
<tr>
<td>config</td>
<td>Configuration header. Allows for changing some compile-time options.</td>
</tr>
<tr>
<td>slist<br>
fixed_slist</td>
<td>Singly-linked list.<br>
fixed_slist is a version which is implemented via a fixed block of contiguous memory.</td>
</tr>
<tr>
<td>list<br>
fixed_list</td>
<td>Doubly-linked list.</td>
</tr>
<tr>
<td>intrusive_list<br>
intrusive_slist</td>
<td>List whereby the contained item provides the node implementation.</td>
</tr>
<tr>
<td>array</td>
<td>Wrapper for a C-style array which extends it to act like an STL container.</td>
</tr>
<tr>
<td>vector<br>
fixed_vector</td>
<td>Resizable array container.</td>
</tr>
<tr>
<td>vector_set<br>
vector_multiset<br></td>
<td>Set implemented via a vector instead of a tree. Speed and memory use is improved but resizing is slower.</td>
</tr>
<tr>
<td>vector_map<br>
vector_multimap<br></td>
<td>Map implemented via a vector instead of a tree. Speed and memory use is improved but resizing is slower.</td>
</tr>
<tr>
<td style="vertical-align: top;">deque<br></td>
<td style="vertical-align: top;">Double-ended queue, but also with random access. Acts like a vector but insertions and
removals are efficient.<br></td>
</tr>
<tr>
<td>bit_vector</td>
<td>Implements a vector of bool, but the actual storage is done with one bit per bool. Not the same thing as a
bitset.</td>
</tr>
<tr>
<td>bitset</td>
<td>Implements an efficient arbitrarily-sized bitfield. Note that this is not strictly the same thing as a vector of
bool (bit_vector), as it is optimized to act like an arbitrary set of flags and not to be a generic container which can
be iterated, inserted, removed, etc.</td>
</tr>
<tr>
<td>set<br>
multiset<br>
fixed_set<br>
fixed_multiset<br></td>
<td>A set is a sorted unique collection, multiset is sorted but non-unique collection.</td>
</tr>
<tr>
<td>map<br>
multimap<br>
fixed_map<br>
fixed_multimap</td>
<td>A map is a sorted associative collection implemented via a tree. It is also known as dictionary.</td>
</tr>
<tr>
<td>hash_map<br>
hash_multimap<br>
fixed_hash_map<br>
fixed_hash_multimap</td>
<td>Map implemented via a hash table.</td>
</tr>
<tr>
<td>intrusive_hash_map<br>
intrusive_hash_multimap<br>
intrusive_hash_set<br>
intrusive_hash_multiset</td>
<td>hash_map whereby the contained item provides the node implementation, much like intrusive_list.</td>
</tr>
<tr>
<td>hash_set<br>
hash_multiset<br>
fixed_hash_set<br>
fixed_hash_map<br></td>
<td>Set implemented via a hash table.</td>
</tr>
<tr>
<td>basic_string<br>
fixed_string<br>
fixed_substring</td>
<td>basic_string is a character string/array.<br>
fixed_substring is a string which is a reference to a range within another string or character array.<br>
cow_string is a string which implements copy-on-write.</td>
</tr>
<tr>
<td>algorithm</td>
<td>min/max, find, binary_search, random_shuffle, reverse, etc.&nbsp;</td>
</tr>
<tr>
<td style="vertical-align: top;">sort<br></td>
<td style="vertical-align: top;">Sorting functionality, including functionality not in STL. quick_sort, heap_sort,
merge_sort, shell_sort, insertion_sort, etc.<br></td>
</tr>
<tr>
<td>numeric</td>
<td>Numeric algorithms: accumulate, inner_product, partial_sum, adjacent_difference, etc.</td>
</tr>
<tr>
<td style="vertical-align: top;">heap<br></td>
<td style="vertical-align: top;">Heap structure functionality: make_heap, push_heap, pop_heap, sort_heap, is_heap,
remove_heap, etc.<br></td>
</tr>
<tr>
<td style="vertical-align: top;">stack<br></td>
<td style="vertical-align: top;">Adapts any container into a stack.<br></td>
</tr>
<tr>
<td style="vertical-align: top;">queue<br></td>
<td style="vertical-align: top;">Adapts any container into a queue.<br></td>
</tr>
<tr>
<td style="vertical-align: top;">priority_queue<br></td>
<td style="vertical-align: top;">Implements a conventional priority queue via a heap structure.<br></td>
</tr>
<tr>
<td>type_traits</td>
<td>Type information, useful for writing optimized and robust code. Also used for implementing optimized containers and
algorithms.</td>
</tr>
<tr>
<td style="vertical-align: top;">utility<br></td>
<td style="vertical-align: top;">pair, make_pair, rel_ops, etc.<br></td>
</tr>
<tr>
<td style="vertical-align: top;">functional<br></td>
<td style="vertical-align: top;">Function objects.<br></td>
</tr>
<tr>
<td style="vertical-align: top;">iterator<br></td>
<td style="vertical-align: top;">Iteration for containers and algorithms.<br></td>
</tr>
<tr>
<td>smart_ptr</td>
<td>Smart pointers:&nbsp;shared_ptr, shared_array, weak_ptr, scoped_ptr, scoped_array, linked_ptr, linked_array,
intrusive_ptr.</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<h2>Module Behaviour</h2>
<p>The overhead sizes listed here refer to an optimized release build; debug builds may add some additional overhead. Some
of the overhead sizes may be off by a little bit (usually at most 4 bytes). This is because the values reported here
are those that refer to when EASTL's container optimizations have been complete. These optimizations may not have been
completed as you are reading this.</p>
<table style="width: 100%;" border="1" cellpadding="1" cellspacing="1">
<tbody>
<tr>
<td style="width: 15%; vertical-align: top; height: 13px; font-weight: bold;">
<p>Container</p>
</td>
<td style="font-weight: bold; text-align: center;" height="13" valign="top" width="10%">
<p>Stores</p>
</td>
<td style="font-weight: bold; text-align: center;">Container Overhead (32 bit)</td>
<td style="font-weight: bold; text-align: center;">Container Overhead (64 bit)</td>
<td style="font-weight: bold; text-align: center;" height="13" valign="top" width="10%">
<p>Node Overhead (32 bit)</p>
</td>
<td style="font-weight: bold; text-align: center;">Node Overhead (64 bit)</td>
<td style="font-weight: bold; text-align: center;" height="13" valign="top" width="9%">
<p>Iterator category</p>
</td>
<td style="text-align: center; font-weight: bold;">size() efficiency</td>
<td style="text-align: center; font-weight: bold;">operator[] efficiency</td>
<td style="font-weight: bold; text-align: center;" height="13" valign="top" width="16%">
<p>Insert efficiency</p>
</td>
<td style="font-weight: bold; text-align: center;" height="13" valign="top" width="16%">
<p>Erase via Iterator efficiency</p>
</td>
<td style="font-weight: bold; text-align: center;" height="13" valign="top" width="7%">
<p>Find efficiency</p>
</td>
<td style="font-weight: bold; text-align: center;" height="13" valign="top" width="10%">
<p>Sort efficiency</p>
</td>
</tr>
<tr>
<td>slist</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">f</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">n+</td>
</tr>
<tr>
<td height="13" valign="top" width="15%">
<p>list</p>
</td>
<td style="text-align: center;" height="13" valign="top" width="10%">
<p>T</p>
</td>
<td style="text-align: center;">12</td>
<td style="text-align: center;">24</td>
<td style="text-align: center;" height="13" valign="top" width="10%">
<p>8</p>
</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;" height="13" valign="top" width="9%">
<p>b</p>
</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;" height="13" valign="top" width="16%">
<p>1</p>
</td>
<td style="text-align: center;" height="13" valign="top" width="16%">
<p>1</p>
</td>
<td style="text-align: center;" height="13" valign="top" width="7%">
<p>n</p>
</td>
<td style="text-align: center;" height="13" valign="top" width="10%">
<p>n log(n)</p>
</td>
</tr>
<tr>
<td>intrusive_slist</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">f</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">n+</td>
</tr>
<tr>
<td>intrusive_list</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">n log(n)</td>
</tr>
<tr>
<td>array</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">r</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">n log(n)</td>
</tr>
<tr>
<td>vector</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">32</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">r</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">n log(n)</td>
</tr>
<tr>
<td>vector_set</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">32</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">r</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>vector_multiset</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">32</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">r</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>vector_map</td>
<td style="text-align: center;">Key, T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">32</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">r</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>vector_multimap</td>
<td style="text-align: center;">Key, T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">32</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">r</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>deque</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">44</td>
<td style="text-align: center;">84</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">r</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1&nbsp;at begin or end,<br>
else n / 2</td>
<td style="text-align: center;">1&nbsp;at begin or end,<br>
else n / 2</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">n log(n)</td>
</tr>
<tr>
<td>bit_vector</td>
<td style="text-align: center;">bool</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">r</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">n log(n)</td>
</tr>
<tr>
<td>string (all types)</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">32</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">r</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">1&nbsp;at end, else n</td>
<td style="text-align: center;">n</td>
<td style="text-align: center;">n log(n)</td>
</tr>
<tr>
<td>set</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">24</td>
<td style="text-align: center;">44</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">28</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>multiset</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">24</td>
<td style="text-align: center;">44</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">28</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>map</td>
<td style="text-align: center;">Key, T</td>
<td style="text-align: center;">24</td>
<td style="text-align: center;">44</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">28</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>multimap</td>
<td style="text-align: center;">Key, T</td>
<td style="text-align: center;">24</td>
<td style="text-align: center;">44</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">28</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">log(n)</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td>hash_set</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">20</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
</tr>
<tr>
<td>hash_multiset</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">20</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1<br></td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
</tr>
<tr>
<td>hash_map</td>
<td style="text-align: center;">Key, T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">20</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
</tr>
<tr>
<td>hash_multimap</td>
<td style="text-align: center;">Key, T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">20</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
</tr>
<tr>
<td>intrusive_hash_set</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">20</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
</tr>
<tr>
<td>intrusive_hash_multiset</td>
<td style="text-align: center;">T</td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">20</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
</tr>
<tr>
<td>intrusive_hash_map</td>
<td style="text-align: center;">T <small>(Key == T)</small></td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">20</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
</tr>
<tr>
<td>intrusive_hash_multimap</td>
<td style="text-align: center;">T <small>(Key == T)&nbsp;</small></td>
<td style="text-align: center;">16</td>
<td style="text-align: center;">20</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">b</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">-</td>
</tr>
</tbody>
</table>
<ul>
<li>- means that the operation does not exist.</li>
<li>1 means amortized constant time. Also known as O(1)</li>
<li>n means time proportional to the container size. Also known as O(n)</li>
<li>log(n) means time proportional to the natural logarithm of the container size. Also known as O(log(n))</li>
<li>n log(n) means time proportional to log(n) times the size of the container. Also known as O(n log(n))</li>
<li>n+ means that the time is at least n, and possibly higher.</li>
<li>Iterator meanings are: f = forward iterator; b = bidirectional iterator, r = random iterator.</li>
<li>Overhead indicates approximate per-element overhead memory required in bytes. Overhead doesn't include possible
additional overhead that may be imposed by the memory heap used to allocate nodes. General heaps tend to have between 4
and 16 bytes of overhead per allocation, depending on the heap.</li>
<li>Some overhead values are dependent on the structure alignment characteristics in effect. The values reported here
are those that would be in effect for a system that requires pointers to be aligned on boundaries of their size and
allocations with a minimum of 4 bytes (thus one byte values get rounded up to 4).</li>
<li>Some overhead values are dependent on the size_type used by containers. size_type defaults to size_t, but it is possible to force it to be 4 bytes for 64 bit machines by defining EASTL_SIZE_T_32BIT.</li>
<li>Inserting at the end of a vector may cause the vector to be resized; resizing a vector is O(n). However, the
amortized time complexity for vector insertions at the end is constant.</li>
<li>Sort assumes the usage of the best possible sort for a large container of random data. Some sort algorithms (e.g.
quick_sort) require random access iterators and so the sorting of some containers requires a different sort algorithm.
We do not include bucket or radix sorts, as they are always O(n).</li>
<li>Some containers (e.g. deque, hash*) have unusual data structures that make per-container and per-node overhead
calculations not quite account for all memory.</li>
</ul>
<hr style="width: 100%; height: 2px;">
End of document<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</body>
</html>
+86
View File
@@ -0,0 +1,86 @@
body
{
font-family: Georgia, "Times New Roman", Times, serif;
font-size: 12pt;
}
h1
{
font-family: Verdana, Arial, Helvetica, sans-serif;
display: block;
background-color: #BBCCDD;
border: 2px solid #000000;
font-size: 16pt;
font-weight: bold;
padding: 6px;
}
h2
{
font-size: 14pt;
font-family: Verdana;
border-bottom: 2px solid black;
}
h3
{
font-family: Verdana;
font-size: 13pt;
font-weight: bold;
}
.code-example
{
display: block;
background-color: #D1DDE9;
margin-left: 3em;
margin-right: 3em;
margin-top: 1em;
margin-bottom: 1em;
padding: 8px;
border: 2px solid #7993C8;
font-family: "Courier New", Courier, mono;
font-size: 10pt;
white-space: pre;
}
.code-example-span
{
font-family: "Courier New", Courier, mono;
font-size: 10pt;
white-space: pre;
}
.code-example-comment
{
background-color: #e0e0f0;
padding: 0px 0px;
font-family: "Courier New", Courier, mono;
font-size: 10pt;
white-space: pre;
color: #999999;
margin: auto auto;
}
.faq-question
{
background-color: #D9E2EC;
font-size: 12pt;
font-weight: bold;
margin-top: 0em;
padding-left:5px;
padding-right:8px;
padding-top:2px;
padding-bottom:3px;
margin-bottom: 0.5em;
}
.faq-answer
{
display: block;
margin: 4pt 1em 0.8em;
}
.indented {
margin-left: 50px;
}
Binary file not shown.
File diff suppressed because it is too large Load Diff
+397
View File
@@ -0,0 +1,397 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_ALLOCATOR_H
#define EASTL_ALLOCATOR_H
#include <EASTL/internal/config.h>
#include <EABase/nullptr.h>
#include <stddef.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// alloc_flags
///
/// Defines allocation flags.
///
enum alloc_flags
{
MEM_TEMP = 0, // Low memory, not necessarily actually temporary.
MEM_PERM = 1 // High memory, for things that won't be unloaded.
};
/// allocator
///
/// In this allocator class, note that it is not templated on any type and
/// instead it simply allocates blocks of memory much like the C malloc and
/// free functions. It can be thought of as similar to C++ std::allocator<char>.
/// The flags parameter has meaning that is specific to the allocation
///
/// C++11's std::allocator (20.6.9) doesn't have a move constructor or assignment
/// operator. This is possibly because std::allocators are associated with types
/// instead of as instances. The potential non-equivalance of C++ std::allocator
/// instances has been a source of some acknowledged design problems.
/// We don't implement support for move construction or assignment in eastl::allocator,
/// but users can define their own allocators which do have move functions and
/// the eastl containers are compatible with such allocators (i.e. nothing unexpected
/// will happen).
///
class EASTL_API allocator
{
public:
EASTL_ALLOCATOR_EXPLICIT allocator(const char* pName = EASTL_NAME_VAL(EASTL_ALLOCATOR_DEFAULT_NAME));
allocator(const allocator& x);
allocator(const allocator& x, const char* pName);
allocator& operator=(const allocator& x);
void* allocate(size_t n, int flags = 0);
void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0);
void deallocate(void* p, size_t n);
const char* get_name() const;
void set_name(const char* pName);
protected:
#if EASTL_NAME_ENABLED
const char* mpName; // Debug name, used to track memory.
#endif
};
bool operator==(const allocator& a, const allocator& b);
#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
bool operator!=(const allocator& a, const allocator& b);
#endif
/// dummy_allocator
///
/// Defines an allocator which does nothing. It returns NULL from allocate calls.
///
class EASTL_API dummy_allocator
{
public:
EASTL_ALLOCATOR_EXPLICIT dummy_allocator(const char* = NULL) { }
dummy_allocator(const dummy_allocator&) { }
dummy_allocator(const dummy_allocator&, const char*) { }
dummy_allocator& operator=(const dummy_allocator&) { return *this; }
void* allocate(size_t, int = 0) { return NULL; }
void* allocate(size_t, size_t, size_t, int = 0) { return NULL; }
void deallocate(void*, size_t) { }
const char* get_name() const { return ""; }
void set_name(const char*) { }
};
inline bool operator==(const dummy_allocator&, const dummy_allocator&) { return true; }
#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
inline bool operator!=(const dummy_allocator&, const dummy_allocator&) { return false; }
#endif
/// Defines a static default allocator which is constant across all types.
/// This is different from get_default_allocator, which is is bound at
/// compile-time and expected to differ per allocator type.
/// Currently this Default Allocator applies only to CoreAllocatorAdapter.
/// To consider: This naming of this function is too similar to get_default_allocator
/// and instead should be named something like GetStaticDefaultAllocator.
EASTL_API allocator* GetDefaultAllocator();
EASTL_API allocator* SetDefaultAllocator(allocator* pAllocator);
/// get_default_allocator
///
/// This templated function allows the user to implement a default allocator
/// retrieval function that any part of EASTL can use. EASTL containers take
/// an Allocator parameter which identifies an Allocator class to use. But
/// different kinds of allocators have different mechanisms for retrieving
/// a default allocator instance, and some don't even intrinsically support
/// such functionality. The user can override this get_default_allocator
/// function in order to provide the glue between EASTL and whatever their
/// system's default allocator happens to be.
///
/// Example usage:
/// MyAllocatorType* gpSystemAllocator;
///
/// MyAllocatorType* get_default_allocator(const MyAllocatorType*)
/// { return gpSystemAllocator; }
///
template <typename Allocator>
Allocator* get_default_allocator(const Allocator*);
EASTLAllocatorType* get_default_allocator(const EASTLAllocatorType*);
/// default_allocfreemethod
///
/// Implements a default allocfreemethod which uses the default global allocator.
/// This version supports only default alignment.
///
void* default_allocfreemethod(size_t n, void* pBuffer, void* /*pContext*/);
/// allocate_memory
///
/// This is a memory allocation dispatching function.
/// To do: Make aligned and unaligned specializations.
/// Note that to do this we will need to use a class with a static
/// function instead of a standalone function like below.
///
template <typename Allocator>
void* allocate_memory(Allocator& a, size_t n, size_t alignment, size_t alignmentOffset);
} // namespace eastl
#ifndef EASTL_USER_DEFINED_ALLOCATOR // If the user hasn't declared that he has defined a different allocator implementation elsewhere...
EA_DISABLE_ALL_VC_WARNINGS()
#include <new>
EA_RESTORE_ALL_VC_WARNINGS()
#if !EASTL_DLL // If building a regular library and not building EASTL as a DLL...
// It is expected that the application define the following
// versions of operator new for the application. Either that or the
// user needs to override the implementation of the allocator class.
void* operator new[](size_t size, const char* pName, int flags, unsigned debugFlags, const char* file, int line);
void* operator new[](size_t size, size_t alignment, size_t alignmentOffset, const char* pName, int flags, unsigned debugFlags, const char* file, int line);
#endif
namespace eastl
{
inline allocator::allocator(const char* EASTL_NAME(pName))
{
#if EASTL_NAME_ENABLED
mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
#endif
}
inline allocator::allocator(const allocator& EASTL_NAME(alloc))
{
#if EASTL_NAME_ENABLED
mpName = alloc.mpName;
#endif
}
inline allocator::allocator(const allocator&, const char* EASTL_NAME(pName))
{
#if EASTL_NAME_ENABLED
mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
#endif
}
inline allocator& allocator::operator=(const allocator& EASTL_NAME(alloc))
{
#if EASTL_NAME_ENABLED
mpName = alloc.mpName;
#endif
return *this;
}
inline const char* allocator::get_name() const
{
#if EASTL_NAME_ENABLED
return mpName;
#else
return EASTL_ALLOCATOR_DEFAULT_NAME;
#endif
}
inline void allocator::set_name(const char* EASTL_NAME(pName))
{
#if EASTL_NAME_ENABLED
mpName = pName;
#endif
}
inline void* allocator::allocate(size_t n, int flags)
{
#if EASTL_NAME_ENABLED
#define pName mpName
#else
#define pName EASTL_ALLOCATOR_DEFAULT_NAME
#endif
#if EASTL_DLL
return allocate(n, EASTL_SYSTEM_ALLOCATOR_MIN_ALIGNMENT, 0, flags);
#elif (EASTL_DEBUGPARAMS_LEVEL <= 0)
return ::new((char*)0, flags, 0, (char*)0, 0) char[n];
#elif (EASTL_DEBUGPARAMS_LEVEL == 1)
return ::new( pName, flags, 0, (char*)0, 0) char[n];
#else
return ::new( pName, flags, 0, __FILE__, __LINE__) char[n];
#endif
}
inline void* allocator::allocate(size_t n, size_t alignment, size_t offset, int flags)
{
#if EASTL_DLL
// We currently have no support for implementing flags when
// using the C runtime library operator new function. The user
// can use SetDefaultAllocator to override the default allocator.
EA_UNUSED(offset); EA_UNUSED(flags);
size_t adjustedAlignment = (alignment > EA_PLATFORM_PTR_SIZE) ? alignment : EA_PLATFORM_PTR_SIZE;
void* p = new char[n + adjustedAlignment + EA_PLATFORM_PTR_SIZE];
void* pPlusPointerSize = (void*)((uintptr_t)p + EA_PLATFORM_PTR_SIZE);
void* pAligned = (void*)(((uintptr_t)pPlusPointerSize + adjustedAlignment - 1) & ~(adjustedAlignment - 1));
void** pStoredPtr = (void**)pAligned - 1;
EASTL_ASSERT(pStoredPtr >= p);
*(pStoredPtr) = p;
EASTL_ASSERT(((size_t)pAligned & ~(alignment - 1)) == (size_t)pAligned);
return pAligned;
#elif (EASTL_DEBUGPARAMS_LEVEL <= 0)
return ::new(alignment, offset, (char*)0, flags, 0, (char*)0, 0) char[n];
#elif (EASTL_DEBUGPARAMS_LEVEL == 1)
return ::new(alignment, offset, pName, flags, 0, (char*)0, 0) char[n];
#else
return ::new(alignment, offset, pName, flags, 0, __FILE__, __LINE__) char[n];
#endif
#undef pName // See above for the definition of this.
}
inline void allocator::deallocate(void* p, size_t)
{
#if EASTL_DLL
if (p != nullptr)
{
void* pOriginalAllocation = *((void**)p - 1);
delete[](char*)pOriginalAllocation;
}
#else
delete[](char*)p;
#endif
}
inline bool operator==(const allocator&, const allocator&)
{
return true; // All allocators are considered equal, as they merely use global new/delete.
}
#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
inline bool operator!=(const allocator&, const allocator&)
{
return false; // All allocators are considered equal, as they merely use global new/delete.
}
#endif
} // namespace eastl
#endif // EASTL_USER_DEFINED_ALLOCATOR
namespace eastl
{
template <typename Allocator>
inline Allocator* get_default_allocator(const Allocator*)
{
return NULL; // By default we return NULL; the user must make specialization of this function in order to provide their own implementation.
}
inline EASTLAllocatorType* get_default_allocator(const EASTLAllocatorType*)
{
return EASTLAllocatorDefault(); // For the built-in allocator EASTLAllocatorType, we happen to already have a function for returning the default allocator instance, so we provide it.
}
inline void* default_allocfreemethod(size_t n, void* pBuffer, void* /*pContext*/)
{
EASTLAllocatorType* const pAllocator = EASTLAllocatorDefault();
if(pBuffer) // If freeing...
{
EASTLFree(*pAllocator, pBuffer, n);
return NULL; // The return value is meaningless for the free.
}
else // allocating
return EASTLAlloc(*pAllocator, n);
}
/// allocate_memory
///
/// This is a memory allocation dispatching function.
/// To do: Make aligned and unaligned specializations.
/// Note that to do this we will need to use a class with a static
/// function instead of a standalone function like below.
///
template <typename Allocator>
inline void* allocate_memory(Allocator& a, size_t n, size_t alignment, size_t alignmentOffset)
{
void *result;
if (alignment <= EASTL_ALLOCATOR_MIN_ALIGNMENT)
{
result = EASTLAlloc(a, n);
// Ensure the result is correctly aligned. An assertion likely indicates a mismatch between EASTL_ALLOCATOR_MIN_ALIGNMENT and the minimum alignment
// of EASTLAlloc. If there is a mismatch it may be necessary to define EASTL_ALLOCATOR_MIN_ALIGNMENT to be the minimum alignment of EASTLAlloc, or
// to increase the alignment of EASTLAlloc to match EASTL_ALLOCATOR_MIN_ALIGNMENT.
EASTL_ASSERT((reinterpret_cast<size_t>(result)& ~(alignment - 1)) == reinterpret_cast<size_t>(result));
}
else
{
result = EASTLAllocAligned(a, n, alignment, alignmentOffset);
// Ensure the result is correctly aligned. An assertion here may indicate a bug in the allocator.
auto resultMinusOffset = (char*)result - alignmentOffset;
EA_UNUSED(resultMinusOffset);
EASTL_ASSERT((reinterpret_cast<size_t>(resultMinusOffset)& ~(alignment - 1)) == reinterpret_cast<size_t>(resultMinusOffset));
}
return result;
}
}
#endif // Header include guard
+130
View File
@@ -0,0 +1,130 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_ALLOCATOR_MALLOC_H
#define EASTL_ALLOCATOR_MALLOC_H
#include <EABase/eahave.h>
#include <EASTL/allocator.h>
#include <stddef.h>
// EASTL_ALIGNED_MALLOC_AVAILABLE
//
// Identifies if the standard library provides a built-in aligned version of malloc.
// Defined as 0 or 1, depending on the standard library or platform availability.
// None of the viable C functions provides for an aligned malloc with offset, so we
// don't consider that supported in any case.
//
// Options for aligned allocations:
// C11 aligned_alloc http://linux.die.net/man/3/aligned_alloc
// glibc memalign http://linux.die.net/man/3/posix_memalign
// Posix posix_memalign http://pubs.opengroup.org/onlinepubs/000095399/functions/posix_memalign.html
// VC++ _aligned_malloc http://msdn.microsoft.com/en-us/library/8z34s9c6%28VS.80%29.aspx This is not suitable, since it has a limitation that you need to free via _aligned_free.
//
#if !defined EASTL_ALIGNED_MALLOC_AVAILABLE
#if defined(EA_PLATFORM_POSIX) && !defined(EA_PLATFORM_APPLE)
// memalign is more consistently available than posix_memalign, though its location isn't consistent across
// platforms and compiler libraries. Typically it's declared in one of three headers: stdlib.h, malloc.h, or malloc/malloc.h
#include <stdlib.h> // memalign, posix_memalign.
#define EASTL_ALIGNED_MALLOC_AVAILABLE 1
#if EA_HAS_INCLUDE_AVAILABLE
#if EA_HAS_INCLUDE(<malloc/malloc.h>)
#include <malloc/malloc.h>
#elif EA_HAS_INCLUDE(<malloc.h>)
#include <malloc.h>
#endif
#elif defined(EA_PLATFORM_BSD)
#include <malloc/malloc.h>
#elif defined(__clang__)
#if __has_include(<malloc/malloc.h>)
#include <malloc/malloc.h>
#elif __has_include(<malloc.h>)
#include <malloc.h>
#endif
#else
#include <malloc.h>
#endif
#else
#define EASTL_ALIGNED_MALLOC_AVAILABLE 0
#endif
#endif
namespace eastl
{
///////////////////////////////////////////////////////////////////////////////
// allocator_malloc
//
// Implements an EASTL allocator that uses malloc/free as opposed to
// new/delete or PPMalloc Malloc/Free.
//
// Example usage:
// vector<int, allocator_malloc> intVector;
//
class allocator_malloc
{
public:
allocator_malloc(const char* = NULL)
{ }
allocator_malloc(const allocator_malloc&)
{ }
allocator_malloc(const allocator_malloc&, const char*)
{ }
allocator_malloc& operator=(const allocator_malloc&)
{ return *this; }
bool operator==(const allocator_malloc&)
{ return true; }
bool operator!=(const allocator_malloc&)
{ return false; }
void* allocate(size_t n, int /*flags*/ = 0)
{ return malloc(n); }
void* allocate(size_t n, size_t alignment, size_t alignmentOffset, int /*flags*/ = 0)
{
#if EASTL_ALIGNED_MALLOC_AVAILABLE
if((alignmentOffset % alignment) == 0) // We check for (offset % alignmnent == 0) instead of (offset == 0) because any block which is aligned on e.g. 64 also is aligned at an offset of 64 by definition.
return memalign(alignment, n); // memalign is more consistently available than posix_memalign.
#else
if((alignment <= EASTL_SYSTEM_ALLOCATOR_MIN_ALIGNMENT) && ((alignmentOffset % alignment) == 0))
return malloc(n);
#endif
return NULL;
}
void deallocate(void* p, size_t /*n*/)
{ free(p); }
const char* get_name() const
{ return "allocator_malloc"; }
void set_name(const char*)
{ }
};
} // namespace eastl
#endif // Header include guard
+652
View File
@@ -0,0 +1,652 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements the eastl::any which is part of the C++ standard STL
// library specification.
//
// eastl::any is a type-safe container for single values of any type. Our
// implementation makes use of the "small local buffer" optimization to avoid
// unnecessary dynamic memory allocation if the specified type is eligible to
// be stored in its local buffer. The user type must satisfy the size
// requirements and must be no-throw move-constructible to qualify for the local
// buffer optimization.
//
// To consider: Implement a fixed_any<SIZE> variant to allow users to customize
// the size of the "small local buffer" optimization.
//
// http://en.cppreference.com/w/cpp/utility/any
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_ANY_H
#define EASTL_ANY_H
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
#include <EASTL/internal/config.h>
#include <EASTL/internal/in_place_t.h>
#if EASTL_RTTI_ENABLED
#include <typeinfo>
#endif
#if EASTL_EXCEPTIONS_ENABLED
#include <exception>
#endif
namespace eastl
{
///////////////////////////////////////////////////////////////////////////////
// bad_any_cast
//
// The type thrown by any_cast on failure.
//
// http://en.cppreference.com/w/cpp/utility/any/bad_any_cast
//
#if EASTL_EXCEPTIONS_ENABLED
struct bad_cast : std::exception
{
const char* what() const EA_NOEXCEPT EA_OVERRIDE
{ return "bad cast"; }
};
struct bad_any_cast : public bad_cast
{
const char* what() const EA_NOEXCEPT EA_OVERRIDE
{ return "bad_any_cast"; }
};
#endif
namespace Internal
{
// utility to switch between exceptions and asserts
inline void DoBadAnyCast()
{
#if EASTL_EXCEPTIONS_ENABLED
throw bad_any_cast();
#else
EASTL_ASSERT_MSG(false, "bad_any_cast\n");
// NOTE(rparolin): CRASH!
// You crashed here because you requested a type that was not contained in the object.
// We choose to intentionally crash here instead of returning invalid data to the calling
// code which could cause hard to track down bugs.
*((volatile int*)0) = 0xDEADC0DE;
#endif
}
template<typename T, typename... Args>
void* DefaultConstruct(Args&&... args)
{
auto* pMem = EASTLAllocatorDefault()->allocate(sizeof(T), alignof(T), 0);
return ::new(pMem) T(eastl::forward<Args>(args)...);
}
template<typename T>
void DefaultDestroy(T* p)
{
p->~T();
EASTLAllocatorDefault()->deallocate(static_cast<void*>(p), sizeof(T));
}
}
///////////////////////////////////////////////////////////////////////////////
// 20.7.3, class any
//
class any
{
//////////////////////////////////////////////////////////////////////////////////////////
// storage_operation
//
// operations supported by the storage handler
//
enum class storage_operation
{
GET,
DESTROY,
COPY,
MOVE,
TYPE_INFO
};
//////////////////////////////////////////////////////////////////////////////////////////
// storage
//
// the underlying storage type which enables the switching between objects stored in
// the heap and objects stored within the any type.
//
union storage
{
typedef aligned_storage_t<4 * sizeof(void*), alignment_of<void*>::value> internal_storage_t;
void* external_storage = nullptr;
internal_storage_t internal_storage;
};
//////////////////////////////////////////////////////////////////////////////////////////
// use_internal_storage
//
// determines when the "local buffer optimization" is used
//
template <typename T>
using use_internal_storage = bool_constant
<
is_nothrow_move_constructible<T>::value
&& (sizeof(T) <= sizeof(storage)) &&
(alignment_of<storage>::value % alignment_of<T>::value == 0)
>;
//////////////////////////////////////////////////////////////////////////////////////////
// non-member friend functions
//
template <class ValueType> friend const ValueType* any_cast(const any* pAny) EA_NOEXCEPT;
template <class ValueType> friend ValueType* any_cast(any* pAny) EA_NOEXCEPT;
template <class ValueType> friend ValueType any_cast(const any& operand);
template <class ValueType> friend ValueType any_cast(any& operand);
template <class ValueType> friend ValueType any_cast(any&& operand);
//Adding Unsafe any cast operations
template <class ValueType> friend const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT;
template <class ValueType> friend ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT;
//////////////////////////////////////////////////////////////////////////////////////////
// internal storage handler
//
template <typename T>
struct storage_handler_internal
{
template <typename V>
static void construct(storage& s, V&& v)
{
::new(&s.internal_storage) T(eastl::forward<V>(v));
}
template <typename... Args>
static void construct_inplace(storage& s, Args... args)
{
::new(&s.internal_storage) T(eastl::forward<Args>(args)...);
}
template <class NT, class U, class... Args>
static void construct_inplace(storage& s, std::initializer_list<U> il, Args&&... args)
{
::new(&s.internal_storage) NT(il, eastl::forward<Args>(args)...);
}
static inline void destroy(any& refAny)
{
T& t = *static_cast<T*>(static_cast<void*>(&refAny.m_storage.internal_storage));
EA_UNUSED(t);
t.~T();
refAny.m_handler = nullptr;
}
static void* handler_func(storage_operation op, const any* pThis, any* pOther)
{
switch (op)
{
case storage_operation::GET:
{
EASTL_ASSERT(pThis);
return (void*)(&pThis->m_storage.internal_storage);
}
break;
case storage_operation::DESTROY:
{
EASTL_ASSERT(pThis);
destroy(const_cast<any&>(*pThis));
}
break;
case storage_operation::COPY:
{
EASTL_ASSERT(pThis);
EASTL_ASSERT(pOther);
construct(pOther->m_storage, *(T*)(&pThis->m_storage.internal_storage));
}
break;
case storage_operation::MOVE:
{
EASTL_ASSERT(pThis);
EASTL_ASSERT(pOther);
construct(pOther->m_storage, eastl::move(*(T*)(&pThis->m_storage.internal_storage)));
destroy(const_cast<any&>(*pThis));
}
break;
case storage_operation::TYPE_INFO:
{
#if EASTL_RTTI_ENABLED
return (void*)&typeid(T);
#endif
}
break;
default:
{
EASTL_ASSERT_MSG(false, "unknown storage operation\n");
}
break;
};
return nullptr;
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// external storage handler
//
template <typename T>
struct storage_handler_external
{
template <typename V>
static inline void construct(storage& s, V&& v)
{
s.external_storage = Internal::DefaultConstruct<T>(eastl::forward<V>(v));
}
template <typename... Args>
static inline void construct_inplace(storage& s, Args... args)
{
s.external_storage = Internal::DefaultConstruct<T>(eastl::forward<Args>(args)...);
}
template <class NT, class U, class... Args>
static inline void construct_inplace(storage& s, std::initializer_list<U> il, Args&&... args)
{
s.external_storage = Internal::DefaultConstruct<NT>(il, eastl::forward<Args>(args)...);
}
static inline void destroy(any& refAny)
{
Internal::DefaultDestroy(static_cast<T*>(refAny.m_storage.external_storage));
refAny.m_handler = nullptr;
}
static void* handler_func(storage_operation op, const any* pThis, any* pOther)
{
switch (op)
{
case storage_operation::GET:
{
EASTL_ASSERT(pThis);
EASTL_ASSERT(pThis->m_storage.external_storage);
return static_cast<void*>(pThis->m_storage.external_storage);
}
break;
case storage_operation::DESTROY:
{
EASTL_ASSERT(pThis);
destroy(*const_cast<any*>(pThis));
}
break;
case storage_operation::COPY:
{
EASTL_ASSERT(pThis);
EASTL_ASSERT(pOther);
construct(pOther->m_storage, *static_cast<T*>(pThis->m_storage.external_storage));
}
break;
case storage_operation::MOVE:
{
EASTL_ASSERT(pThis);
EASTL_ASSERT(pOther);
construct(pOther->m_storage, eastl::move(*(T*)(pThis->m_storage.external_storage)));
destroy(const_cast<any&>(*pThis));
}
break;
case storage_operation::TYPE_INFO:
{
#if EASTL_RTTI_ENABLED
return (void*)&typeid(T);
#endif
}
break;
default:
{
EASTL_ASSERT_MSG(false, "unknown storage operation\n");
}
break;
};
return nullptr;
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// storage_handler_ptr
//
// defines the function signature of the storage handler that both the internal and
// external storage handlers must implement to retrieve the underlying type of the any
// object.
//
using storage_handler_ptr = void* (*)(storage_operation, const any*, any*);
//////////////////////////////////////////////////////////////////////////////////////////
// storage_handler
//
// based on the specified type T we select the appropriate underlying storage handler
// based on the 'use_internal_storage' trait.
//
template <typename T>
using storage_handler = typename conditional<use_internal_storage<T>::value,
storage_handler_internal<T>,
storage_handler_external<T>>::type;
//////////////////////////////////////////////////////////////////////////////////////////
// data layout
//
storage m_storage;
storage_handler_ptr m_handler;
public:
#ifndef EA_COMPILER_GNUC
// TODO(rparolin): renable constexpr for GCC
EA_CONSTEXPR
#endif
any() EA_NOEXCEPT
: m_storage(), m_handler(nullptr) {}
any(const any& other) : m_handler(nullptr)
{
if (other.m_handler)
{
// NOTE(rparolin): You can not simply copy the underlying
// storage because it could hold a pointer to an object on the
// heap which breaks the copy semantics of the language.
other.m_handler(storage_operation::COPY, &other, this);
m_handler = other.m_handler;
}
}
any(any&& other) EA_NOEXCEPT : m_handler(nullptr)
{
if(other.m_handler)
{
// NOTE(rparolin): You can not simply move the underlying
// storage because because the storage class has effectively
// type erased user type so we have to defer to the handler
// function to get the type back and pass on the move request.
m_handler = eastl::move(other.m_handler);
other.m_handler(storage_operation::MOVE, &other, this);
}
}
~any() { reset(); }
template <class ValueType>
any(ValueType&& value,
typename eastl::enable_if<!eastl::is_same<typename eastl::decay<ValueType>::type, any>::value>::type* = 0)
{
typedef decay_t<ValueType> DecayedValueType;
static_assert(is_copy_constructible<DecayedValueType>::value, "ValueType must be copy-constructible");
storage_handler<DecayedValueType>::construct(m_storage, eastl::forward<ValueType>(value));
m_handler = &storage_handler<DecayedValueType>::handler_func;
}
template <class T, class... Args>
explicit any(in_place_type_t<T>, Args&&... args)
{
typedef storage_handler<decay_t<T>> StorageHandlerT;
static_assert(eastl::is_constructible<T, Args...>::value, "T must be constructible with Args...");
StorageHandlerT::construct_inplace(m_storage, eastl::forward<Args>(args)...);
m_handler = &StorageHandlerT::handler_func;
}
template <class T, class U, class... Args>
explicit any(in_place_type_t<T>,
std::initializer_list<U> il,
Args&&... args,
typename eastl::enable_if<eastl::is_constructible<T, std::initializer_list<U>&, Args...>::value,
void>::type* = 0)
{
typedef storage_handler<decay_t<T>> StorageHandlerT;
StorageHandlerT::construct_inplace(m_storage, il, eastl::forward<Args>(args)...);
m_handler = &StorageHandlerT::handler_func;
}
// 20.7.3.2, assignments
template <class ValueType>
any& operator=(ValueType&& value)
{
static_assert(is_copy_constructible<decay_t<ValueType>>::value, "ValueType must be copy-constructible");
any(eastl::forward<ValueType>(value)).swap(*this);
return *this;
}
any& operator=(const any& other)
{
any(other).swap(*this);
return *this;
}
any& operator=(any&& other) EA_NOEXCEPT
{
any(eastl::move(other)).swap(*this);
return *this;
}
// 20.7.3.3, modifiers
#if EASTL_VARIADIC_TEMPLATES_ENABLED
template <class T, class... Args>
void emplace(Args&&... args)
{
typedef storage_handler<decay_t<T>> StorageHandlerT;
static_assert(eastl::is_constructible<T, Args...>::value, "T must be constructible with Args...");
reset();
StorageHandlerT::construct_inplace(m_storage, eastl::forward<Args>(args)...);
m_handler = &StorageHandlerT::handler_func;
}
template <class NT, class U, class... Args>
typename eastl::enable_if<eastl::is_constructible<NT, std::initializer_list<U>&, Args...>::value, void>::type
emplace(std::initializer_list<U> il, Args&&... args)
{
typedef storage_handler<decay_t<NT>> StorageHandlerT;
reset();
StorageHandlerT::construct_inplace(m_storage, il, eastl::forward<Args>(args)...);
m_handler = &StorageHandlerT::handler_func;
}
#endif
void reset() EA_NOEXCEPT
{
if(m_handler)
m_handler(storage_operation::DESTROY, this, nullptr);
}
void swap(any& other) EA_NOEXCEPT
{
if(this == &other)
return;
if(m_handler && other.m_handler)
{
any tmp;
tmp.m_handler = other.m_handler;
other.m_handler(storage_operation::MOVE, &other, &tmp);
other.m_handler = m_handler;
m_handler(storage_operation::MOVE, this, &other);
m_handler = tmp.m_handler;
tmp.m_handler(storage_operation::MOVE, &tmp, this);
}
else if (m_handler == nullptr && other.m_handler)
{
eastl::swap(m_handler, other.m_handler);
m_handler(storage_operation::MOVE, &other, this);
}
else if(m_handler && other.m_handler == nullptr)
{
eastl::swap(m_handler, other.m_handler);
other.m_handler(storage_operation::MOVE, this, &other);
}
//else if (m_handler == nullptr && other.m_handler == nullptr)
//{
// // nothing to swap
//}
}
// 20.7.3.4, observers
bool has_value() const EA_NOEXCEPT { return m_handler != nullptr; }
#if EASTL_RTTI_ENABLED
inline const std::type_info& type() const EA_NOEXCEPT
{
if(m_handler)
{
auto* pTypeInfo = m_handler(storage_operation::TYPE_INFO, this, nullptr);
return *static_cast<const std::type_info*>(pTypeInfo);
}
else
{
return typeid(void);
}
}
#endif
};
//////////////////////////////////////////////////////////////////////////////////////////
// 20.7.4, non-member functions
//
inline void swap(any& rhs, any& lhs) EA_NOEXCEPT { rhs.swap(lhs); }
//////////////////////////////////////////////////////////////////////////////////////////
// 20.7.4, The non-member any_cast functions provide type-safe access to the contained object.
//
template <class ValueType>
inline ValueType any_cast(const any& operand)
{
static_assert(eastl::is_reference<ValueType>::value || eastl::is_copy_constructible<ValueType>::value,
"ValueType must be a reference or copy constructible");
auto* p = any_cast<typename add_const<typename remove_reference<ValueType>::type>::type>(&operand);
if(p == nullptr)
Internal::DoBadAnyCast();
return *p;
}
template <class ValueType>
inline ValueType any_cast(any& operand)
{
static_assert(eastl::is_reference<ValueType>::value || eastl::is_copy_constructible<ValueType>::value,
"ValueType must be a reference or copy constructible");
auto* p = any_cast<typename remove_reference<ValueType>::type>(&operand);
if(p == nullptr)
Internal::DoBadAnyCast();
return *p;
}
template <class ValueType>
inline ValueType any_cast(any&& operand)
{
static_assert(eastl::is_reference<ValueType>::value || eastl::is_copy_constructible<ValueType>::value,
"ValueType must be a reference or copy constructible");
auto* p = any_cast<typename remove_reference<ValueType>::type>(&operand);
if (p == nullptr)
Internal::DoBadAnyCast();
return *p;
}
// NOTE(rparolin): The runtime type check was commented out because in DLL builds the templated function pointer
// value will be different -- completely breaking the validation mechanism. Due to the fact that eastl::any uses
// type erasure we can't refresh (on copy/move) the cached function pointer to the internal handler function because
// we don't statically know the type.
template <class ValueType>
inline const ValueType* any_cast(const any* pAny) EA_NOEXCEPT
{
return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler<decay_t<ValueType>>::handler_func)
#if EASTL_RTTI_ENABLED
&& pAny->type() == typeid(typename remove_reference<ValueType>::type)
#endif
) ?
static_cast<const ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) :
nullptr;
}
template <class ValueType>
inline ValueType* any_cast(any* pAny) EA_NOEXCEPT
{
return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler<decay_t<ValueType>>::handler_func)
#if EASTL_RTTI_ENABLED
&& pAny->type() == typeid(typename remove_reference<ValueType>::type)
#endif
) ?
static_cast<ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) :
nullptr;
}
//Unsafe operations - use with caution
template <class ValueType>
inline const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT
{
return unsafe_any_cast<ValueType>(const_cast<any*>(pAny));
}
template <class ValueType>
inline ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT
{
return static_cast<ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr));
}
//////////////////////////////////////////////////////////////////////////////////////////
// make_any
//
#if EASTL_VARIADIC_TEMPLATES_ENABLED
template <class T, class... Args>
inline any make_any(Args&&... args)
{
return any(eastl::in_place<T>, eastl::forward<Args>(args)...);
}
template <class T, class U, class... Args>
inline any make_any(std::initializer_list<U> il, Args&&... args)
{
return any(eastl::in_place<T>, il, eastl::forward<Args>(args)...);
}
#endif
} // namespace eastl
#endif // EASTL_ANY_H
+589
View File
@@ -0,0 +1,589 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Implements a templated array class as per the C++ standard TR1 (technical
// report 1, which is a list of proposed C++ library amendments).
// The primary distinctions between this array and TR1 array are:
// - array::size_type is defined as eastl_size_t instead of size_t in order
// to save memory and run faster on 64 bit systems.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_ARRAY_H
#define EASTL_ARRAY_H
#include <EASTL/internal/config.h>
#include <EASTL/iterator.h>
#include <EASTL/algorithm.h>
#include <EASTL/utility.h>
#include <stddef.h>
#if EASTL_EXCEPTIONS_ENABLED
EA_DISABLE_ALL_VC_WARNINGS()
#include <stdexcept> // std::out_of_range, std::length_error.
EA_RESTORE_ALL_VC_WARNINGS()
#endif
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
///////////////////////////////////////////////////////////////////////
/// array
///
/// Implements a templated array class as per the C++ standard TR1.
/// This class allows you to use a built-in C style array like an STL vector.
/// It does not let you change its size, as it is just like a C built-in array.
/// Our implementation here strives to remove function call nesting, as that
/// makes it hard for us to profile debug builds due to function call overhead.
/// Note that this is intentionally a struct with public data, as per the
/// C++ standard update proposal requirements.
///
/// Example usage:
/// array<int, 5> a = { { 0, 1, 2, 3, 4 } }; // Strict compilers such as GCC require the double brackets.
/// a[2] = 4;
/// for(array<int, 5>::iterator i = a.begin(); i < a.end(); ++i)
/// *i = 0;
///
template <typename T, size_t N = 1>
struct array
{
public:
typedef array<T, N> this_type;
typedef T value_type;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef value_type* iterator;
typedef const value_type* const_iterator;
typedef eastl::reverse_iterator<iterator> reverse_iterator;
typedef eastl::reverse_iterator<const_iterator> const_reverse_iterator;
typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t.
typedef ptrdiff_t difference_type;
public:
enum
{
count = N
};
// Note that the member data is intentionally public.
// This allows for aggregate initialization of the
// object (e.g. array<int, 5> a = { 0, 3, 2, 4 }; )
value_type mValue[N ? N : 1];
public:
// We intentionally provide no constructor, destructor, or assignment operator.
void fill(const value_type& value);
// Unlike the swap function for other containers, array::swap takes linear time,
// may exit via an exception, and does not cause iterators to become associated with the other container.
void swap(this_type& x) EA_NOEXCEPT_IF(eastl::is_nothrow_swappable<value_type>::value);
EA_CPP14_CONSTEXPR iterator begin() EA_NOEXCEPT;
EA_CPP14_CONSTEXPR const_iterator begin() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR const_iterator cbegin() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR iterator end() EA_NOEXCEPT;
EA_CPP14_CONSTEXPR const_iterator end() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR const_iterator cend() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR reverse_iterator rbegin() EA_NOEXCEPT;
EA_CPP14_CONSTEXPR const_reverse_iterator rbegin() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR const_reverse_iterator crbegin() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR reverse_iterator rend() EA_NOEXCEPT;
EA_CPP14_CONSTEXPR const_reverse_iterator rend() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR const_reverse_iterator crend() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR bool empty() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR size_type size() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR size_type max_size() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR T* data() EA_NOEXCEPT;
EA_CPP14_CONSTEXPR const T* data() const EA_NOEXCEPT;
EA_CPP14_CONSTEXPR reference operator[](size_type i);
EA_CPP14_CONSTEXPR const_reference operator[](size_type i) const;
EA_CPP14_CONSTEXPR const_reference at(size_type i) const;
EA_CPP14_CONSTEXPR reference at(size_type i);
EA_CPP14_CONSTEXPR reference front();
EA_CPP14_CONSTEXPR const_reference front() const;
EA_CPP14_CONSTEXPR reference back();
EA_CPP14_CONSTEXPR const_reference back() const;
bool validate() const;
int validate_iterator(const_iterator i) const;
}; // class array
///////////////////////////////////////////////////////////////////////////
// template deduction guides
///////////////////////////////////////////////////////////////////////////
#ifdef __cpp_deduction_guides
template <class T, class... U> array(T, U...) -> array<T, 1 + sizeof...(U)>;
#endif
///////////////////////////////////////////////////////////////////////
// array
///////////////////////////////////////////////////////////////////////
template <typename T, size_t N>
inline void array<T, N>::fill(const value_type& value)
{
eastl::fill_n(&mValue[0], N, value);
}
template <typename T, size_t N>
inline void array<T, N>::swap(this_type& x) EA_NOEXCEPT_IF(eastl::is_nothrow_swappable<value_type>::value)
{
eastl::swap_ranges(&mValue[0], &mValue[N], &x.mValue[0]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::iterator
array<T, N>::begin() EA_NOEXCEPT
{
return &mValue[0];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_iterator
array<T, N>::begin() const EA_NOEXCEPT
{
return &mValue[0];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_iterator
array<T, N>::cbegin() const EA_NOEXCEPT
{
return &mValue[0];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::iterator
array<T, N>::end() EA_NOEXCEPT
{
return &mValue[N];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_iterator
array<T, N>::end() const EA_NOEXCEPT
{
return &mValue[N];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_iterator
array<T, N>::cend() const EA_NOEXCEPT
{
return &mValue[N];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::reverse_iterator
array<T, N>::rbegin() EA_NOEXCEPT
{
return reverse_iterator(&mValue[N]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reverse_iterator
array<T, N>::rbegin() const EA_NOEXCEPT
{
return const_reverse_iterator(&mValue[N]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reverse_iterator
array<T, N>::crbegin() const EA_NOEXCEPT
{
return const_reverse_iterator(&mValue[N]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::reverse_iterator
array<T, N>::rend() EA_NOEXCEPT
{
return reverse_iterator(&mValue[0]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reverse_iterator
array<T, N>::rend() const EA_NOEXCEPT
{
return const_reverse_iterator(static_cast<const_iterator>(&mValue[0]));
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reverse_iterator
array<T, N>::crend() const EA_NOEXCEPT
{
return const_reverse_iterator(static_cast<const_iterator>(&mValue[0]));
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::size_type
array<T, N>::size() const EA_NOEXCEPT
{
return (size_type)N;
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::size_type
array<T, N>::max_size() const EA_NOEXCEPT
{
return (size_type)N;
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline bool array<T, N>::empty() const EA_NOEXCEPT
{
return (N == 0);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::reference
array<T, N>::operator[](size_type i)
{
return mValue[i];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reference
array<T, N>::operator[](size_type i) const
{
return mValue[i];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::reference
array<T, N>::front()
{
return mValue[0];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reference
array<T, N>::front() const
{
return mValue[0];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::reference
array<T, N>::back()
{
return mValue[N - 1];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reference
array<T, N>::back() const
{
return mValue[N - 1];
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline T* array<T, N>::data() EA_NOEXCEPT
{
return mValue;
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline const T* array<T, N>::data() const EA_NOEXCEPT
{
return mValue;
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::const_reference array<T, N>::at(size_type i) const
{
#if EASTL_EXCEPTIONS_ENABLED
if(EASTL_UNLIKELY(i >= N))
throw std::out_of_range("array::at -- out of range");
#elif EASTL_ASSERT_ENABLED
if(EASTL_UNLIKELY(i >= N))
EASTL_FAIL_MSG("array::at -- out of range");
#endif
return static_cast<const_reference>(mValue[i]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline typename array<T, N>::reference array<T, N>::at(size_type i)
{
#if EASTL_EXCEPTIONS_ENABLED
if(EASTL_UNLIKELY(i >= N))
throw std::out_of_range("array::at -- out of range");
#elif EASTL_ASSERT_ENABLED
if(EASTL_UNLIKELY(i >= N))
EASTL_FAIL_MSG("array::at -- out of range");
#endif
return static_cast<reference>(mValue[i]);
}
template <typename T, size_t N>
inline bool array<T, N>::validate() const
{
return true; // There is nothing to do.
}
template <typename T, size_t N>
inline int array<T, N>::validate_iterator(const_iterator i) const
{
if(i >= mValue)
{
if(i < (mValue + N))
return (isf_valid | isf_current | isf_can_dereference);
if(i <= (mValue + N))
return (isf_valid | isf_current);
}
return isf_none;
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline bool operator==(const array<T, N>& a, const array<T, N>& b)
{
return eastl::equal(&a.mValue[0], &a.mValue[N], &b.mValue[0]);
}
#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
template <typename T, size_t N>
inline synth_three_way_result<T> operator<=>(const array<T, N>& a, const array<T,N>& b)
{
return eastl::lexicographical_compare_three_way(&a.mValue[0], &a.mValue[N], &b.mValue[0], &b.mValue[N], synth_three_way{});
}
#else
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline bool operator<(const array<T, N>& a, const array<T, N>& b)
{
return eastl::lexicographical_compare(&a.mValue[0], &a.mValue[N], &b.mValue[0], &b.mValue[N]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline bool operator!=(const array<T, N>& a, const array<T, N>& b)
{
return !eastl::equal(&a.mValue[0], &a.mValue[N], &b.mValue[0]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline bool operator>(const array<T, N>& a, const array<T, N>& b)
{
return eastl::lexicographical_compare(&b.mValue[0], &b.mValue[N], &a.mValue[0], &a.mValue[N]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline bool operator<=(const array<T, N>& a, const array<T, N>& b)
{
return !eastl::lexicographical_compare(&b.mValue[0], &b.mValue[N], &a.mValue[0], &a.mValue[N]);
}
template <typename T, size_t N>
EA_CPP14_CONSTEXPR inline bool operator>=(const array<T, N>& a, const array<T, N>& b)
{
return !eastl::lexicographical_compare(&a.mValue[0], &a.mValue[N], &b.mValue[0], &b.mValue[N]);
}
#endif
template <typename T, size_t N>
inline void swap(array<T, N>& a, array<T, N>& b)
{
eastl::swap_ranges(&a.mValue[0], &a.mValue[N], &b.mValue[0]);
}
///////////////////////////////////////////////////////////////////////
// to_array
///////////////////////////////////////////////////////////////////////
namespace internal
{
template<class T, size_t N, size_t... I>
EA_CONSTEXPR auto to_array(T (&a)[N], index_sequence<I...>)
{
return eastl::array<eastl::remove_cv_t<T>, N>{{a[I]...}};
}
template<class T, size_t N, size_t... I>
EA_CONSTEXPR auto to_array(T (&&a)[N], index_sequence<I...>)
{
return eastl::array<eastl::remove_cv_t<T>, N>{{eastl::move(a[I])...}};
}
}
template<class T, size_t N>
EA_CONSTEXPR eastl::array<eastl::remove_cv_t<T>, N> to_array(T (&a)[N])
{
static_assert(eastl::is_constructible_v<T, T&>, "element type T must be copy-initializable");
static_assert(!eastl::is_array_v<T>, "passing multidimensional arrays to to_array is ill-formed");
return internal::to_array(a, eastl::make_index_sequence<N>{});
}
template<class T, size_t N>
EA_CONSTEXPR eastl::array<eastl::remove_cv_t<T>, N> to_array(T (&&a)[N])
{
static_assert(eastl::is_move_constructible_v<T>, "element type T must be move-constructible");
static_assert(!eastl::is_array_v<T>, "passing multidimensional arrays to to_array is ill-formed");
return internal::to_array(eastl::move(a), eastl::make_index_sequence<N>{});
}
#if EASTL_TUPLE_ENABLED
template <typename T, size_t N>
class tuple_size<array<T, N>> : public integral_constant<size_t, N>
{
};
template <typename T, size_t N>
class tuple_size<const array<T, N>> : public integral_constant<size_t, N>
{
};
template <size_t I, typename T, size_t N>
class tuple_element<I, array<T, N>>
{
public:
using type = T;
};
template <size_t I, typename T, size_t N>
class tuple_element<I, const array<T, N>>
{
public:
using type = const T;
};
template <size_t I>
struct GetArray
{
template <typename T, size_t N>
static EA_CONSTEXPR T& getInternal(array<T, N>& a)
{
return a[I];
}
template <typename T, size_t N>
static EA_CONSTEXPR const T& getInternal(const array<T, N>& a)
{
return a[I];
}
template <typename T, size_t N>
static EA_CONSTEXPR T&& getInternal(array<T, N>&& a)
{
return eastl::forward<T>(a[I]);
}
};
template <size_t I, typename T, size_t N>
EA_CONSTEXPR tuple_element_t<I, array<T, N>>& get(array<T, N>& p)
{
return GetArray<I>::getInternal(p);
}
template <size_t I, typename T, size_t N>
EA_CONSTEXPR const tuple_element_t<I, array<T, N>>& get(const array<T, N>& p)
{
return GetArray<I>::getInternal(p);
}
template <size_t I, typename T, size_t N>
EA_CONSTEXPR tuple_element_t<I, array<T, N>>&& get(array<T, N>&& p)
{
return GetArray<I>::getInternal(eastl::move(p));
}
#endif // EASTL_TUPLE_ENABLED
} // namespace eastl
///////////////////////////////////////////////////////////////
// C++17 structured binding support for eastl::array
//
#ifndef EA_COMPILER_NO_STRUCTURED_BINDING
#include <tuple>
template <typename T, size_t N>
class std::tuple_size<::eastl::array<T, N>> : public ::eastl::integral_constant<size_t, N>
{
};
template <size_t I, typename T, size_t N>
struct std::tuple_element<I, ::eastl::array<T, N>>
{
static_assert(I < N, "index is out of bounds");
using type = T;
};
#endif // EA_COMPILER_NO_STRUCTURED_BINDING
#endif // Header include guard
File diff suppressed because it is too large Load Diff
+172
View File
@@ -0,0 +1,172 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_BIT_H
#define EASTL_BIT_H
#include <EASTL/internal/config.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once
#endif
#include <EASTL/internal/memory_base.h>
#include <EASTL/type_traits.h>
#include <string.h> // memcpy
namespace eastl
{
// eastl::bit_cast
// Obtains a value of type To by reinterpreting the object representation of 'from'.
// Every bit in the value representation of the returned To object is equal to the
// corresponding bit in the object representation of 'from'.
//
// In order for bit_cast to be constexpr, the compiler needs to explicitly support
// it by providing the __builtin_bit_cast builtin. If that builtin is not available,
// then we memcpy into aligned storage at runtime and return that instead.
//
// Both types To and From must be equal in size, and must be trivially copyable.
#if defined(EASTL_CONSTEXPR_BIT_CAST_SUPPORTED) && EASTL_CONSTEXPR_BIT_CAST_SUPPORTED
template<typename To, typename From,
typename = eastl::enable_if_t<
sizeof(To) == sizeof(From)
&& eastl::is_trivially_copyable<To>::value
&& eastl::is_trivially_copyable<From>::value
>
>
EA_CONSTEXPR To bit_cast(const From& from) EA_NOEXCEPT
{
return __builtin_bit_cast(To, from);
}
#else
template<typename To, typename From,
typename = eastl::enable_if_t<
sizeof(To) == sizeof(From)
&& eastl::is_trivially_copyable<To>::value
&& eastl::is_trivially_copyable<From>::value
>
>
inline To bit_cast(const From& from) EA_NOEXCEPT
{
typename eastl::aligned_storage<sizeof(To), alignof(To)>::type to;
::memcpy(eastl::addressof(to), eastl::addressof(from), sizeof(To));
return reinterpret_cast<To&>(to);
}
#endif // EASTL_CONSTEXPR_BIT_CAST_SUPPORTED
#if defined(EA_COMPILER_CPP20_ENABLED)
#ifndef EASTL_COUNT_LEADING_ZEROES
#if defined(__GNUC__)
#if (EA_PLATFORM_PTR_SIZE == 8)
#define EASTL_COUNT_LEADING_ZEROES __builtin_clzll
#else
#define EASTL_COUNT_LEADING_ZEROES __builtin_clz
#endif
#endif
#ifndef EASTL_COUNT_LEADING_ZEROES
static inline int eastl_count_leading_zeroes(uint64_t x)
{
if(x)
{
int n = 0;
if(x & UINT64_C(0xFFFFFFFF00000000)) { n += 32; x >>= 32; }
if(x & 0xFFFF0000) { n += 16; x >>= 16; }
if(x & 0xFFFFFF00) { n += 8; x >>= 8; }
if(x & 0xFFFFFFF0) { n += 4; x >>= 4; }
if(x & 0xFFFFFFFC) { n += 2; x >>= 2; }
if(x & 0xFFFFFFFE) { n += 1; }
return 63 - n;
}
return 64;
}
static inline int eastl_count_leading_zeroes(uint32_t x)
{
if(x)
{
int n = 0;
if(x <= 0x0000FFFF) { n += 16; x <<= 16; }
if(x <= 0x00FFFFFF) { n += 8; x <<= 8; }
if(x <= 0x0FFFFFFF) { n += 4; x <<= 4; }
if(x <= 0x3FFFFFFF) { n += 2; x <<= 2; }
if(x <= 0x7FFFFFFF) { n += 1; }
return n;
}
return 32;
}
#define EASTL_COUNT_LEADING_ZEROES eastl_count_leading_zeroes
#endif
#endif
template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>>
EA_CONSTEXPR int countl_zero(const T num) EA_NOEXCEPT
{
EA_CONSTEXPR auto DIGITS = eastl::numeric_limits<T>::digits;
EA_CONSTEXPR auto DIGITS_U = eastl::numeric_limits<unsigned>::digits;
EA_CONSTEXPR auto DIGITS_ULL = eastl::numeric_limits<unsigned long long>::digits;
if (num == 0)
{
return DIGITS;
}
if constexpr (DIGITS <= DIGITS_U)
{
EA_CONSTEXPR auto DIFF = DIGITS_U - DIGITS;
return EASTL_COUNT_LEADING_ZEROES(static_cast<uint32_t>(num)) - DIFF;
}
else
{
EA_CONSTEXPR auto DIFF = DIGITS_ULL - DIGITS;
return EASTL_COUNT_LEADING_ZEROES(static_cast<uint64_t>(num)) - DIFF;
}
}
template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>>
EA_CONSTEXPR bool has_single_bit(const T num) EA_NOEXCEPT
{
return num != 0 && (num & (num - 1)) == 0;
}
template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>>
EA_CONSTEXPR T bit_ceil(const T num) EA_NOEXCEPT
{
if (num <= 1U)
{
return T(1);
}
const auto shift = eastl::numeric_limits<T>::digits - eastl::countl_zero(static_cast<T>(num - 1));
return static_cast<T>(T(1) << shift);
}
template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>>
EA_CONSTEXPR T bit_floor(const T num) EA_NOEXCEPT
{
if (num == 0)
{
return T(0);
}
const auto shift = eastl::numeric_limits<T>::digits - eastl::countl_zero(num) - 1;
return static_cast<T>(T(1) << shift);
}
template <typename T, typename = eastl::enable_if_t<eastl::is_unsigned_v<T>>>
EA_CONSTEXPR T bit_width(const T num) EA_NOEXCEPT
{
return static_cast<T>(eastl::numeric_limits<T>::digits - eastl::countl_zero(num));
}
#endif
} // namespace eastl
#endif // EASTL_BIT_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+88
View File
@@ -0,0 +1,88 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_ADAPTORS_H
#define EASTL_ADAPTORS_H
#include <EASTL/internal/config.h>
#include <EASTL/internal/move_help.h>
#include <EASTL/type_traits.h>
#include <EASTL/iterator.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
EA_DISABLE_VC_WARNING(4512 4626)
#if defined(_MSC_VER) && (_MSC_VER >= 1900) // VS2015+
EA_DISABLE_VC_WARNING(5027) // move assignment operator was implicitly defined as deleted
#endif
namespace eastl
{
/// reverse
///
/// This adaptor allows reverse iteration of a container in ranged base for-loops.
///
/// for (auto& i : reverse(c)) { ... }
///
template <typename Container>
struct reverse_wrapper
{
template <typename C>
reverse_wrapper(C&& c)
: mContainer(eastl::forward<C>(c))
{
/**
* NOTE:
*
* Due to reference collapsing rules of universal references Container type is either
*
* const C& if the input is a const lvalue
* C& if the input is a non-const lvalue
* C if the input is an rvalue
* const C if the input is a const rvalue thus the object will have to be copied and the copy-ctor will be called
*
*
* Thus we either move the whole container into this object or take a reference to the lvalue avoiding the copy.
* The static_assert below ensures this.
*/
static_assert(eastl::is_same_v<C, Container>, "Reference collapsed deduced type must be the same as the deduced Container type!");
}
Container mContainer;
};
template <typename Container>
auto begin(const reverse_wrapper<Container>& w) -> decltype(eastl::rbegin(w.mContainer))
{
return eastl::rbegin(w.mContainer);
}
template <typename Container>
auto end(const reverse_wrapper<Container>& w) -> decltype(eastl::rend(w.mContainer))
{
return eastl::rend(w.mContainer);
}
template <typename Container>
reverse_wrapper<Container> reverse(Container&& c)
{
return reverse_wrapper<Container>(eastl::forward<Container>(c));
}
} // namespace eastl
#if defined(_MSC_VER) && (_MSC_VER >= 1900) // VS2015+
EA_RESTORE_VC_WARNING()
#endif
EA_RESTORE_VC_WARNING()
#endif // Header include guard
+117
View File
@@ -0,0 +1,117 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// The design for call_traits here is very similar to that found in template
// metaprogramming libraries such as Boost, GCC, and Metrowerks, given that
// these libraries have established this interface as a defacto standard for
// solving this problem. Also, these are described in various books on the
// topic of template metaprogramming, such as "Modern C++ Design".
//
// See http://www.boost.org/libs/utility/call_traits.htm or search for
// call_traits in Google for a description of call_traits.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_CALL_TRAITS_H
#define EASTL_CALL_TRAITS_H
#include <EASTL/internal/config.h>
#include <EASTL/type_traits.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
template <typename T, bool small_>
struct ct_imp2 { typedef const T& param_type; };
template <typename T>
struct ct_imp2<T, true> { typedef const T param_type; };
template <typename T, bool isp, bool b1>
struct ct_imp { typedef const T& param_type; };
template <typename T, bool isp>
struct ct_imp<T, isp, true> { typedef typename ct_imp2<T, sizeof(T) <= sizeof(void*)>::param_type param_type; };
template <typename T, bool b1>
struct ct_imp<T, true, b1> { typedef T const param_type; };
template <typename T>
struct call_traits
{
public:
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef typename ct_imp<T, is_pointer<T>::value, is_arithmetic<T>::value>::param_type param_type;
};
template <typename T>
struct call_traits<T&>
{
typedef T& value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T& param_type;
};
template <typename T, size_t N>
struct call_traits<T [N]>
{
private:
typedef T array_type[N];
public:
typedef const T* value_type;
typedef array_type& reference;
typedef const array_type& const_reference;
typedef const T* const param_type;
};
template <typename T, size_t N>
struct call_traits<const T [N]>
{
private:
typedef const T array_type[N];
public:
typedef const T* value_type;
typedef array_type& reference;
typedef const array_type& const_reference;
typedef const T* const param_type;
};
} // namespace eastl
#endif // Header include guard
+460
View File
@@ -0,0 +1,460 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// The compressed pair class is very similar to std::pair, but if either of the
// template arguments are empty classes, then the "empty base-class optimization"
// is applied to compress the size of the pair.
//
// The design for compressed_pair here is very similar to that found in template
// metaprogramming libraries such as Boost, GCC, and Metrowerks, given that
// these libraries have established this interface as a defacto standard for
// solving this problem. Also, these are described in various books on the
// topic of template metaprogramming, such as "Modern C++ Design".
//
// template <typename T1, typename T2>
// class compressed_pair
// {
// public:
// typedef T1 first_type;
// typedef T2 second_type;
// typedef typename call_traits<first_type>::param_type first_param_type;
// typedef typename call_traits<second_type>::param_type second_param_type;
// typedef typename call_traits<first_type>::reference first_reference;
// typedef typename call_traits<second_type>::reference second_reference;
// typedef typename call_traits<first_type>::const_reference first_const_reference;
// typedef typename call_traits<second_type>::const_reference second_const_reference;
//
// compressed_pair() : base() {}
// compressed_pair(first_param_type x, second_param_type y);
// explicit compressed_pair(first_param_type x);
// explicit compressed_pair(second_param_type y);
//
// compressed_pair& operator=(const compressed_pair&);
//
// first_reference first();
// first_const_reference first() const;
//
// second_reference second();
// second_const_reference second() const;
//
// void swap(compressed_pair& y);
// };
//
// The two members of the pair can be accessed using the member functions first()
// and second(). Note that not all member functions can be instantiated for all
// template parameter types. In particular compressed_pair can be instantiated for
// reference and array types, however in these cases the range of constructors that
// can be used are limited. If types T1 and T2 are the same type, then there is
// only one version of the single-argument constructor, and this constructor
// initialises both values in the pair to the passed value.
//
// Note that compressed_pair can not be instantiated if either of the template
// arguments is a union type, unless there is compiler support for is_union,
// or if is_union is specialised for the union type.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_COMPRESSED_PAIR_H
#define EASTL_COMPRESSED_PAIR_H
#include <EASTL/internal/config.h>
#include <EASTL/type_traits.h>
#include <EASTL/bonus/call_traits.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
#if defined(_MSC_VER) && (_MSC_VER >= 1900) // VS2015 or later
EA_DISABLE_VC_WARNING(4626 5027) // warning C4626: 'eastl::compressed_pair_imp<T1,T2,0>': assignment operator was implicitly defined as deleted because a base class assignment operator is inaccessible or deleted
#endif
namespace eastl
{
template <typename T1, typename T2>
class compressed_pair;
template <typename T1, typename T2, bool isSame, bool firstEmpty, bool secondEmpty>
struct compressed_pair_switch;
template <typename T1, typename T2>
struct compressed_pair_switch<T1, T2, false, false, false>{ static const int value = 0; };
template <typename T1, typename T2>
struct compressed_pair_switch<T1, T2, false, true, false> { static const int value = 1; };
template <typename T1, typename T2>
struct compressed_pair_switch<T1, T2, false, false, true> { static const int value = 2; };
template <typename T1, typename T2>
struct compressed_pair_switch<T1, T2, false, true, true> { static const int value = 3; };
template <typename T1, typename T2>
struct compressed_pair_switch<T1, T2, true, true, true> { static const int value = 4; };
template <typename T1, typename T2>
struct compressed_pair_switch<T1, T2, true, false, false> { static const int value = 5; };
template <typename T1, typename T2, int version>
class compressed_pair_imp;
template <typename T>
inline void cp_swap(T& t1, T& t2)
{
T tTemp = t1;
t1 = t2;
t2 = tTemp;
}
// Derive from neither
template <typename T1, typename T2>
class compressed_pair_imp<T1, T2, 0>
{
public:
typedef T1 first_type;
typedef T2 second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;
compressed_pair_imp() {}
compressed_pair_imp(first_param_type x, second_param_type y)
: mFirst(x), mSecond(y) {}
compressed_pair_imp(first_param_type x)
: mFirst(x) {}
compressed_pair_imp(second_param_type y)
: mSecond(y) {}
first_reference first() { return mFirst; }
first_const_reference first() const { return mFirst; }
second_reference second() { return mSecond; }
second_const_reference second() const { return mSecond; }
void swap(compressed_pair<T1, T2>& y)
{
cp_swap(mFirst, y.first());
cp_swap(mSecond, y.second());
}
private:
first_type mFirst;
second_type mSecond;
};
// Derive from T1
template <typename T1, typename T2>
class compressed_pair_imp<T1, T2, 1> : private T1
{
public:
typedef T1 first_type;
typedef T2 second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;
compressed_pair_imp() {}
compressed_pair_imp(first_param_type x, second_param_type y)
: first_type(x), mSecond(y) {}
compressed_pair_imp(first_param_type x)
: first_type(x) {}
compressed_pair_imp(second_param_type y)
: mSecond(y) {}
first_reference first() { return *this; }
first_const_reference first() const { return *this; }
second_reference second() { return mSecond; }
second_const_reference second() const { return mSecond; }
void swap(compressed_pair<T1,T2>& y)
{
// No need to swap empty base class
cp_swap(mSecond, y.second());
}
private:
second_type mSecond;
};
// Derive from T2
template <typename T1, typename T2>
class compressed_pair_imp<T1, T2, 2> : private T2
{
public:
typedef T1 first_type;
typedef T2 second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;
compressed_pair_imp() {}
compressed_pair_imp(first_param_type x, second_param_type y)
: second_type(y), mFirst(x) {}
compressed_pair_imp(first_param_type x)
: mFirst(x) {}
compressed_pair_imp(second_param_type y)
: second_type(y) {}
first_reference first() { return mFirst; }
first_const_reference first() const { return mFirst; }
second_reference second() { return *this; }
second_const_reference second() const { return *this; }
void swap(compressed_pair<T1,T2>& y)
{
// No need to swap empty base class
cp_swap(mFirst, y.first());
}
private:
first_type mFirst;
};
// Derive from T1 and T2
template <typename T1, typename T2>
class compressed_pair_imp<T1, T2, 3> : private T1, private T2
{
public:
typedef T1 first_type;
typedef T2 second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;
compressed_pair_imp() {}
compressed_pair_imp(first_param_type x, second_param_type y)
: first_type(x), second_type(y) {}
compressed_pair_imp(first_param_type x)
: first_type(x) {}
compressed_pair_imp(second_param_type y)
: second_type(y) {}
first_reference first() { return *this; }
first_const_reference first() const { return *this; }
second_reference second() { return *this; }
second_const_reference second() const { return *this; }
// No need to swap empty bases
void swap(compressed_pair<T1, T2>&)
{ }
};
// T1 == T2, T1 and T2 are both empty
// Note does not actually store an instance of T2 at all;
// but reuses T1 base class for both first() and second().
template <typename T1, typename T2>
class compressed_pair_imp<T1, T2, 4> : private T1
{
public:
typedef T1 first_type;
typedef T2 second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;
compressed_pair_imp() {}
compressed_pair_imp(first_param_type x, second_param_type)
: first_type(x) {}
compressed_pair_imp(first_param_type x)
: first_type(x) {}
first_reference first() { return *this; }
first_const_reference first() const { return *this; }
second_reference second() { return *this; }
second_const_reference second() const { return *this; }
void swap(compressed_pair<T1, T2>&) { }
};
// T1 == T2 and are not empty
template <typename T1, typename T2>
class compressed_pair_imp<T1, T2, 5>
{
public:
typedef T1 first_type;
typedef T2 second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;
compressed_pair_imp() {}
compressed_pair_imp(first_param_type x, second_param_type y)
: mFirst(x), mSecond(y) {}
compressed_pair_imp(first_param_type x)
: mFirst(x), mSecond(x) {}
first_reference first() { return mFirst; }
first_const_reference first() const { return mFirst; }
second_reference second() { return mSecond; }
second_const_reference second() const { return mSecond; }
void swap(compressed_pair<T1, T2>& y)
{
cp_swap(mFirst, y.first());
cp_swap(mSecond, y.second());
}
private:
first_type mFirst;
second_type mSecond;
};
template <typename T1, typename T2>
class compressed_pair
: private compressed_pair_imp<T1, T2,
compressed_pair_switch<
T1,
T2,
is_same<typename remove_cv<T1>::type, typename remove_cv<T2>::type>::value,
is_empty<T1>::value,
is_empty<T2>::value>::value>
{
private:
typedef compressed_pair_imp<T1, T2,
compressed_pair_switch<
T1,
T2,
is_same<typename remove_cv<T1>::type, typename remove_cv<T2>::type>::value,
is_empty<T1>::value,
is_empty<T2>::value>::value> base;
public:
typedef T1 first_type;
typedef T2 second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;
compressed_pair() : base() {}
compressed_pair(first_param_type x, second_param_type y) : base(x, y) {}
explicit compressed_pair(first_param_type x) : base(x) {}
explicit compressed_pair(second_param_type y) : base(y) {}
first_reference first() { return base::first(); }
first_const_reference first() const { return base::first(); }
second_reference second() { return base::second(); }
second_const_reference second() const { return base::second(); }
void swap(compressed_pair& y) { base::swap(y); }
};
// Partial specialisation for case where T1 == T2:
template <typename T>
class compressed_pair<T, T>
: private compressed_pair_imp<T, T,
compressed_pair_switch<
T,
T,
is_same<typename remove_cv<T>::type, typename remove_cv<T>::type>::value,
is_empty<T>::value,
is_empty<T>::value>::value>
{
private:
typedef compressed_pair_imp<T, T,
compressed_pair_switch<
T,
T,
is_same<typename remove_cv<T>::type, typename remove_cv<T>::type>::value,
is_empty<T>::value,
is_empty<T>::value>::value> base;
public:
typedef T first_type;
typedef T second_type;
typedef typename call_traits<first_type>::param_type first_param_type;
typedef typename call_traits<second_type>::param_type second_param_type;
typedef typename call_traits<first_type>::reference first_reference;
typedef typename call_traits<second_type>::reference second_reference;
typedef typename call_traits<first_type>::const_reference first_const_reference;
typedef typename call_traits<second_type>::const_reference second_const_reference;
compressed_pair() : base() {}
compressed_pair(first_param_type x, second_param_type y) : base(x, y) {}
explicit compressed_pair(first_param_type x) : base(x) {}
first_reference first() { return base::first(); }
first_const_reference first() const { return base::first(); }
second_reference second() { return base::second(); }
second_const_reference second() const { return base::second(); }
void swap(compressed_pair<T, T>& y) { base::swap(y); }
};
template <typename T1, typename T2>
inline void swap(compressed_pair<T1, T2>& x, compressed_pair<T1, T2>& y)
{
x.swap(y);
}
} // namespace eastl
#if defined(_MSC_VER) && (_MSC_VER >= 1900) // VS2015 or later
EA_RESTORE_VC_WARNING()
#endif
#endif // Header include guard
+50
View File
@@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_RING_BUFFER_H
#define EASTL_FIXED_RING_BUFFER_H
#include <EASTL/internal/config.h>
#include <EASTL/fixed_vector.h>
#include <EASTL/bonus/ring_buffer.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// fixed_ring_buffer
///
/// This is a convenience template alias for creating a fixed-sized
/// ring_buffer using eastl::fixed_vector as its storage container. This has
/// been tricky for users to get correct due to the constructor requirements
/// of eastl::ring_buffer leaking the implementation detail of the sentinel
/// value being used internally. In addition, it was not obvious what the
/// correct allocator_type template parameter should be used for containers
/// providing both a default allocator type and an overflow allocator type.
///
/// We are over-allocating the fixed_vector container to accommodate the
/// ring_buffer sentinel to prevent that implementation detail leaking into
/// user code.
///
/// Example usage:
///
/// fixed_ring_buffer<int, 8> rb = {0, 1, 2, 3, 4, 5, 6, 7};
/// or
/// fixed_ring_buffer<int, 8> rb(8); // capacity doesn't need to respect sentinel
/// rb.push_back(0);
///
///
#if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES)
template <typename T, size_t N>
using fixed_ring_buffer =
ring_buffer<T, fixed_vector<T, N + 1, false>, typename fixed_vector<T, N + 1, false>::overflow_allocator_type>;
#endif
} // namespace eastl
#endif // Header include guard
+210
View File
@@ -0,0 +1,210 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXEDTUPLEVECTOR_H
#define EASTL_FIXEDTUPLEVECTOR_H
#include <EASTL/bonus/tuple_vector.h>
#include <EASTL/internal/fixed_pool.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_FIXED_TUPLE_VECTOR_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
/// In the case of fixed-size containers, the allocator name always refers
/// to overflow allocations.
///
#ifndef EASTL_FIXED_TUPLE_VECTOR_DEFAULT_NAME
#define EASTL_FIXED_TUPLE_VECTOR_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_tuple_vector" // Unless the user overrides something, this is "EASTL fixed_vector".
#endif
/// EASTL_FIXED_TUPLE_VECTOR_DEFAULT_ALLOCATOR
///
#ifndef EASTL_FIXED_TUPLE_VECTOR_DEFAULT_ALLOCATOR
#define EASTL_FIXED_TUPLE_VECTOR_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_TUPLE_VECTOR_DEFAULT_NAME)
#endif
// External interface of fixed_tuple_vector
template <size_t nodeCount, bool bEnableOverflow, typename... Ts>
class fixed_tuple_vector : public TupleVecInternal::TupleVecImpl<fixed_vector_allocator<
TupleVecInternal::TupleRecurser<Ts...>::GetTotalAllocationSize(nodeCount, 0), 1,
TupleVecInternal::TupleRecurser<Ts...>::GetTotalAlignment(), 0,
bEnableOverflow, EASTLAllocatorType>, make_index_sequence<sizeof...(Ts)>, Ts...>
{
public:
typedef fixed_vector_allocator<
TupleVecInternal::TupleRecurser<Ts...>::GetTotalAllocationSize(nodeCount, 0), 1,
TupleVecInternal::TupleRecurser<Ts...>::GetTotalAlignment(), 0,
bEnableOverflow, EASTLAllocatorType> fixed_allocator_type;
typedef aligned_buffer<fixed_allocator_type::kNodesSize, fixed_allocator_type::kNodeAlignment> aligned_buffer_type;
typedef fixed_tuple_vector<nodeCount, bEnableOverflow, Ts...> this_type;
typedef EASTLAllocatorType overflow_allocator_type;
typedef TupleVecInternal::TupleVecImpl<fixed_allocator_type, make_index_sequence<sizeof...(Ts)>, Ts...> base_type;
typedef typename base_type::size_type size_type;
private:
aligned_buffer_type mBuffer;
public:
fixed_tuple_vector()
: base_type(fixed_allocator_type(mBuffer.buffer), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{ }
fixed_tuple_vector(const overflow_allocator_type& allocator)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{ }
fixed_tuple_vector(this_type&& x)
: base_type(fixed_allocator_type(mBuffer.buffer), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::get_allocator().copy_overflow_allocator(x.get_allocator());
base_type::DoInitFromIterator(make_move_iterator(x.begin()), make_move_iterator(x.end()));
x.clear();
}
fixed_tuple_vector(this_type&& x, const overflow_allocator_type& allocator)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitFromIterator(make_move_iterator(x.begin()), make_move_iterator(x.end()));
x.clear();
}
fixed_tuple_vector(const this_type& x)
: base_type(fixed_allocator_type(mBuffer.buffer), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::get_allocator().copy_overflow_allocator(x.get_allocator());
base_type::DoInitFromIterator(x.begin(), x.end());
}
fixed_tuple_vector(const this_type& x, const overflow_allocator_type& allocator)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitFromIterator(x.begin(), x.end());
}
template <typename MoveIterBase>
fixed_tuple_vector(move_iterator<MoveIterBase> begin, move_iterator<MoveIterBase> end, const overflow_allocator_type& allocator = EASTL_FIXED_TUPLE_VECTOR_DEFAULT_ALLOCATOR)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitFromIterator(begin, end);
}
template <typename Iterator>
fixed_tuple_vector(Iterator begin, Iterator end, const overflow_allocator_type& allocator = EASTL_FIXED_TUPLE_VECTOR_DEFAULT_ALLOCATOR)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitFromIterator(begin, end);
}
fixed_tuple_vector(size_type n, const overflow_allocator_type& allocator = EASTL_FIXED_TUPLE_VECTOR_DEFAULT_ALLOCATOR)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitDefaultFill(n);
}
fixed_tuple_vector(size_type n, const Ts&... args)
: base_type(fixed_allocator_type(mBuffer.buffer), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitFillArgs(n, args...);
}
fixed_tuple_vector(size_type n, const Ts&... args, const overflow_allocator_type& allocator)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitFillArgs(n, args...);
}
fixed_tuple_vector(size_type n,
typename base_type::const_reference_tuple tup,
const overflow_allocator_type& allocator = EASTL_FIXED_TUPLE_VECTOR_DEFAULT_ALLOCATOR)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitFillTuple(n, tup);
}
fixed_tuple_vector(const typename base_type::value_tuple* first, const typename base_type::value_tuple* last,
const overflow_allocator_type& allocator = EASTL_FIXED_TUPLE_VECTOR_DEFAULT_ALLOCATOR)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitFromTupleArray(first, last);
}
fixed_tuple_vector(std::initializer_list<typename base_type::value_tuple> iList,
const overflow_allocator_type& allocator = EASTL_FIXED_TUPLE_VECTOR_DEFAULT_ALLOCATOR)
: base_type(fixed_allocator_type(mBuffer.buffer, allocator), mBuffer.buffer, nodeCount, fixed_allocator_type::kNodeSize)
{
base_type::DoInitFromTupleArray(iList.begin(), iList.end());
}
this_type& operator=(const this_type& other)
{
base_type::operator=(other);
return *this;
}
this_type& operator=(this_type&& other)
{
base_type::clear();
// OK to call DoInitFromIterator in a non-ctor scenario because clear() reset everything, more-or-less
base_type::DoInitFromIterator(make_move_iterator(other.begin()), make_move_iterator(other.end()));
other.clear();
return *this;
}
this_type& operator=(std::initializer_list<typename base_type::value_tuple> iList)
{
base_type::operator=(iList);
return *this;
}
void swap(this_type& x)
{
// If both containers are using the heap instead of local memory
// then we can do a fast pointer swap instead of content swap.
if ((has_overflowed() && x.has_overflowed()) && (get_overflow_allocator() == x.get_overflow_allocator()))
{
base_type::swap(x);
}
else
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
}
// Returns the max fixed size, which is the user-supplied nodeCount parameter.
size_type max_size() const { return nodeCount; }
// Returns true if the fixed space has been fully allocated. Note that if overflow is enabled,
// the container size can be greater than nodeCount but full() could return true because the
// fixed space may have a recently freed slot.
bool full() const { return (base_type::mNumElements >= nodeCount) || ((void*)base_type::mpData != (void*)mBuffer.buffer); }
// Returns true if the allocations spilled over into the overflow allocator. Meaningful
// only if overflow is enabled.
bool has_overflowed() const { return ((void*)base_type::mpData != (void*)mBuffer.buffer); }
// Returns the value of the bEnableOverflow template parameter.
bool can_overflow() const { return bEnableOverflow; }
const overflow_allocator_type& get_overflow_allocator() const { return base_type::get_allocator().get_overflow_allocator(); }
};
template <size_t nodeCount, bool bEnableOverflow, typename... Ts>
inline void swap(fixed_tuple_vector<nodeCount, bEnableOverflow, Ts...>& a,
fixed_tuple_vector<nodeCount, bEnableOverflow, Ts...>& b)
{
a.swap(b);
}
} // namespace eastl
#endif // EASTL_TUPLEVECTOR_H
+694
View File
@@ -0,0 +1,694 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// intrusive_sdlist is a special kind of intrusive list which we say is
// "singly-doubly" linked. Instead of having a typical intrusive list node
// which looks like this:
//
// struct intrusive_sdlist_node {
// intrusive_sdlist_node *mpNext;
// intrusive_sdlist_node *mpPrev;
// };
//
// We instead have one that looks like this:
//
// struct intrusive_sdlist_node {
// intrusive_sdlist_node* mpNext;
// intrusive_sdlist_node** mppPrevNext;
// };
//
// This may seem to be suboptimal, but it has one specific advantage: it allows
// the intrusive_sdlist class to be the size of only one pointer instead of two.
// This may seem like a minor optimization, but some users have wanted to create
// thousands of empty instances of these.
// This is because while an intrusive_list class looks like this:
//
// class intrusive_list {
// intrusive_list_node mBaseNode;
// };
//
// an intrusive_sdlist class looks like this:
//
// class intrusive_sdlist {
// intrusive_sdlist_node* mpNext;
// };
//
// So here we make a list of plusses and minuses of intrusive sdlists
// compared to intrusive_lists and intrusive_slists:
//
// | list | slist | sdlist
// ---------------------------------------------------------
// min size | 8 | 4 | 4
// node size | 8 | 4 | 8
// anonymous erase | yes | no | yes
// reverse iteration | yes | no | no
//
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_INTRUSIVE_SDLIST_H
#define EASTL_INTRUSIVE_SDLIST_H
#include <EASTL/internal/config.h>
#include <EASTL/iterator.h>
#include <EASTL/algorithm.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// intrusive_sdlist_node
///
struct intrusive_sdlist_node
{
intrusive_sdlist_node* mpNext;
intrusive_sdlist_node** mppPrevNext;
};
/// IntrusiveSDListIterator
///
template <typename T, typename Pointer, typename Reference>
struct IntrusiveSDListIterator
{
typedef IntrusiveSDListIterator<T, Pointer, Reference> this_type;
typedef IntrusiveSDListIterator<T, T*, T&> iterator;
typedef IntrusiveSDListIterator<T, const T*, const T&> const_iterator;
typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t.
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef T node_type;
typedef Pointer pointer;
typedef Reference reference;
typedef EASTL_ITC_NS::forward_iterator_tag iterator_category;
public:
pointer mpNode;
public:
IntrusiveSDListIterator();
explicit IntrusiveSDListIterator(pointer pNode); // Note that you can also construct an iterator from T via this, since value_type == node_type.
IntrusiveSDListIterator(const iterator& x);
reference operator*() const;
pointer operator->() const;
this_type& operator++();
this_type operator++(int);
}; // struct IntrusiveSDListIterator
/// intrusive_sdlist_base
///
/// Provides a template-less base class for intrusive_sdlist.
///
class intrusive_sdlist_base
{
public:
typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t.
typedef ptrdiff_t difference_type;
protected:
intrusive_sdlist_node* mpNext;
public:
intrusive_sdlist_base();
bool empty() const; ///< Returns true if the container is empty.
size_type size() const; ///< Returns the number of elements in the list; O(n).
void clear(); ///< Clears the list; O(1). No deallocation occurs.
void pop_front(); ///< Removes an element from the front of the list; O(1). The element must be present, but is not deallocated.
void reverse(); ///< Reverses a list so that front and back are swapped; O(n).
//bool validate() const; ///< Scans a list for linkage inconsistencies; O(n) time, O(1) space. Returns false if errors are detected, such as loops or branching.
}; // class intrusive_sdlist_base
/// intrusive_sdlist
///
template <typename T = intrusive_sdlist_node>
class intrusive_sdlist : public intrusive_sdlist_base
{
public:
typedef intrusive_sdlist<T> this_type;
typedef intrusive_sdlist_base base_type;
typedef T node_type;
typedef T value_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::difference_type difference_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef IntrusiveSDListIterator<T, T*, T&> iterator;
typedef IntrusiveSDListIterator<T, const T*, const T&> const_iterator;
typedef eastl::reverse_iterator<iterator> reverse_iterator;
typedef eastl::reverse_iterator<const_iterator> const_reverse_iterator;
public:
intrusive_sdlist(); ///< Creates an empty list.
intrusive_sdlist(const this_type& x); ///< Creates an empty list; ignores the argument.
this_type& operator=(const this_type& x); ///< Clears the list; ignores the argument.
iterator begin(); ///< Returns an iterator pointing to the first element in the list.
const_iterator begin() const; ///< Returns a const_iterator pointing to the first element in the list.
const_iterator cbegin() const; ///< Returns a const_iterator pointing to the first element in the list.
iterator end(); ///< Returns an iterator pointing one-after the last element in the list.
const_iterator end() const; ///< Returns a const_iterator pointing one-after the last element in the list.
const_iterator cend() const; ///< Returns a const_iterator pointing one-after the last element in the list.
reference front(); ///< Returns a reference to the first element. The list must be empty.
const_reference front() const; ///< Returns a const reference to the first element. The list must be empty.
void push_front(value_type& value); ///< Adds an element to the front of the list; O(1). The element is not copied. The element must not be in any other list.
void push_back(value_type& value); ///< Adds an element to the back of the list; O(N). The element is not copied. The element must not be in any other list.
void pop_back(); ///< Removes an element from the back of the list; O(N). The element must be present, but is not deallocated.
bool contains(const value_type& value) const; ///< Returns true if the given element is in the list; O(n). Equivalent to (locate(x) != end()).
iterator locate(value_type& value); ///< Converts a reference to an object in the list back to an iterator, or returns end() if it is not part of the list. O(n)
const_iterator locate(const value_type& value) const; ///< Converts a const reference to an object in the list back to a const iterator, or returns end() if it is not part of the list. O(n)
iterator insert(iterator position, value_type& value); ///< Inserts an element before the element pointed to by the iterator. O(1)
iterator erase(iterator position); ///< Erases the element pointed to by the iterator. O(1)
iterator erase(iterator first, iterator last); ///< Erases elements within the iterator range [first, last). O(1).
void swap(intrusive_sdlist& x); ///< Swaps the contents of two intrusive lists; O(1).
static void remove(value_type& value); ///< Erases an element from a list; O(1). Note that this is static so you don't need to know which list the element, although it must be in some list.
void splice(iterator position, value_type& value); ///< Moves the given element into this list before the element pointed to by position; O(1).
///< Required: x must be in some list or have first/next pointers that point it itself.
void splice(iterator position, this_type& x); ///< Moves the contents of a list into this list before the element pointed to by position; O(1).
///< Required: &x != this (same as std::list).
void splice(iterator position, this_type& x, iterator xPosition); ///< Moves the given element pointed to i within the list x into the current list before
///< the element pointed to by position; O(1).
void splice(iterator position, this_type& x, iterator first, iterator last); ///< Moves the range of elements [first, last) from list x into the current list before
///< the element pointed to by position; O(1).
///< Required: position must not be in [first, last). (same as std::list).
bool validate() const;
int validate_iterator(const_iterator i) const;
}; // intrusive_sdlist
///////////////////////////////////////////////////////////////////////
// IntrusiveSDListIterator functions
///////////////////////////////////////////////////////////////////////
template <typename T, typename Pointer, typename Reference>
inline IntrusiveSDListIterator<T, Pointer, Reference>::IntrusiveSDListIterator()
{
#if EASTL_DEBUG
mpNode = NULL;
#endif
}
template <typename T, typename Pointer, typename Reference>
inline IntrusiveSDListIterator<T, Pointer, Reference>::IntrusiveSDListIterator(pointer pNode)
: mpNode(pNode)
{
}
template <typename T, typename Pointer, typename Reference>
inline IntrusiveSDListIterator<T, Pointer, Reference>::IntrusiveSDListIterator(const iterator& x)
: mpNode(x.mpNode)
{
}
template <typename T, typename Pointer, typename Reference>
inline typename IntrusiveSDListIterator<T, Pointer, Reference>::reference
IntrusiveSDListIterator<T, Pointer, Reference>::operator*() const
{
return *mpNode;
}
template <typename T, typename Pointer, typename Reference>
inline typename IntrusiveSDListIterator<T, Pointer, Reference>::pointer
IntrusiveSDListIterator<T, Pointer, Reference>::operator->() const
{
return mpNode;
}
template <typename T, typename Pointer, typename Reference>
inline typename IntrusiveSDListIterator<T, Pointer, Reference>::this_type&
IntrusiveSDListIterator<T, Pointer, Reference>::operator++()
{
mpNode = static_cast<node_type*>(mpNode->mpNext);
return *this;
}
template <typename T, typename Pointer, typename Reference>
inline typename IntrusiveSDListIterator<T, Pointer, Reference>::this_type
IntrusiveSDListIterator<T, Pointer, Reference>::operator++(int)
{
this_type temp = *this;
mpNode = static_cast<node_type*>(mpNode->mpNext);
return temp;
}
// The C++ defect report #179 requires that we support comparisons between const and non-const iterators.
// Thus we provide additional template paremeters here to support this. The defect report does not
// require us to support comparisons between reverse_iterators and const_reverse_iterators.
template <typename T, typename PointerA, typename ReferenceA, typename PointerB, typename ReferenceB>
inline bool operator==(const IntrusiveSDListIterator<T, PointerA, ReferenceA>& a,
const IntrusiveSDListIterator<T, PointerB, ReferenceB>& b)
{
return a.mpNode == b.mpNode;
}
template <typename T, typename PointerA, typename ReferenceA, typename PointerB, typename ReferenceB>
inline bool operator!=(const IntrusiveSDListIterator<T, PointerA, ReferenceA>& a,
const IntrusiveSDListIterator<T, PointerB, ReferenceB>& b)
{
return a.mpNode != b.mpNode;
}
// We provide a version of operator!= for the case where the iterators are of the
// same type. This helps prevent ambiguity errors in the presence of rel_ops.
template <typename T, typename Pointer, typename Reference>
inline bool operator!=(const IntrusiveSDListIterator<T, Pointer, Reference>& a,
const IntrusiveSDListIterator<T, Pointer, Reference>& b)
{
return a.mpNode != b.mpNode;
}
///////////////////////////////////////////////////////////////////////
// intrusive_sdlist_base
///////////////////////////////////////////////////////////////////////
inline intrusive_sdlist_base::intrusive_sdlist_base()
{ mpNext = NULL; }
inline bool intrusive_sdlist_base::empty() const
{ return mpNext == NULL; }
inline intrusive_sdlist_base::size_type intrusive_sdlist_base::size() const
{
size_type n = 0;
for(const intrusive_sdlist_node* pCurrent = mpNext; pCurrent; pCurrent = pCurrent->mpNext)
n++;
return n;
}
inline void intrusive_sdlist_base::clear()
{ mpNext = NULL; } // Note that we don't do anything with the list nodes.
inline void intrusive_sdlist_base::pop_front()
{
// To consider: Set mpNext's pointers to NULL in debug builds.
mpNext = mpNext->mpNext;
mpNext->mppPrevNext = &mpNext;
}
///////////////////////////////////////////////////////////////////////
// intrusive_sdlist
///////////////////////////////////////////////////////////////////////
template <typename T>
inline intrusive_sdlist<T>::intrusive_sdlist()
{
}
template <typename T>
inline intrusive_sdlist<T>::intrusive_sdlist(const this_type& /*x*/)
: intrusive_sdlist_base()
{
// We intentionally ignore argument x.
}
template <typename T>
inline typename intrusive_sdlist<T>::this_type& intrusive_sdlist<T>::operator=(const this_type& /*x*/)
{
return *this; // We intentionally ignore argument x.
}
template <typename T>
inline typename intrusive_sdlist<T>::iterator intrusive_sdlist<T>::begin()
{ return iterator(static_cast<T*>(mpNext)); }
template <typename T>
inline typename intrusive_sdlist<T>::const_iterator intrusive_sdlist<T>::begin() const
{ return const_iterator(static_cast<T*>(const_cast<intrusive_sdlist_node*>(mpNext))); }
template <typename T>
inline typename intrusive_sdlist<T>::const_iterator intrusive_sdlist<T>::cbegin() const
{ return const_iterator(static_cast<T*>(const_cast<intrusive_sdlist_node*>(mpNext))); }
template <typename T>
inline typename intrusive_sdlist<T>::iterator intrusive_sdlist<T>::end()
{ return iterator(static_cast<T*>(NULL)); }
template <typename T>
inline typename intrusive_sdlist<T>::const_iterator intrusive_sdlist<T>::end() const
{ return const_iterator(static_cast<const T*>(NULL)); }
template <typename T>
inline typename intrusive_sdlist<T>::const_iterator intrusive_sdlist<T>::cend() const
{ return const_iterator(static_cast<const T*>(NULL)); }
template <typename T>
inline typename intrusive_sdlist<T>::reference intrusive_sdlist<T>::front()
{ return *static_cast<T*>(mpNext); }
template <typename T>
inline typename intrusive_sdlist<T>::const_reference intrusive_sdlist<T>::front() const
{ return *static_cast<const T*>(mpNext); }
template <typename T>
inline void intrusive_sdlist<T>::push_front(value_type& value)
{
value.mpNext = mpNext;
value.mppPrevNext = &mpNext;
if(mpNext)
mpNext->mppPrevNext = &value.mpNext;
mpNext = &value;
}
template <typename T>
inline void intrusive_sdlist<T>::push_back(value_type& value)
{
intrusive_sdlist_node* pNext = mpNext;
intrusive_sdlist_node** ppPrevNext = &mpNext;
while(pNext)
{
ppPrevNext = &pNext->mpNext;
pNext = pNext->mpNext;
}
*ppPrevNext = &value;
value.mppPrevNext = ppPrevNext;
value.mpNext = NULL;
}
template <typename T>
inline void intrusive_sdlist<T>::pop_back()
{
node_type* pCurrent = static_cast<node_type*>(mpNext);
while(pCurrent->mpNext)
pCurrent = static_cast<node_type*>(pCurrent->mpNext);
*pCurrent->mppPrevNext = NULL;
}
template <typename T>
inline bool intrusive_sdlist<T>::contains(const value_type& value) const
{
const intrusive_sdlist_node* pCurrent;
for(pCurrent = mpNext; pCurrent; pCurrent = pCurrent->mpNext)
{
if(pCurrent == &value)
break;
}
return (pCurrent != NULL);
}
template <typename T>
inline typename intrusive_sdlist<T>::iterator intrusive_sdlist<T>::locate(value_type& value)
{
intrusive_sdlist_node* pCurrent;
for(pCurrent = static_cast<value_type*>(mpNext); pCurrent; pCurrent = pCurrent->mpNext)
{
if(pCurrent == &value)
break;
}
return iterator(static_cast<value_type*>(pCurrent));
}
template <typename T>
inline typename intrusive_sdlist<T>::const_iterator intrusive_sdlist<T>::locate(const T& value) const
{
const intrusive_sdlist_node* pCurrent;
for(pCurrent = static_cast<value_type*>(mpNext); pCurrent; pCurrent = pCurrent->mpNext)
{
if(pCurrent == &value)
break;
}
return const_iterator(static_cast<value_type*>(const_cast<intrusive_sdlist_node*>(pCurrent)));
}
template <typename T>
inline typename intrusive_sdlist<T>::iterator
intrusive_sdlist<T>::insert(iterator position, value_type& value)
{
value.mppPrevNext = position.mpNode->mppPrevNext;
value.mpNext = position.mpNode;
*value.mppPrevNext = &value;
position.mpNode->mppPrevNext = &value.mpNext;
return iterator(&value);
}
template <typename T>
inline typename intrusive_sdlist<T>::iterator
intrusive_sdlist<T>::erase(iterator position)
{
*position.mpNode->mppPrevNext = position.mpNode->mpNext;
position.mpNode->mpNext->mppPrevNext = position.mpNode->mppPrevNext;
return iterator(position.mpNode);
}
template <typename T>
inline typename intrusive_sdlist<T>::iterator
intrusive_sdlist<T>::erase(iterator first, iterator last)
{
if(first.mpNode) // If not erasing the end...
{
*first.mpNode->mppPrevNext = last.mpNode;
if(last.mpNode) // If not erasing to the end...
last.mpNode->mppPrevNext = first.mpNode->mppPrevNext;
}
return last;
}
template <typename T>
inline void intrusive_sdlist<T>::remove(value_type& value)
{
*value.mppPrevNext = value.mpNext;
if(value.mpNext)
value.mpNext->mppPrevNext = value.mppPrevNext;
}
template <typename T>
void intrusive_sdlist<T>::swap(intrusive_sdlist& x)
{
// swap anchors
intrusive_sdlist_node* const temp(mpNext);
mpNext = x.mpNext;
x.mpNext = temp;
if(x.mpNext)
x.mpNext->mppPrevNext = &mpNext;
if(mpNext)
mpNext->mppPrevNext = &x.mpNext;
}
// To do: Complete these splice functions. Might want to look at intrusive_sdlist for help.
template <typename T>
void intrusive_sdlist<T>::splice(iterator /*position*/, value_type& /*value*/)
{
EASTL_ASSERT(false); // If you need this working, ask Paul Pedriana or submit a working version for inclusion.
}
template <typename T>
void intrusive_sdlist<T>::splice(iterator /*position*/, intrusive_sdlist& /*x*/)
{
EASTL_ASSERT(false); // If you need this working, ask Paul Pedriana or submit a working version for inclusion.
}
template <typename T>
void intrusive_sdlist<T>::splice(iterator /*position*/, intrusive_sdlist& /*x*/, iterator /*xPosition*/)
{
EASTL_ASSERT(false); // If you need this working, ask Paul Pedriana or submit a working version for inclusion.
}
template <typename T>
void intrusive_sdlist<T>::splice(iterator /*position*/, intrusive_sdlist& /*x*/, iterator /*first*/, iterator /*last*/)
{
EASTL_ASSERT(false); // If you need this working, ask Paul Pedriana or submit a working version for inclusion.
}
template <typename T>
inline bool intrusive_sdlist<T>::validate() const
{
return true; // To do.
}
template <typename T>
inline int intrusive_sdlist<T>::validate_iterator(const_iterator i) const
{
// To do: Come up with a more efficient mechanism of doing this.
for(const_iterator temp = begin(), tempEnd = end(); temp != tempEnd; ++temp)
{
if(temp == i)
return (isf_valid | isf_current | isf_can_dereference);
}
if(i == end())
return (isf_valid | isf_current);
return isf_none;
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename T>
bool operator==(const intrusive_sdlist<T>& a, const intrusive_sdlist<T>& b)
{
// If we store an mSize member for intrusive_sdlist, we want to take advantage of it here.
typename intrusive_sdlist<T>::const_iterator ia = a.begin();
typename intrusive_sdlist<T>::const_iterator ib = b.begin();
typename intrusive_sdlist<T>::const_iterator enda = a.end();
typename intrusive_sdlist<T>::const_iterator endb = b.end();
while((ia != enda) && (ib != endb) && (*ia == *ib))
{
++ia;
++ib;
}
return (ia == enda) && (ib == endb);
}
template <typename T>
bool operator<(const intrusive_sdlist<T>& a, const intrusive_sdlist<T>& b)
{
return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
}
template <typename T>
bool operator!=(const intrusive_sdlist<T>& a, const intrusive_sdlist<T>& b)
{
return !(a == b);
}
template <typename T>
bool operator>(const intrusive_sdlist<T>& a, const intrusive_sdlist<T>& b)
{
return b < a;
}
template <typename T>
bool operator<=(const intrusive_sdlist<T>& a, const intrusive_sdlist<T>& b)
{
return !(b < a);
}
template <typename T>
bool operator>=(const intrusive_sdlist<T>& a, const intrusive_sdlist<T>& b)
{
return !(a < b);
}
template <typename T>
void swap(intrusive_sdlist<T>& a, intrusive_sdlist<T>& b)
{
a.swap(b);
}
} // namespace eastl
#endif // Header include guard
+321
View File
@@ -0,0 +1,321 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// *** Note ***
// This implementation is incomplete.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_INTRUSIVE_SLIST_H
#define EASTL_INTRUSIVE_SLIST_H
#include <EASTL/internal/config.h>
#include <EASTL/iterator.h>
#include <EASTL/algorithm.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// intrusive_slist_node
///
struct intrusive_slist_node
{
intrusive_slist_node* mpNext;
};
/// IntrusiveSListIterator
///
template <typename T, typename Pointer, typename Reference>
struct IntrusiveSListIterator
{
typedef IntrusiveSListIterator<T, Pointer, Reference> this_type;
typedef IntrusiveSListIterator<T, T*, T&> iterator;
typedef IntrusiveSListIterator<T, const T*, const T&> const_iterator;
typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t.
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef T node_type;
typedef Pointer pointer;
typedef Reference reference;
typedef EASTL_ITC_NS::forward_iterator_tag iterator_category;
public:
node_type* mpNode;
public:
IntrusiveSListIterator();
explicit IntrusiveSListIterator(pointer pNode); // Note that you can also construct an iterator from T via this, since value_type == node_type.
IntrusiveSListIterator(const iterator& x);
reference operator*() const;
pointer operator->() const;
this_type& operator++();
this_type operator++(int);
}; // struct IntrusiveSListIterator
/// intrusive_slist_base
///
/// Provides a template-less base class for intrusive_slist.
///
class intrusive_slist_base
{
public:
typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t.
typedef ptrdiff_t difference_type;
protected:
intrusive_slist_node* mpNext;
public:
intrusive_slist_base();
bool empty() const; ///< Returns true if the container is empty.
size_type size() const; ///< Returns the number of elements in the list; O(n).
void clear(); ///< Clears the list; O(1). No deallocation occurs.
void pop_front(); ///< Removes an element from the front of the list; O(1). The element must be present, but is not deallocated.
void reverse(); ///< Reverses a list so that front and back are swapped; O(n).
//bool validate() const; ///< Scans a list for linkage inconsistencies; O(n) time, O(1) space. Returns false if errors are detected, such as loops or branching.
}; // class intrusive_slist_base
/// intrusive_slist
///
template <typename T = intrusive_slist_node>
class intrusive_slist : public intrusive_slist_base
{
public:
typedef intrusive_slist<T> this_type;
typedef intrusive_slist_base base_type;
typedef T node_type;
typedef T value_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::difference_type difference_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef IntrusiveSListIterator<T, T*, T&> iterator;
typedef IntrusiveSListIterator<T, const T*, const T&> const_iterator;
public:
intrusive_slist(); ///< Creates an empty list.
//intrusive_slist(const this_type& x); ///< Creates an empty list; ignores the argument. To consider: Is this a useful function?
//this_type& operator=(const this_type& x); ///< Clears the list; ignores the argument. To consider: Is this a useful function?
iterator begin(); ///< Returns an iterator pointing to the first element in the list. O(1).
const_iterator begin() const; ///< Returns a const_iterator pointing to the first element in the list. O(1).
const_iterator cbegin() const; ///< Returns a const_iterator pointing to the first element in the list. O(1).
iterator end(); ///< Returns an iterator pointing one-after the last element in the list. O(1).
const_iterator end() const; ///< Returns a const_iterator pointing one-after the last element in the list. O(1).
const_iterator cend() const; ///< Returns a const_iterator pointing one-after the last element in the list. O(1).
iterator before_begin(); ///< Returns iterator to position before begin. O(1).
const_iterator before_begin() const; ///< Returns iterator to previous position. O(1).
const_iterator cbefore_begin() const; ///< Returns iterator to previous position. O(1).
iterator previous(const_iterator position); ///< Returns iterator to previous position. O(n).
const_iterator previous(const_iterator position) const; ///< Returns iterator to previous position. O(n).
reference front(); ///< Returns a reference to the first element. The list must be empty.
const_reference front() const; ///< Returns a const reference to the first element. The list must be empty.
void push_front(value_type& value); ///< Adds an element to the front of the list; O(1). The element is not copied. The element must not be in any other list.
void pop_front(); ///< Removes an element from the back of the list; O(n). The element must be present, but is not deallocated.
bool contains(const value_type& value) const; ///< Returns true if the given element is in the list; O(n). Equivalent to (locate(x) != end()).
iterator locate(value_type& value); ///< Converts a reference to an object in the list back to an iterator, or returns end() if it is not part of the list. O(n)
const_iterator locate(const value_type& value) const; ///< Converts a const reference to an object in the list back to a const iterator, or returns end() if it is not part of the list. O(n)
iterator insert(iterator position, value_type& value); ///< Inserts an element before the element pointed to by the iterator. O(n)
iterator insert_after(iterator position, value_type& value); ///< Inserts an element after the element pointed to by the iterator. O(1)
iterator erase(iterator position); ///< Erases the element pointed to by the iterator. O(n)
iterator erase_after(iterator position); ///< Erases the element after the element pointed to by the iterator. O(1)
iterator erase(iterator first, iterator last); ///< Erases elements within the iterator range [first, last). O(n).
iterator erase_after(iterator before_first, iterator last); ///< Erases elements within the iterator range [before_first, last). O(1).
void swap(this_type& x); ///< Swaps the contents of two intrusive lists; O(1).
void splice(iterator position, value_type& value); ///< Moves the given element into this list before the element pointed to by position; O(n).
///< Required: x must be in some list or have first/next pointers that point it itself.
void splice(iterator position, this_type& x); ///< Moves the contents of a list into this list before the element pointed to by position; O(n).
///< Required: &x != this (same as std::list).
void splice(iterator position, this_type& x, iterator xPosition); ///< Moves the given element pointed to i within the list x into the current list before
///< the element pointed to by position; O(n).
void splice(iterator position, this_type& x, iterator first, iterator last); ///< Moves the range of elements [first, last) from list x into the current list before
///< the element pointed to by position; O(n).
///< Required: position must not be in [first, last). (same as std::list).
void splice_after(iterator position, value_type& value); ///< Moves the given element into this list after the element pointed to by position; O(1).
///< Required: x must be in some list or have first/next pointers that point it itself.
void splice_after(iterator position, this_type& x); ///< Moves the contents of a list into this list after the element pointed to by position; O(n).
///< Required: &x != this (same as std::list).
void splice_after(iterator position, this_type& x, iterator xPrevious); ///< Moves the element after xPrevious to be after position. O(1).
///< Required: &x != this (same as std::list).
void splice_after(iterator position, this_type& x, iterator before_first, iterator before_last); ///< Moves the elements in the range of [before_first+1, before_last+1) to be after position. O(1).
bool validate() const;
int validate_iterator(const_iterator i) const;
}; // intrusive_slist
///////////////////////////////////////////////////////////////////////
// IntrusiveSListIterator
///////////////////////////////////////////////////////////////////////
template <typename T, typename Pointer, typename Reference>
inline IntrusiveSListIterator<T, Pointer, Reference>::IntrusiveSListIterator()
{
#if EASTL_DEBUG
mpNode = NULL;
#endif
}
template <typename T, typename Pointer, typename Reference>
inline IntrusiveSListIterator<T, Pointer, Reference>::IntrusiveSListIterator(pointer pNode)
: mpNode(pNode)
{
}
template <typename T, typename Pointer, typename Reference>
inline IntrusiveSListIterator<T, Pointer, Reference>::IntrusiveSListIterator(const iterator& x)
: mpNode(x.mpNode)
{
}
///////////////////////////////////////////////////////////////////////
// intrusive_slist_base
///////////////////////////////////////////////////////////////////////
// To do.
///////////////////////////////////////////////////////////////////////
// intrusive_slist
///////////////////////////////////////////////////////////////////////
// To do.
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename T>
bool operator==(const intrusive_slist<T>& a, const intrusive_slist<T>& b)
{
// If we store an mSize member for intrusive_slist, we want to take advantage of it here.
typename intrusive_slist<T>::const_iterator ia = a.begin();
typename intrusive_slist<T>::const_iterator ib = b.begin();
typename intrusive_slist<T>::const_iterator enda = a.end();
typename intrusive_slist<T>::const_iterator endb = b.end();
while((ia != enda) && (ib != endb) && (*ia == *ib))
{
++ia;
++ib;
}
return (ia == enda) && (ib == endb);
}
template <typename T>
bool operator<(const intrusive_slist<T>& a, const intrusive_slist<T>& b)
{
return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
}
template <typename T>
bool operator!=(const intrusive_slist<T>& a, const intrusive_slist<T>& b)
{
return !(a == b);
}
template <typename T>
bool operator>(const intrusive_slist<T>& a, const intrusive_slist<T>& b)
{
return b < a;
}
template <typename T>
bool operator<=(const intrusive_slist<T>& a, const intrusive_slist<T>& b)
{
return !(b < a);
}
template <typename T>
bool operator>=(const intrusive_slist<T>& a, const intrusive_slist<T>& b)
{
return !(a < b);
}
template <typename T>
void swap(intrusive_slist<T>& a, intrusive_slist<T>& b)
{
a.swap(b);
}
} // namespace eastl
#endif // Header include guard
+932
View File
@@ -0,0 +1,932 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_LIST_MAP_H
#define EASTL_LIST_MAP_H
#include <EASTL/map.h>
namespace eastl
{
/// EASTL_MAP_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
///
#ifndef EASTL_LIST_MAP_DEFAULT_NAME
#define EASTL_LIST_MAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " list_map" // Unless the user overrides something, this is "EASTL list_map".
#endif
/// EASTL_MAP_DEFAULT_ALLOCATOR
///
#ifndef EASTL_LIST_MAP_DEFAULT_ALLOCATOR
#define EASTL_LIST_MAP_DEFAULT_ALLOCATOR allocator_type(EASTL_LIST_MAP_DEFAULT_NAME)
#endif
/// list_map_data_base
///
/// We define a list_map_data_base separately from list_map_data (below), because it
/// allows us to have non-templated operations, and it makes it so that the
/// list_map anchor node doesn't carry a T with it, which would waste space and
/// possibly lead to surprising the user due to extra Ts existing that the user
/// didn't explicitly create. The downside to all of this is that it makes debug
/// viewing of an list_map harder, given that the node pointers are of type
/// list_map_data_base and not list_map_data.
///
struct list_map_data_base
{
list_map_data_base* mpNext;
list_map_data_base* mpPrev;
};
/// list_map_data
///
template <typename Value>
struct list_map_data : public list_map_data_base
{
typedef Value value_type;
list_map_data(const value_type& value);
value_type mValue; // This is a pair of key/value.
};
/// list_map_iterator
///
template <typename T, typename Pointer, typename Reference>
struct list_map_iterator
{
typedef list_map_iterator<T, Pointer, Reference> this_type;
typedef list_map_iterator<T, T*, T&> iterator;
typedef list_map_iterator<T, const T*, const T&> const_iterator;
typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t.
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef list_map_data_base base_node_type;
typedef list_map_data<T> node_type;
typedef Pointer pointer;
typedef Reference reference;
typedef EASTL_ITC_NS::bidirectional_iterator_tag iterator_category;
public:
node_type* mpNode;
public:
list_map_iterator();
list_map_iterator(const base_node_type* pNode);
list_map_iterator(const iterator& x);
reference operator*() const;
pointer operator->() const;
this_type& operator++();
this_type operator++(int);
this_type& operator--();
this_type operator--(int);
}; // list_map_iterator
/// use_value_first
///
/// operator()(x) simply returns x.mValue.first. Used in list_map.
/// This is similar to eastl::use_first, however it assumes that the input type is an object
/// whose mValue is an eastl::pair, and the first value in the pair is the desired return.
///
template <typename Object>
struct use_value_first
{
typedef Object argument_type;
typedef typename Object::value_type::first_type result_type;
const result_type& operator()(const Object& x) const
{ return x.mValue.first; }
};
/// list_map
///
/// Implements a map like container, which also provides functionality similar to a list.
///
/// Note: Like a map, keys must still be unique. As such, push_back() and push_front() operations
/// return a bool indicating success, or failure if the entry's key is already in use.
///
/// list_map is designed to improve performance for situations commonly implemented as:
/// A map, which must be iterated over to find the oldest entry, or purge expired entries.
/// A list, which must be iterated over to remove a player's record when they sign off.
///
/// list_map requires a little more memory per node than either a list or map alone,
/// and many of list_map's functions have a higher operational cost (CPU time) than their
/// counterparts in list and map. However, as the node count increases, list_map quickly outperforms
/// either a list or a map when find [by-index] and front/back type operations are required.
///
/// In essence, list_map avoids O(n) iterations at the expense of additional costs to quick (O(1) and O(log n) operations:
/// push_front(), push_back(), pop_front() and pop_back() have O(log n) operation time, similar to map::insert(), rather than O(1) time like a list,
/// however, front() and back() maintain O(1) operation time.
///
/// As a canonical example, consider a large backlog of player group invites, which are removed when either:
/// The invitation times out - in main loop: while( !listMap.empty() && listMap.front().IsExpired() ) { listMap.pop_front(); }
/// The player rejects the outstanding invitation - on rejection: iter = listMap.find(playerId); if (iter != listMap.end()) { listMap.erase(iter); }
///
/// For a similar example, consider a high volume pending request container which must:
/// Time out old requests (similar to invites timing out above)
/// Remove requests once they've been handled (similar to rejecting invites above)
///
/// For such usage patterns, the performance benefits of list_map become dramatic with
/// common O(n) operations once the node count rises to hundreds or more.
///
/// When high performance is a priority, Containers with thousands of nodes or more
/// can quickly result in unacceptable performance when executing even infrequenty O(n) operations.
///
/// In order to maintain strong performance, avoid iterating over list_map whenever possible.
///
///////////////////////////////////////////////////////////////////////
/// find_as
/// In order to support the ability to have a tree of strings but
/// be able to do efficiently lookups via char pointers (i.e. so they
/// aren't converted to string objects), we provide the find_as
/// function. This function allows you to do a find with a key of a
/// type other than the tree's key type. See the find_as function
/// for more documentation on this.
///
///////////////////////////////////////////////////////////////////////
/// Pool allocation
/// If you want to make a custom memory pool for a list_map container, your pool
/// needs to contain items of type list_map::node_type. So if you have a memory
/// pool that has a constructor that takes the size of pool items and the
/// count of pool items, you would do this (assuming that MemoryPool implements
/// the Allocator interface):
/// typedef list_map<Widget, int, less<Widget>, MemoryPool> WidgetMap; // Delare your WidgetMap type.
/// MemoryPool myPool(sizeof(WidgetMap::node_type), 100); // Make a pool of 100 Widget nodes.
/// WidgetMap myMap(&myPool); // Create a map that uses the pool.
///
template <typename Key, typename T, typename Compare = eastl::less<Key>, typename Allocator = EASTLAllocatorType>
class list_map
: protected rbtree<Key, eastl::list_map_data<eastl::pair<const Key, T> >, Compare, Allocator, eastl::use_value_first<eastl::list_map_data<eastl::pair<const Key, T> > >, true, true>
{
public:
typedef rbtree<Key, eastl::list_map_data<eastl::pair<const Key, T> >, Compare, Allocator,
eastl::use_value_first<eastl::list_map_data<eastl::pair<const Key, T> > >, true, true> base_type;
typedef list_map<Key, T, Compare, Allocator> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::key_type key_type;
typedef T mapped_type;
typedef typename eastl::pair<const Key, T> value_type; // This is intentionally different from base_type::value_type
typedef value_type& reference;
typedef const value_type& const_reference;
typedef typename base_type::node_type node_type; // Despite the internal and external values being different, we're keeping the node type the same as the base
// in order to allow for pool allocation. See EASTL/map.h for more information.
typedef typename eastl::list_map_iterator<value_type, value_type*, value_type&> iterator; // This is intentionally different from base_type::iterator
typedef typename eastl::list_map_iterator<value_type, const value_type*, const value_type&> const_iterator; // This is intentionally different from base_type::const_iterator
typedef eastl::reverse_iterator<iterator> reverse_iterator;
typedef eastl::reverse_iterator<const_iterator> const_reverse_iterator;
typedef typename base_type::allocator_type allocator_type;
typedef typename eastl::pair<iterator, bool> insert_return_type; // This is intentionally removed, as list_map doesn't support insert() functions, in favor of list like push_back and push_front
typedef typename eastl::use_first<value_type> extract_key; // This is intentionally different from base_type::extract_key
using base_type::get_allocator;
using base_type::set_allocator;
using base_type::key_comp;
using base_type::empty;
using base_type::size;
protected:
typedef typename eastl::list_map_data<eastl::pair<const Key, T> > internal_value_type;
protected:
// internal base node, acting as the sentinel for list like behaviors
list_map_data_base mNode;
public:
list_map(const allocator_type& allocator = EASTL_LIST_MAP_DEFAULT_ALLOCATOR);
list_map(const Compare& compare, const allocator_type& allocator = EASTL_MAP_DEFAULT_ALLOCATOR);
// To do: Implement the following:
//list_map(const this_type& x);
//list_map(this_type&& x);
//list_map(this_type&& x, const allocator_type& allocator);
//list_map(std::initializer_list<mapped_type> ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_LIST_MAP_DEFAULT_ALLOCATOR);
//template <typename Iterator>
//list_map(Iterator itBegin, Iterator itEnd);
//this_type& operator=(const this_type& x);
//this_type& operator=(std::initializer_list<mapped_type> ilist);
//this_type& operator=(this_type&& x);
//void swap(this_type& x);
public:
// iterators
iterator begin() EA_NOEXCEPT;
const_iterator begin() const EA_NOEXCEPT;
const_iterator cbegin() const EA_NOEXCEPT;
iterator end() EA_NOEXCEPT;
const_iterator end() const EA_NOEXCEPT;
const_iterator cend() const EA_NOEXCEPT;
reverse_iterator rbegin() EA_NOEXCEPT;
const_reverse_iterator rbegin() const EA_NOEXCEPT;
const_reverse_iterator crbegin() const EA_NOEXCEPT;
reverse_iterator rend() EA_NOEXCEPT;
const_reverse_iterator rend() const EA_NOEXCEPT;
const_reverse_iterator crend() const EA_NOEXCEPT;
public:
// List like methods
reference front();
const_reference front() const;
reference back();
const_reference back() const;
// push_front and push_back which takes in a key/value pair
bool push_front(const value_type& value);
bool push_back(const value_type& value);
// push_front and push_back which take key and value separately, for convenience
bool push_front(const key_type& key, const mapped_type& value);
bool push_back(const key_type& key, const mapped_type& value);
void pop_front();
void pop_back();
public:
// Map like methods
iterator find(const key_type& key);
const_iterator find(const key_type& key) const;
template <typename U, typename Compare2>
iterator find_as(const U& u, Compare2 compare2);
template <typename U, typename Compare2>
const_iterator find_as(const U& u, Compare2 compare2) const;
size_type count(const key_type& key) const;
size_type erase(const key_type& key);
public:
// Shared methods which are common to list and map
iterator erase(const_iterator position);
reverse_iterator erase(const_reverse_iterator position);
void clear();
void reset_lose_memory();
bool validate() const;
int validate_iterator(const_iterator i) const;
public:
// list like functionality which is in consideration for implementation:
// iterator insert(const_iterator position, const value_type& value);
// void remove(const mapped_type& x);
public:
// list like functionality which may be implemented, but is discouraged from implementation:
// due to the liklihood that they would require O(n) time to execute.
// template <typename Predicate>
// void remove_if(Predicate);
// void reverse();
// void sort();
// template<typename Compare>
// void sort(Compare compare);
public:
// map like functionality which list_map does not support, due to abmiguity with list like functionality:
#if !defined(EA_COMPILER_NO_DELETED_FUNCTIONS)
template <typename InputIterator>
list_map(InputIterator first, InputIterator last, const Compare& compare, const allocator_type& allocator = EASTL_RBTREE_DEFAULT_ALLOCATOR) = delete;
insert_return_type insert(const value_type& value) = delete;
iterator insert(const_iterator position, const value_type& value) = delete;
template <typename InputIterator>
void insert(InputIterator first, InputIterator last) = delete;
insert_return_type insert(const key_type& key) = delete;
iterator erase(const_iterator first, const_iterator last) = delete;
reverse_iterator erase(reverse_iterator first, reverse_iterator last) = delete;
void erase(const key_type* first, const key_type* last) = delete;
iterator lower_bound(const key_type& key) = delete;
const_iterator lower_bound(const key_type& key) const = delete;
iterator upper_bound(const key_type& key) = delete;
const_iterator upper_bound(const key_type& key) const = delete;
eastl::pair<iterator, iterator> equal_range(const key_type& key) = delete;
eastl::pair<const_iterator, const_iterator> equal_range(const key_type& key) const = delete;
mapped_type& operator[](const key_type& key) = delete; // Of map, multimap, set, and multimap, only map has operator[].
#endif
public:
// list like functionality which list_map does not support, due to ambiguity with map like functionality:
#if 0
reference push_front() = delete;
void* push_front_uninitialized() = delete;
reference push_back() = delete;
void* push_back_uninitialized() = delete;
iterator insert(const_iterator position) = delete;
void insert(const_iterator position, size_type n, const value_type& value) = delete;
template <typename InputIterator>
void insert(const_iterator position, InputIterator first, InputIterator last) = delete;
iterator erase(const_iterator first, const_iterator last) = delete;
reverse_iterator erase(const_reverse_iterator first, const_reverse_iterator last) = delete;
void splice(const_iterator position, this_type& x) = delete
void splice(const_iterator position, this_type& x, const_iterator i) = delete;
void splice(const_iterator position, this_type& x, const_iterator first, const_iterator last) = delete;
void merge(this_type& x) = delete;
template <typename Compare>
void merge(this_type& x, Compare compare) = delete;
void unique() = delete; // Uniqueness is enforced by map functionality
template <typename BinaryPredicate>
void unique(BinaryPredicate) = delete; // Uniqueness is enforced by map functionality
#endif
}; // list_map
///////////////////////////////////////////////////////////////////////
// list_map_data
///////////////////////////////////////////////////////////////////////
template <typename Value>
inline list_map_data<Value>::list_map_data(const Value& value)
: mValue(value)
{
mpNext = NULL; // GCC 4.8 is generating warnings about referencing these values in list_map::push_front unless we
mpPrev = NULL; // initialize them here. The compiler seems to be mistaken, as our code isn't actually using them unintialized.
}
///////////////////////////////////////////////////////////////////////
// list_map_iterator
///////////////////////////////////////////////////////////////////////
template <typename T, typename Pointer, typename Reference>
inline list_map_iterator<T, Pointer, Reference>::list_map_iterator()
: mpNode(NULL)
{
// Empty
}
template <typename T, typename Pointer, typename Reference>
inline list_map_iterator<T, Pointer, Reference>::list_map_iterator(const base_node_type* pNode)
: mpNode(static_cast<node_type*>(const_cast<base_node_type*>(pNode)))
{
// Empty
}
template <typename T, typename Pointer, typename Reference>
inline list_map_iterator<T, Pointer, Reference>::list_map_iterator(const iterator& x)
: mpNode(const_cast<node_type*>(x.mpNode))
{
// Empty
}
template <typename T, typename Pointer, typename Reference>
inline typename list_map_iterator<T, Pointer, Reference>::reference
list_map_iterator<T, Pointer, Reference>::operator*() const
{
return mpNode->mValue;
}
template <typename T, typename Pointer, typename Reference>
inline typename list_map_iterator<T, Pointer, Reference>::pointer
list_map_iterator<T, Pointer, Reference>::operator->() const
{
return &mpNode->mValue;
}
template <typename T, typename Pointer, typename Reference>
inline typename list_map_iterator<T, Pointer, Reference>::this_type&
list_map_iterator<T, Pointer, Reference>::operator++()
{
mpNode = static_cast<node_type*>(mpNode->mpNext);
return *this;
}
template <typename T, typename Pointer, typename Reference>
inline typename list_map_iterator<T, Pointer, Reference>::this_type
list_map_iterator<T, Pointer, Reference>::operator++(int)
{
this_type temp(*this);
mpNode = static_cast<node_type*>(mpNode->mpNext);
return temp;
}
template <typename T, typename Pointer, typename Reference>
inline typename list_map_iterator<T, Pointer, Reference>::this_type&
list_map_iterator<T, Pointer, Reference>::operator--()
{
mpNode = static_cast<node_type*>(mpNode->mpPrev);
return *this;
}
template <typename T, typename Pointer, typename Reference>
inline typename list_map_iterator<T, Pointer, Reference>::this_type
list_map_iterator<T, Pointer, Reference>::operator--(int)
{
this_type temp(*this);
mpNode = static_cast<node_type*>(mpNode->mpPrev);
return temp;
}
// We provide additional template paremeters here to support comparisons between const and non-const iterators.
// See C++ defect report #179, or EASTL/list.h for more information.
template <typename T, typename PointerA, typename ReferenceA, typename PointerB, typename ReferenceB>
inline bool operator==(const list_map_iterator<T, PointerA, ReferenceA>& a,
const list_map_iterator<T, PointerB, ReferenceB>& b)
{
return a.mpNode == b.mpNode;
}
template <typename T, typename PointerA, typename ReferenceA, typename PointerB, typename ReferenceB>
inline bool operator!=(const list_map_iterator<T, PointerA, ReferenceA>& a,
const list_map_iterator<T, PointerB, ReferenceB>& b)
{
return a.mpNode != b.mpNode;
}
// We provide a version of operator!= for the case where the iterators are of the
// same type. This helps prevent ambiguity errors in the presence of rel_ops.
template <typename T, typename Pointer, typename Reference>
inline bool operator!=(const list_map_iterator<T, Pointer, Reference>& a,
const list_map_iterator<T, Pointer, Reference>& b)
{
return a.mpNode != b.mpNode;
}
///////////////////////////////////////////////////////////////////////
// list_map
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, typename Compare, typename Allocator>
inline list_map<Key, T, Compare, Allocator>::list_map(const allocator_type& allocator)
: base_type(allocator)
{
mNode.mpNext = &mNode;
mNode.mpPrev = &mNode;
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline list_map<Key, T, Compare, Allocator>::list_map(const Compare& compare, const allocator_type& allocator)
: base_type(compare, allocator)
{
mNode.mpNext = &mNode;
mNode.mpPrev = &mNode;
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::iterator
list_map<Key, T, Compare, Allocator>::begin() EA_NOEXCEPT
{
return iterator(mNode.mpNext);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_iterator
list_map<Key, T, Compare, Allocator>::begin() const EA_NOEXCEPT
{
return const_iterator(mNode.mpNext);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_iterator
list_map<Key, T, Compare, Allocator>::cbegin() const EA_NOEXCEPT
{
return const_iterator(mNode.mpNext);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::iterator
list_map<Key, T, Compare, Allocator>::end() EA_NOEXCEPT
{
return iterator(&mNode);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_iterator
list_map<Key, T, Compare, Allocator>::end() const EA_NOEXCEPT
{
return const_iterator(&mNode);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_iterator
list_map<Key, T, Compare, Allocator>::cend() const EA_NOEXCEPT
{
return const_iterator(&mNode);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::reverse_iterator
list_map<Key, T, Compare, Allocator>::rbegin() EA_NOEXCEPT
{
return reverse_iterator(&mNode);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_reverse_iterator
list_map<Key, T, Compare, Allocator>::rbegin() const EA_NOEXCEPT
{
return const_reverse_iterator(&mNode);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_reverse_iterator
list_map<Key, T, Compare, Allocator>::crbegin() const EA_NOEXCEPT
{
return const_reverse_iterator(&mNode);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::reverse_iterator
list_map<Key, T, Compare, Allocator>::rend() EA_NOEXCEPT
{
return reverse_iterator(mNode.mpNext);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_reverse_iterator
list_map<Key, T, Compare, Allocator>::rend() const EA_NOEXCEPT
{
return const_reverse_iterator(mNode.mpNext);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_reverse_iterator
list_map<Key, T, Compare, Allocator>::crend() const EA_NOEXCEPT
{
return const_reverse_iterator(mNode.mpNext);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::reference
list_map<Key, T, Compare, Allocator>::front()
{
#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED
if (EASTL_UNLIKELY(static_cast<internal_value_type*>(mNode.mpNext) == &mNode))
EASTL_FAIL_MSG("list_map::front -- empty container");
#else
// We allow the user to reference an empty container.
#endif
return static_cast<internal_value_type*>(mNode.mpNext)->mValue;
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_reference
list_map<Key, T, Compare, Allocator>::front() const
{
#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED
if (EASTL_UNLIKELY(static_cast<internal_value_type*>(mNode.mpNext) == &mNode))
EASTL_FAIL_MSG("list_map::front -- empty container");
#else
// We allow the user to reference an empty container.
#endif
return static_cast<internal_value_type*>(mNode.mpNext)->mValue;
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::reference
list_map<Key, T, Compare, Allocator>::back()
{
#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED
if (EASTL_UNLIKELY(static_cast<internal_value_type*>(mNode.mpNext) == &mNode))
EASTL_FAIL_MSG("list_map::back -- empty container");
#else
// We allow the user to reference an empty container.
#endif
return static_cast<internal_value_type*>(mNode.mpPrev)->mValue;
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_reference
list_map<Key, T, Compare, Allocator>::back() const
{
#if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED
if (EASTL_UNLIKELY(static_cast<internal_value_type*>(mNode.mpNext) == &mNode))
EASTL_FAIL_MSG("list_map::back -- empty container");
#else
// We allow the user to reference an empty container.
#endif
return static_cast<internal_value_type*>(mNode.mpPrev)->mValue;
}
template <typename Key, typename T, typename Compare, typename Allocator>
bool list_map<Key, T, Compare, Allocator>::push_front(const value_type& value)
{
internal_value_type tempValue(value);
typename base_type::insert_return_type baseReturn = base_type::insert(tempValue);
// Did the insert succeed?
if (baseReturn.second)
{
internal_value_type* pNode = &(*baseReturn.first);
pNode->mpNext = mNode.mpNext;
pNode->mpPrev = &mNode;
mNode.mpNext->mpPrev = pNode;
mNode.mpNext = pNode;
return true;
}
else
{
return false;
}
}
template <typename Key, typename T, typename Compare, typename Allocator>
bool list_map<Key, T, Compare, Allocator>::push_back(const value_type& value)
{
internal_value_type tempValue(value);
typename base_type::insert_return_type baseReturn = base_type::insert(tempValue);
// Did the insert succeed?
if (baseReturn.second)
{
internal_value_type* pNode = &(*baseReturn.first);
pNode->mpPrev = mNode.mpPrev;
pNode->mpNext = &mNode;
mNode.mpPrev->mpNext = pNode;
mNode.mpPrev = pNode;
return true;
}
else
{
return false;
}
}
template <typename Key, typename T, typename Compare, typename Allocator>
bool list_map<Key, T, Compare, Allocator>::push_front(const key_type& key, const mapped_type& value)
{
return push_front(eastl::make_pair(key, value));
}
template <typename Key, typename T, typename Compare, typename Allocator>
bool list_map<Key, T, Compare, Allocator>::push_back(const key_type& key, const mapped_type& value)
{
return push_back(eastl::make_pair(key, value));
}
template <typename Key, typename T, typename Compare, typename Allocator>
void list_map<Key, T, Compare, Allocator>::pop_front()
{
#if EASTL_ASSERT_ENABLED
if (EASTL_UNLIKELY(empty()))
EASTL_FAIL_MSG("list_map::pop_front -- empty container");
#endif
erase(static_cast<internal_value_type*>(mNode.mpNext)->mValue.first);
}
template <typename Key, typename T, typename Compare, typename Allocator>
void list_map<Key, T, Compare, Allocator>::pop_back()
{
#if EASTL_ASSERT_ENABLED
if (EASTL_UNLIKELY(empty()))
EASTL_FAIL_MSG("list_map::pop_back -- empty container");
#endif
erase(static_cast<internal_value_type*>(mNode.mpPrev)->mValue.first);
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::iterator
list_map<Key, T, Compare, Allocator>::find(const key_type& key)
{
typename base_type::iterator baseIter = base_type::find(key);
if (baseIter != base_type::end())
{
return iterator(&(*baseIter));
}
else
{
return end();
}
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::const_iterator
list_map<Key, T, Compare, Allocator>::find(const key_type& key) const
{
typename base_type::const_iterator baseIter = base_type::find(key);
if (baseIter != base_type::end())
{
return const_iterator(&(*baseIter));
}
else
{
return end();
}
}
template <typename Key, typename T, typename Compare, typename Allocator>
template <typename U, typename Compare2>
inline typename list_map<Key, T, Compare, Allocator>::iterator
list_map<Key, T, Compare, Allocator>::find_as(const U& u, Compare2 compare2)
{
typename base_type::iterator baseIter = base_type::find_as(u, compare2);
if (baseIter != base_type::end())
{
return iterator(&(*baseIter));
}
else
{
return end();
}
}
template <typename Key, typename T, typename Compare, typename Allocator>
template <typename U, typename Compare2>
inline typename list_map<Key, T, Compare, Allocator>::const_iterator
list_map<Key, T, Compare, Allocator>::find_as(const U& u, Compare2 compare2) const
{
typename base_type::const_iterator baseIter = base_type::find_as(u, compare2);
if (baseIter != base_type::end())
{
return const_iterator(&(*baseIter));
}
else
{
return end();
}
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::size_type
list_map<Key, T, Compare, Allocator>::count(const key_type& key) const
{
const typename base_type::const_iterator it = base_type::find(key);
return (it != base_type::end()) ? 1 : 0;
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::size_type
list_map<Key, T, Compare, Allocator>::erase(const key_type& key)
{
typename base_type::iterator baseIter = base_type::find(key);
if (baseIter != base_type::end())
{
internal_value_type* node = &(*baseIter);
node->mpNext->mpPrev = node->mpPrev;
node->mpPrev->mpNext = node->mpNext;
base_type::erase(baseIter);
return 1;
}
return 0;
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::iterator
list_map<Key, T, Compare, Allocator>::erase(const_iterator position)
{
iterator posIter(position.mpNode); // Convert from const.
iterator eraseIter(posIter++);
erase(eraseIter->first);
return posIter;
}
template <typename Key, typename T, typename Compare, typename Allocator>
inline typename list_map<Key, T, Compare, Allocator>::reverse_iterator
list_map<Key, T, Compare, Allocator>::erase(const_reverse_iterator position)
{
return reverse_iterator(erase((++position).base()));
}
template <typename Key, typename T, typename Compare, typename Allocator>
void list_map<Key, T, Compare, Allocator>::clear()
{
base_type::clear();
mNode.mpNext = &mNode;
mNode.mpPrev = &mNode;
}
template <typename Key, typename T, typename Compare, typename Allocator>
void list_map<Key, T, Compare, Allocator>::reset_lose_memory()
{
base_type::reset_lose_memory();
mNode.mpNext = &mNode;
mNode.mpPrev = &mNode;
}
template <typename Key, typename T, typename Compare, typename Allocator>
bool list_map<Key, T, Compare, Allocator>::validate() const
{
if (!base_type::validate())
{
return false;
}
size_type nodeCount(0);
list_map_data_base* node = mNode.mpNext;
while (node != &mNode)
{
internal_value_type* data = static_cast<internal_value_type*>(node);
if (base_type::find(data->mValue.first) == base_type::end())
{
return false;
}
node = node->mpNext;
++nodeCount;
}
if (nodeCount != size())
{
return false;
}
nodeCount = 0;
node = mNode.mpPrev;
while (node != &mNode)
{
internal_value_type* data = static_cast<internal_value_type*>(node);
if (base_type::find(data->mValue.first) == base_type::end())
{
return false;
}
node = node->mpPrev;
++nodeCount;
}
if (nodeCount != size())
{
return false;
}
return true;
}
template <typename Key, typename T, typename Compare, typename Allocator>
int list_map<Key, T, Compare, Allocator>::validate_iterator(const_iterator iter) const
{
for (const_iterator temp = begin(), tempEnd = end(); temp != tempEnd; ++temp)
{
if (temp == iter)
{
return (isf_valid | isf_current | isf_can_dereference);
}
}
if (iter == end())
return (isf_valid | isf_current);
return isf_none;
}
} // namespace eastl
#endif // Header include guard
+424
View File
@@ -0,0 +1,424 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// lru_cache is a container that simplifies caching of objects in a map.
// Basically, you give the container a key, like a string, and the data you want.
// The container provides callback mechanisms to generate data if it's missing
// as well as delete data when it's purged from the cache. This container
// uses a least recently used method: whatever the oldest item is will be
// replaced with a new entry.
//
// Algorithmically, the container is a combination of a map and a list.
// The list stores the age of the entries by moving the entry to the head
// of the list on each access, either by a call to get() or to touch().
// The map is just the map as one would expect.
//
// This is useful for caching off data that is expensive to generate,
// for example text to speech wave files that are dynamically generated,
// but that will need to be reused, as is the case in narration of menu
// entries as a user scrolls through the entries.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_LRUCACHE_H
#define EASTL_LRUCACHE_H
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once
#endif
#include <EASTL/list.h>
#include <EASTL/unordered_map.h>
#include <EASTL/optional.h>
namespace eastl
{
/// EASTL_LRUCACHE_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
///
#ifndef EASTL_LRUCACHE_DEFAULT_NAME
#define EASTL_LRUCACHE_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " lru_cache" // Unless the user overrides something, this is "EASTL lru_cache".
#endif
/// EASTL_LRUCACHE_DEFAULT_ALLOCATOR
///
#ifndef EASTL_LRUCACHE_DEFAULT_ALLOCATOR
#define EASTL_LRUCACHE_DEFAULT_ALLOCATOR allocator_type(EASTL_LRUCACHE_DEFAULT_NAME)
#endif
/// lru_cache
///
/// Implements a caching map based off of a key and data.
/// LRUList parameter is any container that guarantees the validity of its iterator even after a modification (e.g. list)
/// LRUMap is any mapping container that can map a key to some data. By default, we use unordered_set, but it might be better
/// to use hash_map or some other structure depending on your key/data combination. For example, you may want to swap the
/// map backing if using strings as keys or if the data objects are small. In any case, unordered_set is a good default and should
/// work well enough since the purpose of this class is to cache results of expensive, order of milliseconds, operations
///
/// Algorithmic Performance (default data structures):
/// touch() -> O(1)
/// insert() / update(), get() / operator[] -> equivalent to unordered_set (O(1) on average, O(n) worst)
/// size() -> O(1)
///
/// All accesses to a given key (insert, update, get) will push that key to most recently used.
/// If the data objects are shared between threads, it would be best to use a smartptr to manage the lifetime of the data.
/// as it could be removed from the cache while in use by another thread.
template <typename Key,
typename Value,
typename Allocator = EASTLAllocatorType,
typename list_type = eastl::list<Key, Allocator>,
typename map_type = eastl::unordered_map<Key,
eastl::pair<Value, typename list_type::iterator>,
eastl::hash<Key>,
eastl::equal_to<Key>,
Allocator>>
class lru_cache
{
public:
using key_type = Key;
using value_type = Value;
using allocator_type = Allocator;
using size_type = eastl_size_t;
using list_iterator = typename list_type::iterator;
using map_iterator = typename map_type::iterator;
using data_container_type = eastl::pair<value_type, list_iterator>;
using iterator = typename map_type::iterator;
using const_iterator = typename map_type::const_iterator;
using this_type = lru_cache<key_type, value_type, Allocator, list_type, map_type>;
using create_callback_type = eastl::function<value_type(key_type)>;
using delete_callback_type = eastl::function<void(const value_type &)>;
/// lru_cache constructor
///
/// Creates a Key / Value map that only stores size Value objects until it deletes them.
/// For complex objects or operations, the creator and deletor callbacks can be used.
/// This works just like a regular map object: on access, the Value will be created if it doesn't exist, returned otherwise.
explicit lru_cache(size_type size,
const allocator_type& allocator = EASTL_LRUCACHE_DEFAULT_ALLOCATOR,
create_callback_type creator = nullptr,
delete_callback_type deletor = nullptr)
: m_list(allocator)
, m_map(allocator)
, m_capacity(size)
, m_create_callback(creator)
, m_delete_callback(deletor)
{
}
/// lru_cache destructor
///
/// Iterates across every entry in the map and calls the deletor before calling the standard destructors
~lru_cache()
{
// Destruct everything we have cached
for (auto& iter : m_map)
{
if (m_delete_callback)
m_delete_callback(iter.second.first);
}
}
lru_cache(std::initializer_list<eastl::pair<Key, Value>> il)
: lru_cache(static_cast<size_type>(il.size()))
{
for(auto& p : il)
insert_or_assign(p.first, p.second);
}
// TODO(rparolin): Why do we prevent copies? And what about moves?
lru_cache(const this_type&) = delete;
this_type &operator=(const this_type&) = delete;
/// insert
///
/// insert key k with value v.
/// If key already exists, no change is made and the return value is false.
/// If the key doesn't exist, the data is added to the map and the return value is true.
bool insert(const key_type& k, const value_type& v)
{
if (m_map.find(k) == m_map.end())
{
make_space();
m_list.push_front(k);
m_map[k] = data_container_type(v, m_list.begin());
return true;
}
else
{
return false;
}
}
/// emplace
///
/// Places a new object in place k created with args
/// If the key already exists, it is replaced.
template <typename... Args>
void emplace(const key_type& k, Args&&... args)
{
make_space();
m_list.push_front(k);
m_map.emplace(k, data_container_type(eastl::forward<Args>(args)..., m_list.begin()));
}
/// insert_or_assign
///
/// Same as add, but replaces the data at key k, if it exists, with the new entry v
/// Note that the deletor for the old v will be called before it's replaced with the new value of v
void insert_or_assign(const key_type& k, const value_type& v)
{
auto iter = m_map.find(k);
if (m_map.find(k) != m_map.end())
{
assign(iter, v);
}
else
{
insert(k, v);
}
}
/// contains
///
/// Returns true if key k exists in the cache
bool contains(const key_type& k) const
{
return m_map.find(k) != m_map.end();
}
/// at
///
/// Retrives the data for key k, not valid if k does not exist
eastl::optional<value_type> at(const key_type& k)
{
auto iter = m_map.find(k);
if (iter != m_map.end())
{
return iter->second.first;
}
else
{
return eastl::nullopt;
}
}
/// get
///
/// Retrives the data for key k. If no data exists, it will be created by calling the
/// creator.
value_type& get(const key_type& k)
{
auto iter = m_map.find(k);
// The entry exists in the cache
if (iter != m_map.end())
{
touch(k);
return iter->second.first;
}
else // The entry doesn't exist in the cache, so create one
{
// Add the entry to the map
insert(k, m_create_callback ? m_create_callback(k) : value_type());
// return the new data
return m_map[k].first;
}
}
/// Equivalent to get(k)
value_type& operator[](const key_type& k) { return get(k); }
/// erase
///
/// erases key k from the cache.
/// If k does not exist, returns false. If k exists, returns true.
bool erase(const key_type& k)
{
auto iter = m_map.find(k);
if (iter != m_map.end())
{
m_list.erase(iter->second.second);
// Delete the actual entry
map_erase(iter);
return true;
}
return false;
}
/// erase_oldest
///
/// Removes the oldest entry from the cache.
void erase_oldest()
{
auto key = m_list.back();
m_list.pop_back();
// Delete the actual entry
auto iter = m_map.find(key);
map_erase(iter);
}
/// touch
///
/// Touches key k, marking it as most recently used.
/// If k does not exist, returns false. If the touch was successful, returns true.
bool touch(const key_type& k)
{
auto iter = m_map.find(k);
if (iter != m_map.end())
{
touch(iter);
return true;
}
return false;
}
/// touch
///
/// Touches key at iterator iter, moving it to most recently used position
void touch(iterator& iter)
{
auto listRef = iter->second.second;
m_list.erase(listRef);
m_list.push_front(iter->first);
iter->second.second = m_list.begin();
}
/// assign
///
/// Updates key k with data v.
/// If key k does not exist, returns false and no changes are made.
/// If key k exists, existing data has its deletor called and key k's data is replaced with new v data
bool assign(const key_type& k, const value_type& v)
{
auto iter = m_map.find(k);
if (iter != m_map.end())
{
assign(iter, v);
return true;
}
return false;
}
/// assign
///
/// Updates data at spot iter with data v.
void assign(iterator& iter, const value_type& v)
{
if (m_delete_callback)
m_delete_callback(iter->second.first);
touch(iter);
iter->second.first = v;
}
// standard container functions
iterator begin() EA_NOEXCEPT { return m_map.begin(); }
iterator end() EA_NOEXCEPT { return m_map.end(); }
iterator rbegin() EA_NOEXCEPT { return m_map.rbegin(); }
iterator rend() EA_NOEXCEPT { return m_map.rend(); }
const_iterator begin() const EA_NOEXCEPT { return m_map.begin(); }
const_iterator cbegin() const EA_NOEXCEPT { return m_map.cbegin(); }
const_iterator crbegin() const EA_NOEXCEPT { return m_map.crbegin(); }
const_iterator end() const EA_NOEXCEPT { return m_map.end(); }
const_iterator cend() const EA_NOEXCEPT { return m_map.cend(); }
const_iterator crend() const EA_NOEXCEPT { return m_map.crend(); }
bool empty() const EA_NOEXCEPT { return m_map.empty(); }
size_type size() const EA_NOEXCEPT { return m_map.size(); }
size_type capacity() const EA_NOEXCEPT { return m_capacity; }
void clear() EA_NOEXCEPT
{
// Since we have a delete callback, we want to reuse the trim function by cheating the max
// size to clear all the entries to avoid duplicating code.
auto old_max = m_capacity;
m_capacity = 0;
trim();
m_capacity = old_max;
}
/// resize
///
/// Resizes the cache. Can be used to either expand or contract the cache.
/// In the case of a contraction, the oldest entries will be evicted with their respective
/// deletors called before completing.
void resize(size_type newSize)
{
m_capacity = newSize;
trim();
}
void setCreateCallback(create_callback_type callback) { m_create_callback = callback; }
void setDeleteCallback(delete_callback_type callback) { m_delete_callback = callback; }
// EASTL extensions
const allocator_type& get_allocator() const EA_NOEXCEPT { return m_map.get_allocator(); }
allocator_type& get_allocator() EA_NOEXCEPT { return m_map.get_allocator(); }
void set_allocator(const allocator_type& allocator) { m_map.set_allocator(allocator); m_list.set_allocator(allocator); }
/// Does not reset the callbacks
void reset_lose_memory() EA_NOEXCEPT { m_map.reset_lose_memory(); m_list.reset_lose_memory(); }
private:
inline void map_erase(map_iterator pos)
{
if (m_delete_callback)
m_delete_callback(pos->second.first);
m_map.erase(pos);
}
bool trim()
{
if (size() <= m_capacity)
{
return false; // No trim necessary
}
// We need to trim
do
{
erase_oldest();
} while (m_list.size() > m_capacity);
return true;
}
void make_space()
{
if (size() == m_capacity)
{
erase_oldest();
}
}
private:
list_type m_list;
map_type m_map;
size_type m_capacity;
create_callback_type m_create_callback;
delete_callback_type m_delete_callback;
};
}
#endif
+81
View File
@@ -0,0 +1,81 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_OVERLOADED_H
#define EASTL_OVERLOADED_H
#include <EASTL/internal/move_help.h>
#include <EASTL/type_traits.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed
// improvements in apps as a result.
#endif
namespace eastl
{
///////////////////////////////////////////////////////////////////////////
/// overloaded
///
/// A helper class that permits you to combine multiple function objects into one.
/// Typically, this helper is really handy when visiting an eastl::variant with multiple lambdas.
/// Example:
///
/// eastl::variant<int, string> v{42};
///
/// eastl::visit(
/// eastl::overloaded{
/// [](const int& x) { std::cout << "Visited an integer: " << x << "\n"; }, // Will reach that lambda with x == 42.
/// [](const string& s) { std::cout << "Visited an string: " << s << "\n"; }
/// },
/// v
/// );
///////////////////////////////////////////////////////////////////////////
template <class... T>
struct overloaded;
template <class T>
struct overloaded<T> : T
{
template <class U>
EA_CPP14_CONSTEXPR overloaded(U&& u) : T(eastl::forward<U>(u))
{
}
using T::operator();
};
template <class T, class... R>
struct overloaded<T, R...> : T, overloaded<R...>
{
template <class U, class... V>
EA_CPP14_CONSTEXPR overloaded(U&& u, V&&... v) : T(eastl::forward<U>(u)), overloaded<R...>(eastl::forward<V>(v)...)
{
}
using T::operator();
using overloaded<R...>::operator();
};
#ifdef __cpp_deduction_guides
template <class... T>
overloaded(T...) -> overloaded<T...>;
#endif
///////////////////////////////////////////////////////////////////////////
/// make_overloaded
///
/// Helper function to create an overloaded instance when lacking deduction guides.
/// make_overloaded(f1, f2, f3) == overloaded{f1, f2, f3}
///////////////////////////////////////////////////////////////////////////
template <class... T>
EA_CPP14_CONSTEXPR overloaded<typename eastl::remove_cvref<T>::type...> make_overloaded(T&&... t)
{
return overloaded<typename eastl::remove_cvref<T>::type...>{eastl::forward<T>(t)...};
}
} // namespace eastl
#endif // EASTL_OVERLOADED_H
File diff suppressed because it is too large Load Diff
+204
View File
@@ -0,0 +1,204 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// This file implements additional sort algorithms beyond the basic set.
// Included here are:
// selection_sort -- Unstable.
// shaker_sort -- Stable.
// bucket_sort -- Stable.
//
//////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_SORT_EXTRA_H
#define EASTL_SORT_EXTRA_H
#include <EASTL/internal/config.h>
#include <EASTL/iterator.h>
#include <EASTL/algorithm.h>
#include <EASTL/functional.h>
#include <EASTL/heap.h>
#include <EASTL/sort.h> // For backwards compatibility due to sorts moved from here to sort.h.
#include <EASTL/allocator.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// selection_sort
///
/// Implements the SelectionSort algorithm.
///
template <typename ForwardIterator, typename StrictWeakOrdering>
void selection_sort(ForwardIterator first, ForwardIterator last, StrictWeakOrdering compare)
{
ForwardIterator iCurrent, iMin;
for(; first != last; ++first)
{
iCurrent = first;
iMin = iCurrent;
for(++iCurrent; iCurrent != last; ++iCurrent)
{
if(compare(*iCurrent, *iMin))
{
EASTL_VALIDATE_COMPARE(!compare(*iMin, *iCurrent)); // Validate that the compare function is sane.
iMin = iCurrent;
}
}
if(first != iMin)
eastl::iter_swap(first, iMin);
}
} // selection_sort
template <typename ForwardIterator>
inline void selection_sort(ForwardIterator first, ForwardIterator last)
{
typedef eastl::less<typename eastl::iterator_traits<ForwardIterator>::value_type> Less;
eastl::selection_sort<ForwardIterator, Less>(first, last, Less());
}
/// shaker_sort
///
/// Implements the ShakerSort algorithm, which is a sorting algorithm which
/// improves on bubble_sort by sweeping both from left to right and right
/// to left, resulting in less iteration.
///
template <typename BidirectionalIterator, typename StrictWeakOrdering>
void shaker_sort(BidirectionalIterator first, BidirectionalIterator last, StrictWeakOrdering compare)
{
if(first != last)
{
BidirectionalIterator iCurrent, iNext, iLastModified;
--last;
while(first != last)
{
iLastModified = first;
for(iCurrent = first; iCurrent != last; iCurrent = iNext)
{
iNext = iCurrent;
++iNext;
if(compare(*iNext, *iCurrent))
{
EASTL_VALIDATE_COMPARE(!compare(*iCurrent, *iNext)); // Validate that the compare function is sane.
iLastModified = iCurrent;
eastl::iter_swap(iCurrent, iNext);
}
}
last = iLastModified;
if(first != last)
{
for(iCurrent = last; iCurrent != first; iCurrent = iNext)
{
iNext = iCurrent;
--iNext;
if(compare(*iCurrent, *iNext))
{
EASTL_VALIDATE_COMPARE(!compare(*iNext, *iCurrent)); // Validate that the compare function is sane.
iLastModified = iCurrent;
eastl::iter_swap(iNext, iCurrent);
}
}
first = iLastModified;
}
}
}
} // shaker_sort
template <typename BidirectionalIterator>
inline void shaker_sort(BidirectionalIterator first, BidirectionalIterator last)
{
typedef eastl::less<typename eastl::iterator_traits<BidirectionalIterator>::value_type> Less;
eastl::shaker_sort<BidirectionalIterator, Less>(first, last, Less());
}
/// bucket_sort
///
/// Implements the BucketSort algorithm.
///
/// Example usage:
/// const size_t kElementRange = 32;
/// vector<int> intArray(1000);
///
/// for(int i = 0; i < 1000; i++)
/// intArray[i] = rand() % kElementRange;
///
/// vector< vector<int> > bucketArray(kElementRange);
/// bucket_sort(intArray.begin(), intArray.end(), bucketArray, eastl::hash_use_self<int>());
///
template <typename T>
struct hash_use_self
{
T operator()(const T& x) const
{ return x; }
};
// Requires buckeyArray to be an array of arrays with a size equal to the range of values
// returned by the hash function. The hash function is required to return a unique value
// for each uniquely sorted element. Usually the way this is done is the elements are
// integers of a limited range (e.g. 0-64) and the hash function returns the element value
// itself. If you had a case where all elements were always even numbers (e.g. 0-128),
// you could use a custom hash function that returns (element value / 2).
//
// The user is required to provide an empty bucketArray to this function. This function returns
// with the bucketArray non-empty. This function doesn't clear the bucketArray because that takes
// time and the user might not need it to be cleared, at least at that time.
//
template <typename ForwardIterator, typename ContainerArray, typename HashFunction>
void bucket_sort(ForwardIterator first, ForwardIterator last, ContainerArray& bucketArray, HashFunction hash /*= hash_use_self*/)
{
for(ForwardIterator iInput = first; iInput != last; ++iInput)
bucketArray[hash(*iInput)].push_back(*iInput);
for(typename ContainerArray::const_iterator iBucket = bucketArray.begin(); iBucket != bucketArray.end(); ++iBucket)
first = eastl::copy((*iBucket).begin(), (*iBucket).end(), first);
}
} // namespace eastl
#endif // Header include guard
File diff suppressed because it is too large Load Diff
+759
View File
@@ -0,0 +1,759 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements the eastl::chrono specification which is part of the
// standard STL date and time library. eastl::chrono implements all the
// mechanisms required to capture and manipulate times retrieved from the
// provided clocks. It implements the all of the features to allow type safe
// durations to be used in code.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_CHRONO_H
#define EASTL_CHRONO_H
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once
#endif
#include <EASTL/internal/config.h>
#include <EASTL/type_traits.h>
#include <EASTL/numeric_limits.h>
#include <EASTL/ratio.h>
// TODO: move to platform specific cpp or header file
#if defined EA_PLATFORM_MICROSOFT
EA_DISABLE_ALL_VC_WARNINGS()
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#undef NOMINMAX
#define NOMINMAX
#include <Windows.h>
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
EA_RESTORE_ALL_VC_WARNINGS()
#endif
#if defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_MINGW)
// Nothing to do
#elif defined(EA_PLATFORM_APPLE)
#include <mach/mach_time.h>
#elif defined(EA_PLATFORM_POSIX) || defined(EA_PLATFORM_MINGW) || defined(EA_PLATFORM_ANDROID)
// Posix means Linux, Unix, and Macintosh OSX, among others (including Linux-based mobile platforms).
#if defined(EA_PLATFORM_MINGW)
#include <pthread_time.h>
#endif
#include <time.h>
#if (defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC))
#include <errno.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif
#endif
namespace eastl
{
namespace chrono
{
///////////////////////////////////////////////////////////////////////////////
// treat_as_floating_point
///////////////////////////////////////////////////////////////////////////////
template <class Rep>
struct treat_as_floating_point : is_floating_point<Rep> {};
///////////////////////////////////////////////////////////////////////////////
// 20.12.4, duration_values
///////////////////////////////////////////////////////////////////////////////
template <class Rep>
struct duration_values
{
public:
EASTL_FORCE_INLINE static EA_CONSTEXPR Rep zero() { return Rep(0); }
EASTL_FORCE_INLINE static EA_CONSTEXPR Rep max() { return eastl::numeric_limits<Rep>::max(); }
EASTL_FORCE_INLINE static EA_CONSTEXPR Rep min() { return eastl::numeric_limits<Rep>::lowest(); }
};
///////////////////////////////////////////////////////////////////////////////
// duration fwd_decl
///////////////////////////////////////////////////////////////////////////////
template <typename Rep, typename Period = ratio<1>>
class duration;
namespace Internal
{
///////////////////////////////////////////////////////////////////////////////
// IsRatio
///////////////////////////////////////////////////////////////////////////////
template <typename> struct IsRatio : eastl::false_type {};
template <intmax_t N, intmax_t D> struct IsRatio<ratio<N, D>> : eastl::true_type {};
template <intmax_t N, intmax_t D> struct IsRatio<const ratio<N, D>> : eastl::true_type {};
template <intmax_t N, intmax_t D> struct IsRatio<volatile ratio<N, D>> : eastl::true_type {};
template <intmax_t N, intmax_t D> struct IsRatio<const volatile ratio<N, D>> : eastl::true_type {};
///////////////////////////////////////////////////////////////////////////////
// IsDuration
///////////////////////////////////////////////////////////////////////////////
template<typename> struct IsDuration : eastl::false_type{};
template<typename Rep, typename Period> struct IsDuration<duration<Rep, Period>> : eastl::true_type{};
template<typename Rep, typename Period> struct IsDuration<const duration<Rep, Period>> : eastl::true_type{};
template<typename Rep, typename Period> struct IsDuration<volatile duration<Rep, Period>> : eastl::true_type{};
template<typename Rep, typename Period> struct IsDuration<const volatile duration<Rep, Period>> : eastl::true_type{};
///////////////////////////////////////////////////////////////////////////////
// RatioGCD
///////////////////////////////////////////////////////////////////////////////
template <class Period1, class Period2>
struct RatioGCD
{
static_assert(IsRatio<Period1>::value, "Period1 is not a eastl::ratio type");
static_assert(IsRatio<Period2>::value, "Period2 is not a eastl::ratio type");
typedef ratio<eastl::Internal::gcd<Period1::num, Period2::num>::value,
eastl::Internal::lcm<Period1::den, Period2::den>::value> type;
};
};
///////////////////////////////////////////////////////////////////////////////
// 20.12.5.7, duration_cast
///////////////////////////////////////////////////////////////////////////////
namespace Internal
{
template <typename FromDuration,
typename ToDuration,
typename CommonPeriod =
typename ratio_divide<typename FromDuration::period, typename ToDuration::period>::type,
typename CommonRep = typename eastl::decay<typename eastl::common_type<typename ToDuration::rep,
typename FromDuration::rep,
intmax_t>::type>::type,
bool = CommonPeriod::num == 1,
bool = CommonPeriod::den == 1>
struct DurationCastImpl;
template <typename FromDuration, typename ToDuration, typename CommonPeriod, typename CommonRep>
struct DurationCastImpl<FromDuration, ToDuration, CommonPeriod, CommonRep, true, true>
{
inline static ToDuration DoCast(const FromDuration& fd)
{
return ToDuration(static_cast<typename ToDuration::rep>(fd.count()));
}
};
template <typename FromDuration, typename ToDuration, typename CommonPeriod, typename CommonRep>
struct DurationCastImpl<FromDuration, ToDuration, CommonPeriod, CommonRep, false, true>
{
inline static ToDuration DoCast(const FromDuration& d)
{
return ToDuration(static_cast<typename ToDuration::rep>(static_cast<CommonRep>(d.count()) *
static_cast<CommonRep>(CommonPeriod::num)));
}
};
template <typename FromDuration, typename ToDuration, typename CommonPeriod, typename CommonRep>
struct DurationCastImpl<FromDuration, ToDuration, CommonPeriod, CommonRep, true, false>
{
inline static ToDuration DoCast(const FromDuration& d)
{
return ToDuration(static_cast<typename ToDuration::rep>(static_cast<CommonRep>(d.count()) /
static_cast<CommonRep>(CommonPeriod::den)));
}
};
template <typename FromDuration, typename ToDuration, typename CommonPeriod, typename CommonRep>
struct DurationCastImpl<FromDuration, ToDuration, CommonPeriod, CommonRep, false, false>
{
inline static ToDuration DoCast(const FromDuration& d)
{
return ToDuration(static_cast<typename ToDuration::rep>(static_cast<CommonRep>(d.count()) *
static_cast<CommonRep>(CommonPeriod::num) /
static_cast<CommonRep>(CommonPeriod::den)));
}
};
}; // namespace Internal
///////////////////////////////////////////////////////////////////////////////
// duration_cast
///////////////////////////////////////////////////////////////////////////////
template <typename ToDuration, typename Rep, typename Period>
inline typename eastl::enable_if<Internal::IsDuration<ToDuration>::value, ToDuration>::type
duration_cast(const duration<Rep, Period>& d)
{
typedef typename duration<Rep, Period>::this_type FromDuration;
return Internal::DurationCastImpl<FromDuration, ToDuration>::DoCast(d);
}
///////////////////////////////////////////////////////////////////////////////
// duration
///////////////////////////////////////////////////////////////////////////////
template <class Rep, class Period>
class duration
{
Rep mRep;
public:
typedef Rep rep;
typedef Period period;
typedef duration<Rep, Period> this_type;
#if defined(EA_COMPILER_NO_DEFAULTED_FUNCTIONS)
EA_CONSTEXPR duration()
: mRep() {}
duration(const duration& other)
: mRep(Rep(other.mRep)) {}
duration& operator=(const duration& other)
{ mRep = other.mRep; return *this; }
#else
EA_CONSTEXPR duration() = default;
duration(const duration&) = default;
duration& operator=(const duration&) = default;
#endif
///////////////////////////////////////////////////////////////////////////////
// conversion constructors
///////////////////////////////////////////////////////////////////////////////
template <class Rep2>
inline EA_CONSTEXPR explicit duration(
const Rep2& rep2,
typename eastl::enable_if<eastl::is_convertible<Rep2, Rep>::value &&
(treat_as_floating_point<Rep>::value ||
!treat_as_floating_point<Rep2>::value)>::type** = 0)
: mRep(static_cast<Rep>(rep2)) {}
template <class Rep2, class Period2>
EA_CONSTEXPR duration(const duration<Rep2, Period2>& d2,
typename eastl::enable_if<treat_as_floating_point<Rep>::value ||
(eastl::ratio_divide<Period2, Period>::type::den == 1 &&
!treat_as_floating_point<Rep2>::value),
void>::type** = 0)
: mRep(duration_cast<duration>(d2).count()) {}
///////////////////////////////////////////////////////////////////////////////
// returns the count of ticks
///////////////////////////////////////////////////////////////////////////////
EA_CONSTEXPR Rep count() const { return mRep; }
///////////////////////////////////////////////////////////////////////////////
// static accessors of special duration values
///////////////////////////////////////////////////////////////////////////////
EA_CONSTEXPR inline static duration zero() { return duration(duration_values<Rep>::zero()); }
EA_CONSTEXPR inline static duration min() { return duration(duration_values<Rep>::min()); }
EA_CONSTEXPR inline static duration max() { return duration(duration_values<Rep>::max()); }
///////////////////////////////////////////////////////////////////////////////
// const arithmetic operations
///////////////////////////////////////////////////////////////////////////////
EA_CONSTEXPR inline duration operator+() const { return *this; }
EA_CONSTEXPR inline duration operator-() const { return duration(0-mRep); }
///////////////////////////////////////////////////////////////////////////////
// arithmetic operations
///////////////////////////////////////////////////////////////////////////////
inline duration operator++(int) { return duration(mRep++); }
inline duration operator--(int) { return duration(mRep--); }
inline duration& operator++() { ++mRep; return *this; }
inline duration& operator--() { --mRep; return *this; }
inline duration& operator+=(const duration& d) { mRep += d.count(); return *this; }
inline duration& operator-=(const duration& d) { mRep -= d.count(); return *this; }
inline duration& operator*=(const Rep& rhs) { mRep *= rhs; return *this; }
inline duration& operator/=(const Rep& rhs) { mRep /= rhs; return *this; }
inline duration& operator%=(const Rep& rhs) { mRep %= rhs; return *this; }
inline duration& operator%=(const duration& d) { mRep %= d.count(); return *this; }
};
///////////////////////////////////////////////////////////////////////////////
// 20.12.5.5, arithmetic operations with durations as arguments
///////////////////////////////////////////////////////////////////////////////
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type EASTL_FORCE_INLINE
operator+(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs)
{
typedef typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type common_duration_t;
return common_duration_t(common_duration_t(lhs).count() + common_duration_t(rhs).count());
}
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type EASTL_FORCE_INLINE
operator-(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs)
{
typedef typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type common_duration_t;
return common_duration_t(common_duration_t(lhs).count() - common_duration_t(rhs).count());
}
template <typename Rep1, typename Period1, typename Rep2>
duration<typename eastl::common_type<Rep1, Rep2>::type, Period1> EASTL_FORCE_INLINE
operator*(const duration<Rep1, Period1>& lhs, const Rep2& rhs)
{
typedef duration<typename eastl::common_type<Rep1, Rep2>::type, Period1> common_duration_t;
return common_duration_t(common_duration_t(lhs).count() * rhs);
}
template <typename Rep1, typename Rep2, typename Period2>
duration<typename eastl::common_type<Rep1, Rep2>::type, Period2> EASTL_FORCE_INLINE
operator*(const Rep1& lhs, const duration<Rep2, Period2>& rhs)
{
typedef duration<typename eastl::common_type<Rep1, Rep2>::type, Period2> common_duration_t;
return common_duration_t(lhs * common_duration_t(rhs).count());
}
template <typename Rep1, typename Period1, typename Rep2>
duration<typename eastl::common_type<Rep1, Rep2>::type, Period1> EASTL_FORCE_INLINE
operator/(const duration<Rep1, Period1>& lhs, const Rep2& rhs)
{
typedef duration<typename eastl::common_type<Rep1, Rep2>::type, Period1> common_duration_t;
return common_duration_t(common_duration_t(lhs).count() / rhs);
}
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type EASTL_FORCE_INLINE
operator/(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs)
{
typedef typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type common_duration_t;
return common_duration_t(common_duration_t(lhs).count() / common_duration_t(rhs).count());
}
template <typename Rep1, typename Period1, typename Rep2>
duration<typename eastl::common_type<Rep1, Rep2>::type, Period1> EASTL_FORCE_INLINE
operator%(const duration<Rep1, Period1>& lhs, const Rep2& rhs)
{
typedef duration<typename eastl::common_type<Rep1, Rep2>::type, Period1> common_duration_t;
return common_duration_t(common_duration_t(lhs).count() % rhs);
}
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type EASTL_FORCE_INLINE
operator%(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs)
{
typedef typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type common_duration_t;
return common_duration_t(common_duration_t(lhs).count() % common_duration_t(rhs).count());
}
///////////////////////////////////////////////////////////////////////////////
// 20.12.5.6, compares two durations
///////////////////////////////////////////////////////////////////////////////
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
EASTL_FORCE_INLINE bool operator==(const duration<Rep1, Period1>& lhs,
const duration<Rep2, Period2>& rhs)
{
typedef typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type common_duration_t;
return common_duration_t(lhs).count() == common_duration_t(rhs).count();
}
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
EASTL_FORCE_INLINE bool operator<(const duration<Rep1, Period1>& lhs,
const duration<Rep2, Period2>& rhs)
{
typedef typename eastl::common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type common_duration_t;
return common_duration_t(lhs).count() < common_duration_t(rhs).count();
}
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
EASTL_FORCE_INLINE bool operator!=(const duration<Rep1, Period1>& lhs,
const duration<Rep2, Period2>& rhs)
{
return !(lhs == rhs);
}
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
EASTL_FORCE_INLINE bool operator<=(const duration<Rep1, Period1>& lhs,
const duration<Rep2, Period2>& rhs)
{
return !(rhs < lhs);
}
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
EASTL_FORCE_INLINE bool operator>(const duration<Rep1, Period1>& lhs,
const duration<Rep2, Period2>& rhs)
{
return rhs < lhs;
}
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
EASTL_FORCE_INLINE bool operator>=(const duration<Rep1, Period1>& lhs,
const duration<Rep2, Period2>& rhs)
{
return !(lhs < rhs);
}
///////////////////////////////////////////////////////////////////////////////
// standard duration units
///////////////////////////////////////////////////////////////////////////////
typedef duration<int64_t, nano> nanoseconds;
typedef duration<int64_t, micro> microseconds;
typedef duration<int64_t, milli> milliseconds;
typedef duration<int64_t> seconds;
typedef duration<int, ratio<60>> minutes;
typedef duration<int, ratio<3600>> hours;
///////////////////////////////////////////////////////////////////////////////
// 20.12.6, time_point
///////////////////////////////////////////////////////////////////////////////
template <typename Clock, typename Duration = typename Clock::duration>
class time_point
{
Duration mDuration;
public:
typedef Clock clock;
typedef Duration duration;
typedef typename Duration::rep rep;
typedef typename Duration::period period;
inline EA_CONSTEXPR time_point() : mDuration(Duration::zero()) {}
EA_CONSTEXPR explicit time_point(const Duration& other) : mDuration(other) {}
template <typename Duration2>
inline EA_CONSTEXPR time_point(
const time_point<Clock, Duration2>& t,
typename eastl::enable_if<eastl::is_convertible<Duration2, Duration>::value>::type** = 0)
: mDuration(t.time_since_epoch()) {}
EA_CONSTEXPR Duration time_since_epoch() const { return mDuration; }
time_point& operator+=(const Duration& d) { mDuration += d; return *this; }
time_point& operator-=(const Duration& d) { mDuration -= d; return *this; }
static EA_CONSTEXPR time_point min() { return time_point(Duration::min()); }
static EA_CONSTEXPR time_point max() { return time_point(Duration::max()); }
};
///////////////////////////////////////////////////////////////////////////////
// 20.12.6.5, time_point arithmetic
///////////////////////////////////////////////////////////////////////////////
template <class Clock, class Duration1, class Rep2, class Period2>
inline EA_CONSTEXPR time_point<Clock, typename eastl::common_type<Duration1, duration<Rep2, Period2>>::type>
operator+(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs)
{
typedef time_point<Clock, typename eastl::common_type<Duration1, duration<Rep2, Period2>>::type> common_timepoint_t;
return common_timepoint_t(lhs.time_since_epoch() + rhs);
}
template <class Rep1, class Period1, class Clock, class Duration2>
inline EA_CONSTEXPR time_point<Clock, typename eastl::common_type<Duration2, duration<Rep1, Period1>>::type>
operator+(const duration<Rep1, Period1>& lhs, const time_point<Clock, Duration2>& rhs)
{
typedef time_point<Clock, typename eastl::common_type<Duration2, duration<Rep1, Period1>>::type> common_timepoint_t;
return common_timepoint_t(lhs + rhs.time_since_epoch());
}
template <class Clock, class Duration1, class Rep2, class Period2>
inline EA_CONSTEXPR time_point<Clock, typename eastl::common_type<Duration1, duration<Rep2, Period2>>::type>
operator-(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs)
{
typedef time_point<Clock, typename eastl::common_type<Duration1, duration<Rep2, Period2>>::type> common_timepoint_t;
return common_timepoint_t(lhs.time_since_epoch() - rhs);
}
template <class Clock, class Duration1, class Duration2>
inline EA_CONSTEXPR typename eastl::common_type<Duration1, Duration2>::type operator-(
const time_point<Clock, Duration1>& lhs,
const time_point<Clock, Duration2>& rhs)
{
return lhs.time_since_epoch() - rhs.time_since_epoch();
}
template <class Clock, class Duration1, class Duration2>
inline EA_CONSTEXPR bool operator==(const time_point<Clock, Duration1>& lhs,
const time_point<Clock, Duration2>& rhs)
{
return lhs.time_since_epoch() == rhs.time_since_epoch();
}
template <class Clock, class Duration1, class Duration2>
inline EA_CONSTEXPR bool operator!=(const time_point<Clock, Duration1>& lhs,
const time_point<Clock, Duration2>& rhs)
{
return !(lhs == rhs);
}
template <class Clock, class Duration1, class Duration2>
inline EA_CONSTEXPR bool operator<(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs)
{
return lhs.time_since_epoch() < rhs.time_since_epoch();
}
template <class Clock, class Duration1, class Duration2>
inline EA_CONSTEXPR bool operator<=(const time_point<Clock, Duration1>& lhs,
const time_point<Clock, Duration2>& rhs)
{
return !(rhs < lhs);
}
template <class Clock, class Duration1, class Duration2>
inline EA_CONSTEXPR bool operator>(const time_point<Clock, Duration1>& lhs, const time_point<Clock, Duration2>& rhs)
{
return rhs < lhs;
}
template <class Clock, class Duration1, class Duration2>
inline EA_CONSTEXPR bool operator>=(const time_point<Clock, Duration1>& lhs,
const time_point<Clock, Duration2>& rhs)
{
return !(lhs < rhs);
}
///////////////////////////////////////////////////////////////////////////////
// 20.12.6.7, time_point_cast
///////////////////////////////////////////////////////////////////////////////
template <typename ToDuration, typename Clock, typename Duration>
EA_CONSTEXPR time_point<Clock, ToDuration> time_point_cast(
const time_point<Clock, Duration>& t,
typename eastl::enable_if<Internal::IsDuration<ToDuration>::value>::type** = 0)
{
return time_point<Clock, ToDuration>(duration_cast<ToDuration>(t.time_since_epoch()));
}
///////////////////////////////////////////////////////////////////////////////
// 20.12.7, clocks
///////////////////////////////////////////////////////////////////////////////
namespace Internal
{
#if defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_MINGW)
#define EASTL_NS_PER_TICK 1
#elif defined EA_PLATFORM_SONY
#define EASTL_NS_PER_TICK 1
#elif defined EA_PLATFORM_POSIX
#define EASTL_NS_PER_TICK _XTIME_NSECS_PER_TICK
#else
#define EASTL_NS_PER_TICK 100
#endif
#if defined(EA_PLATFORM_POSIX)
typedef chrono::nanoseconds::period SystemClock_Period;
typedef chrono::nanoseconds::period SteadyClock_Period;
#else
typedef eastl::ratio_multiply<eastl::ratio<EASTL_NS_PER_TICK, 1>, nano>::type SystemClock_Period;
typedef eastl::ratio_multiply<eastl::ratio<EASTL_NS_PER_TICK, 1>, nano>::type SteadyClock_Period;
#endif
///////////////////////////////////////////////////////////////////////////////
// Internal::GetTicks
///////////////////////////////////////////////////////////////////////////////
inline uint64_t GetTicks()
{
#if defined EA_PLATFORM_MICROSOFT
auto queryFrequency = []
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
return double(1000000000.0L / (long double)frequency.QuadPart); // nanoseconds per tick
};
auto queryCounter = []
{
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return counter.QuadPart;
};
EA_DISABLE_VC_WARNING(4640) // warning C4640: construction of local static object is not thread-safe (VS2013)
static auto frequency = queryFrequency(); // cache cpu frequency on first call
EA_RESTORE_VC_WARNING()
return uint64_t(frequency * (double)queryCounter());
#elif defined EA_PLATFORM_SONY
static_assert(false, "Implementing GetTicks() requires first party support");
return 0;
#elif defined(EA_PLATFORM_APPLE)
auto queryTimeInfo = []
{
mach_timebase_info_data_t info;
mach_timebase_info(&info);
return info;
};
static auto timeInfo = queryTimeInfo();
uint64_t t = mach_absolute_time();
t *= timeInfo.numer;
t /= timeInfo.denom;
return t;
#elif defined(EA_PLATFORM_POSIX) // Posix means Linux, Unix, and Macintosh OSX, among others (including Linux-based mobile platforms).
#if (defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC))
timespec ts;
int result = clock_gettime(CLOCK_MONOTONIC, &ts);
if (result == -1 && errno == EINVAL)
result = clock_gettime(CLOCK_REALTIME, &ts);
const uint64_t nNanoseconds = (uint64_t)ts.tv_nsec + ((uint64_t)ts.tv_sec * UINT64_C(1000000000));
return nNanoseconds;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
const uint64_t nMicroseconds = (uint64_t)tv.tv_usec + ((uint64_t)tv.tv_sec * 1000000);
return nMicroseconds;
#endif
#else
#error "chrono not implemented for platform"
#endif
}
} // namespace Internal
///////////////////////////////////////////////////////////////////////////////
// system_clock
///////////////////////////////////////////////////////////////////////////////
class system_clock
{
public:
typedef int64_t rep; // signed arithmetic type representing the number of ticks in the clock's duration
typedef Internal::SystemClock_Period period;
typedef chrono::duration<rep, period> duration; // duration<rep, period>, capable of representing negative durations
typedef chrono::time_point<system_clock> time_point;
// true if the time between ticks is always increases monotonically
EA_CONSTEXPR_OR_CONST static bool is_steady = false;
// returns a time point representing the current point in time.
static time_point now() EA_NOEXCEPT
{
return time_point(duration(Internal::GetTicks()));
}
};
///////////////////////////////////////////////////////////////////////////////
// steady_clock
///////////////////////////////////////////////////////////////////////////////
class steady_clock
{
public:
typedef int64_t rep; // signed arithmetic type representing the number of ticks in the clock's duration
typedef Internal::SteadyClock_Period period;
typedef chrono::duration<rep, period> duration; // duration<rep, period>, capable of representing negative durations
typedef chrono::time_point<steady_clock> time_point;
// true if the time between ticks is always increases monotonically
EA_CONSTEXPR_OR_CONST static bool is_steady = true;
// returns a time point representing the current point in time.
static time_point now() EA_NOEXCEPT
{
return time_point(duration(Internal::GetTicks()));
}
};
///////////////////////////////////////////////////////////////////////////////
// high_resolution_clock
///////////////////////////////////////////////////////////////////////////////
typedef system_clock high_resolution_clock;
} // namespace chrono
///////////////////////////////////////////////////////////////////////////////
// duration common_type specialization
///////////////////////////////////////////////////////////////////////////////
template <typename Rep1, typename Period1, typename Rep2, typename Period2>
struct common_type<chrono::duration<Rep1, Period1>, chrono::duration<Rep2, Period2>>
{
typedef chrono::duration<typename eastl::decay<typename eastl::common_type<Rep1, Rep2>::type>::type,
typename chrono::Internal::RatioGCD<Period1, Period2>::type> type;
};
///////////////////////////////////////////////////////////////////////////////
// time_point common_type specialization
///////////////////////////////////////////////////////////////////////////////
template <typename Clock, typename Duration1, typename Duration2>
struct common_type<chrono::time_point<Clock, Duration1>, chrono::time_point<Clock, Duration2>>
{
typedef chrono::time_point<Clock, typename eastl::common_type<Duration1, Duration2>::type> type;
};
///////////////////////////////////////////////////////////////////////////////
// chrono_literals
///////////////////////////////////////////////////////////////////////////////
#if EASTL_USER_LITERALS_ENABLED && EASTL_INLINE_NAMESPACES_ENABLED
// Disabling the Clang/GCC/MSVC warning about using user
// defined literals without a leading '_' as they are reserved
// for standard libary usage.
EA_DISABLE_VC_WARNING(4455)
EA_DISABLE_CLANG_WARNING(-Wuser-defined-literals)
EA_DISABLE_GCC_WARNING(-Wliteral-suffix)
inline namespace literals
{
inline namespace chrono_literals
{
///////////////////////////////////////////////////////////////////////////////
// integer chrono literals
///////////////////////////////////////////////////////////////////////////////
EA_CONSTEXPR chrono::hours operator"" h(unsigned long long h) { return chrono::hours(h); }
EA_CONSTEXPR chrono::minutes operator"" min(unsigned long long m) { return chrono::minutes(m); }
EA_CONSTEXPR chrono::seconds operator"" s(unsigned long long s) { return chrono::seconds(s); }
EA_CONSTEXPR chrono::milliseconds operator"" ms(unsigned long long ms) { return chrono::milliseconds(ms); }
EA_CONSTEXPR chrono::microseconds operator"" us(unsigned long long us) { return chrono::microseconds(us); }
EA_CONSTEXPR chrono::nanoseconds operator"" ns(unsigned long long ns) { return chrono::nanoseconds(ns); }
///////////////////////////////////////////////////////////////////////////////
// float chrono literals
///////////////////////////////////////////////////////////////////////////////
EA_CONSTEXPR chrono::duration<long double, ratio<3600, 1>> operator"" h(long double h)
{ return chrono::duration<long double, ratio<3600, 1>>(h); }
EA_CONSTEXPR chrono::duration<long double, ratio<60, 1>> operator"" min(long double m)
{ return chrono::duration<long double, ratio<60, 1>>(m); }
EA_CONSTEXPR chrono::duration<long double> operator"" s(long double s)
{ return chrono::duration<long double>(s); }
EA_CONSTEXPR chrono::duration<float, milli> operator"" ms(long double ms)
{ return chrono::duration<long double, milli>(ms); }
EA_CONSTEXPR chrono::duration<float, micro> operator"" us(long double us)
{ return chrono::duration<long double, micro>(us); }
EA_CONSTEXPR chrono::duration<float, nano> operator"" ns(long double ns)
{ return chrono::duration<long double, nano>(ns); }
} // namespace chrono_literals
}// namespace literals
EA_RESTORE_GCC_WARNING() // -Wliteral-suffix
EA_RESTORE_CLANG_WARNING() // -Wuser-defined-literals
EA_RESTORE_VC_WARNING() // warning: 4455
#endif
} // namespace eastl
#if EASTL_USER_LITERALS_ENABLED && EASTL_INLINE_NAMESPACES_ENABLED
namespace chrono
{
using namespace eastl::literals::chrono_literals;
} // namespace chrono
#endif
#endif
+45
View File
@@ -0,0 +1,45 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_COMPARE_H
#define EASTL_COMPARE_H
#include <EABase/eabase.h>
namespace eastl
{
#if defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
struct synth_three_way
{
template <typename T, typename U>
constexpr auto operator()(const T& t, const U& u) const requires requires
{
{t < u}->std::convertible_to<bool>;
{u < t}->std::convertible_to<bool>;
}
{
if constexpr (std::three_way_comparable_with<T, U>)
{
return t <=> u;
}
else
{
return (t < u) ? std::weak_ordering::less :
(u < t) ? std::weak_ordering::greater :
std::weak_ordering::equivalent;
}
}
};
template <typename T, typename U=T>
using synth_three_way_result = decltype(synth_three_way{}(declval<T&>(), declval<U&>()));
#endif
} // namespace eastl
#endif // Header include guard
+70
View File
@@ -0,0 +1,70 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_CORE_ALLOCATOR_H
#define EASTL_CORE_ALLOCATOR_H
#if EASTL_CORE_ALLOCATOR_ENABLED
#include <coreallocator/icoreallocator.h>
namespace EA
{
namespace Allocator
{
/// EASTLCoreAllocatorImpl
///
/// EASTL provides an out of the box implementation of the
/// ICoreAllocator interface. This is provided as a convenience for
/// users who wish to provide ICoreAllocator implementations for EASTL to use.
///
/// EASTL has a dependency on coreallocator so to provide an out of
/// the box implementation for EASTLCoreAlloctor and EASTLCoreDeleter
/// that can be used and tested. Historically we could not test
/// ICoreAllocator interface because we relied on the code being linked
/// in user code.
///
class EASTLCoreAllocatorImpl : public ICoreAllocator
{
public:
virtual void* Alloc(size_t size, const char* name, unsigned int flags)
{
return ::operator new[](size, name, flags, 0, __FILE__, __LINE__);
}
virtual void* Alloc(size_t size, const char* name, unsigned int flags, unsigned int alignment, unsigned int alignOffset = 0)
{
return ::operator new[](size, alignment, alignOffset, name, flags, 0, __FILE__, __LINE__);
}
virtual void Free(void* ptr, size_t size = 0)
{
::operator delete(static_cast<char*>(ptr));
}
virtual void* AllocDebug(size_t size, const DebugParams debugParams, unsigned int flags)
{
return Alloc(size, debugParams.mName, flags);
}
virtual void* AllocDebug(size_t size, const DebugParams debugParams, unsigned int flags, unsigned int align, unsigned int alignOffset = 0)
{
return Alloc(size, debugParams.mName, flags, align, alignOffset);
}
static EASTLCoreAllocatorImpl* GetDefaultAllocator();
};
inline EASTLCoreAllocatorImpl* EASTLCoreAllocatorImpl::GetDefaultAllocator()
{
static EASTLCoreAllocatorImpl allocator;
return &allocator;
}
}
}
#endif // EASTL_CORE_ALLOCATOR_ENABLED
#endif // EASTL_CORE_ALLOCATOR_H
+368
View File
@@ -0,0 +1,368 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Implements an EASTL allocator that uses an ICoreAllocator.
// However, this header file is not dependent on ICoreAllocator or its package.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_CORE_ALLOCATOR_ADAPTER_H
#define EASTL_CORE_ALLOCATOR_ADAPTER_H
#if EASTL_CORE_ALLOCATOR_ENABLED
#include <EASTL/internal/config.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
/// EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR
///
/// This allows the application to override the default name for the default global core allocator.
/// However, you must be careful in your usage of this, as if this file is shared between uses then
/// you will need to be careful that your override of this doesn't conflict with others.
///
#ifndef EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR
#define EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR AllocatorType::GetDefaultAllocator
#endif
namespace EA
{
namespace Allocator
{
/// CoreAllocatorAdapter
///
/// Implements the EASTL allocator interface.
/// Allocates memory from an instance of ICoreAllocator or another class with an equivalent interface.
/// ICoreAllocator is a pure-virtual memory allocation interface used by a number of EA games and
/// shared libraries. It's completely unrelated to EASTL, but it's prevalent enough that it's useful
/// for EASTL to have a built-in adapter for this interface. ICoreAllocator is declared in the
/// CoreAllocator package icoreallocator_interface.h header, but CoreAllocatorAdapter can work with
/// any equivalent interface, as defined below.
///
/// Expected interface:
/// enum AllocFlags {
/// kFlagTempMemory = 0,
/// kFlagPermMemory = 1
/// };
///
/// struct CoreAllocator {
/// void* Alloc(size_t size, const char* name, unsigned int allocFlags);
/// void* Alloc(size_t size, const char* name, unsigned int allocFlags, // Not required unless you are working with types that require custom alignment.
/// unsigned int align, unsigned int alignOffset = 0);
/// void Free(void* block, size_t size = 0);
/// static CoreAllocator* GetDefaultAllocator();
/// };
///
/// Example usage:
/// #include <coreallocator/icoreallocator_interface.h>
/// typedef EA::Allocator::CoreAllocatorAdapter<EASTLTestCoreAllocator> Adapter;
/// eastl::list<Widget, Adapter> widgetList(Adapter("UI/WidgetList", pSomeCoreAllocator));
/// widgetList.push_back(Widget());
///
/// Example usage:
/// #include <MyEquivalentCoreAllocatorInterface.h>
/// eastl::list<Widget, CoreAllocatorAdapter<MyCoreAllocatorInterface> > widgetList;
/// widgetList.push_back(Widget());
///
/// Example usage:
/// #include <coreallocator/icoreallocator_interface.h>
/// typedef EA::Allocator::CoreAllocatorAdapter<EASTLTestCoreAllocator> Adapter;
/// typedef eastl::list<Widget, Adapter> WidgetList;
/// CoreAllocatorFixed<WidgetList::node_type> widgetCoreAllocator(pFixedAllocatorForWidgetListValueType); // CoreAllocatorFixed is a hypothetical implementation of the ICoreAllocator interface.
/// WidgetList widgetList(Adapter("UI/WidgetList", &widgetCoreAllocator)); // Note that the widgetCoreAllocator is declared before and thus destroyed after the widget list.
///
template<class AllocatorType>
class CoreAllocatorAdapter
{
public:
typedef CoreAllocatorAdapter<AllocatorType> this_type;
public:
// To do: Make this constructor explicit, when there is no known code dependent on it being otherwise.
CoreAllocatorAdapter(const char* pName = EASTL_NAME_VAL(EASTL_ALLOCATOR_DEFAULT_NAME), AllocatorType* pAllocator = EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR());
CoreAllocatorAdapter(const char* pName, AllocatorType* pAllocator, int flags);
CoreAllocatorAdapter(const CoreAllocatorAdapter& x);
CoreAllocatorAdapter(const CoreAllocatorAdapter& x, const char* pName);
CoreAllocatorAdapter& operator=(const CoreAllocatorAdapter& x);
void* allocate(size_t n, int flags = 0);
void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0);
void deallocate(void* p, size_t n);
AllocatorType* get_allocator() const;
void set_allocator(AllocatorType* pAllocator);
int get_flags() const;
void set_flags(int flags);
const char* get_name() const;
void set_name(const char* pName);
public: // Public because otherwise VC++ generates (possibly invalid) warnings about inline friend template specializations.
AllocatorType* mpCoreAllocator;
int mnFlags; // Allocation flags. See ICoreAllocator/AllocFlags.
#if EASTL_NAME_ENABLED
const char* mpName; // Debug name, used to track memory.
#endif
};
template<class AllocatorType>
bool operator==(const CoreAllocatorAdapter<AllocatorType>& a, const CoreAllocatorAdapter<AllocatorType>& b);
template<class AllocatorType>
bool operator!=(const CoreAllocatorAdapter<AllocatorType>& a, const CoreAllocatorAdapter<AllocatorType>& b);
/// EASTLICoreAllocator
///
/// Provides a standardized typedef for ICoreAllocator;
///
/// Example usage:
/// eastl::list<Widget, EASTLICoreAllocator> widgetList("UI/WidgetList", pSomeCoreAllocator);
/// widgetList.push_back(Widget());
///
class ICoreAllocator;
class EASTLCoreAllocatorImpl;
typedef CoreAllocatorAdapter<ICoreAllocator> EASTLICoreAllocatorAdapter;
typedef CoreAllocatorAdapter<EASTLCoreAllocatorImpl> EASTLCoreAllocatorAdapter;
typedef EASTLICoreAllocatorAdapter EASTLICoreAllocator; // for backwards compatibility
/// EASTLICoreDeleter
///
/// Implements a functor which can free memory from the specified
/// ICoreAllocator interface. This is a convenience object provided for
/// users who wish to have EASTL containers deallocate memory obtained from
/// ICoreAllocator interfaces.
///
template <class AllocatorType>
class CoreDeleterAdapter
{
public:
typedef CoreDeleterAdapter<AllocatorType> this_type;
AllocatorType* mpCoreAllocator;
public:
CoreDeleterAdapter(AllocatorType* pAllocator = EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR()) EA_NOEXCEPT
: mpCoreAllocator(pAllocator) {}
~CoreDeleterAdapter() EA_NOEXCEPT {}
template <typename T>
void operator()(T* p)
{
p->~T();
mpCoreAllocator->Free(p);
}
CoreDeleterAdapter(const CoreDeleterAdapter& in) { mpCoreAllocator = in.mpCoreAllocator; }
CoreDeleterAdapter(CoreDeleterAdapter&& in)
{
mpCoreAllocator = in.mpCoreAllocator;
in.mpCoreAllocator = nullptr;
}
CoreDeleterAdapter& operator=(const CoreDeleterAdapter& in)
{
mpCoreAllocator = in.mpCoreAllocator;
return *this;
}
CoreDeleterAdapter& operator=(CoreDeleterAdapter&& in)
{
mpCoreAllocator = in.mpCoreAllocator;
in.mpCoreAllocator = nullptr;
return *this;
}
};
/// EASTLICoreDeleter
///
/// Provides a standardized typedef for ICoreAllocator implementations.
///
/// Example usage:
/// eastl::shared_ptr<A> foo(pA, EASTLCoreDeleter());
///
typedef CoreDeleterAdapter<ICoreAllocator> EASTLICoreDeleterAdapter;
typedef CoreDeleterAdapter<EASTLCoreAllocatorImpl> EASTLCoreDeleterAdapter;
} // namespace Allocator
} // namespace EA
///////////////////////////////////////////////////////////////////////////////
// Inlines
///////////////////////////////////////////////////////////////////////////////
namespace EA
{
namespace Allocator
{
template<class AllocatorType>
inline CoreAllocatorAdapter<AllocatorType>::CoreAllocatorAdapter(const char* EASTL_NAME(pName), AllocatorType* pCoreAllocator)
: mpCoreAllocator(pCoreAllocator), mnFlags(0)
{
#if EASTL_NAME_ENABLED
mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
#endif
}
template<class AllocatorType>
inline CoreAllocatorAdapter<AllocatorType>::CoreAllocatorAdapter(const char* EASTL_NAME(pName), AllocatorType* pCoreAllocator, int flags)
: mpCoreAllocator(pCoreAllocator), mnFlags(flags)
{
#if EASTL_NAME_ENABLED
mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
#endif
}
template<class AllocatorType>
inline CoreAllocatorAdapter<AllocatorType>::CoreAllocatorAdapter(const CoreAllocatorAdapter& x)
: mpCoreAllocator(x.mpCoreAllocator), mnFlags(x.mnFlags)
{
#if EASTL_NAME_ENABLED
mpName = x.mpName;
#endif
}
template<class AllocatorType>
inline CoreAllocatorAdapter<AllocatorType>::CoreAllocatorAdapter(const CoreAllocatorAdapter& x, const char* EASTL_NAME(pName))
: mpCoreAllocator(x.mpCoreAllocator), mnFlags(x.mnFlags)
{
#if EASTL_NAME_ENABLED
mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
#endif
}
template<class AllocatorType>
inline CoreAllocatorAdapter<AllocatorType>& CoreAllocatorAdapter<AllocatorType>::operator=(const CoreAllocatorAdapter& x)
{
mpCoreAllocator = x.mpCoreAllocator;
mnFlags = x.mnFlags;
#if EASTL_NAME_ENABLED
mpName = x.mpName;
#endif
return *this;
}
template<class AllocatorType>
inline void* CoreAllocatorAdapter<AllocatorType>::allocate(size_t n, int /*flags*/)
{
// It turns out that EASTL itself doesn't use the flags parameter,
// whereas the user here might well want to specify a flags
// parameter. So we use ours instead of the one passed in.
return mpCoreAllocator->Alloc(n, EASTL_NAME_VAL(mpName), (unsigned)mnFlags);
}
template<class AllocatorType>
inline void* CoreAllocatorAdapter<AllocatorType>::allocate(size_t n, size_t alignment, size_t offset, int /*flags*/)
{
// It turns out that EASTL itself doesn't use the flags parameter,
// whereas the user here might well want to specify a flags
// parameter. So we use ours instead of the one passed in.
return mpCoreAllocator->Alloc(n, EASTL_NAME_VAL(mpName), (unsigned)mnFlags, (unsigned)alignment, (unsigned)offset);
}
template<class AllocatorType>
inline void CoreAllocatorAdapter<AllocatorType>::deallocate(void* p, size_t n)
{
return mpCoreAllocator->Free(p, n);
}
template<class AllocatorType>
inline AllocatorType* CoreAllocatorAdapter<AllocatorType>::get_allocator() const
{
return mpCoreAllocator;
}
template<class AllocatorType>
inline void CoreAllocatorAdapter<AllocatorType>::set_allocator(AllocatorType* pAllocator)
{
mpCoreAllocator = pAllocator;
}
template<class AllocatorType>
inline int CoreAllocatorAdapter<AllocatorType>::get_flags() const
{
return mnFlags;
}
template<class AllocatorType>
inline void CoreAllocatorAdapter<AllocatorType>::set_flags(int flags)
{
mnFlags = flags;
}
template<class AllocatorType>
inline const char* CoreAllocatorAdapter<AllocatorType>::get_name() const
{
#if EASTL_NAME_ENABLED
return mpName;
#else
return EASTL_ALLOCATOR_DEFAULT_NAME;
#endif
}
template<class AllocatorType>
inline void CoreAllocatorAdapter<AllocatorType>::set_name(const char* pName)
{
#if EASTL_NAME_ENABLED
mpName = pName;
#else
(void)pName;
#endif
}
template<class AllocatorType>
inline bool operator==(const CoreAllocatorAdapter<AllocatorType>& a, const CoreAllocatorAdapter<AllocatorType>& b)
{
return (a.mpCoreAllocator == b.mpCoreAllocator) &&
(a.mnFlags == b.mnFlags);
}
template<class AllocatorType>
inline bool operator!=(const CoreAllocatorAdapter<AllocatorType>& a, const CoreAllocatorAdapter<AllocatorType>& b)
{
return (a.mpCoreAllocator != b.mpCoreAllocator) ||
(a.mnFlags != b.mnFlags);
}
} // namespace Allocator
} // namespace EA
#endif // EASTL_CORE_ALLOCATOR_ENABLED
#endif // Header include guard
File diff suppressed because it is too large Load Diff
+93
View File
@@ -0,0 +1,93 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// eastl::finally is an implementation of the popular cpp idiom RAII - Resource
// Acquisition Is Initialization. eastl::finally guarantees that the user
// provided callable will be executed upon whatever mechanism is used to leave
// the current scope. This can guard against user errors but this is a popular
// technique to write robust code in execution environments that have exceptions
// enabled.
//
// Example:
// void foo()
// {
// void* p = malloc(128);
// auto _ = eastl::make_finally([&] { free(p); });
//
// // Code that may throw an exception...
//
// } // eastl::finally guaranteed to call 'free' at scope exit.
//
// References:
// * https://www.bfilipek.com/2017/04/finalact.html
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FINALLY_H
#define EASTL_FINALLY_H
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once
#endif
#include <EASTL/internal/config.h>
#include <EASTL/internal/move_help.h>
#include <EASTL/type_traits.h>
namespace eastl
{
///////////////////////////////////////////////////////////////////////////
// finally
//
// finally is the type that calls the users callback on scope exit.
//
template <typename Functor>
class finally
{
static_assert(!eastl::is_lvalue_reference_v<Functor>, "eastl::finally requires the callable is passed as an rvalue reference.");
Functor m_functor;
bool m_engaged = false;
public:
finally(Functor f) : m_functor(eastl::move(f)), m_engaged(true) {}
finally(finally&& other) : m_functor(eastl::move(other.m_functor)), m_engaged(other.m_engaged)
{
other.dismiss();
}
~finally() { execute(); }
finally(const finally&) = delete;
finally& operator=(const finally&) = delete;
finally& operator=(finally&&) = delete;
inline void dismiss() { m_engaged = false; }
inline void execute()
{
if (m_engaged)
m_functor();
dismiss();
}
};
///////////////////////////////////////////////////////////////////////////
// make_finally
//
// this utility function is the standard mechansim to perform the required
// type deduction on the users provided callback inorder to create a
// 'finally' object.
//
template <typename F>
auto make_finally(F&& f)
{
return finally<F>(eastl::forward<F>(f));
}
}
#endif // EASTL_FINALLY_H
+455
View File
@@ -0,0 +1,455 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements the following
// fixed_allocator
// fixed_allocator_with_overflow
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_ALLOCATOR_H
#define EASTL_FIXED_ALLOCATOR_H
#include <EASTL/internal/config.h>
#include <EASTL/internal/fixed_pool.h>
#include <EASTL/functional.h>
#include <EASTL/memory.h>
#include <EASTL/allocator.h>
#include <EASTL/type_traits.h>
EA_DISABLE_ALL_VC_WARNINGS();
#include <new>
EA_RESTORE_ALL_VC_WARNINGS();
EA_DISABLE_VC_WARNING(4275); // non dll-interface class used as base for DLL-interface classkey 'identifier'
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
///////////////////////////////////////////////////////////////////////////
// fixed_allocator
///////////////////////////////////////////////////////////////////////////
/// fixed_allocator
///
/// Implements an allocator which allocates a single fixed size where
/// the size, alignment, and memory used for the pool is defined at
/// runtime by the user. This is different from fixed containers
/// such as fixed_list whereby the size and alignment are determined
/// at compile time and the memory is directly built into the container's
/// member data.
///
/// If the pool's memory is exhausted or was never initialized, the
/// allocate function returns NULL. Consider the fixed_allocator_with_overflow
/// class as an alternative in order to deal with this situation.
///
/// This class requires the user to call container.get_allocator().init()
/// after constructing the container. There currently isn't a way to
/// construct the container with the initialization parameters, though
/// with some effort such a thing could probably be made possible.
/// It's not as simple as it might first seem, due to the non-copyable
/// nature of fixed allocators. A side effect of this limitation is that
/// you cannot copy-construct a container using fixed_allocators.
///
/// Another side-effect is that you cannot swap two containers using
/// a fixed_allocator, as a swap requires temporary memory allocated by
/// an equivalent allocator, and such a thing cannot be done implicitly.
/// A workaround for the swap limitation is that you can implement your
/// own swap whereby you provide an explicitly created temporary object.
///
/// Note: Be careful to set the allocator's node size to the size of the
/// container node and not the size of the contained object. Note that the
/// example code below uses IntListNode.
///
/// Example usage:
/// typedef eastl::list<int, fixed_allocator> IntList;
/// typedef IntList::node_type IntListNode;
///
/// IntListNode buffer[200];
/// IntList intList;
/// intList.get_allocator().init(buffer, sizeof(buffer), sizeof(IntListNode), __alignof(IntListNode));
///
class EASTL_API fixed_allocator : public fixed_pool_base
{
public:
/// fixed_allocator
///
/// Default constructor. The user usually will need to call init() after
/// constructing via this constructor.
///
fixed_allocator(const char* /*pName*/ = EASTL_FIXED_POOL_DEFAULT_NAME)
: fixed_pool_base(NULL)
{
}
/// fixed_allocator
///
/// Copy constructor. The user usually will need to call init() after
/// constructing via this constructor. By their nature, fixed-allocators
/// cannot be copied in any useful way, as by their nature the user
/// must manually initialize them.
///
fixed_allocator(const fixed_allocator&)
: fixed_pool_base(NULL)
{
}
/// operator=
///
/// By their nature, fixed-allocators cannot be copied in any
/// useful way, as by their nature the user must manually
/// initialize them.
///
fixed_allocator& operator=(const fixed_allocator&)
{
return *this;
}
// init
//
// No init here, as the base class version is sufficient.
//
//void init(void* pMemory, size_t memorySize, size_t nodeSize,
// size_t alignment, size_t alignmentOffset = 0);
/// allocate
///
/// Allocates a new object of the size specified upon class initialization.
/// Returns NULL if there is no more memory.
///
void* allocate(size_t n, int /*flags*/ = 0)
{
// To consider: Verify that 'n' is what the user initialized us with.
Link* pLink = mpHead;
if(pLink) // If we have space...
{
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
if(++mnCurrentSize > mnPeakSize)
mnPeakSize = mnCurrentSize;
#endif
mpHead = pLink->mpNext;
return pLink;
}
else
{
// If there's no free node in the free list, just
// allocate another from the reserved memory area
if(mpNext != mpCapacity)
{
pLink = mpNext;
mpNext = reinterpret_cast<Link*>(reinterpret_cast<char*>(mpNext) + n);
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
if(++mnCurrentSize > mnPeakSize)
mnPeakSize = mnCurrentSize;
#endif
return pLink;
}
// EASTL_ASSERT(false); To consider: enable this assert. However, we intentionally disable it because this isn't necessarily an assertable error.
return NULL;
}
}
/// allocate
///
void* allocate(size_t n, size_t /*alignment*/, size_t /*offset*/, int flags = 0)
{
return allocate(n, flags);
}
/// deallocate
///
/// Frees the given object which was allocated by allocate().
/// If the given node was not allocated by allocate() then the behaviour
/// is undefined.
///
void deallocate(void* p, size_t)
{
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
--mnCurrentSize;
#endif
((Link*)p)->mpNext = mpHead;
mpHead = ((Link*)p);
}
using fixed_pool_base::can_allocate;
const char* get_name() const
{
return EASTL_FIXED_POOL_DEFAULT_NAME;
}
void set_name(const char*)
{
// Nothing to do. We don't allocate memory.
}
}; // fixed_allocator
bool operator==(const fixed_allocator& a, const fixed_allocator& b);
bool operator!=(const fixed_allocator& a, const fixed_allocator& b);
///////////////////////////////////////////////////////////////////////////
// fixed_allocator_with_overflow
///////////////////////////////////////////////////////////////////////////
/// fixed_allocator_with_overflow
///
/// Implements an allocator which allocates a single fixed size where
/// the size, alignment, and memory used for the pool is defined at
/// runtime by the user. This is different from fixed containers
/// such as fixed_list whereby the size and alignment are determined
/// at compile time and the memory is directly built into the container's
/// member data.
///
/// Note: Be careful to set the allocator's node size to the size of the
/// container node and not the size of the contained object. Note that the
/// example code below uses IntListNode.
///
/// This class requires the user to call container.get_allocator().init()
/// after constructing the container. There currently isn't a way to
/// construct the container with the initialization parameters, though
/// with some effort such a thing could probably be made possible.
/// It's not as simple as it might first seem, due to the non-copyable
/// nature of fixed allocators. A side effect of this limitation is that
/// you cannot copy-construct a container using fixed_allocators.
///
/// Another side-effect is that you cannot swap two containers using
/// a fixed_allocator, as a swap requires temporary memory allocated by
/// an equivalent allocator, and such a thing cannot be done implicitly.
/// A workaround for the swap limitation is that you can implement your
/// own swap whereby you provide an explicitly created temporary object.
///
/// Example usage:
/// typedef eastl::list<int, fixed_allocator_with_overflow> IntList;
/// typedef IntList::node_type IntListNode;
///
/// IntListNode buffer[200];
/// IntList intList;
/// intList.get_allocator().init(buffer, sizeof(buffer), sizeof(IntListNode), __alignof(IntListNode));
///
class EASTL_API fixed_allocator_with_overflow : public fixed_pool_base
{
public:
/// fixed_allocator_with_overflow
///
/// Default constructor. The user usually will need to call init() after
/// constructing via this constructor.
///
fixed_allocator_with_overflow(const char* pName = EASTL_FIXED_POOL_DEFAULT_NAME)
: fixed_pool_base(NULL)
, mOverflowAllocator(pName)
, mpPoolBegin(nullptr)
, mpPoolEnd(nullptr)
, mnNodeSize(0)
{
}
/// fixed_allocator_with_overflow
///
/// Copy constructor. The user usually will need to call init() after
/// constructing via this constructor. By their nature, fixed-allocators
/// cannot be copied in any useful way, as by their nature the user
/// must manually initialize them.
///
fixed_allocator_with_overflow(const fixed_allocator_with_overflow&)
: fixed_pool_base(NULL)
, mpPoolBegin(nullptr)
, mpPoolEnd(nullptr)
, mnNodeSize(0)
{
}
/// operator=
///
/// By their nature, fixed-allocators cannot be copied in any
/// useful way, as by their nature the user must manually
/// initialize them.
///
fixed_allocator_with_overflow& operator=(const fixed_allocator_with_overflow& x)
{
#if EASTL_ALLOCATOR_COPY_ENABLED
mOverflowAllocator = x.mOverflowAllocator;
#else
(void)x;
#endif
return *this;
}
/// init
///
void init(void* pMemory, size_t memorySize, size_t nodeSize,
size_t alignment, size_t alignmentOffset = 0)
{
fixed_pool_base::init(pMemory, memorySize, nodeSize, alignment, alignmentOffset);
mpPoolBegin = pMemory;
mpPoolEnd = (void*)((uintptr_t)pMemory + memorySize);
mnNodeSize = (eastl_size_t)nodeSize;
}
/// allocate
///
/// Allocates a new object of the size specified upon class initialization.
/// Returns NULL if there is no more memory.
///
void* allocate(size_t /*n*/, int /*flags*/ = 0)
{
// To consider: Verify that 'n' is what the user initialized us with.
void* p;
if(mpHead) // If we have space...
{
p = mpHead;
mpHead = mpHead->mpNext;
}
else
{
// If there's no free node in the free list, just
// allocate another from the reserved memory area
if (mpNext != mpCapacity)
{
p = mpNext;
mpNext = reinterpret_cast<Link*>(reinterpret_cast<char*>(mpNext) + mnNodeSize);
}
else
p = mOverflowAllocator.allocate(mnNodeSize);
}
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
if(p && (++mnCurrentSize > mnPeakSize))
mnPeakSize = mnCurrentSize;
#endif
return p;
}
/// allocate
///
void* allocate(size_t n, size_t /*alignment*/, size_t /*offset*/, int flags = 0)
{
return allocate(n, flags);
}
/// deallocate
///
/// Frees the given object which was allocated by allocate().
/// If the given node was not allocated by allocate() then the behaviour
/// is undefined.
///
void deallocate(void* p, size_t)
{
#if EASTL_FIXED_SIZE_TRACKING_ENABLED
--mnCurrentSize;
#endif
if((p >= mpPoolBegin) && (p < mpPoolEnd))
{
((Link*)p)->mpNext = mpHead;
mpHead = ((Link*)p);
}
else
mOverflowAllocator.deallocate(p, (size_t)mnNodeSize);
}
using fixed_pool_base::can_allocate;
const char* get_name() const
{
return mOverflowAllocator.get_name();
}
void set_name(const char* pName)
{
mOverflowAllocator.set_name(pName);
}
protected:
EASTLAllocatorType mOverflowAllocator; // To consider: Allow the user to define the type of this, presumably via a template parameter.
void* mpPoolBegin; // To consider: We have these member variables and ideally we shouldn't need them. The problem is that
void* mpPoolEnd; // the information about the pool buffer and object size is stored in the owning container
eastl_size_t mnNodeSize; // and we can't have access to it without increasing the amount of code we need and by templating
// more code. It may turn out that simply storing data here is smaller in the end.
}; // fixed_allocator_with_overflow // Granted, this class is usually used for debugging purposes, but perhaps there is an elegant solution.
bool operator==(const fixed_allocator_with_overflow& a, const fixed_allocator_with_overflow& b);
bool operator!=(const fixed_allocator_with_overflow& a, const fixed_allocator_with_overflow& b);
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
inline bool operator==(const fixed_allocator&, const fixed_allocator&)
{
return false;
}
inline bool operator!=(const fixed_allocator&, const fixed_allocator&)
{
return false;
}
inline bool operator==(const fixed_allocator_with_overflow&, const fixed_allocator_with_overflow&)
{
return false;
}
inline bool operator!=(const fixed_allocator_with_overflow&, const fixed_allocator_with_overflow&)
{
return false;
}
} // namespace eastl
EA_RESTORE_VC_WARNING();
#endif // Header include guard
+218
View File
@@ -0,0 +1,218 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_FUNCTION_H
#define EASTL_FIXED_FUNCTION_H
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once
#endif
#include <EASTL/internal/function_detail.h>
namespace eastl
{
template <int, typename>
class fixed_function;
namespace internal
{
template <typename>
struct is_fixed_function
: public eastl::false_type {};
template <int SIZE_IN_BYTES, typename R, typename... Args>
struct is_fixed_function<eastl::fixed_function<SIZE_IN_BYTES, R(Args...)>>
: public eastl::true_type {};
template<typename T>
EA_CONSTEXPR bool is_fixed_function_v = is_fixed_function<T>::value;
}
#define EASTL_INTERNAL_FIXED_FUNCTION_STATIC_ASSERT(TYPE) \
static_assert(sizeof(TYPE) <= sizeof(typename Base::FunctorStorageType), \
"fixed_function local buffer is not large enough to hold the callable object.")
#define EASTL_INTERNAL_FIXED_FUNCTION_NEW_SIZE_STATIC_ASSERT(NEW_SIZE_IN_BYTES) \
static_assert(SIZE_IN_BYTES >= NEW_SIZE_IN_BYTES, \
"fixed_function local buffer is not large enough to hold the new fixed_function type.")
template <typename Functor>
using EASTL_DISABLE_OVERLOAD_IF_FIXED_FUNCTION =
eastl::disable_if_t<internal::is_fixed_function_v<eastl::decay_t<Functor>>>;
// fixed_function
//
template <int SIZE_IN_BYTES, typename R, typename... Args>
class fixed_function<SIZE_IN_BYTES, R(Args...)> : public internal::function_detail<SIZE_IN_BYTES, R(Args...)>
{
using Base = internal::function_detail<SIZE_IN_BYTES, R(Args...)>;
public:
using typename Base::result_type;
fixed_function() EA_NOEXCEPT = default;
fixed_function(std::nullptr_t p) EA_NOEXCEPT
: Base(p)
{
}
fixed_function(const fixed_function& other)
: Base(other)
{
}
fixed_function(fixed_function&& other)
: Base(eastl::move(other))
{
}
template <typename Functor,
typename = EASTL_INTERNAL_FUNCTION_VALID_FUNCTION_ARGS(Functor, R, Args..., Base, fixed_function),
typename = EASTL_DISABLE_OVERLOAD_IF_FIXED_FUNCTION<Functor>>
fixed_function(Functor functor)
: Base(eastl::move(functor))
{
EASTL_INTERNAL_FIXED_FUNCTION_STATIC_ASSERT(Functor);
}
template<int NEW_SIZE_IN_BYTES>
fixed_function(const fixed_function<NEW_SIZE_IN_BYTES, R(Args...)>& other)
: Base(other)
{
EASTL_INTERNAL_FIXED_FUNCTION_NEW_SIZE_STATIC_ASSERT(NEW_SIZE_IN_BYTES);
}
template<int NEW_SIZE_IN_BYTES>
fixed_function(fixed_function<NEW_SIZE_IN_BYTES, R(Args...)>&& other)
: Base(eastl::move(other))
{
EASTL_INTERNAL_FIXED_FUNCTION_NEW_SIZE_STATIC_ASSERT(NEW_SIZE_IN_BYTES);
}
~fixed_function() EA_NOEXCEPT = default;
fixed_function& operator=(const fixed_function& other)
{
Base::operator=(other);
return *this;
}
fixed_function& operator=(fixed_function&& other)
{
Base::operator=(eastl::move(other));
return *this;
}
fixed_function& operator=(std::nullptr_t p) EA_NOEXCEPT
{
Base::operator=(p);
return *this;
}
template<int NEW_SIZE_IN_BYTES>
fixed_function& operator=(const fixed_function<NEW_SIZE_IN_BYTES, R(Args...)>& other)
{
EASTL_INTERNAL_FIXED_FUNCTION_NEW_SIZE_STATIC_ASSERT(NEW_SIZE_IN_BYTES);
Base::operator=(other);
return *this;
}
template<int NEW_SIZE_IN_BYTES>
fixed_function& operator=(fixed_function<NEW_SIZE_IN_BYTES, R(Args...)>&& other)
{
EASTL_INTERNAL_FIXED_FUNCTION_NEW_SIZE_STATIC_ASSERT(NEW_SIZE_IN_BYTES);
Base::operator=(eastl::move(other));
return *this;
}
template <typename Functor,
typename = EASTL_INTERNAL_FUNCTION_VALID_FUNCTION_ARGS(Functor, R, Args..., Base, fixed_function),
typename = EASTL_DISABLE_OVERLOAD_IF_FIXED_FUNCTION<Functor>>
fixed_function& operator=(Functor&& functor)
{
EASTL_INTERNAL_FIXED_FUNCTION_STATIC_ASSERT(eastl::decay_t<Functor>);
Base::operator=(eastl::forward<Functor>(functor));
return *this;
}
template <typename Functor>
fixed_function& operator=(eastl::reference_wrapper<Functor> f) EA_NOEXCEPT
{
EASTL_INTERNAL_FIXED_FUNCTION_STATIC_ASSERT(eastl::reference_wrapper<Functor>);
Base::operator=(f);
return *this;
}
void swap(fixed_function& other) EA_NOEXCEPT
{
Base::swap(other);
}
explicit operator bool() const EA_NOEXCEPT
{
return Base::operator bool();
}
R operator ()(Args... args) const
{
return Base::operator ()(eastl::forward<Args>(args)...);
}
#if EASTL_RTTI_ENABLED
const std::type_info& target_type() const EA_NOEXCEPT
{
return Base::target_type();
}
template <typename Functor>
Functor* target() EA_NOEXCEPT
{
return Base::target();
}
template <typename Functor>
const Functor* target() const EA_NOEXCEPT
{
return Base::target();
}
#endif
};
template <int S, typename R, typename... Args>
bool operator==(const fixed_function<S, R(Args...)>& f, std::nullptr_t) EA_NOEXCEPT
{
return !f;
}
template <int S, typename R, typename... Args>
bool operator==(std::nullptr_t, const fixed_function<S, R(Args...)>& f) EA_NOEXCEPT
{
return !f;
}
template <int S, typename R, typename... Args>
bool operator!=(const fixed_function<S, R(Args...)>& f, std::nullptr_t) EA_NOEXCEPT
{
return !!f;
}
template <int S, typename R, typename... Args>
bool operator!=(std::nullptr_t, const fixed_function<S, R(Args...)>& f) EA_NOEXCEPT
{
return !!f;
}
template <int S, typename R, typename... Args>
void swap(fixed_function<S, R(Args...)>& lhs, fixed_function<S, R(Args...)>& rhs)
{
lhs.swap(rhs);
}
} // namespace eastl
#endif // EASTL_FIXED_FUNCTION_H
+828
View File
@@ -0,0 +1,828 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements a hash_map and hash_multimap which use a fixed size
// memory pool for its buckets and nodes.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_HASH_MAP_H
#define EASTL_FIXED_HASH_MAP_H
#include <EASTL/hash_map.h>
#include <EASTL/internal/fixed_pool.h>
EA_DISABLE_VC_WARNING(4127) // Conditional expression is constant
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_FIXED_HASH_MAP_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
/// In the case of fixed-size containers, the allocator name always refers
/// to overflow allocations.
///
#ifndef EASTL_FIXED_HASH_MAP_DEFAULT_NAME
#define EASTL_FIXED_HASH_MAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_hash_map" // Unless the user overrides something, this is "EASTL fixed_hash_map".
#endif
#ifndef EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME
#define EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_hash_multimap" // Unless the user overrides something, this is "EASTL fixed_hash_multimap".
#endif
/// EASTL_FIXED_HASH_MAP_DEFAULT_ALLOCATOR
/// EASTL_FIXED_HASH_MULTIMAP_DEFAULT_ALLOCATOR
///
#ifndef EASTL_FIXED_HASH_MAP_DEFAULT_ALLOCATOR
#define EASTL_FIXED_HASH_MAP_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_HASH_MAP_DEFAULT_NAME)
#endif
#ifndef EASTL_FIXED_HASH_MULTIMAP_DEFAULT_ALLOCATOR
#define EASTL_FIXED_HASH_MULTIMAP_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME)
#endif
/// fixed_hash_map
///
/// Implements a hash_map with a fixed block of memory identified by the nodeCount and bucketCount
/// template parameters.
///
/// Template parameters:
/// Key The key type for the map. This is a map of Key to T (value).
/// T The value type for the map.
/// nodeCount The max number of objects to contain. This value must be >= 1.
/// bucketCount The number of buckets to use. This value must be >= 2.
/// bEnableOverflow Whether or not we should use the global heap if our object pool is exhausted.
/// Hash hash_set hash function. See hash_set.
/// Predicate hash_set equality testing function. See hash_set.
///
template <typename Key, typename T, size_t nodeCount, size_t bucketCount = nodeCount + 1, bool bEnableOverflow = true,
typename Hash = eastl::hash<Key>, typename Predicate = eastl::equal_to<Key>, bool bCacheHashCode = false, typename OverflowAllocator = EASTLAllocatorType>
class fixed_hash_map : public hash_map<Key,
T,
Hash,
Predicate,
fixed_hashtable_allocator<
bucketCount + 1,
sizeof(typename hash_map<Key, T, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type),
nodeCount,
EASTL_ALIGN_OF(eastl::pair<Key, T>),
0,
bEnableOverflow,
OverflowAllocator>,
bCacheHashCode>
{
public:
typedef fixed_hashtable_allocator<bucketCount + 1, sizeof(typename hash_map<Key, T, Hash, Predicate,
OverflowAllocator, bCacheHashCode>::node_type), nodeCount, EASTL_ALIGN_OF(eastl::pair<Key, T>), 0,
bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type;
typedef hash_map<Key, T, Hash, Predicate, fixed_allocator_type, bCacheHashCode> base_type;
typedef fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator> this_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::size_type size_type;
enum { kMaxSize = nodeCount };
using base_type::mAllocator;
using base_type::clear;
protected:
node_type** mBucketBuffer[bucketCount + 1]; // '+1' because the hash table needs a null terminating bucket.
char mNodeBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
public:
explicit fixed_hash_map(const overflow_allocator_type& overflowAllocator);
explicit fixed_hash_map(const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate());
fixed_hash_map(const Hash& hashFunction,
const Predicate& predicate,
const overflow_allocator_type& overflowAllocator);
template <typename InputIterator>
fixed_hash_map(InputIterator first, InputIterator last,
const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate());
fixed_hash_map(const this_type& x);
fixed_hash_map(this_type&& x);
fixed_hash_map(this_type&& x, const overflow_allocator_type& overflowAllocator);
fixed_hash_map(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_MAP_DEFAULT_ALLOCATOR);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const;
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
void clear(bool clearBuckets);
}; // fixed_hash_map
/// fixed_hash_multimap
///
/// Implements a hash_multimap with a fixed block of memory identified by the nodeCount and bucketCount
/// template parameters.
///
/// Template parameters:
/// Key The key type for the map. This is a map of Key to T (value).
/// T The value type for the map.
/// nodeCount The max number of objects to contain. This value must be >= 1.
/// bucketCount The number of buckets to use. This value must be >= 2.
/// bEnableOverflow Whether or not we should use the global heap if our object pool is exhausted.
/// Hash hash_set hash function. See hash_set.
/// Predicate hash_set equality testing function. See hash_set.
///
template <typename Key, typename T, size_t nodeCount, size_t bucketCount = nodeCount + 1, bool bEnableOverflow = true,
typename Hash = eastl::hash<Key>, typename Predicate = eastl::equal_to<Key>, bool bCacheHashCode = false, typename OverflowAllocator = EASTLAllocatorType>
class fixed_hash_multimap : public hash_multimap<Key,
T,
Hash,
Predicate,
fixed_hashtable_allocator<
bucketCount + 1,
sizeof(typename hash_multimap<Key, T, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type),
nodeCount,
EASTL_ALIGN_OF(eastl::pair<Key, T>),
0,
bEnableOverflow,
OverflowAllocator>,
bCacheHashCode>
{
public:
typedef fixed_hashtable_allocator<bucketCount + 1, sizeof(typename hash_multimap<Key, T, Hash, Predicate,
OverflowAllocator, bCacheHashCode>::node_type), nodeCount, EASTL_ALIGN_OF(eastl::pair<Key, T>), 0,
bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type;
typedef hash_multimap<Key, T, Hash, Predicate, fixed_allocator_type, bCacheHashCode> base_type;
typedef fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator> this_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::size_type size_type;
enum { kMaxSize = nodeCount };
using base_type::mAllocator;
using base_type::clear;
protected:
node_type** mBucketBuffer[bucketCount + 1]; // '+1' because the hash table needs a null terminating bucket.
char mNodeBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
public:
explicit fixed_hash_multimap(const overflow_allocator_type& overflowAllocator);
explicit fixed_hash_multimap(const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate());
fixed_hash_multimap(const Hash& hashFunction,
const Predicate& predicate,
const overflow_allocator_type& overflowAllocator);
template <typename InputIterator>
fixed_hash_multimap(InputIterator first, InputIterator last,
const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate());
fixed_hash_multimap(const this_type& x);
fixed_hash_multimap(this_type&& x);
fixed_hash_multimap(this_type&& x, const overflow_allocator_type& overflowAllocator);
fixed_hash_multimap(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_MULTIMAP_DEFAULT_ALLOCATOR);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const;
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
void clear(bool clearBuckets);
}; // fixed_hash_multimap
///////////////////////////////////////////////////////////////////////
// fixed_hash_map
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_map(const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), Hash(),
Predicate(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_map(const Hash& hashFunction,
const Predicate& predicate)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if (!bEnableOverflow)
{
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
}
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_map(const Hash& hashFunction,
const Predicate& predicate,
const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if (!bEnableOverflow)
{
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
}
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
template <typename InputIterator>
fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_map(InputIterator first, InputIterator last,
const Hash& hashFunction,
const Predicate& predicate)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
base_type::insert(first, last);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_map(const this_type& x)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_map(this_type&& x)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer))
{
// This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here.
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_map(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
// This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here.
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_map(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), Hash(),
Predicate(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
base_type::insert(ilist.begin(), ilist.end());
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(const this_type& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(this_type&& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
base_type::insert(ilist.begin(), ilist.end());
return *this;
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
reset_lose_memory()
{
base_type::mnBucketCount = (size_type)base_type::mRehashPolicy.GetPrevBucketCount((uint32_t)bucketCount);
base_type::mnElementCount = 0;
base_type::mRehashPolicy.mnNextResize = 0;
base_type::get_allocator().reset(mNodeBuffer);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::size_type
fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline const typename fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::overflow_allocator_type&
fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::overflow_allocator_type&
fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
set_overflow_allocator(const overflow_allocator_type& allocator)
{
mAllocator.set_overflow_allocator(allocator);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
clear(bool clearBuckets)
{
base_type::DoFreeNodes(base_type::mpBucketArray, base_type::mnBucketCount);
if(clearBuckets)
{
base_type::DoFreeBuckets(base_type::mpBucketArray, base_type::mnBucketCount);
reset_lose_memory();
}
base_type::mpBucketArray = (node_type**)mBucketBuffer;
base_type::mnElementCount = 0;
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode>
inline void swap(fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode>& a,
fixed_hash_map<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
///////////////////////////////////////////////////////////////////////
// fixed_hash_multimap
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multimap(const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), Hash(),
Predicate(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if (!bEnableOverflow)
{
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
}
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multimap(const Hash& hashFunction,
const Predicate& predicate)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multimap(const Hash& hashFunction,
const Predicate& predicate,
const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
template <typename InputIterator>
fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multimap(InputIterator first, InputIterator last,
const Hash& hashFunction,
const Predicate& predicate)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
base_type::insert(first, last);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multimap(const this_type& x)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(),fixed_allocator_type(NULL, mBucketBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multimap(this_type&& x)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(),fixed_allocator_type(NULL, mBucketBuffer))
{
// This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here.
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multimap(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
// This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here.
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multimap(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), Hash(),
Predicate(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTIMAP_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
base_type::insert(ilist.begin(), ilist.end());
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(const this_type& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(this_type&& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
base_type::insert(ilist.begin(), ilist.end());
return *this;
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
reset_lose_memory()
{
base_type::mnBucketCount = (size_type)base_type::mRehashPolicy.GetPrevBucketCount((uint32_t)bucketCount);
base_type::mnElementCount = 0;
base_type::mRehashPolicy.mnNextResize = 0;
base_type::get_allocator().reset(mNodeBuffer);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::size_type
fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline const typename fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::overflow_allocator_type&
fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::overflow_allocator_type&
fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::set_overflow_allocator(const overflow_allocator_type& allocator)
{
mAllocator.set_overflow_allocator(allocator);
}
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
clear(bool clearBuckets)
{
base_type::DoFreeNodes(base_type::mpBucketArray, base_type::mnBucketCount);
if(clearBuckets)
{
base_type::DoFreeBuckets(base_type::mpBucketArray, base_type::mnBucketCount);
reset_lose_memory();
}
base_type::mpBucketArray = (node_type**)mBucketBuffer;
base_type::mnElementCount = 0;
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode>
inline void swap(fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode>& a,
fixed_hash_multimap<Key, T, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
} // namespace eastl
EA_RESTORE_VC_WARNING()
#endif // Header include guard
+790
View File
@@ -0,0 +1,790 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements a hash_set which uses a fixed size memory pool for
// its buckets and nodes.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_HASH_SET_H
#define EASTL_FIXED_HASH_SET_H
#include <EASTL/hash_set.h>
#include <EASTL/internal/fixed_pool.h>
EA_DISABLE_VC_WARNING(4127) // Conditional expression is constant
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_FIXED_HASH_SET_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
/// In the case of fixed-size containers, the allocator name always refers
/// to overflow allocations.
///
#ifndef EASTL_FIXED_HASH_SET_DEFAULT_NAME
#define EASTL_FIXED_HASH_SET_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_hash_set" // Unless the user overrides something, this is "EASTL fixed_hash_set".
#endif
#ifndef EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME
#define EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_hash_multiset" // Unless the user overrides something, this is "EASTL fixed_hash_multiset".
#endif
/// EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR
/// EASTL_FIXED_HASH_MULTISET_DEFAULT_ALLOCATOR
///
#ifndef EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR
#define EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_HASH_SET_DEFAULT_NAME)
#endif
#ifndef EASTL_FIXED_HASH_MULTISET_DEFAULT_ALLOCATOR
#define EASTL_FIXED_HASH_MULTISET_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME)
#endif
/// fixed_hash_set
///
/// Implements a hash_set with a fixed block of memory identified by the nodeCount and bucketCount
/// template parameters.
///
/// Template parameters:
/// Value The type of object the hash_set holds.
/// nodeCount The max number of objects to contain. This value must be >= 1.
/// bucketCount The number of buckets to use. This value must be >= 2.
/// bEnableOverflow Whether or not we should use the global heap if our object pool is exhausted.
/// Hash hash_set hash function. See hash_set.
/// Predicate hash_set equality testing function. See hash_set.
///
template <typename Value, size_t nodeCount, size_t bucketCount = nodeCount + 1, bool bEnableOverflow = true,
typename Hash = eastl::hash<Value>, typename Predicate = eastl::equal_to<Value>, bool bCacheHashCode = false, typename OverflowAllocator = EASTLAllocatorType>
class fixed_hash_set : public hash_set<Value,
Hash,
Predicate,
fixed_hashtable_allocator<
bucketCount + 1,
sizeof(typename hash_set<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type),
nodeCount,
EASTL_ALIGN_OF(typename hash_set<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type),
0,
bEnableOverflow,
OverflowAllocator>,
bCacheHashCode>
{
public:
typedef fixed_hashtable_allocator<bucketCount + 1, sizeof(typename hash_set<Value, Hash, Predicate,
OverflowAllocator, bCacheHashCode>::node_type), nodeCount,
EASTL_ALIGN_OF(typename hash_set<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type),
0, bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type;
typedef fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator> this_type;
typedef hash_set<Value, Hash, Predicate, fixed_allocator_type, bCacheHashCode> base_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::size_type size_type;
enum { kMaxSize = nodeCount };
using base_type::mAllocator;
protected:
node_type** mBucketBuffer[bucketCount + 1]; // '+1' because the hash table needs a null terminating bucket.
char mNodeBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
public:
explicit fixed_hash_set(const overflow_allocator_type& overflowAllocator);
explicit fixed_hash_set(const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate());
fixed_hash_set(const Hash& hashFunction,
const Predicate& predicate,
const overflow_allocator_type& overflowAllocator);
template <typename InputIterator>
fixed_hash_set(InputIterator first, InputIterator last,
const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate());
fixed_hash_set(const this_type& x);
fixed_hash_set(this_type&& x);
fixed_hash_set(this_type&& x, const overflow_allocator_type& overflowAllocator);
fixed_hash_set(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_SET_DEFAULT_ALLOCATOR);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const;
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
}; // fixed_hash_set
/// fixed_hash_multiset
///
/// Implements a hash_multiset with a fixed block of memory identified by the nodeCount and bucketCount
/// template parameters.
///
/// Value The type of object the hash_set holds.
/// nodeCount The max number of objects to contain. This value must be >= 1.
/// bucketCount The number of buckets to use. This value must be >= 2.
/// bEnableOverflow Whether or not we should use the global heap if our object pool is exhausted.
/// Hash hash_set hash function. See hash_set.
/// Predicate hash_set equality testing function. See hash_set.
///
template <typename Value, size_t nodeCount, size_t bucketCount = nodeCount + 1, bool bEnableOverflow = true,
typename Hash = eastl::hash<Value>, typename Predicate = eastl::equal_to<Value>, bool bCacheHashCode = false, typename OverflowAllocator = EASTLAllocatorType>
class fixed_hash_multiset : public hash_multiset<Value,
Hash,
Predicate,
fixed_hashtable_allocator<
bucketCount + 1,
sizeof(typename hash_multiset<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type),
nodeCount,
EASTL_ALIGN_OF(typename hash_multiset<Value, Hash, Predicate, OverflowAllocator, bCacheHashCode>::node_type),
0,
bEnableOverflow,
OverflowAllocator>,
bCacheHashCode>
{
public:
typedef fixed_hashtable_allocator<bucketCount + 1, sizeof(typename hash_multiset<Value, Hash, Predicate,
OverflowAllocator, bCacheHashCode>::node_type), nodeCount, EASTL_ALIGN_OF(typename hash_multiset<Value, Hash, Predicate,
OverflowAllocator, bCacheHashCode>::node_type), 0,
bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type;
typedef hash_multiset<Value, Hash, Predicate, fixed_allocator_type, bCacheHashCode> base_type;
typedef fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator> this_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::size_type size_type;
enum { kMaxSize = nodeCount };
using base_type::mAllocator;
protected:
node_type** mBucketBuffer[bucketCount + 1]; // '+1' because the hash table needs a null terminating bucket.
char mNodeBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
public:
explicit fixed_hash_multiset(const overflow_allocator_type& overflowAllocator);
explicit fixed_hash_multiset(const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate());
fixed_hash_multiset(const Hash& hashFunction,
const Predicate& predicate,
const overflow_allocator_type& overflowAllocator);
template <typename InputIterator>
fixed_hash_multiset(InputIterator first, InputIterator last,
const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate());
fixed_hash_multiset(const this_type& x);
fixed_hash_multiset(this_type&& x);
fixed_hash_multiset(this_type&& x, const overflow_allocator_type& overflowAllocator);
fixed_hash_multiset(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_HASH_MULTISET_DEFAULT_ALLOCATOR);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const;
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
}; // fixed_hash_multiset
///////////////////////////////////////////////////////////////////////
// fixed_hash_set
///////////////////////////////////////////////////////////////////////
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_set(const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount),
Hash(), Predicate(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if (!bEnableOverflow)
{
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
}
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_set(const Hash& hashFunction,
const Predicate& predicate)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount),
hashFunction, predicate, fixed_allocator_type(NULL, mBucketBuffer))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_set(const Hash& hashFunction,
const Predicate& predicate,
const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount),
hashFunction, predicate, fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if (!bEnableOverflow)
{
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
}
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
template <typename InputIterator>
fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_set(InputIterator first, InputIterator last,
const Hash& hashFunction,
const Predicate& predicate)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
{
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
}
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
base_type::insert(first, last);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_set(const this_type& x)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_set<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::fixed_hash_set(this_type&& x)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer))
{
// This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here.
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_set<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::fixed_hash_set(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount),
x.hash_function(), x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
// This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here.
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_set<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_set(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), Hash(),
Predicate(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_SET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
base_type::insert(ilist.begin(), ilist.end());
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
typename fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(const this_type& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_set<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_set<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(this_type&& x)
{
operator=(x);
return *this;
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_set<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_set<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
base_type::insert(ilist.begin(), ilist.end());
return *this;
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
swap(this_type& x)
{
// We must do a brute-force swap, because fixed containers cannot share memory allocations.
// Note that we create a temp value on the stack. This approach may fail if the size of the
// container is too large. We have a rule against allocating memory from the heap, and so
// if the user wants to swap two large objects of this class, the user will currently need
// to implement it manually. To consider: add code to allocate a temporary buffer if the
// size of the container is too large for the stack.
EASTL_ASSERT(sizeof(x) < EASTL_MAX_STACK_USAGE); // It is dangerous to try to create objects that are too big for the stack.
const this_type temp(*this); // Can't call eastl::swap because that would
*this = x; // itself call this member swap function.
x = temp;
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
void fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
reset_lose_memory()
{
base_type::reset_lose_memory();
base_type::get_allocator().reset(mNodeBuffer);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::size_type
fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline const typename fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::overflow_allocator_type&
fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::overflow_allocator_type&
fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
set_overflow_allocator(const overflow_allocator_type& allocator)
{
mAllocator.set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode>
inline void swap(fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode>& a,
fixed_hash_set<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode>& b)
{
a.swap(b);
}
///////////////////////////////////////////////////////////////////////
// fixed_hash_multiset
///////////////////////////////////////////////////////////////////////
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multiset(const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), Hash(),
Predicate(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multiset(const Hash& hashFunction,
const Predicate& predicate)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multiset(const Hash& hashFunction,
const Predicate& predicate,
const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
template <typename InputIterator>
inline fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multiset(InputIterator first, InputIterator last,
const Hash& hashFunction,
const Predicate& predicate)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), hashFunction,
predicate, fixed_allocator_type(NULL, mBucketBuffer))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
base_type::insert(first, last);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multiset(const this_type& x)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multiset<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::fixed_hash_multiset(this_type&& x)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), x.hash_function(),
x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer))
{
// This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here.
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multiset<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::fixed_hash_multiset(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount),
x.hash_function(), x.equal_function(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
// This implementation is the same as above. If we could rely on using C++11 delegating constructor support then we could just call that here.
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
mAllocator.reset(mNodeBuffer);
base_type::insert(x.begin(), x.end());
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline fixed_hash_multiset<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
fixed_hash_multiset(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(prime_rehash_policy::GetPrevBucketCountOnly(bucketCount), Hash(),
Predicate(), fixed_allocator_type(NULL, mBucketBuffer, overflowAllocator))
{
EASTL_CT_ASSERT((nodeCount >= 1) && (bucketCount >= 2));
if(!bEnableOverflow)
base_type::set_max_load_factor(10000.f); // Set it so that we will never resize.
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_HASH_MULTISET_DEFAULT_NAME);
#endif
mAllocator.reset(mNodeBuffer);
base_type::insert(ilist.begin(), ilist.end());
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(const this_type& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multiset<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_multiset<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(this_type&& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multiset<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::this_type&
fixed_hash_multiset<Key, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
base_type::insert(ilist.begin(), ilist.end());
return *this;
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
reset_lose_memory()
{
base_type::reset_lose_memory();
base_type::get_allocator().reset(mNodeBuffer);
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::size_type
fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline const typename fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::overflow_allocator_type&
fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline typename fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::overflow_allocator_type&
fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode, typename OverflowAllocator>
inline void fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode, OverflowAllocator>::
set_overflow_allocator(const overflow_allocator_type& allocator)
{
mAllocator.set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Value, size_t nodeCount, size_t bucketCount, bool bEnableOverflow, typename Hash, typename Predicate, bool bCacheHashCode>
inline void swap(fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode>& a,
fixed_hash_multiset<Value, nodeCount, bucketCount, bEnableOverflow, Hash, Predicate, bCacheHashCode>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
} // namespace eastl
EA_RESTORE_VC_WARNING()
#endif // Header include guard
+388
View File
@@ -0,0 +1,388 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements a list which uses a fixed size memory pool for its nodes.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_LIST_H
#define EASTL_FIXED_LIST_H
#include <EASTL/list.h>
#include <EASTL/internal/fixed_pool.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_FIXED_LIST_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
/// In the case of fixed-size containers, the allocator name always refers
/// to overflow allocations.
///
#ifndef EASTL_FIXED_LIST_DEFAULT_NAME
#define EASTL_FIXED_LIST_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_list" // Unless the user overrides something, this is "EASTL fixed_list".
#endif
/// EASTL_FIXED_LIST_DEFAULT_ALLOCATOR
///
#ifndef EASTL_FIXED_LIST_DEFAULT_ALLOCATOR
#define EASTL_FIXED_LIST_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_LIST_DEFAULT_NAME)
#endif
/// fixed_list
///
/// fixed_list is a list which uses a single block of contiguous memory
/// for its nodes. The purpose of this is to reduce memory usage relative
/// to a conventional memory allocation system (with block headers), to
/// increase allocation speed (often due to avoidance of mutex locks),
/// to increase performance (due to better memory locality), and to decrease
/// memory fragmentation due to the way that fixed block allocators work.
///
/// The primary downside to a fixed_list is that the number of nodes it
/// can contain is fixed upon its declaration. If you want a fixed_list
/// that doesn't have this limitation, then you probably don't want a
/// fixed_list. You can always create your own memory allocator that works
/// the way you want.
///
/// Template parameters:
/// T The type of object the list holds.
/// nodeCount The max number of objects to contain.
/// bEnableOverflow Whether or not we should use the overflow heap if our object pool is exhausted.
/// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap.
///
template <typename T, size_t nodeCount, bool bEnableOverflow = true, typename OverflowAllocator = EASTLAllocatorType>
class fixed_list : public list<T, fixed_node_allocator<sizeof(typename list<T>::node_type),
nodeCount, EASTL_ALIGN_OF(typename list<T>::node_type), 0, bEnableOverflow, OverflowAllocator> >
{
public:
typedef fixed_node_allocator<sizeof(typename list<T>::node_type), nodeCount,
EASTL_ALIGN_OF(typename list<T>::node_type), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef OverflowAllocator overflow_allocator_type;
typedef list<T, fixed_allocator_type> base_type;
typedef fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::iterator iterator;
enum { kMaxSize = nodeCount };
using base_type::assign;
using base_type::resize;
using base_type::insert;
using base_type::size;
using base_type::get_allocator;
protected:
char mBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
using base_type::internalAllocator;
public:
fixed_list();
explicit fixed_list(const overflow_allocator_type& overflowAllocator); // Only applicable if bEnableOverflow is true.
explicit fixed_list(size_type n); // Currently we don't support overflowAllocator specification for other constructors, for simplicity.
fixed_list(size_type n, const value_type& value);
fixed_list(const this_type& x);
fixed_list(this_type&& x);
fixed_list(this_type&&, const overflow_allocator_type& overflowAllocator);
fixed_list(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_LIST_DEFAULT_ALLOCATOR);
template <typename InputIterator>
fixed_list(InputIterator first, InputIterator last);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const; // Returns the max fixed size, which is the user-supplied nodeCount parameter.
bool full() const; // Returns true if the fixed space has been fully allocated. Note that if overflow is enabled, the container size can be greater than nodeCount but full() could return true because the fixed space may have a recently freed slot.
bool has_overflowed() const; // Returns true if the allocations spilled over into the overflow allocator. Meaningful only if overflow is enabled.
bool can_overflow() const; // Returns the value of the bEnableOverflow template parameter.
// OverflowAllocator
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
}; // fixed_list
///////////////////////////////////////////////////////////////////////
// fixed_list
///////////////////////////////////////////////////////////////////////
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_list()
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_LIST_DEFAULT_NAME);
#endif
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_list(const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_LIST_DEFAULT_NAME);
#endif
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_list(size_type n)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_LIST_DEFAULT_NAME);
#endif
resize(n);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_list(size_type n, const value_type& value)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_LIST_DEFAULT_NAME);
#endif
resize(n, value);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_list(const this_type& x)
: base_type(fixed_allocator_type(mBuffer))
{
internalAllocator().copy_overflow_allocator(x.internalAllocator());
#if EASTL_NAME_ENABLED
internalAllocator().set_name(x.internalAllocator().get_name());
#endif
assign(x.begin(), x.end());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_list(this_type&& x)
: base_type(fixed_allocator_type(mBuffer))
{
// Since we are a fixed_list, we can't normally swap pointers unless both this and
// x are using using overflow and the overflow allocators are equal. To do:
//if(has_overflowed() && x.has_overflowed() && (get_overflow_allocator() == x.get_overflow_allocator()))
//{
// We can swap contents and may need to swap the allocators as well.
//}
// The following is currently identical to the fixed_vector(const this_type& x) code above. If it stays that
// way then we may want to make a shared implementation.
internalAllocator().copy_overflow_allocator(x.internalAllocator());
#if EASTL_NAME_ENABLED
internalAllocator().set_name(x.internalAllocator().get_name());
#endif
assign(x.begin(), x.end());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_list(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
// See comments above.
internalAllocator().copy_overflow_allocator(x.internalAllocator());
#if EASTL_NAME_ENABLED
internalAllocator().set_name(x.internalAllocator().get_name());
#endif
assign(x.begin(), x.end());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_list(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
assign(ilist.begin(), ilist.end());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
template <typename InputIterator>
fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_list(InputIterator first, InputIterator last)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_LIST_DEFAULT_NAME);
#endif
assign(first, last);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(const this_type& x)
{
if(this != &x)
{
base_type::clear();
#if EASTL_ALLOCATOR_COPY_ENABLED
internalAllocator() = x.internalAllocator(); // The primary effect of this is to copy the overflow allocator.
#endif
base_type::assign(x.begin(), x.end()); // It would probably be better to implement this like list::operator=.
}
return *this;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(this_type&& x)
{
return operator=(x);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
base_type::assign(ilist.begin(), ilist.end());
return *this;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::reset_lose_memory()
{
base_type::reset_lose_memory();
get_allocator().reset(mBuffer);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::size_type
fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::full() const
{
// Note: This implementation isn't right in the case of bEnableOverflow = true because it will return
// false for the case that there are free nodes from the buffer but also nodes from the dynamic heap.
// This can happen if the container exceeds the fixed size and then frees some of the nodes from the fixed buffer.
// The only simple fix for this is to take on another member variable which tracks whether this overflow
// has occurred at some point in the past.
return !internalAllocator().can_allocate(); // This is the quickest way of detecting this. has_overflowed uses a different method because it can't use this quick method.
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::has_overflowed() const
{
#if EASTL_FIXED_SIZE_TRACKING_ENABLED // If we can use this faster pathway (as size() may be slow)...
return (internalAllocator().mPool.mnPeakSize > kMaxSize);
#else
return (size() > kMaxSize);
#endif
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::can_overflow() const
{
return bEnableOverflow;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline const typename fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::overflow_allocator_type&
fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return internalAllocator().get_overflow_allocator();
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::overflow_allocator_type&
fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return internalAllocator().get_overflow_allocator();
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void
fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>::set_overflow_allocator(const overflow_allocator_type& allocator)
{
internalAllocator().set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void swap(fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>& a,
fixed_list<T, nodeCount, bEnableOverflow, OverflowAllocator>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
} // namespace eastl
#endif // Header include guard
+580
View File
@@ -0,0 +1,580 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements a map and multimap which use a fixed size memory
// pool for their nodes.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_MAP_H
#define EASTL_FIXED_MAP_H
#include <EASTL/map.h>
#include <EASTL/fixed_set.h> // Included because fixed_rbtree_base resides here.
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_FIXED_MAP_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
/// In the case of fixed-size containers, the allocator name always refers
/// to overflow allocations.
///
#ifndef EASTL_FIXED_MAP_DEFAULT_NAME
#define EASTL_FIXED_MAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_map" // Unless the user overrides something, this is "EASTL fixed_map".
#endif
#ifndef EASTL_FIXED_MULTIMAP_DEFAULT_NAME
#define EASTL_FIXED_MULTIMAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_multimap" // Unless the user overrides something, this is "EASTL fixed_multimap".
#endif
/// EASTL_FIXED_MAP_DEFAULT_ALLOCATOR
/// EASTL_FIXED_MULTIMAP_DEFAULT_ALLOCATOR
///
#ifndef EASTL_FIXED_MAP_DEFAULT_ALLOCATOR
#define EASTL_FIXED_MAP_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_MAP_DEFAULT_NAME)
#endif
#ifndef EASTL_FIXED_MULTIMAP_DEFAULT_ALLOCATOR
#define EASTL_FIXED_MULTIMAP_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_MULTIMAP_DEFAULT_NAME)
#endif
/// fixed_map
///
/// Implements a map with a fixed block of memory identified by the
/// nodeCount template parameter.
///
/// Key The key object (key in the key/value pair).
/// T The mapped object (value in the key/value pair).
/// nodeCount The max number of objects to contain.
/// bEnableOverflow Whether or not we should use the global heap if our object pool is exhausted.
/// Compare Compare function/object for set ordering.
/// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap.
///
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow = true, typename Compare = eastl::less<Key>, typename OverflowAllocator = EASTLAllocatorType>
class fixed_map : public map<Key, T, Compare, fixed_node_allocator<sizeof(typename map<Key, T>::node_type),
nodeCount, EASTL_ALIGN_OF(eastl::pair<Key, T>), 0, bEnableOverflow, OverflowAllocator> >
{
public:
typedef fixed_node_allocator<sizeof(typename map<Key, T>::node_type), nodeCount,
EASTL_ALIGN_OF(eastl::pair<Key, T>), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type;
typedef fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator> this_type;
typedef map<Key, T, Compare, fixed_allocator_type> base_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::size_type size_type;
enum { kMaxSize = nodeCount };
using base_type::insert;
protected:
char mBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
using base_type::mAllocator;
public:
fixed_map();
explicit fixed_map(const overflow_allocator_type& overflowAllocator);
explicit fixed_map(const Compare& compare);
fixed_map(const this_type& x);
fixed_map(this_type&& x);
fixed_map(this_type&& x, const overflow_allocator_type& overflowAllocator);
fixed_map(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_MAP_DEFAULT_ALLOCATOR);
template <typename InputIterator>
fixed_map(InputIterator first, InputIterator last);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const;
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
}; // fixed_map
/// fixed_multimap
///
/// Implements a multimap with a fixed block of memory identified by the
/// nodeCount template parameter.
///
/// Key The key object (key in the key/value pair).
/// T The mapped object (value in the key/value pair).
/// nodeCount The max number of objects to contain.
/// bEnableOverflow Whether or not we should use the global heap if our object pool is exhausted.
/// Compare Compare function/object for set ordering.
/// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap.
///
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow = true, typename Compare = eastl::less<Key>, typename OverflowAllocator = EASTLAllocatorType>
class fixed_multimap : public multimap<Key, T, Compare, fixed_node_allocator<sizeof(typename multimap<Key, T>::node_type),
nodeCount, EASTL_ALIGN_OF(eastl::pair<Key, T>), 0, bEnableOverflow, OverflowAllocator> >
{
public:
typedef fixed_node_allocator<sizeof(typename multimap<Key, T>::node_type), nodeCount,
EASTL_ALIGN_OF(eastl::pair<Key, T>), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type;
typedef multimap<Key, T, Compare, fixed_allocator_type> base_type;
typedef fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator> this_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::size_type size_type;
enum { kMaxSize = nodeCount };
using base_type::insert;
protected:
char mBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
using base_type::mAllocator;
using base_type::get_compare;
public:
fixed_multimap();
fixed_multimap(const overflow_allocator_type& overflowAllocator);
explicit fixed_multimap(const Compare& compare);
fixed_multimap(const this_type& x);
fixed_multimap(this_type&& x);
fixed_multimap(this_type&& x, const overflow_allocator_type& overflowAllocator);
fixed_multimap(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_MULTIMAP_DEFAULT_ALLOCATOR);
template <typename InputIterator>
fixed_multimap(InputIterator first, InputIterator last);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const;
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
}; // fixed_multimap
///////////////////////////////////////////////////////////////////////
// fixed_map
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_map()
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MAP_DEFAULT_NAME);
#endif
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_map(const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MAP_DEFAULT_NAME);
#endif
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_map(const Compare& compare)
: base_type(compare, fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MAP_DEFAULT_NAME);
#endif
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_map(const this_type& x)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_map(this_type&& x)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_map(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer, overflowAllocator))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_map(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MAP_DEFAULT_NAME);
#endif
insert(ilist.begin(), ilist.end());
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
template <typename InputIterator>
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_map(InputIterator first, InputIterator last)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MAP_DEFAULT_NAME);
#endif
insert(first, last);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(const this_type& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
insert(ilist.begin(), ilist.end());
return *this;
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(this_type&& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::reset_lose_memory()
{
base_type::reset_lose_memory();
base_type::get_allocator().reset(mBuffer);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::size_type
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline const typename fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::overflow_allocator_type&
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::overflow_allocator_type&
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::set_overflow_allocator(const overflow_allocator_type& allocator)
{
mAllocator.set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void swap(fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>& a,
fixed_map<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
///////////////////////////////////////////////////////////////////////
// fixed_multimap
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multimap()
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTIMAP_DEFAULT_NAME);
#endif
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multimap(const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTIMAP_DEFAULT_NAME);
#endif
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multimap(const Compare& compare)
: base_type(compare, fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTIMAP_DEFAULT_NAME);
#endif
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multimap(const this_type& x)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multimap(this_type&& x)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multimap(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer, overflowAllocator))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multimap(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTIMAP_DEFAULT_NAME);
#endif
insert(ilist.begin(), ilist.end());
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
template <typename InputIterator>
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::
fixed_multimap(InputIterator first, InputIterator last)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTIMAP_DEFAULT_NAME);
#endif
insert(first, last);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(const this_type& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
insert(ilist.begin(), ilist.end());
return *this;
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(this_type&& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::reset_lose_memory()
{
base_type::reset_lose_memory();
base_type::get_allocator().reset(mBuffer);
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::size_type
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline const typename fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::overflow_allocator_type&
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::overflow_allocator_type&
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::set_overflow_allocator(const overflow_allocator_type& allocator)
{
mAllocator.set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void swap(fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>& a,
fixed_multimap<Key, T, nodeCount, bEnableOverflow, Compare, OverflowAllocator>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
} // namespace eastl
#endif // Header include guard
+578
View File
@@ -0,0 +1,578 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements a set and multiset which use a fixed size memory
// pool for their nodes.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_SET_H
#define EASTL_FIXED_SET_H
#include <EASTL/set.h>
#include <EASTL/internal/fixed_pool.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_FIXED_SET_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
/// In the case of fixed-size containers, the allocator name always refers
/// to overflow allocations.
///
#ifndef EASTL_FIXED_SET_DEFAULT_NAME
#define EASTL_FIXED_SET_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_set" // Unless the user overrides something, this is "EASTL fixed_set".
#endif
#ifndef EASTL_FIXED_MULTISET_DEFAULT_NAME
#define EASTL_FIXED_MULTISET_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_multiset" // Unless the user overrides something, this is "EASTL fixed_multiset".
#endif
/// EASTL_FIXED_SET_DEFAULT_ALLOCATOR
/// EASTL_FIXED_MULTISET_DEFAULT_ALLOCATOR
///
#ifndef EASTL_FIXED_SET_DEFAULT_ALLOCATOR
#define EASTL_FIXED_SET_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_SET_DEFAULT_NAME)
#endif
#ifndef EASTL_FIXED_MULTISET_DEFAULT_ALLOCATOR
#define EASTL_FIXED_MULTISET_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_MULTISET_DEFAULT_NAME)
#endif
/// fixed_set
///
/// Implements a set with a fixed block of memory identified by the
/// nodeCount template parameter.
///
/// Template parameters:
/// Key The type of object the set holds (a.k.a. value).
/// nodeCount The max number of objects to contain.
/// bEnableOverflow Whether or not we should use the global heap if our object pool is exhausted.
/// Compare Compare function/object for set ordering.
/// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap.
///
template <typename Key, size_t nodeCount, bool bEnableOverflow = true, typename Compare = eastl::less<Key>, typename OverflowAllocator = EASTLAllocatorType>
class fixed_set : public set<Key, Compare, fixed_node_allocator<sizeof(typename set<Key>::node_type),
nodeCount, EASTL_ALIGN_OF(Key), 0, bEnableOverflow, OverflowAllocator> >
{
public:
typedef fixed_node_allocator<sizeof(typename set<Key>::node_type), nodeCount,
EASTL_ALIGN_OF(Key), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type;
typedef set<Key, Compare, fixed_allocator_type> base_type;
typedef fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator> this_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::size_type size_type;
enum { kMaxSize = nodeCount };
using base_type::insert;
protected:
char mBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
using base_type::mAllocator;
using base_type::get_compare;
public:
fixed_set();
fixed_set(const overflow_allocator_type& overflowAllocator);
explicit fixed_set(const Compare& compare);
fixed_set(const this_type& x);
fixed_set(this_type&& x);
fixed_set(this_type&& x, const overflow_allocator_type& overflowAllocator);
fixed_set(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_SET_DEFAULT_ALLOCATOR);
template <typename InputIterator>
fixed_set(InputIterator first, InputIterator last);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const;
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
}; // fixed_set
/// fixed_multiset
///
/// Implements a multiset with a fixed block of memory identified by the
/// nodeCount template parameter.
///
/// Key The type of object the set holds (a.k.a. value).
/// nodeCount The max number of objects to contain.
/// bEnableOverflow Whether or not we should use the global heap if our object pool is exhausted.
/// Compare Compare function/object for set ordering.
/// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap.
///
template <typename Key, size_t nodeCount, bool bEnableOverflow = true, typename Compare = eastl::less<Key>, typename OverflowAllocator = EASTLAllocatorType>
class fixed_multiset : public multiset<Key, Compare, fixed_node_allocator<sizeof(typename multiset<Key>::node_type),
nodeCount, EASTL_ALIGN_OF(Key), 0, bEnableOverflow, OverflowAllocator> >
{
public:
typedef fixed_node_allocator<sizeof(typename multiset<Key>::node_type), nodeCount,
EASTL_ALIGN_OF(Key), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type;
typedef multiset<Key, Compare, fixed_allocator_type> base_type;
typedef fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator> this_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::size_type size_type;
enum { kMaxSize = nodeCount };
using base_type::insert;
protected:
char mBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
using base_type::mAllocator;
public:
fixed_multiset();
fixed_multiset(const overflow_allocator_type& overflowAllocator);
explicit fixed_multiset(const Compare& compare);
fixed_multiset(const this_type& x);
fixed_multiset(this_type&& x);
fixed_multiset(this_type&& x, const overflow_allocator_type& overflowAllocator);
fixed_multiset(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_MULTISET_DEFAULT_ALLOCATOR);
template <typename InputIterator>
fixed_multiset(InputIterator first, InputIterator last);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const;
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
}; // fixed_multiset
///////////////////////////////////////////////////////////////////////
// fixed_set
///////////////////////////////////////////////////////////////////////
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_set()
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_SET_DEFAULT_NAME);
#endif
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_set(const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_SET_DEFAULT_NAME);
#endif
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_set(const Compare& compare)
: base_type(compare, fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_SET_DEFAULT_NAME);
#endif
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_set(const this_type& x)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_set(this_type&& x)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_set(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer, overflowAllocator))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_set(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_SET_DEFAULT_NAME);
#endif
insert(ilist.begin(), ilist.end());
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
template <typename InputIterator>
fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_set(InputIterator first, InputIterator last)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_SET_DEFAULT_NAME);
#endif
insert(first, last);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(const this_type& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
insert(ilist.begin(), ilist.end());
return *this;
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(this_type&& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::reset_lose_memory()
{
base_type::reset_lose_memory();
base_type::get_allocator().reset(mBuffer);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::size_type
fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline const typename fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::overflow_allocator_type&
fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::overflow_allocator_type&
fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::set_overflow_allocator(const overflow_allocator_type& allocator)
{
mAllocator.set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void swap(fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>& a,
fixed_set<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
///////////////////////////////////////////////////////////////////////
// fixed_multiset
///////////////////////////////////////////////////////////////////////
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multiset()
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTISET_DEFAULT_NAME);
#endif
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multiset(const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTISET_DEFAULT_NAME);
#endif
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multiset(const Compare& compare)
: base_type(compare, fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTISET_DEFAULT_NAME);
#endif
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multiset(const this_type& x)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multiset(this_type&& x)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multiset(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(x.get_compare(), fixed_allocator_type(mBuffer, overflowAllocator))
{
mAllocator.copy_overflow_allocator(x.mAllocator);
#if EASTL_NAME_ENABLED
mAllocator.set_name(x.mAllocator.get_name());
#endif
base_type::operator=(x);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multiset(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTISET_DEFAULT_NAME);
#endif
insert(ilist.begin(), ilist.end());
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
template <typename InputIterator>
fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::fixed_multiset(InputIterator first, InputIterator last)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
mAllocator.set_name(EASTL_FIXED_MULTISET_DEFAULT_NAME);
#endif
insert(first, last);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(const this_type& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
insert(ilist.begin(), ilist.end());
return *this;
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::this_type&
fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::operator=(this_type&& x)
{
base_type::operator=(x);
return *this;
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::reset_lose_memory()
{
base_type::reset_lose_memory();
base_type::get_allocator().reset(mBuffer);
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::size_type
fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline const typename fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::overflow_allocator_type&
fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline typename fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::overflow_allocator_type&
fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return mAllocator.get_overflow_allocator();
}
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>::set_overflow_allocator(const overflow_allocator_type& allocator)
{
mAllocator.set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Key, size_t nodeCount, bool bEnableOverflow, typename Compare, typename OverflowAllocator>
inline void swap(fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>& a,
fixed_multiset<Key, nodeCount, bEnableOverflow, Compare, OverflowAllocator>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
} // namespace eastl
#endif // Header include guard
+389
View File
@@ -0,0 +1,389 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements an slist which uses a fixed size memory pool for its nodes.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_SLIST_H
#define EASTL_FIXED_SLIST_H
#include <EASTL/slist.h>
#include <EASTL/internal/fixed_pool.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_FIXED_SLIST_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
/// In the case of fixed-size containers, the allocator name always refers
/// to overflow allocations.
///
#ifndef EASTL_FIXED_SLIST_DEFAULT_NAME
#define EASTL_FIXED_SLIST_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_slist" // Unless the user overrides something, this is "EASTL fixed_slist".
#endif
/// EASTL_FIXED_SLIST_DEFAULT_ALLOCATOR
///
#ifndef EASTL_FIXED_SLIST_DEFAULT_ALLOCATOR
#define EASTL_FIXED_SLIST_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_SLIST_DEFAULT_NAME)
#endif
/// fixed_slist
///
/// fixed_slist is an slist which uses a single block of contiguous memory
/// for its nodes. The purpose of this is to reduce memory usage relative
/// to a conventional memory allocation system (with block headers), to
/// increase allocation speed (often due to avoidance of mutex locks),
/// to increase performance (due to better memory locality), and to decrease
/// memory fragmentation due to the way that fixed block allocators work.
///
/// The primary downside to a fixed_slist is that the number of nodes it
/// can contain is fixed upon its declaration. If you want a fixed_slist
/// that doesn't have this limitation, then you probably don't want a
/// fixed_slist. You can always create your own memory allocator that works
/// the way you want.
///
/// Template parameters:
/// T The type of object the slist holds.
/// nodeCount The max number of objects to contain.
/// bEnableOverflow Whether or not we should use the overflow heap if our object pool is exhausted.
/// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap.
///
template <typename T, size_t nodeCount, bool bEnableOverflow = true, typename OverflowAllocator = EASTLAllocatorType>
class fixed_slist : public slist<T, fixed_node_allocator<sizeof(typename slist<T>::node_type),
nodeCount, EASTL_ALIGN_OF(typename slist<T>::node_type), 0, bEnableOverflow, OverflowAllocator> >
{
public:
typedef fixed_node_allocator<sizeof(typename slist<T>::node_type), nodeCount,
EASTL_ALIGN_OF(typename slist<T>::node_type), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef OverflowAllocator overflow_allocator_type;
typedef slist<T, fixed_allocator_type> base_type;
typedef fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::node_type node_type;
enum { kMaxSize = nodeCount };
using base_type::assign;
using base_type::resize;
using base_type::size;
protected:
char mBuffer[fixed_allocator_type::kBufferSize]; // kBufferSize will take into account alignment requirements.
using base_type::internalAllocator;
public:
fixed_slist();
explicit fixed_slist(const overflow_allocator_type& overflowAllocator); // Only applicable if bEnableOverflow is true.
explicit fixed_slist(size_type n); // Currently we don't support overflowAllocator specification for other constructors, for simplicity.
fixed_slist(size_type n, const value_type& value);
fixed_slist(const this_type& x);
fixed_slist(this_type&& x);
fixed_slist(this_type&&, const overflow_allocator_type&);
fixed_slist(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_SLIST_DEFAULT_ALLOCATOR);
template <typename InputIterator>
fixed_slist(InputIterator first, InputIterator last);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<value_type> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const; // Returns the max fixed size, which is the user-supplied nodeCount parameter.
bool full() const; // Returns true if the fixed space has been fully allocated. Note that if overflow is enabled, the container size can be greater than nodeCount but full() could return true because the fixed space may have a recently freed slot.
bool has_overflowed() const; // Returns true if the allocations spilled over into the overflow allocator. Meaningful only if overflow is enabled.
bool can_overflow() const; // Returns the value of the bEnableOverflow template parameter.
// OverflowAllocator
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
}; // fixed_slist
///////////////////////////////////////////////////////////////////////
// slist
///////////////////////////////////////////////////////////////////////
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_slist()
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_SLIST_DEFAULT_NAME);
#endif
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_slist(const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_SLIST_DEFAULT_NAME);
#endif
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_slist(size_type n)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_SLIST_DEFAULT_NAME);
#endif
resize(n);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_slist(size_type n, const value_type& value)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_SLIST_DEFAULT_NAME);
#endif
resize(n, value);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_slist(const this_type& x)
: base_type(fixed_allocator_type(mBuffer))
{
internalAllocator().copy_overflow_allocator(x.internalAllocator());
#if EASTL_NAME_ENABLED
internalAllocator().set_name(x.internalAllocator().get_name());
#endif
assign(x.begin(), x.end());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_slist(this_type&& x)
: base_type(fixed_allocator_type(mBuffer))
{
// Since we are a fixed_list, we can't normally swap pointers unless both this and
// x are using using overflow and the overflow allocators are equal. To do:
//if(has_overflowed() && x.has_overflowed() && (get_overflow_allocator() == x.get_overflow_allocator()))
//{
// We can swap contents and may need to swap the allocators as well.
//}
// The following is currently identical to the fixed_vector(const this_type& x) code above. If it stays that
// way then we may want to make a shared implementation.
internalAllocator().copy_overflow_allocator(x.internalAllocator());
#if EASTL_NAME_ENABLED
internalAllocator().set_name(x.internalAllocator().get_name());
#endif
assign(x.begin(), x.end());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_slist(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
// See comments above.
internalAllocator().copy_overflow_allocator(x.internalAllocator());
#if EASTL_NAME_ENABLED
internalAllocator().set_name(x.internalAllocator().get_name());
#endif
assign(x.begin(), x.end());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_slist(std::initializer_list<value_type> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_SLIST_DEFAULT_NAME);
#endif
assign(ilist.begin(), ilist.end());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
template <typename InputIterator>
fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_slist(InputIterator first, InputIterator last)
: base_type(fixed_allocator_type(mBuffer))
{
#if EASTL_NAME_ENABLED
internalAllocator().set_name(EASTL_FIXED_SLIST_DEFAULT_NAME);
#endif
assign(first, last);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(const this_type& x)
{
if(this != &x)
{
base_type::clear();
#if EASTL_ALLOCATOR_COPY_ENABLED
internalAllocator() = x.internalAllocator(); // The primary effect of this is to copy the overflow allocator.
#endif
base_type::assign(x.begin(), x.end()); // It would probably be better to implement this like slist::operator=.
}
return *this;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(this_type&& x)
{
return operator=(x);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(std::initializer_list<value_type> ilist)
{
base_type::clear();
base_type::assign(ilist.begin(), ilist.end());
return *this;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::reset_lose_memory()
{
base_type::reset_lose_memory();
base_type::get_allocator().reset(mBuffer);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::size_type
fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::full() const
{
// Note: This implementation isn't right in the case of bEnableOverflow = true because it will return
// false for the case that there are free nodes from the buffer but also nodes from the dynamic heap.
// This can happen if the container exceeds the fixed size and then frees some of the nodes from the fixed buffer.
return !internalAllocator().can_allocate(); // This is the quickest way of detecting this. has_overflowed uses a different method because it can't use this quick method.
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::has_overflowed() const
{
#if EASTL_FIXED_SIZE_TRACKING_ENABLED // If we can use this faster pathway (as size() may be slow)...
return (internalAllocator().mPool.mnPeakSize > kMaxSize);
#else
return (size() > kMaxSize);
#endif
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::can_overflow() const
{
return bEnableOverflow;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline const typename fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::overflow_allocator_type&
fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return internalAllocator().get_overflow_allocator();
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::overflow_allocator_type&
fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return internalAllocator().get_overflow_allocator();
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void
fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>::set_overflow_allocator(const overflow_allocator_type& allocator)
{
internalAllocator().set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void swap(fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>& a,
fixed_slist<T, nodeCount, bEnableOverflow, OverflowAllocator>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
} // namespace eastl
#endif // Header include guard
+805
View File
@@ -0,0 +1,805 @@
/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements a string which uses a fixed size memory pool.
// The bEnableOverflow template parameter allows the container to resort to
// heap allocations if the memory pool is exhausted.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_STRING_H
#define EASTL_FIXED_STRING_H
#include <EASTL/internal/config.h>
#include <EASTL/string.h>
#include <EASTL/internal/fixed_pool.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_FIXED_STRING_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
/// In the case of fixed-size containers, the allocator name always refers
/// to overflow allocations.
///
#ifndef EASTL_FIXED_STRING_DEFAULT_NAME
#define EASTL_FIXED_STRING_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_string" // Unless the user overrides something, this is "EASTL fixed_string".
#endif
/// fixed_string
///
/// A fixed_string with bEnableOverflow == true is identical to a regular
/// string in terms of its behavior. All the expectations of regular string
/// apply to it and no additional expectations come from it. When bEnableOverflow
/// is false, fixed_string behaves like regular string with the exception that
/// its capacity can never increase. All operations you do on such a fixed_string
/// which require a capacity increase will result in undefined behavior or an
/// C++ allocation exception, depending on the configuration of EASTL.
///
/// Note: The nodeCount value is the amount of characters to allocate, which needs to
/// take into account a terminating zero. Thus if you want to store strings with a strlen
/// of 30, the nodeCount value must be at least 31.
///
/// Template parameters:
/// T The type of object the string holds (char, wchar_t, char8_t, char16_t, char32_t).
/// nodeCount The max number of objects to contain.
/// bEnableOverflow Whether or not we should use the overflow heap if our object pool is exhausted.
/// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap.
///
/// Notes:
/// The nodeCount value must be at least 2, one for a character and one for a terminating 0.
///
/// As of this writing, the string class necessarily reallocates when an insert of
/// self is done into self. As a result, the fixed_string class doesn't support
/// inserting self into self unless the bEnableOverflow template parameter is true.
///
/// Example usage:
/// fixed_string<char, 128 + 1, true> fixedString("hello world"); // Can hold up to a strlen of 128.
///
/// fixedString = "hola mundo";
/// fixedString.clear();
/// fixedString.resize(200);
/// fixedString.sprintf("%f", 1.5f);
///
template <typename T, int nodeCount, bool bEnableOverflow = true, typename OverflowAllocator = EASTLAllocatorType>
class fixed_string : public basic_string<T, fixed_vector_allocator<sizeof(T), nodeCount, EASTL_ALIGN_OF(T), 0, bEnableOverflow, OverflowAllocator> >
{
public:
typedef fixed_vector_allocator<sizeof(T), nodeCount, EASTL_ALIGN_OF(T),
0, bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type;
typedef basic_string<T, fixed_allocator_type> base_type;
typedef fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::CtorDoNotInitialize CtorDoNotInitialize;
typedef typename base_type::CtorSprintf CtorSprintf;
typedef aligned_buffer<nodeCount * sizeof(T), EASTL_ALIGN_OF(T)> aligned_buffer_type;
enum { kMaxSize = nodeCount - 1 }; // -1 because we need to save one element for the silent terminating null.
using base_type::npos;
using base_type::mPair;
using base_type::append;
using base_type::resize;
using base_type::clear;
using base_type::capacity;
using base_type::size;
using base_type::sprintf_va_list;
using base_type::DoAllocate;
using base_type::DoFree;
using base_type::internalLayout;
using base_type::get_allocator;
protected:
union // We define a union in order to avoid strict pointer aliasing issues with compilers like GCC.
{
value_type mArray[1];
aligned_buffer_type mBuffer; // Question: Why are we doing this aligned_buffer thing? Why not just do an array of value_type, given that we are using just strings of char types.
};
public:
fixed_string();
explicit fixed_string(const overflow_allocator_type& overflowAllocator); // Only applicable if bEnableOverflow is true.
fixed_string(const base_type& x, size_type position, size_type n = base_type::npos); // Currently we don't support overflowAllocator specification for other constructors, for simplicity.
fixed_string(const value_type* p, size_type n);
fixed_string(const value_type* p);
fixed_string(size_type n, const value_type& value);
fixed_string(const this_type& x);
fixed_string(const this_type& x, const overflow_allocator_type& overflowAllocator);
fixed_string(const base_type& x);
fixed_string(const value_type* pBegin, const value_type* pEnd);
fixed_string(CtorDoNotInitialize, size_type n);
fixed_string(CtorSprintf, const value_type* pFormat, ...);
fixed_string(std::initializer_list<T> ilist, const overflow_allocator_type& overflowAllocator);
fixed_string(this_type&& x);
fixed_string(this_type&& x, const overflow_allocator_type& overflowAllocator);
this_type& operator=(const this_type& x);
this_type& operator=(const base_type& x);
this_type& operator=(const value_type* p);
this_type& operator=(const value_type c);
this_type& operator=(std::initializer_list<T> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void set_capacity(size_type n);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const;
bool full() const; // Returns true if the fixed space has been fully allocated. Note that if overflow is enabled, the container size can be greater than nodeCount but full() could return true because the fixed space may have a recently freed slot.
bool has_overflowed() const; // Returns true if the allocations spilled over into the overflow allocator. Meaningful only if overflow is enabled.
bool can_overflow() const; // Returns the value of the bEnableOverflow template parameter.
// The inherited versions of substr/left/right call the basic_string constructor,
// which will call the overflow allocator and fail if bEnableOverflow == false
this_type substr(size_type position, size_type n) const;
this_type left(size_type n) const;
this_type right(size_type n) const;
// OverflowAllocator
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
}; // fixed_string
///////////////////////////////////////////////////////////////////////
// fixed_string
///////////////////////////////////////////////////////////////////////
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string()
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME);
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer.buffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME);
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(const this_type& x)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
get_allocator().copy_overflow_allocator(x.get_allocator());
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(x);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(const this_type& x, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer.buffer, overflowAllocator))
{
get_allocator().copy_overflow_allocator(x.get_allocator());
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(x);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(const base_type& x)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(x);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(const base_type& x, size_type position, size_type n)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(x, position, n);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(const value_type* p, size_type n)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME);
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(p, n);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(const value_type* p)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME);
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(p); // There better be enough space to hold the assigned string.
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(size_type n, const value_type& value)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME);
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(n, value); // There better be enough space to hold the assigned string.
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(const value_type* pBegin, const value_type* pEnd)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME);
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(pBegin, pEnd);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(CtorDoNotInitialize, size_type n)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME);
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
if(n < nodeCount)
{
internalLayout().SetHeapSize(n);
*internalLayout().HeapEndPtr() = 0;
}
else
{
internalLayout().SetHeapSize(0);
*internalLayout().HeapEndPtr() = 0;
resize(n);
}
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(CtorSprintf, const value_type* pFormat, ...)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME);
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
va_list arguments;
va_start(arguments, pFormat);
sprintf_va_list(pFormat, arguments);
va_end(arguments);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(std::initializer_list<T> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer.buffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_STRING_DEFAULT_NAME);
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(ilist.begin(), ilist.end());
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(this_type&& x)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
// We copy from x instead of trade with it. We need to do so because fixed_ containers use local memory buffers.
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(x); // Let x destruct its own items.
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_string(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer.buffer, overflowAllocator))
{
// We copy from x instead of trade with it. We need to do so because fixed_ containers use local memory buffers.
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapCapacity(nodeCount - 1);
internalLayout().SetHeapSize(0);
*internalLayout().HeapBeginPtr() = 0;
append(x); // Let x destruct its own items.
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(const this_type& x)
{
if(this != &x)
{
clear();
#if EASTL_ALLOCATOR_COPY_ENABLED
get_allocator() = x.get_allocator();
#endif
append(x);
}
return *this;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
this_type& fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(const base_type& x)
{
if(static_cast<base_type*>(this) != &x)
{
clear();
#if EASTL_ALLOCATOR_COPY_ENABLED
get_allocator() = x.get_allocator();
#endif
append(x);
}
return *this;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
this_type& fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(const value_type* p)
{
if(internalLayout().HeapBeginPtr() != p)
{
clear();
append(p);
}
return *this;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
this_type& fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(const value_type c)
{
clear();
append((size_type)1, c);
return *this;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
this_type& fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(std::initializer_list<T> ilist)
{
clear();
append(ilist.begin(), ilist.end());
return *this;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
this_type& fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(this_type&& x)
{
// We copy from x instead of trade with it. We need to do so because fixed_ containers use local memory buffers.
// if(static_cast<base_type*>(this) != &x) This should be impossible, so we disable it until proven otherwise.
{
clear();
#if EASTL_ALLOCATOR_COPY_ENABLED
get_allocator() = x.get_allocator();
#endif
append(x); // Let x destruct its own items.
}
return *this;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::swap(this_type& x)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::set_capacity(size_type n)
{
const size_type nPrevSize = internalLayout().GetSize();
const size_type nPrevCapacity = capacity();
if(n == npos) // If the user means to set the capacity so that it equals the size (i.e. free excess capacity)...
n = nPrevSize;
if(n != nPrevCapacity) // If the request results in a capacity change...
{
const size_type allocSize = (n + 1); // +1 because the terminating 0 isn't included in the supplied capacity value. So now n refers the amount of memory we need.
if(can_overflow() && (((uintptr_t)internalLayout().HeapBeginPtr() != (uintptr_t)mBuffer.buffer) || (allocSize > kMaxSize))) // If we are or would be using dynamically allocated memory instead of our fixed-size member buffer...
{
T* const pNewData = (allocSize <= kMaxSize) ? (T*)&mBuffer.buffer[0] : DoAllocate(allocSize);
T* const pCopyEnd = (n < nPrevSize) ? (internalLayout().HeapBeginPtr() + n) : internalLayout().HeapEndPtr();
CharStringUninitializedCopy(internalLayout().HeapBeginPtr(), pCopyEnd, pNewData); // Copy [internalLayout().heap.mpBegin, pCopyEnd) to pNewData.
if((uintptr_t)internalLayout().HeapBeginPtr() != (uintptr_t)mBuffer.buffer)
DoFree(internalLayout().HeapBeginPtr(), internalLayout().GetHeapCapacity() + 1);
internalLayout().SetHeapSize((size_type)(pCopyEnd - internalLayout().HeapBeginPtr()));
internalLayout().SetHeapBeginPtr(pNewData);
internalLayout().SetHeapCapacity(allocSize - 1);
} // Else the new capacity would be within our fixed buffer.
else if(n < nPrevSize) // If the newly requested capacity is less than our size, we do what vector::set_capacity does and resize, even though we actually aren't reducing the capacity.
resize(n);
}
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::reset_lose_memory()
{
internalLayout().SetHeapBeginPtr(mArray);
internalLayout().SetHeapSize(0);
internalLayout().SetHeapCapacity(nodeCount - 1);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
size_type fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::full() const
{
// If size >= capacity, then we are definitely full.
// Also, if our size is smaller but we've switched away from mBuffer due to a previous overflow, then we are considered full.
return ((size_t)(internalLayout().HeapEndPtr() - internalLayout().HeapBeginPtr()) >= kMaxSize) || ((void*)internalLayout().HeapBeginPtr() != (void*)mBuffer.buffer);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::has_overflowed() const
{
// This will be incorrect for the case that bOverflowEnabled is true and the container was resized
// down to a small size where the fixed buffer could take over ownership of the data again.
// The only simple fix for this is to take on another member variable which tracks whether this overflow
// has occurred at some point in the past.
return ((void*)internalLayout().HeapBeginPtr() != (void*)mBuffer.buffer);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::can_overflow() const
{
return bEnableOverflow;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
this_type fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::substr(size_type position, size_type n) const
{
#if EASTL_STRING_OPT_RANGE_ERRORS
if(position > internalLayout().GetSize())
base_type::ThrowRangeException();
#endif
return fixed_string(internalLayout().HeapBeginPtr() + position,
internalLayout().HeapBeginPtr() + position + eastl::min_alt(n, internalLayout().GetSize() - position));
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
this_type fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::left(size_type n) const
{
const size_type nLength = size();
if(n < nLength)
return fixed_string(internalLayout().HeapBeginPtr(), internalLayout().HeapBeginPtr() + n);
return *this;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
this_type fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::right(size_type n) const
{
const size_type nLength = size();
if(n < nLength)
return fixed_string(internalLayout().HeapEndPtr() - n, internalLayout().HeapEndPtr());
return *this;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline const typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
overflow_allocator_type& fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return get_allocator().get_overflow_allocator();
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::
overflow_allocator_type& fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return get_allocator().get_overflow_allocator();
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::set_overflow_allocator(const overflow_allocator_type& allocator)
{
get_allocator().set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
// Operator +
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(const fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>& a,
const fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>& b)
{
// We have a problem here because need to return an fixed_string by value. This will typically result in it
// using stack space equal to its size. That size may be too large to be workable.
typedef fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type;
this_type result(const_cast<this_type&>(a).get_overflow_allocator());
result.append(a);
result.append(b);
return result;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(const typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::value_type* p,
const fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>& b)
{
typedef fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type;
const typename this_type::size_type n = (typename this_type::size_type)CharStrlen(p);
this_type result(const_cast<this_type&>(b).get_overflow_allocator());
result.append(p, p + n);
result.append(b);
return result;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::value_type c,
const fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>& b)
{
typedef fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type;
this_type result(const_cast<this_type&>(b).get_overflow_allocator());
result.push_back(c);
result.append(b);
return result;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(const fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>& a,
const typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::value_type* p)
{
typedef fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type;
const typename this_type::size_type n = (typename this_type::size_type)CharStrlen(p);
this_type result(const_cast<this_type&>(a).get_overflow_allocator());
result.append(a);
result.append(p, p + n);
return result;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(const fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>& a,
typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::value_type c)
{
typedef fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type;
this_type result(const_cast<this_type&>(a).get_overflow_allocator());
result.append(a);
result.push_back(c);
return result;
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>&& a,
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>&& b)
{
a.append(b); // Using an rvalue by name results in it becoming an lvalue.
return eastl::move(a);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>&& a,
const fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>& b)
{
a.append(b);
return eastl::move(a);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(const typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::value_type* p,
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>&& b)
{
b.insert(0, p);
return eastl::move(b);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>&& a,
const typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::value_type* p)
{
a.append(p);
return eastl::move(a);
}
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator> operator+(fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>&& a,
typename fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>::value_type c)
{
a.push_back(c);
return eastl::move(a);
}
// operator ==, !=, <, >, <=, >= come from the string implementations.
template <typename T, int nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void swap(fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>& a,
fixed_string<T, nodeCount, bEnableOverflow, OverflowAllocator>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
} // namespace eastl
#endif // Header include guard
+275
View File
@@ -0,0 +1,275 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_SUBSTRING_H
#define EASTL_FIXED_SUBSTRING_H
#include <EASTL/string.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// fixed_substring
///
/// Implements a string which is a reference to a segment of characters.
/// This class is efficient because it allocates no memory and copies no
/// memory during construction and assignment, but rather refers directly
/// to the segment of chracters. A common use of this is to have a
/// fixed_substring efficiently refer to a substring within another string.
///
/// You cannot directly resize a fixed_substring (e.g. via resize, insert,
/// append, erase), but you can assign a different substring to it.
/// You can modify the characters within a substring in place.
/// As of this writing, in the name of being lean and simple it is the
/// user's responsibility to not call unsupported resizing functions
/// such as those listed above. A detailed listing of the functions which
/// are not supported is given below in the class declaration.
///
/// The c_str function doesn't act as one might hope, as it simply
/// returns the pointer to the beginning of the string segment and the
/// 0-terminator may be beyond the end of the segment. If you want to
/// always be able to use c_str as expected, use the fixed string solution
/// we describe below.
///
/// Another use of fixed_substring is to provide C++ string-like functionality
/// with a C character array. This allows you to work on a C character array
/// as if it were a C++ string as opposed using the C string API. Thus you
/// can do this:
///
/// void DoSomethingForUser(char* timeStr, size_t timeStrCapacity)
/// {
/// fixed_substring tmp(timeStr, timeStrCapacity);
/// tmp = "hello ";
/// tmp += "world";
/// }
///
/// Note that this class constructs and assigns from const string pointers
/// and const string objects, yet this class does not declare its member
/// data as const. This is a concession in order to allow this implementation
/// to be simple and lean. It is the user's responsibility to make sure
/// that strings that should not or can not be modified are either not
/// used by fixed_substring or are not modified by fixed_substring.
///
/// A more flexible alternative to fixed_substring is fixed_string.
/// fixed_string has none of the functional limitations that fixed_substring
/// has and like fixed_substring it doesn't allocate memory. However,
/// fixed_string makes a *copy* of the source string and uses local
/// memory to store that copy. Also, fixed_string objects on the stack
/// are going to have a limit as to their maximum size.
///
/// Notes:
/// As of this writing, the string class necessarily reallocates when
/// an insert of self is done into self. As a result, the fixed_substring
/// class doesn't support inserting self into self.
///
/// Example usage:
/// basic_string<char> str("hello world");
/// fixed_substring<char> sub(str, 2, 5); // sub == "llo w"
///
template <typename T>
class fixed_substring : public basic_string<T>
{
public:
typedef basic_string<T> base_type;
typedef fixed_substring<T> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::iterator iterator;
typedef typename base_type::const_iterator const_iterator;
using base_type::npos;
using base_type::mPair;
using base_type::AllocateSelf;
using base_type::internalLayout;
using base_type::get_allocator;
private:
void SetInternalHeapLayout(value_type* pBeginPtr, size_type nSize, size_type nCap)
{
internalLayout().SetHeapBeginPtr(pBeginPtr);
internalLayout().SetHeapSize(nSize);
internalLayout().SetHeapCapacity(nCap);
}
public:
fixed_substring()
: base_type()
{
}
fixed_substring(const fixed_substring& x)
: fixed_substring(static_cast<const base_type&>(x))
{}
fixed_substring(const base_type& x)
: base_type()
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
assign(x);
}
// We gain no benefit from having an rvalue move constructor or assignment operator,
// as this class is a const class.
fixed_substring(const base_type& x, size_type position, size_type n = base_type::npos)
: base_type()
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
assign(x, position, n);
}
fixed_substring(const value_type* p, size_type n)
: base_type()
{
assign(p, n);
}
fixed_substring(const value_type* p)
: base_type()
{
assign(p);
}
fixed_substring(const value_type* pBegin, const value_type* pEnd)
: base_type()
{
assign(pBegin, pEnd);
}
~fixed_substring()
{
// We need to reset, as otherwise the parent destructor will
// attempt to free our memory.
AllocateSelf();
}
this_type& operator=(const this_type& x)
{
assign(x);
return *this;
}
this_type& operator=(const base_type& x)
{
assign(x);
return *this;
}
this_type& operator=(const value_type* p)
{
assign(p);
return *this;
}
this_type& assign(const base_type& x)
{
// By design, we need to cast away const-ness here.
SetInternalHeapLayout(const_cast<value_type*>(x.data()), x.size(), x.size());
return *this;
}
this_type& assign(const base_type& x, size_type position, size_type n)
{
// By design, we need to cast away const-ness here.
SetInternalHeapLayout(const_cast<value_type*>(x.data()) + position, n, n);
return *this;
}
this_type& assign(const value_type* p, size_type n)
{
// By design, we need to cast away const-ness here.
SetInternalHeapLayout(const_cast<value_type*>(p), n, n);
return *this;
}
this_type& assign(const value_type* p)
{
// By design, we need to cast away const-ness here.
SetInternalHeapLayout(const_cast<value_type*>(p), (size_type)CharStrlen(p), (size_type)CharStrlen(p));
return *this;
}
this_type& assign(const value_type* pBegin, const value_type* pEnd)
{
// By design, we need to cast away const-ness here.
SetInternalHeapLayout(const_cast<value_type*>(pBegin), (size_type)(pEnd - pBegin), (size_type)(pEnd - pBegin));
return *this;
}
// Partially supported functionality
//
// When using fixed_substring on a character sequence that is within another
// string, the following functions may do one of two things:
// 1 Attempt to reallocate
// 2 Write a 0 char at the end of the fixed_substring
//
// Item #1 will result in a crash, due to the attempt by the underlying
// string class to free the substring memory. Item #2 will result in a 0
// char being written to the character array. Item #2 may or may not be
// a problem, depending on how you use fixed_substring. Thus the following
// functions cannot be used safely.
#if 0 // !defined(EA_COMPILER_NO_DELETED_FUNCTIONS) We may want to enable these deletions after some investigation of possible user impact.
this_type& operator=(value_type c) = delete;
void resize(size_type n, value_type c) = delete;
void resize(size_type n) = delete;
void reserve(size_type = 0) = delete;
void set_capacity(size_type n) = delete;
void clear() = delete;
this_type& operator+=(const base_type& x) = delete;
this_type& operator+=(const value_type* p) = delete;
this_type& operator+=(value_type c) = delete;
this_type& append(const base_type& x) = delete;
this_type& append(const base_type& x, size_type position, size_type n) = delete;
this_type& append(const value_type* p, size_type n) = delete;
this_type& append(const value_type* p) = delete;
this_type& append(size_type n) = delete;
this_type& append(size_type n, value_type c) = delete;
this_type& append(const value_type* pBegin, const value_type* pEnd) = delete;
this_type& append_sprintf_va_list(const value_type* pFormat, va_list arguments) = delete;
this_type& append_sprintf(const value_type* pFormat, ...) = delete;
void push_back(value_type c) = delete;
void pop_back() = delete;
this_type& assign(size_type n, value_type c) = delete;
this_type& insert(size_type position, const base_type& x) = delete;
this_type& insert(size_type position, const base_type& x, size_type beg, size_type n) = delete;
this_type& insert(size_type position, const value_type* p, size_type n) = delete;
this_type& insert(size_type position, const value_type* p) = delete;
this_type& insert(size_type position, size_type n, value_type c) = delete;
iterator insert(const_iterator p, value_type c) = delete;
void insert(const_iterator p, size_type n, value_type c) = delete;
void insert(const_iterator p, const value_type* pBegin, const value_type* pEnd) = delete;
this_type& erase(size_type position = 0, size_type n = npos) = delete;
iterator erase(const_iterator p) = delete;
iterator erase(const_iterator pBegin, const_iterator pEnd) = delete;
void swap(base_type& x) = delete;
this_type& sprintf_va_list(const value_type* pFormat, va_list arguments) = delete;
this_type& sprintf(const value_type* pFormat, ...) = delete;
#endif
}; // fixed_substring
} // namespace eastl
#endif // Header include guard
+625
View File
@@ -0,0 +1,625 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements a vector which uses a fixed size memory pool.
// The bEnableOverflow template parameter allows the container to resort to
// heap allocations if the memory pool is exhausted.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_FIXED_VECTOR_H
#define EASTL_FIXED_VECTOR_H
#include <EASTL/vector.h>
#include <EASTL/internal/fixed_pool.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_FIXED_VECTOR_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
/// In the case of fixed-size containers, the allocator name always refers
/// to overflow allocations.
///
#ifndef EASTL_FIXED_VECTOR_DEFAULT_NAME
#define EASTL_FIXED_VECTOR_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_vector" // Unless the user overrides something, this is "EASTL fixed_vector".
#endif
/// EASTL_FIXED_VECTOR_DEFAULT_ALLOCATOR
///
#ifndef EASTL_FIXED_VECTOR_DEFAULT_ALLOCATOR
#define EASTL_FIXED_VECTOR_DEFAULT_ALLOCATOR overflow_allocator_type(EASTL_FIXED_VECTOR_DEFAULT_NAME)
#endif
/// fixed_vector
///
/// A fixed_vector with bEnableOverflow == true is identical to a regular
/// vector in terms of its behavior. All the expectations of regular vector
/// apply to it and no additional expectations come from it. When bEnableOverflow
/// is false, fixed_vector behaves like regular vector with the exception that
/// its capacity can never increase. All operations you do on such a fixed_vector
/// which require a capacity increase will result in undefined behavior or an
/// C++ allocation exception, depending on the configuration of EASTL.
///
/// Template parameters:
/// T The type of object the vector holds.
/// nodeCount The max number of objects to contain.
/// bEnableOverflow Whether or not we should use the overflow heap if our object pool is exhausted.
/// OverflowAllocator Overflow allocator, which is only used if bEnableOverflow == true. Defaults to the global heap.
///
/// Note: The nodeCount value must be at least 1.
///
/// Example usage:
/// fixed_vector<Widget, 128, true> fixedVector);
///
/// fixedVector.push_back(Widget());
/// fixedVector.resize(200);
/// fixedVector.clear();
///
template <typename T, size_t nodeCount, bool bEnableOverflow = true, typename OverflowAllocator = typename eastl::type_select<bEnableOverflow, EASTLAllocatorType, EASTLDummyAllocatorType>::type>
class fixed_vector : public vector<T, fixed_vector_allocator<sizeof(T), nodeCount, EASTL_ALIGN_OF(T), 0, bEnableOverflow, OverflowAllocator> >
{
public:
typedef fixed_vector_allocator<sizeof(T), nodeCount, EASTL_ALIGN_OF(T),
0, bEnableOverflow, OverflowAllocator> fixed_allocator_type;
typedef OverflowAllocator overflow_allocator_type;
typedef vector<T, fixed_allocator_type> base_type;
typedef fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::reference reference;
typedef typename base_type::iterator iterator;
typedef typename base_type::const_iterator const_iterator;
typedef aligned_buffer<nodeCount * sizeof(T), EASTL_ALIGN_OF(T)> aligned_buffer_type;
enum { kMaxSize = nodeCount };
using base_type::get_allocator;
using base_type::mpBegin;
using base_type::mpEnd;
using base_type::internalCapacityPtr;
using base_type::resize;
using base_type::clear;
using base_type::size;
using base_type::assign;
using base_type::npos;
using base_type::DoAllocate;
using base_type::DoFree;
using base_type::DoAssign;
using base_type::DoAssignFromIterator;
protected:
aligned_buffer_type mBuffer;
public:
fixed_vector();
explicit fixed_vector(const overflow_allocator_type& overflowAllocator); // Only applicable if bEnableOverflow is true.
explicit fixed_vector(size_type n); // Currently we don't support overflowAllocator specification for other constructors, for simplicity.
fixed_vector(size_type n, const value_type& value);
fixed_vector(const this_type& x);
fixed_vector(this_type&& x);
fixed_vector(this_type&& x, const overflow_allocator_type& overflowAllocator);
fixed_vector(std::initializer_list<T> ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_VECTOR_DEFAULT_ALLOCATOR);
template <typename InputIterator>
fixed_vector(InputIterator first, InputIterator last);
this_type& operator=(const this_type& x);
this_type& operator=(std::initializer_list<T> ilist);
this_type& operator=(this_type&& x);
void swap(this_type& x);
void set_capacity(size_type n);
void clear(bool freeOverflow);
void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs.
size_type max_size() const; // Returns the max fixed size, which is the user-supplied nodeCount parameter.
bool full() const; // Returns true if the fixed space has been fully allocated. Note that if overflow is enabled, the container size can be greater than nodeCount but full() could return true because the fixed space may have a recently freed slot.
bool has_overflowed() const; // Returns true if the allocations spilled over into the overflow allocator. Meaningful only if overflow is enabled.
bool can_overflow() const; // Returns the value of the bEnableOverflow template parameter.
void* push_back_uninitialized();
void push_back(const value_type& value); // We implement push_back here because we have a specialization that's
reference push_back(); // smaller for the case of overflow being disabled.
void push_back(value_type&& value);
// OverflowAllocator
const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT;
overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT;
void set_overflow_allocator(const overflow_allocator_type& allocator);
protected:
void* DoPushBackUninitialized(true_type);
void* DoPushBackUninitialized(false_type);
void DoPushBack(true_type, const value_type& value);
void DoPushBack(false_type, const value_type& value);
void DoPushBackMove(true_type, value_type&& value);
void DoPushBackMove(false_type, value_type&& value);
reference DoPushBack(false_type);
reference DoPushBack(true_type);
}; // fixed_vector
///////////////////////////////////////////////////////////////////////
// fixed_vector
///////////////////////////////////////////////////////////////////////
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_vector()
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_VECTOR_DEFAULT_NAME);
#endif
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_vector(const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer.buffer, overflowAllocator))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_VECTOR_DEFAULT_NAME);
#endif
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_vector(size_type n)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_VECTOR_DEFAULT_NAME);
#endif
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
resize(n);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_vector(size_type n, const value_type& value)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_VECTOR_DEFAULT_NAME);
#endif
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
resize(n, value);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_vector(const this_type& x)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
get_allocator().copy_overflow_allocator(x.get_allocator());
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
base_type::template DoAssign<const_iterator, false>(x.begin(), x.end(), false_type());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_vector(this_type&& x)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
// Since we are a fixed_vector, we can't swap pointers. We can possibly so something like fixed_swap or
// we can just do an assignment from x. If we want to do the former then we need to have some complicated
// code to deal with overflow or no overflow, and whether the memory is in the fixed-size buffer or in
// the overflow allocator. 90% of the time the memory should be in the fixed buffer, in which case
// a simple assignment is no worse than the fancy pathway.
// Since we are a fixed_list, we can't normally swap pointers unless both this and
// x are using using overflow and the overflow allocators are equal. To do:
//if(has_overflowed() && x.has_overflowed() && (get_overflow_allocator() == x.get_overflow_allocator()))
//{
// We can swap contents and may need to swap the allocators as well.
//}
// The following is currently identical to the fixed_vector(const this_type& x) code above. If it stays that
// way then we may want to make a shared implementation.
get_allocator().copy_overflow_allocator(x.get_allocator());
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
base_type::template DoAssign<move_iterator<iterator>, true>(eastl::make_move_iterator(x.begin()), eastl::make_move_iterator(x.end()), false_type());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_vector(this_type&& x, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer.buffer, overflowAllocator))
{
// See the discussion above.
// The following is currently identical to the fixed_vector(const this_type& x) code above. If it stays that
// way then we may want to make a shared implementation.
get_allocator().copy_overflow_allocator(x.get_allocator());
#if EASTL_NAME_ENABLED
get_allocator().set_name(x.get_allocator().get_name());
#endif
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
base_type::template DoAssign<iterator, true>(x.begin(), x.end(), false_type());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_vector(std::initializer_list<T> ilist, const overflow_allocator_type& overflowAllocator)
: base_type(fixed_allocator_type(mBuffer.buffer, overflowAllocator))
{
typedef typename std::initializer_list<value_type>::iterator InputIterator;
typedef typename eastl::iterator_traits<InputIterator>::iterator_category IC;
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
base_type::template DoAssignFromIterator<InputIterator, false>(ilist.begin(), ilist.end(), IC());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
template <typename InputIterator>
fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::fixed_vector(InputIterator first, InputIterator last)
: base_type(fixed_allocator_type(mBuffer.buffer))
{
#if EASTL_NAME_ENABLED
get_allocator().set_name(EASTL_FIXED_VECTOR_DEFAULT_NAME);
#endif
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
base_type::template DoAssign<InputIterator, false>(first, last, is_integral<InputIterator>());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(const this_type& x)
{
if(this != &x)
{
clear();
#if EASTL_ALLOCATOR_COPY_ENABLED
get_allocator() = x.get_allocator(); // The primary effect of this is to copy the overflow allocator.
#endif
base_type::template DoAssign<const_iterator, false>(x.begin(), x.end(), false_type()); // Shorter route.
}
return *this;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(std::initializer_list<T> ilist)
{
typedef typename std::initializer_list<value_type>::iterator InputIterator;
typedef typename eastl::iterator_traits<InputIterator>::iterator_category IC;
clear();
base_type::template DoAssignFromIterator<InputIterator, false>(ilist.begin(), ilist.end(), IC());
return *this;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::this_type&
fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::operator=(this_type&& x)
{
// Since we are a fixed_vector, we can't swap pointers. We can possibly do something like fixed_swap or
// we can just do an assignment from x. If we want to do the former then we need to have some complicated
// code to deal with overflow or no overflow, and whether the memory is in the fixed-size buffer or in
// the overflow allocator. 90% of the time the memory should be in the fixed buffer, in which case
// a simple assignment is no worse than the fancy pathway.
if (this != &x)
{
clear();
#if EASTL_ALLOCATOR_COPY_ENABLED
get_allocator() = x.get_allocator(); // The primary effect of this is to copy the overflow allocator.
#endif
base_type::template DoAssign<move_iterator<iterator>, true>(eastl::make_move_iterator(x.begin()), eastl::make_move_iterator(x.end()), false_type()); // Shorter route.
}
return *this;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::swap(this_type& x)
{
if((has_overflowed() && x.has_overflowed()) && (get_overflow_allocator() == x.get_overflow_allocator())) // If both containers are using the heap instead of local memory
{ // then we can do a fast pointer swap instead of content swap.
eastl::swap(mpBegin, x.mpBegin);
eastl::swap(mpEnd, x.mpEnd);
eastl::swap(internalCapacityPtr(), x.internalCapacityPtr());
}
else
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(*this, x);
}
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::set_capacity(size_type n)
{
const size_type nPrevSize = (size_type)(mpEnd - mpBegin);
const size_type nPrevCapacity = (size_type)(internalCapacityPtr() - mpBegin);
if(n == npos) // If the user means to set the capacity so that it equals the size (i.e. free excess capacity)...
n = nPrevSize;
if(n != nPrevCapacity) // If the request results in a capacity change...
{
if(can_overflow() && (((uintptr_t)mpBegin != (uintptr_t)mBuffer.buffer) || (n > kMaxSize))) // If we are or would be using dynamically allocated memory instead of our fixed-size member buffer...
{
T* const pNewData = (n <= kMaxSize) ? (T*)&mBuffer.buffer[0] : DoAllocate(n);
T* const pCopyEnd = (n < nPrevSize) ? (mpBegin + n) : mpEnd;
eastl::uninitialized_move_ptr(mpBegin, pCopyEnd, pNewData); // Move [mpBegin, pCopyEnd) to p.
eastl::destruct(mpBegin, mpEnd);
if((uintptr_t)mpBegin != (uintptr_t)mBuffer.buffer)
DoFree(mpBegin, (size_type)(internalCapacityPtr() - mpBegin));
mpEnd = pNewData + (pCopyEnd - mpBegin);
mpBegin = pNewData;
internalCapacityPtr() = mpBegin + n;
} // Else the new capacity would be within our fixed buffer.
else if(n < nPrevSize) // If the newly requested capacity is less than our size, we do what vector::set_capacity does and resize, even though we actually aren't reducing the capacity.
resize(n);
}
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename Allocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, Allocator>::clear(bool freeOverflow)
{
base_type::clear();
if (freeOverflow && mpBegin != (value_type*)&mBuffer.buffer[0])
{
EASTLFree(get_allocator(), mpBegin, (internalCapacityPtr() - mpBegin) * sizeof(T));
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
}
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::reset_lose_memory()
{
mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0];
internalCapacityPtr() = mpBegin + nodeCount;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::size_type
fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::max_size() const
{
return kMaxSize;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::full() const
{
// If size >= capacity, then we are definitely full.
// Also, if our size is smaller but we've switched away from mBuffer due to a previous overflow, then we are considered full.
return ((size_t)(mpEnd - mpBegin) >= kMaxSize) || ((void*)mpBegin != (void*)mBuffer.buffer);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::has_overflowed() const
{
// This will be incorrect for the case that bOverflowEnabled is true and the container was resized
// down to a small size where the fixed buffer could take over ownership of the data again.
// The only simple fix for this is to take on another member variable which tracks whether this overflow
// has occurred at some point in the past.
return ((void*)mpBegin != (void*)mBuffer.buffer);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline bool fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::can_overflow() const
{
return bEnableOverflow;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void* fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::push_back_uninitialized()
{
return DoPushBackUninitialized(typename type_select<bEnableOverflow, true_type, false_type>::type());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void* fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::DoPushBackUninitialized(true_type)
{
return base_type::push_back_uninitialized();
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void* fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::DoPushBackUninitialized(false_type)
{
EASTL_ASSERT(mpEnd < internalCapacityPtr());
return mpEnd++;
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::push_back(const value_type& value)
{
DoPushBack(typename type_select<bEnableOverflow, true_type, false_type>::type(), value);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::DoPushBack(true_type, const value_type& value)
{
base_type::push_back(value);
}
// This template specializes for overflow NOT enabled.
// In this configuration, there is no need for the heavy weight push_back() which tests to see if the container should grow (it never will)
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::DoPushBack(false_type, const value_type& value)
{
EASTL_ASSERT(mpEnd < internalCapacityPtr());
::new((void*)mpEnd++) value_type(value);
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::reference fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::push_back()
{
return DoPushBack(typename type_select<bEnableOverflow, true_type, false_type>::type());
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::reference fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::DoPushBack(true_type)
{
return base_type::push_back();
}
// This template specializes for overflow NOT enabled.
// In this configuration, there is no need for the heavy weight push_back() which tests to see if the container should grow (it never will)
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::reference fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::DoPushBack(false_type)
{
EASTL_ASSERT(mpEnd < internalCapacityPtr());
::new((void*)mpEnd++) value_type; // Note that this isn't value_type() as that syntax doesn't work on all compilers for POD types.
return *(mpEnd - 1); // Same as return back();
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::push_back(value_type&& value)
{
DoPushBackMove(typename type_select<bEnableOverflow, true_type, false_type>::type(), eastl::move(value));
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::DoPushBackMove(true_type, value_type&& value)
{
base_type::push_back(eastl::move(value)); // This will call vector::push_back(value_type &&), and possibly swap value with *mpEnd.
}
// This template specializes for overflow NOT enabled.
// In this configuration, there is no need for the heavy weight push_back() which tests to see if the container should grow (it never will)
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::DoPushBackMove(false_type, value_type&& value)
{
EASTL_ASSERT(mpEnd < internalCapacityPtr());
::new((void*)mpEnd++) value_type(eastl::move(value)); // This will call the value_type(value_type&&) constructor, and possibly swap value with *mpEnd.
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline const typename fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::overflow_allocator_type&
fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::get_overflow_allocator() const EA_NOEXCEPT
{
return get_allocator().get_overflow_allocator();
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline typename fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::overflow_allocator_type&
fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::get_overflow_allocator() EA_NOEXCEPT
{
return get_allocator().get_overflow_allocator();
}
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void
fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>::set_overflow_allocator(const overflow_allocator_type& allocator)
{
get_allocator().set_overflow_allocator(allocator);
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
// operator ==, !=, <, >, <=, >= come from the vector implementations.
template <typename T, size_t nodeCount, bool bEnableOverflow, typename OverflowAllocator>
inline void swap(fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>& a,
fixed_vector<T, nodeCount, bEnableOverflow, OverflowAllocator>& b)
{
// Fixed containers use a special swap that can deal with excessively large buffers.
eastl::fixed_swap(a, b);
}
} // namespace eastl
#endif // Header include guard
File diff suppressed because it is too large Load Diff
+636
View File
@@ -0,0 +1,636 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file is based on the TR1 (technical report 1) reference implementation
// of the unordered_set/unordered_map C++ classes as of about 4/2005. Most likely
// many or all C++ library vendors' implementations of this classes will be
// based off of the reference version and so will look pretty similar to this
// file as well as other vendors' versions.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_HASH_MAP_H
#define EASTL_HASH_MAP_H
#include <EASTL/internal/config.h>
#include <EASTL/internal/hashtable.h>
#include <EASTL/functional.h>
#include <EASTL/utility.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_HASH_MAP_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
///
#ifndef EASTL_HASH_MAP_DEFAULT_NAME
#define EASTL_HASH_MAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " hash_map" // Unless the user overrides something, this is "EASTL hash_map".
#endif
/// EASTL_HASH_MULTIMAP_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
///
#ifndef EASTL_HASH_MULTIMAP_DEFAULT_NAME
#define EASTL_HASH_MULTIMAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " hash_multimap" // Unless the user overrides something, this is "EASTL hash_multimap".
#endif
/// EASTL_HASH_MAP_DEFAULT_ALLOCATOR
///
#ifndef EASTL_HASH_MAP_DEFAULT_ALLOCATOR
#define EASTL_HASH_MAP_DEFAULT_ALLOCATOR allocator_type(EASTL_HASH_MAP_DEFAULT_NAME)
#endif
/// EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR
///
#ifndef EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR
#define EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR allocator_type(EASTL_HASH_MULTIMAP_DEFAULT_NAME)
#endif
/// hash_map
///
/// Implements a hash_map, which is a hashed associative container.
/// Lookups are O(1) (that is, they are fast) but the container is
/// not sorted. Note that lookups are only O(1) if the hash table
/// is well-distributed (non-colliding). The lookup approaches
/// O(n) behavior as the table becomes increasingly poorly distributed.
///
/// set_max_load_factor
/// If you want to make a hashtable never increase its bucket usage,
/// call set_max_load_factor with a very high value such as 100000.f.
///
/// bCacheHashCode
/// We provide the boolean bCacheHashCode template parameter in order
/// to allow the storing of the hash code of the key within the map.
/// When this option is disabled, the rehashing of the table will
/// call the hash function on the key. Setting bCacheHashCode to true
/// is useful for cases whereby the calculation of the hash value for
/// a contained object is very expensive.
///
/// find_as
/// In order to support the ability to have a hashtable of strings but
/// be able to do efficiently lookups via char pointers (i.e. so they
/// aren't converted to string objects), we provide the find_as
/// function. This function allows you to do a find with a key of a
/// type other than the hashtable key type.
///
/// Example find_as usage:
/// hash_map<string, int> hashMap;
/// i = hashMap.find_as("hello"); // Use default hash and compare.
///
/// Example find_as usage (namespaces omitted for brevity):
/// hash_map<string, int> hashMap;
/// i = hashMap.find_as("hello", hash<char*>(), equal_to_2<string, char*>());
///
template <typename Key, typename T, typename Hash = eastl::hash<Key>, typename Predicate = eastl::equal_to<Key>,
typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false>
class hash_map
: public hashtable<Key, eastl::pair<const Key, T>, Allocator, eastl::use_first<eastl::pair<const Key, T> >, Predicate,
Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, true>
{
public:
typedef hashtable<Key, eastl::pair<const Key, T>, Allocator,
eastl::use_first<eastl::pair<const Key, T> >,
Predicate, Hash, mod_range_hashing, default_ranged_hash,
prime_rehash_policy, bCacheHashCode, true, true> base_type;
typedef hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::key_type key_type;
typedef T mapped_type;
typedef typename base_type::value_type value_type; // NOTE: 'value_type = pair<const key_type, mapped_type>'.
typedef typename base_type::allocator_type allocator_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::insert_return_type insert_return_type;
typedef typename base_type::iterator iterator;
typedef typename base_type::const_iterator const_iterator;
using base_type::insert;
public:
/// hash_map
///
/// Default constructor.
///
hash_map()
: this_type(EASTL_HASH_MAP_DEFAULT_ALLOCATOR)
{
// Empty
}
/// hash_map
///
/// Constructor which creates an empty container with allocator.
///
explicit hash_map(const allocator_type& allocator)
: base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(),
Predicate(), eastl::use_first<eastl::pair<const Key, T> >(), allocator)
{
// Empty
}
/// hash_map
///
/// Constructor which creates an empty container, but start with nBucketCount buckets.
/// We default to a small nBucketCount value, though the user really should manually
/// specify an appropriate value in order to prevent memory from being reallocated.
///
explicit hash_map(size_type nBucketCount, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MAP_DEFAULT_ALLOCATOR)
: base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(),
predicate, eastl::use_first<eastl::pair<const Key, T> >(), allocator)
{
// Empty
}
hash_map(const this_type& x)
: base_type(x)
{
}
hash_map(this_type&& x)
: base_type(eastl::move(x))
{
}
hash_map(this_type&& x, const allocator_type& allocator)
: base_type(eastl::move(x), allocator)
{
}
/// hash_map
///
/// initializer_list-based constructor.
/// Allows for initializing with brace values (e.g. hash_map<int, char*> hm = { {3,"c"}, {4,"d"}, {5,"e"} }; )
///
hash_map(std::initializer_list<value_type> ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MAP_DEFAULT_ALLOCATOR)
: base_type(ilist.begin(), ilist.end(), nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(),
predicate, eastl::use_first<eastl::pair<const Key, T> >(), allocator)
{
// Empty
}
/// hash_map
///
/// An input bucket count of <= 1 causes the bucket count to be equal to the number of
/// elements in the input range.
///
template <typename ForwardIterator>
hash_map(ForwardIterator first, ForwardIterator last, size_type nBucketCount = 0, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MAP_DEFAULT_ALLOCATOR)
: base_type(first, last, nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(),
predicate, eastl::use_first<eastl::pair<const Key, T> >(), allocator)
{
// Empty
}
this_type& operator=(const this_type& x)
{
return static_cast<this_type&>(base_type::operator=(x));
}
this_type& operator=(std::initializer_list<value_type> ilist)
{
return static_cast<this_type&>(base_type::operator=(ilist));
}
this_type& operator=(this_type&& x)
{
return static_cast<this_type&>(base_type::operator=(eastl::move(x)));
}
/// insert
///
/// This is an extension to the C++ standard. We insert a default-constructed
/// element with the given key. The reason for this is that we can avoid the
/// potentially expensive operation of creating and/or copying a mapped_type
/// object on the stack.
insert_return_type insert(const key_type& key)
{
return base_type::DoInsertKey(true_type(), key);
}
T& at(const key_type& k)
{
iterator it = base_type::find(k);
if (it == base_type::end())
{
#if EASTL_EXCEPTIONS_ENABLED
// throw exeption if exceptions enabled
throw std::out_of_range("invalid hash_map<K, T> key");
#else
// assert false if asserts enabled
EASTL_ASSERT_MSG(false, "invalid hash_map<K, T> key");
#endif
}
// undefined behaviour if exceptions and asserts are disabled and it == end()
return it->second;
}
const T& at(const key_type& k) const
{
const_iterator it = base_type::find(k);
if (it == base_type::end())
{
#if EASTL_EXCEPTIONS_ENABLED
// throw exeption if exceptions enabled
throw std::out_of_range("invalid hash_map<K, T> key");
#else
// assert false if asserts enabled
EASTL_ASSERT_MSG(false, "invalid hash_map<K, T> key");
#endif
}
// undefined behaviour if exceptions and asserts are disabled and it == end()
return it->second;
}
insert_return_type insert(key_type&& key)
{
return base_type::DoInsertKey(true_type(), eastl::move(key));
}
mapped_type& operator[](const key_type& key)
{
return (*base_type::DoInsertKey(true_type(), key).first).second;
// Slower reference version:
//const typename base_type::iterator it = base_type::find(key);
//if(it != base_type::end())
// return (*it).second;
//return (*base_type::insert(value_type(key, mapped_type())).first).second;
}
mapped_type& operator[](key_type&& key)
{
// The Standard states that this function "inserts the value value_type(std::move(key), mapped_type())"
return (*base_type::DoInsertKey(true_type(), eastl::move(key)).first).second;
}
// try_emplace API added in C++17
template <class... Args>
inline insert_return_type try_emplace(const key_type& k, Args&&... args)
{
return try_emplace_forwarding(k, eastl::forward<Args>(args)...);
}
template <class... Args>
inline insert_return_type try_emplace(key_type&& k, Args&&... args) {
return try_emplace_forwarding(eastl::move(k), eastl::forward<Args>(args)...);
}
template <class... Args>
inline iterator try_emplace(const_iterator, const key_type& k, Args&&... args) {
// Currently, the first parameter is ignored.
insert_return_type result = try_emplace(k, eastl::forward<Args>(args)...);
return base_type::DoGetResultIterator(true_type(), result);
}
template <class... Args>
inline iterator try_emplace(const_iterator, key_type&& k, Args&&... args) {
// Currently, the first parameter is ignored.
insert_return_type result = try_emplace(eastl::move(k), eastl::forward<Args>(args)...);
return base_type::DoGetResultIterator(true_type(), result);
}
private:
template <class K, class... Args>
insert_return_type try_emplace_forwarding(K&& k, Args&&... args)
{
const auto key_data = base_type::DoFindKeyData(k);
if (key_data.node)
{ // Node exists, no insertion needed.
return eastl::pair<iterator, bool>(
iterator(key_data.node, base_type::mpBucketArray + key_data.bucket_index), false);
}
else
{
node_type* const pNodeNew =
base_type::DoAllocateNode(piecewise_construct, eastl::forward_as_tuple(eastl::forward<K>(k)),
forward_as_tuple(eastl::forward<Args>(args)...));
// the key might have been moved from above, so we can't use `k` anymore.
const auto& key = base_type::mExtractKey(pNodeNew->mValue);
return base_type::template DoInsertUniqueNode<true>(key, key_data.code, key_data.bucket_index, pNodeNew);
}
}
}; // hash_map
/// hash_map erase_if
///
/// https://en.cppreference.com/w/cpp/container/unordered_map/erase_if
template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode, typename UserPredicate>
typename eastl::hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>::size_type erase_if(eastl::hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate)
{
auto oldSize = c.size();
// Erases all elements that satisfy the predicate from the container.
for (auto i = c.begin(), last = c.end(); i != last;)
{
if (predicate(*i))
{
i = c.erase(i);
}
else
{
++i;
}
}
return oldSize - c.size();
}
/// hash_multimap
///
/// Implements a hash_multimap, which is the same thing as a hash_map
/// except that contained elements need not be unique. See the
/// documentation for hash_set for details.
///
template <typename Key, typename T, typename Hash = eastl::hash<Key>, typename Predicate = eastl::equal_to<Key>,
typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false>
class hash_multimap
: public hashtable<Key, eastl::pair<const Key, T>, Allocator, eastl::use_first<eastl::pair<const Key, T> >, Predicate,
Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, false>
{
public:
typedef hashtable<Key, eastl::pair<const Key, T>, Allocator,
eastl::use_first<eastl::pair<const Key, T> >,
Predicate, Hash, mod_range_hashing, default_ranged_hash,
prime_rehash_policy, bCacheHashCode, true, false> base_type;
typedef hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::key_type key_type;
typedef T mapped_type;
typedef typename base_type::value_type value_type; // Note that this is pair<const key_type, mapped_type>.
typedef typename base_type::allocator_type allocator_type;
typedef typename base_type::node_type node_type;
typedef typename base_type::insert_return_type insert_return_type;
typedef typename base_type::iterator iterator;
using base_type::insert;
private:
using base_type::insert_or_assign;
public:
/// hash_multimap
///
/// Default constructor.
///
explicit hash_multimap(const allocator_type& allocator = EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR)
: base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(),
Predicate(), eastl::use_first<eastl::pair<const Key, T> >(), allocator)
{
// Empty
}
/// hash_multimap
///
/// Constructor which creates an empty container, but start with nBucketCount buckets.
/// We default to a small nBucketCount value, though the user really should manually
/// specify an appropriate value in order to prevent memory from being reallocated.
///
explicit hash_multimap(size_type nBucketCount, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR)
: base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(),
predicate, eastl::use_first<eastl::pair<const Key, T> >(), allocator)
{
// Empty
}
hash_multimap(const this_type& x)
: base_type(x)
{
}
hash_multimap(this_type&& x)
: base_type(eastl::move(x))
{
}
hash_multimap(this_type&& x, const allocator_type& allocator)
: base_type(eastl::move(x), allocator)
{
}
/// hash_multimap
///
/// initializer_list-based constructor.
/// Allows for initializing with brace values (e.g. hash_multimap<int, char*> hm = { {3,"c"}, {3,"C"}, {4,"d"} }; )
///
hash_multimap(std::initializer_list<value_type> ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR)
: base_type(ilist.begin(), ilist.end(), nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(),
predicate, eastl::use_first<eastl::pair<const Key, T> >(), allocator)
{
// Empty
}
/// hash_multimap
///
/// An input bucket count of <= 1 causes the bucket count to be equal to the number of
/// elements in the input range.
///
template <typename ForwardIterator>
hash_multimap(ForwardIterator first, ForwardIterator last, size_type nBucketCount = 0, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTIMAP_DEFAULT_ALLOCATOR)
: base_type(first, last, nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(),
predicate, eastl::use_first<eastl::pair<const Key, T> >(), allocator)
{
// Empty
}
this_type& operator=(const this_type& x)
{
return static_cast<this_type&>(base_type::operator=(x));
}
this_type& operator=(std::initializer_list<value_type> ilist)
{
return static_cast<this_type&>(base_type::operator=(ilist));
}
this_type& operator=(this_type&& x)
{
return static_cast<this_type&>(base_type::operator=(eastl::move(x)));
}
/// insert
///
/// This is an extension to the C++ standard. We insert a default-constructed
/// element with the given key. The reason for this is that we can avoid the
/// potentially expensive operation of creating and/or copying a mapped_type
/// object on the stack.
insert_return_type insert(const key_type& key)
{
return base_type::DoInsertKey(false_type(), key);
}
insert_return_type insert(key_type&& key)
{
return base_type::DoInsertKey(false_type(), eastl::move(key));
}
}; // hash_multimap
/// hash_multimap erase_if
///
/// https://en.cppreference.com/w/cpp/container/unordered_multimap/erase_if
template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode, typename UserPredicate>
typename eastl::hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>::size_type erase_if(eastl::hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate)
{
auto oldSize = c.size();
// Erases all elements that satisfy the predicate from the container.
for (auto i = c.begin(), last = c.end(); i != last;)
{
if (predicate(*i))
{
i = c.erase(i);
}
else
{
++i;
}
}
return oldSize - c.size();
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode>
inline bool operator==(const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a,
const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& b)
{
typedef typename hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>::const_iterator const_iterator;
// We implement branching with the assumption that the return value is usually false.
if(a.size() != b.size())
return false;
// For map (with its unique keys), we need only test that each element in a can be found in b,
// as there can be only one such pairing per element. multimap needs to do a something more elaborate.
for(const_iterator ai = a.begin(), aiEnd = a.end(), biEnd = b.end(); ai != aiEnd; ++ai)
{
const_iterator bi = b.find(ai->first);
if((bi == biEnd) || !(*ai == *bi)) // We have to compare the values, because lookups are done by keys alone but the full value_type of a map is a key/value pair.
return false; // It's possible that two elements in the two containers have identical keys but different values.
}
return true;
}
#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode>
inline bool operator!=(const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a,
const hash_map<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& b)
{
return !(a == b);
}
#endif
template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode>
inline bool operator==(const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a,
const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& b)
{
typedef typename hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>::const_iterator const_iterator;
typedef typename eastl::iterator_traits<const_iterator>::difference_type difference_type;
// We implement branching with the assumption that the return value is usually false.
if(a.size() != b.size())
return false;
// We can't simply search for each element of a in b, as it may be that the bucket for
// two elements in a has those same two elements in b but in different order (which should
// still result in equality). Also it's possible that one bucket in a has two elements which
// both match a solitary element in the equivalent bucket in b (which shouldn't result in equality).
eastl::pair<const_iterator, const_iterator> aRange;
eastl::pair<const_iterator, const_iterator> bRange;
for(const_iterator ai = a.begin(), aiEnd = a.end(); ai != aiEnd; ai = aRange.second) // For each element in a...
{
aRange = a.equal_range(ai->first); // Get the range of elements in a that are equal to ai.
bRange = b.equal_range(ai->first); // Get the range of elements in b that are equal to ai.
// We need to verify that aRange == bRange. First make sure the range sizes are equivalent...
const difference_type aDistance = eastl::distance(aRange.first, aRange.second);
const difference_type bDistance = eastl::distance(bRange.first, bRange.second);
if(aDistance != bDistance)
return false;
// At this point, aDistance > 0 and aDistance == bDistance.
// Implement a fast pathway for the case that there's just a single element.
if(aDistance == 1)
{
if(!(*aRange.first == *bRange.first)) // We have to compare the values, because lookups are done by keys alone but the full value_type of a map is a key/value pair.
return false; // It's possible that two elements in the two containers have identical keys but different values. Ditto for the permutation case below.
}
else
{
// Check to see if these aRange and bRange are any permutation of each other.
// This check gets slower as there are more elements in the range.
if(!eastl::is_permutation(aRange.first, aRange.second, bRange.first))
return false;
}
}
return true;
}
#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
template <typename Key, typename T, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode>
inline bool operator!=(const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& a,
const hash_multimap<Key, T, Hash, Predicate, Allocator, bCacheHashCode>& b)
{
return !(a == b);
}
#endif
} // namespace eastl
#endif // Header include guard
+486
View File
@@ -0,0 +1,486 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file is based on the TR1 (technical report 1) reference implementation
// of the unordered_set/unordered_map C++ classes as of about 4/2005. Most likely
// many or all C++ library vendors' implementations of this classes will be
// based off of the reference version and so will look pretty similar to this
// file as well as other vendors' versions.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_HASH_SET_H
#define EASTL_HASH_SET_H
#include <EASTL/internal/config.h>
#include <EASTL/internal/hashtable.h>
#include <EASTL/functional.h>
#include <EASTL/utility.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
/// EASTL_HASH_SET_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
///
#ifndef EASTL_HASH_SET_DEFAULT_NAME
#define EASTL_HASH_SET_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " hash_set" // Unless the user overrides something, this is "EASTL hash_set".
#endif
/// EASTL_HASH_MULTISET_DEFAULT_NAME
///
/// Defines a default container name in the absence of a user-provided name.
///
#ifndef EASTL_HASH_MULTISET_DEFAULT_NAME
#define EASTL_HASH_MULTISET_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " hash_multiset" // Unless the user overrides something, this is "EASTL hash_multiset".
#endif
/// EASTL_HASH_SET_DEFAULT_ALLOCATOR
///
#ifndef EASTL_HASH_SET_DEFAULT_ALLOCATOR
#define EASTL_HASH_SET_DEFAULT_ALLOCATOR allocator_type(EASTL_HASH_SET_DEFAULT_NAME)
#endif
/// EASTL_HASH_MULTISET_DEFAULT_ALLOCATOR
///
#ifndef EASTL_HASH_MULTISET_DEFAULT_ALLOCATOR
#define EASTL_HASH_MULTISET_DEFAULT_ALLOCATOR allocator_type(EASTL_HASH_MULTISET_DEFAULT_NAME)
#endif
/// hash_set
///
/// Implements a hash_set, which is a hashed unique-item container.
/// Lookups are O(1) (that is, they are fast) but the container is
/// not sorted. Note that lookups are only O(1) if the hash table
/// is well-distributed (non-colliding). The lookup approaches
/// O(n) behavior as the table becomes increasingly poorly distributed.
///
/// set_max_load_factor
/// If you want to make a hashtable never increase its bucket usage,
/// call set_max_load_factor with a very high value such as 100000.f.
///
/// bCacheHashCode
/// We provide the boolean bCacheHashCode template parameter in order
/// to allow the storing of the hash code of the key within the map.
/// When this option is disabled, the rehashing of the table will
/// call the hash function on the key. Setting bCacheHashCode to true
/// is useful for cases whereby the calculation of the hash value for
/// a contained object is very expensive.
///
/// find_as
/// In order to support the ability to have a hashtable of strings but
/// be able to do efficiently lookups via char pointers (i.e. so they
/// aren't converted to string objects), we provide the find_as
/// function. This function allows you to do a find with a key of a
/// type other than the hashtable key type.
///
/// Example find_as usage:
/// hash_set<string> hashSet;
/// i = hashSet.find_as("hello"); // Use default hash and compare.
///
/// Example find_as usage (namespaces omitted for brevity):
/// hash_set<string> hashSet;
/// i = hashSet.find_as("hello", hash<char*>(), equal_to_2<string, char*>());
///
template <typename Value, typename Hash = eastl::hash<Value>, typename Predicate = eastl::equal_to<Value>,
typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false>
class hash_set
: public hashtable<Value, Value, Allocator, eastl::use_self<Value>, Predicate,
Hash, mod_range_hashing, default_ranged_hash,
prime_rehash_policy, bCacheHashCode, false, true>
{
public:
typedef hashtable<Value, Value, Allocator, eastl::use_self<Value>, Predicate,
Hash, mod_range_hashing, default_ranged_hash,
prime_rehash_policy, bCacheHashCode, false, true> base_type;
typedef hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::allocator_type allocator_type;
typedef typename base_type::node_type node_type;
public:
/// hash_set
///
/// Default constructor.
///
hash_set()
: this_type(EASTL_HASH_SET_DEFAULT_ALLOCATOR)
{
// Empty
}
/// hash_set
///
/// Constructor which creates an empty container with allocator.
///
explicit hash_set(const allocator_type& allocator)
: base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_self<Value>(), allocator)
{
// Empty
}
/// hash_set
///
/// Constructor which creates an empty container, but start with nBucketCount buckets.
/// We default to a small nBucketCount value, though the user really should manually
/// specify an appropriate value in order to prevent memory from being reallocated.
///
explicit hash_set(size_type nBucketCount, const Hash& hashFunction = Hash(), const Predicate& predicate = Predicate(),
const allocator_type& allocator = EASTL_HASH_SET_DEFAULT_ALLOCATOR)
: base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_self<Value>(), allocator)
{
// Empty
}
hash_set(const this_type& x)
: base_type(x)
{
}
hash_set(this_type&& x)
: base_type(eastl::move(x))
{
}
hash_set(this_type&& x, const allocator_type& allocator)
: base_type(eastl::move(x), allocator)
{
}
/// hash_set
///
/// initializer_list-based constructor.
/// Allows for initializing with brace values (e.g. hash_set<int> hs = { 3, 4, 5, }; )
///
hash_set(std::initializer_list<value_type> ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_SET_DEFAULT_ALLOCATOR)
: base_type(ilist.begin(), ilist.end(), nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_self<Value>(), allocator)
{
// Empty
}
/// hash_set
///
/// An input bucket count of <= 1 causes the bucket count to be equal to the number of
/// elements in the input range.
///
template <typename FowardIterator>
hash_set(FowardIterator first, FowardIterator last, size_type nBucketCount = 0, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_SET_DEFAULT_ALLOCATOR)
: base_type(first, last, nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_self<Value>(), allocator)
{
// Empty
}
this_type& operator=(const this_type& x)
{
return static_cast<this_type&>(base_type::operator=(x));
}
this_type& operator=(std::initializer_list<value_type> ilist)
{
return static_cast<this_type&>(base_type::operator=(ilist));
}
this_type& operator=(this_type&& x)
{
return static_cast<this_type&>(base_type::operator=(eastl::move(x)));
}
}; // hash_set
/// hash_set erase_if
///
/// https://en.cppreference.com/w/cpp/container/unordered_set/erase_if
template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode, typename UserPredicate>
typename eastl::hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>::size_type erase_if(eastl::hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate)
{
auto oldSize = c.size();
// Erases all elements that satisfy the predicate pred from the container.
for (auto i = c.begin(), last = c.end(); i != last;)
{
if (predicate(*i))
{
i = c.erase(i);
}
else
{
++i;
}
}
return oldSize - c.size();
}
/// hash_multiset
///
/// Implements a hash_multiset, which is the same thing as a hash_set
/// except that contained elements need not be unique. See the documentation
/// for hash_set for details.
///
template <typename Value, typename Hash = eastl::hash<Value>, typename Predicate = eastl::equal_to<Value>,
typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false>
class hash_multiset
: public hashtable<Value, Value, Allocator, eastl::use_self<Value>, Predicate,
Hash, mod_range_hashing, default_ranged_hash,
prime_rehash_policy, bCacheHashCode, false, false>
{
public:
typedef hashtable<Value, Value, Allocator, eastl::use_self<Value>, Predicate,
Hash, mod_range_hashing, default_ranged_hash,
prime_rehash_policy, bCacheHashCode, false, false> base_type;
typedef hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode> this_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::value_type value_type;
typedef typename base_type::allocator_type allocator_type;
typedef typename base_type::node_type node_type;
public:
/// hash_multiset
///
/// Default constructor.
///
explicit hash_multiset(const allocator_type& allocator = EASTL_HASH_MULTISET_DEFAULT_ALLOCATOR)
: base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_self<Value>(), allocator)
{
// Empty
}
/// hash_multiset
///
/// Constructor which creates an empty container, but start with nBucketCount buckets.
/// We default to a small nBucketCount value, though the user really should manually
/// specify an appropriate value in order to prevent memory from being reallocated.
///
explicit hash_multiset(size_type nBucketCount, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTISET_DEFAULT_ALLOCATOR)
: base_type(nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_self<Value>(), allocator)
{
// Empty
}
hash_multiset(const this_type& x)
: base_type(x)
{
}
hash_multiset(this_type&& x)
: base_type(eastl::move(x))
{
}
hash_multiset(this_type&& x, const allocator_type& allocator)
: base_type(eastl::move(x), allocator)
{
}
/// hash_multiset
///
/// initializer_list-based constructor.
/// Allows for initializing with brace values (e.g. hash_set<int> hs = { 3, 3, 4, }; )
///
hash_multiset(std::initializer_list<value_type> ilist, size_type nBucketCount = 0, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTISET_DEFAULT_ALLOCATOR)
: base_type(ilist.begin(), ilist.end(), nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_self<Value>(), allocator)
{
// Empty
}
/// hash_multiset
///
/// An input bucket count of <= 1 causes the bucket count to be equal to the number of
/// elements in the input range.
///
template <typename FowardIterator>
hash_multiset(FowardIterator first, FowardIterator last, size_type nBucketCount = 0, const Hash& hashFunction = Hash(),
const Predicate& predicate = Predicate(), const allocator_type& allocator = EASTL_HASH_MULTISET_DEFAULT_ALLOCATOR)
: base_type(first, last, nBucketCount, hashFunction, mod_range_hashing(), default_ranged_hash(), predicate, eastl::use_self<Value>(), allocator)
{
// Empty
}
this_type& operator=(const this_type& x)
{
return static_cast<this_type&>(base_type::operator=(x));
}
this_type& operator=(std::initializer_list<value_type> ilist)
{
return static_cast<this_type&>(base_type::operator=(ilist));
}
this_type& operator=(this_type&& x)
{
return static_cast<this_type&>(base_type::operator=(eastl::move(x)));
}
}; // hash_multiset
/// hash_multiset erase_if
///
/// https://en.cppreference.com/w/cpp/container/unordered_multiset/erase_if
template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode, typename UserPredicate>
typename eastl::hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>::size_type erase_if(eastl::hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& c, UserPredicate predicate)
{
auto oldSize = c.size();
// Erases all elements that satisfy the predicate pred from the container.
for (auto i = c.begin(), last = c.end(); i != last;)
{
if (predicate(*i))
{
i = c.erase(i);
}
else
{
++i;
}
}
return oldSize - c.size();
}
///////////////////////////////////////////////////////////////////////
// global operators
///////////////////////////////////////////////////////////////////////
template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode>
inline bool operator==(const hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>& a,
const hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>& b)
{
typedef typename hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>::const_iterator const_iterator;
// We implement branching with the assumption that the return value is usually false.
if(a.size() != b.size())
return false;
// For set (with its unique keys), we need only test that each element in a can be found in b,
// as there can be only one such pairing per element. multiset needs to do a something more elaborate.
for(const_iterator ai = a.begin(), aiEnd = a.end(), biEnd = b.end(); ai != aiEnd; ++ai)
{
const_iterator bi = b.find(*ai);
if((bi == biEnd) || !(*ai == *bi)) // We have to compare values in addition to making sure the lookups succeeded. This is because the lookup is done via the user-supplised Predicate
return false; // which isn't strictly required to be identical to the Value operator==, though 99% of the time it will be so.
}
return true;
}
#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode>
inline bool operator!=(const hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>& a,
const hash_set<Value, Hash, Predicate, Allocator, bCacheHashCode>& b)
{
return !(a == b);
}
#endif
template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode>
inline bool operator==(const hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& a,
const hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& b)
{
typedef typename hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>::const_iterator const_iterator;
typedef typename eastl::iterator_traits<const_iterator>::difference_type difference_type;
// We implement branching with the assumption that the return value is usually false.
if(a.size() != b.size())
return false;
// We can't simply search for each element of a in b, as it may be that the bucket for
// two elements in a has those same two elements in b but in different order (which should
// still result in equality). Also it's possible that one bucket in a has two elements which
// both match a solitary element in the equivalent bucket in b (which shouldn't result in equality).
eastl::pair<const_iterator, const_iterator> aRange;
eastl::pair<const_iterator, const_iterator> bRange;
for(const_iterator ai = a.begin(), aiEnd = a.end(); ai != aiEnd; ai = aRange.second) // For each element in a...
{
aRange = a.equal_range(*ai); // Get the range of elements in a that are equal to ai.
bRange = b.equal_range(*ai); // Get the range of elements in b that are equal to ai.
// We need to verify that aRange == bRange. First make sure the range sizes are equivalent...
const difference_type aDistance = eastl::distance(aRange.first, aRange.second);
const difference_type bDistance = eastl::distance(bRange.first, bRange.second);
if(aDistance != bDistance)
return false;
// At this point, aDistance > 0 and aDistance == bDistance.
// Implement a fast pathway for the case that there's just a single element.
if(aDistance == 1)
{
if(!(*aRange.first == *bRange.first)) // We have to compare values in addition to making sure the distance (element count) was equal. This is because the lookup is done via the user-supplised Predicate
return false; // which isn't strictly required to be identical to the Value operator==, though 99% of the time it will be so. Ditto for the is_permutation usage below.
}
else
{
// Check to see if these aRange and bRange are any permutation of each other.
// This check gets slower as there are more elements in the range.
if(!eastl::is_permutation(aRange.first, aRange.second, bRange.first))
return false;
}
}
return true;
}
#if !defined(EA_COMPILER_HAS_THREE_WAY_COMPARISON)
template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode>
inline bool operator!=(const hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& a,
const hash_multiset<Value, Hash, Predicate, Allocator, bCacheHashCode>& b)
{
return !(a == b);
}
#endif
} // namespace eastl
#endif // Header include guard
+685
View File
@@ -0,0 +1,685 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// This file implements heap functionality much like the std C++ heap algorithms.
// Such heaps are not the same thing as memory heaps or pools, but rather are
// semi-sorted random access containers which have the primary purpose of
// supporting the implementation of priority_queue and similar data structures.
//
// The primary distinctions between this heap functionality and std::heap are:
// - This heap exposes some extra functionality such as is_heap and change_heap.
// - This heap is more efficient than versions found in typical STL
// implementations such as STLPort, Microsoft, and Metrowerks. This comes
// about due to better use of array dereferencing and branch prediction.
// You should expect of 5-30%, depending on the usage and platform.
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// The publicly usable functions we define are:
// push_heap -- Adds an entry to a heap. Same as C++ std::push_heap.
// pop_heap -- Removes the top entry from a heap. Same as C++ std::pop_heap.
// make_heap -- Converts an array to a heap. Same as C++ std::make_heap.
// sort_heap -- Sorts a heap in place. Same as C++ std::sort_heap.
// remove_heap -- Removes an arbitrary entry from a heap.
// change_heap -- Changes the priority of an entry in the heap.
// is_heap -- Returns true if an array appears is in heap format. Same as C++11 std::is_heap.
// is_heap_until -- Returns largest part of the range which is a heap. Same as C++11 std::is_heap_until.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_HEAP_H
#define EASTL_HEAP_H
#include <EASTL/internal/config.h>
#include <EASTL/iterator.h>
#include <stddef.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
namespace eastl
{
///////////////////////////////////////////////////////////////////////
// promote_heap (internal function)
///////////////////////////////////////////////////////////////////////
template <typename RandomAccessIterator, typename Distance, typename T, typename ValueType>
inline void promote_heap_impl(RandomAccessIterator first, Distance topPosition, Distance position, T value)
{
for(Distance parentPosition = (position - 1) >> 1; // This formula assumes that (position > 0). // We use '>> 1' instead of '/ 2' because we have seen VC++ generate better code with >>.
(position > topPosition) && eastl::less<ValueType>()(*(first + parentPosition), value);
parentPosition = (position - 1) >> 1)
{
*(first + position) = eastl::forward<ValueType>(*(first + parentPosition)); // Swap the node with its parent.
position = parentPosition;
}
*(first + position) = eastl::forward<ValueType>(value);
}
/// promote_heap
///
/// Moves a value in the heap from a given position upward until
/// it is sorted correctly. It's kind of like bubble-sort, except that
/// instead of moving linearly from the back of a list to the front,
/// it moves from the bottom of the tree up the branches towards the
/// top. But otherwise is just like bubble-sort.
///
/// This function requires that the value argument refer to a value
/// that is currently not within the heap.
///
template <typename RandomAccessIterator, typename Distance, typename T>
inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, const T& value)
{
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
promote_heap_impl<RandomAccessIterator, Distance, const T&, const value_type>(first, topPosition, position, value);
}
/// promote_heap
///
/// Moves a value in the heap from a given position upward until
/// it is sorted correctly. It's kind of like bubble-sort, except that
/// instead of moving linearly from the back of a list to the front,
/// it moves from the bottom of the tree up the branches towards the
/// top. But otherwise is just like bubble-sort.
///
/// This function requires that the value argument refer to a value
/// that is currently not within the heap.
///
template <typename RandomAccessIterator, typename Distance, typename T>
inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, T&& value)
{
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
promote_heap_impl<RandomAccessIterator, Distance, T&&, value_type>(first, topPosition, position, eastl::forward<T>(value));
}
template <typename RandomAccessIterator, typename Distance, typename T, typename Compare, typename ValueType>
inline void promote_heap_impl(RandomAccessIterator first, Distance topPosition, Distance position, T value, Compare compare)
{
for(Distance parentPosition = (position - 1) >> 1; // This formula assumes that (position > 0). // We use '>> 1' instead of '/ 2' because we have seen VC++ generate better code with >>.
(position > topPosition) && compare(*(first + parentPosition), value);
parentPosition = (position - 1) >> 1)
{
*(first + position) = eastl::forward<ValueType>(*(first + parentPosition)); // Swap the node with its parent.
position = parentPosition;
}
*(first + position) = eastl::forward<ValueType>(value);
}
/// promote_heap
///
/// Takes a Compare(a, b) function (or function object) which returns true if a < b.
/// For example, you could use the standard 'less' comparison object.
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
/// This function requires that the value argument refer to a value
/// that is currently not within the heap.
///
template <typename RandomAccessIterator, typename Distance, typename T, typename Compare>
inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, const T& value, Compare compare)
{
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
promote_heap_impl<RandomAccessIterator, Distance, const T&, Compare, const value_type>(first, topPosition, position, value, compare);
}
/// promote_heap
///
/// Takes a Compare(a, b) function (or function object) which returns true if a < b.
/// For example, you could use the standard 'less' comparison object.
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
/// This function requires that the value argument refer to a value
/// that is currently not within the heap.
///
template <typename RandomAccessIterator, typename Distance, typename T, typename Compare>
inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, T&& value, Compare compare)
{
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
promote_heap_impl<RandomAccessIterator, Distance, T&&, Compare, value_type>(first, topPosition, position, eastl::forward<T>(value), compare);
}
///////////////////////////////////////////////////////////////////////
// adjust_heap (internal function)
///////////////////////////////////////////////////////////////////////
template <typename RandomAccessIterator, typename Distance, typename T, typename ValueType>
void adjust_heap_impl(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T value)
{
// We do the conventional approach of moving the position down to the
// bottom then inserting the value at the back and moving it up.
Distance childPosition = (2 * position) + 2;
for(; childPosition < heapSize; childPosition = (2 * childPosition) + 2)
{
if(eastl::less<ValueType>()(*(first + childPosition), *(first + (childPosition - 1)))) // Choose the larger of the two children.
--childPosition;
*(first + position) = eastl::forward<ValueType>(*(first + childPosition)); // Swap positions with this child.
position = childPosition;
}
if(childPosition == heapSize) // If we are at the very last index of the bottom...
{
*(first + position) = eastl::forward<ValueType>(*(first + (childPosition - 1)));
position = childPosition - 1;
}
eastl::promote_heap<RandomAccessIterator, Distance, T>(first, topPosition, position, eastl::forward<ValueType>(value));
}
/// adjust_heap
///
/// Given a position that has just been vacated, this function moves
/// new values into that vacated position appropriately. The value
/// argument is an entry which will be inserted into the heap after
/// we move nodes into the positions that were vacated.
///
/// This function requires that the value argument refer to a value
/// that is currently not within the heap.
///
template <typename RandomAccessIterator, typename Distance, typename T>
void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, const T& value)
{
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
adjust_heap_impl<RandomAccessIterator, Distance, const T&, const value_type>(first, topPosition, heapSize, position, eastl::forward<const T&>(value));
}
/// adjust_heap
///
/// Given a position that has just been vacated, this function moves
/// new values into that vacated position appropriately. The value
/// argument is an entry which will be inserted into the heap after
/// we move nodes into the positions that were vacated.
///
/// This function requires that the value argument refer to a value
/// that is currently not within the heap.
///
template <typename RandomAccessIterator, typename Distance, typename T>
void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T&& value)
{
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
adjust_heap_impl<RandomAccessIterator, Distance, T&&, value_type>(first, topPosition, heapSize, position, eastl::forward<T>(value));
}
template <typename RandomAccessIterator, typename Distance, typename T, typename Compare, typename ValueType>
void adjust_heap_impl(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T value, Compare compare)
{
// We do the conventional approach of moving the position down to the
// bottom then inserting the value at the back and moving it up.
Distance childPosition = (2 * position) + 2;
for(; childPosition < heapSize; childPosition = (2 * childPosition) + 2)
{
if(compare(*(first + childPosition), *(first + (childPosition - 1)))) // Choose the larger of the two children.
--childPosition;
*(first + position) = eastl::forward<ValueType>(*(first + childPosition)); // Swap positions with this child.
position = childPosition;
}
if(childPosition == heapSize) // If we are at the bottom...
{
*(first + position) = eastl::forward<ValueType>(*(first + (childPosition - 1)));
position = childPosition - 1;
}
eastl::promote_heap<RandomAccessIterator, Distance, T, Compare>(first, topPosition, position, eastl::forward<ValueType>(value), compare);
}
/// adjust_heap
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
/// This function requires that the value argument refer to a value
/// that is currently not within the heap.
///
template <typename RandomAccessIterator, typename Distance, typename T, typename Compare>
void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, const T& value, Compare compare)
{
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
adjust_heap_impl<RandomAccessIterator, Distance, const T&, Compare, const value_type>(first, topPosition, heapSize, position, eastl::forward<const T&>(value), compare);
}
/// adjust_heap
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
/// This function requires that the value argument refer to a value
/// that is currently not within the heap.
///
template <typename RandomAccessIterator, typename Distance, typename T, typename Compare>
void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T&& value, Compare compare)
{
typedef typename iterator_traits<RandomAccessIterator>::value_type value_type;
adjust_heap_impl<RandomAccessIterator, Distance, T&&, Compare, value_type>(first, topPosition, heapSize, position, eastl::forward<T>(value), compare);
}
///////////////////////////////////////////////////////////////////////
// push_heap
///////////////////////////////////////////////////////////////////////
/// push_heap
///
/// Adds an item to a heap (which is an array). The item necessarily
/// comes from the back of the heap (array). Thus, the insertion of a
/// new item in a heap is a two step process: push_back and push_heap.
///
/// Example usage:
/// vector<int> heap;
///
/// heap.push_back(3);
/// push_heap(heap.begin(), heap.end()); // Places '3' appropriately.
///
template <typename RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last)
{
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
const value_type tempBottom(eastl::forward<value_type>(*(last - 1)));
eastl::promote_heap<RandomAccessIterator, difference_type, value_type>
(first, (difference_type)0, (difference_type)(last - first - 1), eastl::forward<const value_type>(tempBottom));
}
/// push_heap
///
/// This version is useful for cases where your object comparison is unusual
/// or where you want to have the heap store pointers to objects instead of
/// storing the objects themselves (often in order to improve cache coherency
/// while doing sorting).
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
template <typename RandomAccessIterator, typename Compare>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
{
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
const value_type tempBottom(*(last - 1));
eastl::promote_heap<RandomAccessIterator, difference_type, value_type, Compare>
(first, (difference_type)0, (difference_type)(last - first - 1), tempBottom, compare);
}
///////////////////////////////////////////////////////////////////////
// pop_heap
///////////////////////////////////////////////////////////////////////
/// pop_heap
///
/// Removes the first item from the heap (which is an array), and adjusts
/// the heap so that the highest priority item becomes the new first item.
///
/// Example usage:
/// vector<int> heap;
///
/// heap.push_back(2);
/// heap.push_back(3);
/// heap.push_back(1);
/// <use heap[0], which is the highest priority item in the heap>
/// pop_heap(heap.begin(), heap.end()); // Moves heap[0] to the back of the heap and adjusts the heap.
/// heap.pop_back(); // Remove value that was just at the top of the heap
///
template <typename RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last)
{
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
value_type tempBottom(eastl::forward<value_type>(*(last - 1)));
*(last - 1) = eastl::forward<value_type>(*first);
eastl::adjust_heap<RandomAccessIterator, difference_type, value_type>
(first, (difference_type)0, (difference_type)(last - first - 1), 0, eastl::forward<value_type>(tempBottom));
}
/// pop_heap
///
/// This version is useful for cases where your object comparison is unusual
/// or where you want to have the heap store pointers to objects instead of
/// storing the objects themselves (often in order to improve cache coherency
/// while doing sorting).
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
template <typename RandomAccessIterator, typename Compare>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
{
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
value_type tempBottom(eastl::forward<value_type>(*(last - 1)));
*(last - 1) = eastl::forward<value_type>(*first);
eastl::adjust_heap<RandomAccessIterator, difference_type, value_type, Compare>
(first, (difference_type)0, (difference_type)(last - first - 1), 0, eastl::forward<value_type>(tempBottom), compare);
}
///////////////////////////////////////////////////////////////////////
// make_heap
///////////////////////////////////////////////////////////////////////
/// make_heap
///
/// Given an array, this function converts it into heap format.
/// The complexity is O(n), where n is count of the range.
/// The input range is not required to be in any order.
///
template <typename RandomAccessIterator>
void make_heap(RandomAccessIterator first, RandomAccessIterator last)
{
// We do bottom-up heap construction as per Sedgewick. Such construction is O(n).
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
const difference_type heapSize = last - first;
if(heapSize >= 2) // If there is anything to do... (we need this check because otherwise the math fails below).
{
difference_type parentPosition = ((heapSize - 2) >> 1) + 1; // We use '>> 1' instead of '/ 2' because we have seen VC++ generate better code with >>.
do{
--parentPosition;
value_type temp(eastl::forward<value_type>(*(first + parentPosition)));
eastl::adjust_heap<RandomAccessIterator, difference_type, value_type>
(first, parentPosition, heapSize, parentPosition, eastl::forward<value_type>(temp));
} while(parentPosition != 0);
}
}
template <typename RandomAccessIterator, typename Compare>
void make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
{
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
const difference_type heapSize = last - first;
if(heapSize >= 2) // If there is anything to do... (we need this check because otherwise the math fails below).
{
difference_type parentPosition = ((heapSize - 2) >> 1) + 1; // We use '>> 1' instead of '/ 2' because we have seen VC++ generate better code with >>.
do{
--parentPosition;
value_type temp(eastl::forward<value_type>(*(first + parentPosition)));
eastl::adjust_heap<RandomAccessIterator, difference_type, value_type, Compare>
(first, parentPosition, heapSize, parentPosition, eastl::forward<value_type>(temp), compare);
} while(parentPosition != 0);
}
}
///////////////////////////////////////////////////////////////////////
// sort_heap
///////////////////////////////////////////////////////////////////////
/// sort_heap
///
/// After the application if this algorithm, the range it was applied to
/// is no longer a heap, though it will be a reverse heap (smallest first).
/// The item with the lowest priority will be first, and the highest last.
/// This is not a stable sort because the relative order of equivalent
/// elements is not necessarily preserved.
/// The range referenced must be valid; all pointers must be dereferenceable
/// and within the sequence the last position is reachable from the first
/// by incrementation.
/// The complexity is at most O(n * log(n)), where n is count of the range.
///
template <typename RandomAccessIterator>
inline void sort_heap(RandomAccessIterator first, RandomAccessIterator last)
{
for(; (last - first) > 1; --last) // We simply use the heap to sort itself.
eastl::pop_heap<RandomAccessIterator>(first, last);
}
/// sort_heap
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
template <typename RandomAccessIterator, typename Compare>
inline void sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
{
for(; (last - first) > 1; --last) // We simply use the heap to sort itself.
eastl::pop_heap<RandomAccessIterator, Compare>(first, last, compare);
}
///////////////////////////////////////////////////////////////////////
// remove_heap
///////////////////////////////////////////////////////////////////////
/// remove_heap
///
/// Removes an arbitrary entry from the heap and adjusts the heap appropriately.
/// This function is unlike pop_heap in that pop_heap moves the top item
/// to the back of the heap, whereas remove_heap moves an arbitrary item to
/// the back of the heap.
///
/// Note: Since this function moves the element to the back of the heap and
/// doesn't actually remove it from the given container, the user must call
/// the container erase function if the user wants to erase the element
/// from the container.
///
template <typename RandomAccessIterator, typename Distance>
inline void remove_heap(RandomAccessIterator first, Distance heapSize, Distance position)
{
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
const value_type tempBottom(*(first + heapSize - 1));
*(first + heapSize - 1) = *(first + position);
eastl::adjust_heap<RandomAccessIterator, difference_type, value_type>
(first, (difference_type)0, (difference_type)(heapSize - 1), (difference_type)position, tempBottom);
}
/// remove_heap
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
/// Note: Since this function moves the element to the back of the heap and
/// doesn't actually remove it from the given container, the user must call
/// the container erase function if the user wants to erase the element
/// from the container.
///
template <typename RandomAccessIterator, typename Distance, typename Compare>
inline void remove_heap(RandomAccessIterator first, Distance heapSize, Distance position, Compare compare)
{
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
const value_type tempBottom(*(first + heapSize - 1));
*(first + heapSize - 1) = *(first + position);
eastl::adjust_heap<RandomAccessIterator, difference_type, value_type, Compare>
(first, (difference_type)0, (difference_type)(heapSize - 1), (difference_type)position, tempBottom, compare);
}
///////////////////////////////////////////////////////////////////////
// change_heap
///////////////////////////////////////////////////////////////////////
/// change_heap
///
/// Given a value in the heap that has changed in priority, this function
/// adjusts the heap appropriately. The heap size remains unchanged after
/// this operation.
///
template <typename RandomAccessIterator, typename Distance>
inline void change_heap(RandomAccessIterator first, Distance heapSize, Distance position)
{
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
eastl::remove_heap<RandomAccessIterator, Distance>(first, heapSize, position);
value_type tempBottom(*(first + heapSize - 1));
eastl::promote_heap<RandomAccessIterator, difference_type, value_type>
(first, (difference_type)0, (difference_type)(heapSize - 1), tempBottom);
}
/// change_heap
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
template <typename RandomAccessIterator, typename Distance, typename Compare>
inline void change_heap(RandomAccessIterator first, Distance heapSize, Distance position, Compare compare)
{
typedef typename eastl::iterator_traits<RandomAccessIterator>::difference_type difference_type;
typedef typename eastl::iterator_traits<RandomAccessIterator>::value_type value_type;
eastl::remove_heap<RandomAccessIterator, Distance, Compare>(first, heapSize, position, compare);
value_type tempBottom(*(first + heapSize - 1));
eastl::promote_heap<RandomAccessIterator, difference_type, value_type, Compare>
(first, (difference_type)0, (difference_type)(heapSize - 1), tempBottom, compare);
}
///////////////////////////////////////////////////////////////////////
// is_heap_until
///////////////////////////////////////////////////////////////////////
/// is_heap_until
///
template <typename RandomAccessIterator>
inline RandomAccessIterator is_heap_until(RandomAccessIterator first, RandomAccessIterator last)
{
int counter = 0;
for(RandomAccessIterator child = first + 1; child < last; ++child, counter ^= 1)
{
if(*first < *child) // We must use operator <, and are not allowed to use > or >= here.
return child;
first += counter; // counter switches between 0 and 1 every time through.
}
return last;
}
/// is_heap_until
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
template <typename RandomAccessIterator, typename Compare>
inline RandomAccessIterator is_heap_until(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
{
int counter = 0;
for(RandomAccessIterator child = first + 1; child < last; ++child, counter ^= 1)
{
if(compare(*first, *child))
return child;
first += counter; // counter switches between 0 and 1 every time through.
}
return last;
}
///////////////////////////////////////////////////////////////////////
// is_heap
///////////////////////////////////////////////////////////////////////
/// is_heap
///
/// This is a useful debugging algorithm for verifying that a random
/// access container is in heap format.
///
template <typename RandomAccessIterator>
inline bool is_heap(RandomAccessIterator first, RandomAccessIterator last)
{
return (eastl::is_heap_until(first, last) == last);
}
/// is_heap
///
/// The Compare function must work equivalently to the compare function used
/// to make and maintain the heap.
///
template <typename RandomAccessIterator, typename Compare>
inline bool is_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare)
{
return (eastl::is_heap_until(first, last, compare) == last);
}
// To consider: The following may be a faster implementation for most cases.
//
// template <typename RandomAccessIterator>
// inline bool is_heap(RandomAccessIterator first, RandomAccessIterator last)
// {
// if(((uintptr_t)(last - first) & 1) == 0) // If the range has an even number of elements...
// --last;
//
// RandomAccessIterator parent = first, child = (first + 1);
//
// for(; child < last; child += 2, ++parent)
// {
// if((*parent < *child) || (*parent < *(child + 1)))
// return false;
// }
//
// if((((uintptr_t)(last - first) & 1) == 0) && (*parent < *child))
// return false;
//
// return true;
// }
} // namespace eastl
#endif // Header include guard
+96
View File
@@ -0,0 +1,96 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
//
// This file #includes <initializer_list> if it's available, else it defines
// its own version of std::initializer_list. It does not define eastl::initializer_list
// because that would not provide any use, due to how the C++11 Standard works.
///////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_INITIALIZER_LIST_H
#define EASTL_INITIALIZER_LIST_H
#include <EASTL/internal/config.h>
#include <EABase/eahave.h>
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result.
#endif
#if defined(EA_HAVE_CPP11_INITIALIZER_LIST) // If the compiler can generate calls to std::initializer_list...
// The initializer_list type must be declared in the std namespace, as that's the
// namespace the compiler uses when generating code to use it.
EA_DISABLE_ALL_VC_WARNINGS()
#include <initializer_list>
EA_RESTORE_ALL_VC_WARNINGS()
#else
// If you get an error here about initializer_list being already defined, then the EA_HAVE_CPP11_INITIALIZER_LIST define from <EABase/eahave.h> needs to be updated.
namespace std
{
// See the C++11 Standard, section 18.9.
template<class E>
class initializer_list
{
public:
typedef E value_type;
typedef const E& reference;
typedef const E& const_reference;
typedef size_t size_type;
typedef const E* iterator; // Must be const, as initializer_list (and its mpArray) is an immutable temp object.
typedef const E* const_iterator;
private:
iterator mpArray;
size_type mArraySize;
// This constructor is private, but the C++ compiler has the ability to call it, as per the C++11 Standard.
initializer_list(const_iterator pArray, size_type arraySize)
: mpArray(pArray), mArraySize(arraySize) { }
public:
initializer_list() EA_NOEXCEPT // EA_NOEXCEPT requires a recent version of EABase.
: mpArray(NULL), mArraySize(0) { }
size_type size() const EA_NOEXCEPT { return mArraySize; }
const_iterator begin() const EA_NOEXCEPT { return mpArray; } // Must be const_iterator, as initializer_list (and its mpArray) is an immutable temp object.
const_iterator end() const EA_NOEXCEPT { return mpArray + mArraySize; }
};
template<class T>
const T* begin(std::initializer_list<T> ilist) EA_NOEXCEPT
{
return ilist.begin();
}
template<class T>
const T* end(std::initializer_list<T> ilist) EA_NOEXCEPT
{
return ilist.end();
}
}
#endif
#endif // Header include guard
+65
View File
@@ -0,0 +1,65 @@
/////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_ATOMIC_INTERNAL_ARCH_H
#define EASTL_ATOMIC_INTERNAL_ARCH_H
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once
#endif
/////////////////////////////////////////////////////////////////////////////////
//
// Include the architecture specific implementations
//
#if defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)
#include "x86/arch_x86.h"
#elif defined(EA_PROCESSOR_ARM32) || defined(EA_PROCESSOR_ARM64)
#include "arm/arch_arm.h"
#endif
/////////////////////////////////////////////////////////////////////////////////
#include "arch_fetch_add.h"
#include "arch_fetch_sub.h"
#include "arch_fetch_and.h"
#include "arch_fetch_xor.h"
#include "arch_fetch_or.h"
#include "arch_add_fetch.h"
#include "arch_sub_fetch.h"
#include "arch_and_fetch.h"
#include "arch_xor_fetch.h"
#include "arch_or_fetch.h"
#include "arch_exchange.h"
#include "arch_cmpxchg_weak.h"
#include "arch_cmpxchg_strong.h"
#include "arch_load.h"
#include "arch_store.h"
#include "arch_compiler_barrier.h"
#include "arch_cpu_pause.h"
#include "arch_memory_barrier.h"
#include "arch_signal_fence.h"
#include "arch_thread_fence.h"
#endif /* EASTL_ATOMIC_INTERNAL_ARCH_H */
@@ -0,0 +1,173 @@
/////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_ATOMIC_INTERNAL_ARCH_ADD_FETCH_H
#define EASTL_ATOMIC_INTERNAL_ARCH_ADD_FETCH_H
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once
#endif
/////////////////////////////////////////////////////////////////////////////////
//
// void EASTL_ARCH_ATOMIC_ADD_FETCH_*_N(type, type ret, type * ptr, type val)
//
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_8)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_8)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_8)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_8)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_8)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_16)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_16)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_16)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_16)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_16)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_32)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_32)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_32)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_32)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_32)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_64)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_64)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_64)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_64)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_64)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_128)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_128)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_128)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_128)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_128)
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_128_AVAILABLE 0
#endif
#endif /* EASTL_ATOMIC_INTERNAL_ARCH_ADD_FETCH_H */
@@ -0,0 +1,173 @@
/////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_ATOMIC_INTERNAL_ARCH_AND_FETCH_H
#define EASTL_ATOMIC_INTERNAL_ARCH_AND_FETCH_H
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once
#endif
/////////////////////////////////////////////////////////////////////////////////
//
// void EASTL_ARCH_ATOMIC_AND_FETCH_*_N(type, type ret, type * ptr, type val)
//
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_8)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_8)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_8)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_8)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_8)
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_16)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_16)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_16)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_16)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_16)
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_32)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_32)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_32)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_32)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_32)
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_64)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_64)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_64)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_64)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_64)
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_128)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_128)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_128)
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_128)
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_128)
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_128_AVAILABLE 0
#endif
#endif /* EASTL_ATOMIC_INTERNAL_ARCH_AND_FETCH_H */
@@ -0,0 +1,430 @@
/////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////
#ifndef EASTL_ATOMIC_INTERNAL_ARCH_CMPXCHG_STRONG_H
#define EASTL_ATOMIC_INTERNAL_ARCH_CMPXCHG_STRONG_H
#if defined(EA_PRAGMA_ONCE_SUPPORTED)
#pragma once
#endif
/////////////////////////////////////////////////////////////////////////////////
//
// void EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_*_*_N(type, bool ret, type * ptr, type * expected, type desired)
//
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128_AVAILABLE 0
#endif
#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128_AVAILABLE 1
#else
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128_AVAILABLE 0
#endif
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
//
// void EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_*_N(type, bool ret, type * ptr, type * expected, type desired)
//
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_8_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_8(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_8_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_8(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_8_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_8(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_8_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_8(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_8_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_8(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_16_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_16(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_16_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_16(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_16_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_16(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_16_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_16(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_16_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_16(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_32_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_32(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_32_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_32(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_32_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_32(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_32_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_32(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_32_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_32(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_64_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_64(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_64_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_64(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_64_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_64(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_64_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_64(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_64_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_64(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_128_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_128(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_128_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_128(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_128_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_128(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_128_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_128(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired)
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_128_AVAILABLE \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128_AVAILABLE
#define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_128(type, ret, ptr, expected, desired) \
EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired)
#endif /* EASTL_ATOMIC_INTERNAL_ARCH_CMPXCHG_STRONG_H */

Some files were not shown because too many files have changed in this diff Show More