diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..328d671a2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,75 @@ +--- +Language: Cpp +Standard: c++20 + +AlignAfterOpenBracket: Align +AlignOperands: true +AlignConsecutiveBitFields: Consecutive +AlignEscapedNewlines: Left + +PointerAlignment: Left +ReferenceAlignment: Left +QualifierAlignment: Left + +BreakBeforeBraces: Allman +BreakConstructorInitializers: AfterColon +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: false +PackConstructorInitializers: Never +EmptyLineBeforeAccessModifier: LogicalBlock +EmptyLineAfterAccessModifier: Never +KeepEmptyLinesAtTheStartOfBlocks: false +SeparateDefinitionBlocks: Always + +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +AccessModifierOffset: -4 +IndentCaseLabels: false +NamespaceIndentation: All + +ColumnLimit: 100 + +BitFieldColonSpacing: Both +SpacesBeforeTrailingComments: 1 +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: After +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInParentheses: false +SpacesInSquareBrackets: false + +UseCRLF: false + +FixNamespaceComments: true + +AllowShortIfStatementsOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline + +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^(<|")(assert|debug).h(>|")$' + Priority: 5 + CaseSensitive: true + - Regex: '^(<|")EASTL/' + Priority: 4 + CaseSensitive: true + - Regex: '^(<|")(types|klibc|libc\+\+|c?stdint|c?stddef|c?stdarg|stdlib|stdbool|c?math|c?limits|c?inttypes|c?float|c?ctype|c?string|ranges|transform)(.h)?(>|")$' + Priority: 3 + CaseSensitive: true + - Regex: '^(<|")Arch*' + Priority: 2 + CaseSensitive: true + - Regex: '.*' + Priority: 1 + CaseSensitive: false diff --git a/.clangd b/.clangd new file mode 100644 index 000000000..1e30f4c7e --- /dev/null +++ b/.clangd @@ -0,0 +1,20 @@ +CompileFlags: + CompilationDatabase: /tmp/sweb + Remove: [-fno-sync-libcalls] +Completion: + AllScopes: true +Hover: + ShowAKA: Yes +Diagnostics: + ClangTidy: + Add: + - modernize* + - bugprone-* + Remove: + - modernize-use-trailing-return-type + UnusedIncludes: Strict +--- +If: + PathMatch: "lib/EASTl/*" +Diagnostics: + Suppress: "*" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..7a5f7914c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +end_of_line = lf +insert_final_newline = true + +# Tab indentation required +[Makefile] +indent_style = tab +indent_size = 2 + +[{*.md,*.rst}] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore index be412d2d1..27e96b163 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,16 @@ sweb.kdev* .cdtbuild .idea cmake-build-*/* +.dir-locals.el # OS-specific files *.DS_Store *Thumbs.db +.directory + +GPATH +GRTAGS +GTAGS + +compile_commands.json +.gdb_history diff --git a/CMakeLists.txt b/CMakeLists.txt index 693cde00c..e9ee14bfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,21 +1,49 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.17) + +cmake_policy(SET CMP0079 NEW) + +if ("${ARCH}" STREQUAL "") + set (ARCH "x86/64") +endif("${ARCH}" STREQUAL "") +MESSAGE("-- Target architecture: ${ARCH}") + +if ("${ARCH}" MATCHES "arm.*") + add_definitions(-DARM) +endif("${ARCH}" MATCHES "arm.*") + +# Needs to be set before everything else +set(CMAKE_TOOLCHAIN_FILE arch/${ARCH}/CMakeLists.compiler) + set(CMAKE_DISABLE_SOURCE_CHANGES ON) set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(BUILD_SHARED_LIBS OFF) # Include custom CMake modules from the cmake/ subdirectory list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) +project(sweb + LANGUAGES C CXX ASM) + +add_executable(kernel) +set_target_properties(kernel PROPERTIES OUTPUT_NAME kernel.x) + +# https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#interface-libraries +add_library(arch_options INTERFACE) +add_library(kernel_options INTERFACE) +add_library(userspace_options INTERFACE) + +target_link_libraries(kernel_options INTERFACE arch_options) +target_link_libraries(userspace_options INTERFACE arch_options) +target_link_libraries(kernel PUBLIC kernel_options) + + set(HDD_IMAGE_RAW "SWEB-flat.vmdk") set(HDD_IMAGE "SWEB.qcow2") +set(INITRD_FILE ${PROJECT_BINARY_DIR}/sweb_initrd) +# set(HDD_IMAGE_FILE ${PROJECT_BINARY_DIR}/SWEB-flat.vmdk) -if ("${ARCH}" STREQUAL "") - set (ARCH "x86/64") -endif("${ARCH}" STREQUAL "") -MESSAGE("-- Target architecture: ${ARCH}") - -if ("${ARCH}" MATCHES "arm.*") - add_definitions(-DARM) -endif("${ARCH}" MATCHES "arm.*") unset(CMAKE_CROSSCOMPILING) if(APPLE OR WIN32) @@ -26,10 +54,6 @@ endif(APPLE OR WIN32) # add-dbg for userspace binaries include(AddDebugInfo) -include(arch/${ARCH}/CMakeLists.compiler) - -project(sweb - LANGUAGES C CXX ASM) string(REPLACE "/" ";" ARCH_LIST ${ARCH}) string(REPLACE "/" "_" ARCH_ESC ${ARCH}) @@ -81,8 +105,6 @@ set(EXECUTABLE_OUTPUT_PATH "${PROJECT_BINARY_DIR}") include(arch/${ARCH}/CMakeLists.include) include(arch/${ARCH}/CMakeLists.userspace) -string (REPLACE ";" " " KERNEL_CMAKE_C_FLAGS_STR "${KERNEL_CMAKE_C_FLAGS}") -set(CMAKE_ASM_FLAGS ${KERNEL_CMAKE_C_FLAGS_STR}) if(CMAKE_CROSSCOMPILING) set(CMAKE_CROSS_COMPILE_FLAGS -G "Unix Makefiles") @@ -99,133 +121,105 @@ endif() function(ADD_PROJECT_LIBRARY LIBRARY_NAME) arch2obj(archobj_libname ${LIBRARY_NAME}) - file(GLOB source_files ${SOURCE_WILDCARDS}) - - set(library_files) - - if(source_files) - set(library_files ${source_files}) - endif(source_files) + file(GLOB source_files CONFIGURE_DEPENDS ${SOURCE_WILDCARDS}) - if(archobj_libname) - set(library_files ${library_files} $) - endif(archobj_libname) + target_sources(kernel PUBLIC + ${source_files}) - if(library_files) - add_library(${LIBRARY_NAME} ${library_files}) - - if(archobj_libname) - add_dependencies(${LIBRARY_NAME} ${archobj_libname}) - endif(archobj_libname) - - target_compile_options(${LIBRARY_NAME} PRIVATE - $<$:${KERNEL_CMAKE_CXX_FLAGS}> - $<$:${KERNEL_CMAKE_C_FLAGS}> - ) - - set(ENV{LIBRARY_NAMES} "$ENV{LIBRARY_NAMES};${LIBRARY_NAME}") - endif(library_files) + target_link_libraries(kernel PUBLIC + ${archobj_libname}) endfunction(ADD_PROJECT_LIBRARY) set (SOURCE_WILDCARDS *.cpp *.c *.S) set(LIBRARY_FILENAMES) -#Initialize global (environment) variables +# Initialize global (environment) variables set(ENV{LIBRARY_NAMES}) -#Create target for userspace libc (Need to to that here because some files (e.g. syscall.c) are all over the place) +# Build the Linker command +target_link_options(kernel_options INTERFACE + -g -u entry -Wl,-T ${CMAKE_SOURCE_DIR}/arch/${ARCH}/utils/kernel-ld-script.ld ${ARCH_APPEND_LD_ARGUMENTS}) + + +# Create target for userspace libc (Need to to that here because some files (e.g. syscall.c) are all over the place) add_library(userspace_libc "") -target_compile_options(userspace_libc PUBLIC ${ARCH_USERSPACE_COMPILE_OPTIONS}) +target_link_libraries(userspace_libc PUBLIC userspace_options) -#Add the source directories +# Add the source directories add_subdirectory(arch) add_subdirectory(common) add_subdirectory(utils) add_subdirectory(userspace) +add_subdirectory(lib) +target_link_libraries(kernel_libc PUBLIC kernel_options) +target_link_libraries(kernel PUBLIC kernel_libc) -#FINAL_LIB_NAMES should contain the names of all libraries -#these names can be used to link the kernel, no unpacking of *.a files is needed anymore +# FINAL_LIB_NAMES should contain the names of all libraries +# these names can be used to link the kernel, no unpacking of *.a files is needed anymore set(FINAL_LIB_NAMES $ENV{LIBRARY_NAMES}) -#Name of the executables of the userspace, needed for dependency checking +# Name of the executables of the userspace, needed for dependency checking set(FINAL_USERSPACE_NAMES $ENV{USERSPACE_NAMES}) -#Build the Linker command -set(KERNEL_LD_ARGUMENT ${KERNEL_LD_ARGUMENT} -g -u entry -Wl,-T ${CMAKE_SOURCE_DIR}/arch/${ARCH}/utils/kernel-ld-script.ld) -set(KERNEL_LD_ARGUMENT ${KERNEL_LD_ARGUMENT} -o ${PROJECT_BINARY_DIR}/kernel.x) -#set(KERNEL_LD_ARGUMENT ${KERNEL_LD_ARGUMENT} -Wl,-Map -Wl,${PROJECT_BINARY_DIR}/kernel.map) -set(KERNEL_LD_ARGUMENT ${KERNEL_LD_ARGUMENT} -Wl,--start-group) -foreach(libfile ${FINAL_LIB_NAMES}) - set(KERNEL_LD_ARGUMENT ${KERNEL_LD_ARGUMENT} ${LIBRARY_OUTPUT_PATH}/lib${libfile}.a) -endforeach(libfile) -set(KERNEL_LD_ARGUMENT ${KERNEL_LD_ARGUMENT} -Wl,--end-group) -set(KERNEL_LD_ARGUMENT ${KERNEL_LD_ARGUMENT} ${ARCH_APPEND_LD_ARGUMENTS}) -#Build userspace exe2minixfs command +# Build userspace exe2minixfs command set(MINIXFS_ARGUMENT "") foreach(file $ENV{USERSPACE_NAMES_EXE2MINIX}) list(APPEND MINIXFS_ARGUMENT ${file}) endforeach(file) -file(GLOB userspace_data userspace/data/*) +file(GLOB userspace_data CONFIGURE_DEPENDS userspace/data/*) foreach(file ${userspace_data}) get_filename_component(datafile ${file} NAME) list(APPEND MINIXFS_ARGUMENT ${file} ${datafile}) endforeach(file) + + #Custom Target: hdd_image #Creates the hd image and copies all files to it add_custom_target (hdd_image ALL - DEPENDS kernel_to_image userspace_to_image - COMMAND qemu-img convert -f raw -O qcow2 ${HDD_IMAGE_RAW} ${HDD_IMAGE} + DEPENDS kernel_to_image userspace_to_image + BYPRODUCTS ${HDD_IMAGE} + COMMAND qemu-img convert -f raw -O qcow2 ${HDD_IMAGE_RAW} ${HDD_IMAGE} ) #Custom Command: invoke exe2minixfs and copy boot files to our hd image add_custom_target (kernel_to_image - DEPENDS blank_hdd_image kernel_image exe2minixfs + DEPENDS blank_hdd_image kernel exe2minixfs mkminixfs userspace_to_image "${HDD_IMAGE_RAW}" "${CMAKE_SOURCE_DIR}/utils/images/menu.lst" # "${INITRD_FILE}" + BYPRODUCTS kernel.dbg # x86/32 doesn't generate kernel.dbg COMMAND ${CMAKE_COMMAND} -E touch "./kernel.dbg" - COMMAND ./exe2minixfs ${HDD_IMAGE_RAW} 32256 "${CMAKE_SOURCE_DIR}/utils/images/menu.lst" boot/grub/menu.lst - ./kernel.x boot/kernel.x - ./kernel.dbg boot/kernel.dbg + #COMMAND ./mkminixfs "${HDD_IMAGE_RAW}" 1 + COMMAND ./exe2minixfs "${HDD_IMAGE_RAW}" 1 "${CMAKE_SOURCE_DIR}/utils/images/menu.lst" boot/grub/menu.lst + $ boot/kernel.x + ./kernel.dbg boot/kernel.dbg + #"${INITRD_FILE}" boot/initrd WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMENT "Copying kernel files to image..." ) -#Custom Command: Outputs kernel_image -#Executes the linker command after all libraries where build successfully -if("${ARCH}" MATCHES "armv8") -#For armv8 we need to execute the final linking step twice because after the first time -#The debug info gets extracted and linked in at the second link step -add_custom_target (kernel_image - DEPENDS ${FINAL_LIB_NAMES} add-dbg - COMMAND ${LD_EXECUTABLE} ${KERNEL_LD_ARGUMENT} ${KERNEL_IMAGE_OBJCOPY} - COMMAND ${LD_EXECUTABLE} ${KERNEL_LD_ARGUMENT} ${ADD_LD_ARGUMENT} - WORKING_DIRECTORY ${LIBRARY_OUTPUT_PATH} -) - -else() - -add_custom_target (kernel_image - DEPENDS ${FINAL_LIB_NAMES} add-dbg - COMMAND ${LD_EXECUTABLE} ${KERNEL_LD_ARGUMENT} ${KERNEL_IMAGE_OBJCOPY} - WORKING_DIRECTORY ${LIBRARY_OUTPUT_PATH} -) - -endif() +add_custom_command (OUTPUT ${INITRD_FILE} + DEPENDS mkminixfs exe2minixfs + COMMAND dd if=/dev/zero of="${INITRD_FILE}" bs=512 count=1024 + COMMAND ./mkminixfs -d "${INITRD_FILE}" + COMMAND ./exe2minixfs "${INITRD_FILE}" 0 ${CMAKE_SOURCE_DIR}/README.md README.md + COMMENT "Creating empty initrd..." + ) #Custom Command: invoke exe2minixfs and copy all userspace programs to our hd image second partition add_custom_target (userspace_to_image - DEPENDS blank_hdd_image ${FINAL_USERSPACE_NAMES} exe2minixfs - COMMAND mkdir -p userspace/data - COMMAND cp -f ${CMAKE_SOURCE_DIR}/userspace/data/* ${PROJECT_BINARY_DIR}/userspace/data - COMMAND ./exe2minixfs ${HDD_IMAGE_RAW} 10321920 ${MINIXFS_ARGUMENT} + DEPENDS blank_hdd_image ${FINAL_USERSPACE_NAMES} exe2minixfs mkminixfs "${HDD_IMAGE_RAW}" # "${INITRD_FILE}" + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/userspace/data ${PROJECT_BINARY_DIR}/userspace/data + # COMMAND ./mkminixfs -d "${HDD_IMAGE_RAW}" -p 2 + COMMAND ./exe2minixfs "${HDD_IMAGE_RAW}" 2 ${MINIXFS_ARGUMENT} + # COMMAND ./exe2minixfs "${INITRD_FILE}" 0 ${MINIXFS_ARGUMENT} WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMENT "Copying userspace programs to image..." ) @@ -289,17 +283,25 @@ add_custom_target(rungdb #Custom target: make mrproper #Makes really clean (except for the object files) add_custom_target(mrproper - COMMAND make clean + COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR} -t clean COMMAND ${PROJECT_SOURCE_DIR}/utils/prompt.sh "rm -fR ${PROJECT_BINARY_DIR}/*: remove all arguments recursively [Y/n]? " - COMMAND rm -fR ${PROJECT_BINARY_DIR}/* || true - COMMAND cmake -DARCH=${ARCH} ${PROJECT_SOURCE_DIR} ${CMAKE_CROSS_COMPILE_FLAGS} + COMMAND ${CMAKE_COMMAND} -E rm -fR -- ${PROJECT_BINARY_DIR}/* + COMMAND ${CMAKE_COMMAND} -DARCH=${ARCH} ${PROJECT_SOURCE_DIR} ${CMAKE_CROSS_COMPILE_FLAGS} +) + +#Custom target: make cleandisk +#Deletes the disk image so it is freshly recreated on next build +add_custom_target(cleandisk + COMMENT "Removing disk image" + COMMAND ${CMAKE_COMMAND} -E rm -f -- ${PROJECT_BINARY_DIR}/SWEB-flat.vmdk + COMMAND ${CMAKE_COMMAND} -E rm -f -- ${PROJECT_BINARY_DIR}/SWEB.qcow2 ) #Custom target: make debug add_custom_target(debug - COMMAND make clean + COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR} -t clean COMMAND ${PROJECT_SOURCE_DIR}/utils/prompt.sh "rm -fR ${PROJECT_BINARY_DIR}/*: remove all arguments recursively [Y/n]? " - COMMAND rm -fR ${PROJECT_BINARY_DIR}/* || true + COMMAND ${CMAKE_COMMAND} -E rm -fR -- ${PROJECT_BINARY_DIR}/* COMMAND cmake -DDEBUG=1 -DARCH=${ARCH} ${PROJECT_SOURCE_DIR} ${CMAKE_CROSS_COMPILE_FLAGS} ) diff --git a/arch/CMakeLists.txt b/arch/CMakeLists.txt index 432effb4e..3d6528a05 100644 --- a/arch/CMakeLists.txt +++ b/arch/CMakeLists.txt @@ -1,5 +1,6 @@ -include_directories(common/include +target_include_directories(kernel PUBLIC + common/include ../common/include ../common/include/cache ../common/include/console @@ -10,9 +11,7 @@ include_directories(common/include ../common/include/fs/unixfs ../common/include/kernel ../common/include/mm - ../common/include/ustl ../common/include/util ) include(${ARCH}/CMakeLists.subfolders) - diff --git a/arch/arm/CMakeLists.txt b/arch/arm/CMakeLists.txt index 6c3e66374..946f17140 100644 --- a/arch/arm/CMakeLists.txt +++ b/arch/arm/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC common/include common/include/usb ../${ARCH}/include diff --git a/arch/arm/common/include/ArchBoardSpecific.h b/arch/arm/common/include/ArchBoardSpecific.h index fd2af9470..d0fe47071 100644 --- a/arch/arm/common/include/ArchBoardSpecific.h +++ b/arch/arm/common/include/ArchBoardSpecific.h @@ -6,7 +6,7 @@ class ArchBoardSpecific { public: static pointer getVESAConsoleLFBPtr(); - static uint32 getUsableMemoryRegion(uint32 region, pointer &start_address, pointer &end_address, uint32 &type); + static size_t getUsableMemoryRegion(size_t region, pointer &start_address, pointer &end_address, size_t &type); static void frameBufferInit(); static void onIdle(); static void enableTimer(); @@ -20,4 +20,3 @@ class ArchBoardSpecific static void uart0_irq_handler(); static void disableMulticore(uint32* spin); }; - diff --git a/arch/arm/common/include/ArchCpuLocalStorage.h b/arch/arm/common/include/ArchCpuLocalStorage.h new file mode 100644 index 000000000..2bd85804e --- /dev/null +++ b/arch/arm/common/include/ArchCpuLocalStorage.h @@ -0,0 +1,15 @@ +#pragma once + +#define cpu_local +#define __cpu + + +namespace CpuLocalStorage +{ + // size_t getCLSSize(); + + // char* allocCLS(); + // void setCLS(char* cls); + bool ClsInitialized(); + // void* getClsBase(); +}; diff --git a/arch/arm/common/include/ArchMemory.h b/arch/arm/common/include/ArchMemory.h index 5c4f34281..1d84cacb5 100644 --- a/arch/arm/common/include/ArchMemory.h +++ b/arch/arm/common/include/ArchMemory.h @@ -1,8 +1,29 @@ #pragma once -#include "types.h" #include "paging-definitions.h" -#include "uvector.h" + +#include "types.h" + +#include "EASTL/vector.h" + +class ArchMemoryMapping +{ +public: + PageDirEntry* pd; + PageTableEntry* pt; + + pointer page; //address to the ident mapped ppn + + size_t pd_ppn; + size_t pt_ppn; + + size_t page_ppn; //the physical page number + + size_t page_size; + + size_t pdi; + size_t pti; +}; /** * @@ -13,7 +34,7 @@ class ArchMemory { public: -/** +/** * Constructor * creates a new Page-Directory for a UserProccess by copying the * Kernel-Page-Directory @@ -21,15 +42,25 @@ class ArchMemory */ ArchMemory(); -/** + ArchMemory(uint32_t page_dir_page); + + +/** + * Destructor. Recursively deletes the page directory and all page tables + * + */ + ~ArchMemory(); + +/** * * maps a virtual page to a physical page (pde and pte need to be set up first) * - * @param virtual_page + * @param virtual_page * @param physical_page * @param user_access PTE User/Supervisor Flag, governing the binary Paging * Privilege Mechanism */ + [[nodiscard]] __attribute__((warn_unused_result)) bool mapPage(uint32 virtual_page, uint32 physical_page, uint32 user_access); /** @@ -39,11 +70,6 @@ class ArchMemory */ void unmapPage(uint32 virtual_page); -/** - * Destructor. Recursively deletes the page directory and all page tables - * - */ - ~ArchMemory(); /** * Takes a Physical Page Number in Real Memory and returns a virtual address than @@ -80,14 +106,15 @@ class ArchMemory */ static uint32 get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, uint32 *physical_page, uint32 *physical_pte_page=0); - /** - * - * maps a virtual page to a physical page in kernel mapping - * - * @param virtual_page - * @param physical_page - */ - static void mapKernelPage(uint32 virtual_page, uint32 physical_page); + /** + * maps a virtual page to a physical page in kernel mapping + * + * @param virtual_page + * @param physical_page + * @return true if the page has been mapped + */ + [[nodiscard]] + static bool mapKernelPage(size_t virtual_page, size_t physical_page, bool can_alloc_pages = false, bool memory_mapped_io = false); /** * removes the mapping to a virtual_page by marking its PTE Entry as non valid @@ -103,38 +130,41 @@ class ArchMemory uint32 page_dir_page_; uint32 getRootOfPagingStructure(); + static void loadPagingStructureRoot(size_t ttbr0_value); + + static PageDirEntry* getKernelPagingStructureRootVirt(); + static size_t getKernelPagingStructureRootPhys(); static const size_t RESERVED_START = 0x80000ULL; static const size_t RESERVED_END = 0x80400ULL; -private: - PageTableEntry* getPTE(size_t vpn); - static PageTableEntry* getIdentAddressOfPT(PageDirEntry *page_directory, uint32 pde_vpn); - -/** - * Adds a page directory entry to the given page directory. - * (In other words, adds the reference to a new page table to a given - * page directory.) - * - * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. - * @param physical_page_table_page physical page of the new page table. - */ - void insertPT(uint32 pde_vpn); - -/** - * Removes a page directory entry from a given page directory if it is present - * in the first place. Futhermore, the target page table is assured to be - * empty. - * - * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. - */ - void checkAndRemovePT(uint32 pde_vpn); - - ustl::vector pt_ppns_; - - ArchMemory(ArchMemory const &src); // not yet implemented - ArchMemory &operator=(ArchMemory const &src); // should never be implemented + static ArchMemory& kernelArchMemory(); +private: + ArchMemory& operator=(const ArchMemory& src) = delete; // should never be implemented + + PageTableEntry* getPTE(size_t vpn); + static PageTableEntry* getIdentAddressOfPT(PageDirEntry* page_directory, uint32 pde_vpn); + + /** + * Adds a page directory entry to the given page directory. + * (In other words, adds the reference to a new page table to a given + * page directory.) + * + * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. + * @param physical_page_table_page physical page of the new page table. + */ + void insertPT(uint32 pde_vpn); + + /** + * Removes a page directory entry from a given page directory if it is present + * in the first place. Futhermore, the target page table is assured to be + * empty. + * + * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. + */ + void checkAndRemovePT(uint32 pde_vpn); + + eastl::vector pt_ppns_; }; - diff --git a/arch/arm/common/include/ArchMulticore.h b/arch/arm/common/include/ArchMulticore.h new file mode 100644 index 000000000..2820ce0a6 --- /dev/null +++ b/arch/arm/common/include/ArchMulticore.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Cpu.h" +#include "Mutex.h" + +#include "ArchCpuLocalStorage.h" + +#include "EASTL/vector.h" + +class IrqDomain; + +class ArchCpu : public Cpu +{ +public: + ArchCpu(); + + IrqDomain& rootIrqDomain(); +private: +}; + +class ArchMulticore +{ +public: + static void initialize(); + + static void startOtherCPUs(); + /* static void stopAllCpus(); */ + static void stopOtherCpus(); + + static void sendFunctionCallMessage(ArchCpu& cpu, RemoteFunctionCallMessage* fcall_message); + + static void initCpuLocalData(bool boot_cpu = false); + +private: +}; diff --git a/arch/arm/common/include/ArchThreads.h b/arch/arm/common/include/ArchThreads.h index 6dce0f5ac..1898fac59 100644 --- a/arch/arm/common/include/ArchThreads.h +++ b/arch/arm/common/include/ArchThreads.h @@ -1,7 +1,13 @@ #pragma once +#include "SpinLock.h" + #include "types.h" +#include "EASTL/unique_ptr.h" + +extern SpinLock global_atomic_add_lock; + /** * The flag for full barrier synchronization. */ @@ -23,12 +29,9 @@ struct ArchThreadRegisters class Thread; class ArchMemory; -/** - * this is where the thread info for task switching is stored - * - */ -extern ArchThreadRegisters *currentThreadRegisters; -extern Thread *currentThread; + +extern "C" void memory_barrier(); + /** * Collection of architecture dependant code concerning Task Switching @@ -38,6 +41,8 @@ class ArchThreads { public: + [[noreturn]] static void startThreads(Thread* init_thread); + /** * allocates space for the currentThreadRegisters * @@ -50,7 +55,19 @@ class ArchThreads * @param start_function instruction pointer is set so start function * @param stack stackpointer */ - static void createKernelRegisters(ArchThreadRegisters *&info, void* start_function, void* stack); + static eastl::unique_ptr createKernelRegisters(void* start_function, + void* stack); + + /** + * creates the ArchThreadRegisters for a user thread + * @param info where the ArchThreadRegisters is saved + * @param start_function instruction pointer is set so start function + * @param user_stack pointer to the userstack + * @param kernel_stack pointer to the kernel stack + */ + static eastl::unique_ptr createUserRegisters(void* start_function, + void* user_stack, + void* kernel_stack); /** * changes an existing ArchThreadRegisters so that execution will start / continue @@ -61,22 +78,17 @@ class ArchThreads * @param the ArchThreadRegisters that we are going to mangle * @param start_function instruction pointer for the next instruction that gets executed */ - static void changeInstructionPointer(ArchThreadRegisters *info, void* function); + static void changeInstructionPointer(ArchThreadRegisters& info, void* function); -/** - * creates the ArchThreadRegisters for a user thread - * @param info where the ArchThreadRegisters is saved - * @param start_function instruction pointer is set so start function - * @param user_stack pointer to the userstack - * @param kernel_stack pointer to the kernel stack - */ - static void createUserRegisters(ArchThreadRegisters *&info, void* start_function, void* user_stack, void* kernel_stack); + static void* getInstructionPointer(ArchThreadRegisters& info); -/** - * - * on x86: invokes int65, whose handler facilitates a task switch - * - */ + + + /** + * + * on x86: invokes int65, whose handler facilitates a task switch + * + */ static void yield(); /** @@ -87,6 +99,10 @@ class ArchThreads */ static void setAddressSpace(Thread *thread, ArchMemory& arch_memory); + static void switchToAddressSpace(Thread* thread); + static void switchToAddressSpace(ArchMemory& arch_memory); + + /** * uninterruptable locked operation * exchanges value in variable lock with new_value and returns the old_value @@ -95,7 +111,41 @@ class ArchThreads * @param new_value to set variable lock to * @returns old_value of variable lock */ - static uint32 testSetLock(uint32 &lock, uint32 new_value); + template + static T testSetLock(T& lock, T new_value) + { + if constexpr (__atomic_always_lock_free(sizeof(T), 0)) + { + return __atomic_exchange_n(&lock, new_value, __ATOMIC_SEQ_CST); + } + else + { + T result; + memory_barrier(); + asm("swp %[r], %[n], [%[l]]" : [r]"=&r"(result) : [n]"r"(new_value), [l]"r"(&lock)); + memory_barrier(); + return result; + } + } + + /** + * Counterpart to testSetLock() + * Writes 0 to the lock variable and provides a memory release barrier + * (ensures all previous memory stores are visible) + */ + template + static void syncLockRelease(volatile T &lock) + { + if constexpr (__atomic_always_lock_free(sizeof(T), 0)) + { + __sync_lock_release(&lock); + } + else + { + lock = 0; + memory_barrier(); + } + } /** * atomically increments or decrements value by increment @@ -104,10 +154,22 @@ class ArchThreads * @param increment can be positive or negative * @returns old value of value */ - static uint32 atomic_add(uint32 &value, int32 increment); - static int32 atomic_add(int32 &value, int32 increment); - static uint64 atomic_add(uint64 &value, int64 increment); - static int64 atomic_add(int64 &value, int64 increment); + template + static T atomic_add(T &value, T increment) + { + if constexpr (__atomic_always_lock_free(sizeof(T), 0)) + { + return __sync_fetch_and_add(&value, increment); + } + else + { + global_atomic_add_lock.acquire(); + T result = value; + value += increment; + global_atomic_add_lock.release(); + return result; + } + } /** * Atomically set a target to another value. @@ -115,8 +177,19 @@ class ArchThreads * @param target The target which shall be set * @param value The value which shall be set */ - static void atomic_set(uint32 &target, uint32 value); - static void atomic_set(int32 &target, int32 value); + template + static void atomic_set(T& target, T value) + { + if constexpr (__atomic_always_lock_free(sizeof(T), 0)) + { + __atomic_store_n (&target, value, __ATOMIC_SEQ_CST); + } + else + { + // just re-use the method for exchange. Under ARM the build-ins do not work... + testSetLock(target, value); + } + } /** * @@ -133,4 +206,3 @@ class ArchThreads */ static void debugCheckNewThread(Thread* thread); }; - diff --git a/arch/arm/common/include/MMCDriver.h b/arch/arm/common/include/MMCDriver.h index 0cfe71737..5c76d148e 100644 --- a/arch/arm/common/include/MMCDriver.h +++ b/arch/arm/common/include/MMCDriver.h @@ -1,15 +1,17 @@ #pragma once #include "BDDriver.h" +#include "Device.h" +#include "DeviceDriver.h" #include "Mutex.h" class BDRequest; -class MMCDriver : public BDDriver +class MMCDrive : public BDDriver, public Device { public: - MMCDriver(); - virtual ~MMCDriver(); + MMCDrive(); + ~MMCDrive() override; /** * adds the given request to a list and checkes the type of the @@ -17,7 +19,7 @@ class MMCDriver : public BDDriver * or the function returns otherwise. * */ - uint32 addRequest(BDRequest *); + uint32_t addRequest(BDRequest *) override; /** * @param 1 sector where it should be started to read @@ -25,7 +27,7 @@ class MMCDriver : public BDDriver * @param 3 buffer where to save all that was read * */ - int32 readSector(uint32, uint32, void *); + int32_t readSector(uint32_t, uint32_t, void *) override; /** * @param 1 sector where it should be started to write @@ -33,12 +35,12 @@ class MMCDriver : public BDDriver * @param 3 buffer, which content should be written to the sectors * */ - int32 writeSector(uint32, uint32, void *); + int32_t writeSector(uint32_t, uint32_t, void *) override; - uint32 getNumSectors(); - uint32 getSectorSize(); - void serviceIRQ(); - uint32 SPT; + uint32_t getNumSectors() override; + uint32_t getSectorSize() override; + void serviceIRQ() override; + uint32_t SPT; private: /** @@ -46,18 +48,31 @@ class MMCDriver : public BDDriver * @param 2 buffer where to save the block that was read * */ - int32 readBlock(uint32, void *); + int32_t readBlock(uint32_t, void *); /** * @param 1 start address * @param 2 buffer, which content should be written to the bloc * */ - int32 writeBlock(uint32, void *); + int32_t writeBlock(uint32_t, void *); Mutex lock_; - uint32 rca_; - uint32 sector_size_; - uint32 num_sectors_; + uint32_t rca_; + uint32_t sector_size_; + uint32_t num_sectors_; }; +class MMCDeviceDriver : public BasicDeviceDriver, + public Driver +{ +public: + MMCDeviceDriver(); + ~MMCDeviceDriver() override = default; + + static MMCDeviceDriver& instance(); + + void doDeviceDetection() override; + +private: +}; diff --git a/arch/arm/common/include/offsets.h b/arch/arm/common/include/offsets.h index 56f34c951..91a94a04e 100644 --- a/arch/arm/common/include/offsets.h +++ b/arch/arm/common/include/offsets.h @@ -36,3 +36,4 @@ */ #define USER_BREAK 0x80000000 +#define KERNEL_START 0x80000000 diff --git a/arch/arm/common/include/paging-definitions.h b/arch/arm/common/include/paging-definitions.h index b65862e50..817579c52 100644 --- a/arch/arm/common/include/paging-definitions.h +++ b/arch/arm/common/include/paging-definitions.h @@ -64,3 +64,16 @@ typedef struct static_assert(sizeof(PageTableEntry) == 4, "PageTableEntry is not 32 bit"); +struct VAddr +{ + union + { + uint32 addr; + struct + { + uint32 offset : 12; + uint32 pti : 8; + uint32 pdi : 12; + } __attribute__((__packed__));; + } __attribute__((__packed__));; +} __attribute__((__packed__)); diff --git a/arch/arm/common/include/types.h b/arch/arm/common/include/types.h index d29264eff..274ed5d03 100644 --- a/arch/arm/common/include/types.h +++ b/arch/arm/common/include/types.h @@ -1,28 +1,34 @@ #pragma once -typedef char int8; -typedef unsigned char uint8; +#include "stdint.h" +#include "klibc/sys/types.h" -typedef short int16; -typedef unsigned short uint16; +typedef int8_t int8; +typedef uint8_t uint8; -typedef int int32; -typedef unsigned int uint32; +typedef int16_t int16; +typedef uint16_t uint16; -typedef unsigned long long uint64; -typedef long long int64; +typedef int32_t int32; +typedef uint32_t uint32; -typedef uint32 pointer; +typedef uint64_t uint64; +typedef int64_t int64; -typedef uint32 l_off_t; +typedef uint32_t pointer; -typedef uint32 mode_t; -typedef uint32 uid_t; -typedef uint32 gid_t; -typedef uint32 size_t; -typedef int32 ssize_t; +typedef uint32_t l_off_t; -#pragma GCC poison double float +typedef uint32_t mode_t; +typedef uint32_t uid_t; +typedef uint32_t gid_t; + +typedef __SIZE_TYPE__ size_t; + +typedef size_t ppn_t; +typedef size_t vpn_t; + +/* #pragma GCC poison double float */ #define Min(x,y) (((x)<(y))?(x):(y)) #define Max(x,y) (((x)>(y))?(x):(y)) @@ -30,4 +36,3 @@ typedef int32 ssize_t; #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) #define unreachable() __builtin_unreachable() - diff --git a/arch/arm/common/include/usb/usb_types.h b/arch/arm/common/include/usb/usb_types.h index 3a6e60f1e..10d809ac6 100644 --- a/arch/arm/common/include/usb/usb_types.h +++ b/arch/arm/common/include/usb/usb_types.h @@ -15,7 +15,9 @@ extern "C" { #endif +#ifndef NULL #define NULL ((void*)0) +#endif /** Unsigned 8 bit type */ typedef unsigned char u8; diff --git a/arch/arm/common/source/ArchCommon.cpp b/arch/arm/common/source/ArchCommon.cpp index 3019505ff..13b017fb7 100644 --- a/arch/arm/common/source/ArchCommon.cpp +++ b/arch/arm/common/source/ArchCommon.cpp @@ -1,17 +1,33 @@ #include "ArchCommon.h" -#include "ArchBoardSpecific.h" -#include "offsets.h" -#include "kprintf.h" -#include "ArchMemory.h" -#include "TextConsole.h" + #include "FrameBufferConsole.h" -#include "backtrace.h" +#include "KernelMemoryManager.h" +#include "MMCDriver.h" +#include "PlatformBus.h" +#include "SMP.h" +#include "SerialManager.h" #include "Stabs2DebugInfo.h" +#include "TextConsole.h" +#include "backtrace.h" +#include "kprintf.h" +#include "offsets.h" + +#include "ArchBoardSpecific.h" +#include "ArchCpuLocalStorage.h" +#include "ArchMemory.h" #define PHYSICAL_MEMORY_AVAILABLE 8*1024*1024 +RangeAllocator<> mmio_addr_allocator; + +extern void* kernel_start_address; extern void* kernel_end_address; +pointer ArchCommon::getKernelStartAddress() +{ + return (pointer)&kernel_start_address; +} + pointer ArchCommon::getKernelEndAddress() { return (pointer)&kernel_end_address; @@ -28,68 +44,73 @@ pointer ArchCommon::getFreeKernelMemoryEnd() } -uint32 ArchCommon::haveVESAConsole(uint32 is_paging_set_up __attribute__((unused))) +size_t ArchCommon::haveVESAConsole(size_t is_paging_set_up __attribute__((unused))) { return true; } -uint32 ArchCommon::getNumModules(uint32 is_paging_set_up __attribute__((unused))) +size_t ArchCommon::getNumModules(size_t is_paging_set_up __attribute__((unused))) { return 1; } -uint32 ArchCommon::getModuleStartAddress(uint32 num __attribute__((unused)), uint32 is_paging_set_up __attribute__((unused))) +size_t ArchCommon::getModuleStartAddress(size_t num __attribute__((unused)), size_t is_paging_set_up __attribute__((unused))) { return 0x80000000U; } -uint32 ArchCommon::getModuleEndAddress(uint32 num __attribute__((unused)), uint32 is_paging_set_up __attribute__((unused))) +size_t ArchCommon::getModuleEndAddress(size_t num __attribute__((unused)), size_t is_paging_set_up __attribute__((unused))) { return getKernelEndAddress(); } -uint32 ArchCommon::getVESAConsoleHeight() +const char* ArchCommon::getModuleName([[maybe_unused]]size_t num, [[maybe_unused]]size_t is_paging_set_up) +{ + return "kernel"; +} + +size_t ArchCommon::getVESAConsoleHeight() { return 480; } -uint32 ArchCommon::getVESAConsoleWidth() +size_t ArchCommon::getVESAConsoleWidth() { return 640; } -pointer ArchCommon::getVESAConsoleLFBPtr(uint32 is_paging_set_up __attribute__((unused))) +pointer ArchCommon::getVESAConsoleLFBPtr(size_t is_paging_set_up __attribute__((unused))) { return ArchBoardSpecific::getVESAConsoleLFBPtr(); } -pointer ArchCommon::getFBPtr(uint32 is_paging_set_up __attribute__((unused))) +pointer ArchCommon::getFBPtr(size_t is_paging_set_up __attribute__((unused))) { return getVESAConsoleLFBPtr(); } -uint32 ArchCommon::getVESAConsoleBitsPerPixel() +size_t ArchCommon::getVESAConsoleBitsPerPixel() { return 16; } -uint32 ArchCommon::getNumUseableMemoryRegions() +size_t ArchCommon::getNumUseableMemoryRegions() { return 1; } -uint32 ArchCommon::getUsableMemoryRegion(uint32 region, pointer &start_address, pointer &end_address, uint32 &type) +size_t ArchCommon::getUseableMemoryRegion(size_t region, pointer &start_address, pointer &end_address, size_t &type) { return ArchBoardSpecific::getUsableMemoryRegion(region, start_address, end_address, type); } -Console* ArchCommon::createConsole(uint32 count) +Console* ArchCommon::createConsole(size_t count) { ArchBoardSpecific::frameBufferInit(); return new FrameBufferConsole(count); } -Stabs2DebugInfo const *kernel_debug_info = 0; +const Stabs2DebugInfo* kernel_debug_info = 0; void ArchCommon::initDebug() { @@ -98,13 +119,12 @@ void ArchCommon::initDebug() extern unsigned char stabstr_start_address_nr; - kernel_debug_info = new Stabs2DebugInfo((char const *)&stab_start_address_nr, - (char const *)&stab_end_address_nr, - (char const *)&stabstr_start_address_nr); - + kernel_debug_info = + new Stabs2DebugInfo((const char*)&stab_start_address_nr, (const char*)&stab_end_address_nr, + (const char*)&stabstr_start_address_nr); } -extern "C" void halt() +void ArchCommon::halt() { asm("mcr p15, 0, %[v], c7, c0, 4" : : [v]"r" (0)); // Wait for interrupt } @@ -115,10 +135,36 @@ void ArchCommon::idle() halt(); } +void ArchCommon::spinlockPause() +{ +} + +uint64 ArchCommon::cpuTimestamp() +{ + uint64 timestamp; + + // // Read PMCCNTR/CCNT Register + // // https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/System-Control-Registers-in-a-PMSA-implementation/PMSA-System-control-registers-descriptions--in-register-order/PMCCNTR--Performance-Monitors-Cycle-Count-Register--PMSA?lang=en + // asm volatile ("mrc p15, 0, %0, c9, c13, 0\n" + // : "=r"(timestamp)); + + // // Read physical count register + // // https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/System-Control-Registers-in-a-PMSA-implementation/PMSA-System-control-registers-descriptions--in-register-order/CNTPCT--Physical-Count-register--PMSA?lang=en + // asm("mrrc p15, 0, %Q0, %R0, c14\n" + // : "=r" (timestamp)); + + // Read virtual count register + // https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/System-Control-Registers-in-a-PMSA-implementation/PMSA-System-control-registers-descriptions--in-register-order/CNTVCT--Virtual-Count-register--PMSA?lang=en + asm("mrrc p15, 1, %Q0, %R0, c14\n" + : "=r" (timestamp)); + return timestamp; +} + +// Called to register destructors of static variables at "program exit". +// The kernel never exits, so simply do nothing here extern "C" void __aeabi_atexit() { - assert(false && "would not make sense in a kernel"); } extern "C" void __aeabi_unwind_cpp_pr0() @@ -130,3 +176,41 @@ extern "C" void raise() { assert(false && "no exception handling implemented"); } + +void ArchCommon::postBootInit() +{ +} + +void ArchCommon::initPlatformDrivers() +{ + PlatformBus::instance().registerDriver(SerialManager::instance()); +} + +void ArchCommon::initBlockDeviceDrivers() +{ + PlatformBus::instance().registerDriver(MMCDeviceDriver::instance()); +} + +void ArchCommon::reservePagesPreKernelInit([[maybe_unused]]Allocator& alloc) +{ +} + +void ArchCommon::initKernelVirtualAddressAllocator() +{ + mmio_addr_allocator.setUseable(KERNEL_START, (size_t)-1); + mmio_addr_allocator.setUnuseable(getKernelStartAddress(), getKernelEndAddress()); + mmio_addr_allocator.setUnuseable(KernelMemoryManager::instance()->getKernelHeapStart(), KernelMemoryManager::instance()->getKernelHeapMaxEnd()); + // TODO: ident mapping end + // mmio_addr_allocator.setUnuseable(IDENT_MAPPING_START, IDENT_MAPPING_END); + debug(MAIN, "Usable MMIO ranges:\n"); + mmio_addr_allocator.printUsageInfo(); +} + +cpu_local size_t heart_beat_value = 0; +const char* clock = "/-\\|"; + +void ArchCommon::drawHeartBeat() +{ + ((FrameBufferConsole*)main_console)->consoleSetCharacter(0,SMP::currentCpuId(),clock[heart_beat_value], CONSOLECOLOR::GREEN); + heart_beat_value = (heart_beat_value + 1) % 4; +} diff --git a/arch/arm/common/source/ArchCpuLocalStorage.cpp b/arch/arm/common/source/ArchCpuLocalStorage.cpp new file mode 100644 index 000000000..fe8c6e488 --- /dev/null +++ b/arch/arm/common/source/ArchCpuLocalStorage.cpp @@ -0,0 +1,6 @@ +#include "ArchCpuLocalStorage.h" + +bool CpuLocalStorage::ClsInitialized() +{ + return true; +} diff --git a/arch/arm/common/source/ArchInterrupts.cpp b/arch/arm/common/source/ArchInterrupts.cpp index 7c1f68e8c..4d92a9f52 100644 --- a/arch/arm/common/source/ArchInterrupts.cpp +++ b/arch/arm/common/source/ArchInterrupts.cpp @@ -1,11 +1,18 @@ -#include "types.h" #include "ArchInterrupts.h" + +#include "InterruptUtils.h" +#include "Scheduler.h" +#include "SystemState.h" +#include "Thread.h" #include "kprintf.h" #include "kstring.h" -#include "InterruptUtils.h" -#include "ArchThreads.h" + #include "ArchBoardSpecific.h" -#include "Thread.h" +#include "ArchThreads.h" + +#include "types.h" + +cpu_local IrqDomain cpu_irq_vector_domain_("CPU interrupt vector"); extern uint8 boot_stack[]; @@ -96,7 +103,7 @@ void __attribute__((naked)) arch_irqHandler_ARM4_XRQ_SWINT() asm("mov sp, %[v]" : : [v]"r" (((uint32*)boot_stack) + 0x1000));\ SWITCH_CPU_MODE("0xdf"); -void ArchInterrupts::initialise() +static void initInterruptHandlers() { INSTALL_INTERRUPT_HANDLER(ARM4_XRQ_RESET, "0xD3"); INSTALL_INTERRUPT_HANDLER(ARM4_XRQ_UNDEF, "0xDB"); @@ -107,6 +114,11 @@ void ArchInterrupts::initialise() INSTALL_INTERRUPT_HANDLER(ARM4_XRQ_FIQ, "0xD1"); } +void ArchInterrupts::initialise() +{ + initInterruptHandlers(); +} + void ArchInterrupts::enableTimer() { ArchBoardSpecific::enableTimer(); diff --git a/arch/arm/common/source/ArchMemory.cpp b/arch/arm/common/source/ArchMemory.cpp index a39c6e6fc..4e8e0b142 100644 --- a/arch/arm/common/source/ArchMemory.cpp +++ b/arch/arm/common/source/ArchMemory.cpp @@ -1,9 +1,11 @@ #include "ArchMemory.h" -#include "kprintf.h" -#include "assert.h" + #include "PageManager.h" -#include "offsets.h" +#include "kprintf.h" #include "kstring.h" +#include "offsets.h" + +#include "assert.h" #define PT_SIZE 1024 #define PD_SIZE 16384 @@ -46,7 +48,7 @@ PageTableEntry* ArchMemory::getPTE(size_t vpn) ArchMemory::ArchMemory() { - page_dir_page_ = PageManager::instance()->allocPPN(PD_SIZE); + page_dir_page_ = PageManager::instance().allocPPN(PD_SIZE); debug(A_MEMORY, "ArchMemory::ArchMemory(): Got new Page no. %x\n", page_dir_page_); PageDirEntry *new_page_directory = (PageDirEntry*) getIdentAddressOfPPN(page_dir_page_); @@ -55,6 +57,12 @@ ArchMemory::ArchMemory() new_page_directory[p].pt.size = PDE_SIZE_NONE; } +ArchMemory::ArchMemory(uint32_t page_dir_page) +{ + page_dir_page_ = page_dir_page; + debug(A_MEMORY, "ArchMemory::ArchMemory(%zx)\n", page_dir_page_); +} + void ArchMemory::checkAndRemovePT(uint32 pde_vpn) { PageDirEntry *page_directory = (PageDirEntry *) getIdentAddressOfPPN(page_dir_page_); @@ -76,16 +84,16 @@ void ArchMemory::checkAndRemovePT(uint32 pde_vpn) for (size_t i = 0; i < 4; ++i) { uint32 pt_slot = (pt_ppn - PHYS_OFFSET_4K) * 4 + i; - if (ustl::find(pt_ppns_.begin(), pt_ppns_.end(), pt_slot) == pt_ppns_.end()) + if (eastl::find(pt_ppns_.begin(), pt_ppns_.end(), pt_slot) == pt_ppns_.end()) return; } - PageManager::instance()->freePPN(pt_ppn - PHYS_OFFSET_4K); + PageManager::instance().freePPN(pt_ppn - PHYS_OFFSET_4K); for (size_t i = 0; i < 4; ++i) { uint32 pt_slot = (pt_ppn - PHYS_OFFSET_4K) * 4 + i; - pt_ppns_.erase(ustl::find(pt_ppns_.begin(), pt_ppns_.end(), pt_slot)); + pt_ppns_.erase(eastl::find(pt_ppns_.begin(), pt_ppns_.end(), pt_slot)); } } @@ -102,7 +110,7 @@ void ArchMemory::unmapPage(uint32 virtual_page) assert(pte_base[pte_vpn].size == PTE_SIZE_SMALL); pte_base[pte_vpn].size = PTE_SIZE_NONE; - PageManager::instance()->freePPN(pte_base[pte_vpn].page_ppn - PHYS_OFFSET_4K); + PageManager::instance().freePPN(pte_base[pte_vpn].page_ppn - PHYS_OFFSET_4K); ((uint32*)pte_base)[pte_vpn] = 0; // for easier debugging checkAndRemovePT(pde_vpn); @@ -121,7 +129,7 @@ void ArchMemory::insertPT(uint32 pde_vpn) } else { - physical_page_table_page = PageManager::instance()->allocPPN(); + physical_page_table_page = PageManager::instance().allocPPN(); offset = 0; for (size_t i = 1; i < 4; ++i) pt_ppns_.push_back(physical_page_table_page * 4 + i); @@ -174,7 +182,7 @@ ArchMemory::~ArchMemory() if (pte_base[pte_vpn].size == PTE_SIZE_SMALL) { pte_base[pte_vpn].size = PTE_SIZE_NONE; - PageManager::instance()->freePPN(pte_base[pte_vpn].page_ppn - PHYS_OFFSET_4K); + PageManager::instance().freePPN(pte_base[pte_vpn].page_ppn - PHYS_OFFSET_4K); } } page_directory[pde_vpn].pt.size = PDE_SIZE_NONE; @@ -182,17 +190,17 @@ ArchMemory::~ArchMemory() bool free_pt_page = true; for (size_t i = 0; i < 4; ++i) { - auto it = ustl::find(pt_ppns_.begin(), pt_ppns_.end(), page_directory[pde_vpn].pt.pt_ppn * 4 + i); + auto it = eastl::find(pt_ppns_.begin(), pt_ppns_.end(), page_directory[pde_vpn].pt.pt_ppn * 4 + i); if (it == pt_ppns_.end()) free_pt_page = false; else pt_ppns_.erase(it); } if (free_pt_page) - PageManager::instance()->freePPN(page_directory[pde_vpn].pt.pt_ppn - PHYS_OFFSET_4K); + PageManager::instance().freePPN(page_directory[pde_vpn].pt.pt_ppn - PHYS_OFFSET_4K); } } - PageManager::instance()->freePPN(page_dir_page_, PD_SIZE); + PageManager::instance().freePPN(page_dir_page_, PD_SIZE); } pointer ArchMemory::checkAddressValid(uint32 vaddress_to_check) @@ -245,17 +253,22 @@ uint32 ArchMemory::get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, uint32 * return 0; } -void ArchMemory::mapKernelPage(uint32 virtual_page, uint32 physical_page) +bool ArchMemory::mapKernelPage(size_t virtual_page, size_t physical_page, [[maybe_unused]]bool can_alloc_pages, [[maybe_unused]]bool memory_mapped_io) { PageDirEntry *page_directory = kernel_page_directory; uint32 pde_vpn = virtual_page / PAGE_TABLE_ENTRIES; uint32 pte_vpn = virtual_page % PAGE_TABLE_ENTRIES; assert(page_directory[pde_vpn].page.size == PDE_SIZE_PT && "kernel page table has to be mapped already"); PageTableEntry *pte_base = getIdentAddressOfPT(page_directory, pde_vpn); - assert(pte_base[pte_vpn].size == PTE_SIZE_NONE && "tried to map page but there was already a page mapped"); + if (pte_base[pte_vpn].size != PTE_SIZE_NONE) { + return false; + } + // assert(pte_base[pte_vpn].size == PTE_SIZE_NONE && "tried to map page but there was already a page mapped"); pte_base[pte_vpn].permissions = PAGE_PERMISSION_KERNEL; pte_base[pte_vpn].page_ppn = physical_page + PHYS_OFFSET_4K; pte_base[pte_vpn].size = PTE_SIZE_SMALL; + + return true; } void ArchMemory::unmapKernelPage(uint32 virtual_page) @@ -268,10 +281,32 @@ void ArchMemory::unmapKernelPage(uint32 virtual_page) assert(pte_base[pte_vpn].size != PTE_SIZE_SMALL && "tried to unmap page but there was no page mapped"); pte_base[pte_vpn].size = PTE_SIZE_NONE; pte_base[pte_vpn].permissions = PAGE_PERMISSION_NONE; - PageManager::instance()->freePPN(pte_base[pte_vpn].page_ppn - PHYS_OFFSET_4K); + PageManager::instance().freePPN(pte_base[pte_vpn].page_ppn - PHYS_OFFSET_4K); } uint32 ArchMemory::getRootOfPagingStructure() { return page_dir_page_; } + +PageDirEntry* ArchMemory::getKernelPagingStructureRootVirt() +{ + return kernel_page_directory; +} + +size_t ArchMemory::getKernelPagingStructureRootPhys() +{ + return (size_t)VIRTUAL_TO_PHYSICAL_BOOT((size_t)getKernelPagingStructureRootVirt()); +} + +void ArchMemory::loadPagingStructureRoot(size_t ttbr0_value) +{ + __asm__ __volatile__("MCR p15, 0, %[ttbr0], c2, c0, 0\n" + ::[ttbr0]"r"(ttbr0_value)); +} + +ArchMemory& ArchMemory::kernelArchMemory() +{ + static ArchMemory kernel_arch_mem((size_t)getKernelPagingStructureRootPhys()/PAGE_SIZE); + return kernel_arch_mem; +} diff --git a/arch/arm/common/source/ArchMulticore.cpp b/arch/arm/common/source/ArchMulticore.cpp new file mode 100644 index 000000000..8b22e133e --- /dev/null +++ b/arch/arm/common/source/ArchMulticore.cpp @@ -0,0 +1,63 @@ +#include "ArchMulticore.h" + +#include "SMP.h" + +#include "ArchInterrupts.h" + +#include "debug.h" + +extern eastl::atomic running_cpus; + +ArchCpu::ArchCpu() +{ + setId(0); + debug(A_MULTICORE, "Initializing ArchCpu %zx\n", id()); + SMP::addCpuToList(this); +} + +IrqDomain& ArchCpu::rootIrqDomain() +{ + if (static bool initialized = false; !initialized) + { + initialized = true; + new (&cpu_irq_vector_domain_) IrqDomain("CPU interrupt vector"); + } + + return cpu_irq_vector_domain_; +} + +void ArchMulticore::initialize() +{ + assert(running_cpus == 0); + running_cpus = 1; + // CPULocalStorage::setCLS(CPULocalStorage::allocCLS()); + ArchMulticore::initCpuLocalData(true); +} + +void ArchMulticore::initCpuLocalData([[maybe_unused]]bool boot_cpu) +{ + // The constructor of objects declared as cpu_local will be called automatically + // the first time the cpu_local object is used. + // cpu_local isn't actually cpu_local on armv8 (no cls implemented) -> initialized via global + // constructors + (void)SMP::currentCpuId(); + debug(A_MULTICORE, "Initializing CPU local objects for CPU %zu\n", SMP::currentCpuId()); + + // idle_thread = new IdleThread(); + // debug(A_MULTICORE, "CPU %zu: %s initialized\n", getCpuID(), idle_thread->getName()); + // idle_thread->pinned_to_cpu = getCpuID(); + // Scheduler::instance()->addNewThread(idle_thread); +} + +void ArchMulticore::startOtherCPUs() +{ +} + +void ArchMulticore::stopOtherCpus() +{ +} + +void ArchMulticore::sendFunctionCallMessage([[maybe_unused]]ArchCpu& cpu, [[maybe_unused]]RemoteFunctionCallMessage* fcall_message) +{ + assert(false && "Not implemented"); +} diff --git a/arch/arm/common/source/ArchThreads.cpp b/arch/arm/common/source/ArchThreads.cpp index 9a801b0af..4fa0a5098 100644 --- a/arch/arm/common/source/ArchThreads.cpp +++ b/arch/arm/common/source/ArchThreads.cpp @@ -1,13 +1,15 @@ #include "ArchThreads.h" -#include "ArchMemory.h" -#include "kprintf.h" -#include "paging-definitions.h" -#include "offsets.h" -#include "Thread.h" + #include "Scheduler.h" #include "SpinLock.h" +#include "Thread.h" +#include "kprintf.h" +#include "offsets.h" +#include "paging-definitions.h" -SpinLock global_atomic_add_lock(""); +#include "ArchMemory.h" + +SpinLock global_atomic_add_lock("global_atomic_add_lock"); extern PageDirEntry kernel_page_directory[]; @@ -28,94 +30,76 @@ void ArchThreads::setAddressSpace(Thread *thread, ArchMemory& arch_memory) thread->user_registers_->ttbr0 = LOAD_BASE + arch_memory.page_dir_page_ * PAGE_SIZE; } -void ArchThreads::createKernelRegisters(ArchThreadRegisters *&info, void* start_function, void* stack) +void ArchThreads::switchToAddressSpace(Thread* thread) { - info = (ArchThreadRegisters*)new uint8[sizeof(ArchThreadRegisters)]; - memset((void*)info, 0, sizeof(ArchThreadRegisters)); - pointer pageDirectory = VIRTUAL_TO_PHYSICAL_BOOT(((pointer)kernel_page_directory)); - assert((pageDirectory) != 0); - assert(((pageDirectory) & 0x3FFF) == 0); - assert(!((pointer)start_function & 0x3)); - info->pc = (pointer)start_function; - info->lr = (pointer)start_function; - info->cpsr = 0x6000001F; - info->sp = (pointer)stack & ~0xF; - info->r[11] = (pointer)stack & ~0xF; // r11 is the fp - info->ttbr0 = pageDirectory; - assert((pageDirectory) != 0); - assert(((pageDirectory) & 0x3FFF) == 0); + ArchMemory::loadPagingStructureRoot(thread->kernel_registers_->ttbr0); } -void ArchThreads::changeInstructionPointer(ArchThreadRegisters *info, void* function) +void ArchThreads::switchToAddressSpace(ArchMemory& arch_memory) { - info->pc = (pointer)function; - info->lr = (pointer)function; + size_t ttbr0_value = (LOAD_BASE + arch_memory.page_dir_page_ * PAGE_SIZE); + ArchMemory::loadPagingStructureRoot(ttbr0_value); } -void ArchThreads::createUserRegisters(ArchThreadRegisters *&info, void* start_function, void* user_stack, void* kernel_stack) +eastl::unique_ptr ArchThreads::createKernelRegisters(void* start_function, void* stack) { - info = (ArchThreadRegisters*)new uint8[sizeof(ArchThreadRegisters)]; - memset((void*)info, 0, sizeof(ArchThreadRegisters)); - pointer pageDirectory = VIRTUAL_TO_PHYSICAL_BOOT(((pointer)kernel_page_directory)); - assert((pageDirectory) != 0); - assert(((pageDirectory) & 0x3FFF) == 0); assert(!((pointer)start_function & 0x3)); - info->pc = (pointer)start_function; - info->lr = (pointer)start_function; - info->cpsr = 0x60000010; - info->sp = (pointer)user_stack & ~0xF; - info->r[11] = (pointer)user_stack & ~0xF; // r11 is the fp - info->sp0 = (pointer)kernel_stack & ~0xF; - info->ttbr0 = pageDirectory; + + auto regs = eastl::make_unique(); + + pointer pageDirectory = VIRTUAL_TO_PHYSICAL_BOOT(((pointer)kernel_page_directory)); assert((pageDirectory) != 0); assert(((pageDirectory) & 0x3FFF) == 0); -} -void ArchThreads::yield() -{ - asm("swi #0xffff"); -} + regs->pc = (pointer)start_function; + regs->lr = (pointer)start_function; + regs->cpsr = 0x6000001F; + regs->sp = (pointer)stack & ~0xF; + regs->r[11] = (pointer)stack & ~0xF; // r11 is the fp + regs->ttbr0 = pageDirectory; -extern "C" void memory_barrier(); -extern "C" uint32 arch_TestAndSet(uint32, uint32, uint32 new_value, uint32 *lock); -uint32 ArchThreads::testSetLock(uint32 &lock, uint32 new_value) -{ - uint32 result; - memory_barrier(); - asm("swp %[r], %[n], [%[l]]" : [r]"=&r"(result) : [n]"r"(new_value), [l]"r"(&lock)); - memory_barrier(); - return result; + return regs; } -extern "C" uint32 arch_atomic_add(uint32, uint32, uint32 increment, uint32 *value); -uint32 ArchThreads::atomic_add(uint32 &value, int32 increment) +void ArchThreads::changeInstructionPointer(ArchThreadRegisters& info, void* function) { - global_atomic_add_lock.acquire(); - uint32 result = value; - value += increment; - global_atomic_add_lock.release(); - return result; + info.pc = (pointer)function; + info.lr = (pointer)function; } -int32 ArchThreads::atomic_add(int32 &value, int32 increment) +void* ArchThreads::getInstructionPointer(ArchThreadRegisters& info) { - return (int32) ArchThreads::atomic_add((uint32 &) value, increment); + return (void*)info.pc; } -uint64 ArchThreads::atomic_add(uint64 &value, int64 increment) +eastl::unique_ptr ArchThreads::createUserRegisters(void* start_function, void* user_stack, void* kernel_stack) { - global_atomic_add_lock.acquire(); - uint64 result = value; - value += increment; - global_atomic_add_lock.release(); - return result; + assert(!((pointer)start_function & 0x3)); + + auto regs = eastl::make_unique(); + + pointer pageDirectory = VIRTUAL_TO_PHYSICAL_BOOT(((pointer)kernel_page_directory)); + assert((pageDirectory) != 0); + assert(((pageDirectory) & 0x3FFF) == 0); + + regs->pc = (pointer)start_function; + regs->lr = (pointer)start_function; + regs->cpsr = 0x60000010; + regs->sp = (pointer)user_stack & ~0xF; + regs->r[11] = (pointer)user_stack & ~0xF; // r11 is the fp + regs->sp0 = (pointer)kernel_stack & ~0xF; + regs->ttbr0 = pageDirectory; + + return regs; } -int64 ArchThreads::atomic_add(int64 &value, int64 increment) +void ArchThreads::yield() { - return (int64) ArchThreads::atomic_add((uint64 &) value, increment); + asm("swi #0xffff"); } +extern "C" uint32 arch_TestAndSet(uint32, uint32, uint32 new_value, uint32 *lock); + void ArchThreads::printThreadRegisters(Thread *thread, bool verbose) { printThreadRegisters(thread,0,verbose); @@ -124,7 +108,7 @@ void ArchThreads::printThreadRegisters(Thread *thread, bool verbose) void ArchThreads::printThreadRegisters(Thread *thread, uint32 userspace_registers, bool verbose) { - ArchThreadRegisters *info = userspace_registers?thread->user_registers_:thread->kernel_registers_; + ArchThreadRegisters *info = userspace_registers ? thread->user_registers_.get() : thread->kernel_registers_.get(); if (!info) { kprintfd("%sThread: %18p, has no %s registers. %s\n",userspace_registers?" User":"Kernel",thread,userspace_registers?"User":"Kernel",userspace_registers?"":"This should never(!) occur. How did you do that?"); @@ -145,18 +129,6 @@ void ArchThreads::printThreadRegisters(Thread *thread, uint32 userspace_register } - -void ArchThreads::atomic_set(uint32& target, uint32 value) -{ - // just re-use the method for exchange. Under ARM the build-ins do not work... - testSetLock(target, value); -} - -void ArchThreads::atomic_set(int32& target, int32 value) -{ - atomic_set((uint32&)target, (uint32)value); -} - extern "C" void threadStartHack(); void ArchThreads::debugCheckNewThread(Thread* thread) @@ -168,7 +140,7 @@ void ArchThreads::debugCheckNewThread(Thread* thread) assert(thread->kernel_registers_->sp0 == 0 && "kernel register set needs no backup of kernel esp"); assert(thread->kernel_registers_->sp == thread->kernel_registers_->r[11] && "new kernel stack must be empty"); assert(thread->kernel_registers_->sp != currentThread->kernel_registers_->sp && thread->kernel_registers_->r[11] != currentThread->kernel_registers_->r[11] && "all threads need their own stack"); - assert(thread->kernel_registers_->ttbr0 < 0x80000000 - BOARD_LOAD_BASE && "ttbr0 contains the physical page dir address"); + assert(thread->kernel_registers_->ttbr0 < 0x80000000 - BOARD_LOAD_BASE && "ttbr0 needs to contain the physical page dir address"); if (thread->user_registers_ == 0) return; assert(thread->kernel_registers_->pc == 0 && "user threads should not start execution in kernel mode"); @@ -180,3 +152,10 @@ void ArchThreads::debugCheckNewThread(Thread* thread) return; assert(currentThread->user_registers_->sp0 != thread->user_registers_->sp0 && "no 2 threads may have the same esp0 value"); } + +[[noreturn]] void ArchThreads::startThreads([[maybe_unused]]Thread* init_thread) +{ + ArchInterrupts::enableInterrupts(); + yield(); + assert(false); +} diff --git a/arch/arm/common/source/IDEDriver.cpp b/arch/arm/common/source/IDEDriver.cpp deleted file mode 100644 index f6cf0ab0f..000000000 --- a/arch/arm/common/source/IDEDriver.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "BDManager.h" -#include "BDVirtualDevice.h" -#include "IDEDriver.h" -#include "MMCDriver.h" -#include "kstring.h" -#include "ArchInterrupts.h" -#include "kprintf.h" - -uint32 IDEDriver::doDeviceDetection() -{ - const char* name = "idea"; - MMCDriver* drv = new MMCDriver(); - BDVirtualDevice *bdv = new BDVirtualDevice(drv, 0, drv->getNumSectors(), drv->getSectorSize(), name, true); - BDManager::getInstance()->addVirtualDevice(bdv); - debug(IDE_DRIVER, "doDetection: initialized with MMCDriver!\n"); - processMBR(drv, 0, drv->SPT, name); - return 1; -} - -int32 IDEDriver::processMBR(BDDriver * drv, uint32 sector, uint32 SPT, const char *name) -{ - uint32 offset = 0, numsec = 0; - uint16 buff[256]; // read buffer - debug(IDE_DRIVER, "processMBR:reading MBR\n"); - - static uint32 part_num = 0; -// char part_num_str[2]; -// char part_name[10]; - - uint32 read_res = drv->readSector(sector, 1, (void *) buff); - - if (read_res != 0) - { - debug(IDE_DRIVER, "processMBR: drv returned BD_ERROR\n"); - return -1; - } - - MBR *mbr = (MBR *) buff; - - if (mbr->signature == 0xAA55) - { - debug(IDE_DRIVER, "processMBR: | Valid PC MBR | \n"); - FP * fp = (FP *) mbr->parts; - uint32 i; - for (i = 0; i < 4; i++, fp++) - { - switch (fp->systid) - { - case 0x00: - // do nothing - break; - case 0x05: // DOS extended partition - case 0x0F: // Windows extended partition - case 0x85: // linux extended partition - debug(IDE_DRIVER, "ext. part. at: %d \n", fp->relsect); - if (processMBR(drv, sector + fp->relsect, SPT, name) == -1) - processMBR(drv, sector + fp->relsect - SPT, SPT, name); - break; - default: - // offset = fp->relsect - SPT; - offset = fp->relsect; - numsec = fp->numsect; - - char part_name[6]; - strncpy(part_name, name, 4); - part_name[4] = part_num + '0'; - part_name[5] = 0; - part_num++; - BDVirtualDevice *bdv = new BDVirtualDevice(drv, offset, numsec, drv->getSectorSize(), part_name, true); - - // set Partition Type (FileSystem identifier) - bdv->setPartitionType(fp->systid); - - BDManager::getInstance()->addVirtualDevice(bdv); - break; - } - } - } - else - { - debug(IDE_DRIVER, "processMBR: | Invalid PC MBR %d | \n", mbr->signature); - return -1; - } - - debug(IDE_DRIVER, "processMBR:, done with partitions \n"); - return 0; -} diff --git a/arch/arm/common/source/InterruptUtils.cpp b/arch/arm/common/source/InterruptUtils.cpp index f45e038c5..484971a92 100644 --- a/arch/arm/common/source/InterruptUtils.cpp +++ b/arch/arm/common/source/InterruptUtils.cpp @@ -1,29 +1,30 @@ #include "InterruptUtils.h" -#include "ArchBoardSpecific.h" #include "BDManager.h" -#include "KeyboardManager.h" -#include "new.h" -#include "ArchMemory.h" -#include "ArchThreads.h" -#include "ArchCommon.h" #include "Console.h" #include "FrameBufferConsole.h" -#include "Terminal.h" -#include "kprintf.h" +#include "KeyboardManager.h" +#include "Loader.h" +#include "PageFaultHandler.h" #include "Scheduler.h" +#include "Syscall.h" +#include "Terminal.h" +#include "Thread.h" +#include "TimerTickHandler.h" +#include "backtrace.h" #include "debug_bochs.h" +#include "kprintf.h" +#include "new.h" +#include "paging-definitions.h" -#include "panic.h" - -#include "Thread.h" +#include "ArchBoardSpecific.h" +#include "ArchCommon.h" #include "ArchInterrupts.h" -#include "backtrace.h" +#include "ArchMemory.h" +#include "ArchThreads.h" -#include "Loader.h" -#include "Syscall.h" -#include "paging-definitions.h" -#include "PageFaultHandler.h" +#include "assert.h" +#include "debug.h" extern uint32* currentStack; extern Console* main_console; @@ -78,18 +79,13 @@ void pageFaultHandler(uint32 address, uint32 type) } present = !((status == FLAG_TRANSLATION_PAGE) || (status == FLAG_TRANSLATION_SECTION)); - PageFaultHandler::enterPageFault(address, currentThread->switch_to_userspace_, present, writing, fetch); + // TODO: use real ip + PageFaultHandler::enterPageFault(address, 0, currentThread->switch_to_userspace_, present, writing, fetch); } void timer_irq_handler() { - static uint32 heart_beat_value = 0; - const char* clock = "/-\\|"; - ((FrameBufferConsole*)main_console)->consoleSetCharacter(0,0,clock[heart_beat_value],Console::GREEN); - heart_beat_value = (heart_beat_value + 1) % 4; - - Scheduler::instance()->incTicks(); - Scheduler::instance()->schedule(); + TimerTickHandler::handleTimerTick(); } void arch_uart1_irq_handler() @@ -116,7 +112,7 @@ void arch_swi_irq_handler() else if (swi == 0x0) // syscall { currentThread->switch_to_userspace_ = 0; - currentThreadRegisters = currentThread->kernel_registers_; + currentThreadRegisters = currentThread->kernel_registers_.get(); ArchInterrupts::enableInterrupts(); auto ret = Syscall::syscallException(currentThread->user_registers_->r[0], currentThread->user_registers_->r[1], @@ -127,7 +123,7 @@ void arch_swi_irq_handler() currentThread->user_registers_->r[0] = ret; ArchInterrupts::disableInterrupts(); currentThread->switch_to_userspace_ = 1; - currentThreadRegisters = currentThread->user_registers_; + currentThreadRegisters = currentThread->user_registers_.get(); } else { @@ -142,7 +138,10 @@ extern "C" void exceptionHandler(uint32 type) { assert(!currentThread || currentThread->isStackCanaryOK()); debug(A_INTERRUPTS, "InterruptUtils::exceptionHandler: type = %x\n", type); - assert((currentThreadRegisters->cpsr & (0xE0)) == 0); + + // TODO: should probably only check bits 6 and 7 ? (not 5) + // https://developer.arm.com/documentation/ddi0406/b/System-Level-Architecture/The-System-Level-Programmers--Model/ARM-processor-modes-and-core-registers/Program-Status-Registers--PSRs- + assert((currentThreadRegisters->spsr & (0xE0)) == 0 && "interrupt occurred while IRQ/FIQ masked"); if (!currentThread) { Scheduler::instance()->schedule(); @@ -165,7 +164,7 @@ extern "C" void exceptionHandler(uint32 type) kprintfd("\nCPU Fault type = %x\n",type); ArchThreads::printThreadRegisters(currentThread,false); currentThread->switch_to_userspace_ = 0; - currentThreadRegisters = currentThread->kernel_registers_; + currentThreadRegisters = currentThread->kernel_registers_.get(); ArchInterrupts::enableInterrupts(); currentThread->kill(); for(;;); diff --git a/arch/arm/common/source/SerialManager.cpp b/arch/arm/common/source/SerialManager.cpp index 512114777..294e8e0dc 100644 --- a/arch/arm/common/source/SerialManager.cpp +++ b/arch/arm/common/source/SerialManager.cpp @@ -1,35 +1,31 @@ -#include "ArchSerialInfo.h" #include "SerialManager.h" -#include "kstring.h" #include "debug_bochs.h" #include "kprintf.h" +#include "kstring.h" -SerialManager * SerialManager::instance_ = 0; +#include "ArchSerialInfo.h" -SerialManager::SerialManager() : num_ports( 0 ) +SerialManager::SerialManager() : + BasicDeviceDriver("Serial Port Driver"), + num_ports(0) { - assert(false); -}; +} -SerialManager::~SerialManager() +void SerialManager::doDeviceDetection() { - assert(false); -}; +} -uint32 SerialManager::get_num_ports() +uint32 SerialManager::get_num_ports() const { - assert(false); return num_ports; -}; +} -uint32 SerialManager::do_detection(uint32 is_paging_set_up __attribute__((unused))) +uint32 SerialManager::do_detection([[maybe_unused]]uint32 is_paging_set_up) { - assert(false); return num_ports; } -void SerialManager::service_irq(uint32 irq_num __attribute__((unused))) +void SerialManager::service_irq([[maybe_unused]]uint32 irq_num) { - assert(false); } diff --git a/arch/arm/common/source/arch_backtrace.cpp b/arch/arm/common/source/arch_backtrace.cpp index f5200538d..3d2292509 100644 --- a/arch/arm/common/source/arch_backtrace.cpp +++ b/arch/arm/common/source/arch_backtrace.cpp @@ -1,11 +1,14 @@ -#include "kprintf.h" -#include "Thread.h" #include "arch_backtrace.h" + #include "InterruptUtils.h" -#include "ArchThreads.h" #include "KernelMemoryManager.h" // for use of "kernel_end_address" -#include "umap.h" +#include "Thread.h" +#include "kprintf.h" + #include "ArchCommon.h" +#include "ArchThreads.h" + +#include "EASTL/map.h" struct StackFrame { @@ -76,5 +79,3 @@ int backtrace_user(pointer *call_stack, int size, Thread *thread, bool /*use_sto return i; } - - diff --git a/arch/arm/common/source/assert.cpp b/arch/arm/common/source/assert.cpp index 101ac6849..8a716236d 100644 --- a/arch/arm/common/source/assert.cpp +++ b/arch/arm/common/source/assert.cpp @@ -1,19 +1,19 @@ #include "assert.h" -#include "kprintf.h" -#include "panic.h" -#include "debug_bochs.h" + +#include "SystemState.h" #include "Thread.h" +#include "debug_bochs.h" +#include "kprintf.h" + #include "ArchInterrupts.h" extern Thread* currentThread; -extern "C" void halt(); - -__attribute__((noreturn)) void sweb_assert(const char *condition, uint32 line, const char* file) +__attribute__((noreturn)) void sweb_assert(const char *condition, uint32 line, const char* file, const char* function) { ArchInterrupts::disableInterrupts(); system_state = KPANIC; - kprintfd("KERNEL PANIC: Assertion %s failed in File %s on Line %d\n",condition, file, line); + kprintfd("KERNEL PANIC: Assertion %s failed in File %s, Function %s on Line %d\n", condition, file, function, line); if (currentThread != 0) currentThread->printBacktrace(false); while(1); diff --git a/arch/arm/common/source/boot.cpp b/arch/arm/common/source/boot.cpp index 9801f204a..d48944a32 100644 --- a/arch/arm/common/source/boot.cpp +++ b/arch/arm/common/source/boot.cpp @@ -1,10 +1,13 @@ -#include "types.h" #include "board_constants.h" #include "init_boottime_pagetables.h" -#include "assert.h" #include "kstring.h" + #include "ArchBoardSpecific.h" +#include "types.h" + +#include "assert.h" + extern "C" void __attribute__((naked)) PagingMode(); extern "C" void startup(); extern uint8 bss_start_address; diff --git a/arch/arm/common/source/usb/CMakeLists.txt b/arch/arm/common/source/usb/CMakeLists.txt index d6d16d72b..cfd431e64 100644 --- a/arch/arm/common/source/usb/CMakeLists.txt +++ b/arch/arm/common/source/usb/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC ../../include/usb ../../include/usb/usbd ) diff --git a/arch/arm/common/source/usb/platform.c b/arch/arm/common/source/usb/platform.c index be6cb82c6..de10085b5 100644 --- a/arch/arm/common/source/usb/platform.c +++ b/arch/arm/common/source/usb/platform.c @@ -13,7 +13,7 @@ #include "debug.h" #include "board_constants.h" #include "kmalloc.h" -#include "kstring.h" +#include "string.h" void* MemoryReserve(u32 length __attribute__((unused)), void* physicalAddress) { return physicalAddress; diff --git a/arch/arm/common/userspace/CMakeLists.txt b/arch/arm/common/userspace/CMakeLists.txt index 07c5172fc..cdb6508f9 100644 --- a/arch/arm/common/userspace/CMakeLists.txt +++ b/arch/arm/common/userspace/CMakeLists.txt @@ -1,4 +1,6 @@ -FILE(GLOB userspace_libc_SOURCES ${CMAKE_CURRENT_LIST_DIR}/*.c) +cmake_minimum_required(VERSION 3.11) + +FILE(GLOB userspace_libc_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.c) target_sources(userspace_libc PRIVATE diff --git a/arch/arm/integratorcp/CMakeLists.compiler b/arch/arm/integratorcp/CMakeLists.compiler index 1a4e834aa..05a1df830 100644 --- a/arch/arm/integratorcp/CMakeLists.compiler +++ b/arch/arm/integratorcp/CMakeLists.compiler @@ -3,8 +3,12 @@ INCLUDE(CMakeForceCompiler) # this one is important SET(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) SET(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(triple arm-none-eabi) +set(CMAKE_C_COMPILER_TARGET ${triple}) + # which compilers to use for C and C++ # find_program(CMAKE_ASM_COMPILER arm-none-eabi-gcc) @@ -14,7 +18,16 @@ find_program(CMAKE_CXX_COMPILER arm-none-eabi-g++) find_program(LD_EXECUTABLE arm-none-eabi-gcc) find_program(OBJCOPY_EXECUTABLE arm-none-eabi-objcopy) -if(${CMAKE_VERSION} VERSION_LESS "3.6.0") - CMAKE_FORCE_CXX_COMPILER(${CMAKE_CXX_COMPILER} STATIC_LIBRARY) +if(${CMAKE_VERSION} VERSION_LESS "3.6.0") + CMAKE_FORCE_CXX_COMPILER(${CMAKE_CXX_COMPILER} STATIC_LIBRARY) CMAKE_FORCE_C_COMPILER(${CMAKE_C_COMPILER} STATIC_LIBRARY) endif() + + +if(${CMAKE_C_COMPILER} STREQUAL "CMAKE_C_COMPILER-NOTFOUND") + message(SEND_ERROR "No suitable C compiler found!") +endif() + +if(${CMAKE_CXX_COMPILER} STREQUAL "CMAKE_CXX_COMPILER-NOTFOUND") + message(SEND_ERROR "No suitable C++ compiler found!") +endif() diff --git a/arch/arm/integratorcp/CMakeLists.include b/arch/arm/integratorcp/CMakeLists.include index 2bce6fc95..50e180ba8 100644 --- a/arch/arm/integratorcp/CMakeLists.include +++ b/arch/arm/integratorcp/CMakeLists.include @@ -1,34 +1,52 @@ set(KERNEL_BINARY kernel.x) -set(ARCH_ARM_ICP_KERNEL_CFLAGS -O0 -gstabs2 -Wall -Wextra -Werror -Wno-error=format -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -ffreestanding -mapcs -marm -march=armv5te -Wno-strict-aliasing -fshort-wchar ${NOPICFLAG}) +set(ARCH_ARM_ICP_KERNEL_CFLAGS -O0 -gdwarf-4 -Wall -Wextra -Werror -Wno-error=format -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -ffreestanding -Wno-strict-aliasing -fshort-wchar ${NOPICFLAG}) set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++17 -Wno-nonnull-compare -nostdinc++ -fno-rtti ${ARCH_ARM_ICP_KERNEL_CFLAGS}) set(KERNEL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -std=gnu11 ${ARCH_ARM_ICP_KERNEL_CFLAGS}) -set(ARCH_LD_ARGUMENTS -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -nostdinc -nostdlib -nodefaultlibs) -set(KERNEL_LD_ARGUMENT ${ARCH_LD_ARGUMENTS} ${NOPIEFLAG}) -set(ARCH_APPEND_LD_ARGUMENTS -lgcc) + +target_compile_options(arch_options INTERFACE + -mapcs -marm -march=armv5te -mcpu=arm926ej-s) + +target_link_options(arch_options INTERFACE + -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -nostdinc -nostdlib -nodefaultlibs) + + +target_link_options(kernel_options INTERFACE + ${NOPIEFLAG}) + +target_link_libraries(kernel_options INTERFACE + -Wl,--no-whole-archive -Wl,-lgcc -Wl,--whole-archive) + +target_compile_options(kernel_options INTERFACE + $<$:${KERNEL_CMAKE_CXX_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + ) MACRO(ARCH2OBJ ARCHOBJ_LIBNAME LIBRARY_NAME) ENDMACRO(ARCH2OBJ) +set(AVAILABLE_MEMORY 8M) -set(KERNEL_IMAGE_OBJCOPY ) +set(QEMU_BIN qemu-system-arm) +set(QEMU_FLAGS_COMMON -M integratorcp -m ${AVAILABLE_MEMORY} -kernel kernel.x -serial stdio -sd ${HDD_IMAGE} -no-reboot) +string(REPLACE ";" " " QEMU_FLAGS_COMMON_STR "${QEMU_FLAGS_COMMON}") # qemu: Run qemu in non debugging mode add_custom_target(qemu - COMMAND qemu-system-arm -M integratorcp -m 8M -kernel kernel.x -serial stdio -sd ${HDD_IMAGE} -no-reboot - COMMENT "Executing `qemu-system-arm -M integratorcp -m 8M -kernel kernel.x -serial stdio -sd ${HDD_IMAGE} -no-reboot`" + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} + COMMENT "Executing `${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR}`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I ) # qemugdb: Run qemu in debugging mode add_custom_target(qemugdb - COMMAND qemu-system-arm -M integratorcp -s -S -m 8M -kernel kernel.x -serial stdio -sd ${HDD_IMAGE} -no-reboot - COMMENT "Executing `qemu-system-arm -M integratorcp -s -S -m 8M -kernel kernel.x -serial stdio -sd ${HDD_IMAGE} -no-reboot`" + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -s -S + COMMENT "Executing `${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -s -S`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I ) - diff --git a/arch/arm/integratorcp/CMakeLists.txt b/arch/arm/integratorcp/CMakeLists.txt index cd746baf4..a8df1eae7 100644 --- a/arch/arm/integratorcp/CMakeLists.txt +++ b/arch/arm/integratorcp/CMakeLists.txt @@ -1,8 +1,7 @@ -include_directories( +target_include_directories(kernel PUBLIC ../common/include/usb ../common/include include ) add_subdirectory(source) - diff --git a/arch/arm/integratorcp/CMakeLists.userspace b/arch/arm/integratorcp/CMakeLists.userspace index abdc77796..6014381e2 100644 --- a/arch/arm/integratorcp/CMakeLists.userspace +++ b/arch/arm/integratorcp/CMakeLists.userspace @@ -1,9 +1,20 @@ +cmake_policy(SET CMP0079 NEW) + INCLUDE(CMakeForceCompiler) SET(CMAKE_SYSTEM_NAME Generic) SET(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) find_program(CMAKE_C_COMPILER arm-none-eabi-gcc) -set(ARCH_APPEND_LD_ARGUMENTS -Wl,-no-whole-archive -Wl,-lgcc) -set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -std=gnu11 -gstabs2 -O0 -static -nostdinc -fno-builtin -nostdlib -fno-stack-protector -fno-common -mapcs -marm -Werror=implicit-function-declaration -Wno-error=unused-variable -fno-stack-clash-protection) -set(ARCH_USERSPACE_LINKER_OPTIONS -static) +set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -gdwarf-4 -O0 -static -nostdinc -fno-builtin -nostdlib -nolibc -fno-stack-protector -fno-common -Wno-error=unused-variable -fno-stack-clash-protection) + +target_compile_options(userspace_options INTERFACE + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu++20 -nostdinc++> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu11 -Werror=implicit-function-declaration> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS}> +) + +target_link_libraries(userspace_options INTERFACE + -Wl,--no-whole-archive -Wl,-lgcc -Wl,--whole-archive) +target_link_options(userspace_options INTERFACE + -static) diff --git a/arch/arm/integratorcp/source/ArchBoardSpecific.cpp b/arch/arm/integratorcp/source/ArchBoardSpecific.cpp index 236182d07..676ac6999 100644 --- a/arch/arm/integratorcp/source/ArchBoardSpecific.cpp +++ b/arch/arm/integratorcp/source/ArchBoardSpecific.cpp @@ -1,15 +1,17 @@ #include "ArchBoardSpecific.h" +#include "FrameBufferConsole.h" +#include "InterruptUtils.h" #include "KeyboardManager.h" +#include "Scheduler.h" #include "board_constants.h" -#include "InterruptUtils.h" -#include "ArchCommon.h" -#include "assert.h" +#include "kprintf.h" #include "offsets.h" + +#include "ArchCommon.h" #include "ArchInterrupts.h" -#include "Scheduler.h" -#include "FrameBufferConsole.h" -#include "kprintf.h" + +#include "assert.h" #define PHYSICAL_MEMORY_AVAILABLE 8*1024*1024 @@ -18,7 +20,7 @@ pointer ArchBoardSpecific::getVESAConsoleLFBPtr() return ((PHYSICAL_MEMORY_AVAILABLE - ArchCommon::getVESAConsoleWidth() * ArchCommon::getVESAConsoleHeight() * ArchCommon::getVESAConsoleBitsPerPixel() / 8) & ~0xFFF); } -uint32 ArchBoardSpecific::getUsableMemoryRegion(uint32 region __attribute__((unused)), pointer &start_address, pointer &end_address, uint32 &type) +uint32 ArchBoardSpecific::getUseableMemoryRegion(uint32 region __attribute__((unused)), pointer &start_address, pointer &end_address, uint32 &type) { start_address = 0; end_address = ArchCommon::getVESAConsoleLFBPtr(0); @@ -34,14 +36,14 @@ void ArchBoardSpecific::frameBufferInit() // frame buffer initialization code from http://wiki.osdev.org/ARM_Integrator-CP_PL110_Dirty typedef struct _PL110MMIO { - uint32 volatile tim0; //0 - uint32 volatile tim1; //4 - uint32 volatile tim2; //8 - uint32 volatile d; //c - uint32 volatile upbase; //10 - uint32 volatile f; //14 - uint32 volatile g; //18 - uint32 volatile control; //1c + volatile uint32 tim0; // 0 + volatile uint32 tim1; // 4 + volatile uint32 tim2; // 8 + volatile uint32 d; // c + volatile uint32 upbase; // 10 + volatile uint32 f; // 14 + volatile uint32 g; // 18 + volatile uint32 control; // 1c } PL110MMIO; PL110MMIO *plio; diff --git a/arch/arm/integratorcp/source/MMCDriver.cpp b/arch/arm/integratorcp/source/MMCDriver.cpp index 466578eef..3d76b3b5c 100644 --- a/arch/arm/integratorcp/source/MMCDriver.cpp +++ b/arch/arm/integratorcp/source/MMCDriver.cpp @@ -1,11 +1,14 @@ -#include "BDManager.h" -#include "BDRequest.h" #include "MMCDriver.h" -#include "ArchInterrupts.h" +#include "BDManager.h" +#include "BDRequest.h" +#include "BDVirtualDevice.h" +#include "MasterBootRecord.h" #include "Scheduler.h" #include "kprintf.h" +#include "ArchInterrupts.h" + #define TIMEOUT_WARNING() do { kprintfd("%s:%d: timeout. THIS MIGHT CAUSE SERIOUS TROUBLE!\n", __PRETTY_FUNCTION__, __LINE__); } while (0) struct MMCI @@ -88,8 +91,13 @@ uint32 mmc_send_acmd(uint32 command, uint32 arg, uint32* response) return mmc_send_cmd(command, arg, response); } -MMCDriver::MMCDriver() : - SPT(63), lock_("MMCDriver::lock_"), rca_(0), sector_size_(512), num_sectors_(0) +MMCDrive::MMCDrive() : + Device(eastl::string("MMC disk")), + SPT(63), + lock_("MMCDriver::lock_"), + rca_(0), + sector_size_(512), + num_sectors_(210672) { debug(MMC_DRIVER, "MMCDriver()\n"); uint32 response; @@ -104,12 +112,12 @@ MMCDriver::MMCDriver() : mmc_send_cmd(7, rca_ << 16, 0); } -MMCDriver::~MMCDriver() +MMCDrive::~MMCDrive() { } -uint32 MMCDriver::addRequest(BDRequest * br) +uint32 MMCDrive::addRequest(BDRequest * br) { ScopeLock lock(lock_); debug(MMC_DRIVER, "addRequest %d!\n", br->getCmd()); @@ -118,10 +126,10 @@ uint32 MMCDriver::addRequest(BDRequest * br) switch (br->getCmd()) { - case BDRequest::BD_READ: + case BDRequest::BD_CMD::BD_READ: res = readSector(br->getStartBlock(), br->getNumBlocks(), br->getBuffer()); break; - case BDRequest::BD_WRITE: + case BDRequest::BD_CMD::BD_WRITE: res = writeSector(br->getStartBlock(), br->getNumBlocks(), br->getBuffer()); break; default: @@ -130,11 +138,11 @@ uint32 MMCDriver::addRequest(BDRequest * br) } debug(MMC_DRIVER, "addRequest:No IRQ operation !!\n"); - br->setStatus(BDRequest::BD_DONE); + br->setStatus(BDRequest::BD_RESULT::BD_DONE); return res; } -int32 MMCDriver::readBlock(uint32 address, void *buffer) +int32 MMCDrive::readBlock(uint32 address, void *buffer) { debug(MMC_DRIVER, "readBlock: address: %x, buffer: %p\n", address, buffer); uint32 response; @@ -155,7 +163,7 @@ int32 MMCDriver::readBlock(uint32 address, void *buffer) return 0; } -int32 MMCDriver::readSector(uint32 start_sector, uint32 num_sectors, void *buffer) +int32 MMCDrive::readSector(uint32 start_sector, uint32 num_sectors, void *buffer) { debug(MMC_DRIVER, "readSector: start: %x, num: %x, buffer: %p\n", start_sector, num_sectors, buffer); for (uint32 i = 0; i < num_sectors; ++i) @@ -165,7 +173,7 @@ int32 MMCDriver::readSector(uint32 start_sector, uint32 num_sectors, void *buffe return 0; } -int32 MMCDriver::writeBlock(uint32 address, void *buffer) +int32 MMCDrive::writeBlock(uint32 address, void *buffer) { debug(MMC_DRIVER, "writeBlock: address: %x, buffer: %p\n", address, buffer); uint32 response; @@ -184,7 +192,7 @@ int32 MMCDriver::writeBlock(uint32 address, void *buffer) return 0; } -int32 MMCDriver::writeSector(uint32 start_sector, uint32 num_sectors, void * buffer) +int32 MMCDrive::writeSector(uint32 start_sector, uint32 num_sectors, void * buffer) { debug(MMC_DRIVER, "writeSector: start: %x, num: %x, buffer: %p\n", start_sector, num_sectors, buffer); for (uint32 i = 0; i < num_sectors; ++i) @@ -194,16 +202,42 @@ int32 MMCDriver::writeSector(uint32 start_sector, uint32 num_sectors, void * buf return 0; } -uint32 MMCDriver::getNumSectors() +uint32 MMCDrive::getNumSectors() { - return 210672; // fixed number of sectors for now + return num_sectors_; } -uint32 MMCDriver::getSectorSize() +uint32 MMCDrive::getSectorSize() { return sector_size_; } -void MMCDriver::serviceIRQ() +void MMCDrive::serviceIRQ() +{ +} + +MMCDeviceDriver::MMCDeviceDriver() : + BasicDeviceDriver("MMC device driver") { } + +MMCDeviceDriver& MMCDeviceDriver::instance() +{ + static MMCDeviceDriver instance_; + return instance_; +} + +void MMCDeviceDriver::doDeviceDetection() +{ + // Assume we have a MMC drive + // Need to name this "idea" for compatibility with userspace disk detection + // even though it has nothing to do with IDE + constexpr const char* disk_name = "idea"; + MMCDrive* drv = new MMCDrive(); + bindDevice(*drv); + + auto *bdv = new BDVirtualDevice(drv, 0, drv->getNumSectors(), drv->getSectorSize(), disk_name, true); + BDManager::instance().addVirtualDevice(bdv); + + detectMBRPartitions(bdv, drv, 0, drv->SPT, disk_name); +} diff --git a/arch/arm/integratorcp/source/init_boottime_pagetables.cpp b/arch/arm/integratorcp/source/init_boottime_pagetables.cpp index 3627719eb..c579fd12a 100644 --- a/arch/arm/integratorcp/source/init_boottime_pagetables.cpp +++ b/arch/arm/integratorcp/source/init_boottime_pagetables.cpp @@ -1,8 +1,10 @@ -#include "types.h" -#include "paging-definitions.h" -#include "offsets.h" #include "init_boottime_pagetables.h" +#include "offsets.h" +#include "paging-definitions.h" + +#include "types.h" + extern "C" void initialiseBootTimePaging() { PageDirEntry *pde_start = (PageDirEntry*) VIRTUAL_TO_PHYSICAL_BOOT((pointer )kernel_page_directory); diff --git a/arch/arm/integratorcp/utils/kernel-ld-script.ld b/arch/arm/integratorcp/utils/kernel-ld-script.ld index 96822ef8d..624268cc6 100644 --- a/arch/arm/integratorcp/utils/kernel-ld-script.ld +++ b/arch/arm/integratorcp/utils/kernel-ld-script.ld @@ -10,7 +10,7 @@ LS_Phys = 0x1000; SECTIONS { - .text LS_Virt : AT(LS_Phys) + .text LS_Virt : AT(LS_Phys) { PROVIDE(kernel_start_address = ABSOLUTE(.)); @@ -24,7 +24,7 @@ SECTIONS *(.rodata*) ro_data_end_address = .; } - + .data ALIGN(4096) : AT(LS_Phys + (LS_Data - LS_Code)) { LS_Data = .; @@ -32,7 +32,30 @@ SECTIONS *(.data*) data_end_address = .; } - + + .init_array : AT(LS_Phys + (__init_array_start - LS_Code)) + { + __init_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + __init_array_end = .; + } + + .preinit_array : AT(LS_Phys + (__preinit_array_start - LS_Code)) + { + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } + + .fini_array : AT(LS_Phys + (__fini_array_start - LS_Code)) + { + __fini_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + __fini_array_end = .; + } + .bss ALIGN(4096) : AT(LS_Phys + (LS_Bss - LS_Code)) { LS_Bss = .; @@ -53,7 +76,7 @@ SECTIONS . = ALIGN(4096); stab_end_address_nr = .; } - + .stabstr : AT(LS_Phys + (LS_Stabstr - LS_Code)) { LS_Stabstr = .; @@ -65,7 +88,7 @@ SECTIONS PROVIDE(kernel_end_address = .); } - + .ARM.attributes 0 : { *(.ARM.attributes) } .comment 0 : { *(.comment) } } diff --git a/arch/arm/rpi2/CMakeLists.compiler b/arch/arm/rpi2/CMakeLists.compiler index 8474c5a26..50926b7f0 100644 --- a/arch/arm/rpi2/CMakeLists.compiler +++ b/arch/arm/rpi2/CMakeLists.compiler @@ -3,6 +3,7 @@ INCLUDE(CMakeForceCompiler) # this one is important SET(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) SET(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # which compilers to use for C and C++ @@ -14,7 +15,16 @@ find_program(CMAKE_CXX_COMPILER arm-none-eabi-g++) find_program(LD_EXECUTABLE arm-none-eabi-gcc) find_program(OBJCOPY_EXECUTABLE arm-none-eabi-objcopy) -if(${CMAKE_VERSION} VERSION_LESS "3.6.0") - CMAKE_FORCE_CXX_COMPILER(${CMAKE_CXX_COMPILER} STATIC_LIBRARY) +if(${CMAKE_VERSION} VERSION_LESS "3.6.0") + CMAKE_FORCE_CXX_COMPILER(${CMAKE_CXX_COMPILER} STATIC_LIBRARY) CMAKE_FORCE_C_COMPILER(${CMAKE_C_COMPILER} STATIC_LIBRARY) endif() + + +if(${CMAKE_C_COMPILER} STREQUAL "CMAKE_C_COMPILER-NOTFOUND") + message(SEND_ERROR "No suitable C compiler found!") +endif() + +if(${CMAKE_CXX_COMPILER} STREQUAL "CMAKE_CXX_COMPILER-NOTFOUND") + message(SEND_ERROR "No suitable C++ compiler found!") +endif() diff --git a/arch/arm/rpi2/CMakeLists.include b/arch/arm/rpi2/CMakeLists.include index 594ed595d..930c8e527 100644 --- a/arch/arm/rpi2/CMakeLists.include +++ b/arch/arm/rpi2/CMakeLists.include @@ -1,21 +1,33 @@ set(KERNEL_BINARY kernel.x) -set(ARCH_RPI_KERNEL_CFLAGS -O0 -gstabs2 -Wall -Wextra -Werror -Wno-error=format -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -ffreestanding -mapcs -marm -Wno-strict-aliasing -march=armv6 -fshort-wchar ${NOPICFLAG}) +set(ARCH_RPI_KERNEL_CFLAGS -O0 -gdwarf-4 -Wall -Wextra -Werror -Wno-error=format -Wno-packed-bitfield-compat -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -fno-omit-frame-pointer -ffreestanding -Wno-strict-aliasing -fshort-wchar ${NOPICFLAG}) -set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++17 -Wno-nonnull-compare -nostdinc++ -fno-rtti ${ARCH_RPI_KERNEL_CFLAGS}) +set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++20 -Wno-nonnull-compare -nostdinc++ -fno-rtti ${ARCH_RPI_KERNEL_CFLAGS}) set(KERNEL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -std=gnu11 ${ARCH_RPI_KERNEL_CFLAGS}) +target_compile_options(arch_options INTERFACE + -mapcs -marm -mcpu=cortex-a7+nofp) -set(ARCH_LD_ARGUMENTS -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -nostdinc -nostdlib -nodefaultlibs) -set(KERNEL_LD_ARGUMENT ${ARCH_LD_ARGUMENTS} ${NOPIEFLAG}) -set(ARCH_APPEND_LD_ARGUMENTS -Wl,-lgcc) +target_link_options(arch_options INTERFACE + -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -nostdinc -nostdlib -nodefaultlibs) + + +target_compile_options(kernel_options INTERFACE + $<$:${KERNEL_CMAKE_CXX_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> +) + +target_link_options(kernel_options INTERFACE + ${NOPIEFLAG}) + +target_link_libraries(kernel_options INTERFACE + -Wl,--no-whole-archive -Wl,-lgcc -Wl,--whole-archive) MACRO(ARCH2OBJ ARCHOBJ_LIBNAME LIBRARY_NAME) ENDMACRO(ARCH2OBJ) -set(KERNEL_IMAGE_OBJCOPY ) - # sdcard: Create an sdcard for the raspberry pi add_custom_target(sdcard @@ -33,17 +45,16 @@ add_custom_target(sdcardq # qemu: Run qemu in non debugging mode add_custom_target(qemu - COMMAND qemu-system-arm -kernel kernel.x -cpu arm1176 -m 1024 -M raspi2 -no-reboot -drive if=sd,file=${HDD_IMAGE} -serial stdio -d guest_errors,unimp - COMMENT "Executing `qemu-system-arm -kernel kernel.x -cpu arm1176 -m 1024 -M raspi2 -no-reboot -drive if=sd,file=${HDD_IMAGE} -serial stdio -d guest_errors,unimp`" + COMMAND qemu-system-arm -kernel kernel.x -m 1024 -M raspi2b -no-reboot -drive if=sd,file=${HDD_IMAGE} -serial stdio -d guest_errors,unimp -s + COMMENT "Executing `qemu-system-arm -kernel kernel.x -m 1024 -M raspi2b -no-reboot -drive if=sd,file=${HDD_IMAGE} -serial stdio -d guest_errors,unimp -s`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I ) # qemugdb: Run qemu in non debugging mode add_custom_target(qemugdb - COMMAND qemu-system-arm -kernel kernel.x -cpu arm1176 -m 512 -M raspi2 -no-reboot -drive if=sd,file=${HDD_IMAGE} -serial stdio -d guest_errors,unimp -s -S - COMMENT "Executing `qemu-system-arm -kernel kernel.x -cpu arm1176 -m 512 -M raspi2 -no-reboot -drive if=sd,file=${HDD_IMAGE} -serial stdio -d guest_errors,unimp -s -S`" + COMMAND qemu-system-arm -kernel kernel.x -m 1024 -M raspi2b -no-reboot -drive if=sd,file=${HDD_IMAGE} -serial stdio -d guest_errors,unimp -s -S + COMMENT "Executing `qemu-system-arm -kernel kernel.x -m 1024 -M raspi2b -no-reboot -drive if=sd,file=${HDD_IMAGE} -serial stdio -d guest_errors,unimp -s -S`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I ) - diff --git a/arch/arm/rpi2/CMakeLists.txt b/arch/arm/rpi2/CMakeLists.txt index f0ba9b54c..a8df1eae7 100644 --- a/arch/arm/rpi2/CMakeLists.txt +++ b/arch/arm/rpi2/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC ../common/include/usb ../common/include include diff --git a/arch/arm/rpi2/CMakeLists.userspace b/arch/arm/rpi2/CMakeLists.userspace index b62e4289c..41252f854 100644 --- a/arch/arm/rpi2/CMakeLists.userspace +++ b/arch/arm/rpi2/CMakeLists.userspace @@ -1,9 +1,20 @@ +cmake_policy(SET CMP0079 NEW) + INCLUDE(CMakeForceCompiler) SET(CMAKE_SYSTEM_NAME Generic) SET(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) find_program(CMAKE_C_COMPILER arm-none-eabi-gcc) -set(ARCH_APPEND_LD_ARGUMENTS -Wl,-no-whole-archive -Wl,-lgcc) -set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -std=gnu11 -g -O0 -static -nostdinc -fno-builtin -nostdlib -fno-stack-protector -fno-common -mapcs -marm -Werror=implicit-function-declaration -Wno-error=unused-variable -fno-stack-clash-protection) -set(ARCH_USERSPACE_LINKER_OPTIONS -static) +set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -g -gdwarf-4 -O0 -static -nostdinc -fno-builtin -nostdlib -nolibc -fno-stack-protector -fno-common -Wno-error=unused-variable -fno-stack-clash-protection) + +target_compile_options(userspace_options INTERFACE + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu++20 -nostdinc++> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu11 -Werror=implicit-function-declaration> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS}> +) + +target_link_libraries(userspace_options INTERFACE + -Wl,--no-whole-archive -Wl,-lgcc -Wl,--whole-archive) +target_link_options(userspace_options INTERFACE + -static) diff --git a/arch/arm/rpi2/include/board_constants.h b/arch/arm/rpi2/include/board_constants.h index 5a56722fa..e86e0bd76 100644 --- a/arch/arm/rpi2/include/board_constants.h +++ b/arch/arm/rpi2/include/board_constants.h @@ -1,10 +1,79 @@ #pragma once +#define MMIO_ORIGINAL 0x7e000000 +#define MMIO_REMAPPED 0x3f000000 +#define VIRT_MMIO_OFFSET 0x90000000 + +#define MMIO_OFFSET(x) ((x) - MMIO_ORIGINAL) +#define MMIO_TO_VIRT(x) (VIRT_MMIO_OFFSET + MMIO_OFFSET(x)) + +#define BCM2836_INTERRUPT_CONTROLLER_BASE 0x7e00b200 +#define BCM2836_INTC_VIRT MMIO_TO_VIRT(BCM2836_INTERRUPT_CONTROLLER_BASE) + + #define SERIAL_BASE 0x86001000 #define SERIAL_FLAG_REGISTER 0x18 #define SERIAL_BUFFER_FULL (1 << 5) -#define PIC_BASE 0x9000B200 +#define MMIO_BASE VIRT_MMIO_OFFSET + 0x3f0 +#define PIC_BASE BCM2836_INTC_VIRT +#define TIMER_BASE VIRT_MMIO_OFFSET + 0xB400 #define HCD_DESIGNWARE_BASE ((void*)0x90980000) #define BOARD_LOAD_BASE 0 +#define PIC_REG(offset) (uint32*)(PIC_BASE + (offset)) + +#define PIC_ENABLE_BASIC_OFFSET 0x18 + +#define PIC_PEND_BASIC PIC_REG(0x00) +#define PIC_PEND1 PIC_REG(0x04) +#define PIC_PEND2 PIC_REG(0x08) +#define PIC_FIQ_CONTROL PIC_REG(0x0C) +#define PIC_ENABLE1 PIC_REG(0x10) +#define PIC_ENABLE2 PIC_REG(0x14) +#define PIC_ENABLE_BASIC PIC_REG(0x18) +#define PIC_DISABLE1 PIC_REG(0x1C) +#define PIC_DISABLE2 PIC_REG(0x20) +#define PIC_DISABLE_BASIC PIC_REG(0x24) + +// IRQ pend basic +#define PIC_IRQ_ARM_TIMER (1 << 0) +#define PIC_IRQ_MAILBOX (1 << 1) +#define PIC_IRQ_DOORBELL0 (1 << 2) +#define PIC_IRQ_DOORBELL1 (1 << 3) + +// IRQ pend1 +#define PIC_IRQ_SYSTIMER(x) (1 << (x)) +#define PIC_IRQ_AUX (1 << 29) + +// IRQ pend2 +#define PIC_IRQ_GPIO(x) (1 << (17 + (x)) +#define PIC_IRQ_I2C (1 << 21) +#define PIC_IRQ_SPI (1 << 22) +#define PIC_IRQ_PCM (1 << 23) +#define PIC_IRQ_UART (1 << 25) + +// Timer +#define TIMER_REG(offset) (uint32*)(TIMER_BASE + (offset)) + +#define TIMER_LOAD TIMER_REG(0x00) +#define TIMER_CTRL TIMER_REG(0x08) +#define TIMER_CLEAR TIMER_REG(0x0C) + +#define TIMER_CTRL_32BIT (1 << 1) +#define TIMER_IRQ_ENABLE (1 << 5) +#define TIMER_CTRL_ENABLE (1 << 7) + +// CPU local interrupt controller +#define LOCAL_INT_CONTROLLER_PHYS 0x40000000 +#define LOCAL_INT_CONTROLLER_VIRT 0x90100000 + +#define ARM_CORE0_TIM_IRQCNTL_OFFS 0x40 +#define ARM_CORE0_IRQ_SOURCE_OFFS 0x60 + +#define LOCAL_INT_CONTROLLER_REG(offs) (LOCAL_INT_CONTROLLER_VIRT + (offs)) + +#define ARM_CORE0_TIM_IRQCNTL LOCAL_INT_CONTROLLER_REG(ARM_CORE0_TIM_IRQCNTL_OFFS) +#define ARM_CORE0_IRQ_SOURCE LOCAL_INT_CONTROLLER_REG(ARM_CORE0_IRQ_SOURCE_OFFS) + +#define TIM_IRQCNTL_CNTVIRQ_IRQ (1 << 3) diff --git a/arch/arm/rpi2/source/ArchBoardSpecific.cpp b/arch/arm/rpi2/source/ArchBoardSpecific.cpp index 3821e883c..5721af89f 100644 --- a/arch/arm/rpi2/source/ArchBoardSpecific.cpp +++ b/arch/arm/rpi2/source/ArchBoardSpecific.cpp @@ -1,15 +1,17 @@ #include "ArchBoardSpecific.h" +#include "FrameBufferConsole.h" +#include "InterruptUtils.h" #include "KeyboardManager.h" +#include "Scheduler.h" #include "board_constants.h" -#include "InterruptUtils.h" -#include "ArchCommon.h" -#include "assert.h" +#include "kprintf.h" #include "offsets.h" + +#include "ArchCommon.h" #include "ArchInterrupts.h" -#include "Scheduler.h" -#include "FrameBufferConsole.h" -#include "kprintf.h" + +#include "assert.h" #define PHYSICAL_MEMORY_AVAILABLE 8*1024*1024 @@ -35,7 +37,7 @@ pointer ArchBoardSpecific::getVESAConsoleLFBPtr() return framebuffer; } -uint32 ArchBoardSpecific::getUsableMemoryRegion(uint32 region __attribute__((unused)), pointer &start_address, pointer &end_address, uint32 &type) +size_t ArchBoardSpecific::getUsableMemoryRegion(size_t region __attribute__((unused)), pointer &start_address, pointer &end_address, size_t &type) { start_address = 0; end_address = ((PHYSICAL_MEMORY_AVAILABLE - ArchCommon::getVESAConsoleWidth() * ArchCommon::getVESAConsoleHeight() * ArchCommon::getVESAConsoleBitsPerPixel() / 8) & ~0xFFF); @@ -91,23 +93,67 @@ void ArchBoardSpecific::onIdle() // the usb stack should work with less dynamic memory and more stack variables, then it would be less complicated } +#define USE_SYSTEM_TIMER + +size_t timer_loading_value = 0; void ArchBoardSpecific::enableTimer() { - uint32* pic_base_enable = (uint32*)0x9000B218; - *pic_base_enable = 0x1; - - uint32* timer_load = (uint32*)0x9000B400; - //uint32* timer_value = timer_load + 1; - *timer_load = 0x800; - uint32* timer_control = timer_load + 2; - *timer_control = (1 << 7) | (1 << 5) | (1 << 2); - uint32* timer_clear = timer_load + 3; - *timer_clear = 0x1; +#ifdef USE_SYSTEM_TIMER + debug(A_BOOT, "Enabling system timer, addr %p\n", (uint32*)ARM_CORE0_TIM_IRQCNTL); + size_t timer_prescaler = 20; // (1/prescaler) s = prescaler Hz + uint32 timer_frequency = 0; + asm("mrc p15, 0, %0, c14, c0, 0\n" + : "=r" (timer_frequency)); + + timer_loading_value = timer_frequency / timer_prescaler; + debug(A_BOOT, "Setting timer tval: %u\n", timer_loading_value); + asm("mcr p15, 0, %0, c14, c3, 0\n" + :: "r" (timer_loading_value)); + + asm("mrc p15, 0, %0, c14, c3, 0\n" + : "=r" (timer_loading_value)); + debug(A_BOOT, "Remaining timer tval: %u\n", timer_loading_value); + + // CNTV_CTL + // enable timer + asm("mcr p15, 0, %0, c14, c3, 1\n" + :: "r" (1)); + + debug(A_BOOT, "Timer enabled, freq: %u\n", timer_frequency); + + + asm("mrc p15, 0, %0, c14, c3, 0\n" + : "=r" (timer_loading_value)); + debug(A_BOOT, "Remaining timer tval: %u\n", timer_loading_value); + + asm("mrc p15, 0, %0, c14, c3, 0\n" + : "=r" (timer_loading_value)); + debug(A_BOOT, "Remaining timer tval: %u\n", timer_loading_value); + + uint32* core0_irq_control = (uint32*)(ARM_CORE0_TIM_IRQCNTL); + *core0_irq_control = TIM_IRQCNTL_CNTVIRQ_IRQ; +#else + // This timer is not actually implemented in QEMU hardware emulation + + // uint32* pic_base_enable = (uint32*)0x9000B218; + // *pic_base_enable = 0x1; + *PIC_ENABLE_BASIC = 1; + + // uint32* timer_load = (uint32*)0x9000B400; + // //uint32* timer_value = timer_load + 1; + // *timer_load = 0x800; + *TIMER_LOAD = 0x800; + // uint32* timer_control = timer_load + 2; + // *timer_control = (1 << 7) | (1 << 5) | (1 << 2); + *TIMER_CTRL = TIMER_IRQ_ENABLE | TIMER_CTRL_ENABLE | (1 << 2); + // uint32* timer_clear = timer_load + 3; + // *timer_clear = 0x1; + *TIMER_CLEAR = 1; +#endif } -void ArchBoardSpecific::setTimerFrequency(uint32 freq) +void ArchBoardSpecific::setTimerFrequency([[maybe_unused]]uint32 freq) { - (void)freq; debug(A_BOOT, "Sorry, setTimerFrequency not implemented!\n"); } @@ -126,13 +172,22 @@ void ArchBoardSpecific::disableKBD() void ArchBoardSpecific::keyboard_irq_handler() { - KeyboardManager::instance()->serviceIRQ(); + KeyboardManager::instance().serviceIRQ(); +} + +void resetTimer() +{ + asm("mcr p15, 0, %0, c14, c3, 0\n" :: "r" (timer_loading_value)); } extern void timer_irq_handler(); void ArchBoardSpecific::timer0_irq_handler() { +#ifdef USE_SYSTEM_TIMER + resetTimer(); + timer_irq_handler(); +#else uint32* timer_raw = (uint32*)0x9000B410; if ((*timer_raw & 0x1) != 0) { @@ -142,14 +197,26 @@ void ArchBoardSpecific::timer0_irq_handler() timer_irq_handler(); } +#endif } #define IRQ(X) ((*pic) & (1 << X)) void ArchBoardSpecific::irq_handler() { +#ifdef USE_SYSTEM_TIMER + uint32* core0_irq_data = (uint32*)ARM_CORE0_IRQ_SOURCE; + if (*core0_irq_data & (1 << 3)) + { + timer0_irq_handler(); + } +#else + uint32* pic = (uint32*)PIC_BASE; if (IRQ(0)) + { timer0_irq_handler(); + } +#endif } extern "C" void ArchBoardSpecific::disableMulticore(uint32* spin) { @@ -161,4 +228,4 @@ extern "C" void ArchBoardSpecific::disableMulticore(uint32* spin) { "cmpeq r0, #0\n" // did this succeed? "bne multicore_spin\n" // no - we are not the first core, spin indefinitely : : [r]"r"(spin)); -} \ No newline at end of file +} diff --git a/arch/arm/rpi2/source/KeyboardManager.cpp b/arch/arm/rpi2/source/KeyboardManager.cpp index b416ca071..0a6365915 100644 --- a/arch/arm/rpi2/source/KeyboardManager.cpp +++ b/arch/arm/rpi2/source/KeyboardManager.cpp @@ -37,12 +37,16 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 128 }; -KeyboardManager *KeyboardManager::instance_ = 0; extern struct UsbDevice *Devices[]; KeyboardManager::KeyboardManager() : - keyboard_buffer_(256), extended_scancode(0), keyboard_status_(0), usb_kbd_addr_(0), current_key_(0) + IrqDomain("Keyboard"), + keyboard_buffer_(256), + extended_scancode(0), + keyboard_status_(0), + usb_kbd_addr_(0), + current_key_(0) { UsbInitialise(); for (uint32 i = 0; i < 32; ++i) @@ -55,10 +59,6 @@ KeyboardManager::KeyboardManager() : usb_kbd_addr_ = KeyboardGetAddress(0); } -KeyboardManager::~KeyboardManager() -{ -} - void KeyboardManager::kb_wait() { } diff --git a/arch/arm/rpi2/source/MMCDriver.cpp b/arch/arm/rpi2/source/MMCDriver.cpp index 62498c368..f38196096 100644 --- a/arch/arm/rpi2/source/MMCDriver.cpp +++ b/arch/arm/rpi2/source/MMCDriver.cpp @@ -1,29 +1,31 @@ -#include "BDManager.h" -#include "BDRequest.h" #include "MMCDriver.h" -#include "ArchInterrupts.h" +#include "BDManager.h" +#include "BDRequest.h" +#include "BDVirtualDevice.h" +#include "MasterBootRecord.h" #include "Scheduler.h" #include "kprintf.h" +#include "ArchInterrupts.h" struct MMCI { - uint32 arg2; - uint32 blksizecnt; - uint32 arg1; - uint32 cmdtm; - uint32 resp0; - uint32 resp1; - uint32 resp2; - uint32 resp3; - uint32 data; - uint32 status; - uint32 control0; - uint32 control1; - uint32 interrupt; - uint32 irpt_mask; - uint32 irpt_en; - uint32 control2; + uint32_t arg2; + uint32_t blksizecnt; + uint32_t arg1; + uint32_t cmdtm; + uint32_t resp0; + uint32_t resp1; + uint32_t resp2; + uint32_t resp3; + uint32_t data; + uint32_t status; + uint32_t control0; + uint32_t control1; + uint32_t interrupt; + uint32_t irpt_mask; + uint32_t irpt_en; + uint32_t control2; uint32_t cap0; uint32_t cap1; uint32_t rsvd1; @@ -47,7 +49,7 @@ struct MMCI { struct MMCI* mmci = (struct MMCI*) 0x8C000000; -uint32 mmc_send_cmd(uint32 command, uint32 arg, uint32* response, uint32 data = 0) +uint32_t mmc_send_cmd(uint32_t command, uint32_t arg, uint32_t* response, uint32_t data = 0) { while(mmci->status & 0x1); @@ -74,7 +76,7 @@ uint32 mmc_send_cmd(uint32 command, uint32 arg, uint32* response, uint32 data = return mmci->status; } -uint32 mmc_send_acmd(uint32 command, uint32 arg, uint32* response) +uint32_t mmc_send_acmd(uint32_t command, uint32_t arg, uint32_t* response) { do { @@ -87,11 +89,17 @@ uint32 mmc_send_acmd(uint32 command, uint32 arg, uint32* response) } -MMCDriver::MMCDriver() : SPT(63), lock_("MMCDriver::lock_"), rca_(0), sector_size_(512), num_sectors_(210672) +MMCDrive::MMCDrive() : + Device(eastl::string("MMC disk")), + SPT(63), + lock_("MMCDriver::lock_"), + rca_(0), + sector_size_(512), + num_sectors_(210672) { // unsigned int check; debug(MMC_DRIVER,"MMCDriver()\n"); - uint32 response; + uint32_t response; uint32_t ver = mmci->slotisr_ver; uint32_t vendor = ver >> 24; @@ -139,24 +147,24 @@ MMCDriver::MMCDriver() : SPT(63), lock_("MMCDriver::lock_"), rca_(0), sector_siz mmci->irpt_mask = 0xFFFFFFFF; } -MMCDriver::~MMCDriver() +MMCDrive::~MMCDrive() { } -uint32 MMCDriver::addRequest( BDRequest * br) +uint32_t MMCDrive::addRequest( BDRequest * br) { ScopeLock lock(lock_); - debug(MMC_DRIVER, "addRequest %d!\n", br->getCmd() ); + debug(MMC_DRIVER, "addRequest %d!\n", (int)br->getCmd()); - int32 res = -1; + int32_t res = -1; switch( br->getCmd() ) { - case BDRequest::BD_READ: + case BDRequest::BD_CMD::BD_READ: res = readSector( br->getStartBlock(), br->getNumBlocks(), br->getBuffer() ); break; - case BDRequest::BD_WRITE: + case BDRequest::BD_CMD::BD_WRITE: res = writeSector( br->getStartBlock(), br->getNumBlocks(), br->getBuffer() ); break; default: @@ -165,19 +173,19 @@ uint32 MMCDriver::addRequest( BDRequest * br) } debug(MMC_DRIVER, "addRequest:No IRQ operation !!\n"); - br->setStatus( BDRequest::BD_DONE ); + br->setStatus( BDRequest::BD_RESULT::BD_DONE ); return res; } -int32 MMCDriver::readBlock ( uint32 address, void *buffer ) +int32_t MMCDrive::readBlock ( uint32_t address, void *buffer ) { - debug(MMC_DRIVER,"readBlock: address: %x, buffer: %p\n",address, buffer); + debug(MMC_DRIVER,"readBlock: address: %x, buffer: %p\n", address, buffer); - uint32 response; + uint32_t response; mmc_send_cmd(17,address,&response,1); - uint32* buffer32 = (uint32*) buffer; -// uint8* buffer8 = (uint8*) buffer; - uint32 i = 0; + uint32_t* buffer32 = (uint32_t*) buffer; +// uint8_t* buffer8 = (uint8_t*) buffer; + uint32_t i = 0; while (i < sector_size_ / sizeof(uint32)) { while (!(mmci->interrupt & (1 << 5))); @@ -186,24 +194,24 @@ int32 MMCDriver::readBlock ( uint32 address, void *buffer ) return 0; } -int32 MMCDriver::readSector ( uint32 start_sector, uint32 num_sectors, void *buffer ) +int32_t MMCDrive::readSector ( uint32_t start_sector, uint32_t num_sectors, void *buffer ) { - debug(MMC_DRIVER,"readSector: start: %x, num: %x, buffer: %p\n",start_sector, num_sectors, buffer); - for (uint32 i = 0; i < num_sectors; ++i) + debug(MMC_DRIVER,"readSector: start: %x, num: %x, buffer: %p\n", start_sector, num_sectors, buffer); + for (uint32_t i = 0; i < num_sectors; ++i) { readBlock((start_sector + i) * sector_size_, (char*)buffer + i * sector_size_); } return 0; } -int32 MMCDriver::writeBlock ( uint32 address, void *buffer) +int32_t MMCDrive::writeBlock ( uint32_t address, void *buffer) { - debug(MMC_DRIVER,"readBlock: address: %x, buffer: %p\n",address, buffer); - uint32 response; + debug(MMC_DRIVER,"readBlock: address: %x, buffer: %p\n", address, buffer); + uint32_t response; mmc_send_cmd(24, address, &response,1); - uint32* buffer32 = (uint32*) buffer; - // uint8* buffer8 = (uint8*) buffer; - uint32 i = 0; + uint32_t* buffer32 = (uint32_t*) buffer; + // uint8_t* buffer8 = (uint8_t*) buffer; + uint32_t i = 0; while (i < sector_size_ / sizeof(uint32)) { while (!(mmci->interrupt & (1 << 5))); @@ -212,26 +220,53 @@ int32 MMCDriver::writeBlock ( uint32 address, void *buffer) return 0; } -int32 MMCDriver::writeSector ( uint32 start_sector, uint32 num_sectors, void * buffer) +int32_t MMCDrive::writeSector ( uint32_t start_sector, uint32_t num_sectors, void * buffer) { - debug(MMC_DRIVER,"writeSector: start: %x, num: %x, buffer: %p\n",start_sector, num_sectors, buffer); - for (uint32 i = 0; i < num_sectors; ++i) + debug(MMC_DRIVER,"writeSector: start: %x, num: %x, buffer: %p\n", start_sector, num_sectors, buffer); + for (uint32_t i = 0; i < num_sectors; ++i) { writeBlock((start_sector + i) * sector_size_, (char*)buffer + i * sector_size_); } return 0; } -uint32 MMCDriver::getNumSectors() +uint32_t MMCDrive::getNumSectors() { return num_sectors_; } -uint32 MMCDriver::getSectorSize() +uint32_t MMCDrive::getSectorSize() { return sector_size_; } -void MMCDriver::serviceIRQ() +void MMCDrive::serviceIRQ() +{ +} + + +MMCDeviceDriver::MMCDeviceDriver() : + BasicDeviceDriver("MMC device driver") { } + +MMCDeviceDriver& MMCDeviceDriver::instance() +{ + static MMCDeviceDriver instance_; + return instance_; +} + +void MMCDeviceDriver::doDeviceDetection() +{ + // Assume we have a MMC drive + // Need to name this "idea" for compatibility with userspace disk detection + // even though it has nothing to do with IDE + constexpr const char* disk_name = "idea"; + MMCDrive* drv = new MMCDrive(); + bindDevice(*drv); + + auto *bdv = new BDVirtualDevice(drv, 0, drv->getNumSectors(), drv->getSectorSize(), disk_name, true); + BDManager::instance().addVirtualDevice(bdv); + + detectMBRPartitions(bdv, drv, 0, drv->SPT, disk_name); +} diff --git a/arch/arm/rpi2/source/init_boottime_pagetables.cpp b/arch/arm/rpi2/source/init_boottime_pagetables.cpp index 8a32f344d..c42ea6b16 100644 --- a/arch/arm/rpi2/source/init_boottime_pagetables.cpp +++ b/arch/arm/rpi2/source/init_boottime_pagetables.cpp @@ -1,8 +1,10 @@ -#include "types.h" -#include "paging-definitions.h" -#include "offsets.h" #include "init_boottime_pagetables.h" +#include "offsets.h" +#include "paging-definitions.h" + +#include "types.h" + extern "C" void initialiseBootTimePaging() { PageDirEntry *pde_start = (PageDirEntry*)(((char*)kernel_page_directory) - PHYSICAL_TO_VIRTUAL_OFFSET); @@ -52,6 +54,7 @@ extern "C" void initialiseBootTimePaging() mapBootTimePage(pde_start,0x860,mmio_base + 2); // pl011 mapBootTimePage(pde_start,0x8C0,mmio_base + 3); // emmc mapBootTimePage(pde_start,0x900,mmio_base); // most devices (ic, timer, gpu, ...) + mapBootTimePage(pde_start,0x901,0x400); // local interrupt controller mapBootTimePage(pde_start,0x909,mmio_base + 9); // map for csud diff --git a/arch/arm/rpi2/utils/kernel-ld-script.ld b/arch/arm/rpi2/utils/kernel-ld-script.ld index a2d51aae8..8e430aee5 100644 --- a/arch/arm/rpi2/utils/kernel-ld-script.ld +++ b/arch/arm/rpi2/utils/kernel-ld-script.ld @@ -10,7 +10,7 @@ LS_Phys = 0x0000; SECTIONS { - .text LS_Virt : AT(LS_Phys) + .text LS_Virt : AT(LS_Phys) { PROVIDE(kernel_start_address = ABSOLUTE(.)); @@ -24,7 +24,7 @@ SECTIONS *(.rodata*) ro_data_end_address = .; } - + .data ALIGN(4096) : AT(LS_Phys + (LS_Data - LS_Code)) { LS_Data = .; @@ -32,7 +32,30 @@ SECTIONS *(.data*) data_end_address = .; } - + + .init_array : AT(LS_Phys + (__init_array_start - LS_Code)) + { + __init_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + __init_array_end = .; + } + + .preinit_array : AT(LS_Phys + (__preinit_array_start - LS_Code)) + { + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } + + .fini_array : AT(LS_Phys + (__fini_array_start - LS_Code)) + { + __fini_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + __fini_array_end = .; + } + .bss ALIGN(4096) : AT(LS_Phys + (LS_Bss - LS_Code)) { LS_Bss = .; @@ -53,7 +76,7 @@ SECTIONS . = ALIGN(4096); stab_end_address_nr = .; } - + .stabstr : AT(LS_Phys + (LS_Stabstr - LS_Code)) { LS_Stabstr = .; @@ -65,7 +88,7 @@ SECTIONS PROVIDE(kernel_end_address = .); } - + .multicore : AT(LS_Phys + (LS_Multicore - LS_Code)) { LS_Multicore = .; @@ -75,7 +98,7 @@ SECTIONS . = ALIGN(4096); multicore_end_address = .; } - + .ARM.attributes 0 : { *(.ARM.attributes) } .comment 0 : { *(.comment) } } diff --git a/arch/armv8/CMakeLists.txt b/arch/armv8/CMakeLists.txt index 3dea25a06..7dee7d6f4 100644 --- a/arch/armv8/CMakeLists.txt +++ b/arch/armv8/CMakeLists.txt @@ -1,8 +1,7 @@ -include_directories( +target_include_directories(kernel PUBLIC common/include ../${ARCH}/include ) add_subdirectory(common/source) add_subdirectory(common/userspace) - diff --git a/arch/armv8/common/include/ArchCpuLocalStorage.h b/arch/armv8/common/include/ArchCpuLocalStorage.h new file mode 100644 index 000000000..4ce87b64c --- /dev/null +++ b/arch/armv8/common/include/ArchCpuLocalStorage.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#define cpu_local +#define __cpu + +namespace CpuLocalStorage +{ + size_t getClsSize(); + + char* allocCls(); + void setCls(char* cls); + bool ClsInitialized(); + void* getClsBase(); +}; diff --git a/arch/armv8/common/include/ArchMemory.h b/arch/armv8/common/include/ArchMemory.h index 3bc134e05..99a509df7 100644 --- a/arch/armv8/common/include/ArchMemory.h +++ b/arch/armv8/common/include/ArchMemory.h @@ -1,9 +1,11 @@ #pragma once #include "offsets.h" -#include "types.h" #include "paging-definitions.h" -#include "uvector.h" + +#include "types.h" + +#include "EASTL/vector.h" class ArchMemoryMapping { @@ -42,24 +44,35 @@ class ArchMemory { public: -/** +/** * Constructor * creates a new Page-Directory for a UserProccess by copying the * Kernel-Page-Directory * */ ArchMemory(); + ArchMemory(ppn_t paging_root_page); + -/** +/** + * Destructor. Recursively deletes the page directory and all page tables + * + */ + ~ArchMemory(); + + void printMappings(); + +/** * * maps a virtual page to a physical page (pde and pte need to be set up first) * - * @param virtual_page + * @param virtual_page * @param physical_page * @param user_access PTE User/Supervisor Flag, governing the binary Paging * Privilege Mechanism */ - bool mapPage(size_t virtual_page, size_t physical_page, size_t user_access); + [[nodiscard]] + bool mapPage(vpn_t virtual_page, ppn_t physical_page, bool user_access); /** * removes the mapping to a virtual_page by marking its PTE Entry as non valid @@ -68,11 +81,6 @@ class ArchMemory */ bool unmapPage(size_t virtual_page); -/** - * Destructor. Recursively deletes the page directory and all page tables - * - */ - ~ArchMemory(); /** * Takes a Physical Page Number in Real Memory and returns a virtual address than @@ -117,8 +125,10 @@ class ArchMemory * * @param virtual_page * @param physical_page + * @return true if the page has been mapped */ - static void mapKernelPage(size_t virtual_page, size_t physical_page); + [[nodiscard]] + static bool mapKernelPage(size_t virtual_page, size_t physical_page, bool can_alloc_pages = false, bool memory_mapped_io = false); /** * removes the mapping to a virtual_page by marking its PTE Entry as non valid @@ -139,34 +149,43 @@ class ArchMemory uint16_t address_space_id = 0; size_t getRootOfPagingStructure(); + static void loadPagingStructureRoot(size_t ttbr0_value); + + static Level1Entry* getKernelPagingStructureRootVirt(); + static size_t getKernelPagingStructureRootPhys(); static const size_t RESERVED_START = 0xFFFFFFC000000ULL; static const size_t RESERVED_END = 0xFFFFFFC000400ULL; -private: - - /** - * Removes a paging entry from a given page_table if it is present - * in the first place. Futhermore, the target page table is assured to be - * empty. - * - * @param table_ptr physical page containing the target paging_table. - * @param index Index of the paging entry to be removed - */ - template static bool checkAndRemove(pointer table_ptr, size_t index); + static void flushLocalTranslationCaches(size_t addr); + static void flushAllTranslationCaches(size_t addr); - /** - * Removes a page directory entry from a given page directory if it is present - * in the first place. Futhermore, the target page table is assured to be - * empty. - * - * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. - */ - void checkAndRemovePT(size_t pde_vpn); + static ArchMemory& kernelArchMemory(); - - ArchMemory(ArchMemory const &src); // not yet implemented - ArchMemory &operator=(ArchMemory const &src); // should never be implemented +private: + ArchMemory& operator=(const ArchMemory& src) = delete; // should never be implemented + + /** + * Removes a paging entry from a given page_table if it is present + * in the first place. Futhermore, the target page table is assured to be + * empty. + * + * @param table_ptr physical page containing the target paging_table. + * @param index Index of the paging entry to be removed + */ + template static bool checkAndRemove(pointer table_ptr, size_t index); + + template static bool tableEmpty(T* table); + + template static void removeEntry(T* table, size_t index); + + /** + * Removes a page directory entry from a given page directory if it is present + * in the first place. Futhermore, the target page table is assured to be + * empty. + * + * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. + */ + void checkAndRemovePT(size_t pde_vpn); }; - diff --git a/arch/armv8/common/include/ArchMulticore.h b/arch/armv8/common/include/ArchMulticore.h new file mode 100644 index 000000000..ad3ff8d0a --- /dev/null +++ b/arch/armv8/common/include/ArchMulticore.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Cpu.h" +#include "Mutex.h" + +#include "EASTL/vector.h" + +class IrqDomain; + +class ArchCpu : public Cpu +{ +public: + ArchCpu(); + + IrqDomain& rootIrqDomain(); +private: +}; + +class ArchMulticore +{ +public: + static void initialize(); + + static void startOtherCPUs(); + static void stopAllCpus(); + static void stopOtherCpus(); + + static void sendFunctionCallMessage(ArchCpu& cpu, RemoteFunctionCallMessage* fcall_message); + + static void initCpu(); + static void initCpuLocalData(bool boot_cpu = false); + + static char* cpuStackTop(); + +private: + static void prepareAPStartup(size_t entry_addr); + + [[noreturn]] static void waitForSystemStart(); +}; diff --git a/arch/armv8/common/include/ArchThreads.h b/arch/armv8/common/include/ArchThreads.h index 514347611..5c590d547 100644 --- a/arch/armv8/common/include/ArchThreads.h +++ b/arch/armv8/common/include/ArchThreads.h @@ -2,6 +2,8 @@ #include "types.h" +#include "EASTL/unique_ptr.h" + /** * The flag for full barrier synchronization. */ @@ -21,19 +23,13 @@ struct ArchThreadRegisters NeonQ Q[32]; size_t SPSR; size_t ELR; - size_t SP; + size_t SP; // Saved previous stack pointer (SP_EL0 when coming from user mode, else SP) size_t TTBR0; - size_t SP_SM; + size_t SP_SM; // Kernel stack pointer }; class Thread; class ArchMemory; -/** - * this is where the thread info for task switching is stored - * - */ -extern ArchThreadRegisters *currentThreadRegisters; -extern Thread *currentThread; /** * Collection of architecture dependant code concerning Task Switching @@ -43,6 +39,8 @@ class ArchThreads { public: + [[noreturn]] static void startThreads(Thread* init_thread); + /** * allocates space for the currentThreadRegisters * @@ -55,7 +53,8 @@ class ArchThreads * @param start_function instruction pointer is set so start function * @param stack stackpointer */ - static void createKernelRegisters(ArchThreadRegisters *&info, void* start_function, void* stack); + static eastl::unique_ptr createKernelRegisters(void* start_function, + void* kernel_stack); /** * changes an existing ArchThreadRegisters so that execution will start / continue @@ -66,7 +65,9 @@ class ArchThreads * @param the ArchThreadRegisters that we are going to mangle * @param start_function instruction pointer for the next instruction that gets executed */ - static void changeInstructionPointer(ArchThreadRegisters *info, void* function); + static void changeInstructionPointer(ArchThreadRegisters& info, void* function); + + static void* getInstructionPointer(ArchThreadRegisters& info); /** * creates the ArchThreadRegisters for a user thread @@ -75,7 +76,9 @@ class ArchThreads * @param user_stack pointer to the userstack * @param kernel_stack pointer to the kernel stack */ - static void createUserRegisters(ArchThreadRegisters *&info, void* start_function, void* user_stack, void* kernel_stack); + static eastl::unique_ptr createUserRegisters(void* start_function, + void* user_stack, + void* kernel_stack); /** * @@ -85,13 +88,16 @@ class ArchThreads static void yield(); /** - * sets a threads CR3 register to the given page dir / etc. defining its address space + * sets a threads address space register to given address space * * @param *thread Pointer to Thread Object * @param arch_memory a reference to the arch memory object to use */ static void setAddressSpace(Thread *thread, ArchMemory& arch_memory); + static void switchToAddressSpace(Thread* thread); + static void switchToAddressSpace(ArchMemory& arch_memory); + /** * uninterruptable locked operation * exchanges value in variable lock with new_value and returns the old_value @@ -102,6 +108,17 @@ class ArchThreads */ static size_t testSetLock(size_t &lock, size_t new_value); + /** + * Counterpart to testSetLock() + * Writes 0 to the lock variable and provides a memory release barrier + * (ensures all previous memory stores are visible) + */ + template + static void syncLockRelease(volatile T &lock) + { + __sync_lock_release(&lock); + } + /** * atomically increments or decrements value by increment * @@ -137,4 +154,3 @@ class ArchThreads */ static void debugCheckNewThread(Thread* thread); }; - diff --git a/arch/armv8/common/include/MMCDriver.h b/arch/armv8/common/include/MMCDriver.h index f3d4d325a..6f97d5685 100644 --- a/arch/armv8/common/include/MMCDriver.h +++ b/arch/armv8/common/include/MMCDriver.h @@ -1,15 +1,17 @@ #pragma once #include "BDDriver.h" +#include "Device.h" +#include "DeviceDriver.h" #include "Mutex.h" class BDRequest; -class MMCDriver : public BDDriver +class MMCDrive : public BDDriver, public Device { public: - NO_OPTIMIZE MMCDriver(); - virtual ~MMCDriver(); + NO_OPTIMIZE MMCDrive(); + ~MMCDrive() override; /** * adds the given request to a list and checkes the type of the @@ -17,7 +19,7 @@ class MMCDriver : public BDDriver * or the function returns otherwise. * */ - uint32 addRequest(BDRequest *); + uint32 addRequest(BDRequest *) override; /** * @param 1 sector where it should be started to read @@ -25,7 +27,7 @@ class MMCDriver : public BDDriver * @param 3 buffer where to save all that was read * */ - int32 readSector(uint32, uint32, void *); + int32 readSector(uint32, uint32, void *) override; /** * @param 1 sector where it should be started to write @@ -33,11 +35,11 @@ class MMCDriver : public BDDriver * @param 3 buffer, which content should be written to the sectors * */ - int32 writeSector(uint32, uint32, void *); + int32 writeSector(uint32, uint32, void *) override; - uint32 getNumSectors(); - uint32 getSectorSize(); - void serviceIRQ(); + uint32 getNumSectors() override; + uint32 getSectorSize() override; + void serviceIRQ() override; uint32 SPT; private: @@ -61,3 +63,16 @@ class MMCDriver : public BDDriver uint32 num_sectors_; }; +class MMCDeviceDriver : public BasicDeviceDriver, + public Driver +{ +public: + MMCDeviceDriver(); + ~MMCDeviceDriver() override = default; + + static MMCDeviceDriver& instance(); + + void doDeviceDetection() override; + +private: +}; diff --git a/arch/armv8/common/include/offsets.h b/arch/armv8/common/include/offsets.h index 84db0ee60..eef7248f8 100644 --- a/arch/armv8/common/include/offsets.h +++ b/arch/armv8/common/include/offsets.h @@ -49,3 +49,6 @@ #define KERNEL_LEVEL3_TABLES 8 #define NUMBER_KERNEL_PAGEING_TABLES (KERNEL_LEVEL1_TABLES + KERNEL_LEVEL2_TABLES + KERNEL_LEVEL3_TABLES) + +#define TTBR1_LIMIT_LOW 0xFFFFFF8000000000ULL +#define KERNEL_START TTBR1_LIMIT_LOW diff --git a/arch/armv8/common/include/paging-definitions.h b/arch/armv8/common/include/paging-definitions.h index 58d184660..94d731cf1 100644 --- a/arch/armv8/common/include/paging-definitions.h +++ b/arch/armv8/common/include/paging-definitions.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" + #define PAGE_ENTRIES 512 #define LEVEL0_ENTRIES PAGE_ENTRIES #define LEVEL1_ENTRIES PAGE_ENTRIES @@ -107,3 +108,20 @@ typedef union struct Level12TableEntry table; struct Level2BlockEntry block; } __attribute__((__packed__)) Level2Entry; + +struct VAddr +{ + union + { + uint64 addr; + struct + { + uint64 offset : 12; + uint64 l3i : 9; + uint64 l2i : 9; + uint64 l1i : 9; + uint64 l0i : 9; // ignored/always required to be the same since TCR_EL1.T0SZ/T1SZ == 25 + uint64 reserved : 16; + } __attribute__((__packed__));; + } __attribute__((__packed__));; +} __attribute__((__packed__)); diff --git a/arch/armv8/common/include/types.h b/arch/armv8/common/include/types.h index 0c5fd3fbc..6796ad4ad 100644 --- a/arch/armv8/common/include/types.h +++ b/arch/armv8/common/include/types.h @@ -1,29 +1,33 @@ #pragma once -typedef signed char int8; -typedef unsigned char uint8; +#include "stdint.h" +#include "klibc/sys/types.h" -typedef signed short int int16; -typedef unsigned short int uint16; +typedef int8_t int8; +typedef uint8_t uint8; -typedef signed int int32; -typedef unsigned int uint32; +typedef int16_t int16; +typedef uint16_t uint16; -typedef unsigned long int uint64; -typedef signed long int int64; +typedef int32_t int32; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef int64_t int64; typedef uint32 l_off_t; typedef uint32 mode_t; typedef uint32 uid_t; typedef uint32 gid_t; -typedef uint64 size_t; -typedef int64 ssize_t; +typedef __SIZE_TYPE__ size_t; + +typedef uint64 ppn_t; +typedef size_t vpn_t; typedef size_t pointer; -#pragma GCC poison double float +/* #pragma GCC poison double float */ #define Min(x,y) (((x)<(y))?(x):(y)) #define Max(x,y) (((x)>(y))?(x):(y)) diff --git a/arch/armv8/common/source/ArchCommon.cpp b/arch/armv8/common/source/ArchCommon.cpp index b0f959b9b..66199ccbd 100644 --- a/arch/armv8/common/source/ArchCommon.cpp +++ b/arch/armv8/common/source/ArchCommon.cpp @@ -1,17 +1,33 @@ #include "ArchCommon.h" -#include "ArchBoardSpecific.h" -#include "offsets.h" -#include "kprintf.h" -#include "ArchMemory.h" -#include "TextConsole.h" + #include "FrameBufferConsole.h" -#include "backtrace.h" +#include "KernelMemoryManager.h" +#include "MMCDriver.h" +#include "PlatformBus.h" +#include "SMP.h" #include "SWEBDebugInfo.h" +#include "SerialManager.h" +#include "TextConsole.h" +#include "backtrace.h" +#include "kprintf.h" +#include "offsets.h" + +#include "ArchBoardSpecific.h" +#include "ArchCpuLocalStorage.h" +#include "ArchMemory.h" #define PHYSICAL_MEMORY_AVAILABLE 8*1024*1024 +RangeAllocator<> mmio_addr_allocator; + +extern void* kernel_start_address; extern void* kernel_end_address; +pointer ArchCommon::getKernelStartAddress() +{ + return (pointer)&kernel_start_address; +} + pointer ArchCommon::getKernelEndAddress() { return (pointer)&kernel_end_address; @@ -48,6 +64,11 @@ size_t ArchCommon::getModuleEndAddress(size_t num __attribute__((unused)), size_ return getKernelEndAddress(); } +const char* ArchCommon::getModuleName([[maybe_unused]]size_t num, [[maybe_unused]]size_t is_paging_set_up) +{ + return "kernel"; +} + size_t ArchCommon::getVESAConsoleHeight() { return 480; @@ -78,7 +99,7 @@ size_t ArchCommon::getNumUseableMemoryRegions() return 1; } -size_t ArchCommon::getUsableMemoryRegion(size_t region, pointer &start_address, pointer &end_address, size_t &type) +size_t ArchCommon::getUseableMemoryRegion(size_t region, pointer &start_address, pointer &end_address, size_t &type) { return ArchBoardSpecific::getUsableMemoryRegion(region, start_address, end_address, type); } @@ -89,13 +110,20 @@ Console* ArchCommon::createConsole(size_t count) return new FrameBufferConsole(count); } -Stabs2DebugInfo const *kernel_debug_info = 0; +const Stabs2DebugInfo* kernel_debug_info = 0; void ArchCommon::initDebug() { extern unsigned char swebdbg_start_address_nr; extern unsigned char swebdbg_end_address_nr; + if (&swebdbg_start_address_nr == &swebdbg_end_address_nr) + { + debug(MAIN, "Empty debug info!\n"); + kernel_debug_info = new SWEBDebugInfo(nullptr, nullptr); + return; + } + kernel_debug_info = new SWEBDebugInfo((const char *)&swebdbg_start_address_nr, (const char*)&swebdbg_end_address_nr); } @@ -123,7 +151,7 @@ extern "C" void dumpBss() } } -extern "C" void halt() +extern "C" void ArchCommon::halt() { asm volatile("wfi"); } @@ -135,3 +163,53 @@ void ArchCommon::idle() halt(); } +void ArchCommon::spinlockPause() +{ +} + +uint64 ArchCommon::cpuTimestamp() +{ + uint64 timestamp; + asm volatile ("isb; mrs %0, cntvct_el0" + : "=r" (timestamp)); + + return timestamp; +} + +void ArchCommon::postBootInit() +{ +} + +void ArchCommon::initPlatformDrivers() +{ + PlatformBus::instance().registerDriver(SerialManager::instance()); +} + +void ArchCommon::initBlockDeviceDrivers() +{ + PlatformBus::instance().registerDriver(MMCDeviceDriver::instance()); +} + +void ArchCommon::reservePagesPreKernelInit([[maybe_unused]]Allocator& alloc) +{ +} + +void ArchCommon::initKernelVirtualAddressAllocator() +{ + mmio_addr_allocator.setUseable(KERNEL_START, (size_t)-1); + mmio_addr_allocator.setUnuseable(getKernelStartAddress(), getKernelEndAddress()); + mmio_addr_allocator.setUnuseable(KernelMemoryManager::instance()->getKernelHeapStart(), KernelMemoryManager::instance()->getKernelHeapMaxEnd()); + // TODO: ident mapping end + // mmio_addr_allocator.setUnuseable(IDENT_MAPPING_START, IDENT_MAPPING_END); + debug(MAIN, "Usable MMIO ranges:\n"); + mmio_addr_allocator.printUsageInfo(); +} + +cpu_local size_t heart_beat_value = 0; +const char* clock = "/-\\|"; + +void ArchCommon::drawHeartBeat() +{ + ((FrameBufferConsole*)main_console)->consoleSetCharacter(0,SMP::currentCpuId(),clock[heart_beat_value], CONSOLECOLOR::GREEN); + heart_beat_value = (heart_beat_value + 1) % 4; +} diff --git a/arch/armv8/common/source/ArchCpuLocalStorage.cpp b/arch/armv8/common/source/ArchCpuLocalStorage.cpp new file mode 100644 index 000000000..f15a7f17c --- /dev/null +++ b/arch/armv8/common/source/ArchCpuLocalStorage.cpp @@ -0,0 +1,60 @@ +#include "ArchCpuLocalStorage.h" + +#include + +#include "debug.h" + +extern char cls_start; +extern char cls_end; +extern char tbss_start; +extern char tbss_end; +extern char tdata_start; +extern char tdata_end; + + +void* CpuLocalStorage::getClsBase() +{ + void* cls = 0; + asm("MRS %[cls], TPIDR_EL1\n" + :[cls]"=g"(cls)); + return cls; +} + +bool CpuLocalStorage::ClsInitialized() +{ + return getClsBase(); +} + +size_t CpuLocalStorage::getClsSize() +{ + return &cls_end - &cls_start; +} + +char* CpuLocalStorage::allocCls() +{ + debug(A_MULTICORE, "Allocating CPU local storage\n"); + + size_t cls_size = getClsSize(); + size_t tbss_size = &tbss_end - &tbss_start; + size_t tdata_size = &tdata_end - &tdata_start; + debug(A_MULTICORE, "cls_base: [%p, %p), size: %zx\n", &cls_start, &cls_end, cls_size); + debug(A_MULTICORE, "tbss: [%p, %p), size: %zx\n", &tbss_start, &tbss_end, tbss_size); + debug(A_MULTICORE, "tdata: [%p, %p), size: %zx\n", &tdata_start, &tdata_end, tdata_size); + + char* cls_base = new char[cls_size + sizeof(void*)]{}; + debug(A_MULTICORE, "Allocated new cls_base at [%p, %p)\n", cls_base, cls_base + cls_size + sizeof(void*)); + + debug(A_MULTICORE, "Initializing tdata at [%p, %p) and tbss at [%p, %p)\n", + cls_base + (&tdata_start - &cls_start), cls_base + (&tdata_start - &cls_start) + tdata_size, + cls_base + (&tbss_start - &cls_start), cls_base + (&tbss_start - &cls_start) + tbss_size); + memcpy(cls_base + (&tdata_start - &cls_start), &tdata_start, tdata_size); + + return cls_base; +} + +void CpuLocalStorage::setCls(char* cls) +{ + debug(A_MULTICORE, "Set CLS: %p\n", cls); + asm("MSR TPIDR_EL1, %[cls]\n" + ::[cls]"g"(cls)); +} diff --git a/arch/armv8/common/source/ArchInterrupts.cpp b/arch/armv8/common/source/ArchInterrupts.cpp index 38c7b7820..5487af735 100644 --- a/arch/armv8/common/source/ArchInterrupts.cpp +++ b/arch/armv8/common/source/ArchInterrupts.cpp @@ -1,18 +1,25 @@ -#include "types.h" #include "ArchInterrupts.h" + +#include "InterruptUtils.h" +#include "Scheduler.h" +#include "SystemState.h" +#include "Thread.h" #include "kprintf.h" #include "kstring.h" -#include "InterruptUtils.h" -#include "ArchThreads.h" + #include "ArchBoardSpecific.h" -#include "Thread.h" +#include "ArchThreads.h" + +#include "types.h" + +cpu_local IrqDomain cpu_irq_vector_domain_("CPU interrupt vector"); extern "C" void exceptionHandler(size_t int_id, size_t curr_el, size_t exc_syndrome, size_t fault_address, size_t return_addr); extern "C" const size_t kernel_sp_struct_offset = (size_t)&((ArchThreadRegisters *)NULL)->SP_SM; extern uint8 boot_stack[]; //in interrupt_entry.S is the code for the actual context switching -extern "C" size_t interruptEntry(size_t int_id, size_t curr_el, size_t exc_syndrome, size_t fault_address, size_t return_addr ) +extern "C" size_t interruptEntry(size_t int_id, size_t curr_el, size_t exc_syndrome, size_t fault_address, size_t return_addr) { exceptionHandler(int_id, curr_el, exc_syndrome, fault_address, return_addr); @@ -100,4 +107,3 @@ void ArchInterrupts::yieldIfIFSet() __asm__ __volatile__("nop"); } } - diff --git a/arch/armv8/common/source/ArchMemory.cpp b/arch/armv8/common/source/ArchMemory.cpp index 3b714baf6..800c4cf27 100644 --- a/arch/armv8/common/source/ArchMemory.cpp +++ b/arch/armv8/common/source/ArchMemory.cpp @@ -1,9 +1,14 @@ #include "ArchMemory.h" -#include "kprintf.h" -#include "assert.h" + #include "PageManager.h" -#include "offsets.h" +#include "SMP.h" +#include "kprintf.h" #include "kstring.h" +#include "offsets.h" + +#include "ArchMulticore.h" + +#include "assert.h" Level1Entry kernel_paging_level1[KERNEL_LEVEL1_TABLES * LEVEL1_ENTRIES] __attribute__((aligned(PAGE_SIZE))); //one page for ident and one for the kernel pages @@ -15,48 +20,93 @@ uint16 ASID_COUNTER = 0; ArchMemory::ArchMemory() { - paging_root_page_ = PageManager::instance()->allocPPN(PAGE_SIZE); + paging_root_page_ = PageManager::instance().allocPPN(PAGE_SIZE); address_space_id = ASID_COUNTER++; debug(A_MEMORY, "ArchMemory::ArchMemory(): Got new Page no. %zx\n", paging_root_page_); } +ArchMemory::ArchMemory(ppn_t paging_root_page) : + paging_root_page_(paging_root_page) +{ + debug(A_MEMORY, "ArchMemory::ArchMemory(%zx)\n", paging_root_page_); + address_space_id = ASID_COUNTER++; +} + +void ArchMemory::loadPagingStructureRoot(size_t ttbr0_value) +{ + __asm__ __volatile__("MSR TTBR0_EL1 , %[ttbr0]\n" + ::[ttbr0]"r"(ttbr0_value)); +} + +template +bool ArchMemory::tableEmpty(T* table) +{ + for (size_t i = 0; i < NUM_ENTRIES; i++) + { + if (table[i].entry_descriptor_type) + return false; + } + return true; +} + +template +void ArchMemory::removeEntry(T* table, size_t index) +{ + assert(table[index].entry_descriptor_type); + + table[index].entry_descriptor_type = 0; + memset(&table[index], 0, sizeof(table[index])); +} + bool ArchMemory::unmapPage(size_t virtual_page) { ArchMemoryMapping m = resolveMapping(virtual_page); + debug(A_MEMORY, "Unmap vpn %zx => %zx[%zx]->%zx[%zx]->%zx[%zx]->%zx\n", + virtual_page, + m.level1_ppn, m.level1_index, + m.level2_ppn, m.level2_index, + m.level3_ppn, m.level3_index, + m.page_ppn); assert(m.page_ppn != 0 && m.page_size == PAGE_SIZE && m.level3_entry[m.level3_index].entry_descriptor_type == ENTRY_DESCRIPTOR_PAGE); - m.level3_entry[m.level3_index].entry_descriptor_type = 0; - PageManager::instance()->freePPN(m.page_ppn); - ((size_t*)m.level3_entry)[m.level3_index] = 0; + removeEntry(m.level3_entry, m.level3_index); - bool empty = checkAndRemove((pointer)m.level3_entry, m.level3_index); + bool l2_empty = false; + bool l3_empty = tableEmpty(m.level3_entry); - if(empty) + if(l3_empty) { - empty = checkAndRemove((pointer)m.level2_entry, m.level2_index); - PageManager::instance()->freePPN(m.level3_ppn); + l2_empty = tableEmpty(&m.level2_entry->table); } - if(empty) - { - empty = checkAndRemove((pointer)m.level1_entry, m.level1_index); - PageManager::instance()->freePPN(m.level2_ppn); - } + flushAllTranslationCaches(virtual_page * PAGE_SIZE); // Needs to happen after page table entries have been modified but before PPNs are freed + + PageManager::instance().freePPN(m.page_ppn); + if(l3_empty) { PageManager::instance().freePPN(m.level3_ppn); } + if(l2_empty) { PageManager::instance().freePPN(m.level2_ppn); } return true; } -bool ArchMemory::mapPage(size_t virtual_page, size_t physical_page, size_t user_access) +bool ArchMemory::mapPage(vpn_t virtual_page, ppn_t physical_page, bool user_access) { - debug(A_MEMORY, "%zx %zx %zx %zx\n", paging_root_page_, virtual_page, physical_page, user_access); ArchMemoryMapping m = resolveMapping(paging_root_page_, virtual_page); + debug(A_MEMORY, "Map vpn %zx => %zx[%zx]->%zx[%zx]->%zx[%zx]->%zx to ppn: %lx, user: %u\n", + virtual_page, + m.level1_ppn, m.level1_index, + m.level2_ppn, m.level2_index, + m.level3_ppn, m.level3_index, + m.page_ppn, + physical_page, + user_access); + assert((m.page_size == 0) || (m.page_size == PAGE_SIZE)); if(m.level2_ppn == 0) { - m.level2_ppn = PageManager::instance()->allocPPN(PAGE_SIZE); + m.level2_ppn = PageManager::instance().allocPPN(PAGE_SIZE); m.level2_entry = (Level2Entry*)getIdentAddressOfPPN(m.level2_ppn); m.level1_entry[m.level1_index].table_address = m.level2_ppn; m.level1_entry[m.level1_index].entry_descriptor_type = ENTRY_DESCRIPTOR_PAGE; @@ -64,7 +114,7 @@ bool ArchMemory::mapPage(size_t virtual_page, size_t physical_page, size_t user_ if(m.level3_ppn == 0) { - m.level3_ppn = PageManager::instance()->allocPPN(PAGE_SIZE); + m.level3_ppn = PageManager::instance().allocPPN(PAGE_SIZE); m.level3_entry = (Level3Entry*)getIdentAddressOfPPN(m.level3_ppn); m.level2_entry[m.level2_index].table.table_address = m.level3_ppn; m.level2_entry[m.level2_index].table.entry_descriptor_type = ENTRY_DESCRIPTOR_PAGE; @@ -82,7 +132,6 @@ bool ArchMemory::mapPage(size_t virtual_page, size_t physical_page, size_t user_ return true; } - assert(false); // you should never get here return false; } @@ -97,31 +146,34 @@ ArchMemory::~ArchMemory() assert(level1_entry[level1_index].entry_descriptor_type != ENTRY_DESCRIPTOR_BLOCK); if(level1_entry[level1_index].entry_descriptor_type == ENTRY_DESCRIPTOR_TABLE) { + auto l2_ppn = level1_entry[level1_index].table_address; Level2Entry * level2_entry = (Level2Entry *)getIdentAddressOfPPN(level1_entry[level1_index].table_address); for(int level2_index = 0; level2_index < LEVEL2_ENTRIES; level2_index++) { assert(level2_entry[level2_index].table.entry_descriptor_type != ENTRY_DESCRIPTOR_BLOCK); if(level2_entry[level2_index].table.entry_descriptor_type == ENTRY_DESCRIPTOR_TABLE) { + auto l3_ppn = level2_entry[level2_index].table.table_address; Level3Entry * level3_entry = (Level3Entry *)getIdentAddressOfPPN(level2_entry[level2_index].table.table_address); for(int level3_index = 0; level3_index < LEVEL3_ENTRIES; level3_index++) { if(level3_entry[level3_index].entry_descriptor_type == ENTRY_DESCRIPTOR_PAGE) { - level3_entry[level3_index].entry_descriptor_type = 0; - PageManager::instance()->freePPN(level3_entry[level3_index].page_address); + auto page_ppn = level3_entry[level3_index].page_address; + removeEntry(level3_entry, level3_index); + PageManager::instance().freePPN(page_ppn); } } - level2_entry[level2_index].table.entry_descriptor_type = 0; - PageManager::instance()->freePPN(level2_entry[level2_index].table.table_address); + removeEntry(&level2_entry->table, level2_index); + PageManager::instance().freePPN(l3_ppn); } } - level1_entry[level1_index].entry_descriptor_type = 0; - PageManager::instance()->freePPN(level1_entry[level1_index].table_address); + removeEntry(level1_entry, level1_index); + PageManager::instance().freePPN(l2_ppn); } } - PageManager::instance()->freePPN(paging_root_page_); + PageManager::instance().freePPN(paging_root_page_); } template @@ -160,7 +212,7 @@ pointer ArchMemory::checkAddressValid(size_t vaddress_to_check) size_t ArchMemory::get_PPN_Of_VPN_In_KernelMapping(size_t virtual_page, size_t *physical_page, size_t *physical_pte_page) { - ArchMemoryMapping m = resolveMapping(VIRTUAL_TO_PHYSICAL_BOOT((size_t)kernel_paging_level1) / PAGE_SIZE, virtual_page); + ArchMemoryMapping m = resolveMapping(getKernelPagingStructureRootPhys() / PAGE_SIZE, virtual_page); if (physical_page) *physical_page = m.page_ppn; @@ -171,7 +223,7 @@ size_t ArchMemory::get_PPN_Of_VPN_In_KernelMapping(size_t virtual_page, size_t * return m.page_size; } -const ArchMemoryMapping ArchMemory::resolveMapping(uint64 vpage) +const ArchMemoryMapping ArchMemory::resolveMapping(size_t vpage) { return resolveMapping(paging_root_page_, vpage); } @@ -180,7 +232,7 @@ const ArchMemoryMapping ArchMemory::resolveMapping(size_t level1_ppn, size_t vpa { ArchMemoryMapping m; - size_t total_num_pages = PageManager::instance()->getTotalNumPages(); + size_t total_num_pages = PageManager::instance().getTotalNumPages(); m.level3_index = vpage; m.level2_index = m.level3_index / LEVEL3_ENTRIES; @@ -232,15 +284,25 @@ const ArchMemoryMapping ArchMemory::resolveMapping(size_t level1_ppn, size_t vpa m.page = getIdentAddressOfPPN(m.page_ppn); } else if(m.level2_entry[m.level2_index].table.entry_descriptor_type != 0) - assert(0); + { + assert(false && "Invalid level2 page table entry type"); + } return m; } -void ArchMemory::mapKernelPage(size_t virtual_page, size_t physical_page) +bool ArchMemory::mapKernelPage(size_t virtual_page, size_t physical_page, [[maybe_unused]]bool can_alloc_pages, [[maybe_unused]]bool memory_mapped_io) { ArchMemoryMapping m = resolveMapping(VIRTUAL_TO_PHYSICAL_BOOT((size_t)kernel_paging_level1) / PAGE_SIZE, virtual_page); + debug(A_MEMORY, "Map (kernel) %zx => l1: %zx, l2: %zx, l3: %zx, page: %zx, new page: %zx\n", + virtual_page, m.level1_ppn, m.level2_ppn, m.level3_ppn, m.page_ppn, physical_page); + + if (m.page) + { + debug(A_MEMORY, "Map (kernel) %zx, abort: page is already mapped\n", virtual_page); + return false; + } Level1Entry* level1_entry = kernel_paging_level1; assert(level1_entry[m.level1_index].entry_descriptor_type == ENTRY_DESCRIPTOR_TABLE); @@ -256,6 +318,8 @@ void ArchMemory::mapKernelPage(size_t virtual_page, size_t physical_page) level3_entry[m.level3_index].shareability_field = SHARE_ISH; level3_entry[m.level3_index].access_flag = ACCESS_FLAG; level3_entry[m.level3_index].page_address = physical_page; + + return true; } void ArchMemory::unmapKernelPage(size_t virtual_page) @@ -272,10 +336,140 @@ void ArchMemory::unmapKernelPage(size_t virtual_page) *((size_t*)&(level3_entry[m.level3_index])) = 0; - PageManager::instance()->freePPN(m.page_ppn); + PageManager::instance().freePPN(m.page_ppn); } size_t ArchMemory::getRootOfPagingStructure() { return paging_root_page_; } + +Level1Entry* ArchMemory::getKernelPagingStructureRootVirt() +{ + return kernel_paging_level1; +} + +size_t ArchMemory::getKernelPagingStructureRootPhys() +{ + return (size_t)VIRTUAL_TO_PHYSICAL_BOOT((size_t)getKernelPagingStructureRootVirt()); +} + +ArchMemory& ArchMemory::kernelArchMemory() +{ + static ArchMemory kernel_arch_mem((size_t)getKernelPagingStructureRootPhys()/PAGE_SIZE); + return kernel_arch_mem; +} + + +void ArchMemory::printMappings() +{ + size_t tcr_el1 = 0; + asm volatile ("mrs %0, tcr_el1" : "=r" (tcr_el1)); + + debug(A_MEMORY, "tcr_el1: %zx\n", tcr_el1); + + debug(A_MEMORY, "Page mappings for %zx\n", paging_root_page_); + + Level1Entry * level1_entry = (Level1Entry *)getIdentAddressOfPPN(paging_root_page_); + + debug(A_MEMORY, "Root page - %zx\n", paging_root_page_); + + // L0 skipped + for(int level1_index = 0; level1_index < LEVEL1_ENTRIES; level1_index++) + { + VAddr start{TTBR1_LIMIT_LOW}; + VAddr end{TTBR1_LIMIT_LOW}; + start.l1i = level1_index; + end.l1i = level1_index+1; + + if (level1_entry[level1_index].entry_descriptor_type == ENTRY_DESCRIPTOR_TABLE) + { + debug(A_MEMORY, "L1 -- [%12lx, %12lx)] %5zx[%5x]->%5zx\n", + start.addr, end.addr, paging_root_page_, + level1_index, level1_entry[level1_index].table_address); + } + else if (level1_entry[level1_index].entry_descriptor_type == ENTRY_DESCRIPTOR_BLOCK) + { + debug(A_MEMORY, "L1 -- [%12lx, %12lx)] %5zx[%5x]->%5zx BLOCK\n", + start.addr, end.addr, paging_root_page_, + level1_index, level1_entry[level1_index].table_address); + continue; + } + + if(level1_entry[level1_index].entry_descriptor_type == ENTRY_DESCRIPTOR_TABLE) + { + Level2Entry * level2_entry = (Level2Entry *)getIdentAddressOfPPN(level1_entry[level1_index].table_address); + for(int level2_index = 0; level2_index < LEVEL2_ENTRIES; level2_index++) + { + VAddr start{TTBR1_LIMIT_LOW}; + VAddr end{TTBR1_LIMIT_LOW}; + start.l1i = level1_index; + end.l1i = level2_index == LEVEL1_ENTRIES-1 ? level1_index+1 : level1_index; + start.l2i = level2_index; + end.l2i = level2_index+1; + + if (level2_entry[level2_index].table.entry_descriptor_type == ENTRY_DESCRIPTOR_TABLE) + { + debug(A_MEMORY, "L2 -- [%12lx, %12lx)] %5zx[%5x]->%5zx[%5x]->%5zx\n", + start.addr, end.addr, paging_root_page_, level1_index, level1_entry[level1_index].table_address, level2_index, level2_entry[level2_index].table.table_address); + } + else if (level2_entry[level2_index].table.entry_descriptor_type == ENTRY_DESCRIPTOR_BLOCK) + { + debug(A_MEMORY, "L2 -- [%12lx, %12lx)] %5zx[%5x]->%5zx[%5x]->%5x BLOCK\n", + start.addr, end.addr, paging_root_page_, level1_index, level1_entry[level1_index].table_address, level2_index, level2_entry[level2_index].block.block_address); + continue; + } + + + if(level2_entry[level2_index].table.entry_descriptor_type == ENTRY_DESCRIPTOR_TABLE) + { + Level3Entry * level3_entry = (Level3Entry *)getIdentAddressOfPPN(level2_entry[level2_index].table.table_address); + for(int level3_index = 0; level3_index < LEVEL3_ENTRIES; level3_index++) + { + VAddr start{TTBR1_LIMIT_LOW}; + VAddr end{TTBR1_LIMIT_LOW}; + start.l1i = level1_index; + end.l1i = level2_index == LEVEL2_ENTRIES-1 ? level1_index+1 : level1_index; + start.l2i = level2_index; + end.l2i = level3_index == LEVEL3_ENTRIES-1 ? level2_index+1 : level2_index; + start.l3i = level3_index; + end.l3i = level3_index+1; + + + if(level3_entry[level3_index].entry_descriptor_type == ENTRY_DESCRIPTOR_PAGE) + { + debug(A_MEMORY, "L3 -- [%12lx, %12lx)] %5zx[%5x]->%5zx[%5x]->%5zx[%5x]->%5zx PAGE\n", + start.addr, end.addr, paging_root_page_, + level1_index, + level1_entry[level1_index].table_address, + level2_index, + level2_entry[level2_index].table.table_address, + level3_index, + level3_entry[level3_index].page_address); + } + } + } + } + } + } +} + + +void ArchMemory::flushLocalTranslationCaches(size_t addr) +{ + if(A_MEMORY & OUTPUT_ADVANCED) + { + debug(A_MEMORY, "CPU %zx flushing translation caches for address %zx\n", SMP::currentCpuId(), addr); + } + + asm volatile("DSB ISHST\n" // Data synchronization barrier + "TLBI VAAE1, %[vpn]\n" // Invalidate all TLB entries for vpn + ::[vpn]"r"(addr >> 12)); +} + +void ArchMemory::flushAllTranslationCaches(size_t addr) +{ + flushLocalTranslationCaches(addr); + + // TODO: not yet implemented +} diff --git a/arch/armv8/common/source/ArchMulticore.cpp b/arch/armv8/common/source/ArchMulticore.cpp new file mode 100644 index 000000000..0959ec3e3 --- /dev/null +++ b/arch/armv8/common/source/ArchMulticore.cpp @@ -0,0 +1,73 @@ +#include "ArchMulticore.h" + +#include "SMP.h" + +#include "ArchCpuLocalStorage.h" +#include "ArchInterrupts.h" + +#include "EASTL/atomic.h" + +#include "debug.h" + +extern eastl::atomic running_cpus; + +size_t readCpuIdRegister() +{ + uint64_t mpidr_el1 = 0; + asm("MRS %[mpidr_el1], MPIDR_EL1\n" + :[mpidr_el1]"=g"(mpidr_el1)); + return mpidr_el1 & 0xFF; +} + +ArchCpu::ArchCpu() +{ + setId(readCpuIdRegister()); + debug(A_MULTICORE, "Initializing ArchCpu %zx\n", id()); + SMP::addCpuToList(this); +} + +IrqDomain& ArchCpu::rootIrqDomain() +{ + if (static bool initialized = false; !initialized) + { + initialized = true; + new (&cpu_irq_vector_domain_) IrqDomain("CPU interrupt vector"); + } + + return cpu_irq_vector_domain_; +} + +void ArchMulticore::initialize() +{ + assert(running_cpus == 0); + running_cpus = 1; + CpuLocalStorage::setCls(CpuLocalStorage::allocCls()); + ArchMulticore::initCpuLocalData(true); +} + +void ArchMulticore::initCpuLocalData([[maybe_unused]]bool boot_cpu) +{ + // The constructor of objects declared as cpu_local will be called automatically + // the first time the cpu_local object is used. + // cpu_local isn't actually cpu_local on armv8 (no cls implemented) -> initialized via global + // constructors + debug(A_MULTICORE, "Initializing CPU local objects for CPU %zu\n", SMP::currentCpuId()); + + // idle_thread = new IdleThread(); + // debug(A_MULTICORE, "CPU %zu: %s initialized\n", getCpuID(), idle_thread->getName()); + // idle_thread->pinned_to_cpu = getCpuID(); + // Scheduler::instance()->addNewThread(idle_thread); +} + +void ArchMulticore::startOtherCPUs() +{ +} + +void ArchMulticore::stopOtherCpus() +{ +} + +void ArchMulticore::sendFunctionCallMessage([[maybe_unused]]ArchCpu& cpu, [[maybe_unused]]RemoteFunctionCallMessage* fcall_message) +{ + assert(false && "Not implemented"); +} diff --git a/arch/armv8/common/source/ArchThreads.cpp b/arch/armv8/common/source/ArchThreads.cpp index 74ab11803..12f6a46b7 100644 --- a/arch/armv8/common/source/ArchThreads.cpp +++ b/arch/armv8/common/source/ArchThreads.cpp @@ -1,19 +1,22 @@ #include "ArchThreads.h" -#include "ArchInterrupts.h" -#include "ArchMemory.h" -#include "kprintf.h" -#include "paging-definitions.h" -#include "offsets.h" -#include "Thread.h" + #include "Scheduler.h" #include "SpinLock.h" +#include "Thread.h" +#include "kprintf.h" +#include "offsets.h" +#include "paging-definitions.h" + +#include "ArchInterrupts.h" +#include "ArchMemory.h" SpinLock global_atomic_add_lock(""); void ArchThreads::initialise() { new (&global_atomic_add_lock) SpinLock("global_atomic_add_lock"); - currentThreadRegisters = (ArchThreadRegisters*) new uint8[sizeof(ArchThreadRegisters)]; + static ArchThreadRegisters boot_thread_registers{}; + currentThreadRegisters = &boot_thread_registers; pointer paging_root = VIRTUAL_TO_PHYSICAL_BOOT(((pointer)kernel_paging_level1)); currentThreadRegisters->TTBR0 = paging_root; } @@ -28,39 +31,65 @@ void ArchThreads::setAddressSpace(Thread *thread, ArchMemory& arch_memory) if (thread->user_registers_) thread->user_registers_->TTBR0 = ttbr0_value; + + if(thread == currentThread) + { + switchToAddressSpace(arch_memory); + } } -void ArchThreads::createKernelRegisters(ArchThreadRegisters *&info, void* start_function, void* stack) +void ArchThreads::switchToAddressSpace(Thread* thread) +{ + ArchMemory::loadPagingStructureRoot(thread->kernel_registers_->TTBR0); +} + +void ArchThreads::switchToAddressSpace(ArchMemory& arch_memory) +{ + size_t ttbr0_value = (LOAD_BASE + arch_memory.paging_root_page_ * PAGE_SIZE) | (((size_t)arch_memory.address_space_id) << 48); + ArchMemory::loadPagingStructureRoot(ttbr0_value); +} + +eastl::unique_ptr ArchThreads::createKernelRegisters(void* start_function, void* stack) { - info = (ArchThreadRegisters*)new uint8[sizeof(ArchThreadRegisters)]; - memset((void*)info, 0, sizeof(ArchThreadRegisters)); assert(!((pointer)start_function & 0x3)); - info->ELR = (pointer)start_function; - info->SPSR = 0x60000005; - info->SP = (pointer)stack & ~0xF; - info->SP_SM = 0; - info->X[29] = (pointer)stack & ~0xF; // X29 is the fp - info->TTBR0 = 0; + + auto regs = eastl::make_unique(); + + regs->ELR = (pointer)start_function; + regs->SPSR = 0x60000005; + regs->SP = (pointer)stack & ~0xF; + regs->SP_SM = 0; + regs->X[29] = (pointer)stack & ~0xF; // X29 is the fp + regs->TTBR0 = 0; + + return regs; +} + +void ArchThreads::changeInstructionPointer(ArchThreadRegisters& info, void* function) +{ + info.ELR = (pointer)function; } -void ArchThreads::changeInstructionPointer(ArchThreadRegisters *info, void* function) +void* ArchThreads::getInstructionPointer(ArchThreadRegisters& info) { - info->ELR = (pointer)function; + return (void*)info.ELR; } -void ArchThreads::createUserRegisters(ArchThreadRegisters *&info, void* start_function, void* user_stack, void* kernel_stack) +eastl::unique_ptr ArchThreads::createUserRegisters(void* start_function, void* user_stack, void* kernel_stack) { - info = (ArchThreadRegisters*)new uint8[sizeof(ArchThreadRegisters)]; - memset((void*)info, 0, sizeof(ArchThreadRegisters)); assert(!((pointer)start_function & 0x3)); - info->ELR = (pointer) start_function; - info->SPSR = 0x60000000; - info->SP = (pointer) user_stack & ~0xF; - info->SP_SM = (pointer) kernel_stack & ~0xF; - info->X[29] = (pointer) user_stack & ~0xF; // X29 is the fp + auto regs = eastl::make_unique(); + + regs->ELR = (pointer) start_function; + regs->SPSR = 0x60000000; + regs->SP = (pointer) user_stack & ~0xF; + regs->SP_SM = (pointer) kernel_stack & ~0xF; + regs->X[29] = (pointer) user_stack & ~0xF; // X29 is the fp + + regs->TTBR0 = 0; - info->TTBR0 = 0; + return regs; } void ArchThreads::yield() @@ -131,7 +160,7 @@ void ArchThreads::printThreadRegisters(Thread *thread, bool verbose) void ArchThreads::printThreadRegisters(Thread *thread, uint32 userspace_registers, bool verbose) { - ArchThreadRegisters *info = userspace_registers?thread->user_registers_:thread->kernel_registers_; + ArchThreadRegisters *info = userspace_registers ? thread->user_registers_.get() : thread->kernel_registers_.get(); if (!info) { kprintfd("%sThread: %18p, has no %s registers. %s\n",userspace_registers?" User":"Kernel",thread,userspace_registers?"User":"Kernel",userspace_registers?"":"This should never(!) occur. How did you do that?"); @@ -175,14 +204,22 @@ void ArchThreads::debugCheckNewThread(Thread* thread) assert(thread->kernel_registers_->SP_SM == 0 && "kernel register set needs no backup of kernel SP_SM"); assert(thread->kernel_registers_->SP == thread->kernel_registers_->X[29] && "new kernel stack must be empty"); assert(thread->kernel_registers_->SP != currentThread->kernel_registers_->SP && thread->kernel_registers_->X[29] != currentThread->kernel_registers_->X[29] && "all threads need their own stack"); - if (thread->user_registers_ == 0) + if (!thread->user_registers_) return; assert(thread->kernel_registers_->ELR == 0 && "user threads should not start execution in kernel mode"); assert(thread->switch_to_userspace_ == 1 && "new user threads must start in userspace"); assert(thread->kernel_registers_->SP == thread->user_registers_->SP_SM && "SP_SM should point to kernel stack"); assert(thread->kernel_registers_->TTBR0 == thread->user_registers_->TTBR0 && "user and kernel part of a thread need to have the same pageing root"); assert(thread->user_registers_->ELR != 0 && "user eip needs to be valid... execution will start there"); - if (currentThread->user_registers_ == 0) + if (!currentThread->user_registers_) return; assert(currentThread->user_registers_->SP_SM != thread->user_registers_->SP_SM && "no 2 threads may have the same esp0 value"); } + + +[[noreturn]] void ArchThreads::startThreads([[maybe_unused]]Thread* init_thread) +{ + ArchInterrupts::enableInterrupts(); + ArchCommon::halt(); + assert(false); +} diff --git a/arch/armv8/common/source/IDEDriver.cpp b/arch/armv8/common/source/IDEDriver.cpp deleted file mode 100644 index f6cf0ab0f..000000000 --- a/arch/armv8/common/source/IDEDriver.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "BDManager.h" -#include "BDVirtualDevice.h" -#include "IDEDriver.h" -#include "MMCDriver.h" -#include "kstring.h" -#include "ArchInterrupts.h" -#include "kprintf.h" - -uint32 IDEDriver::doDeviceDetection() -{ - const char* name = "idea"; - MMCDriver* drv = new MMCDriver(); - BDVirtualDevice *bdv = new BDVirtualDevice(drv, 0, drv->getNumSectors(), drv->getSectorSize(), name, true); - BDManager::getInstance()->addVirtualDevice(bdv); - debug(IDE_DRIVER, "doDetection: initialized with MMCDriver!\n"); - processMBR(drv, 0, drv->SPT, name); - return 1; -} - -int32 IDEDriver::processMBR(BDDriver * drv, uint32 sector, uint32 SPT, const char *name) -{ - uint32 offset = 0, numsec = 0; - uint16 buff[256]; // read buffer - debug(IDE_DRIVER, "processMBR:reading MBR\n"); - - static uint32 part_num = 0; -// char part_num_str[2]; -// char part_name[10]; - - uint32 read_res = drv->readSector(sector, 1, (void *) buff); - - if (read_res != 0) - { - debug(IDE_DRIVER, "processMBR: drv returned BD_ERROR\n"); - return -1; - } - - MBR *mbr = (MBR *) buff; - - if (mbr->signature == 0xAA55) - { - debug(IDE_DRIVER, "processMBR: | Valid PC MBR | \n"); - FP * fp = (FP *) mbr->parts; - uint32 i; - for (i = 0; i < 4; i++, fp++) - { - switch (fp->systid) - { - case 0x00: - // do nothing - break; - case 0x05: // DOS extended partition - case 0x0F: // Windows extended partition - case 0x85: // linux extended partition - debug(IDE_DRIVER, "ext. part. at: %d \n", fp->relsect); - if (processMBR(drv, sector + fp->relsect, SPT, name) == -1) - processMBR(drv, sector + fp->relsect - SPT, SPT, name); - break; - default: - // offset = fp->relsect - SPT; - offset = fp->relsect; - numsec = fp->numsect; - - char part_name[6]; - strncpy(part_name, name, 4); - part_name[4] = part_num + '0'; - part_name[5] = 0; - part_num++; - BDVirtualDevice *bdv = new BDVirtualDevice(drv, offset, numsec, drv->getSectorSize(), part_name, true); - - // set Partition Type (FileSystem identifier) - bdv->setPartitionType(fp->systid); - - BDManager::getInstance()->addVirtualDevice(bdv); - break; - } - } - } - else - { - debug(IDE_DRIVER, "processMBR: | Invalid PC MBR %d | \n", mbr->signature); - return -1; - } - - debug(IDE_DRIVER, "processMBR:, done with partitions \n"); - return 0; -} diff --git a/arch/armv8/common/source/InterruptUtils.cpp b/arch/armv8/common/source/InterruptUtils.cpp index 309ba099b..10930e6a1 100644 --- a/arch/armv8/common/source/InterruptUtils.cpp +++ b/arch/armv8/common/source/InterruptUtils.cpp @@ -1,54 +1,45 @@ #include "InterruptUtils.h" -#include "ArchBoardSpecific.h" #include "BDManager.h" -#include "KeyboardManager.h" -#include "new.h" -#include "ArchMemory.h" -#include "ArchThreads.h" -#include "ArchCommon.h" #include "Console.h" #include "FrameBufferConsole.h" -#include "Terminal.h" -#include "kprintf.h" +#include "KeyboardManager.h" +#include "Loader.h" +#include "PageFaultHandler.h" #include "Scheduler.h" -#include "debug_bochs.h" - -#include "panic.h" - +#include "Syscall.h" +#include "Terminal.h" #include "Thread.h" -#include "ArchInterrupts.h" +#include "TimerTickHandler.h" #include "backtrace.h" - -#include "Loader.h" -#include "Syscall.h" +#include "debug_bochs.h" +#include "kprintf.h" +#include "new.h" #include "paging-definitions.h" -#include "PageFaultHandler.h" +#include "ArchBoardSpecific.h" +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMemory.h" +#include "ArchThreads.h" -extern Console* main_console; +#include "assert.h" +#include "debug.h" -extern ArchThreadRegisters *currentThreadRegisters; -extern Thread *currentThread; +extern Console* main_console; -void pageFaultHandler(size_t address, uint32 type NU, size_t exc_syndrome NU) +void pageFaultHandler(size_t address, size_t ip, uint32 type NU, size_t exc_syndrome NU) { bool writing = exc_syndrome & (1 << 6); bool fetch = type; bool present = (exc_syndrome & 0b111111) == 0b0011; //this is a permission fault - PageFaultHandler::enterPageFault(address, currentThread->switch_to_userspace_, present , writing, fetch); + PageFaultHandler::enterPageFault(address, ip, currentThread->switch_to_userspace_, present , writing, fetch); } void timer_irq_handler() { - static uint32 heart_beat_value = 0; - const char* clock = "/-\\|"; - ((FrameBufferConsole*)main_console)->consoleSetCharacter(0,0,clock[heart_beat_value],Console::GREEN); - heart_beat_value = (heart_beat_value + 1) % 4; - - Scheduler::instance()->incTicks(); - Scheduler::instance()->schedule(); + TimerTickHandler::handleTimerTick(); } void arch_uart1_irq_handler() @@ -74,7 +65,7 @@ void arch_swi_irq_handler(size_t swi) else if (swi == 0x0) // syscall { currentThread->switch_to_userspace_ = 0; - currentThreadRegisters = currentThread->kernel_registers_; + currentThreadRegisters = currentThread->kernel_registers_.get(); ArchInterrupts::enableInterrupts(); auto ret = Syscall::syscallException(currentThread->user_registers_->X[0], currentThread->user_registers_->X[1], @@ -87,7 +78,7 @@ void arch_swi_irq_handler(size_t swi) ArchInterrupts::disableInterrupts(); currentThread->switch_to_userspace_ = 1; - currentThreadRegisters = currentThread->user_registers_; + currentThreadRegisters = currentThread->user_registers_.get(); } else { @@ -96,7 +87,7 @@ void arch_swi_irq_handler(size_t swi) } } -extern "C" void exceptionHandler(size_t int_id , size_t curr_el NU, size_t exc_syndrome , size_t fault_address , size_t return_addr NU ) +extern "C" void exceptionHandler(size_t int_id, size_t curr_el NU, size_t exc_syndrome, size_t fault_address, size_t return_addr) { size_t type = int_id & 0x0F; size_t instruction_specific_syndrome = exc_syndrome & 0x1FFFFFF; @@ -118,11 +109,11 @@ extern "C" void exceptionHandler(size_t int_id , size_t curr_el NU, size_t exc_s } else if(exception_class == ARM_EXC_DATA_ABORT_CURR_EL || exception_class == ARM_EXC_DATA_ABORT_LOWER_EL) { - pageFaultHandler(fault_address , 0, exc_syndrome); + pageFaultHandler(fault_address, return_addr, 0, exc_syndrome); } else if(exception_class == ARM_EXC_INSTR_ABORT_CURR_EL || exception_class == ARM_EXC_INSTR_ABORT_LOWER_EL) { - pageFaultHandler(fault_address , 1, exc_syndrome); + pageFaultHandler(fault_address, return_addr, 1, exc_syndrome); } } else @@ -130,7 +121,7 @@ extern "C" void exceptionHandler(size_t int_id , size_t curr_el NU, size_t exc_s kprintfd("\nCPU Fault type = %zx\n",type); ArchThreads::printThreadRegisters(currentThread,false); currentThread->switch_to_userspace_ = 0; - currentThreadRegisters = currentThread->kernel_registers_; + currentThreadRegisters = currentThread->kernel_registers_.get(); ArchInterrupts::enableInterrupts(); currentThread->kill(); for(;;); diff --git a/arch/armv8/common/source/SerialManager.cpp b/arch/armv8/common/source/SerialManager.cpp index 512114777..294e8e0dc 100644 --- a/arch/armv8/common/source/SerialManager.cpp +++ b/arch/armv8/common/source/SerialManager.cpp @@ -1,35 +1,31 @@ -#include "ArchSerialInfo.h" #include "SerialManager.h" -#include "kstring.h" #include "debug_bochs.h" #include "kprintf.h" +#include "kstring.h" -SerialManager * SerialManager::instance_ = 0; +#include "ArchSerialInfo.h" -SerialManager::SerialManager() : num_ports( 0 ) +SerialManager::SerialManager() : + BasicDeviceDriver("Serial Port Driver"), + num_ports(0) { - assert(false); -}; +} -SerialManager::~SerialManager() +void SerialManager::doDeviceDetection() { - assert(false); -}; +} -uint32 SerialManager::get_num_ports() +uint32 SerialManager::get_num_ports() const { - assert(false); return num_ports; -}; +} -uint32 SerialManager::do_detection(uint32 is_paging_set_up __attribute__((unused))) +uint32 SerialManager::do_detection([[maybe_unused]]uint32 is_paging_set_up) { - assert(false); return num_ports; } -void SerialManager::service_irq(uint32 irq_num __attribute__((unused))) +void SerialManager::service_irq([[maybe_unused]]uint32 irq_num) { - assert(false); } diff --git a/arch/armv8/common/source/arch_backtrace.cpp b/arch/armv8/common/source/arch_backtrace.cpp index e5bb271e3..cd8f01ae9 100644 --- a/arch/armv8/common/source/arch_backtrace.cpp +++ b/arch/armv8/common/source/arch_backtrace.cpp @@ -1,13 +1,17 @@ -#include "kprintf.h" -#include "Thread.h" #include "arch_backtrace.h" + #include "InterruptUtils.h" -#include "ArchThreads.h" #include "KernelMemoryManager.h" // for use of "kernel_end_address" -#include "umap.h" -#include "ArchCommon.h" -#include "offsets.h" #include "Loader.h" +#include "Scheduler.h" +#include "Thread.h" +#include "kprintf.h" +#include "offsets.h" + +#include "ArchCommon.h" +#include "ArchThreads.h" + +#include "EASTL/map.h" struct StackFrame { @@ -15,7 +19,6 @@ struct StackFrame pointer lr; }; -extern Thread* currentThread; int backtrace(pointer *call_stack, int size, Thread *thread, bool use_stored_registers) { @@ -85,5 +88,3 @@ int backtrace_user(pointer *call_stack, int size, Thread *thread, bool /*use_sto return i; } - - diff --git a/arch/armv8/common/source/assert.cpp b/arch/armv8/common/source/assert.cpp index 101ac6849..528deba64 100644 --- a/arch/armv8/common/source/assert.cpp +++ b/arch/armv8/common/source/assert.cpp @@ -1,19 +1,20 @@ #include "assert.h" -#include "kprintf.h" -#include "panic.h" -#include "debug_bochs.h" + +#include "Scheduler.h" +#include "SystemState.h" #include "Thread.h" -#include "ArchInterrupts.h" +#include "debug_bochs.h" +#include "kprintf.h" -extern Thread* currentThread; +#include "ArchInterrupts.h" extern "C" void halt(); -__attribute__((noreturn)) void sweb_assert(const char *condition, uint32 line, const char* file) +[[noreturn]] void sweb_assert(const char *condition, uint32 line, const char* file, const char* function) { ArchInterrupts::disableInterrupts(); system_state = KPANIC; - kprintfd("KERNEL PANIC: Assertion %s failed in File %s on Line %d\n",condition, file, line); + kprintfd("KERNEL PANIC: Assertion %s failed in File %s, Function %s on Line %d\n", condition, file, function, line); if (currentThread != 0) currentThread->printBacktrace(false); while(1); diff --git a/arch/armv8/common/source/boot.S b/arch/armv8/common/source/boot.S index dbe07e6e0..8b8eababd 100644 --- a/arch/armv8/common/source/boot.S +++ b/arch/armv8/common/source/boot.S @@ -1,3 +1,6 @@ +# https://www.airs.com/blog/archives/518 +.section .note.GNU-stack,"",@progbits + //parts of this code are from: //http://infocenter.arm.com/help/topic/com.arm.doc.dai0527a/DAI0527A_baremetal_boot_code_for_ARMv8_A_processors.pdf diff --git a/arch/armv8/common/source/boot.cpp b/arch/armv8/common/source/boot.cpp index c35ff81c0..8aeb33eb8 100644 --- a/arch/armv8/common/source/boot.cpp +++ b/arch/armv8/common/source/boot.cpp @@ -1,11 +1,13 @@ -#include "types.h" #include "board_constants.h" +#include "debug_bochs.h" #include "init_boottime_pagetables.h" -#include "assert.h" #include "kstring.h" -#include "debug_bochs.h" #include "offsets.h" +#include "types.h" + +#include "assert.h" + extern "C" void uartWritePostboot(const char *str); extern "C" void uartWritePreboot(const char *str); extern "C" void setupSerialPort(); @@ -159,13 +161,13 @@ extern "C" void setupSerialPort() uart->CR = 0; //the default clock seems to be 48Mhz - gpio_boot->GPFSEL1 &= ~(0x111 << (4 * 3)); + gpio_boot->GPFSEL1 = gpio_boot->GPFSEL1 & ~(0x111 << (4 * 3)); // set GPIO usage to uart - gpio_boot->GPFSEL1 |= (0x100 << (4 * 3)); + gpio_boot->GPFSEL1 = gpio_boot->GPFSEL1 | (0x100 << (4 * 3)); //do the same for pin 15 - gpio_boot->GPFSEL1 &= ~(0x111 << (5 * 3)); + gpio_boot->GPFSEL1 = gpio_boot->GPFSEL1 & ~(0x111 << (5 * 3)); // set GPIO usage to uart - gpio_boot->GPFSEL1 |= (0x100 << (5 * 3)); + gpio_boot->GPFSEL1 = gpio_boot->GPFSEL1 | (0x100 << (5 * 3)); // disable the pullups gpio_boot->GPPUD = 0; diff --git a/arch/armv8/common/source/interrupt_entry.S b/arch/armv8/common/source/interrupt_entry.S index fece22050..679406953 100644 --- a/arch/armv8/common/source/interrupt_entry.S +++ b/arch/armv8/common/source/interrupt_entry.S @@ -1,3 +1,8 @@ +# https://www.airs.com/blog/archives/518 +.section .note.GNU-stack,"",@progbits + +.text + //parts of this code are from: //http://infocenter.arm.com/help/topic/com.arm.doc.dai0527a/DAI0527A_baremetal_boot_code_for_ARMv8_A_processors.pdf @@ -10,6 +15,7 @@ panic: b panic handle_exception: + // X2 holds address of neon register storage space in currentThreadRegisters (loaded in INTERRUPT_ENTRY) STP Q0 , Q1 , [X2, #32 * 0] //store neon registers STP Q2 , Q3 , [X2, #32 * 1] STP Q4 , Q5 , [X2, #32 * 2] @@ -52,8 +58,8 @@ exc_from_sm: MOV X5 , SP save_context_remainder: - STP X3 , X4 , [X2, #16 * 0] - STP X5 , X6 , [X2, #16 * 1] + STP X3 , X4 , [X2, #16 * 0] // save SPSR_EL1 + ELR_EL1 + STP X5 , X6 , [X2, #16 * 1] // save SP_EL0 + TTBR0_EL1 get_exc_parms: MRS X1 , CurrentEL // current el register @@ -68,10 +74,10 @@ restore_context: LDR X0 , [X0] LDR X1 , =kernel_sp_struct_offset LDR X1 , [X1] - ADD X0 , X0 , X1 + ADD X0 , X0 , X1 // X0 contains addr of currentThreadRegisters->SP_SM - LDP X1 , X2 , [X0, #-16 * 1] - LDP X3 , X4 , [X0, #-16 * 2] + LDP X1 , X2 , [X0, #-16 * 1] // Load saved SP -> X1, TTBR0 -> X2 + LDP X3 , X4 , [X0, #-16 * 2] // Load saved SPSR -> X3, ELR -> X4 check_sp: AND X6 , X3 , #0xf @@ -83,23 +89,25 @@ restore_sp_sm: B restore_rest restore_sp_um: - MSR SP_EL0 , X1 - LDR X1 , =boot_stack + MSR SP_EL0 , X1 // Restore saved SP to SP_EL0 + LDR X1 , =boot_stack // Switch current SP to top of boot_stack ADD X1 , X1 , #0x4000 MOV SP , X1 restore_rest: - MSR TTBR0_EL1 , X2 - MSR SPSR_EL1 , X3 - MSR ELR_EL1 , X4 + MSR TTBR0_EL1 , X2 // Restore TTBR0_EL1 + MSR SPSR_EL1 , X3 // Restore SPSR_EL1 + MSR ELR_EL1 , X4 // Restore ELR_EL1 + // Synchronization barrier DSB ISHST //TLBI ALLE1 DSB ISH ISB + // X0 = address of stored neon registers SUB X0 , X0 , #32 LDP Q30, Q31 , [X0, #-32 * 1] @@ -119,6 +127,7 @@ restore_rest: LDP Q2 , Q3 , [X0, #-32 * 15] LDP Q0 , Q1 , [X0, #-32 * 16] + // X0 = address of stored X0 - X30 registers SUB X0 , X0 , #(32*16) LDP X29 , X30, [X0, #-16 * 1] @@ -138,15 +147,19 @@ restore_rest: LDP X1 , X2 , [X0, #-16 * 15] LDR X0 , [X0, #(-16 * 15)-8] + // Synchronization barrier dmb sy dsb sy isb sy + //Return from exception ERET .macro INTERRUPT_ENTRY interrupt_id nop - + + // Exceptions automatically switch to SP_ELn, so we can safely use the stack here + /* this is used for debugging purposes MOV X0, #\interrupt_id MRS X1 , CurrentEL // current el register @@ -160,6 +173,7 @@ restore_rest: LDR X0, =currentThreadRegisters //get registers of current registers LDR X0 , [X0] + // Store X2 - X30 STP X2 , X3 , [X0, #16 * 1] STP X4 , X5 , [X0, #16 * 2] STP X6 , X7 , [X0, #16 * 3] @@ -176,6 +190,7 @@ restore_rest: STP X28 , X29 , [X0, #16 * 14] STR X30 , [X0, #16 * 15] + // Store X0 - X1 MOV X2 , X0 LDP X0 , X1 , [SP] , #16 STP X0 , X1 , [X2, #16 * 0] @@ -265,4 +280,3 @@ INTERRUPT_ENTRY_INVALID .balign 0x80 lower_el_aarch32_serror: INTERRUPT_ENTRY_INVALID - diff --git a/arch/armv8/rpi3/CMakeLists.compiler b/arch/armv8/rpi3/CMakeLists.compiler index b825f7fd4..d0c806528 100644 --- a/arch/armv8/rpi3/CMakeLists.compiler +++ b/arch/armv8/rpi3/CMakeLists.compiler @@ -3,10 +3,16 @@ INCLUDE(CMakeForceCompiler) # this one is important SET(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR aarch64) SET(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # which compilers to use for C and C++ +# Bare metal aarch-none-elf toolchains are available for download at +# https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads +# e.g. if you have a x86_64 host system running linux, use the tools in the 'x86_64 Linux hosted' category +# x86_64 Linux hosted cross toolchains -> AArch64 bare-metal target (aarch64-none-elf) + find_program(CMAKE_ASM_COMPILER aarch64-linux-gnu-gcc) find_program(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) find_program(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) @@ -15,7 +21,15 @@ find_program(LD_EXECUTABLE aarch64-linux-gnu-gcc) find_program(LD_EXECUTABLE_PURE aarch64-linux-gnu-ld) find_program(OBJCOPY_EXECUTABLE aarch64-linux-gnu-objcopy) -if(${CMAKE_VERSION} VERSION_LESS "3.6.0") - CMAKE_FORCE_CXX_COMPILER(${CMAKE_CXX_COMPILER} STATIC_LIBRARY) +if(${CMAKE_VERSION} VERSION_LESS "3.6.0") + CMAKE_FORCE_CXX_COMPILER(${CMAKE_CXX_COMPILER} STATIC_LIBRARY) CMAKE_FORCE_C_COMPILER(${CMAKE_C_COMPILER} STATIC_LIBRARY) endif() + +if(${CMAKE_C_COMPILER} STREQUAL "CMAKE_C_COMPILER-NOTFOUND") + message(SEND_ERROR "No suitable C compiler found!") +endif() + +if(${CMAKE_CXX_COMPILER} STREQUAL "CMAKE_CXX_COMPILER-NOTFOUND") + message(SEND_ERROR "No suitable C++ compiler found!") +endif() diff --git a/arch/armv8/rpi3/CMakeLists.include b/arch/armv8/rpi3/CMakeLists.include index 341b040e3..c6a2f663a 100644 --- a/arch/armv8/rpi3/CMakeLists.include +++ b/arch/armv8/rpi3/CMakeLists.include @@ -2,38 +2,64 @@ include(CheckCCompilerFlag) set(KERNEL_BINARY kernel.x) -set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -gdwarf-2 -Wall -Wextra -Werror -Wno-nonnull-compare -nostdinc -nostdlib -nostartfiles -nodefaultlibs -nostdinc++ -fno-builtin -fno-rtti -fno-exceptions -fno-stack-protector -ffreestanding -Wno-strict-aliasing -fshort-wchar ${NOPICFLAG}) -set(KERNEL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -std=gnu11 -O2 -gdwarf-2 -Wall -Wextra -Werror -nostdinc -nostartfiles -nodefaultlibs -nostdlib -fno-builtin -fno-exceptions -fno-stack-protector -ffreestanding -Wno-strict-aliasing -fshort-wchar ${NOPICFLAG}) - check_c_compiler_flag(-mno-outline-atomics HAS_MNO_OUTLINE_ATOMICS) if (HAS_MNO_OUTLINE_ATOMICS) - set(KERNEL_CMAKE_C_FLAGS ${KERNEL_CMAKE_C_FLAGS} -mno-outline-atomics) - set(KERNEL_CMAKE_CXX_FLAGS ${KERNEL_CMAKE_CXX_FLAGS} -mno-outline-atomics) + set(MNO_OUTLINE_ATOMIC -mno-outline-atomics) + else() + set(MNO_OUTLINE_ATOMIC "") endif() +set(KERNEL_COMMON_FLAGS -O2 -gdwarf-4 -Wall -Wextra -Werror -Wno-error=format -Wno-nonnull-compare -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-stack-protector -ffreestanding -Wno-strict-aliasing -fshort-wchar -fno-sync-libcalls ${MNO_OUTLINE_ATOMIC} ${NOPICFLAG}) -MACRO(ARCH2OBJ ARCHOBJ_LIBNAME LIBRARY_NAME) -ENDMACRO(ARCH2OBJ) - -set(ARCH_LD_ARGUMENTS -Wl,--build-id=none -Wl,-z -Wl,max-page-size=0x1000 -nostdinc -nostdlib -nodefaultlibs) -set(KERNEL_LD_ARGUMENT ${ARCH_LD_ARGUMENTS} -Wl,-Map=../kernel.map ${NOPIEFLAG}) -set(ARCH_APPEND_LD_ARGUMENTS -Wl,-lgcc) +set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ${KERNEL_COMMON_FLAGS} -std=gnu++20 -nostdinc++ -fno-rtti -fno-exceptions) +set(KERNEL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} ${KERNEL_COMMON_FLAGS} -std=gnu11 -nostdlib -fno-exceptions) set(ADD_LD_ARGUMENT -Wl,"${PROJECT_BINARY_DIR}/lib/debug_info.o") -set(KERNEL_IMAGE_OBJCOPY -COMMAND ${PROJECT_BINARY_DIR}/add-dbg ${PROJECT_BINARY_DIR}/kernel.x ${PROJECT_BINARY_DIR}/kernel.dbg -COMMAND touch ${PROJECT_BINARY_DIR}/debug_info.c -COMMAND ${CMAKE_C_COMPILER} -c ${PROJECT_BINARY_DIR}/debug_info.c -o ${PROJECT_BINARY_DIR}/lib/debug_info.o -COMMAND "${OBJCOPY_EXECUTABLE}" --add-section .swebdbg="${PROJECT_BINARY_DIR}/kernel.dbg" --set-section-flags .swebdbg=load,data ${PROJECT_BINARY_DIR}/lib/debug_info.o -COMMAND rm ${PROJECT_BINARY_DIR}/debug_info.c -) - if ("${VIRTUALIZED_QEMU}" STREQUAL "1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVIRTUALIZED_QEMU") -endif ("${VIRTUALIZED_QEMU}" STREQUAL "1") + endif ("${VIRTUALIZED_QEMU}" STREQUAL "1") + + +target_compile_options(arch_options INTERFACE +) + +target_link_options(arch_options INTERFACE + -Wl,--build-id=none -Wl,-z -Wl,max-page-size=0x1000 -nostdinc -nostdlib -nodefaultlibs) + + +target_link_options(kernel_options INTERFACE + -Wl,-Map=../kernel.map ${NOPIEFLAG}) + +target_link_libraries(kernel_options INTERFACE + -Wl,--no-whole-archive -Wl,-lgcc -Wl,--whole-archive) + +target_compile_options(kernel_options INTERFACE + $<$:${KERNEL_CMAKE_CXX_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> +) + +set_property(TARGET kernel PROPERTY ENABLE_EXPORTS 1) + +MACRO(ARCH2OBJ ARCHOBJ_LIBNAME LIBRARY_NAME) +ENDMACRO(ARCH2OBJ) + +add_custom_command(TARGET kernel + POST_BUILD + BYPRODUCTS lib/debug_info.o kernel.dbg + COMMAND add-dbg $ ${PROJECT_BINARY_DIR}/kernel.dbg + COMMAND touch ${PROJECT_BINARY_DIR}/debug_info.c + COMMAND ${CMAKE_C_COMPILER} -c ${PROJECT_BINARY_DIR}/debug_info.c -o ${PROJECT_BINARY_DIR}/lib/debug_info.o + COMMAND "${OBJCOPY_EXECUTABLE}" --add-section .swebdbg="${PROJECT_BINARY_DIR}/kernel.dbg" --set-section-flags .swebdbg=load,data ${PROJECT_BINARY_DIR}/lib/debug_info.o + COMMAND rm ${PROJECT_BINARY_DIR}/debug_info.c + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMENT "Creating debug info" +) + +# TODO: link debug info into kernel # sdcard: Create an sdcard for the raspberry pi @@ -51,7 +77,7 @@ add_custom_target(sdcardq ) add_custom_target(qemu - COMMAND qemu-system-aarch64 -M raspi3 -cpu cortex-a53 -m 1024 -drive file=${HDD_IMAGE},if=sd -no-reboot -kernel kernel.x -serial stdio -d guest_errors,unimp + COMMAND qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -m 1024 -drive file=${HDD_IMAGE},if=sd -no-reboot -kernel kernel.x -serial stdio -d guest_errors,unimp -s COMMENT "Executing `qemu-system-aarch64 -M raspi3 -cpu cortex-a53 -m 1024 -no-reboot -kernel kernel.x -serial stdio -d guest_errors,unimp`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I @@ -59,7 +85,7 @@ add_custom_target(qemu # qemugdb: Run qemu in non debugging mode add_custom_target(qemugdb - COMMAND qemu-system-aarch64 -M raspi3 -cpu cortex-a53 -m 1024 -drive file=${HDD_IMAGE},if=sd -no-reboot -kernel kernel.x -serial stdio -d guest_errors,unimp -s -S + COMMAND qemu-system-aarch64 -M raspi3b -cpu cortex-a53 -m 1024 -drive file=${HDD_IMAGE},if=sd -no-reboot -kernel kernel.x -serial stdio -d guest_errors,unimp -s -S COMMENT "Executing `qemu-system-aarch64 -M raspi3 -cpu cortex-a53 -m 1024 -no-reboot -kernel kernel.x -serial stdio -d guest_errors,unimp -s -S`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I @@ -70,7 +96,7 @@ add_custom_target(lst COMMAND aarch64-linux-gnu-objdump -d kernel.x > kernel.lst COMMAND reset -I ) - + add_custom_target(image COMMAND rm -rf kernel8.img COMMAND aarch64-linux-gnu-objcopy -O binary kernel.x kernel8.img @@ -83,4 +109,3 @@ add_custom_target(lst # WORKING_DIRECTORY ${PROJECT_BINARY_DIR} # COMMAND reset -I # ) - diff --git a/arch/armv8/rpi3/CMakeLists.userspace b/arch/armv8/rpi3/CMakeLists.userspace index 3f90d87e9..924a2a196 100644 --- a/arch/armv8/rpi3/CMakeLists.userspace +++ b/arch/armv8/rpi3/CMakeLists.userspace @@ -1,9 +1,28 @@ -INCLUDE(CMakeForceCompiler) -SET(CMAKE_SYSTEM_NAME Generic) -SET(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +cmake_policy(SET CMP0079 NEW) + +include(CMakeForceCompiler) +include(CheckCCompilerFlag) +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) find_program(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) -set(ARCH_APPEND_LD_ARGUMENTS -Wl,-no-whole-archive -Wl,-lgcc) +check_c_compiler_flag(-mno-outline-atomics HAS_MNO_OUTLINE_ATOMICS) +if (HAS_MNO_OUTLINE_ATOMICS) + set(MNO_OUTLINE_ATOMIC -mno-outline-atomics) + else() + set(MNO_OUTLINE_ATOMIC "") +endif() + + +set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -g -O0 -gdwarf-4 -static -nostdinc -fno-builtin -nostdlib -nolibc -fno-stack-protector -fno-common -Wno-error=unused-variable -fno-stack-clash-protection -fno-sync-libcalls ${MNO_OUTLINE_ATOMIC}) + +target_link_libraries(userspace_options INTERFACE + -Wl,--no-whole-archive -Wl,-lgcc -Wl,--whole-archive) +target_link_options(userspace_options INTERFACE + -static) -set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -std=gnu11 -g -O0 -static -nostdinc -fno-builtin -nostdlib -fno-stack-protector -fno-common -Werror=implicit-function-declaration -Wno-error=unused-variable -fno-stack-clash-protection) -set(ARCH_USERSPACE_LINKER_OPTIONS -static) +target_compile_options(userspace_options INTERFACE + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu++20 -nostdinc++> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu11 -Werror=implicit-function-declaration> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS}> +) diff --git a/arch/armv8/rpi3/source/ArchBoardSpecific.cpp b/arch/armv8/rpi3/source/ArchBoardSpecific.cpp index b041edea0..106fb2cda 100644 --- a/arch/armv8/rpi3/source/ArchBoardSpecific.cpp +++ b/arch/armv8/rpi3/source/ArchBoardSpecific.cpp @@ -1,18 +1,20 @@ #include "ArchBoardSpecific.h" -#include "KeyboardManager.h" -#include "board_constants.h" +#include "FrameBufferConsole.h" #include "InterruptUtils.h" -#include "ArchCommon.h" -#include "assert.h" -#include "offsets.h" -#include "ArchInterrupts.h" +#include "KeyboardManager.h" #include "Scheduler.h" -#include "FrameBufferConsole.h" +#include "board_constants.h" #include "kprintf.h" +#include "offsets.h" #include "paging-definitions.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" #include "ArchMemory.h" +#include "assert.h" + #define PHYSICAL_MEMORY_AVAILABLE (PAGE_ENTRIES * PAGE_SIZE * 4) pointer framebuffer; @@ -163,7 +165,7 @@ void ArchBoardSpecific::disableKBD() void ArchBoardSpecific::keyboard_irq_handler() { - KeyboardManager::instance()->serviceIRQ(); + KeyboardManager::instance().serviceIRQ(); } void resetTimer() diff --git a/arch/armv8/rpi3/source/KeyboardManager.cpp b/arch/armv8/rpi3/source/KeyboardManager.cpp index f895125ff..a454ea8d1 100644 --- a/arch/armv8/rpi3/source/KeyboardManager.cpp +++ b/arch/armv8/rpi3/source/KeyboardManager.cpp @@ -37,20 +37,20 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 128 }; -KeyboardManager *KeyboardManager::instance_ = 0; extern struct UsbDevice *Devices[]; KeyboardManager::KeyboardManager() : - keyboard_buffer_(256), extended_scancode(0), keyboard_status_(0), usb_kbd_addr_(0), current_key_(0), next_is_up_(0) + IrqDomain("Keyboard"), + keyboard_buffer_(256), + extended_scancode(0), + keyboard_status_(0), + usb_kbd_addr_(0), + current_key_(0), + next_is_up_(0) { - - } -KeyboardManager::~KeyboardManager() -{ -} void KeyboardManager::kb_wait() { diff --git a/arch/armv8/rpi3/source/MMCDriver.cpp b/arch/armv8/rpi3/source/MMCDriver.cpp index 381ec2875..21e1dabe8 100644 --- a/arch/armv8/rpi3/source/MMCDriver.cpp +++ b/arch/armv8/rpi3/source/MMCDriver.cpp @@ -1,10 +1,14 @@ +#include "MMCDriver.h" + #include "BDManager.h" #include "BDRequest.h" -#include "MMCDriver.h" -#include "ArchInterrupts.h" -#include "offsets.h" +#include "BDVirtualDevice.h" +#include "MasterBootRecord.h" #include "Scheduler.h" #include "kprintf.h" +#include "offsets.h" + +#include "ArchInterrupts.h" struct MMCI { uint32 arg2; @@ -44,7 +48,7 @@ struct MMCI { uint32_t slotisr_ver; }__attribute__((packed, aligned(4))); -struct MMCI* mmci = (struct MMCI*) (IDENT_MAPPING_START | PYHSICAL_MMIO_OFFSET |0x00300000); +volatile struct MMCI* mmci = (struct MMCI*) (IDENT_MAPPING_START | PYHSICAL_MMIO_OFFSET |0x00300000); volatile GpioRegisters *gpio = (GpioRegisters*)(IDENT_MAPPING_START | GPIO_REGS_BASE); //the MMC code is from: @@ -220,40 +224,40 @@ int NO_OPTIMIZE mmcSendCommand(uint32 code, uint32 arg) if(mmcGetStatus(SR_CMD_INHIBIT)) assert(false && "MMC ERROR: EMMC busy"); - debug(MMC_DRIVER, "MMC: Send Command: %x %x\n", code , arg); + debug(MMC_DRIVER, "MMC: Send Command: %x %x\n", code, arg); //mmcWaitMicroSeconds(2600000); mmcWaitCycles(10000); - mmci->interrupt=(uint32)mmci->interrupt; - mmci->arg1=arg; + mmci->interrupt = (uint32)mmci->interrupt; + mmci->arg1 = arg; mmci->cmdtm = code; - if(code==CMD_SEND_OP_COND) + if (code == CMD_SEND_OP_COND) mmcWaitMicroSeconds(1000); - else if(code==CMD_SEND_IF_COND || code==CMD_APP_CMD) + else if (code == CMD_SEND_IF_COND || code == CMD_APP_CMD) mmcWaitMicroSeconds(100); - if((tmp_value=mmcWaifForInterrupt(INT_CMD_DONE))) + if ((tmp_value = mmcWaifForInterrupt(INT_CMD_DONE))) assert(false && "MMC ERROR: failed to send EMMC command"); tmp_value = mmci->resp0; - if(code == CMD_GO_IDLE || code == CMD_APP_CMD) + if (code == CMD_GO_IDLE || code == CMD_APP_CMD) return 0; - else if(code == (CMD_APP_CMD | CMD_RSPNS_48)) + else if (code == (CMD_APP_CMD | CMD_RSPNS_48)) return tmp_value & SR_APP_CMD; - else if(code == CMD_SEND_OP_COND) + else if (code == CMD_SEND_OP_COND) return tmp_value; - else if(code == CMD_SEND_IF_COND) + else if (code == CMD_SEND_IF_COND) return tmp_value == arg ? SD_OK : SD_ERROR; - else if(code == CMD_ALL_SEND_CID) + else if (code == CMD_ALL_SEND_CID) { tmp_value |= mmci->resp3; tmp_value |= mmci->resp2; tmp_value |= mmci->resp1; return tmp_value; } - else if(code == CMD_SEND_REL_ADDR) + else if (code == CMD_SEND_REL_ADDR) { mmc_error = CMD_ERRORS_MASK & (((tmp_value & 0x1fff) << 0) @@ -270,7 +274,7 @@ int NO_OPTIMIZE mmcSendCommand(uint32 code, uint32 arg) //read block from sdcard int NO_OPTIMIZE mmcReadBlock(uint32 block_address, uint8 *buffer) { - debug(MMC_DRIVER,"MMC: Reading Block: %x with buffer: %p from mmc card\n", block_address, buffer); + debug(MMC_DRIVER,"MMC: Reading Block: %x with buffer: %p from mmc card\n", block_address, buffer); assert(mmcGetStatus(SR_DAT_INHIBIT) == 0 && "MMC ERROR: Timeout"); @@ -278,21 +282,21 @@ int NO_OPTIMIZE mmcReadBlock(uint32 block_address, uint8 *buffer) mmci->blksizecnt = (1 << 16) | 512; - if(sd_scr[0] & SCR_SUPP_CCS) + if (sd_scr[0] & SCR_SUPP_CCS) { - mmcSendCommand(CMD_READ_SINGLE,block_address); + mmcSendCommand(CMD_READ_SINGLE, block_address); assert(mmc_error == 0 && ""); } - if(!(sd_scr[0] & SCR_SUPP_CCS)) + if (!(sd_scr[0] & SCR_SUPP_CCS)) { - mmcSendCommand(CMD_READ_SINGLE,block_address * 512); + mmcSendCommand(CMD_READ_SINGLE, block_address * 512); assert(mmc_error == 0 && ""); } assert(mmcWaifForInterrupt(INT_READ_RDY) == 0 && "MMC ERROR: Timeout while waiting for ready to read"); - for(int index = 0; index < 128; index++) + for (int index = 0; index < 128; index++) buf[index] = mmci->data; return 0; @@ -309,13 +313,13 @@ int NO_OPTIMIZE mmcWriteBlock(uint32 block_address, uint8 *buffer) mmci->blksizecnt = (1 << 16) | 512; - if(sd_scr[0] & SCR_SUPP_CCS) + if (sd_scr[0] & SCR_SUPP_CCS) { mmcSendCommand(CMD_WRITE_SINGLE, block_address); assert(mmc_error == 0 && ""); } - if(!(sd_scr[0] & SCR_SUPP_CCS)) + if (!(sd_scr[0] & SCR_SUPP_CCS)) { mmcSendCommand(CMD_WRITE_SINGLE, block_address * 512); assert(mmc_error == 0 && ""); @@ -323,7 +327,7 @@ int NO_OPTIMIZE mmcWriteBlock(uint32 block_address, uint8 *buffer) assert(mmcWaifForInterrupt(INT_WRITE_RDY) == 0 && "MMC ERROR: Timeout while waiting for ready to read"); - for(int index = 0; index < 128; index++) + for (int index = 0; index < 128; index++) mmci->data = buf[index]; return 0; @@ -336,36 +340,36 @@ int NO_OPTIMIZE mmcSetClock(uint32 f) int cnt = 100000; - while((mmci->status & (SR_CMD_INHIBIT|SR_DAT_INHIBIT)) && cnt--) + while ((mmci->status & (SR_CMD_INHIBIT|SR_DAT_INHIBIT)) && cnt--) mmcWaitMicroSeconds(10); assert(cnt > 0 && "MMC ERROR: timeout waiting for inhibit flag"); - mmci->control1 &= ~C1_CLK_EN; + mmci->control1 = mmci->control1 & ~C1_CLK_EN; mmcWaitMicroSeconds(10); x = c - 1; - if(!x) + if (!x) shift = 0; else { - if(!(x & 0xffff0000u)) { x <<= 16; shift -= 16; } - if(!(x & 0xff000000u)) { x <<= 8; shift -= 8; } - if(!(x & 0xf0000000u)) { x <<= 4; shift -= 4; } - if(!(x & 0xc0000000u)) { x <<= 2; shift -= 2; } - if(!(x & 0x80000000u)) { x <<= 1; shift -= 1; } - if(shift > 0) shift--; - if(shift > 7) shift = 7; + if (!(x & 0xffff0000u)) { x <<= 16; shift -= 16; } + if (!(x & 0xff000000u)) { x <<= 8; shift -= 8; } + if (!(x & 0xf0000000u)) { x <<= 4; shift -= 4; } + if (!(x & 0xc0000000u)) { x <<= 2; shift -= 2; } + if (!(x & 0x80000000u)) { x <<= 1; shift -= 1; } + if (shift > 0) shift--; + if (shift > 7) shift = 7; } - if(sd_hv > HOST_SPEC_V2) + if (sd_hv > HOST_SPEC_V2) divisor = c; else divisor = (1 << shift); - if(divisor <= 2) + if (divisor <= 2) { divisor = 2; shift = 0; @@ -373,19 +377,19 @@ int NO_OPTIMIZE mmcSetClock(uint32 f) debug(MMC_DRIVER, "MMC Clock divisor: %x shift: %x\n", divisor, shift); - if(sd_hv > HOST_SPEC_V2) + if (sd_hv > HOST_SPEC_V2) h = (divisor & 0x300) >> 2; divisor = (((divisor & 0x0ff) << 8) | h); mmci->control1 = (mmci->control1 & 0xffff003f) | divisor; mmcWaitMicroSeconds(10); - mmci->control1 |= C1_CLK_EN; + mmci->control1 = mmci->control1 | C1_CLK_EN; mmcWaitMicroSeconds(10); cnt = 10000; - while(!(mmci->control1 & C1_CLK_STABLE) && cnt--) + while (!(mmci->control1 & C1_CLK_STABLE) && cnt--) mmcWaitMicroSeconds(10); assert(cnt > 0 && "MMC ERROR: failed to get stable clock"); @@ -442,19 +446,19 @@ int NO_OPTIMIZE mmcInit() // Reset the card. mmci->control0 = 0; - mmci->control1 |= C1_SRST_HC; + mmci->control1 = mmci->control1 | C1_SRST_HC; - cnt=10000; + cnt = 10000; do { mmcWaitMicroSeconds(10); - } while((mmci->control1 & C1_SRST_HC) && cnt--); + } while ((mmci->control1 & C1_SRST_HC) && cnt--); assert(cnt > 0 && "MMC ERROR: failed to reset"); - debug(MMC_DRIVER,"MMC: reset ok\n"); + debug(MMC_DRIVER, "MMC: reset ok\n"); - mmci->control1 |= C1_CLK_INTLEN | C1_TOUNIT_MAX; + mmci->control1 = mmci->control1 | C1_CLK_INTLEN | C1_TOUNIT_MAX; mmcWaitMicroSeconds(10); // Set clock to setup frequency. @@ -474,43 +478,43 @@ int NO_OPTIMIZE mmcInit() mmcSendCommand(CMD_SEND_IF_COND, 0x000001AA); assert(mmc_error == 0); - cnt=6; - tmp_var=0; - while(!(tmp_var & ACMD41_CMD_COMPLETE) && cnt--) + cnt = 6; + tmp_var = 0; + while (!(tmp_var & ACMD41_CMD_COMPLETE) && cnt--) { mmcWaitCycles(400); tmp_var = mmcSendCommand(CMD_SEND_OP_COND,ACMD41_ARG_HC); - debug(MMC_DRIVER,"EMMC: CMD_SEND_OP_COND returned \n"); + debug(MMC_DRIVER,"EMMC: CMD_SEND_OP_COND returned\n"); if(tmp_var & ACMD41_CMD_COMPLETE) - debug(MMC_DRIVER,"COMPLETE \n"); + debug(MMC_DRIVER,"COMPLETE\n"); if(tmp_var & ACMD41_VOLTAGE) - debug(MMC_DRIVER,"VOLTAGE \n"); + debug(MMC_DRIVER,"VOLTAGE\n"); if(tmp_var & ACMD41_CMD_CCS) - debug(MMC_DRIVER,"CCS \n"); + debug(MMC_DRIVER,"CCS\n"); debug(MMC_DRIVER,"%zd \n",tmp_var >> 32); debug(MMC_DRIVER,"%zd\n",tmp_var); - if((int)mmc_error != (int)SD_TIMEOUT && mmc_error != SD_OK ) + if ((int)mmc_error != (int)SD_TIMEOUT && mmc_error != SD_OK ) assert(false && "MMC ERROR: EMMC ACMD41 returned error"); } assert((tmp_var & ACMD41_CMD_COMPLETE) && cnt); assert(tmp_var & ACMD41_VOLTAGE); - if(tmp_var & ACMD41_CMD_CCS) + if (tmp_var & ACMD41_CMD_CCS) ccs = SCR_SUPP_CCS; mmcSendCommand(CMD_ALL_SEND_CID, 0); sd_rca = mmcSendCommand(CMD_SEND_REL_ADDR, 0); - debug(MMC_DRIVER, "EMMC: CMD_SEND_REL_ADDR returned %zx \n", sd_rca); + debug(MMC_DRIVER, "EMMC: CMD_SEND_REL_ADDR returned %zx\n", sd_rca); assert(mmc_error == 0); assert(mmcSetClock(25000000) == 0 && "MMC ERROR: while setting clock"); @@ -526,9 +530,9 @@ int NO_OPTIMIZE mmcInit() tmp_var = 0; cnt = 100000; - while(tmp_var < 2 && cnt) + while (tmp_var < 2 && cnt) { - if( mmci->status & SR_READ_AVAILABLE ) + if (mmci->status & SR_READ_AVAILABLE) sd_scr[tmp_var++] = mmci->data; else mmcWaitMicroSeconds(1); @@ -536,11 +540,11 @@ int NO_OPTIMIZE mmcInit() assert(tmp_var == 2); - if(sd_scr[0] & SCR_SD_BUS_WIDTH_4) + if (sd_scr[0] & SCR_SD_BUS_WIDTH_4) { - mmcSendCommand(CMD_SET_BUS_WIDTH, sd_rca|2); + mmcSendCommand(CMD_SET_BUS_WIDTH, sd_rca | 2); assert(mmc_error == 0); - mmci->control0 |= C0_HCTL_DWITDH; + mmci->control0 = mmci->control0 | C0_HCTL_DWITDH; } sd_scr[0] &= ~SCR_SUPP_CCS; @@ -549,30 +553,35 @@ int NO_OPTIMIZE mmcInit() return SD_OK; } -MMCDriver::MMCDriver() : SPT(63), lock_("MMCDriver::lock_"), rca_(0), sector_size_(512), num_sectors_(210672) +MMCDrive::MMCDrive() : + Device(eastl::string("MMC disk")), + SPT(63), + lock_("MMCDrive::lock_"), + rca_(0), + sector_size_(512), + num_sectors_(210672) { mmcInit(); } -MMCDriver::~MMCDriver() +MMCDrive::~MMCDrive() { - } -uint32 MMCDriver::addRequest( BDRequest * br) +uint32 MMCDrive::addRequest( BDRequest * br) { ScopeLock lock(lock_); - debug(MMC_DRIVER, "addRequest %d!\n", br->getCmd() ); + debug(MMC_DRIVER, "addRequest %d!\n", (int)br->getCmd()); int32 res = -1; - switch( br->getCmd() ) + switch (br->getCmd()) { - case BDRequest::BD_READ: - res = readSector( br->getStartBlock(), br->getNumBlocks(), br->getBuffer() ); + case BDRequest::BD_CMD::BD_READ: + res = readSector(br->getStartBlock(), br->getNumBlocks(), br->getBuffer()); break; - case BDRequest::BD_WRITE: - res = writeSector( br->getStartBlock(), br->getNumBlocks(), br->getBuffer() ); + case BDRequest::BD_CMD::BD_WRITE: + res = writeSector(br->getStartBlock(), br->getNumBlocks(), br->getBuffer()); break; default: res = -1; @@ -580,12 +589,13 @@ uint32 MMCDriver::addRequest( BDRequest * br) } debug(MMC_DRIVER, "addRequest:No IRQ operation !!\n"); - br->setStatus( BDRequest::BD_DONE ); + br->setStatus(BDRequest::BD_RESULT::BD_DONE); return res; } + int sd_readblock(unsigned int block_address, unsigned char *buffer, unsigned int num); -int32 MMCDriver::readBlock ( uint32 address, void *buffer ) +int32 MMCDrive::readBlock(uint32 address, void *buffer) { debug(MMC_DRIVER,"readBlock: address: %x, buffer: %p\n",address, buffer); @@ -594,9 +604,9 @@ int32 MMCDriver::readBlock ( uint32 address, void *buffer ) return 0; } -int32 MMCDriver::readSector ( uint32 start_sector, uint32 num_sectors, void *buffer ) +int32 MMCDrive::readSector(uint32 start_sector, uint32 num_sectors, void *buffer) { - debug(MMC_DRIVER,"readSector: start: %x, num: %x, buffer: %p\n",start_sector, num_sectors, buffer); + debug(MMC_DRIVER,"readSector: start: %x, num: %x, buffer: %p\n", start_sector, num_sectors, buffer); for (uint32 i = 0; i < num_sectors; ++i) { readBlock((start_sector + i) * sector_size_, (char*)buffer + i * sector_size_); @@ -604,7 +614,7 @@ int32 MMCDriver::readSector ( uint32 start_sector, uint32 num_sectors, void *buf return 0; } -int32 MMCDriver::writeBlock ( uint32 address, void *buffer) +int32 MMCDrive::writeBlock(uint32 address, void *buffer) { debug(MMC_DRIVER, "writeBlock: address: %x, buffer: %p\n", address, buffer); @@ -613,9 +623,9 @@ int32 MMCDriver::writeBlock ( uint32 address, void *buffer) return 0; } -int32 MMCDriver::writeSector ( uint32 start_sector, uint32 num_sectors, void * buffer) +int32 MMCDrive::writeSector(uint32 start_sector, uint32 num_sectors, void * buffer) { - debug(MMC_DRIVER,"writeSector: start: %x, num: %x, buffer: %p\n",start_sector, num_sectors, buffer); + debug(MMC_DRIVER,"writeSector: start: %x, num: %x, buffer: %p\n", start_sector, num_sectors, buffer); for (uint32 i = 0; i < num_sectors; ++i) { writeBlock((start_sector + i) * sector_size_, (char*)buffer + i * sector_size_); @@ -623,17 +633,43 @@ int32 MMCDriver::writeSector ( uint32 start_sector, uint32 num_sectors, void * b return 0; } -uint32 MMCDriver::getNumSectors() +uint32 MMCDrive::getNumSectors() { return num_sectors_; } -uint32 MMCDriver::getSectorSize() +uint32 MMCDrive::getSectorSize() { return sector_size_; } -void MMCDriver::serviceIRQ() +void MMCDrive::serviceIRQ() +{ +} + + +MMCDeviceDriver::MMCDeviceDriver() : + BasicDeviceDriver("MMC device driver") +{ +} + +MMCDeviceDriver& MMCDeviceDriver::instance() { + static MMCDeviceDriver instance_; + return instance_; } +void MMCDeviceDriver::doDeviceDetection() +{ + // Assume we have a MMC drive + // Need to name this "idea" for compatibility with userspace disk detection + // even though it has nothing to do with IDE + constexpr const char* disk_name = "idea"; + MMCDrive* drv = new MMCDrive(); + bindDevice(*drv); + + auto *bdv = new BDVirtualDevice(drv, 0, drv->getNumSectors(), drv->getSectorSize(), disk_name, true); + BDManager::instance().addVirtualDevice(bdv); + + detectMBRPartitions(bdv, drv, 0, drv->SPT, disk_name); +} diff --git a/arch/armv8/rpi3/source/debug_bochs.cpp b/arch/armv8/rpi3/source/debug_bochs.cpp index 720bc88bf..fe5ebffd2 100644 --- a/arch/armv8/rpi3/source/debug_bochs.cpp +++ b/arch/armv8/rpi3/source/debug_bochs.cpp @@ -1,8 +1,8 @@ #include "debug_bochs.h" -#include "offsets.h" + #include "KeyboardManager.h" #include "board_constants.h" - +#include "offsets.h" void writeChar2Bochs( char char2Write ) { diff --git a/arch/armv8/rpi3/source/init_boottime_pagetables.cpp b/arch/armv8/rpi3/source/init_boottime_pagetables.cpp index cbc483cc2..bfb2d3ae9 100644 --- a/arch/armv8/rpi3/source/init_boottime_pagetables.cpp +++ b/arch/armv8/rpi3/source/init_boottime_pagetables.cpp @@ -1,8 +1,12 @@ -#include "types.h" -#include "paging-definitions.h" -#include "offsets.h" #include "init_boottime_pagetables.h" +#include "offsets.h" +#include "paging-definitions.h" + +#include "ArchCommon.h" + +#include "types.h" + extern "C" void uartWritePreboot(const char *str); //see ArchMemory.cpp @@ -55,9 +59,16 @@ extern "C" void initialiseBootTimePaging() kernel_paging_level2_kernel_start[index].table.table_address = ((size_t) (kernel_paging_level3_start) / PAGE_SIZE) + index; } + extern void* kernel_start_address; + extern void* kernel_end_address; + VAddr k_start{(pointer)&kernel_start_address}; + //map the first 2MB for kernel memory - for(size_t index = 0; index < PAGE_ENTRIES; index++) - mapBootTimePage(kernel_paging_level3_start,index, index); + for(VAddr a{(pointer)&kernel_start_address}; a.addr < (pointer)&kernel_end_address; a.addr += PAGE_SIZE) + { + size_t l3i = (a.l2i - k_start.l2i)*PAGE_TABLE_ENTRIES + a.l3i; + mapBootTimePage(kernel_paging_level3_start,l3i, l3i); + } //set the mmu l0 selection registers, after boot ttbr0_el1 will be cleared and used for the user space diff --git a/arch/armv8/rpi3/utils/kernel-ld-script.ld b/arch/armv8/rpi3/utils/kernel-ld-script.ld index bb3de5144..a3fc7d580 100644 --- a/arch/armv8/rpi3/utils/kernel-ld-script.ld +++ b/arch/armv8/rpi3/utils/kernel-ld-script.ld @@ -28,7 +28,27 @@ SECTIONS *(.swebdbg) swebdbg_end_address_nr = .; } - + + .tdata ALIGN(4096) : AT(LS_Phys + (LS_tdata - LS_Code)) + { + LS_tdata = .; + cls_start = .; + tdata_start = .; + *(.tdata) + tdata_end = .; + } + + .tbss ALIGN(4096) : AT(LS_Phys + (LS_tbss - LS_Code)) + { + LS_tbss = .; + tbss_start = .; + *(.tbss) + . = ALIGN(8) + 8; /* For C++11 thread_local init-on-first-use flag */ + tbss_end = .; + cls_end = .; + . = ALIGN(8) + 8; + } + .data ALIGN(4096) : AT(LS_Phys + (LS_Data - LS_Code)) { LS_Data = .; @@ -36,7 +56,30 @@ SECTIONS *(.data*) data_end_address = .; } - + + .init_array : AT(LS_Phys + (__init_array_start - LS_Code)) + { + __init_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + __init_array_end = .; + } + + .preinit_array : AT(LS_Phys + (__preinit_array_start - LS_Code)) + { + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } + + .fini_array : AT(LS_Phys + (__fini_array_start - LS_Code)) + { + __fini_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + __fini_array_end = .; + } + .bss ALIGN(4096) : AT(LS_Phys + (LS_Bss - LS_Code)) { LS_Bss = .; @@ -58,7 +101,7 @@ SECTIONS . = ALIGN(4096); stab_end_address_nr = .; } - + .stabstr : AT(LS_Phys + (LS_Stabstr - LS_Code)) { LS_Stabstr = .; @@ -70,7 +113,7 @@ SECTIONS PROVIDE(kernel_end_address = .); } - + .ARM.attributes 0 : { *(.ARM.attributes) } .comment 0 : { *(.comment) } } diff --git a/arch/common/include/ArchCommon.h b/arch/common/include/ArchCommon.h index e52fedc5e..de1afaeee 100644 --- a/arch/common/include/ArchCommon.h +++ b/arch/common/include/ArchCommon.h @@ -1,13 +1,19 @@ #pragma once -#include "types.h" +#include "RangeAllocator.h" #include "paging-definitions.h" +#include "types.h" + class Console; +class Allocator; + +extern RangeAllocator mmio_addr_allocator; class ArchCommon { public: + static pointer getKernelStartAddress(); static pointer getKernelEndAddress(); static pointer getFreeKernelMemoryStart(); static pointer getFreeKernelMemoryEnd(); @@ -30,6 +36,10 @@ class ArchCommon * @return a Pointer to the location of the FrameBuffer */ static pointer getFBPtr(size_t is_paging_set_up = 1); + static size_t getFBWidth(); + static size_t getFBHeight(); + static size_t getFBBitsPerCharacter(); + static size_t getFBSize(); /** * @return number of Useable Memory Regions @@ -46,7 +56,7 @@ class ArchCommon * @param &type of Useable Memory Region * @return 1 if region >= number_of_regions, 0 otherwise */ - static size_t getUsableMemoryRegion(size_t region, pointer &start_address, pointer &end_address, size_t &type); + static size_t getUseableMemoryRegion(size_t region, pointer &start_address, pointer &end_address, size_t &type); /** * @return size_t returns the number of modules loaded by grub @@ -69,6 +79,8 @@ class ArchCommon */ static size_t getModuleEndAddress(size_t num, size_t is_paging_set_up = 1); + static const char* getModuleName(size_t num, size_t is_paging_set_up = 1); + /** * Generates the according console depending on the architecture * @param count the number of consoles to create @@ -83,9 +95,15 @@ class ArchCommon /** * let the CPU idle, f.e. with the halt statement + * calls pre-idle handler functions */ static void idle(); + /** + * let CPU idle until next interrupt + */ + static void halt(); + /** * draw a heartbeat character */ @@ -95,5 +113,20 @@ class ArchCommon * draw some infos/statistics */ static void drawStat(); -}; + static void postBootInit(); + + static void initPlatformDrivers(); + + static void initBlockDeviceDrivers(); + + [[noreturn]] static void callWithStack(char* stack, void (*func)()); + + static uint64 cpuTimestamp(); + + static void spinlockPause(); + + static void reservePagesPreKernelInit(Allocator& pm); + + static void initKernelVirtualAddressAllocator(); +}; diff --git a/arch/common/include/ArchInterrupts.h b/arch/common/include/ArchInterrupts.h index 7228d3187..81eafb997 100644 --- a/arch/common/include/ArchInterrupts.h +++ b/arch/common/include/ArchInterrupts.h @@ -3,8 +3,15 @@ #define IO_TIMEOUT (600000) #define IRQ0_TIMER_FREQUENCY 0 +#include "IrqDomain.h" + +#include "ArchCpuLocalStorage.h" + #include "types.h" +extern cpu_local IrqDomain* cpu_root_irq_domain_; +extern cpu_local IrqDomain cpu_irq_vector_domain_; + class ArchInterrupts { public: @@ -14,9 +21,25 @@ class ArchInterrupts static void setTimerFrequency(uint32 freq); static void disableTimer(); + static void enableIRQ(uint16 num, bool enable = true); + static void disableIRQ(uint16 num); + + static void enableIRQ(const IrqDomain::DomainIrqHandle& domain_irq, bool enable = true); + static void disableIRQ(const IrqDomain::DomainIrqHandle& domain_irq); + + static IrqDomain& currentCpuRootIrqDomain(); + /** + IRQ domain for ISA interrupts (PIT, mouse, keyboard, disk, ...) + */ + static IrqDomain& isaIrqDomain(); + static void enableKBD(); static void disableKBD(); + + static void startOfInterrupt(uint16 number); + static void startOfInterrupt(const IrqDomain::DomainIrqHandle& irq_handle); + /** * Signals EOI to the Interrupt-Controller, so the Controller * can resume sending us Interrupts @@ -25,10 +48,25 @@ class ArchInterrupts * to signal * */ - static void EndOfInterrupt(uint16 number); + static void endOfInterrupt(uint16 number); + static void endOfInterrupt(const IrqDomain::DomainIrqHandle& irq_handle); + + static void handleInterrupt(uint16_t irq); + static void handleInterrupt(const IrqDomain::DomainIrqHandle& irq_handle); static void enableInterrupts(); static bool disableInterrupts(); + static void setInterrupts(bool state) + { + if (state) + { + enableInterrupts(); + } + else + { + disableInterrupts(); + } + } /** * on x86: tests if the IF Flag in EFLAGS is set, aka if the Interrupts are enabled @@ -38,8 +76,34 @@ class ArchInterrupts static bool testIFSet(); /** - * yields if the IF Flag is set, else does hlt + * yields if the IF Flag is set, else does nothing */ static void yieldIfIFSet(); }; +struct ArchThreadRegisters; +class Thread; +extern "C" [[noreturn]] void contextSwitch(Thread* target_thread = nullptr, ArchThreadRegisters* target_registers = nullptr); + +class WithInterrupts +{ +public: + WithInterrupts(bool new_state) + { + previous_state_ = ArchInterrupts::testIFSet(); + ArchInterrupts::setInterrupts(new_state); + } + + ~WithInterrupts() + { + ArchInterrupts::setInterrupts(previous_state_); + } + + bool previousInterruptState() + { + return previous_state_; + } + +private: + bool previous_state_; +}; diff --git a/arch/common/include/ArchSerialInfo.h b/arch/common/include/ArchSerialInfo.h index 36fda09cd..ba67cd866 100644 --- a/arch/common/include/ArchSerialInfo.h +++ b/arch/common/include/ArchSerialInfo.h @@ -11,22 +11,229 @@ class ArchSerialInfo uint8 irq_num; /// IRQ number }; +enum class SerialPortRegister : uint32_t +{ + THR = 0, /// UART Transmitter Holding Buffer + RBR = 0, /// UART Receiver Buffer + DLL = 0, /// UART Divisor Latch Low Byte + IER = 1, /// UART Interrupt Enable Register + DLH = 1, /// UART Divisor Latch High Byte + IIR = 2, /// UART Interrupt Identification Register + FCR = 2, /// UART FIFO Control Register + LCR = 3, /// UART Line Control Register + MCR = 4, /// UART Modem Control Register + LSR = 5, /// UART Line Status Register + MSR = 6, /// UART Modem Status Register + SR = 7, /// UART Scratch Register +}; + +struct SerialPort_InterruptEnableRegister +{ + union + { + struct + { + uint8_t received_data_available_int_enable : 1; + uint8_t transmitter_holding_reg_empty_int_enable : 1; + uint8_t receiver_line_status_int_enable : 1; + uint8_t modem_status_int_enable : 1; + uint8_t sleep_mode : 1; + uint8_t low_power_mode : 1; + uint8_t reserved : 2; + }; + uint8 u8; + }; +}; + +static_assert(sizeof(SerialPort_InterruptEnableRegister) == 1); + +enum class IIR_int : uint8 +{ + MODEM_STATUS = 0b000, + TRANSMITTER_HOLDING_REG_EMPTY = 0b001, + RECEIVED_DATA_AVAILABLE = 0b010, + RECEIVER_LINE_STATUS = 0b011, + TIMEOUT = 0b110, +}; + +enum class IIR_fifo_info : uint8 +{ + NO_FIFO = 0b00, + FIFO_NON_FUNCTIONAL = 0b10, + FIFO_ENABLED = 0b11, +}; + +struct SerialPort_InterruptIdentificationRegister +{ + union + { + struct + { + uint8_t int_pending : 1; // 0 = int pending, 1 = no int pending + IIR_int int_status : 3; + uint8 reserved : 1; + uint8 fifo_64b_enabled : 1; + IIR_fifo_info fifo_info : 2; + }; + + uint8 u8; + }; +}; + +static_assert(sizeof(SerialPort_InterruptIdentificationRegister) == 1); + +enum class LCR_parity : uint8 +{ + NO_PARITY = 0b000, + ODD_PARITY = 0b001, + EVEN_PARITY = 0b011, + MARK = 0b101, + SPACE = 0b111, +}; + +enum class LCR_stop_bits : uint8 +{ + ONE_STOP_BIT = 0, + ONEANDHALF_STOP_BITS = 1, + TWO_STOP_BITS = 1, +}; + +enum class LCR_word_length : uint8 +{ + BITS_5 = 0b00, + BITS_6 = 0b01, + BITS_7 = 0b10, + BITS_8 = 0b11, +}; + +struct SerialPort_LineControlRegister +{ + union + { + struct + { + LCR_word_length word_length : 2; + LCR_stop_bits stop_bits : 1; + LCR_parity parity : 3; + uint8 break_enable : 1; + uint8 divisor_latch : 1; + }; + + uint8 u8; + }; +}; + +static_assert(sizeof(SerialPort_LineControlRegister) == 1); + +struct SerialPort_LineStatusRegister +{ + union + { + struct + { + uint8 data_ready : 1; + uint8 overrun_error : 1; + uint8 parity_error : 1; + uint8 framing_error : 1; + uint8 break_interrupt : 1; + uint8 empty_transmitter_holding_reg : 1; + uint8 empty_data_holding_reg : 1; + uint8 fifo_receive_error : 1; + }; + + uint8 u8; + }; +}; + +static_assert(sizeof(SerialPort_LineStatusRegister) == 1); + +enum class FIFO_TRIGGER_LEVEL : uint8 +{ + TRIGGER_1_BYTE = 0b00, + TRIGGER_4_OR_16_BYTES = 0b01, + TRIGGER_8_OR_32_BYTES = 0b10, + TRIGGER_16_OR_56_BYTES = 0b11, +}; + +struct SerialPort_FifoControlRegister +{ + union + { + struct + { + uint8 enable_fifos : 1; + uint8 clear_receive_fifo : 1; + uint8 clear_transmit_fifo : 1; + uint8 dma_mode_select : 1; + uint8 reserved : 1; + uint8 enable_64_byte_fifo : 1; + FIFO_TRIGGER_LEVEL trigger_level : 2; + }; + + uint8 u8; + }; +}; + +static_assert(sizeof(SerialPort_FifoControlRegister) == 1); + +struct SerialPort_ModemControlRegister +{ + union + { + struct + { + uint8 data_terminal_ready : 1; + uint8 request_to_send : 1; + uint8 aux1 : 1; + uint8 aux2 : 1; + uint8 loopback : 1; + uint8 auto_flow_control : 1; + uint8 reserved : 2; + }; + + uint8 u8; + }; +}; + +static_assert(sizeof(SerialPort_ModemControlRegister) == 1); + +struct SerialPort_ModemStatusRegister +{ + union + { + struct + { + uint8 delta_clear_to_send : 1; + uint8 delta_data_set_ready : 1; + uint8 trailing_edge_ring_indicator : 1; + uint8 delta_data_carrier_detect : 1; + uint8 clear_to_send : 1; + uint8 data_set_ready : 1; + uint8 ring_indicator : 1; + uint8 carrier_detect : 1; + }; + + uint8 u8; + }; +}; + +static_assert(sizeof(SerialPort_ModemStatusRegister) == 1); + +enum class UART_TYPE +{ + /// Old type UART without FIFO buffers + UART_8250, + /// Old type UART with FIFO buffers that do not work + UART_16650, + /// Most common UART with FIFO buffers + UART_16650A, + UART_16750, +}; + class SC { public: - static uint32 IER; /// UART Interrupt Enable Register - static uint32 IIR; /// UART Interrupt Indetification Register - static uint32 FCR; /// UART FIFO Control Register - static uint32 LCR; /// UART Line Control Register - static uint32 MCR; /// UART Modem Control Register - static uint32 LSR; /// UART Line Status Register - static uint32 MSR; /// UART Modem Status Register - - static uint32 UART_16650A; /// Most common UART with FIFO buffers - static uint32 UART_16650; /// Old type UART with FIFO buffers that do not work - static uint32 UART_OLD; /// Old type UART without FIFO buffers - - static uint32 MAX_ARCH_PORTS; /// Maximum serial ports registered in BIOS + /// Maximum serial ports registered in BIOS + static constexpr uint32 MAX_ARCH_PORTS = 4; }; - diff --git a/arch/common/include/BDDriver.h b/arch/common/include/BDDriver.h index aae854e32..1d928b0bb 100644 --- a/arch/common/include/BDDriver.h +++ b/arch/common/include/BDDriver.h @@ -1,29 +1,32 @@ #pragma once -#include "types.h" +#include + class BDRequest; class BDDriver { public: + BDDriver() = default; - virtual ~BDDriver() + BDDriver(uint16_t irq) : + irq(irq) { } - virtual uint32 addRequest(BDRequest *) = 0; + virtual ~BDDriver() = default; + + virtual uint32_t addRequest(BDRequest *) = 0; - virtual int32 readSector(uint32, uint32, void *) = 0; + virtual int32_t readSector(uint32_t, uint32_t, void *) = 0; - virtual int32 writeSector(uint32, uint32, void *) = 0; + virtual int32_t writeSector(uint32_t, uint32_t, void *) = 0; - virtual uint32 getNumSectors() = 0; + virtual uint32_t getNumSectors() = 0; - virtual uint32 getSectorSize() = 0; + virtual uint32_t getSectorSize() = 0; virtual void serviceIRQ() = 0; - uint16 irq; + uint16_t irq; }; - - diff --git a/arch/common/include/BDRequest.h b/arch/common/include/BDRequest.h index a1f072773..66d7efe3b 100644 --- a/arch/common/include/BDRequest.h +++ b/arch/common/include/BDRequest.h @@ -1,20 +1,24 @@ #pragma once +#include "NonBlockingQueue.h" +#include "Scheduler.h" + #include "types.h" -class Thread; +#include "EASTL/atomic.h" -extern Thread * currentThread; +class Thread; class BDRequest { protected: friend class BDVirtualDevice; - friend class ATADriver; - friend class MMCDriver; + friend class RamDiskDriver; + friend class ATADrive; + friend class MMCDrive; friend class BDManager; - typedef enum BD_CMD_ + enum class BD_CMD { BD_READ = 0x00, BD_WRITE = 0x10, @@ -24,7 +28,7 @@ class BDRequest BD_REINIT = 0x31, BD_DEINIT = 0x32, BD_SEND_RAW_CMD = 0x40 - } BD_CMD; + }; /** * Enumeration containing the possible status values. I admit this is very bad @@ -32,12 +36,12 @@ class BDRequest * with these values. * */ - typedef enum BD_RESULT_ + enum class BD_RESULT { BD_QUEUED = 0x00, BD_DONE = 0x20, BD_ERROR = 0x40, - } BD_RESULT; + }; /** * @@ -51,38 +55,43 @@ class BDRequest * checks performed, possible pagefault here * */ - BDRequest( uint32 dev_id, BD_CMD cmd, uint32 start_block = 0, uint32 num_block = 0, void * buffer = 0 ) + BDRequest(uint32 dev_id, + BD_CMD cmd, + uint32 start_block = 0, + uint32 num_block = 0, + void* buffer = nullptr) : + request_id(++request_counter), + dev_id_(dev_id), + cmd_(cmd), + num_block_(num_block), + start_block_(start_block), + buffer_(buffer), + requesting_thread_(currentThread) { - num_block_=num_block; - start_block_=start_block; - dev_id_=dev_id; - cmd_=cmd; - result_= 0; - status_ = BD_QUEUED; - buffer_ = buffer; + } - requesting_thread_ = currentThread; - blocks_done_ = 0; - next_request_ = 0; - }; + uint32 getDevID() const { return dev_id_; } + BD_CMD getCmd() const { return cmd_; } + uint32 getStartBlock() const { return start_block_; } + uint32 getNumBlocks() const { return num_block_; } + uint32 getResult() const { return result_; } + BD_RESULT getStatus() const { return status_; } + uint32 getBlocksDone() const { return blocks_done_; } + void *getBuffer() const { return buffer_; } + Thread *getThread() const { return requesting_thread_; } + + void setStartBlock( uint32 start_blk ) { start_block_ = start_blk; } + void setResult( uint32 result ) { result_ = result; } + void setStatus( BD_RESULT status ) { status_ = status; } + void setBlocksDone( uint32 bdone ) { blocks_done_ = bdone; } + void setNumBlocks(uint32 num_block) { num_block_ = num_block; } - uint32 getDevID(){ return dev_id_; }; - BD_CMD getCmd(){ return cmd_; }; - uint32 getStartBlock(){ return start_block_; }; - uint32 getNumBlocks(){ return num_block_; }; - uint32 getResult(){ return result_; }; - BD_RESULT getStatus(){ return status_; }; - uint32 getBlocksDone(){ return blocks_done_; }; - void *getBuffer(){ return buffer_; }; - Thread *getThread(){ return requesting_thread_; }; - BDRequest *getNextRequest(){ return next_request_; }; - void setStartBlock( uint32 start_blk ){ start_block_=start_blk; }; - void setResult( uint32 result ){ result_=result; }; - void setStatus( BD_RESULT status ){ status_=status; }; - void setBlocksDone( uint32 bdone ){ blocks_done_=bdone; }; - void setNextRequest( BDRequest *next ){ next_request_=next; }; - void setNumBlocks(uint32 num_block){ num_block_ = num_block; }; + friend class NonBlockingQueue; + eastl::atomic next_node_ = nullptr; + + inline static eastl::atomic request_counter = 0; + size_t request_id; private: BDRequest(); @@ -91,11 +100,9 @@ class BDRequest BD_CMD cmd_; uint32 num_block_; uint32 start_block_; - uint32 result_; - BD_RESULT status_; - uint32 blocks_done_; - void *buffer_; + uint32 result_ = 0; + BD_RESULT status_ = BD_RESULT::BD_QUEUED; + uint32 blocks_done_ = 0; + void* buffer_; Thread *requesting_thread_; - BDRequest *next_request_; }; - diff --git a/arch/common/include/Elf64Format.h b/arch/common/include/Elf64Format.h index a46db1764..831811bf1 100644 --- a/arch/common/include/Elf64Format.h +++ b/arch/common/include/Elf64Format.h @@ -161,9 +161,9 @@ class Elf foobar("%x", e_type); foobar("%x", e_machine); foobar("%x", e_version); - foobar("%zx", e_entry); - foobar("%zx", e_phoff); - foobar("%zx", e_shoff); + foobar("%lx", e_entry); + foobar("%lx", e_phoff); + foobar("%lx", e_shoff); foobar("%x", e_flags); foobar("%x", e_ehsize); foobar("%x", e_phentsize); diff --git a/arch/common/include/IDEDriver.h b/arch/common/include/IDEDriver.h deleted file mode 100644 index 6ae98f2d4..000000000 --- a/arch/common/include/IDEDriver.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "types.h" - -class BDDriver; - -class IDEDriver -{ - public: - IDEDriver() - { - doDeviceDetection(); - } - ~IDEDriver() - { - } - - typedef struct fdisk_partition - { - uint8 bootid; // bootable? 0=no, 128=yes - uint8 beghead; - uint8 begcyl; - uint8 begsect; - uint8 systid; // Operating System type indicator code - uint8 endhead; - uint8 endcyl; - uint8 endsect; - uint32 relsect; // first sector relative to start of disk - We actually need only theese two params - uint32 numsect; // number of sectors in partition - } FP; - - typedef struct master_boot_record - { - uint8 bootinst[446]; // GRUB space - uint8 parts[4 * sizeof(FP)]; - uint16 signature; // set to 0xAA55 for PC MBR - } MBR; - - int32 processMBR(BDDriver *, uint32, uint32, const char*); - - uint32 doDeviceDetection(); - -}; - diff --git a/arch/common/include/KeyboardManager.h b/arch/common/include/KeyboardManager.h index 119e5db05..7565c81e7 100644 --- a/arch/common/include/KeyboardManager.h +++ b/arch/common/include/KeyboardManager.h @@ -10,6 +10,7 @@ extern "C" } #endif +#include "IrqDomain.h" #include "RingBuffer.h" #include "atkbd.h" @@ -113,16 +114,16 @@ extern "C" #define KEY_RWIN (KEY_INS + 13) #define KEY_MENU (KEY_INS + 14) -class KeyboardManager +class KeyboardManager : public IrqDomain { public: KeyboardManager(); - ~KeyboardManager(); - static KeyboardManager *instance() + ~KeyboardManager() = default; + + static KeyboardManager& instance() { - if (!instance_) - instance_ = new KeyboardManager(); - return instance_; + static KeyboardManager kb_manager; + return kb_manager; } bool getKeyFromKbd(uint32 &key) @@ -193,9 +194,9 @@ class KeyboardManager RingBuffer keyboard_buffer_; - static uint32 const STANDARD_KEYMAP[]; - static uint32 const E0_KEYS[]; - static uint8 const SET1_SCANCODES[]; + static const uint32 STANDARD_KEYMAP[]; + static const uint32 E0_KEYS[]; + static const uint8 SET1_SCANCODES[]; /** * converts the scancode into a key by looking in the Standard KeyMap * @@ -215,9 +216,4 @@ class KeyboardManager uint32 usb_kbd_addr_; uint32 current_key_; uint32 next_is_up_; - - protected: - - static KeyboardManager *instance_; }; - diff --git a/arch/common/include/assert.h b/arch/common/include/assert.h deleted file mode 100644 index 48c2238c9..000000000 --- a/arch/common/include/assert.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "types.h" - - -#ifdef __cplusplus -extern "C" -{ -#endif - - -#define assert(cond) do { (cond) ? (void)0 : sweb_assert( #cond ,__LINE__,__FILE__); } while (0) - -/** - * called when assertion is used and true - * @param condition assertion that has to be checked - * @param line the function which to jump to - * @param file where the assertion is called - */ -__attribute__((noreturn)) void sweb_assert(const char *condition, uint32 line, const char* file); - - -#ifdef __cplusplus -} -#endif - diff --git a/arch/common/include/debug_bochs.h b/arch/common/include/debug_bochs.h index d872895a8..47ac30e8d 100644 --- a/arch/common/include/debug_bochs.h +++ b/arch/common/include/debug_bochs.h @@ -1,16 +1,16 @@ #pragma once + #include "types.h" /** * writes a char to the bochs terminal * */ -void writeChar2Bochs( char char2Write ); +void writeChar2Bochs(char char2Write); /** * writes a string/line to the bochs terminal * max length is 250 * */ -void writeLine2Bochs( const char *line2Write ); - +void writeLine2Bochs(const char *line2Write); diff --git a/arch/x86/32/CMakeLists.include b/arch/x86/32/CMakeLists.include index 7612546d8..ef9f9f44d 100644 --- a/arch/x86/32/CMakeLists.include +++ b/arch/x86/32/CMakeLists.include @@ -1,35 +1,64 @@ +cmake_minimum_required(VERSION 3.13) + set(KERNEL_BINARY kernel.x) -set(ARCH_X86_32_KERNEL_CFLAGS -m32 -O0 -gstabs2 -Wall -Wextra -Werror -Wno-error=format -Wno-nonnull-compare -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -ffreestanding -mno-red-zone -mno-mmx -mno-sse2 -mno-sse3 -mno-3dnow ${NOPICFLAG}) +set(ARCH_X86_32_KERNEL_CFLAGS -O0 -gdwarf-4 -Wall -Wextra -Werror -Wno-error=format -Wno-nonnull-compare -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -ffreestanding -mno-red-zone -mno-mmx -mno-sse2 -mno-sse3 -mno-3dnow ${NOPICFLAG}) -set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++17 -nostdinc++ -fno-rtti ${ARCH_X86_32_KERNEL_CFLAGS}) +set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++20 -nostdinc++ -fno-rtti ${ARCH_X86_32_KERNEL_CFLAGS}) set(KERNEL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -std=gnu11 ${ARCH_X86_32_KERNEL_CFLAGS}) -set(ARCH_LD_ARGUMENTS -m32 -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -Wl,-melf_i386 -nostdinc -nostdlib -nodefaultlibs) -set(KERNEL_LD_ARGUMENT ${ARCH_LD_ARGUMENTS} -mcmodel=kernel ${NOPIEFLAG}) -set(ARCH_APPEND_LD_ARGUMENTS ) + +target_compile_options(arch_options INTERFACE + -m32) + +target_link_options(arch_options INTERFACE + -m32 -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -Wl,-melf_i386 -nostdinc -nostdlib -nodefaultlibs) + + +target_link_options(kernel_options INTERFACE + -mcmodel=kernel ${NOPIEFLAG}) + +target_compile_options(kernel_options INTERFACE + $<$:${KERNEL_CMAKE_CXX_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + ) + MACRO(ARCH2OBJ ARCHOBJ_LIBNAME LIBRARY_NAME) ENDMACRO(ARCH2OBJ) - -set(KERNEL_IMAGE_OBJCOPY COMMAND ${OBJCOPY_EXECUTABLE} ${PROJECT_BINARY_DIR}/kernel.x --strip-unneeded ${PROJECT_BINARY_DIR}/kernel.x) if ("${DEBUG}" STREQUAL "1") - set(KERNEL_IMAGE_OBJCOPY ) + set(STRIP_DEBUG_INFO "") +else() + set(STRIP_DEBUG_INFO ${OBJCOPY_EXECUTABLE} $ --strip-unneeded $) endif() +add_custom_command(TARGET kernel + POST_BUILD + BYPRODUCTS kernel.dbg kernel.unstripped + COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_BINARY_DIR}/kernel.unstripped + COMMAND ${PROJECT_BINARY_DIR}/add-dbg $ ${PROJECT_BINARY_DIR}/kernel.dbg + COMMAND ${STRIP_DEBUG_INFO}) + +add_dependencies(kernel add-dbg) + + set(AVAILABLE_MEMORY 8M) +set(NUM_CPUS 4) + +set(DISK_MOUNT_ARG -drive file=${HDD_IMAGE},index=0,media=disk,id=sweb-hdd,if=ide) set(QEMU_BIN qemu-system-i386) -set(QEMU_FLAGS_COMMON -m ${AVAILABLE_MEMORY} -drive file=${HDD_IMAGE},index=0,media=disk -debugcon stdio -no-reboot) +set(QEMU_FLAGS_COMMON -m ${AVAILABLE_MEMORY} ${DISK_MOUNT_ARG} -smp ${NUM_CPUS} -debugcon stdio -no-reboot -no-shutdown -s -d guest_errors) string(REPLACE ";" " " QEMU_FLAGS_COMMON_STR "${QEMU_FLAGS_COMMON}") add_custom_target(kvm - COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -cpu kvm32 - COMMENT "Executing `${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -cpu kvm32`" - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - COMMAND reset -I - ) + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -enable-kvm -cpu kvm32 + COMMENT "Executing `${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -enable-kvm -cpu kvm32`" + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMAND reset -I + ) # qemu: Run qemu in non debugging mode add_custom_target(qemu @@ -41,31 +70,31 @@ add_custom_target(qemu # qemugdb: Run qemu in debugging mode add_custom_target(qemugdb - COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -no-kvm -s -S - COMMENT "Executing `gdb ${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -no-kvm -s -S on localhost:1234`" + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -S + COMMENT "Executing `gdb ${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -S on localhost:1234`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I ) # qemutacos: Run qemu in pipe monitor mode for tacos add_custom_target(qemutacos - COMMAND ${QEMU_BIN} -hda ${HDD_IMAGE} -m ${AVAILABLE_MEMORY} -snapshot -monitor pipe:qemu -nographic -debugcon /dev/stdout - COMMENT "Executing `qemu-system-i386 -hda ${HDD_IMAGE} -m 8M -snapshot -monitor pipe:qemu -nographic -debugcon /dev/stdout`" - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - ) + COMMAND ${QEMU_BIN} -hda ${HDD_IMAGE} -m ${AVAILABLE_MEMORY} -snapshot -monitor pipe:qemu -nographic -debugcon /dev/stdout + COMMENT "Executing `qemu-system-i386 -hda ${HDD_IMAGE} -m ${AVAILABLE_MEMORY} -snapshot -monitor pipe:qemu -nographic -debugcon /dev/stdout`" + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + ) # net: Run qemu with network support, make sure to run after target "net-init" add_custom_target(net - COMMAND sudo ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -netdev bridge,id=hn0 -device rtl8139,netdev=hn0,id=nic1 -cpu qemu32 - COMMENT "Executing `sudo ${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -netdev bridge,id=hn0 -device rtl8139,netdev=hn0,id=nic1 -cpu qemu32`" - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - COMMAND reset -I - ) + COMMAND sudo ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -netdev bridge,id=hn0 -device rtl8139,netdev=hn0,id=nic1 -cpu qemu32 + COMMENT "Executing `sudo ${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -netdev bridge,id=hn0 -device rtl8139,netdev=hn0,id=nic1 -cpu qemu32`" + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMAND reset -I + ) # net-init: Initializes br0 for target "net" add_custom_target(net-init - COMMAND sudo ${PROJECT_SOURCE_DIR}/utils/netinit.sh - COMMENT "Executing `sudo utils/netinit.sh`" - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - COMMAND reset -I - ) + COMMAND sudo ${PROJECT_SOURCE_DIR}/utils/netinit.sh + COMMENT "Executing `sudo utils/netinit.sh`" + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMAND reset -I + ) diff --git a/arch/x86/32/CMakeLists.txt b/arch/x86/32/CMakeLists.txt index aa1b4af5c..75547d11d 100644 --- a/arch/x86/32/CMakeLists.txt +++ b/arch/x86/32/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC x86/common/include ../common/include include @@ -7,4 +7,3 @@ include_directories( add_subdirectory(source) add_subdirectory(common) - diff --git a/arch/x86/32/CMakeLists.userspace b/arch/x86/32/CMakeLists.userspace index 008c77c40..c19de8bce 100644 --- a/arch/x86/32/CMakeLists.userspace +++ b/arch/x86/32/CMakeLists.userspace @@ -1,2 +1,10 @@ -set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -std=gnu11 -gstabs2 -fno-omit-frame-pointer -O0 -m32 -static -nostdinc -fno-builtin -nostdlib -fno-stack-protector -fno-common -Werror=implicit-function-declaration -Wno-error=unused-variable -fno-stack-clash-protection) -set(ARCH_USERSPACE_LINKER_OPTIONS -static) +set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -gdwarf-4 -fno-omit-frame-pointer -O0 -static -nostdinc -fno-builtin -nostdlib -nolibc -fno-stack-protector -fno-common -Wno-error=unused-variable -fno-stack-clash-protection) + +target_link_options(userspace_options INTERFACE + -static) + +target_compile_options(userspace_options INTERFACE + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu++20 -nostdinc++> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu11 -Werror=implicit-function-declaration> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS}> +) diff --git a/arch/x86/32/common/CMakeLists.txt b/arch/x86/32/common/CMakeLists.txt index 51c98a383..af59fefaf 100644 --- a/arch/x86/32/common/CMakeLists.txt +++ b/arch/x86/32/common/CMakeLists.txt @@ -1,7 +1,7 @@ -include_directories( +target_include_directories(kernel PUBLIC ../../../${ARCH}/include include - ../include + ../include ) add_subdirectory(source) diff --git a/arch/x86/32/common/include/ArchCpuLocalStorage.h b/arch/x86/32/common/include/ArchCpuLocalStorage.h new file mode 100644 index 000000000..62319553e --- /dev/null +++ b/arch/x86/32/common/include/ArchCpuLocalStorage.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#define cpu_local thread_local +#define __cpu __thread + +struct GDT; + +namespace CpuLocalStorage +{ + size_t getClsSize(); + + char* allocCls(); + void setCls(GDT& gdt, char* cls); + bool ClsInitialized(); + void* getClsBase(); +}; diff --git a/arch/x86/32/common/include/ArchMulticore.h b/arch/x86/32/common/include/ArchMulticore.h new file mode 100644 index 000000000..b5b7e9a6e --- /dev/null +++ b/arch/x86/32/common/include/ArchMulticore.h @@ -0,0 +1,83 @@ +#pragma once + +#include "APIC.h" +#include "Cpu.h" +#include "IdleThread.h" +#include "Mutex.h" +#include "SegmentUtils.h" +#include "paging-definitions.h" + +#include "ArchCpuLocalStorage.h" + +#include "types.h" + +#include "EASTL/atomic.h" +#include "EASTL/vector.h" + +#define CPU_STACK_SIZE 4*PAGE_SIZE + +#define STOP_INT_VECTOR 90 +#define MESSAGE_INT_VECTOR 101 + +class Allocator; + + +struct TLBShootdownRequest +{ + size_t addr; + eastl::atomic ack; + size_t target; + eastl::atomic next; + size_t request_id; + size_t orig_cpu; +}; + +class ArchCpu : public Cpu +{ +public: + ArchCpu(); + + Apic* lapic; + + IrqDomain& rootIrqDomain(); + +private: + IrqDomain** root_domain_ptr; +}; + +extern cpu_local Apic* cpu_lapic; +extern cpu_local char cpu_stack[CPU_STACK_SIZE]; +extern cpu_local TSS cpu_tss; +extern cpu_local IdleThread* idle_thread; + +constexpr size_t AP_STARTUP_PADDR = 0x00; + +class ArchMulticore +{ + public: + static void initialize(); + + static void reservePages(Allocator& alloc); + + static void startOtherCPUs(); + static void stopAllCpus(); + static void stopOtherCpus(); + + static void startAP(uint8_t apic_id, size_t entry_addr); + + static void sendFunctionCallMessage(ArchCpu& cpu, RemoteFunctionCallMessage* fcall_message); + static void notifyMessageAvailable(ArchCpu& cpu); + + static void initApplicationProcessorCpu(); + static void initCpuLocalData(bool boot_cpu = false); + + + static char* cpuStackTop(); + + private: + static void initCpuLocalGDT(GDT& template_gdt); + static void initCpuLocalTSS(size_t boot_stack_top); + static void prepareAPStartup(size_t entry_addr); + + [[noreturn]] static void waitForSystemStart(); +}; diff --git a/arch/x86/32/common/include/ArchThreads.h b/arch/x86/32/common/include/ArchThreads.h index 10c3075ef..f516e5b36 100644 --- a/arch/x86/32/common/include/ArchThreads.h +++ b/arch/x86/32/common/include/ArchThreads.h @@ -2,6 +2,8 @@ #include "types.h" +#include "EASTL/unique_ptr.h" + /** * The flag for full barrier synchronization. */ @@ -11,35 +13,29 @@ struct ArchThreadRegisters { - uint32 eip; // 0 - uint32 cs; // 4 - uint32 eflags; // 8 - uint32 eax; // 12 - uint32 ecx; // 16 - uint32 edx; // 20 - uint32 ebx; // 24 - uint32 esp; // 28 - uint32 ebp; // 32 - uint32 esi; // 36 - uint32 edi; // 40 - uint32 ds; // 44 - uint32 es; // 48 - uint32 fs; // 52 - uint32 gs; // 56 - uint32 ss; // 60 - uint32 esp0; // 64 - uint32 cr3; // 68 - uint32 fpu[27]; // 72 + uint32 eip; + uint32 cs; + uint32 eflags; + uint32 eax; + uint32 ecx; + uint32 edx; + uint32 ebx; + uint32 esp; + uint32 ebp; + uint32 esi; + uint32 edi; + uint32 ds; + uint32 es; + uint32 fs; + uint32 gs; + uint32 ss; + uint32 esp0; + uint32 cr3; + uint32 fpu[27]; }; class Thread; class ArchMemory; -/** - * this is where the thread info for task switching is stored - * - */ -extern ArchThreadRegisters *currentThreadRegisters; -extern Thread *currentThread; /** * Collection of architecture dependant code concerning Task Switching @@ -49,6 +45,9 @@ class ArchThreads { public: + [[noreturn]] static void startThreads(Thread* init_thread); + + /** * allocates space for the currentThreadRegisters */ @@ -60,33 +59,40 @@ class ArchThreads * @param start_function instruction pointer is set so start function * @param stack stackpointer */ - static void createKernelRegisters(ArchThreadRegisters *&info, void* start_function, void* kernel_stack); + static eastl::unique_ptr createKernelRegisters(void* start_function, + void* kernel_stack); -/** - * creates the ArchThreadRegisters for a user thread - * @param info where the ArchThreadRegisters is saved - * @param start_function instruction pointer is set so start function - * @param user_stack pointer to the userstack - * @param kernel_stack pointer to the kernel stack - */ - static void createUserRegisters(ArchThreadRegisters *&info, void* start_function, void* user_stack, void* kernel_stack); + /** + * creates the ArchThreadRegisters for a user thread + * @param info where the ArchThreadRegisters is saved + * @param start_function instruction pointer is set so start function + * @param user_stack pointer to the userstack + * @param kernel_stack pointer to the kernel stack + */ + static eastl::unique_ptr createUserRegisters(void* start_function, + void* user_stack, + void* kernel_stack); -/** - * changes an existing ArchThreadRegisters so that execution will start / continue - * at the function specified - * it does not change anything else, and if the thread info / thread was currently - * executing something else this will lead to a lot of problems - * USE WITH CARE, or better, don't use at all if you're a student - * @param the ArchThreadRegisters that we are going to mangle - * @param start_function instruction pointer for the next instruction that gets executed - */ - static void changeInstructionPointer(ArchThreadRegisters *info, void* function); + /** + * changes an existing ArchThreadRegisters so that execution will start / continue + * at the function specified + * it does not change anything else, and if the thread info / thread was currently + * executing something else this will lead to a lot of problems + * USE WITH CARE, or better, don't use at all if you're a student + * @param the ArchThreadRegisters that we are going to mangle + * @param start_function instruction pointer for the next instruction that gets executed + */ + static void changeInstructionPointer(ArchThreadRegisters& info, void* function); -/** - * - * on x86: invokes int65, whose handler facilitates a task switch - * - */ + static void* getInstructionPointer(ArchThreadRegisters& info); + + static void setInterruptEnableFlag(ArchThreadRegisters& info, bool interrupts_enabled); + + /** + * + * on x86: invokes int65, whose handler facilitates a task switch + * + */ static void yield(); /** @@ -97,6 +103,9 @@ class ArchThreads */ static void setAddressSpace(Thread *thread, ArchMemory& arch_memory); + static void switchToAddressSpace(Thread* thread); + static void switchToAddressSpace(ArchMemory& arch_memory); + /** * uninterruptable locked operation * exchanges value in variable lock with new_value and returns the old_value @@ -105,7 +114,22 @@ class ArchThreads * @param new_value to set variable lock to * @returns old_value of variable lock */ - static uint32 testSetLock(uint32 &lock, uint32 new_value); + template + static T testSetLock(volatile T &lock, T new_value) + { + return __sync_lock_test_and_set(&lock, new_value); + } + +/** + * Counterpart to testSetLock() + * Writes 0 to the lock variable and provides a memory release barrier + * (ensures all previous memory stores are visible) + */ + template + static void syncLockRelease(volatile T &lock) + { + __sync_lock_release(&lock); + } /** * atomically increments or decrements value by increment @@ -153,6 +177,16 @@ class ArchThreads * @param start_function instruction pointer is set to start function * @param stack stackpointer */ - static void createBaseThreadRegisters(ArchThreadRegisters *&info, void* start_function, void* stack); + static eastl::unique_ptr createBaseThreadRegisters(void* start_function, void* stack); }; + +class WithAddressSpace +{ +public: + WithAddressSpace(Thread* thread); + WithAddressSpace(ArchMemory& arch_memory); + ~WithAddressSpace(); +private: + size_t prev_addr_space_; +}; diff --git a/arch/x86/32/common/include/InterruptDescriptorTable.h b/arch/x86/32/common/include/InterruptDescriptorTable.h new file mode 100644 index 000000000..a8f068c54 --- /dev/null +++ b/arch/x86/32/common/include/InterruptDescriptorTable.h @@ -0,0 +1,122 @@ +#pragma once + +#include "SegmentUtils.h" + +#include +#include + +#include "EASTL/array.h" + +using handler_func_t = void (*)(); + +static constexpr size_t NUM_INTERRUPTS = 256; + +// https://wiki.osdev.org/Exceptions +constexpr bool interruptHasErrorcode(size_t N) +{ + if ((N == 8) || (10 <= N && N <= 14) || (N == 17) || (N == 21) || (N == 29) || + (N == 30)) + return true; + + return false; +} + +constexpr int interruptPrivilegeLevel(size_t interrupt_number) +{ + if (interrupt_number == 0x80) + return DPL_USER; + else + return DPL_KERNEL; +} + +struct [[gnu::packed]] InterruptGateDesc +{ + uint16_t offset_low : 16; // low word of handler entry point's address + uint16_t segment_selector : 16; // (code) segment the handler resides in + uint8_t unused : 8; // set to zero + uint8_t type : 4; // set to TYPE_TRAP_GATE or TYPE_INTERRUPT_GATE + uint8_t zero_1 : 1; // unused - set to zero + uint8_t dpl : 2; // descriptor protection level + uint8_t present : 1; // present- flag - set to 1 + uint16_t offset_high : 16; // high word of handler entry point's address + + InterruptGateDesc() = default; + InterruptGateDesc(handler_func_t offset, uint8_t dpl); + + void setOffset(handler_func_t offset); + handler_func_t offset(); + + enum Type + { + // interrupt gate => IF flag *is* cleared + // trap gate => IF flag is *not* cleared + TASK = 0x5, + INTERRUPT_16BIT = 0x6, + TRAP_16BIT = 0x7, + INTERRUPT = 0xE, + TRAP = 0xF, + }; +}; + +struct [[gnu::packed]] IDTR +{ + uint16_t limit; + uintptr_t base; + + void load(); +}; + +/** Interrupt entry stub. + * Pushes interrupt number onto the stack before jumping to the main + * interrupt hander arch_interruptHandler(). Also pushes a fake error code if + * not already done by the processor itself to ensure stack layout consistency. + * + * @tparam N interrupt number + */ +template [[gnu::naked, noreturn]] void interruptEntry() +{ + // compile time constexpr if -> push instruction is only generated if required. No check at runtime + if constexpr (!interruptHasErrorcode(N)) + asm volatile("pushl $0\n"); + + // Main interrupt handler code is responsible for saving registers, etc... + asm volatile("pushl %[num]\n" + "jmp arch_interruptHandler\n" ::[num] "i"(N)); +} + +template auto make_element() +{ + return InterruptGateDesc{&interruptEntry, interruptPrivilegeLevel(I)}; +} + +template +constexpr auto generate_impl(eastl::index_sequence) + -> eastl::array +{ + return {make_element()...}; +} + +template constexpr eastl::array generate() +{ + return generate_impl(eastl::make_index_sequence()); +} + +struct InterruptDescriptorTable +{ + constexpr InterruptDescriptorTable() : + entries(generate()) + { + } + + static InterruptDescriptorTable& instance() + { + static InterruptDescriptorTable idt; + return idt; + } + + constexpr IDTR idtr() { return {sizeof(entries) - 1, (uintptr_t)&entries}; } + + eastl::array entries; + + static_assert(sizeof(entries) == sizeof(InterruptGateDesc) * NUM_INTERRUPTS); +}; diff --git a/arch/x86/32/common/include/InterruptUtils.h b/arch/x86/32/common/include/InterruptUtils.h new file mode 100644 index 000000000..e8c78c7b7 --- /dev/null +++ b/arch/x86/32/common/include/InterruptUtils.h @@ -0,0 +1,78 @@ +#pragma once + +#include "InterruptDescriptorTable.h" + +#include + +#define DPL_KERNEL_SPACE 0 // kernelspace's protection level +#define DPL_USER_SPACE 3 // userspaces's protection level + +namespace InterruptVector +{ + static constexpr size_t NUM_VECTORS = 256; + static constexpr size_t NUM_ISA_INTERRUPTS = 16; + + static constexpr uint8_t REMAP_OFFSET = 32; + + static constexpr uint8_t YIELD = 65; + static constexpr uint8_t IPI_HALT_CPU = 90; + static constexpr uint8_t APIC_ERROR = 91; + static constexpr uint8_t APIC_SPURIOUS = 100; + static constexpr uint8_t IPI_REMOTE_FCALL = 101; + static constexpr uint8_t APIC_TIMER = 127; + static constexpr uint8_t SYSCALL = 0x80; +}; + +// Standard ISA IRQs +enum class ISA_IRQ : uint8_t +{ + PIT = 0, // 0 Programmable Interrupt Timer Interrupt + KEYBOARD = 1, // 1 Keyboard Interrupt + PIC_8259_CASCADE = 2, // 2 Cascade (used internally by the two PICs. never raised) + COM2 = 3, // 3 COM2 (if enabled) + COM1 = 4, // 4 COM1 (if enabled) + LPT2 = 5, // 5 LPT2 (if enabled) + FLOPPY_DISK = 6, // 6 Floppy Disk + LPT1 = 7, // 7 LPT1 / Unreliable "spurious" interrupt (usually) + RTC = 8, // 8 CMOS real-time clock (if enabled) + // 9 Free for peripherals / legacy SCSI / NIC + // 10 Free for peripherals / SCSI / NIC + // 11 Free for peripherals / SCSI / NIC + PS2_MOUSE = 12, // 12 PS2 Mouse + FPU = 13, // 13 FPU / Coprocessor / Inter-processor + ATA_PRIMARY = 14, // 14 Primary ATA Hard Disk + ATA_SECONDARY = 15 // 15 Secondary ATA Hard Disk +}; + +union [[gnu::packed]] PagefaultExceptionErrorCode +{ + uint32_t u32; + struct [[gnu::packed]] + { + uint32_t present : 1; + uint32_t write : 1; + uint32_t user : 1; + uint32_t reserved_write : 1; + uint32_t instruction_fetch : 1; + uint32_t protection_key : 1; + uint32_t shadow_stack : 1; + uint32_t reserved : 8; + uint32_t sgx : 1; + uint32_t reserved2 : 16; + }; +}; + +struct ArchThreadRegisters; + +void interruptHandler(size_t interrupt_num, uint32_t error_code, ArchThreadRegisters* saved_registers); + +void int32_handler_PIT_irq0(); +void int65_handler_swi_yield(); +void int90_handler_halt_cpu(); +void int91_handler_APIC_error(); +void int100_handler_APIC_spurious(); +void int101_handler_cpu_fcall(); +void int127_handler_APIC_timer(); +void syscallHandler(); +void pageFaultHandler(uint32_t address, uint32_t error, uint32_t ip); +void errorHandler(size_t num, size_t eip, size_t cs, size_t spurious); diff --git a/arch/x86/32/common/include/SegmentUtils.h b/arch/x86/32/common/include/SegmentUtils.h index e0ccc02a3..c99d4b470 100644 --- a/arch/x86/32/common/include/SegmentUtils.h +++ b/arch/x86/32/common/include/SegmentUtils.h @@ -2,7 +2,87 @@ #include "types.h" -typedef struct { +#define KERNEL_DS_ENTRY 2 +#define KERNEL_SS_ENTRY 2 +#define KERNEL_CS_ENTRY 3 +#define USER_DS_ENTRY 4 +#define USER_SS_ENTRY 4 +#define USER_CS_ENTRY 5 +#define KERNEL_TSS_ENTRY 6 +#define KERNEL_FS_ENTRY 7 +#define KERNEL_GS_ENTRY 7 + +#define GDT_ENTRY_SIZE 8 + +#define KERNEL_CS (GDT_ENTRY_SIZE * KERNEL_CS_ENTRY) +#define KERNEL_DS (GDT_ENTRY_SIZE * KERNEL_DS_ENTRY) +#define KERNEL_SS (GDT_ENTRY_SIZE * KERNEL_SS_ENTRY) +#define KERNEL_TSS (GDT_ENTRY_SIZE * KERNEL_TSS_ENTRY) +#define KERNEL_FS (GDT_ENTRY_SIZE * KERNEL_FS_ENTRY) +#define KERNEL_GS (GDT_ENTRY_SIZE * KERNEL_GS_ENTRY) + +#define DPL_KERNEL 0 +#define DPL_USER 3 + +#define USER_CS ((GDT_ENTRY_SIZE * USER_CS_ENTRY) | DPL_USER) +#define USER_DS ((GDT_ENTRY_SIZE * USER_DS_ENTRY) | DPL_USER) +#define USER_SS ((GDT_ENTRY_SIZE * USER_SS_ENTRY) | DPL_USER) + +struct SegmentDescriptor +{ + uint16 limitL; + uint16 baseL; + + uint8 baseM; + uint8 typeL; + uint8 limitH :4; + uint8 typeH :4; + uint8 baseH; + + size_t getBase(); + void setBase(size_t base); + void setLimit(size_t limit); +}__attribute__((__packed__)); + +struct GDT +{ + SegmentDescriptor entries[8]; +} __attribute__((__packed__)); + +struct GDT32Ptr +{ + uint16 limit; + uint32 addr; + + GDT32Ptr() = default; + GDT32Ptr(uint16 limit, uint32 addr); + explicit GDT32Ptr(GDT& gdt); + void load(); +}__attribute__((__packed__)); + +typedef struct +{ + uint32 limitL : 16; + uint32 baseLL : 16; + + uint32 baseLM : 8; + uint32 type : 4; + uint32 zero : 1; + uint32 dpl : 2; + uint32 present : 1; + uint32 limitH : 4; + uint32 avl_to_software : 1; + uint32 ignored : 2; + uint32 granularity : 1; + uint32 baseLH : 8; + +}__attribute__((__packed__)) TSSSegmentDescriptor; + +void setTSSSegmentDescriptor(TSSSegmentDescriptor* descriptor, uint32 base, uint32 limit, uint8 dpl); + + +struct TSS +{ uint16 backlink; //0 uint16 pad0; uint32 esp0; //1 @@ -41,13 +121,23 @@ typedef struct { uint16 padA; uint16 debugtrap; uint16 iobase; -} __attribute__((__packed__))TSS; + + void setTaskStack(size_t stack_top); +} __attribute__((__packed__)); class SegmentUtils { public: static void initialise(); - + static void loadKernelSegmentDescriptors(); }; + +void setFSBase(GDT& gdt, size_t fs_base); +void setGSBase(GDT& gdt, size_t fs_base); +size_t getFSBase(GDT &gdt); +size_t getGSBase(GDT &gdt); + +extern GDT gdt; +extern TSS g_tss; diff --git a/arch/x86/32/common/include/bootprint.h b/arch/x86/32/common/include/bootprint.h new file mode 100644 index 000000000..d43d22c76 --- /dev/null +++ b/arch/x86/32/common/include/bootprint.h @@ -0,0 +1,31 @@ +#pragma once + +#include "types.h" + +void memset(char* block, char c, size_t length); + +void setFBrow(uint8 row); +void setFBcol(uint8 col); + +uint8 getFBrow(); +uint8 getFBcol(); + +uint8 getNextFBrow(); + +void clearFB(); + +char* getFBAddr(uint8 row, uint8 col); + +void clearFBrow(uint8 row); + +void FBnewline(); + +void kputc(const char c); + +void kputs(const char* string); + +uint8 nibbleToASCII(char nibble); + +void putHex8(char c); + +void putHex32(uint32 v); diff --git a/arch/x86/32/common/include/offsets.h b/arch/x86/32/common/include/offsets.h index 82676432f..ecc14b6c4 100644 --- a/arch/x86/32/common/include/offsets.h +++ b/arch/x86/32/common/include/offsets.h @@ -1,5 +1,7 @@ #pragma once +#include "types.h" + /** * These are the basic offsets for our memory layout */ @@ -8,7 +10,7 @@ /** * Our image will be at 2gig virtual */ -#define LINK_BASE 0x80000000 +#define LINK_BASE 0x80000000 /** * Our image will be at 1meg physical @@ -23,15 +25,23 @@ /** * returns the virtual address of a physical address by using the offset */ -#define PHYSICAL_TO_VIRTUAL_BOOT(x) ((x) + PHYSICAL_TO_VIRTUAL_OFFSET) +#define PHYSICAL_TO_VIRTUAL_BOOT(x) (((pointer)x) + PHYSICAL_TO_VIRTUAL_OFFSET) /** * returns the physical address of a virtual address by using the offset */ -#define VIRTUAL_TO_PHYSICAL_BOOT(x) ((x) - PHYSICAL_TO_VIRTUAL_OFFSET) +#define VIRTUAL_TO_PHYSICAL_BOOT(x) (((pointer)x) - PHYSICAL_TO_VIRTUAL_OFFSET) /** * Only addresses below 2gig virtual belong to the user space */ #define USER_BREAK 0x80000000 +/** + * End of the non-canonical space, start of kernel space + */ +#define KERNEL_START 0x80000000 + +// 0xc0000000 +#define IDENT_MAPPING_START (3U * 1024U * 1024U * 1024U) +#define IDENT_MAPPING_END (0xffffffff) diff --git a/arch/x86/32/common/include/types.h b/arch/x86/32/common/include/types.h index 7638e67f0..222eb1758 100644 --- a/arch/x86/32/common/include/types.h +++ b/arch/x86/32/common/include/types.h @@ -1,44 +1,38 @@ #pragma once -typedef char int8; -typedef unsigned char uint8; +#include "stdint.h" +#include "klibc/sys/types.h" -typedef short int16; -typedef unsigned short uint16; +typedef int8_t int8; +typedef uint8_t uint8; -typedef int int32; -typedef unsigned int uint32; +typedef int16_t int16; +typedef uint16_t uint16; -typedef unsigned long long uint64; -typedef long long int64; +typedef int32_t int32; +typedef uint32_t uint32; -typedef uint32 pointer; +typedef uint64_t uint64; +typedef int64_t int64; -typedef uint32 l_off_t; +typedef uint32_t l_off_t; -typedef uint32 mode_t; -typedef uint32 size_t; -typedef int32 ssize_t; +typedef uint32_t mode_t; +typedef uint32_t uid_t; +typedef uint32_t gid_t; -typedef uint32 uid_t; -typedef uint32 gid_t; +typedef __SIZE_TYPE__ size_t; -#pragma GCC poison double float +typedef uint32_t ppn_t; +typedef size_t vpn_t; + +typedef size_t pointer; + +/* #pragma GCC poison double float */ #define Min(x,y) (((x)<(y))?(x):(y)) #define Max(x,y) (((x)>(y))?(x):(y)) -#define KERNEL_CS (8*3) -#define KERNEL_DS (8*2) -#define KERNEL_SS (8*2) -#define KERNEL_TSS (8*6) -#define DPL_KERNEL 0 -#define DPL_USER 3 -#define USER_CS ((8*5)|DPL_USER) -#define USER_DS ((8*4)|DPL_USER) -#define USER_SS ((8*4)|DPL_USER) - #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) #define unreachable() __builtin_unreachable() - diff --git a/arch/x86/32/common/source/ArchCommon.cpp b/arch/x86/32/common/source/ArchCommon.cpp index b7c80577e..73cb67bbf 100644 --- a/arch/x86/32/common/source/ArchCommon.cpp +++ b/arch/x86/32/common/source/ArchCommon.cpp @@ -1,16 +1,37 @@ -#include #include "ArchCommon.h" -#include "multiboot.h" -#include "offsets.h" -#include "kprintf.h" -#include "ArchMemory.h" -#include "TextConsole.h" + #include "FrameBufferConsole.h" -#include "backtrace.h" +#include "IDEDriver.h" +#include "KernelMemoryManager.h" +#include "PageManager.h" +#include "PlatformBus.h" +#include "ProgrammableIntervalTimer.h" +#include "SMP.h" +#include "Scheduler.h" +#include "SerialManager.h" #include "Stabs2DebugInfo.h" +#include "TextConsole.h" +#include "backtrace.h" +#include "debug_bochs.h" +#include "kprintf.h" +#include "kstring.h" +#include "multiboot.h" +#include "offsets.h" #include "ports.h" -#include "PageManager.h" +#include + +#include "ArchMemory.h" +#include "ArchMulticore.h" +#if (A_BOOT == A_BOOT | OUTPUT_ENABLED) +#define PRINT(X) writeLine2Bochs((const char*)VIRTUAL_TO_PHYSICAL_BOOT(X)) +#else +#define PRINT(X) +#endif + +RangeAllocator<> mmio_addr_allocator; + +extern void* kernel_start_address; extern void* kernel_end_address; multiboot_info_t* multi_boot_structure_pointer = (multiboot_info_t*)0xDEADDEAD; // must not be in bss segment @@ -24,9 +45,31 @@ extern "C" void parseMultibootHeader() struct multiboot_remainder &orig_mbr = (struct multiboot_remainder &)(*((struct multiboot_remainder*)VIRTUAL_TO_PHYSICAL_BOOT((pointer)&mbr))); + PRINT("Bootloader: "); + writeLine2Bochs((char*)(pointer)(mb_infos->boot_loader_name)); + PRINT("\n"); + + if (mb_infos && mb_infos->f_cmdline) + { + const char* cmdline = (char*)(uintptr_t)mb_infos->cmdline; + size_t len = strlen(cmdline); + if (len+1 <= sizeof(orig_mbr.cmdline)) + { + memcpy(orig_mbr.cmdline, cmdline, len+1); + } + } + + if (mb_infos && mb_infos->f_fb) + { + orig_mbr.have_framebuffer = true; + orig_mbr.framebuffer = mb_infos->framebuffer; + } + if (mb_infos && mb_infos->f_vbe) { - struct vbe_mode* mode_info = (struct vbe_mode*)mb_infos->vbe_mode_info; + orig_mbr.have_vbe = true; + orig_mbr.vbe = mb_infos->vbe; + struct vbe_mode* mode_info = (struct vbe_mode*)mb_infos->vbe.vbe_mode_info; orig_mbr.have_vesa_console = 1; orig_mbr.vesa_lfb_pointer = mode_info->phys_base; orig_mbr.vesa_x_res = mode_info->x_resolution; @@ -43,6 +86,9 @@ extern "C" void parseMultibootHeader() orig_mbr.module_maps[i].start_address = mods[i].mod_start; orig_mbr.module_maps[i].end_address = mods[i].mod_end; strncpy((char*)(uint32)orig_mbr.module_maps[i].name, (const char*)(uint32)mods[i].string, 256); + PRINT("Module: "); + writeLine2Bochs((char*)mods[i].string); + PRINT("\n"); } orig_mbr.num_module_maps = mb_infos->mods_count; } @@ -68,6 +114,17 @@ extern "C" void parseMultibootHeader() ++i; } } + + if (mb_infos && mb_infos->f_elf_shdr) + { + orig_mbr.have_elf_sec_hdr = true; + orig_mbr.elf_sec = mb_infos->elf_sec; + } +} + +pointer ArchCommon::getKernelStartAddress() +{ + return (pointer)&kernel_start_address; } pointer ArchCommon::getKernelEndAddress() @@ -77,7 +134,10 @@ pointer ArchCommon::getKernelEndAddress() pointer ArchCommon::getFreeKernelMemoryStart() { - return (pointer)&kernel_end_address; + pointer free_kernel_memory_start = (pointer)&kernel_end_address; + for (size_t i = 0; i < getNumModules(); ++i) + free_kernel_memory_start = Max(getModuleEndAddress(i), free_kernel_memory_start); + return ((free_kernel_memory_start - 1) | 0xFFF) + 1; } pointer ArchCommon::getFreeKernelMemoryEnd() @@ -109,14 +169,25 @@ uint32 ArchCommon::getNumModules(uint32 is_paging_set_up) } +const char* ArchCommon::getModuleName(size_t num, size_t is_paging_set_up) +{ + if (is_paging_set_up) + return (char*)((size_t)mbr.module_maps[num].name); + else + { + struct multiboot_remainder &orig_mbr = (struct multiboot_remainder &)(*((struct multiboot_remainder*)VIRTUAL_TO_PHYSICAL_BOOT((pointer)&mbr))); + return (char*)orig_mbr.module_maps[num].name; + } +} + uint32 ArchCommon::getModuleStartAddress(uint32 num, uint32 is_paging_set_up) { if (is_paging_set_up) - return mbr.module_maps[num].start_address + 3*1024*1024*1024U; + return mbr.module_maps[num].start_address + PHYSICAL_TO_VIRTUAL_OFFSET; else { struct multiboot_remainder &orig_mbr = (struct multiboot_remainder &)(*((struct multiboot_remainder*)VIRTUAL_TO_PHYSICAL_BOOT((pointer)&mbr))); - return orig_mbr.module_maps[num].start_address ; + return orig_mbr.module_maps[num].start_address; } } @@ -124,7 +195,7 @@ uint32 ArchCommon::getModuleStartAddress(uint32 num, uint32 is_paging_set_up) uint32 ArchCommon::getModuleEndAddress(uint32 num, uint32 is_paging_set_up) { if (is_paging_set_up) - return mbr.module_maps[num].end_address + 3*1024*1024*1024U; + return mbr.module_maps[num].end_address + PHYSICAL_TO_VIRTUAL_OFFSET; else { struct multiboot_remainder &orig_mbr = (struct multiboot_remainder &)(*((struct multiboot_remainder*)VIRTUAL_TO_PHYSICAL_BOOT((pointer)&mbr))); @@ -162,6 +233,26 @@ pointer ArchCommon::getFBPtr(uint32 is_paging_set_up) return 0x000B8000; } +size_t ArchCommon::getFBWidth() +{ + return 80; +} + +size_t ArchCommon::getFBHeight() +{ + return 25; +} + +size_t ArchCommon::getFBBitsPerCharacter() +{ + return 16; +} + +size_t ArchCommon::getFBSize() +{ + return getFBWidth() * getFBHeight() * getFBBitsPerCharacter()/8; +} + uint32 ArchCommon::getVESAConsoleBitsPerPixel() { return mbr.vesa_bits_per_pixel; @@ -178,7 +269,7 @@ uint32 ArchCommon::getNumUseableMemoryRegions() return i; } -uint32 ArchCommon::getUsableMemoryRegion(uint32 region, pointer &start_address, pointer &end_address, uint32 &type) +uint32 ArchCommon::getUseableMemoryRegion(uint32 region, pointer &start_address, pointer &end_address, uint32 &type) { if (region >= MAX_MEMORY_MAPS) return 1; @@ -201,57 +292,144 @@ Console* ArchCommon::createConsole(uint32 count) return new TextConsole(count); } -Stabs2DebugInfo const *kernel_debug_info = 0; +const Stabs2DebugInfo* kernel_debug_info = 0; void ArchCommon::initDebug() { - extern unsigned char stab_start_address_nr; - extern unsigned char stab_end_address_nr; - - extern unsigned char stabstr_start_address_nr; - - kernel_debug_info = new Stabs2DebugInfo((char const *)&stab_start_address_nr, - (char const *)&stab_end_address_nr, - (char const *)&stabstr_start_address_nr); + debug(A_COMMON, "initDebug\n"); + for (size_t i = 0; i < getNumModules(); ++i) + { + debug(A_COMMON, "Checking module from [%zx -> %zx)\n", getModuleStartAddress(i), getModuleEndAddress(i)); + if ((getModuleStartAddress(i) < getModuleEndAddress(i)) && + (memcmp("SWEBDBG1", (const char*)getModuleStartAddress(i), 8) == 0)) + { + debug(A_COMMON, "Found SWEBDBG\n"); + kernel_debug_info = new SWEBDebugInfo((const char*)getModuleStartAddress(i), + (const char*)getModuleEndAddress(i)); + } + } + if (!kernel_debug_info) + { + kernel_debug_info = new SWEBDebugInfo(0, 0); + } + debug(A_COMMON, "initDebug done\n"); } void ArchCommon::idle() +{ + halt(); +} + +void ArchCommon::halt() { asm volatile("hlt"); } +uint64 ArchCommon::cpuTimestamp() +{ + uint64 timestamp; + asm volatile("rdtsc\n" + :"=A"(timestamp)); + return timestamp; +} + #define STATS_OFFSET 22 -#define FREE_PAGES_OFFSET STATS_OFFSET + 11*2 void ArchCommon::drawStat() { - const char* text = "Free pages F9 MemInfo F10 Locks F11 Stacktrace F12 Threads"; - const char* color = "xxxxxxxxxx xx xxx xxx xxx "; + const char* text = "Free pages F9 MemInfo F10 Locks F11 Stacktrace F12 Threads"; + const char* color = "xxxxxxxxxx xx xxx xxx xxx "; - char* fb = (char*)getFBPtr(); - size_t i = 0; - while(text[i]) { - fb[i * 2 + STATS_OFFSET] = text[i]; - fb[i * 2 + STATS_OFFSET + 1] = (char)(color[i] == 'x' ? 0x80 : 0x08); - i++; - } + char* fb = (char*)getFBPtr(); + size_t i = 0; + while(text[i]) { + fb[i * 2 + STATS_OFFSET] = text[i]; + fb[i * 2 + STATS_OFFSET + 1] = (char)(color[i] == 'x' ? ((CONSOLECOLOR::BLACK) | (CONSOLECOLOR::DARK_GREY << 4)) : + ((CONSOLECOLOR::DARK_GREY) | (CONSOLECOLOR::BLACK << 4))); + i++; + } - char itoa_buffer[33]; - memset(itoa_buffer, '\0', sizeof(itoa_buffer)); - itoa(PageManager::instance()->getNumFreePages(), itoa_buffer, 10); + char itoa_buffer[33]; - for(size_t i = 0; (i < sizeof(itoa_buffer)) && (itoa_buffer[i] != '\0'); ++i) - { - fb[i * 2 + FREE_PAGES_OFFSET] = itoa_buffer[i]; - } +#define STATS_FREE_PAGES_START (STATS_OFFSET + 11*2) + memset(fb + STATS_FREE_PAGES_START, 0, 4*2); + memset(itoa_buffer, '\0', sizeof(itoa_buffer)); + itoa(PageManager::instance().getNumFreePages(), itoa_buffer, 10); + for(size_t i = 0; (i < sizeof(itoa_buffer)) && (itoa_buffer[i] != '\0'); ++i) + { + fb[STATS_FREE_PAGES_START + i * 2] = itoa_buffer[i]; + fb[STATS_FREE_PAGES_START + i * 2 + 1] = ((CONSOLECOLOR::WHITE) | (CONSOLECOLOR::BLACK << 4)); + } + +#define STATS_NUM_THREADS_START (80*2 + 73*2) + memset(fb + STATS_NUM_THREADS_START, 0, 4*2); + memset(itoa_buffer, '\0', sizeof(itoa_buffer)); + itoa(Scheduler::instance()->num_threads, itoa_buffer, 10); + for(size_t i = 0; (i < sizeof(itoa_buffer)) && (itoa_buffer[i] != '\0'); ++i) + { + fb[STATS_NUM_THREADS_START + i * 2] = itoa_buffer[i]; + fb[STATS_NUM_THREADS_START + i * 2 + 1] = ((CONSOLECOLOR::WHITE) | (CONSOLECOLOR::BLACK << 4)); + } } +cpu_local size_t heart_beat_value = 0; + void ArchCommon::drawHeartBeat() { + drawStat(); + const char* clock = "/-\\|"; - static uint32 heart_beat_value = 0; char* fb = (char*)getFBPtr(); - fb[0] = clock[heart_beat_value++ % 4]; - fb[1] = 0x9f; + size_t cpu_id = SMP::currentCpuId(); + fb[cpu_id*2] = clock[heart_beat_value++ % 4]; + fb[cpu_id*2 + 1] = 0x9f; +} - drawStat(); + + + +void ArchCommon::postBootInit() +{ + initACPI(); +} + + +[[noreturn]] void ArchCommon::callWithStack(char* stack, void (*func)()) +{ + asm volatile("movl %[stack], %%esp\n" + "calll *%[func]\n" + ::[stack]"r"(stack), + [func]"r"(func)); + assert(false); +} + + +void ArchCommon::spinlockPause() +{ + asm volatile("pause\n"); +} + +void ArchCommon::reservePagesPreKernelInit(Allocator &alloc) +{ + ArchMulticore::reservePages(alloc); +} + +void ArchCommon::initKernelVirtualAddressAllocator() +{ + mmio_addr_allocator.setUseable(KERNEL_START, (size_t)-1); + mmio_addr_allocator.setUnuseable(getKernelStartAddress(), getKernelEndAddress()); + mmio_addr_allocator.setUnuseable(KernelMemoryManager::instance()->getKernelHeapStart(), KernelMemoryManager::instance()->getKernelHeapMaxEnd()); + mmio_addr_allocator.setUnuseable(IDENT_MAPPING_START, IDENT_MAPPING_END); + debug(MAIN, "Usable MMIO ranges:\n"); + mmio_addr_allocator.printUsageInfo(); +} + +void ArchCommon::initBlockDeviceDrivers() +{ + PlatformBus::instance().registerDriver(IDEControllerDriver::instance()); +} + +void ArchCommon::initPlatformDrivers() +{ + PlatformBus::instance().registerDriver(PITDriver::instance()); + PlatformBus::instance().registerDriver(SerialManager::instance()); } diff --git a/arch/x86/32/common/source/ArchCpuLocalStorage.cpp b/arch/x86/32/common/source/ArchCpuLocalStorage.cpp new file mode 100644 index 000000000..15849b2d7 --- /dev/null +++ b/arch/x86/32/common/source/ArchCpuLocalStorage.cpp @@ -0,0 +1,72 @@ +#include "ArchCpuLocalStorage.h" + +#include "SegmentUtils.h" + +#include + +#include "debug.h" + +extern char cls_start; +extern char cls_end; +extern char tbss_start; +extern char tbss_end; +extern char tdata_start; +extern char tdata_end; + +size_t CpuLocalStorage::getClsSize() +{ + return &cls_end - &cls_start; +} + +char* CpuLocalStorage::allocCls() +{ + debug(A_MULTICORE, "Allocating CPU local storage\n"); + + size_t cls_size = getClsSize(); + size_t tbss_size = &tbss_end - &tbss_start; + size_t tdata_size = &tdata_end - &tdata_start; + debug(A_MULTICORE, "cls_base: [%p, %p), size: %zx\n", &cls_start, &cls_end, cls_size); + debug(A_MULTICORE, "tbss: [%p, %p), size: %zx\n", &tbss_start, &tbss_end, tbss_size); + debug(A_MULTICORE, "tdata: [%p, %p), size: %zx\n", &tdata_start, &tdata_end, tdata_size); + + char* cls_base = new char[cls_size + sizeof(void*)]{}; + debug(A_MULTICORE, "Allocated new cls_base at [%p, %p)\n", cls_base, cls_base + cls_size + sizeof(void*)); + + debug(A_MULTICORE, "Initializing tdata at [%p, %p) and tbss at [%p, %p)\n", + cls_base + (&tdata_start - &cls_start), cls_base + (&tdata_start - &cls_start) + tdata_size, + cls_base + (&tbss_start - &cls_start), cls_base + (&tbss_start - &cls_start) + tbss_size); + memcpy(cls_base + (&tdata_start - &cls_start), &tdata_start, tdata_size); + + return cls_base; +} + +void CpuLocalStorage::setCls(GDT& gdt, char* cls) +{ + debug(A_MULTICORE, "Set CLS: %p\n", cls); + void** gs_base = (void**)(cls + getClsSize()); + debug(A_MULTICORE, "Init CLS pointer at %%gs:0 = %p\n", gs_base); + *gs_base = gs_base; + + // %gs base needs to point to end of CLS, not the start. %gs:0 = pointer to %gs base + setGSBase(gdt, (size_t)gs_base); + setFSBase(gdt, (size_t)gs_base); + + debug(A_MULTICORE, "FS base: %p\n", (void*)getFSBase(gdt)); + debug(A_MULTICORE, "GS base: %p\n", (void*)getGSBase(gdt)); +} + +bool CpuLocalStorage::ClsInitialized() +{ + uint32_t gs_val = 0; + asm("mov %%gs, %[gs]\n" + :[gs]"=g"(gs_val)); + + return gs_val == KERNEL_GS; +} + +void* CpuLocalStorage::getClsBase() +{ + void *gs_base = 0; + asm("movl %%gs:0, %[gs_base]\n" : [gs_base] "=r"(gs_base)); + return gs_base; +} diff --git a/arch/x86/32/common/source/ArchInterrupts.cpp b/arch/x86/32/common/source/ArchInterrupts.cpp index 123dfb5ce..5ec2c8dd7 100644 --- a/arch/x86/32/common/source/ArchInterrupts.cpp +++ b/arch/x86/32/common/source/ArchInterrupts.cpp @@ -1,29 +1,124 @@ #include "ArchInterrupts.h" + #include "8259.h" -#include "ports.h" +#include "APIC.h" #include "InterruptUtils.h" +#include "IoApic.h" +#include "KeyboardManager.h" +#include "PlatformBus.h" +#include "ProgrammableIntervalTimer.h" +#include "Scheduler.h" #include "SegmentUtils.h" +#include "SystemState.h" +#include "Thread.h" +#include "offsets.h" +#include "ports.h" + +#include "ArchCpuLocalStorage.h" +#include "ArchMemory.h" +#include "ArchMulticore.h" #include "ArchThreads.h" + #include "assert.h" -#include "Thread.h" +#include "debug.h" + +cpu_local IrqDomain cpu_irq_vector_domain_("CPU interrupt vector", InterruptVector::NUM_VECTORS); +cpu_local IrqDomain* cpu_root_irq_domain_ = &cpu_irq_vector_domain_; + +IrqDomain& ArchInterrupts::currentCpuRootIrqDomain() +{ + assert(cpu_root_irq_domain_); + return *cpu_root_irq_domain_; +} + +IrqDomain& ArchInterrupts::isaIrqDomain() +{ + static IrqDomain isa_irq_domain("ISA IRQ", InterruptVector::NUM_ISA_INTERRUPTS); + return isa_irq_domain; +} + +static void initInterruptDescriptorTable() +{ + auto& idt = InterruptDescriptorTable::instance(); + idt.idtr().load(); + + if (A_INTERRUPTS & OUTPUT_ENABLED & OUTPUT_ADVANCED) + { + for (size_t i = 0; i < idt.entries.size(); ++i) + { + debug(A_INTERRUPTS, + "%3zu -- offset: %p, present: %x, segment_selector: %x, " + "type: %x, dpl: %x\n", + i, idt.entries[i].offset(), idt.entries[i].present, + idt.entries[i].segment_selector, idt.entries[i].type, + idt.entries[i].dpl); + } + } +} + +void initCpuLocalInterruptHandlers() +{ + debug(A_INTERRUPTS, "Initializing interrupt handlers\n"); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::YIELD).useHandler(int65_handler_swi_yield); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::IPI_HALT_CPU).useHandler(int90_handler_halt_cpu); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::APIC_ERROR).useHandler(int91_handler_APIC_error); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::APIC_SPURIOUS).useHandler(int100_handler_APIC_spurious); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::IPI_REMOTE_FCALL).useHandler(int101_handler_cpu_fcall); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::SYSCALL).useHandler(syscallHandler); +} + +void initInterruptControllers() +{ + debug(A_INTERRUPTS, "Initializing interrupt controllers\n"); + assert(CpuLocalStorage::ClsInitialized()); + + PlatformBus::instance().registerDriver(ApicDriver::instance()); + PlatformBus::instance().registerDriver(ApicTimerDriver::instance()); + PlatformBus::instance().registerDriver(IoApicDriver::instance()); + PlatformBus::instance().registerDriver(PIC8259Driver::instance()); + + assert(cpu_root_irq_domain_); + debug(A_INTERRUPTS, "Interrupt controllers initialized\n"); +} void ArchInterrupts::initialise() { - uint16 i; // disableInterrupts(); - initialise8259s(); - SegmentUtils::initialise(); - InterruptUtils::initialise(); - for (i=0;i<16;++i) - disableIRQ(i); + initInterruptDescriptorTable(); + initInterruptControllers(); + initCpuLocalInterruptHandlers(); } void ArchInterrupts::enableTimer() { - enableIRQ(0); + if(cpu_lapic->isInitialized() && cpu_lapic->usingAPICTimer()) + { + debug(A_INTERRUPTS, "Enabling xApic %x timer \n", cpu_lapic->apicId()); + enableIRQ(cpu_lapic->timer_interrupt_controller.irq()); + } + else + { + debug(A_INTERRUPTS, "Enabling PIT timer IRQ\n"); + enableIRQ(PIT::instance().irq()); + } +} + +void ArchInterrupts::disableTimer() +{ + if (cpu_lapic->isInitialized() && cpu_lapic->usingAPICTimer()) + { + debug(A_INTERRUPTS, "Disabling xApic %x timer \n", cpu_lapic->apicId()); + disableIRQ(cpu_lapic->timer_interrupt_controller.irq()); + } + else + { + debug(A_INTERRUPTS, "Disabling PIT timer IRQ\n"); + disableIRQ(PIT::instance().irq()); + } } void ArchInterrupts::setTimerFrequency(uint32 freq) { + debug(A_INTERRUPTS, "Set timer frequency %u\n", freq); uint16_t divisor; if(freq < (uint32)(1193180. / (1 << 16) + 1)) { divisor = 0; @@ -35,33 +130,89 @@ void ArchInterrupts::setTimerFrequency(uint32 freq) outportb(0x40, divisor >> 8); } -void ArchInterrupts::disableTimer() +void ArchInterrupts::enableKBD() { - disableIRQ(0); + debug(A_INTERRUPTS, "Enable keyboard irq\n"); + + enableIRQ(KeyboardManager::instance().irq()); } -void ArchInterrupts::enableKBD() +void ArchInterrupts::disableKBD() { - enableIRQ(1); - enableIRQ(9); + enableIRQ(KeyboardManager::instance().irq(), false); } -void ArchInterrupts::disableKBD() +void ArchInterrupts::enableIRQ(const IrqDomain::DomainIrqHandle& irq_handle, bool enable) +{ + debug(A_INTERRUPTS, "[Cpu %zu] %s %s IRQ %zx\n", SMP::currentCpuId(), + enable ? "Enable" : "Disable", irq_handle.domain().name().c_str(), + irq_handle.irq()); + + for (auto&& domain_irq : irq_handle.forwardMappingChain()) + { + domain_irq.activateInDomain(enable); + } +} + +void ArchInterrupts::disableIRQ(const IrqDomain::DomainIrqHandle& irq_handle) +{ + enableIRQ(irq_handle, false); +} + +void ArchInterrupts::startOfInterrupt(const IrqDomain::DomainIrqHandle& irq_handle) +{ + for (auto&& [domain, local_irqnum] : irq_handle.reverseMappingTree()) + { + if (domain->controller()) + { + domain->controller()->irqStart(local_irqnum); + } + } +} + +void ArchInterrupts::startOfInterrupt(uint16 irqnum) +{ + debugAdvanced(A_INTERRUPTS, "[Cpu %zu] Start of IRQ %u\n", SMP::currentCpuId(), irqnum); + + startOfInterrupt(currentCpuRootIrqDomain().irq(irqnum)); +} + +void ArchInterrupts::endOfInterrupt(const IrqDomain::DomainIrqHandle& irq_handle) +{ + for (auto&& [domain, local_irqnum] : irq_handle.reverseMappingTree()) + { + if (domain->controller()) + { + domain->controller()->ack(local_irqnum); + } + } +} + +void ArchInterrupts::endOfInterrupt(uint16 irqnum) { - disableIRQ(1); + debugAdvanced(A_INTERRUPTS, "[Cpu %zu] Sending EOI for IRQ %u\n", SMP::currentCpuId(), + irqnum); + + endOfInterrupt(currentCpuRootIrqDomain().irq(irqnum)); } -void ArchInterrupts::EndOfInterrupt(uint16 number) +void ArchInterrupts::handleInterrupt(const IrqDomain::DomainIrqHandle& irq_handle) { - sendEOI(number); + for (auto&& domain_irq : irq_handle.reverseMappingTree()) + { + domain_irq.handleInDomain(); + } +} + +void ArchInterrupts::handleInterrupt(uint16_t irqnum) +{ + handleInterrupt(currentCpuRootIrqDomain().irq(irqnum)); } void ArchInterrupts::enableInterrupts() { - __asm__ __volatile__("sti" - : - : - ); + __asm__ __volatile__("sti\n" + "nop\n"); } bool ArchInterrupts::disableInterrupts() @@ -71,8 +222,7 @@ bool ArchInterrupts::disableInterrupts() __asm__ __volatile__("pushfl\n" "popl %0\n" "cli" - : "=a"(ret_val) - :); + : "=a"(ret_val)); return (ret_val & (1 << 9)); //testing IF Flag @@ -85,8 +235,7 @@ bool ArchInterrupts::testIFSet() __asm__ __volatile__( "pushfl\n" "popl %0\n" - : "=a"(ret_val) - :); + : "=a"(ret_val)); return (ret_val & (1 << 9)); //testing IF Flag } @@ -103,62 +252,61 @@ void ArchInterrupts::yieldIfIFSet() } } -struct context_switch_registers { - uint32 es; - uint32 ds; - uint32 edi; - uint32 esi; - uint32 ebp; - uint32 esp; - uint32 ebx; - uint32 edx; - uint32 ecx; - uint32 eax; +struct [[gnu::packed]] context_switch_registers +{ + uint32 gs; // 0-3 + uint32 fs; // 4-7 + uint32 es; // 8-11 + uint32 ds; // 12-15 + uint32 edi; // 16-19 + uint32 esi; // 20-23 + uint32 ebp; // 24-27 + uint32 esp; // 28-31 + uint32 ebx; // 32-35 + uint32 edx; // 36-39 + uint32 ecx; // 40-43 + uint32 eax; // 44-47 }; -struct interrupt_registers { - uint32 eip; - uint32 cs; - uint32 eflags; - uint32 esp3; - uint32 ss3; +struct [[gnu::packed]] interrupt_registers +{ + // TODO: update offsets to include interrupt num + // w/o | w/ error code + uint32 eip; // 48-51 | 52-55 + uint32 cs; // 52-55 | 56-59 + uint32 eflags; // 56-59 | 60-63 + uint32 esp3; // 60-63 | 64-67 + uint32 ss3; // 64-67 | 68-71 }; -#include "kprintf.h" - -extern "C" void arch_dummyHandler(); -extern "C" void arch_dummyHandlerMiddle(); -extern "C" size_t arch_computeDummyHandler(uint32 eip) +struct [[gnu::packed]] SavedContextSwitchRegisters { - size_t dummy_handler_sled_size = (((size_t) arch_dummyHandlerMiddle) - (size_t) arch_dummyHandler); - assert((dummy_handler_sled_size % 128) == 0 && "cannot handle weird padding in the kernel binary"); - dummy_handler_sled_size /= 128; - assert((eip <= (size_t) arch_dummyHandlerMiddle) && "calling dummy handler cannot be outside of dummy handler sled"); - assert((eip >= (size_t) arch_dummyHandler) && "calling dummy handler cannot be outside of dummy handler sled"); - size_t calling_dummy_handler = (eip - (size_t) arch_dummyHandler) / dummy_handler_sled_size - 1; - return calling_dummy_handler; -} + context_switch_registers registers; + uint32_t interrupt_num; + uint32_t error_code; + interrupt_registers iregisters; +}; -extern "C" void arch_saveThreadRegisters(uint32 error) +extern "C" ArchThreadRegisters* +arch_saveThreadRegisters([[maybe_unused]]uint32 error, SavedContextSwitchRegisters* saved_r) { - struct context_switch_registers* registers; - registers = (struct context_switch_registers*) (&error + 2); - struct interrupt_registers* iregisters; - iregisters = (struct interrupt_registers*) (&error + 2 + sizeof(struct context_switch_registers)/sizeof(uint32) + (error)); - ArchThreadRegisters* info = currentThreadRegisters; + auto registers = &saved_r->registers; + auto iregisters = &saved_r->iregisters; + ArchThreadRegisters* info = currentThreadRegisters; - asm("fnsave (%[fpu])\n" - "frstor (%[fpu])\n" - : - : [fpu]"r"(&info->fpu)); - if ((iregisters->cs & 0x3) == 0x3) - { - info->ss = iregisters->ss3; - info->esp = iregisters->esp3; + asm("fnsave (%[fpu])\n" + "frstor (%[fpu])\n" + : + : [fpu] "r"(&info->fpu)); + if ((iregisters->cs & 0x3) == 0x3) + { + info->ss = iregisters->ss3; + info->esp = iregisters->esp3; } else { - info->esp = registers->esp + 0xc; + // Compensate for interrupt frame + error code + int num pushed onto stack before esp is saved via pusha + info->esp = registers->esp + 0x14; } info->eip = iregisters->eip; info->cs = iregisters->cs; @@ -172,24 +320,65 @@ extern "C" void arch_saveThreadRegisters(uint32 error) info->edi = registers->edi; info->ds = registers->ds; info->es = registers->es; + info->fs = registers->fs; + info->gs = registers->gs; assert(!currentThread || currentThread->isStackCanaryOK()); + + return info; } -extern TSS *g_tss; +extern "C" void genericInterruptEntry(SavedContextSwitchRegisters* regs) +{ + // Take registers previously saved on the stack via assembly and store them in the + // saved registers of the thread + auto saved_regs = arch_saveThreadRegisters(0, regs); + + debugAdvanced(A_INTERRUPTS, "[Cpu %zu] Interrupt entry %zu\n", + SMP::currentCpuId(), regs->interrupt_num); -extern "C" void arch_contextSwitch() + interruptHandler(regs->interrupt_num, regs->error_code, saved_regs); +} + +extern "C" [[noreturn]] void contextSwitch(Thread* target_thread, ArchThreadRegisters* target_registers) { - assert(currentThread->isStackCanaryOK() && "Kernel stack corruption detected."); - if(outstanding_EOIs) + target_thread = target_thread ? : currentThread; + target_registers = target_registers ? : currentThreadRegisters; + assert(target_thread); + + if(A_INTERRUPTS & OUTPUT_ADVANCED) { - debug(A_INTERRUPTS, "%zu outstanding End-Of-Interrupt signal(s) on context switch. Probably called yield in the wrong place (e.g. in the scheduler/IRQ0)\n", outstanding_EOIs); - assert(!outstanding_EOIs); + debug(A_INTERRUPTS, "CPU %zu, context switch to thread %p = %s, user: %u, regs: %p at eip %p, esp %p, ebp %p\n", + SMP::currentCpuId(), target_thread, target_thread->getName(), target_thread->switch_to_userspace_, target_registers, + (void*)target_registers->eip, (void*)target_registers->esp, (void*)target_registers->ebp); } - ArchThreadRegisters info = *currentThreadRegisters; // optimization: local copy produces more efficient code in this case - if (currentThread->switch_to_userspace_) + + assert(target_registers); + if (currentThread) + { + assert(currentThread->currently_scheduled_on_cpu_ == SMP::currentCpuId()); + } + + if((SMP::currentCpuId() == 0) && PIC8259::outstanding_EOIs_) // TODO: Check local APIC for pending EOIs + { + debug(A_INTERRUPTS, "%zu pending End-Of-Interrupt signal(s) on context switch. Probably called yield in the wrong place (e.g. in the scheduler)\n", PIC8259::outstanding_EOIs_); + assert(!((SMP::currentCpuId() == 0) && PIC8259::outstanding_EOIs_)); + } + + if (target_thread->switch_to_userspace_) + { + assert(target_thread->holding_lock_list_ == 0 && "Never switch to userspace when holding a lock! Never!"); + assert(target_thread->lock_waiting_on_ == 0 && "How did you even manage to execute code while waiting for a lock?"); + } + + assert(target_thread->isStackCanaryOK() && "Kernel stack corruption detected."); + + currentThread = target_thread; + currentThreadRegisters = target_registers; + currentThread->currently_scheduled_on_cpu_ = SMP::currentCpuId(); + + ArchThreadRegisters info = *target_registers; // optimization: local copy produces more efficient code in this case + if (target_thread->switch_to_userspace_) { - assert(currentThread->holding_lock_list_ == 0 && "Never switch to userspace when holding a lock! Never!"); - assert(currentThread->lock_waiting_on_ == 0 && "How did you even manage to execute code while waiting for a lock?"); asm("push %[ss]" : : [ss]"m"(info.ss)); asm("push %[esp]" : : [esp]"m"(info.esp)); } @@ -197,7 +386,7 @@ extern "C" void arch_contextSwitch() { asm("mov %[esp], %%esp\n" : : [esp]"m"(info.esp)); } - g_tss->esp0 = info.esp0; + cpu_tss.setTaskStack(info.esp0); asm("frstor (%[fpu])\n" : : [fpu]"r"(&info.fpu)); asm("mov %[cr3], %%cr3\n" : : [cr3]"r"(info.cr3)); asm("push %[eflags]\n" : : [eflags]"m"(info.eflags)); @@ -207,6 +396,8 @@ extern "C" void arch_contextSwitch() asm("mov %[edi], %%edi\n" : : [edi]"m"(info.edi)); asm("mov %[es], %%es\n" : : [es]"m"(info.es)); asm("mov %[ds], %%ds\n" : : [ds]"m"(info.ds)); + asm("mov %[fs], %%fs\n" : : [fs]"m"(info.fs)); + asm("mov %[gs], %%gs\n" : : [gs]"m"(info.gs)); // Don't use CPU local storage after loading %gs asm("push %[ebp]\n" : : [ebp]"m"(info.ebp)); asm("pop %%ebp\n" "iret" : : "a"(info.eax), "b"(info.ebx), "c"(info.ecx), "d"(info.edx)); diff --git a/arch/x86/32/common/source/ArchMulticore.cpp b/arch/x86/32/common/source/ArchMulticore.cpp new file mode 100644 index 000000000..8673d956c --- /dev/null +++ b/arch/x86/32/common/source/ArchMulticore.cpp @@ -0,0 +1,278 @@ +#include "ArchMulticore.h" + +#include "APIC.h" +#include "Allocator.h" +#include "InterruptUtils.h" +#include "Scheduler.h" +#include "ScopeLock.h" +#include "SystemState.h" +#include "Thread.h" +#include "offsets.h" + +#include "ArchCommon.h" +#include "ArchCpuLocalStorage.h" +#include "ArchInterrupts.h" +#include "ArchMemory.h" +#include "ArchThreads.h" + +#include "EASTL/atomic.h" + +#include "debug.h" + +cpu_local GDT cpu_gdt; +cpu_local TSS cpu_tss; + +cpu_local XApic cpu_lapic_impl; +cpu_local Apic* cpu_lapic = &cpu_lapic_impl; + +cpu_local char cpu_stack[CPU_STACK_SIZE]; + + +eastl::atomic ap_started = false; + + +extern eastl::atomic running_cpus; + +extern GDT32Ptr ap_gdt32_ptr; +extern GDT ap_gdt32; + +extern GDT gdt; + +extern char apstartup_text_begin; +extern char apstartup_text_end; +extern char apstartup_text_load_begin; +extern char apstartup_text_load_end; + +extern uint32 ap_kernel_cr3; + +extern char ap_paging_root; +extern char ap_paging_root_end; + +static uint8 ap_boot_stack[PAGE_SIZE]; + + +void ArchMulticore::initCpuLocalData(bool boot_cpu) +{ + initCpuLocalGDT(boot_cpu ? gdt : ap_gdt32); + initCpuLocalTSS((size_t)ArchMulticore::cpuStackTop()); + + + // The constructor of objects declared as cpu_local will be called automatically the first time the cpu_local object is used. Other cpu_local objects _may or may not_ also be initialized at the same time. + debug(A_MULTICORE, "Initializing CPU local objects for CPU %zu\n", SMP::currentCpuId()); + + idle_thread = new IdleThread(); + debug(A_MULTICORE, "CPU %zu: %s initialized\n", SMP::currentCpuId(), idle_thread->getName()); + idle_thread->pinned_to_cpu = SMP::currentCpuId(); + Scheduler::instance()->addNewThread(idle_thread); +} + + + +void ArchMulticore::initCpuLocalGDT(GDT& template_gdt) +{ + cpu_gdt = template_gdt; + + debug(A_MULTICORE, "CPU switching to own GDT at: %p\n", &cpu_gdt); + GDT32Ptr(cpu_gdt).load(); + __asm__ __volatile__("mov %[ds], %%ds\n" + "mov %[ds], %%es\n" + "mov %[ds], %%ss\n" + "ljmp %[cs], $1f\n" + "1:\n" + : + :[ds]"a"(KERNEL_DS), + [cs]"i"(KERNEL_CS)); +} + +void ArchMulticore::initCpuLocalTSS(size_t cpu_stack_top) +{ + debug(A_MULTICORE, "CPU init TSS at %p\n", &cpu_tss); + setTSSSegmentDescriptor((TSSSegmentDescriptor*)((char*)&cpu_gdt + KERNEL_TSS), (size_t)&cpu_tss, sizeof(TSS) - 1, 0); + + cpu_tss.setTaskStack(cpu_stack_top); + + __asm__ __volatile__("ltr %%ax" : : "a"(KERNEL_TSS)); +} + + +void ArchMulticore::initialize() +{ + assert(running_cpus == 0); + running_cpus = 1; + + char *cls = CpuLocalStorage::allocCls(); + CpuLocalStorage::setCls(gdt, cls); + ArchMulticore::initCpuLocalData(true); +} + +void ArchMulticore::prepareAPStartup(size_t entry_addr) +{ + size_t apstartup_size = (size_t)(&apstartup_text_load_end - &apstartup_text_load_begin); + debug(A_MULTICORE, "text.apstartup begin: %p, text.apstartup end: %p\n", + &apstartup_text_begin, &apstartup_text_end); + debug(A_MULTICORE, "text.apstartup load begin: %p, text.apstartup load end: %p, size: %zx\n", + &apstartup_text_load_begin, &apstartup_text_load_end, apstartup_size); + debug(A_MULTICORE, "apstartup %p, phys: %p\n", &apstartup_text_load_begin, (void*)entry_addr); + + auto m_load = ArchMemory::kernelArchMemory().resolveMapping((size_t)&apstartup_text_load_begin/PAGE_SIZE); + debug(A_MULTICORE, "apstartup load mapping %p: page: %p, ppn: %x, pt: %p, writeable: %u\n", + &apstartup_text_load_begin, (void*)m_load.page, m_load.page_ppn, m_load.pt, m_load.pt[m_load.pti].writeable); + assert(m_load.pt[m_load.pti].writeable); + + pointer paddr0 = ArchMemory::getIdentAddress(entry_addr); + debug(A_MULTICORE, "Ident mapping for entry addr %x: %x\n", entry_addr, paddr0); + auto m = ArchMemory::kernelArchMemory().resolveMapping(paddr0/PAGE_SIZE); + + assert(m.page && "Page for application processor entry not mapped in kernel"); // TODO: Map if not present + debug(A_MULTICORE, "PPN: %x\n", m.page_ppn); + assert((m.page_ppn == entry_addr/PAGE_SIZE) && "PPN in ident mapping doesn't match expected ppn for AP entry"); + + size_t ap_gdt32_offset = (size_t)&ap_gdt32 - (size_t)&apstartup_text_begin; + size_t ap_gdt32_load_addr = (size_t)&apstartup_text_load_begin + ap_gdt32_offset; + debug(A_MULTICORE, "AP GDT load offset: %zx\n", ap_gdt32_offset); + + // Init AP gdt + debug(A_MULTICORE, "Init AP GDT at %p, (loaded at %zx)\n", &ap_gdt32, ap_gdt32_load_addr); + auto m_ap_gdt = ArchMemory::kernelArchMemory().resolveMapping(((size_t)&ap_gdt32_load_addr)/PAGE_SIZE); + assert(m_ap_gdt.page && "AP GDT virtual address not mapped in kernel"); + assert(m_ap_gdt.pt && m_ap_gdt.pt[m_ap_gdt.pti].writeable && "AP GDT virtual address not writeable"); + + memcpy((void*)ap_gdt32_load_addr, &gdt, sizeof(ap_gdt32)); + + size_t ap_gdt32_ptr_offset = (size_t)&ap_gdt32_ptr - (size_t)&apstartup_text_begin; + size_t ap_gdt32_ptr_load_addr = (size_t)&apstartup_text_load_begin + ap_gdt32_ptr_offset; + GDT32Ptr *ap_gdt32_ptr_load = (GDT32Ptr*)ap_gdt32_ptr_load_addr; + + ap_gdt32_ptr_load->addr = (size_t)&ap_gdt32; + ap_gdt32_ptr_load->limit = sizeof(ap_gdt32) - 1; + + debug(A_MULTICORE, "paddr0: %x\n", paddr0); + + // Init AP PD + size_t ap_pml4_offset = (size_t)&ap_paging_root - (size_t)&apstartup_text_begin; + size_t ap_pml4_load_addr = (size_t)&apstartup_text_load_begin + ap_pml4_offset; + debug(A_MULTICORE, "Init AP PD at %p (loaded at %zx)\n", &ap_paging_root, ap_pml4_load_addr); + memcpy((void*)ap_pml4_load_addr, ArchMemory::getKernelPagingStructureRootVirt(), (size_t)(&ap_paging_root_end - &ap_paging_root)); + + debug(A_MULTICORE, "paddr0: %x\n", paddr0); + debug(A_MULTICORE, "AP PD phys: %x\n", (size_t)&ap_paging_root); + + size_t ap_kernel_cr3_offset = (size_t)&ap_kernel_cr3 - (size_t)&apstartup_text_begin; + size_t ap_kernel_cr3_load_addr = (size_t)&apstartup_text_load_begin + ap_kernel_cr3_offset; + + *(size_t*)ap_kernel_cr3_load_addr = (size_t)&ap_paging_root; + + debug(A_MULTICORE, "Copying apstartup from virt [%p,%p] -> %p (phys: %zx), size: %zx\n", + (void *)&apstartup_text_load_begin, (void *)&apstartup_text_load_end, + (void *)paddr0, (size_t)entry_addr, apstartup_size); + memcpy((void*)paddr0, (void*)&apstartup_text_load_begin, apstartup_size); +} + +void ArchMulticore::startOtherCPUs() +{ + if(cpu_lapic->isInitialized()) + { + debug(A_MULTICORE, "Starting other CPUs\n"); + + prepareAPStartup(AP_STARTUP_PADDR); + + for(auto& other_cpu_lapic : Apic::local_apic_list_) + { + if(other_cpu_lapic.flags.enabled && (other_cpu_lapic.apic_id != cpu_lapic->apicId())) + { + startAP(other_cpu_lapic.apic_id, AP_STARTUP_PADDR); + debug(A_MULTICORE, "BSP waiting for AP %x startup to be complete\n", other_cpu_lapic.apic_id); + while(!ap_started); + ap_started = false; + debug(A_MULTICORE, "AP %u startup complete, BSP continuing\n", other_cpu_lapic.apic_id); + } + } + + ScopeLock l(SMP::cpuListLock()); + for(auto& cpu : SMP::cpuList()) + { + debug(A_MULTICORE, "CPU %zu running\n", cpu->id()); + } + } + else + { + debug(A_MULTICORE, "No local APIC. Cannot start other CPUs\n"); + } +} + +extern "C" void __apstartup32() { + // Hack to avoid automatic function prologue (stack isn't set up yet) + // Load protected mode segments + __asm__ __volatile__( + ".global apstartup32\n" + "apstartup32:\n" + "movw %[K_DS], %%ax\n" + "movw %%ax, %%ds\n" + "movw %%ax, %%ss\n" + "movw %%ax, %%es\n" + "movw %%ax, %%fs\n" + "movw %%ax, %%gs\n" + "movl %[stack], %%esp\n" + "movl %[stack], %%ebp\n" + : + : [K_DS]"i"(KERNEL_DS), + [stack]"i"(ap_boot_stack + sizeof(ap_boot_stack))); + + ArchCommon::callWithStack((char *)ap_boot_stack + sizeof(ap_boot_stack), [] { + ++running_cpus; + debug(A_MULTICORE, "AP startup 32\n"); + debug(A_MULTICORE, "AP switched to stack %p\n", + ap_boot_stack + sizeof(ap_boot_stack)); + + // Enable NX bit + if (PAGE_DIRECTORY_ENTRIES == 512) + { + asm("mov $0xC0000080,%ecx\n" + "rdmsr\n" + "or $0x800,%eax\n" + "wrmsr\n"); + } + + // Stack variables are messed up in this function because we skipped the + // function prologue. Should be fine once we've entered another function. + ArchMulticore::initApplicationProcessorCpu(); + }); +} + +extern void initCpuLocalInterruptHandlers(); + +void ArchMulticore::initApplicationProcessorCpu() +{ + debug(A_MULTICORE, "AP switching from temp kernel paging root to main kernel paging root: %zx\n", (size_t)VIRTUAL_TO_PHYSICAL_BOOT(ArchMemory::getKernelPagingStructureRootVirt())); + ArchMemory::loadPagingStructureRoot(ArchMemory::kernelArchMemory().getValueForCR3()); + + InterruptDescriptorTable::instance().idtr().load(); + + extern char cls_start; + extern char cls_end; + debug(A_MULTICORE, "Setting temporary CLS for AP [%p, %p)\n", &cls_start, &cls_end); + CpuLocalStorage::setCls(ap_gdt32, &cls_start); + currentThread = nullptr; + currentThreadRegisters = nullptr; + + char* cls = CpuLocalStorage::allocCls(); + CpuLocalStorage::setCls(ap_gdt32, cls); + + ApicDriver::instance().cpuLocalInit(); + ApicTimerDriver::instance().cpuLocalInit(); + + initCpuLocalInterruptHandlers(); + + assert(cpu_lapic->apicId() == CPUID::localApicId()); + ArchMulticore::initCpuLocalData(); + + ArchThreads::initialise(); + + debug(A_MULTICORE, "Enable AP timer\n"); + assert(cpu_lapic->isInitialized() && cpu_lapic->usingAPICTimer() && + "Use of local APIC timer is required for SMP"); + ArchInterrupts::enableTimer(); + + debug(A_MULTICORE, "Switching to CPU local stack at %p\n", ArchMulticore::cpuStackTop()); + ArchCommon::callWithStack(ArchMulticore::cpuStackTop(), waitForSystemStart); +} diff --git a/arch/x86/32/common/source/ArchThreads.cpp b/arch/x86/32/common/source/ArchThreads.cpp index cf098f4f3..26118e3c2 100644 --- a/arch/x86/32/common/source/ArchThreads.cpp +++ b/arch/x86/32/common/source/ArchThreads.cpp @@ -1,13 +1,15 @@ #include "ArchThreads.h" -#include "ArchMemory.h" + #include "Loader.h" -#include "kprintf.h" -#include "paging-definitions.h" -#include "offsets.h" +#include "Scheduler.h" +#include "SegmentUtils.h" #include "Thread.h" +#include "kprintf.h" #include "kstring.h" +#include "offsets.h" +#include "paging-definitions.h" - +#include "ArchMemory.h" void ArchThreads::initialise() { @@ -22,66 +24,122 @@ void ArchThreads::setAddressSpace(Thread *thread, ArchMemory& arch_memory) if(thread == currentThread) { - asm volatile("movl %[new_cr3], %%cr3\n" - ::[new_cr3]"r"(arch_memory.getValueForCR3())); + switchToAddressSpace(arch_memory); } } -void ArchThreads::createBaseThreadRegisters(ArchThreadRegisters *&info, void* start_function, void* stack) +void ArchThreads::switchToAddressSpace(Thread* thread) { - info = new ArchThreadRegisters{}; - pointer root_of_kernel_paging_structure = VIRTUAL_TO_PHYSICAL_BOOT(((pointer)ArchMemory::getRootOfKernelPagingStructure())); + ArchMemory::loadPagingStructureRoot(thread->kernel_registers_->cr3); +} - info->eflags = 0x200; - info->cr3 = root_of_kernel_paging_structure; - info->esp = (size_t)stack; - info->ebp = (size_t)stack; - info->eip = (size_t)start_function; +void ArchThreads::switchToAddressSpace(ArchMemory& arch_memory) +{ + ArchMemory::loadPagingStructureRoot(arch_memory.getValueForCR3()); +} - /* fpu (=fninit) */ - info->fpu[0] = 0xFFFF037F; - info->fpu[1] = 0xFFFF0000; - info->fpu[2] = 0xFFFFFFFF; - info->fpu[3] = 0x00000000; - info->fpu[4] = 0x00000000; - info->fpu[5] = 0x00000000; - info->fpu[6] = 0xFFFF0000; +WithAddressSpace::WithAddressSpace(Thread* thread) : + prev_addr_space_(0) +{ + if (thread) + { + prev_addr_space_ = thread->kernel_registers_->cr3; + ArchThreads::switchToAddressSpace(thread); + } } -void ArchThreads::createKernelRegisters(ArchThreadRegisters *&info, void* start_function, void* kernel_stack) +WithAddressSpace::WithAddressSpace(ArchMemory& arch_memory) { - createBaseThreadRegisters(info, start_function, kernel_stack); + prev_addr_space_ = arch_memory.getValueForCR3(); + ArchThreads::switchToAddressSpace(arch_memory); +} - info->cs = KERNEL_CS; - info->ds = KERNEL_DS; - info->es = KERNEL_DS; - info->ss = KERNEL_SS; +WithAddressSpace::~WithAddressSpace() +{ + if (prev_addr_space_) + { + ArchMemory::loadPagingStructureRoot(prev_addr_space_); + } } -void ArchThreads::createUserRegisters(ArchThreadRegisters *&info, void* start_function, void* user_stack, void* kernel_stack) +eastl::unique_ptr ArchThreads::createBaseThreadRegisters( + void* start_function, void* stack) { - createBaseThreadRegisters(info, start_function, user_stack); + auto regs = eastl::make_unique(); + + setInterruptEnableFlag(*regs, true); + regs->cr3 = ArchMemory::kernelArchMemory().getValueForCR3(); + regs->esp = (size_t)stack; + regs->ebp = (size_t)stack; + regs->eip = (size_t)start_function; + + /* fpu (=fninit) */ + regs->fpu[0] = 0xFFFF037F; + regs->fpu[1] = 0xFFFF0000; + regs->fpu[2] = 0xFFFFFFFF; + regs->fpu[3] = 0x00000000; + regs->fpu[4] = 0x00000000; + regs->fpu[5] = 0x00000000; + regs->fpu[6] = 0xFFFF0000; - info->cs = USER_CS; - info->ds = USER_DS; - info->es = USER_DS; - info->ss = USER_SS; - info->esp0 = (size_t)kernel_stack; + return regs; } -void ArchThreads::changeInstructionPointer(ArchThreadRegisters *info, void* function) +eastl::unique_ptr ArchThreads::createKernelRegisters( + void* start_function, void* kernel_stack) { - info->eip = (size_t)function; + auto kregs = createBaseThreadRegisters(start_function, kernel_stack); + + kregs->cs = KERNEL_CS; + kregs->ds = KERNEL_DS; + kregs->es = KERNEL_DS; + kregs->ss = KERNEL_SS; + kregs->fs = KERNEL_FS; + kregs->gs = KERNEL_GS; + assert(kregs->cr3); + + return kregs; } -void ArchThreads::yield() +eastl::unique_ptr ArchThreads::createUserRegisters( + void* start_function, void* user_stack, void* kernel_stack) { - __asm__ __volatile__("int $65"); + auto uregs = createBaseThreadRegisters(start_function, user_stack); + + uregs->cs = USER_CS; + uregs->ds = USER_DS; + uregs->es = USER_DS; + uregs->ss = USER_SS; + uregs->fs = USER_DS; + uregs->gs = USER_DS; + uregs->esp0 = (size_t)kernel_stack; + assert(uregs->cr3); + + return uregs; +} + +void ArchThreads::changeInstructionPointer(ArchThreadRegisters& info, void* function) +{ + info.eip = (size_t)function; +} + +void* ArchThreads::getInstructionPointer(ArchThreadRegisters& info) +{ + return (void*)info.eip; } -uint32 ArchThreads::testSetLock(uint32 &lock, uint32 new_value) +void ArchThreads::setInterruptEnableFlag(ArchThreadRegisters& info, + bool interrupts_enabled) { - return __sync_lock_test_and_set(&lock,new_value); + if (interrupts_enabled) + info.eflags |= 0x200; + else + info.eflags &= ~0x200; +} + +void ArchThreads::yield() +{ + __asm__ __volatile__("int $65"); } uint32 ArchThreads::atomic_add(uint32 &value, int32 increment) @@ -128,13 +186,13 @@ void ArchThreads::atomic_set(int64& target, int64 value) void ArchThreads::printThreadRegisters(Thread *thread, bool verbose) { - printThreadRegisters(thread,0,verbose); - printThreadRegisters(thread,1,verbose); + printThreadRegisters(thread, 0, verbose); + printThreadRegisters(thread, 1, verbose); } void ArchThreads::printThreadRegisters(Thread *thread, uint32 userspace_registers, bool verbose) { - ArchThreadRegisters *info = userspace_registers?thread->user_registers_:thread->kernel_registers_; + ArchThreadRegisters *info = (userspace_registers ? thread->user_registers_.get() : thread->kernel_registers_.get()); if (!info) { kprintfd("%sThread: %18p, has no %s registers. %s\n",userspace_registers?" User":"Kernel",thread,userspace_registers?"User":"Kernel",userspace_registers?"":"This should never(!) occur. How did you do that?"); @@ -142,14 +200,16 @@ void ArchThreads::printThreadRegisters(Thread *thread, uint32 userspace_register else if (verbose) { kprintfd("\t\t%sThread: %10p, info: %10p\n"\ - "\t\t\t eax: %10x ebx: %10x ecx: %10x edx: %10x\n"\ - "\t\t\t esp: %10x ebp: %10x esp0 %10x eip: %10x\n"\ - "\t\t\teflg: %10x cr3: %10x\n", - userspace_registers?" User":"Kernel",thread,info,info->eax,info->ebx,info->ecx,info->edx,info->esp,info->ebp,info->esp0,info->eip,info->eflags,info->cr3); + "\t\t\t eax: %#10x ebx: %#10x ecx: %#10x edx: %#10x\n"\ + "\t\t\t esi: %#10x edi: %#10x esp: %#10x ebp: %#10x\n"\ + "\t\t\tesp0: %#10x eip: %#10x eflg: %#10x cr3: %#10x\n"\ + "\t\t\t ds: %#10x ss: %#10x es: %#10x fs: %#10x\n"\ + "\t\t\t gs: %#10x\n", + userspace_registers?" User":"Kernel",thread,info,info->eax,info->ebx,info->ecx,info->edx,info->esi,info->edi,info->esp,info->ebp,info->esp0,info->eip,info->eflags,info->cr3,info->ds,info->ss,info->es,info->fs,info->gs); } else { - kprintfd("\t%sThread %10p: info %10p eax %10x ebp %10x esp %10x esp0 %10x eip %10x cr3 %10x\n", + kprintfd("\t%sThread %10p: info %10p eax %#10x ebp %#10x esp %#10x esp0 %#10x eip %#10x cr3 %#10x\n", userspace_registers?" User":"Kernel",thread,info,info->eax,info->ebp,info->esp,info->esp0,info->eip,info->cr3); } } @@ -178,3 +238,10 @@ void ArchThreads::debugCheckNewThread(Thread* thread) return; assert(currentThread->user_registers_->esp0 != thread->user_registers_->esp0 && "no 2 threads may have the same esp0 value"); } + + +[[noreturn]] void ArchThreads::startThreads(Thread* init_thread) +{ + contextSwitch(init_thread, init_thread->kernel_registers_.get()); + assert(false); +} diff --git a/arch/x86/32/common/source/CMakeLists.txt b/arch/x86/32/common/source/CMakeLists.txt index 1936acdc7..718b77bd2 100644 --- a/arch/x86/32/common/source/CMakeLists.txt +++ b/arch/x86/32/common/source/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC ../../../common/include ) diff --git a/arch/x86/32/common/source/InterruptDescriptorTable.cpp b/arch/x86/32/common/source/InterruptDescriptorTable.cpp new file mode 100644 index 000000000..dacbc12ca --- /dev/null +++ b/arch/x86/32/common/source/InterruptDescriptorTable.cpp @@ -0,0 +1,34 @@ +#include "InterruptDescriptorTable.h" + +#include "debug.h" + +#define LO_WORD(x) (((uint32)(x)) & 0x0000FFFF) +#define HI_WORD(x) ((((uint32)(x)) >> 16) & 0x0000FFFF) + +InterruptGateDesc::InterruptGateDesc(handler_func_t offset, uint8 dpl) : + segment_selector(KERNEL_CS), + unused(0), + type(INTERRUPT), + zero_1(0), + dpl(dpl), + present(1) +{ + setOffset(offset); +} + +void InterruptGateDesc::setOffset(handler_func_t offset) +{ + offset_low = LO_WORD(offset); + offset_high = HI_WORD(offset); +} + +handler_func_t InterruptGateDesc::offset() +{ + return (handler_func_t)(((uintptr_t)offset_high << 16) | ((uintptr_t)offset_low)); +} + +void IDTR::load() +{ + debug(A_INTERRUPTS, "Loading IDT, base: %zx, limit: %x\n", base, limit); + asm volatile("lidt (%0) " : : "q"(this)); +} diff --git a/arch/x86/32/common/source/InterruptUtils.cpp b/arch/x86/32/common/source/InterruptUtils.cpp index 5d09639b4..399c469a4 100644 --- a/arch/x86/32/common/source/InterruptUtils.cpp +++ b/arch/x86/32/common/source/InterruptUtils.cpp @@ -1,42 +1,32 @@ #include "InterruptUtils.h" -#include "ArchSerialInfo.h" +#include "8259.h" #include "BDManager.h" -#include "ArchMemory.h" -#include "ArchThreads.h" -#include "ArchCommon.h" -#include "kprintf.h" -#include "Scheduler.h" - -#include "SerialManager.h" +#include "Console.h" +#include "ErrorHandlers.h" #include "KeyboardManager.h" -#include "ArchInterrupts.h" -#include "backtrace.h" - -#include "Thread.h" #include "Loader.h" -#include "Syscall.h" -#include "paging-definitions.h" -#include "offsets.h" #include "PageFaultHandler.h" +#include "Scheduler.h" +#include "SerialManager.h" #include "Stabs2DebugInfo.h" +#include "Syscall.h" +#include "Thread.h" +#include "TimerTickHandler.h" +#include "backtrace.h" +#include "kprintf.h" +#include "offsets.h" +#include "paging-definitions.h" -#include "8259.h" - -#define LO_WORD(x) (((uint32)(x)) & 0x0000FFFF) -#define HI_WORD(x) ((((uint32)(x)) >> 16) & 0x0000FFFF) - -#define GATE_SIZE_16_BIT 0 // use 16- bit push -#define GATE_SIZE_32_BIT 1 // use 32- bit push - -#define TYPE_TRAP_GATE 7 // trap gate, i.e. IF flag is *not* cleared -#define TYPE_INTERRUPT_GATE 6 // interrupt gate, i.e. IF flag *is* cleared - -#define DPL_KERNEL_SPACE 0 // kernelspace's protection level -#define DPL_USER_SPACE 3 // userspaces's protection level - -#define SYSCALL_INTERRUPT 0x80 // number of syscall interrupt +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMemory.h" +#include "ArchMulticore.h" +#include "ArchSerialInfo.h" +#include "ArchThreads.h" +#include "assert.h" +#include "debug.h" // --- Pagefault error flags. // PF because/in/caused by/... @@ -56,177 +46,187 @@ #define FLAG_PF_INSTR_FETCH 0x10 // =0: not an instruction fetch // =1: an instruction fetch (need PAE for that) -struct GateDesc + +extern const Stabs2DebugInfo* kernel_debug_info; +extern eastl::atomic_flag assert_print_lock; + +// Standard ISA IRQs +// 0 Programmable Interrupt Timer Interrupt +// 1 Keyboard Interrupt +// 2 Cascade (used internally by the two PICs. never raised) +// 3 COM2 (if enabled) +// 4 COM1 (if enabled) +// 5 LPT2 (if enabled) +// 6 Floppy Disk +// 7 LPT1 / Unreliable "spurious" interrupt (usually) +// 8 CMOS real-time clock (if enabled) +// 9 Free for peripherals / legacy SCSI / NIC +// 10 Free for peripherals / SCSI / NIC +// 11 Free for peripherals / SCSI / NIC +// 12 PS2 Mouse +// 13 FPU / Coprocessor / Inter-processor +// 14 Primary ATA Hard Disk +// 15 Secondary ATA Hard Disk + +// generic interrupt handler -> dispatches to registered handlers for invoked interrupt number +void interruptHandler(size_t interrupt_num, + uint32_t error_code, + ArchThreadRegisters* saved_registers) { - uint16 offset_low; // low word of handler entry point's address - uint16 segment_selector; // (code) segment the handler resides in - uint8 reserved : 5; // reserved. set to zero - uint8 zeros : 3; // set to zero - uint8 type : 3; // set to TYPE_TRAP_GATE or TYPE_INTERRUPT_GATE - uint8 gate_size : 1; // set to GATE_SIZE_16_BIT or GATE_SIZE_32_BIT - uint8 unused : 1; // unsued - set to zero - uint8 dpl : 2; // descriptor protection level - uint8 present : 1; // present- flag - set to 1 - uint16 offset_high; // high word of handler entry point's address -}__attribute__((__packed__)); - -extern "C" void arch_dummyHandler(); -extern "C" void arch_dummyHandlerMiddle(); - -void InterruptUtils::initialise() + debugAdvanced(A_INTERRUPTS, "[CPU %zu] Interrupt handler %zu, error: %x\n", SMP::currentCpuId(), interrupt_num, error_code); + assert(interrupt_num < 256); + + if (interrupt_num == 0xE) + { + uintptr_t pagefault_addr = 0; + asm volatile("movl %%cr2, %[cr2]\n" : [cr2] "=r"(pagefault_addr)); + pageFaultHandler(pagefault_addr, error_code, saved_registers->eip); + } + else if (interrupt_num < 32) + { + errorHandler(interrupt_num, saved_registers->eip, saved_registers->cs, 0); + } + else + { + ArchInterrupts::startOfInterrupt(interrupt_num); + ArchInterrupts::handleInterrupt(interrupt_num); + ArchInterrupts::endOfInterrupt(interrupt_num); + } + + debugAdvanced(A_INTERRUPTS, "Interrupt handler %zu end\n", interrupt_num); +} + +// ISA IRQ 0 remapped to interrupt vector 32 +void int32_handler_PIT_irq0() { - uint32 num_handlers = 0; - for (uint32 i = 0; handlers[i].offset != 0; ++i) - num_handlers = handlers[i].number; - ++num_handlers; - // allocate some memory for our handlers - GateDesc *interrupt_gates = new GateDesc[num_handlers]; - size_t dummy_handler_sled_size = (((size_t) arch_dummyHandlerMiddle) - (size_t) arch_dummyHandler); - assert((dummy_handler_sled_size % 128) == 0 && "cannot handle weird padding in the kernel binary"); - dummy_handler_sled_size /= 128; - - uint32 j = 0; - for (uint32 i = 0; i < num_handlers; ++i) - { - while (handlers[j].number < i && handlers[j].offset != 0) - ++j; - interrupt_gates[i].offset_low = LO_WORD((handlers[j].number == i && handlers[j].offset != 0) ? (size_t)handlers[j].offset : (((size_t)arch_dummyHandler)+i*dummy_handler_sled_size)); - interrupt_gates[i].offset_high = HI_WORD((handlers[j].number == i && handlers[j].offset != 0) ? (size_t)handlers[j].offset : (((size_t)arch_dummyHandler)+i*dummy_handler_sled_size)); - interrupt_gates[i].gate_size = GATE_SIZE_32_BIT; - interrupt_gates[i].present = 1; - interrupt_gates[i].reserved = 0; - interrupt_gates[i].segment_selector = KERNEL_CS; - interrupt_gates[i].type = TYPE_INTERRUPT_GATE; - interrupt_gates[i].unused = 0; - interrupt_gates[i].zeros = 0; - interrupt_gates[i].dpl = ((i == SYSCALL_INTERRUPT && handlers[j].number == i) ? DPL_USER_SPACE : DPL_KERNEL_SPACE); - } + debugAdvanced(A_INTERRUPTS, "[CPU %zu] IRQ 0 called\n", SMP::currentCpuId()); - IDTR idtr; + ArchCommon::callWithStack( + ArchMulticore::cpuStackTop(), + []() + { + TimerTickHandler::handleTimerTick(); - idtr.base = (uint32) interrupt_gates; - idtr.limit = sizeof(GateDesc) * num_handlers - 1; - lidt(&idtr); -} + ((char*)ArchCommon::getFBPtr())[1 + SMP::currentCpuId()*2] = + ((currentThread->console_color << 4) | CONSOLECOLOR::BRIGHT_WHITE); -void InterruptUtils::lidt(IDTR *idtr) -{ - asm volatile("lidt (%0) ": :"q" (idtr)); + ArchInterrupts::endOfInterrupt(InterruptVector::REMAP_OFFSET + (uint8_t)ISA_IRQ::PIT); + contextSwitch(); + assert(false); + }); } -extern "C" void arch_irqHandler_0(); -extern "C" void arch_contextSwitch(); -extern "C" void irqHandler_0() +void int127_handler_APIC_timer() { - ++outstanding_EOIs; - ArchCommon::drawHeartBeat(); + debugAdvanced(A_INTERRUPTS, "[CPU %zu] Interrupt vector %u called\n", SMP::currentCpuId(), InterruptVector::APIC_TIMER); - Scheduler::instance()->incTicks(); + ArchCommon::callWithStack( + ArchMulticore::cpuStackTop(), + []() + { + TimerTickHandler::handleTimerTick(); - Scheduler::instance()->schedule(); - // kprintfd("irq0: Going to leave irq Handler 0\n"); - ArchInterrupts::EndOfInterrupt(0); - arch_contextSwitch(); + ((char*)ArchCommon::getFBPtr())[1 + SMP::currentCpuId() * 2] = + ((currentThread->console_color << 4) | CONSOLECOLOR::BRIGHT_WHITE); + + ArchInterrupts::endOfInterrupt(InterruptVector::APIC_TIMER); + contextSwitch(); + assert(false); + }); } -extern "C" void arch_irqHandler_65(); -extern "C" void irqHandler_65() +// yield +void int65_handler_swi_yield() { - Scheduler::instance()->schedule(); - // kprintfd("irq65: Going to leave int Handler 65 to user\n"); - arch_contextSwitch(); + debugAdvanced(A_INTERRUPTS, "Interrupt %u called on CPU %zu\n", InterruptVector::YIELD, SMP::currentCpuId()); + + ArchCommon::callWithStack(ArchMulticore::cpuStackTop(), + []() + { + Scheduler::instance()->schedule(); + ((char*)ArchCommon::getFBPtr())[1 + SMP::currentCpuId()*2] = + ((currentThread->console_color << 4) | + CONSOLECOLOR::BRIGHT_WHITE); + + ArchInterrupts::endOfInterrupt(InterruptVector::YIELD); + contextSwitch(); + assert(false); + }); } -extern "C" void arch_pageFaultHandler(); -extern "C" void pageFaultHandler(uint32 address, uint32 error) +void pageFaultHandler(uint32_t address, uint32_t error, uint32_t ip) { assert(!(error & FLAG_PF_RSVD) && "Reserved bit set in page table entry"); - PageFaultHandler::enterPageFault(address, error & FLAG_PF_USER, + PageFaultHandler::enterPageFault(address, + ip, + error & FLAG_PF_USER, error & FLAG_PF_PRESENT, error & FLAG_PF_RDWR, error & FLAG_PF_INSTR_FETCH); if (currentThread->switch_to_userspace_) - arch_contextSwitch(); + contextSwitch(); else asm volatile ("movl %%cr3, %%eax; movl %%eax, %%cr3;" ::: "%eax"); } -extern "C" void arch_irqHandler_1(); -extern "C" void irqHandler_1() +void int90_handler_halt_cpu() { - ++outstanding_EOIs; - KeyboardManager::instance()->serviceIRQ(); - ArchInterrupts::EndOfInterrupt(1); + while (assert_print_lock.test_and_set(eastl::memory_order_acquire)); + debug(A_INTERRUPTS, "IRQ %u called, cpu %zu halting\n", InterruptVector::IPI_HALT_CPU, SMP::currentCpuId()); + if (currentThread) + { + debug(BACKTRACE, "CPU %zu backtrace:\n", SMP::currentCpuId()); + currentThread->printBacktrace(false); + } + assert_print_lock.clear(eastl::memory_order_release); + + while(true) + { + ArchCommon::halt(); + } } -extern "C" void arch_irqHandler_3(); -extern "C" void irqHandler_3() +void int91_handler_APIC_error() { - kprintfd("IRQ 3 called\n"); - ++outstanding_EOIs; - SerialManager::getInstance()->service_irq(3); - ArchInterrupts::EndOfInterrupt(3); - kprintfd("IRQ 3 ended\n"); -} + auto error = cpu_lapic->readRegister(); + debugAlways(APIC, "Internal APIC error: %x\n", *(uint32_t*)&error); -extern "C" void arch_irqHandler_4(); -extern "C" void irqHandler_4() -{ - kprintfd("IRQ 4 called\n"); - ++outstanding_EOIs; - SerialManager::getInstance()->service_irq(4); - ArchInterrupts::EndOfInterrupt(4); - kprintfd("IRQ 4 ended\n"); -} + assert(!"Internal APIC error"); -extern "C" void arch_irqHandler_6(); -extern "C" void irqHandler_6() -{ - kprintfd("IRQ 6 called\n"); - kprintfd("IRQ 6 ended\n"); + cpu_lapic->writeRegister({}); } -extern "C" void arch_irqHandler_9(); -extern "C" void irqHandler_9() +void int100_handler_APIC_spurious() { - kprintfd("IRQ 9 called\n"); - ++outstanding_EOIs; - BDManager::getInstance()->serviceIRQ(9); - ArchInterrupts::EndOfInterrupt(9); + debug(A_INTERRUPTS, "IRQ %u called on CPU %zu, spurious APIC interrupt\n", InterruptVector::APIC_SPURIOUS, SMP::currentCpuId()); } -extern "C" void arch_irqHandler_11(); -extern "C" void irqHandler_11() +void int101_handler_cpu_fcall() { - kprintfd("IRQ 11 called\n"); - ++outstanding_EOIs; - BDManager::getInstance()->serviceIRQ(11); - ArchInterrupts::EndOfInterrupt(11); -} + debug(A_INTERRUPTS, "IRQ %u called by CPU %zu\n", InterruptVector::IPI_REMOTE_FCALL, SMP::currentCpuId()); -extern "C" void arch_irqHandler_14(); -extern "C" void irqHandler_14() -{ - //kprintfd( "IRQ 14 called\n" ); - ++outstanding_EOIs; - BDManager::getInstance()->serviceIRQ(14); - ArchInterrupts::EndOfInterrupt(14); -} + auto funcdata = current_cpu.fcall_queue.takeAll(); + while (funcdata != nullptr) + { + debug(A_INTERRUPTS, "CPU %zu: Function call request from CPU %zu\n", SMP::currentCpuId(), funcdata->orig_cpu); -extern "C" void arch_irqHandler_15(); -extern "C" void irqHandler_15() -{ - //kprintfd( "IRQ 15 called\n" ); - ++outstanding_EOIs; - BDManager::getInstance()->serviceIRQ(15); - ArchInterrupts::EndOfInterrupt(15); + funcdata->received.store(true, eastl::memory_order_release); + + assert(funcdata->target_cpu == SMP::currentCpuId()); + assert(funcdata->func); + + funcdata->func(); + + auto next = funcdata->next.load(); + funcdata->done.store(true, eastl::memory_order_release); // funcdata object is invalid as soon as it is acknowledged + funcdata = next; + } } -extern "C" void arch_syscallHandler(); -extern "C" void syscallHandler() +void syscallHandler() { currentThread->switch_to_userspace_ = 0; - currentThreadRegisters = currentThread->kernel_registers_; + currentThreadRegisters = currentThread->kernel_registers_.get(); ArchInterrupts::enableInterrupts(); auto ret = Syscall::syscallException(currentThread->user_registers_->eax, @@ -240,19 +240,15 @@ extern "C" void syscallHandler() ArchInterrupts::disableInterrupts(); currentThread->switch_to_userspace_ = 1; - currentThreadRegisters = currentThread->user_registers_; - //ArchThreads::printThreadRegisters(currentThread,false); - arch_contextSwitch(); + currentThreadRegisters = currentThread->user_registers_.get(); + contextSwitch(); } -extern Stabs2DebugInfo const *kernel_debug_info; -extern const char* errors[]; -extern "C" void arch_errorHandler(); -extern "C" void errorHandler(size_t num, size_t rip, size_t cs, size_t spurious) +void errorHandler(size_t num, size_t eip, size_t cs, size_t spurious) { if (spurious) { - assert(num < 128 && "there are only 128 interrupts"); + assert(num < 256 && "there are only 256 interrupts"); debug(CPU_ERROR, "Spurious Interrupt %zu (%zx)\n", num, num); } else @@ -262,21 +258,21 @@ extern "C" void errorHandler(size_t num, size_t rip, size_t cs, size_t spurious) } const bool userspace = (cs & 0x3); debug(CPU_ERROR, "Instruction Pointer: %zx, Userspace: %d - currentThread: %p %zd" ":%s, switch_to_userspace_: %d\n", - rip, userspace, currentThread, + eip, userspace, currentThread, currentThread ? currentThread->getTID() : -1, currentThread ? currentThread->getName() : 0, currentThread ? currentThread->switch_to_userspace_ : -1); const Stabs2DebugInfo* deb = kernel_debug_info; assert(currentThread && "there should be no fault before there is a current thread"); assert(currentThread->kernel_registers_ && "every thread needs kernel registers"); - ArchThreadRegisters* registers_ = currentThread->kernel_registers_; + const ArchThreadRegisters* registers_ = currentThread->kernel_registers_.get(); if (userspace) { assert(currentThread->loader_ && "User Threads need to have a Loader"); assert(currentThread->user_registers_ && (currentThread->user_registers_->cr3 == currentThread->kernel_registers_->cr3 && "User and Kernel CR3 register values differ, this most likely is a bug!")); deb = currentThread->loader_->getDebugInfos(); - registers_ = currentThread->user_registers_; + registers_ = currentThread->user_registers_.get(); } if(deb && registers_->eip) { @@ -289,17 +285,26 @@ extern "C" void errorHandler(size_t num, size_t rip, size_t cs, size_t spurious) if (spurious) { if (currentThread->switch_to_userspace_) - arch_contextSwitch(); + { + contextSwitch(); + assert(false); + } } else { currentThread->switch_to_userspace_ = false; - currentThreadRegisters = currentThread->kernel_registers_; + currentThreadRegisters = currentThread->kernel_registers_.get(); ArchInterrupts::enableInterrupts(); debug(CPU_ERROR, "Terminating process...\n"); - currentThread->kill(); + if (currentThread->user_registers_) + { + Syscall::exit(888); + } + else + { + currentThread->kill(); + } + + assert(false); } } - -#include "ErrorHandlers.h" // error handler definitions and irq forwarding definitions - diff --git a/arch/x86/32/common/source/SegmentUtils.cpp b/arch/x86/32/common/source/SegmentUtils.cpp index 1ace9d472..74547091a 100644 --- a/arch/x86/32/common/source/SegmentUtils.cpp +++ b/arch/x86/32/common/source/SegmentUtils.cpp @@ -1,68 +1,146 @@ #include "SegmentUtils.h" + +#include "Scheduler.h" #include "kstring.h" -typedef struct { - uint16 limitL; - uint16 baseL; - uint8 baseM; - uint8 typeL; - uint8 limitH : 4; - uint8 typeH : 4; - uint8 baseH; -} __attribute__((__packed__))SegmentDescriptor; - -SegmentDescriptor gdt[7]; -struct GDTPtr +#include "ArchCpuLocalStorage.h" +#include "ArchMulticore.h" + +#include "assert.h" +#include "debug.h" + +GDT gdt; +TSS g_tss; + +GDT32Ptr::GDT32Ptr(uint16 gdt_limit, uint32 gdt_addr) : + limit(gdt_limit), + addr(gdt_addr) +{} + +GDT32Ptr::GDT32Ptr(GDT& gdt) : + limit(sizeof(gdt.entries) - 1), + addr((uint32)(size_t)&gdt.entries) +{} + +void GDT32Ptr::load() { - uint16 limit; - uint32 addr; -} __attribute__((__packed__)) gdt_ptr; + asm("lgdt %[gdt_ptr]\n" + ::[gdt_ptr]"m"(*this)); +} -TSS *g_tss; +size_t SegmentDescriptor::getBase() +{ + return ((size_t)baseH << 24) | ((size_t)baseM << 16) | (size_t)baseL; +} -extern "C" void reload_segments() +void SegmentDescriptor::setBase(size_t base) { - // reload the gdt with the newly set up segments - asm("lgdt (%[gdt_ptr])" : : [gdt_ptr]"m"(gdt_ptr)); - // now prepare all the segment registers to use our segments - asm("mov %%ax, %%ds\n" - "mov %%ax, %%es\n" - "mov %%ax, %%ss\n" - "mov %%ax, %%fs\n" - "mov %%ax, %%gs\n" - : : "a"(KERNEL_DS)); - // jump onto the new code segment - asm("ljmp %[cs],$1f\n" - "1:": : [cs]"i"(KERNEL_CS)); + baseL = base & 0xFFFF; + baseM = (base >> 16) & 0xFF; + baseH = (base >> 24) & 0xFF; +} + +void SegmentDescriptor::setLimit(size_t limit) +{ + limitL = (uint16)(limit & 0xFFFF); + limitH = (uint8) (((limit >> 16U) & 0xF)); } static void setSegmentDescriptor(uint32 index, uint32 base, uint32 limit, uint8 dpl, uint8 code, uint8 tss) { - gdt[index].baseL = (uint16)(base & 0xFFFF); - gdt[index].baseM = (uint8)((base >> 16U) & 0xFF); - gdt[index].baseH = (uint8)((base >> 24U) & 0xFF); - gdt[index].limitL = (uint16)(limit & 0xFFFF); - gdt[index].limitH = (uint8) (((limit >> 16U) & 0xF)); - gdt[index].typeH = tss ? 0 : 0xC; // 4kb + 32bit - gdt[index].typeL = (tss ? 0x89 : 0x92) | (dpl << 5) | (code ? 0x8 : 0); // present bit + memory expands upwards + code + gdt.entries[index].setBase(base); + gdt.entries[index].setLimit(limit); + gdt.entries[index].typeH = tss ? 0 : 0xC; // 4kb + 32bit + gdt.entries[index].typeL = (tss ? 0x89 : 0x92) | (dpl << 5) | (code ? 0x8 : 0); // present bit + memory expands upwards + code +} + +void setTSSSegmentDescriptor(TSSSegmentDescriptor* descriptor, uint32 base, uint32 limit, uint8 dpl) +{ + debug(A_MULTICORE, "setTSSSegmentDescriptor at %p, base: %x, limit: %x, dpl: %x\n", descriptor, base, limit, dpl); + memset(descriptor, 0, sizeof(TSSSegmentDescriptor)); + descriptor->baseLL = (uint16) (base & 0xFFFF); + descriptor->baseLM = (uint8) ((base >> 16U) & 0xFF); + descriptor->baseLH = (uint8) ((base >> 24U) & 0xFF); + descriptor->limitL = (uint16) (limit & 0xFFFF); + descriptor->limitH = (uint8) (((limit >> 16U) & 0xF)); + descriptor->type = 0b1001; + descriptor->dpl = dpl; + descriptor->granularity = 0; + descriptor->present = 1; +} + +void TSS::setTaskStack(size_t stack_top) +{ + ss0 = KERNEL_SS; + esp0 = stack_top; } + void SegmentUtils::initialise() { - setSegmentDescriptor(2, 0, -1U, 0, 0, 0); - setSegmentDescriptor(3, 0, -1U, 0, 1, 0); - setSegmentDescriptor(4, 0, -1U, 3, 0, 0); - setSegmentDescriptor(5, 0, -1U, 3, 1, 0); - - g_tss = (TSS*)new uint8[sizeof(TSS)]; // new uint8[sizeof(TSS)]; - memset((void*)g_tss, 0, sizeof(TSS)); - g_tss->ss0 = KERNEL_SS; - g_tss->iobase = -1; - setSegmentDescriptor(6, (uint32)g_tss, sizeof(TSS)-1, 0, 0, 1); + setSegmentDescriptor(KERNEL_DS_ENTRY, 0, -1U, 0, 0, 0); + setSegmentDescriptor(KERNEL_CS_ENTRY, 0, -1U, 0, 1, 0); + setSegmentDescriptor(USER_DS_ENTRY, 0, -1U, 3, 0, 0); + setSegmentDescriptor(USER_CS_ENTRY, 0, -1U, 3, 1, 0); + setSegmentDescriptor(KERNEL_FS_ENTRY, 0, -1U, 0, 0, 0); + + memset(&g_tss, 0, sizeof(TSS)); + g_tss.ss0 = KERNEL_SS; + g_tss.iobase = -1; + setSegmentDescriptor(KERNEL_TSS_ENTRY, (uint32)&g_tss, sizeof(TSS)-1, 0, 0, 1); + // we have to reload our segment stuff - gdt_ptr.limit = sizeof(gdt) - 1; - gdt_ptr.addr = (uint32)gdt; - reload_segments(); + GDT32Ptr(gdt).load(); + + loadKernelSegmentDescriptors(); + int val = KERNEL_TSS; asm volatile("ltr %0\n" : : "m" (val)); + + debug(A_MULTICORE, "Setting temporary CLS for boot processor\n"); + extern char cls_start; + + CpuLocalStorage::setCls(gdt, &cls_start); + currentThread = nullptr; +} + +void SegmentUtils::loadKernelSegmentDescriptors() +{ + // gs+fs set to normal data segment for now, set to cpu local storage when cls is initialized + asm("mov %[ds], %%ds\n" + "mov %[ds], %%es\n" + "mov %[ds], %%ss\n" + "mov %[ds], %%fs\n" + "mov %[ds], %%gs\n" + "ljmp %[cs], $1f\n" + "1:\n" + ::[ds]"a"(KERNEL_DS), + [cs]"i"(KERNEL_CS)); +} + +size_t getGSBase(GDT& gdt) +{ + return gdt.entries[KERNEL_GS_ENTRY].getBase(); +} + +size_t getFSBase(GDT& gdt) +{ + return gdt.entries[KERNEL_FS_ENTRY].getBase(); +} + + +void setGSBase(GDT& gdt, size_t gs_base) +{ + gdt.entries[KERNEL_FS_ENTRY].setBase(gs_base); + asm("mov %[gs], %%gs\n" + : + :[gs]"a"(KERNEL_GS)); +} + +void setFSBase(GDT& gdt, size_t fs_base) +{ + gdt.entries[KERNEL_FS_ENTRY].setBase(fs_base); + asm("mov %[fs], %%fs\n" + : + :[fs]"a"(KERNEL_FS)); } diff --git a/arch/x86/32/common/source/arch_backtrace.cpp b/arch/x86/32/common/source/arch_backtrace.cpp index 1b41278bd..d65e2bde5 100644 --- a/arch/x86/32/common/source/arch_backtrace.cpp +++ b/arch/x86/32/common/source/arch_backtrace.cpp @@ -1,16 +1,18 @@ -#include "kprintf.h" -#include "Thread.h" -#include "backtrace.h" +#include "arch_backtrace.h" + #include "InterruptUtils.h" -#include "ArchThreads.h" #include "KernelMemoryManager.h" // for use of "kernel_end_address" -#include "umap.h" +#include "Loader.h" +#include "Scheduler.h" +#include "Thread.h" +#include "backtrace.h" +#include "kprintf.h" + #include "ArchCommon.h" #include "ArchMemory.h" -#include "Loader.h" -#include "arch_backtrace.h" +#include "ArchThreads.h" -extern Thread* currentThread; +#include "EASTL/map.h" struct StackFrame { diff --git a/arch/x86/32/common/source/arch_interrupts.S b/arch/x86/32/common/source/arch_interrupts.S index 63487d75d..75820465c 100644 --- a/arch/x86/32/common/source/arch_interrupts.S +++ b/arch/x86/32/common/source/arch_interrupts.S @@ -1,186 +1,77 @@ +# https://www.airs.com/blog/archives/518 +.section .note.GNU-stack,"",@progbits + # ok, this is our main interrupt handling stuff .code32 .text .equ KERNEL_DS, 0x10 +.equ KERNEL_FS, 0x38 +.equ KERNEL_GS, 0x38 + +.macro loadKernelSegments + movw $KERNEL_DS, %ax + movw %ax,%es + movw %ax,%ds + movw $KERNEL_FS, %ax + movw %ax,%fs + movw $KERNEL_GS, %ax + movw %ax,%gs +.endm .macro pushAll pushal push %ds push %es - movw $KERNEL_DS, %ax - movw %ax,%es - movw $KERNEL_DS, %ax - movw %ax,%ds + push %fs + push %gs + loadKernelSegments .endm .macro popAll + pop %gs + pop %fs pop %es pop %ds popal .endm .extern arch_saveThreadRegisters -.extern arch_saveThreadRegistersForPageFault - -.macro irqhandler num -.global arch_irqHandler_\num -.extern irqHandler_\num -.stabs "arch_irqHandler_\num()",36,0,0,arch_irqHandler_\num -arch_irqHandler_\num: - pushall - pushl %ebp - movl %esp,%ebp - pushl $0 - call arch_saveThreadRegisters - leave - call irqHandler_\num - popall - iretl -.endm - -dummyhandlerscratchvariable: - .long 0 - .long 0 - -.extern dummyHandler -.global arch_dummyHandler -arch_dummyHandler: -.rept 128 - call arch_dummyHandlerMiddle -.endr -.extern dummyHandlerMiddle -.global arch_dummyHandlerMiddle -arch_dummyHandlerMiddle: - pushall - pushl %ebp - movl %esp,%ebp - pushl $0 - call arch_saveThreadRegisters - leave - pushl %ebp - movl %esp,%ebp - pushl 44(%esp) - call arch_computeDummyHandler - mov %eax,%ebx - leave - pushl %ebp - movl %esp,%ebp - subl $16,%esp - movl $1, 12(%esp) - movl 64(%esp),%eax - movl %eax, 8(%esp) - movl 60(%esp),%eax - movl %eax, 4(%esp) - movl %ebx, 0(%esp) - call errorHandler - leave - popall - addl $4,%esp - iretl - hlt - -.extern errorHandler -.macro errorhandler num -.global arch_errorHandler_\num -arch_errorHandler_\num: - pushall - pushl %ebp - movl %esp,%ebp - pushl $0 - call arch_saveThreadRegisters - leave - pushl %ebp - movl %esp,%ebp - subl $16,%esp - movl $0, 12(%esp) - movl 64(%esp),%eax - movl %eax, 8(%esp) - movl 60(%esp),%eax - movl %eax, 4(%esp) - movl $\num,0(%esp) - call errorHandler - leave - popall - iretl - hlt -.endm - -.macro errorhandlerWithCode num -.global arch_errorHandler_\num -arch_errorHandler_\num: - pushall - pushl %ebp - movl %esp,%ebp - pushl $1 - call arch_saveThreadRegisters - leave - pushl %ebp - movl %esp,%ebp - subl $16,%esp - movl $0, 12(%esp) - movl 68(%esp),%eax - movl %eax, 8(%esp) - movl 64(%esp),%eax - movl %eax, 4(%esp) - movl $\num,0(%esp) - call errorHandler - leave - popall - addl $4,%esp - iretl - hlt -.endm - - -.extern pageFaultHandler -.global arch_pageFaultHandler -.stabs "arch_pageFaultHandler()",36,0,0,arch_pageFaultHandler -arch_pageFaultHandler: - #we are already on a new stack because a privliedge switch happened - pushall - pushl %ebp - movl %esp,%ebp - pushl $1 - call arch_saveThreadRegisters - leave - pushl %ebp - movl %esp,%ebp - subl $8,%esp - movl 52(%esp),%eax # error cd - movl %eax, 4(%esp) - movl %cr2, %eax # page fault address - movl %eax, 0(%esp) - call pageFaultHandler - leave - popall - addl $4,%esp - iretl - hlt - - -.irp num,0,1,3,4,6,9,11,14,15,65 -irqhandler \num -.endr - -.irp num,8,10,11,12,13,14,17 -errorhandlerWithCode \num -.endr -.irp num,0,4,5,6,7,9,16,18,19 -errorhandler \num -.endr -.global arch_syscallHandler -.extern syscallHandler -.stabs "arch_syscallHandler",36,0,0,arch_syscallHandler -arch_syscallHandler: - pushall - pushl %ebp - movl %esp,%ebp - pushl $0 - call arch_saveThreadRegisters - leave - call syscallHandler - hlt +/* + Called by interrupt_entrystub(), which pushes the interrupt number + as well as a fake error code onto the stack if it wasn't generated by the cpu +*/ +.global arch_interruptHandler +.type arch_interruptHandler, @function +arch_interruptHandler: + pushall + movl %esp, %edx + ## pushl %esp + + ## pushl $0 + ## call arch_saveThreadRegisters + ## addl $8, %esp + + + movl 56(%esp), %eax + pushl %eax /* fake return address = rip at interrupt */ + /* set up fake call stack for debugger backtrace */ +.global arch_interruptHandler_backtrace_fix +.type arch_interruptHandler_backtrace_fix, @function +arch_interruptHandler_backtrace_fix: + pushl %ebp + movl %esp, %ebp + + pushl %edx /* address of saved registers */ + call genericInterruptEntry + + leave + addl $4, %esp /* pop simulated return address */ + + popall + addl $8, %esp /* pop interrupt number and error code */ + iretl + hlt diff --git a/arch/x86/32/common/source/boot.cpp b/arch/x86/32/common/source/boot.cpp index 6c5093a45..1e3c809be 100644 --- a/arch/x86/32/common/source/boot.cpp +++ b/arch/x86/32/common/source/boot.cpp @@ -1,14 +1,19 @@ -#include "types.h" -#include "debug.h" +#include "SegmentUtils.h" +#include "bootprint.h" #include "debug_bochs.h" +#include "kstring.h" #include "multiboot.h" #include "offsets.h" +#include "paging-definitions.h" + #include "ArchCommon.h" #include "ArchMemory.h" -#include "paging-definitions.h" -#include "kstring.h" -#define PRINT(X) do { if (A_BOOT & OUTPUT_ENABLED) { writeLine2Bochs(VIRTUAL_TO_PHYSICAL_BOOT(X)); } } while (0) +#include "types.h" + +#include "debug.h" + +#define PRINT(X) do { if (A_BOOT & OUTPUT_ENABLED) { writeLine2Bochs((char*)VIRTUAL_TO_PHYSICAL_BOOT(X)); kputs((char*)VIRTUAL_TO_PHYSICAL_BOOT(X)); } } while (0) #define MULTIBOOT_PAGE_ALIGN (1<<0) #define MULTIBOOT_MEMORY_INFO (1<<1) @@ -32,15 +37,20 @@ extern uint8 bss_start_address; extern uint8 bss_end_address; extern uint8 boot_stack[]; + + extern "C" void parseMultibootHeader(); extern "C" void initialiseBootTimePaging(); extern "C" void startup(); extern "C" void entry() { + asm("mov %%ebx,%0": "=m"(*((multiboot_info_t**)VIRTUAL_TO_PHYSICAL_BOOT((pointer)&multi_boot_structure_pointer)))); + PRINT("Booting...\n"); + PRINT("Clearing Framebuffer...\n"); memset((void*)(ArchCommon::getFBPtr(0)), 0, 80 * 25 * 2); @@ -66,25 +76,29 @@ extern "C" void entry() } PRINT("Setting CR3 Register...\n"); - asm("mov %[pd],%%cr3" : : [pd]"r"(VIRTUAL_TO_PHYSICAL_BOOT((pointer)ArchMemory::getRootOfKernelPagingStructure()))); + asm("mov %[pd],%%cr3" : : [pd]"r"(VIRTUAL_TO_PHYSICAL_BOOT((pointer)ArchMemory::getKernelPagingStructureRootVirt()))); PRINT("Enable Page Size Extensions...\n"); uint32 cr4; asm("mov %%cr4,%[v]\n" : [v]"=r"(cr4)); - cr4 |= 0x10; + cr4 |= 0x10; // PSE if (PAGE_DIRECTORY_ENTRIES == 512) - cr4 |= 0x20; + cr4 |= 0x20; // PAE asm("mov %[v],%%cr4\n" : : [v]"r"(cr4)); - PRINT("Enable Paging...\n"); + PRINT("Enable Paging...\n"); // Enable paging, write protect in ring 0 and protected mode asm("mov %cr0,%eax\n" "or $0x80010001,%eax\n" "mov %eax,%cr0\n"); + PRINT("Switch to our own stack...\n"); asm("mov %[v],%%esp\n" "mov %%esp,%%ebp\n" : : [v]"i"(boot_stack + 0x4000)); + SegmentUtils::initialise(); + + PRINT("Calling startup()...\n"); asm("jmp *%%eax" : : "a"(startup)); diff --git a/arch/x86/32/common/source/bootprint.cpp b/arch/x86/32/common/source/bootprint.cpp new file mode 100644 index 000000000..09deada5b --- /dev/null +++ b/arch/x86/32/common/source/bootprint.cpp @@ -0,0 +1,117 @@ +#include "debug_bochs.h" +#include "kstring.h" +#include "offsets.h" + +#include "ArchCommon.h" + +#include "types.h" + +uint8 fb_row = 0; +uint8 fb_col = 0; + +char* getFBAddr(uint8 row, uint8 col) +{ + return (char*)ArchCommon::getFBPtr(0) + ((row*80 + col) * 2); +} + +void setFBrow(uint8 row) +{ + *(uint8*)VIRTUAL_TO_PHYSICAL_BOOT(&fb_row) = row; +} +void setFBcol(uint8 col) +{ + *(uint8*)VIRTUAL_TO_PHYSICAL_BOOT(&fb_col) = col; +} + +uint8 getFBrow() +{ + return *(uint8*)VIRTUAL_TO_PHYSICAL_BOOT(&fb_row); +} +uint8 getFBcol() +{ + return *(uint8*)VIRTUAL_TO_PHYSICAL_BOOT(&fb_col); +} + +uint8 getNextFBrow() +{ + return (getFBrow() == 24 ? 0 : getFBrow() + 1); +} + +void clearFB() +{ + memset(getFBAddr(0, 0), 0, 80 * 25 * 2); + setFBrow(0); + setFBcol(0); +} + + + +void clearFBrow(uint8 row) +{ + memset(getFBAddr(row, 0), 0, 80 * 2); +} + +void FBnewline() +{ + uint8 next_row = getNextFBrow(); + clearFBrow(next_row); + setFBrow(next_row); + setFBcol(0); +} + +void kputc(const char c) +{ + //writeChar2Bochs('C'); + if(c == '\n') + { + FBnewline(); + } + else + { + if(getFBcol() == 80) + { + FBnewline(); + } + + uint32 row = getFBrow(); + uint32 col = getFBcol(); + + char* fb_pos = getFBAddr(row, col); + fb_pos[0] = c; + fb_pos[1] = 0x02; + + setFBcol(getFBcol() + 1); + } +} + +void kputs(const char* string) +{ + while(*string != '\0') + { + kputc(*string); + ++string; + } +} + +uint8 nibbleToASCII(char nibble) +{ + nibble &= 0xF; + return nibble < 10 ? '0' + nibble : + 'A' + nibble - 10; +} + +void putHex8(char c) +{ + char nibble_l = c & 0xF; + char nibble_h = c >> 4; + kputc(nibbleToASCII(nibble_h)); + kputc(nibbleToASCII(nibble_l)); +} + +void putHex32(uint32 v) +{ + for(uint8 i = 1; i <= sizeof(v); ++i) + { + putHex8(*((char*)&v + sizeof(v) - i)); + } +} diff --git a/arch/x86/32/common/userspace/CMakeLists.txt b/arch/x86/32/common/userspace/CMakeLists.txt index 07c5172fc..cdb6508f9 100644 --- a/arch/x86/32/common/userspace/CMakeLists.txt +++ b/arch/x86/32/common/userspace/CMakeLists.txt @@ -1,4 +1,6 @@ -FILE(GLOB userspace_libc_SOURCES ${CMAKE_CURRENT_LIST_DIR}/*.c) +cmake_minimum_required(VERSION 3.11) + +FILE(GLOB userspace_libc_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.c) target_sources(userspace_libc PRIVATE diff --git a/arch/x86/32/include/ArchMemory.h b/arch/x86/32/include/ArchMemory.h index 167f9896f..83a751f37 100644 --- a/arch/x86/32/include/ArchMemory.h +++ b/arch/x86/32/include/ArchMemory.h @@ -1,40 +1,63 @@ #pragma once -#include "types.h" #include "paging-definitions.h" +#include "types.h" + #define RESOLVEMAPPING(pd,vpage) ;\ PageDirEntry* page_directory = (PageDirEntry*) ArchMemory::getIdentAddressOfPPN(pd);\ uint32 pde_vpn = (vpage % (PAGE_TABLE_ENTRIES * PAGE_DIRECTORY_ENTRIES)) / PAGE_TABLE_ENTRIES;\ uint32 pte_vpn = (vpage % (PAGE_TABLE_ENTRIES * PAGE_DIRECTORY_ENTRIES)) % PAGE_TABLE_ENTRIES; -extern PageDirEntry kernel_page_directory[]; -extern PageTableEntry kernel_page_tables[]; +extern PageDirEntry kernel_page_directory[PAGE_DIRECTORY_ENTRIES]; +extern PageTableEntry kernel_page_tables[4 * PAGE_TABLE_ENTRIES]; + +union VAddr +{ + size_t addr; + struct + { + size_t offset :12; + size_t pti :10; + size_t pdi :10; + }; +}; + +class ArchMemoryMapping +{ +public: + PageDirEntry* pd; + PageTableEntry* pt; + pointer page; + + ppn_t pd_ppn; + ppn_t pt_ppn; + ppn_t page_ppn; + + size_t page_size; + + size_t pdi; + size_t pti; +}; class ArchMemory { public: ArchMemory(); + ArchMemory(ppn_t page_dir_ppn); + ~ArchMemory(); -/** - * - * maps a virtual page to a physical page (pde and pte need to be set up first) - * - * @param virtual_page - * @param physical_page - * @param user_access PTE User/Supervisor Flag, governing the binary Paging - * Privilege Mechanism - */ - __attribute__((warn_unused_result)) bool mapPage(uint32 virtual_page, uint32 physical_page, uint32 user_access); -/** - * removes the mapping to a virtual_page by marking its PTE Entry as non valid - * - * @param virtual_page which will be invalidated - */ - void unmapPage(uint32 virtual_page); + [[nodiscard]] + bool mapPage(vpn_t virtual_page, ppn_t physical_page, bool user_access); + void unmapPage(vpn_t virtual_page); - ~ArchMemory(); + [[nodiscard]] + static bool mapKernelPage(vpn_t virtual_page, ppn_t physical_page, bool can_alloc_pages = false, bool memory_mapped_io = false); + static void unmapKernelPage(vpn_t virtual_page, bool free_page = true); + + void printMappedPages(); + static void printMappedPages(uint32 page_dir_page); /** * Takes a Physical Page Number in Real Memory and returns a virtual address than @@ -45,15 +68,25 @@ class ArchMemory * @return Virtual Address above 3GB pointing to the start of a memory segment that * is mapped to the physical page given */ - static pointer getIdentAddressOfPPN(uint32 ppn, uint32 page_size=PAGE_SIZE); + static pointer getIdentAddressOfPPN(ppn_t ppn, size_t page_size = PAGE_SIZE); + static pointer getIdentAddress(size_t address); /** - * Checks if a given Virtual Address is valid and is mapped to real memory + * Checks if a given Virtual Address is valid and is mapped to physical memory + * @param pd page directory ppn * @param vaddress_to_check Virtual Address we want to check - * @return paddr: if mapping exists\nzero: if the given virtual address is unmapped - * and accessing it would result in a pageFault + * @return physical address if the virtual address is mapped, zero otherwise */ - pointer checkAddressValid(uint32 vaddress_to_check); + pointer checkAddressValid(size_t vaddress_to_check) const; + static pointer checkAddressValid(ppn_t pd, size_t vaddress_to_check); + + const ArchMemoryMapping resolveMapping(vpn_t vpage) const; + static const ArchMemoryMapping resolveMapping(ppn_t pd, vpn_t vpage); + + size_t getPagingStructureRootPhys() const; + size_t getValueForCR3() const; + + /** * Takes a virtual_page and search through the pageTable and pageDirectory for the @@ -66,61 +99,29 @@ class ArchMemory * @return 0: if the virtual page doesn't map to any physical page\notherwise * returns the page size in byte (4096 for 4KiB pages or 4096*1024 for 4MiB pages) */ - static uint32 get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, uint32 *physical_page, uint32 *physical_pte_page=0); - -/** - * - * maps a virtual page to a physical page in kernel mapping - * - * @param virtual_page - * @param physical_page - */ - static void mapKernelPage(uint32 virtual_page, uint32 physical_page); - -/** - * removes the mapping to a virtual_page by marking its PTE Entry as non valid - * in kernel mapping - * - * @param virtual_page which will be invalidated - */ - static void unmapKernelPage(uint32 virtual_page); + static uint32 get_PPN_Of_VPN_In_KernelMapping(vpn_t virtual_page, uint32* physical_page, uint32* physical_pte_page=0); -/** - * ppn of the page dir page - */ - uint32 page_dir_page_; + static PageDirEntry* getKernelPagingStructureRootVirt(); + static size_t getKernelPagingStructureRootPhys(); + static void loadPagingStructureRoot(size_t cr3_value); - uint32 getRootOfPagingStructure(); - uint32 getValueForCR3(); - static PageDirEntry* getRootOfKernelPagingStructure(); + static void flushLocalTranslationCaches(size_t addr); + static void flushAllTranslationCaches(size_t addr); - static const size_t RESERVED_START = 0x80000ULL; - static const size_t RESERVED_END = 0xC0000ULL; + static ArchMemory& kernelArchMemory(); private: + ArchMemory& operator=(const ArchMemory& src) = delete; // should never be implemented -/** - * Adds a page directory entry to the given page directory. - * (In other words, adds the reference to a new page table to a given - * page directory.) - * - * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. - * @param physical_page_table_page physical page of the new page table. - */ - void insertPT(uint32 pde_vpn, uint32 physical_page_table_page); + template static bool tableEmpty(T* table); -/** - * Removes a page directory entry from a given page directory if it is present - * in the first place. Futhermore, the target page table is assured to be - * empty. - * - * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. - */ - void checkAndRemovePT(uint32 pde_vpn); + template static void removeEntry(T* map, size_t index); - ArchMemory(ArchMemory const &src); // not yet implemented - ArchMemory &operator=(ArchMemory const &src); // should never be implemented + template + void insert(T* table, size_t index, ppn_t ppn, bool user_access, bool writeable); + /** + * ppn of the page dir page + */ + ppn_t page_dir_page_; }; - - diff --git a/arch/x86/32/include/paging-definitions.h b/arch/x86/32/include/paging-definitions.h index 3eaab30b9..87f13a5b2 100644 --- a/arch/x86/32/include/paging-definitions.h +++ b/arch/x86/32/include/paging-definitions.h @@ -32,7 +32,7 @@ struct PageDirPageTableEntry size_t ignored_3 :1; size_t ignored_2 :1; size_t ignored_1 :1; - size_t page_table_ppn :20; + size_t page_ppn :20; } __attribute__((__packed__)); static_assert(sizeof(PageDirPageTableEntry) == 4, "PageDirPageTableEntry is not 32 bit"); @@ -65,6 +65,8 @@ typedef union struct PageDirPageEntry page; } __attribute__((__packed__)) PageDirEntry; +using PageDir = PageDirEntry[PAGE_DIRECTORY_ENTRIES]; + typedef struct { size_t present :1; @@ -84,3 +86,4 @@ typedef struct static_assert(sizeof(PageDirPageEntry) == 4, "PageTableEntry is not 32 bit"); +using PageTable = PageTableEntry[PAGE_TABLE_ENTRIES]; diff --git a/arch/x86/32/pae/CMakeLists.include b/arch/x86/32/pae/CMakeLists.include index 370924643..6cf580946 100644 --- a/arch/x86/32/pae/CMakeLists.include +++ b/arch/x86/32/pae/CMakeLists.include @@ -1,50 +1,85 @@ +cmake_minimum_required(VERSION 3.13) + set(KERNEL_BINARY kernel.x) -set(ARCH_X86_32_PAE_KERNEL_CFLAGS -m32 -O0 -gstabs2 -Wall -Wextra -Werror -Wno-error=format -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -mno-mmx -mno-sse2 -mno-sse3 ${NOPICFLAG}) +set(ARCH_X86_32_PAE_KERNEL_CFLAGS -O0 -gdwarf-4 -Wall -Wextra -Werror -Wno-error=format -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -mno-mmx -mno-sse2 -mno-sse3 ${NOPICFLAG}) -set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++11 -Wno-nonnull-compare -nostdinc++ -fno-rtti ${ARCH_X86_32_PAE_KERNEL_CFLAGS}) +set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++20 -Wno-nonnull-compare -nostdinc++ -fno-rtti ${ARCH_X86_32_PAE_KERNEL_CFLAGS}) set(KERNEL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -std=gnu11 ${ARCH_X86_32_PAE_KERNEL_CFLAGS}) -set(ARCH_LD_ARGUMENTS -m32 -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -Wl,-melf_i386 -nostdinc -nostdlib -nodefaultlibs) -set(KERNEL_LD_ARGUMENT ${ARCH_LD_ARGUMENTS} -mcmodel=kernel ${NOPIEFLAG}) -set(ARCH_APPEND_LD_ARGUMENTS ) +target_compile_options(arch_options INTERFACE + -m32) + +target_link_options(arch_options INTERFACE + -m32 -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -Wl,-melf_i386 -nostdinc -nostdlib -nodefaultlibs) + + +target_link_options(kernel_options INTERFACE + -mcmodel=kernel ${NOPIEFLAG}) + +target_compile_options(kernel_options INTERFACE + $<$:${KERNEL_CMAKE_CXX_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + ) MACRO(ARCH2OBJ ARCHOBJ_LIBNAME LIBRARY_NAME) ENDMACRO(ARCH2OBJ) -set(KERNEL_IMAGE_OBJCOPY ) +if ("${DEBUG}" STREQUAL "1") + set(STRIP_DEBUG_INFO "") +else() + set(STRIP_DEBUG_INFO ${OBJCOPY_EXECUTABLE} $ --strip-unneeded $) +endif() + +add_custom_command(TARGET kernel + POST_BUILD + BYPRODUCTS kernel.dbg kernel.unstripped + COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_BINARY_DIR}/kernel.unstripped + COMMAND ${PROJECT_BINARY_DIR}/add-dbg $ ${PROJECT_BINARY_DIR}/kernel.dbg + COMMAND ${STRIP_DEBUG_INFO}) + +add_dependencies(kernel add-dbg) + + +set(AVAILABLE_MEMORY 8M) +set(NUM_CPUS 4) + +set(DISK_MOUNT_ARG -drive file=${HDD_IMAGE},index=0,media=disk,id=sweb-hdd,if=ide) + +set(QEMU_BIN qemu-system-i386) +set(QEMU_FLAGS_COMMON -m ${AVAILABLE_MEMORY} ${DISK_MOUNT_ARG} -smp ${NUM_CPUS} -debugcon stdio -no-reboot -no-shutdown -s -d guest_errors) +string(REPLACE ";" " " QEMU_FLAGS_COMMON_STR "${QEMU_FLAGS_COMMON}") # kvm: Run kvm in non debugging mode add_custom_target(kvm - COMMAND qemu-system-i386 -m 8M -cpu kvm32 -drive file=${HDD_IMAGE},index=0,media=disk -debugcon stdio -no-reboot - COMMENT "Executing `qemu-system-i386 -m 8M -cpu kvm32 -drive file=${HDD_IMAGE},index=0,media=disk -debugcon stdio -no-reboot`" + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -enable-kvm -cpu kvm32 + COMMENT "Executing `${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -enable-kvm -cpu kvm32`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I ) # qemu: Run qemu in non debugging mode add_custom_target(qemu - COMMAND qemu-system-i386 -m 8M -cpu qemu32 -drive file=${HDD_IMAGE},index=0,media=disk -debugcon stdio -no-reboot - COMMENT "Executing `qemu-system-i386 -m 8M -cpu qemu32 -drive file=${HDD_IMAGE},index=0,media=disk -debugcon stdio -no-reboot`" + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -cpu qemu32 + COMMENT "Executing `${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -cpu qemu32`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I ) -# qemugdb: Run qemu in non debugging mode +# qemugdb: Run qemu in debugging mode add_custom_target(qemugdb - COMMAND qemu-system-i386 -s -S -m 8M -drive file=${HDD_IMAGE},index=0,media=disk -debugcon stdio -no-reboot - COMMENT "Executing `gdb qemu-system-i386 -s -S -m 8M -drive file=${HDD_IMAGE},index=0,media=disk -debugcon stdio -no-reboot on localhost:1234`" + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -S + COMMENT "Executing `gdb ${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -S on localhost:1234`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I ) # qemutacos: Run qemu in pipe monitor mode for tacos add_custom_target(qemutacos - COMMAND qemu-system-i386 -hda ${HDD_IMAGE} -m 8M -snapshot -monitor pipe:qemu -nographic -debugcon stdio - COMMENT "Executing `qemu-system-i386 -hda ${HDD_IMAGE} -m 8M -snapshot -monitor pipe:qemu -nographic -debugcon stdio -no-reboot`" + COMMAND ${QEMU_BIN} -hda ${HDD_IMAGE} -m ${AVAILABLE_MEMORY} -snapshot -monitor pipe:qemu -nographic -debugcon stdio + COMMENT "Executing `qemu-system-i386 -hda ${HDD_IMAGE} -m ${AVAILABLE_MEMORY} -snapshot -monitor pipe:qemu -nographic -debugcon stdio -no-reboot`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} ) - - diff --git a/arch/x86/32/pae/CMakeLists.txt b/arch/x86/32/pae/CMakeLists.txt index 7ee38fa46..bcc9f2712 100644 --- a/arch/x86/32/pae/CMakeLists.txt +++ b/arch/x86/32/pae/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC ../../../common/include ../../common/include ../common/include @@ -7,5 +7,3 @@ include_directories( ) add_subdirectory(source) - - diff --git a/arch/x86/32/pae/CMakeLists.userspace b/arch/x86/32/pae/CMakeLists.userspace index 008c77c40..c19de8bce 100644 --- a/arch/x86/32/pae/CMakeLists.userspace +++ b/arch/x86/32/pae/CMakeLists.userspace @@ -1,2 +1,10 @@ -set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -std=gnu11 -gstabs2 -fno-omit-frame-pointer -O0 -m32 -static -nostdinc -fno-builtin -nostdlib -fno-stack-protector -fno-common -Werror=implicit-function-declaration -Wno-error=unused-variable -fno-stack-clash-protection) -set(ARCH_USERSPACE_LINKER_OPTIONS -static) +set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -gdwarf-4 -fno-omit-frame-pointer -O0 -static -nostdinc -fno-builtin -nostdlib -nolibc -fno-stack-protector -fno-common -Wno-error=unused-variable -fno-stack-clash-protection) + +target_link_options(userspace_options INTERFACE + -static) + +target_compile_options(userspace_options INTERFACE + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu++20 -nostdinc++> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu11 -Werror=implicit-function-declaration> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS}> +) diff --git a/arch/x86/32/pae/include/ArchMemory.h b/arch/x86/32/pae/include/ArchMemory.h index ac1a84ecb..b0d0774b8 100644 --- a/arch/x86/32/pae/include/ArchMemory.h +++ b/arch/x86/32/pae/include/ArchMemory.h @@ -1,51 +1,53 @@ #pragma once -#include "types.h" #include "paging-definitions.h" +#include "types.h" + #define RESOLVEMAPPING(pdpt,vpage) ;\ uint32 pdpte_vpn = vpage / (PAGE_TABLE_ENTRIES * PAGE_DIRECTORY_ENTRIES);\ - PageDirEntry* page_directory = (PageDirEntry*) ArchMemory::getIdentAddressOfPPN(pdpt[pdpte_vpn].page_directory_ppn);\ + PageDirEntry* page_directory = (PageDirEntry*) ArchMemory::getIdentAddressOfPPN(pdpt[pdpte_vpn].page_ppn);\ uint32 pde_vpn = (vpage % (PAGE_TABLE_ENTRIES * PAGE_DIRECTORY_ENTRIES)) / PAGE_TABLE_ENTRIES;\ uint32 pte_vpn = (vpage % (PAGE_TABLE_ENTRIES * PAGE_DIRECTORY_ENTRIES)) % PAGE_TABLE_ENTRIES; -extern PageDirPointerTableEntry kernel_page_directory_pointer_table[]; -extern PageDirEntry kernel_page_directory[]; -extern PageTableEntry kernel_page_tables[]; +extern PageDirPointerTableEntry kernel_page_directory_pointer_table[PAGE_DIRECTORY_POINTER_TABLE_ENTRIES]; +extern PageDirEntry kernel_page_directory[4 * PAGE_DIRECTORY_ENTRIES]; +extern PageTableEntry kernel_page_tables[8 * PAGE_TABLE_ENTRIES]; -class ArchMemory +class ArchMemoryMapping { public: - ArchMemory(); + PageDirPointerTableEntry* pdpt; + PageDirEntry* pd; + PageTableEntry* pt; + pointer page; -/** - * - * maps a virtual page to a physical page (pde and pte need to be set up first) - * - * @param physical_page_directory_page Real Page where the PDE to work on resides - * @param virtual_page - * @param physical_page - * @param user_access PTE User/Supervisor Flag, governing the binary Paging - * Privilege Mechanism - */ - __attribute__((warn_unused_result)) bool mapPage(uint32 virtual_page, uint32 physical_page, uint32 user_access); + ppn_t pd_ppn; + ppn_t pt_ppn; + ppn_t page_ppn; -/** - * removes the mapping to a virtual_page by marking its PTE Entry as non valid - * - * @param physical_page_directory_page Real Page where the PDE to work on resides - * @param virtual_page which will be invalidated - */ - void unmapPage(uint32 virtual_page); + size_t page_size; + size_t pdpti; + size_t pdi; + size_t pti; +}; + +class ArchMemory +{ +public: + ArchMemory(); + ArchMemory(PageDirPointerTableEntry* pdpt_addr); ~ArchMemory(); -/** - * recursively remove a PageDirectoryEntry and all its Pages and PageTables - * - * @param physical_page_directory_page of PDE to remove - */ - void freePageDirectory(uint32 physical_page_directory_page); + + [[nodiscard]] + bool mapPage(vpn_t virtual_page, ppn_t physical_page, bool user_access); + void unmapPage(vpn_t virtual_page); + + [[nodiscard]] + static bool mapKernelPage(vpn_t virtual_page, ppn_t physical_page, bool can_alloc_pages = false, bool memory_mapped_io = false); + static void unmapKernelPage(vpn_t virtual_page, bool free_page = true); /** * Takes a Physical Page Number in Real Memory and returns a virtual address than @@ -56,16 +58,25 @@ class ArchMemory * @return Virtual Address above 3GB pointing to the start of a memory segment that * is mapped to the physical page given */ - static pointer getIdentAddressOfPPN(uint32 ppn, uint32 page_size=PAGE_SIZE); + static pointer getIdentAddressOfPPN(ppn_t ppn, size_t page_size = PAGE_SIZE); + static pointer getIdentAddress(size_t address); /** - * Checks if a given Virtual Address is valid and is mapped to real memory + * Checks if a given Virtual Address is valid and is mapped to physical memory * @param pdpt page dir pointer table * @param vaddress_to_check Virtual Address we want to check - * @return true: if mapping exists\nfalse: if the given virtual address is unmapped - * and accessing it would result in a pageFault + * @return physical address if the virtual address is mapped, zero otherwise */ - pointer checkAddressValid(uint32 vaddress_to_check); + pointer checkAddressValid(size_t vaddress_to_check) const; + static pointer checkAddressValid(PageDirPointerTableEntry* pdpt, size_t vaddress_to_check); + + const ArchMemoryMapping resolveMapping(vpn_t vpage) const; + static const ArchMemoryMapping resolveMapping(PageDirPointerTableEntry* pdpt, vpn_t vpage); + + size_t getPagingStructureRootPhys() const; + size_t getValueForCR3() const; + + /** * Takes a virtual_page and search through the pageTable and pageDirectory for the @@ -78,70 +89,35 @@ class ArchMemory * @return 0: if the virtual page doesn't map to any physical page\notherwise * returns the page size in byte (4096 for 4KiB pages or 4096*1024 for 4MiB pages) */ - static uint32 get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, size_t *physical_page, uint32 *physical_pte_page=0); + static uint32 get_PPN_Of_VPN_In_KernelMapping(vpn_t virtual_page, size_t* physical_page, uint32* physical_pte_page=0); -/** - * - * maps a virtual page to a physical page in kernel mapping - * - * @param virtual_page - * @param physical_page - */ - static void mapKernelPage(uint32 virtual_page, uint32 physical_page); + static PageDirPointerTableEntry* getKernelPagingStructureRootVirt(); + static size_t getKernelPagingStructureRootPhys(); + static void loadPagingStructureRoot(size_t cr3_value); -/** - * removes the mapping to a virtual_page by marking its PTE Entry as non valid - * in kernel mapping - * - * @param virtual_page which will be invalidated - */ - static void unmapKernelPage(uint32 virtual_page); + static void flushLocalTranslationCaches(size_t addr); + static void flushAllTranslationCaches(size_t addr); - - PageDirPointerTableEntry* page_dir_pointer_table_; - PageDirPointerTableEntry* getRootOfPagingStructure(); - uint32 getValueForCR3(); - static PageDirPointerTableEntry* getRootOfKernelPagingStructure(); - - static const size_t RESERVED_START = 0x80000ULL; - static const size_t RESERVED_END = 0xC0000ULL; + static ArchMemory& kernelArchMemory(); private: + ArchMemory& operator=(const ArchMemory& src) = delete; // should never be implemented - void insertPD(uint32 pdpt_vpn, uint32 physical_page_directory_page); -/** - * Adds a page directory entry to the given page directory. - * (In other words, adds the reference to a new page table to a given - * page directory.) - * - * @param physical_page_directory_page physical page containing the target PD. - * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. - * @param physical_page_table_page physical page of the new page table. - */ - void insertPT(PageDirEntry* page_directory, uint32 pde_vpn, uint32 physical_page_table_page); - -/** - * Removes a page directory entry from a given page directory if it is present - * in the first place. Futhermore, the target page table is assured to be - * empty. - * - * @param physical_page_directory_page physical page containing the target PD. - * @param pde_vpn Index of the PDE (i.e. the page table) in the PD. - */ - void checkAndRemovePT(uint32 physical_page_directory_page, uint32 pde_vpn); + template static bool tableEmpty(T* table); - PageDirPointerTableEntry page_dir_pointer_table_space_[2 * PAGE_DIRECTORY_POINTER_TABLE_ENTRIES]; - // why 2* ? this is a hack because this table has to be aligned to its own - // size 0x20... this way we allow to set an aligned pointer in the constructor. - // but why cant we just use __attribute__((aligned(0x20))) ? i'm not sure... - // i think because were in a class and the object will be allocated by the - // KMM which will just return some not-aligned block, thus this aligned block - // gets not-aligned in memory -- DG + template void removeEntry(T* table, size_t index); + template + void insert(T* table, size_t index, ppn_t ppn, bool user_access, bool writeable); - ArchMemory(ArchMemory const &src); // not yet implemented - ArchMemory &operator=(ArchMemory const &src); // should never be implemented + PageDirPointerTableEntry + page_dir_pointer_table_space_[2 * PAGE_DIRECTORY_POINTER_TABLE_ENTRIES]; + // why 2* ? this is a hack because this table has to be aligned to its own + // size 0x20... this way we allow to set an aligned pointer in the constructor. + // but why cant we just use __attribute__((aligned(0x20))) ? i'm not sure... + // i think because were in a class and the object will be allocated by the + // KMM which will just return some not-aligned block, thus this aligned block + // gets not-aligned in memory -- DG + PageDirPointerTableEntry* page_dir_pointer_table_; }; - - diff --git a/arch/x86/32/pae/include/paging-definitions.h b/arch/x86/32/pae/include/paging-definitions.h index 754ab66db..11f4d463e 100644 --- a/arch/x86/32/pae/include/paging-definitions.h +++ b/arch/x86/32/pae/include/paging-definitions.h @@ -2,6 +2,7 @@ #include "types.h" +#include "EASTL/type_traits.h" #define PAGE_DIRECTORY_POINTER_TABLE_ENTRIES 4 #define PAGE_DIRECTORY_ENTRIES 512 @@ -29,11 +30,18 @@ typedef struct size_t ignored_3 :1; size_t ignored_2 :1; size_t ignored_1 :1; - size_t page_directory_ppn :24; // MAXPHYADDR (36) - 12 + size_t page_ppn :24; // MAXPHYADDR (36) - 12 size_t reserved_3 :28; // must be 0 + + using supports_writeable = eastl::false_type; + using supports_user_access = eastl::false_type; } __attribute__((__packed__)) PageDirPointerTableEntry; -static_assert(sizeof(PageDirPointerTableEntry) == 8, "PageDirPointerTableEntry is not 64 bit"); +static_assert(sizeof(PageDirPointerTableEntry) == 8, "PageDirPointerTablePageEntry is not 64 bit"); + +using PageDirPointerTable = PageDirPointerTableEntry[PAGE_DIRECTORY_POINTER_TABLE_ENTRIES]; + +static_assert(sizeof(PageDirPointerTable) == 32, "PageDirPointerTable is not 32 byte"); struct PageDirPageTableEntry { @@ -49,9 +57,12 @@ struct PageDirPageTableEntry size_t ignored_3 :1; size_t ignored_2 :1; size_t ignored_1 :1; - size_t page_table_ppn :24; // MAXPHYADDR (36) - 12 + size_t page_ppn :24; // MAXPHYADDR (36) - 12 size_t reserved_2 :27; // must be 0 size_t execution_disabled :1; + + using supports_writeable = eastl::true_type; + using supports_user_access = eastl::true_type; } __attribute__((__packed__)); static_assert(sizeof(PageDirPageTableEntry) == 8, "PageDirPageTableEntry is not 64 bit"); @@ -76,6 +87,9 @@ struct PageDirPageEntry size_t page_ppn :15; // MAXPHYADDR (36) - 21 size_t reserved_2 :27; // must be 0 size_t execution_disabled :1; + + using supports_writeable = eastl::true_type; + using supports_user_access = eastl::true_type; } __attribute__((__packed__)); static_assert(sizeof(PageDirPageEntry) == 8, "PageDirPageEntry is not 64 bit"); @@ -86,6 +100,12 @@ typedef union struct PageDirPageEntry page; } __attribute__((__packed__)) PageDirEntry; +static_assert(sizeof(PageDirEntry) == 8, "PageDirEntry is not 64 bit"); + +using PageDir = PageDirEntry[PAGE_DIRECTORY_ENTRIES]; + +static_assert(sizeof(PageDir) == PAGE_SIZE, "PageDir is not 4096 byte"); + typedef struct { size_t present :1; @@ -103,6 +123,28 @@ typedef struct size_t page_ppn :24; // MAXPHYADDR (36) - 12 size_t reserved_2 :27; // must be 0 size_t execution_disabled :1; + + using supports_writeable = eastl::true_type; + using supports_user_access = eastl::true_type; } __attribute__((__packed__)) PageTableEntry; static_assert(sizeof(PageTableEntry) == 8, "PageTableEntry is not 64 bit"); + +using PageTable = PageTableEntry[PAGE_TABLE_ENTRIES]; + +static_assert(sizeof(PageTable) == PAGE_SIZE, "PageTable is not 4096 byte"); + + +union VAddr +{ + size_t addr; + struct + { + size_t offset :12; + size_t pti :9; + size_t pdi :9; + size_t pdpti :2; + }; +}; + +static_assert(sizeof(VAddr) == 4, "VAddr is not 32 bit"); diff --git a/arch/x86/32/pae/source/ArchMemory.cpp b/arch/x86/32/pae/source/ArchMemory.cpp index 94bb73a40..02f0414a1 100644 --- a/arch/x86/32/pae/source/ArchMemory.cpp +++ b/arch/x86/32/pae/source/ArchMemory.cpp @@ -1,163 +1,266 @@ #include "ArchMemory.h" -#include "kprintf.h" -#include "assert.h" -#include "offsets.h" + #include "PageManager.h" +#include "kprintf.h" #include "kstring.h" +#include "offsets.h" +#include "paging-definitions.h" +#include + +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMulticore.h" + +#include "assert.h" + +// Also see x86/common/source/ArchMemory.cpp for common functionality PageDirPointerTableEntry kernel_page_directory_pointer_table[PAGE_DIRECTORY_POINTER_TABLE_ENTRIES] __attribute__((aligned(0x20))); PageDirEntry kernel_page_directory[4 * PAGE_DIRECTORY_ENTRIES] __attribute__((aligned(0x1000))); PageTableEntry kernel_page_tables[8 * PAGE_TABLE_ENTRIES] __attribute__((aligned(0x1000))); -ArchMemory::ArchMemory() : page_dir_pointer_table_((PageDirPointerTableEntry*) (((uint32) page_dir_pointer_table_space_ + 0x20) & (~0x1F))) +ArchMemory::ArchMemory() : + page_dir_pointer_table_((PageDirPointerTableEntry*) (((uint32) page_dir_pointer_table_space_ + 0x20) & (~0x1F))) { memcpy(page_dir_pointer_table_, kernel_page_directory_pointer_table, sizeof(PageDirPointerTableEntry) * PAGE_DIRECTORY_POINTER_TABLE_ENTRIES); memset(page_dir_pointer_table_, 0, sizeof(PageDirPointerTableEntry) * PAGE_DIRECTORY_POINTER_TABLE_ENTRIES/2); // should be zero, this is just for safety } -void ArchMemory::checkAndRemovePT(uint32 physical_page_directory_page, uint32 pde_vpn) +ArchMemory::ArchMemory(PageDirPointerTableEntry* pdpt) : + page_dir_pointer_table_(pdpt) { - PageDirEntry *page_directory = (PageDirEntry *) getIdentAddressOfPPN(physical_page_directory_page); - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - assert(page_directory[pde_vpn].page.size == 0); +} - if (!page_directory[pde_vpn].pt.present) return; // PT not present -> do nothing. +template +bool ArchMemory::tableEmpty(T* table) +{ + for (size_t i = 0; i < NUM_ENTRIES; i++) + { + if (table[i].present) + return false; + } + return true; +} - for (uint32 pte_vpn=0; pte_vpn < PAGE_TABLE_ENTRIES; ++pte_vpn) - if (pte_base[pte_vpn].present > 0) - return; //not empty -> do nothing +template +void ArchMemory::removeEntry(T* table, size_t index) +{ + assert(table[index].present); - //else: - page_directory[pde_vpn].pt.present = 0; - PageManager::instance()->freePPN(page_directory[pde_vpn].pt.page_table_ppn); - ((uint64*)page_directory)[pde_vpn] = 0; // for easier debugging + table[index].present = 0; + memset(&table[index], 0, sizeof(table[index])); } void ArchMemory::unmapPage(uint32 virtual_page) { - RESOLVEMAPPING(page_dir_pointer_table_,virtual_page); + ArchMemoryMapping m = resolveMapping(virtual_page); + debug(A_MEMORY, "Unmap %zx => pdpt: %x, pd: %x, pt: %x, page: %x\n", + virtual_page, getPagingStructureRootPhys(), m.pd_ppn, m.pt_ppn, m.page_ppn); + assert(m.page != 0 && m.page_size == PAGE_SIZE); - assert(page_dir_pointer_table_[pdpte_vpn].present); - assert(!page_directory[pde_vpn].page.size); + removeEntry(m.pt, m.pti); - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - assert(pte_base[pte_vpn].present); + bool pd_empty = false; + bool pt_empty = tableEmpty(m.pt); + + if (pt_empty) + { + removeEntry(&m.pd->pt, m.pdi); + pd_empty = tableEmpty(&m.pd->pt); + } + if (pd_empty) + { + removeEntry(m.pdpt, m.pdpti); + } - pte_base[pte_vpn].present = 0; - PageManager::instance()->freePPN(pte_base[pte_vpn].page_ppn); - ((uint64*)pte_base)[pte_vpn] = 0; // for easier debugging + flushAllTranslationCaches(virtual_page * PAGE_SIZE); // Needs to happen after page table entries have been modified but before PPNs are freed - checkAndRemovePT(page_dir_pointer_table_[pdpte_vpn].page_directory_ppn, pde_vpn); + PageManager::instance().freePPN(m.page_ppn); + if(pt_empty) { PageManager::instance().freePPN(m.pt_ppn); } + if(pd_empty) { PageManager::instance().freePPN(m.pd_ppn); } } -void ArchMemory::insertPD(uint32 pdpt_vpn, uint32 physical_page_directory_page) +template +void ArchMemory::insert(T* table, size_t index, ppn_t ppn, bool user_access, bool writeable) { - kprintfd("insertPD: pdpt %p pdpt_vpn %x physical_page_table_page %x\n",page_dir_pointer_table_,pdpt_vpn,physical_page_directory_page); - memset((void*)getIdentAddressOfPPN(physical_page_directory_page), 0,PAGE_SIZE); - memset((void*)(page_dir_pointer_table_ + pdpt_vpn), 0, sizeof(PageDirPointerTableEntry)); - page_dir_pointer_table_[pdpt_vpn].page_directory_ppn = physical_page_directory_page; - page_dir_pointer_table_[pdpt_vpn].present = 1; + if (A_MEMORY & OUTPUT_ADVANCED) + debug(A_MEMORY, "%s: page %p index %zx ppn %x user_access %u, writeable %u\n", + __PRETTY_FUNCTION__, table, index, ppn, user_access, writeable); + + assert(((uint64*)table)[index] == 0); + + // conditional compilation at compile time! + if constexpr (T::supports_writeable::value) + { + table[index].writeable = writeable; + } + if constexpr (T::supports_user_access::value) + { + table[index].user_access = user_access; + } + + table[index].page_ppn = ppn; + table[index].present = 1; } -void ArchMemory::insertPT(PageDirEntry* page_directory, uint32 pde_vpn, uint32 physical_page_table_page) +bool ArchMemory::mapPage(vpn_t virtual_page, ppn_t physical_page, bool user_access) { - kprintfd("insertPT: page_directory %p pde_vpn %x physical_page_table_page %x\n",page_directory,pde_vpn,physical_page_table_page); - memset((void*)getIdentAddressOfPPN(physical_page_table_page), 0, PAGE_SIZE); - memset((void*)(page_directory + pde_vpn), 0, sizeof(PageDirPointerTableEntry)); - page_directory[pde_vpn].pt.writeable = 1; - page_directory[pde_vpn].pt.size = 0; - page_directory[pde_vpn].pt.page_table_ppn = physical_page_table_page; - page_directory[pde_vpn].pt.user_access = 1; - page_directory[pde_vpn].pt.present = 1; + ArchMemoryMapping m = resolveMapping(virtual_page); + debug(A_MEMORY, "Map %zx => pdpt: %x pd: %x, pt: %x, page: %x, user: %u\n", + virtual_page, getPagingStructureRootPhys(), m.pd_ppn, m.pt_ppn, physical_page, user_access); + assert((m.page_size == 0) || (m.page_size == PAGE_SIZE)); + + if (m.pd == 0) + { + m.pd_ppn = PageManager::instance().allocPPN(); + m.pd = (PageDirEntry*) getIdentAddressOfPPN(m.pd_ppn); + insert(m.pdpt, m.pdpti, m.pd_ppn, user_access, true); + } + + if (m.pt == 0) + { + m.pt_ppn = PageManager::instance().allocPPN(); + m.pt = (PageTableEntry*) getIdentAddressOfPPN(m.pt_ppn); + insert((PageDirPageTableEntry*)m.pd, m.pdi, m.pt_ppn, user_access, true); + } + + if (m.page == 0) + { + insert(m.pt, m.pti, physical_page, user_access, true); + return true; + } + + return false; } -bool ArchMemory::mapPage(uint32 virtual_page, uint32 physical_page, uint32 user_access) +ArchMemory::~ArchMemory() { - RESOLVEMAPPING(page_dir_pointer_table_,virtual_page); + debug(A_MEMORY, "~ArchMemory(): Free PDPT %x\n", getPagingStructureRootPhys()); - if (page_dir_pointer_table_[pdpte_vpn].present == 0) - { - uint32 pd_ppn = PageManager::instance()->allocPPN(); - page_directory = (PageDirEntry*) getIdentAddressOfPPN(pd_ppn); - insertPD(pdpte_vpn, pd_ppn); - } + size_t cr3 = 0; + asm("mov %%cr3, %[cr3]\n" : [cr3]"=g"(cr3)); + assert(cr3 != getValueForCR3() && "thread deletes its own arch memory"); - if (page_directory[pde_vpn].pt.present == 0) - { - insertPT(page_directory, pde_vpn, PageManager::instance()->allocPPN()); - } - assert(page_directory[pde_vpn].page.size == 0); + PageDirPointerTableEntry* pdpt = page_dir_pointer_table_; + for (size_t pdpti = 0; pdpti < PAGE_DIRECTORY_POINTER_TABLE_ENTRIES/2; pdpti++) + { + if (pdpt[pdpti].present) + { + auto pd_ppn = pdpt[pdpti].page_ppn; + PageDirEntry* pd = (PageDirEntry*) getIdentAddressOfPPN(pd_ppn); + for (size_t pdi = 0; pdi < PAGE_DIRECTORY_ENTRIES; pdi++) + { + if (pd[pdi].pt.present) + { + assert(pd[pdi].pt.size == 0); + auto pt_ppn = pd[pdi].pt.page_ppn; + PageTableEntry* pt = (PageTableEntry*) getIdentAddressOfPPN(pt_ppn); + for (size_t pti = 0; pti < PAGE_TABLE_ENTRIES; pti++) + { + if (pt[pti].present) + { + auto page_ppn = pt[pti].page_ppn; + removeEntry(pt, pti); + PageManager::instance().freePPN(page_ppn); + } + } + removeEntry(&pd->pt, pdi); + PageManager::instance().freePPN(pt_ppn); + } + } + removeEntry(pdpt, pdpti); + PageManager::instance().freePPN(pd_ppn); + } + } +} - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - if(pte_base[pte_vpn].present == 0) - { - pte_base[pte_vpn].writeable = 1; - pte_base[pte_vpn].user_access = user_access; - pte_base[pte_vpn].page_ppn = physical_page; - pte_base[pte_vpn].present = 1; - return true; - } +pointer ArchMemory::checkAddressValid(uint32 vaddress_to_check) const +{ + return checkAddressValid(page_dir_pointer_table_, vaddress_to_check); +} - return false; +pointer ArchMemory::checkAddressValid(PageDirPointerTableEntry* pdpt, uint32 vaddress_to_check) +{ + ArchMemoryMapping m = resolveMapping(pdpt, vaddress_to_check / PAGE_SIZE); + if (m.page != 0) + { + debug(A_MEMORY, "checkAddressValid, pdpt: %p, vaddr: %#zx -> true\n", pdpt, vaddress_to_check); + return m.page | (vaddress_to_check % m.page_size); + } + else + { + debug(A_MEMORY, "checkAddressValid, pdpt: %p, vaddr: %#zx -> false\n", pdpt, vaddress_to_check); + return 0; + } } -ArchMemory::~ArchMemory() +const ArchMemoryMapping ArchMemory::resolveMapping(size_t vpage) const { - debug ( A_MEMORY,"ArchMemory::~ArchMemory(): Freeing page dir pointer table %p\n",page_dir_pointer_table_ ); - if(page_dir_pointer_table_[0].present) - freePageDirectory(page_dir_pointer_table_[0].page_directory_ppn); // 0-1 GiB - if(page_dir_pointer_table_[1].present) - freePageDirectory(page_dir_pointer_table_[1].page_directory_ppn); // 1-2 GiB + return resolveMapping(page_dir_pointer_table_, vpage); } -void ArchMemory::freePageDirectory(uint32 physical_page_directory_page) +const ArchMemoryMapping ArchMemory::resolveMapping(PageDirPointerTableEntry* pdpt, vpn_t vpage) { - PageDirEntry *page_directory = (PageDirEntry *) getIdentAddressOfPPN(physical_page_directory_page); - for (uint32 pde_vpn=0; pde_vpn < PAGE_DIRECTORY_ENTRIES; ++pde_vpn) + ArchMemoryMapping m; + + VAddr a{vpage*PAGE_SIZE}; + + m.pti = a.pti; + m.pdi = a.pdi; + m.pdpti = a.pdpti; + + if(A_MEMORY & OUTPUT_ADVANCED) { - if (page_directory[pde_vpn].pt.present) - { - if (page_directory[pde_vpn].page.size) - { - page_directory[pde_vpn].page.present=0; - for (uint32 p=0;p<1024;++p) - PageManager::instance()->freePPN(page_directory[pde_vpn].page.page_ppn*1024 + p); - } - else + debug(A_MEMORY, "resolveMapping, vpn: %#zx, pdpti: %zx(%zu), pdi: %zx(%zu), pti: %zx(%zu)\n", vpage, m.pdpti, m.pdpti, m.pdi, m.pdi, m.pti, m.pti); + } + + m.pdpt = pdpt; + m.pd = 0; + m.pt = 0; + m.page = 0; + m.pd_ppn = 0; + m.pt_ppn = 0; + m.page_ppn = 0; + m.page_size = 0; + + if(m.pdpt[m.pdpti].present) + { + m.pd_ppn = m.pdpt[m.pdpti].page_ppn; + assert(m.pd_ppn < PageManager::instance().getTotalNumPages()); + m.pd = (PageDirEntry*) getIdentAddressOfPPN(m.pd_ppn); + + if(m.pd[m.pdi].pt.present && !m.pd[m.pdi].pt.size) { - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - for (uint32 pte_vpn=0; pte_vpn < PAGE_TABLE_ENTRIES; ++pte_vpn) - { - if (pte_base[pte_vpn].present) + m.pt_ppn = m.pd[m.pdi].pt.page_ppn; + assert(m.pt_ppn < PageManager::instance().getTotalNumPages()); + m.pt = (PageTableEntry*) getIdentAddressOfPPN(m.pt_ppn); + if(m.pt[m.pti].present) { - pte_base[pte_vpn].present = 0; - PageManager::instance()->freePPN(pte_base[pte_vpn].page_ppn); + m.page_size = PAGE_SIZE; + m.page_ppn = m.pt[m.pti].page_ppn; + m.page = getIdentAddressOfPPN(m.page_ppn); } - } - page_directory[pde_vpn].pt.present=0; - PageManager::instance()->freePPN(page_directory[pde_vpn].pt.page_table_ppn); } - } + else if(m.pd[m.pdi].pt.present && m.pd[m.pdi].pt.size) + { + m.page_size = PAGE_SIZE * PAGE_TABLE_ENTRIES; + m.page_ppn = m.pd[m.pdi].page.page_ppn; + m.page = getIdentAddressOfPPN(m.page_ppn); + } } - PageManager::instance()->freePPN(physical_page_directory_page); -} -pointer ArchMemory::checkAddressValid(uint32 vaddress_to_check) -{ - RESOLVEMAPPING(page_dir_pointer_table_, vaddress_to_check / PAGE_SIZE); - if (page_dir_pointer_table_[pdpte_vpn].present) + if(A_MEMORY & OUTPUT_ADVANCED) { - if (page_directory[pde_vpn].pt.present) - { - if (page_directory[pde_vpn].page.size) - return getIdentAddressOfPPN(page_directory[pde_vpn].page.page_ppn,PAGE_SIZE * PAGE_TABLE_ENTRIES) | (vaddress_to_check % (PAGE_SIZE * PAGE_TABLE_ENTRIES)); - - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - if (pte_base[pte_vpn].present) - return getIdentAddressOfPPN(pte_base[pte_vpn].page_ppn) | (vaddress_to_check % PAGE_SIZE); - } + debug(A_MEMORY, "resolveMapping, vpn: %#zx, pdpt[%s]: %p, pd[%s]: %#zx, pt[%s]: %#zx, page[%s]: %#zx, size: %#zx\n", + vpage, + (m.pdpt ? "P" : "-"), m.pdpt, + (m.pd ? "P" : "-"), m.pd_ppn, + (m.pt ? "P" : "-"), m.pt_ppn, + (m.page ? "P" : "-"), m.page_ppn, + m.page_size); } - return 0; + + return m; } uint32 ArchMemory::get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, size_t *physical_page, uint32 *physical_pte_page) @@ -177,8 +280,8 @@ uint32 ArchMemory::get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, size_t * else { if (physical_pte_page) - *physical_pte_page = page_directory[pde_vpn].pt.page_table_ppn; - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); + *physical_pte_page = page_directory[pde_vpn].pt.page_ppn; + PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_ppn); if (pte_base[pte_vpn].present) { if (physical_page) @@ -191,56 +294,88 @@ uint32 ArchMemory::get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, size_t * return 0; } -void ArchMemory::mapKernelPage(uint32 virtual_page, uint32 physical_page) +bool ArchMemory::mapKernelPage(uint32 virtual_page, uint32 physical_page, bool can_alloc_pages, bool memory_mapped_io) { - PageDirPointerTableEntry *pdpt = kernel_page_directory_pointer_table; - RESOLVEMAPPING(pdpt, virtual_page); - assert(pdpt[pdpte_vpn].present); - assert(page_directory[pde_vpn].pt.present && page_directory[pde_vpn].pt.size == 0); - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - assert(!pte_base[pte_vpn].present); - pte_base[pte_vpn].writeable = 1; - pte_base[pte_vpn].page_ppn = physical_page; - pte_base[pte_vpn].present = 1; + debug(A_MEMORY, "Map kernel page %#zx -> PPN %#zx, alloc new pages: %u, mmio: %u\n", virtual_page, physical_page, can_alloc_pages, memory_mapped_io); + + ArchMemoryMapping m = resolveMapping(getKernelPagingStructureRootVirt(), virtual_page); + + if (m.page_size) + { + return false; // Page already mapped + } + + assert(m.pd || can_alloc_pages); + if((!m.pd) && can_alloc_pages) + { + m.pd_ppn = PageManager::instance().allocPPN(); + m.pd = (PageDirEntry*) getIdentAddressOfPPN(m.pd_ppn); + + m.pdpt[m.pdpti].page_ppn = m.pd_ppn; + m.pdpt[m.pdpti].present = 1; + } + + assert(m.pt || can_alloc_pages); + if((!m.pt) && can_alloc_pages) + { + m.pt_ppn = PageManager::instance().allocPPN(); + m.pt = (PageTableEntry*) getIdentAddressOfPPN(m.pt_ppn); + + m.pd[m.pdi].pt.page_ppn = m.pt_ppn; + m.pd[m.pdi].pt.writeable = 1; + m.pd[m.pdi].pt.present = 1; + } + + assert(!m.pt[m.pti].present); + + if(memory_mapped_io) + { + m.pt[m.pti].write_through = 1; + m.pt[m.pti].cache_disabled = 1; + } + + m.pt[m.pti].page_ppn = physical_page; + m.pt[m.pti].writeable = 1; + m.pt[m.pti].present = 1; + asm volatile ("movl %%cr3, %%eax; movl %%eax, %%cr3;" ::: "%eax"); + + return true; } -void ArchMemory::unmapKernelPage(uint32 virtual_page) +void ArchMemory::unmapKernelPage(uint32 virtual_page, bool free_page) { - PageDirPointerTableEntry *pdpt = kernel_page_directory_pointer_table; - RESOLVEMAPPING(pdpt, virtual_page); - assert(pdpt[pdpte_vpn].present); - assert(page_directory[pde_vpn].pt.present && page_directory[pde_vpn].pt.size == 0); - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - assert(pte_base[pte_vpn].present); - pte_base[pte_vpn].present = 0; - pte_base[pte_vpn].writeable = 0; - PageManager::instance()->freePPN(pte_base[pte_vpn].page_ppn); + ArchMemoryMapping m = resolveMapping(getKernelPagingStructureRootVirt(), virtual_page); + assert(m.page && (m.page_size == PAGE_SIZE)); + + memset(&m.pt[m.pti], 0, sizeof(m.pt[m.pti])); + + if(free_page) + { + PageManager::instance().freePPN(m.page_ppn); + } + asm volatile ("movl %%cr3, %%eax; movl %%eax, %%cr3;" ::: "%eax"); } -PageDirPointerTableEntry* ArchMemory::getRootOfPagingStructure() +size_t ArchMemory::getPagingStructureRootPhys() const { - return page_dir_pointer_table_; + // last 5 bits must be zero! + assert(((uint32)page_dir_pointer_table_ & 0x1F) == 0); + size_t ppn = 0; + if (get_PPN_Of_VPN_In_KernelMapping(((size_t)page_dir_pointer_table_) / PAGE_SIZE, &ppn) > 0) + return ppn * PAGE_SIZE + ((size_t)page_dir_pointer_table_ % PAGE_SIZE); + assert(false); + return 0; } -PageDirPointerTableEntry* ArchMemory::getRootOfKernelPagingStructure() +PageDirPointerTableEntry* ArchMemory::getKernelPagingStructureRootVirt() { return kernel_page_directory_pointer_table; } -uint32 ArchMemory::getValueForCR3() -{ - // last 5 bits must be zero! - assert(((uint32)page_dir_pointer_table_ & 0x1F) == 0); - size_t ppn = 0; - if (get_PPN_Of_VPN_In_KernelMapping(((size_t)page_dir_pointer_table_) / PAGE_SIZE,&ppn) > 0) - return ppn * PAGE_SIZE + ((size_t)page_dir_pointer_table_ % PAGE_SIZE); - assert(false); - return 0; -} - -pointer ArchMemory::getIdentAddressOfPPN(uint32 ppn, uint32 page_size /* optional */) +ArchMemory& ArchMemory::kernelArchMemory() { - return (3U*1024U*1024U*1024U) + (ppn * page_size); + static ArchMemory kernel_arch_mem(getKernelPagingStructureRootVirt()); + return kernel_arch_mem; } diff --git a/arch/x86/32/pae/source/CMakeLists.txt b/arch/x86/32/pae/source/CMakeLists.txt index 939f5e569..572975f1c 100644 --- a/arch/x86/32/pae/source/CMakeLists.txt +++ b/arch/x86/32/pae/source/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC ../../../common/include ) add_project_library(arch_x86_pae) diff --git a/arch/x86/32/pae/source/arch_apstartup.S b/arch/x86/32/pae/source/arch_apstartup.S new file mode 100644 index 000000000..82c9e3cae --- /dev/null +++ b/arch/x86/32/pae/source/arch_apstartup.S @@ -0,0 +1,183 @@ +# https://www.airs.com/blog/archives/518 +.section .note.GNU-stack,"",@progbits + +# Startup code for APs + +# TODO: Pass these as arguments from the BSP instead of hardcoding +.set KERNEL_CS, 8*3 +.set KERNEL_DS, 8*2 + +.code16gcc +.section .text.apstartup, "ax", @progbits + +.macro ap_print string + leal \string, %esi + movw $0xE9, %dx +1: + lodsb + testb %al, %al + jz 2f + outb %al, %dx + jmp 1b +2: + .endm + +.macro ap_printn string len + leal \string, %esi + movl $\len, %ecx + movw $0xE9, %dx + rep outsb +.endm + + +.macro ap_dump addr num + movl $\addr, %ebx + movl $\num, %ecx +1: + testl %ecx, %ecx + jz 2f + + movb (%ebx), %al + movw $0xE9, %dx + outb %al, %dx + addl $1, %ebx + subl $1, %ecx + jmp 1b +2: +.endm + +# APs start in real mode +.global apstartup +apstartup: + cli + + movw %cs, %ax + movw %ax, %ds + movw %ax, %ss + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + leal (ap_startup_stack - apstartup), %esp + + ap_printn (ap_s_start - apstartup), len_ap_s_start + + ap_printn (ap_s_lgdt - apstartup), len_ap_s_lgdt +ap_load_gdt: + leal (ap_gdt32_ptr - apstartup), %eax + lgdt (%eax) + +ap_load_idt: + lidt ap_idt_ptr + + ap_printn (ap_s_pse - apstartup), len_ap_s_pse + + # Enable PSE + PAE + movl %cr4, %eax + orl $0x30,%eax + movl %eax, %cr4 + + ap_printn (ap_s_cr3 - apstartup), len_ap_s_cr3 + + # Load CR3 + leal (ap_kernel_cr3 - apstartup), %eax + movl (%eax), %eax + movl %eax, %cr3 + + ap_printn (ap_s_prot - apstartup), len_ap_s_prot + +ap_prot_enable: + # Enable protected mode, write protection in kernel mode and paging + movl %cr0, %eax + orl $0x80010001,%eax + movl %eax, %cr0 + + ljmpl $KERNEL_CS, $apstartup32 + hlt +.code32 + +.global ap_s_start +ap_s_start: + .ascii "AP started\n" + .set len_ap_s_start, . - ap_s_start +.global ap_s_pse +ap_s_pse: + .ascii "Enabling PSE + PAE\n" + len_ap_s_pse = . - ap_s_pse +.global ap_s_cr3 +ap_s_cr3: + .ascii "Loading CR3\n" + .set len_ap_s_cr3, . - ap_s_cr3 +.global ap_s_prot +ap_s_prot: + .ascii "Enabling protected mode, write protection in kernel mode and paging\n" + len_ap_s_prot = . - ap_s_prot +ap_s_prot_far_jmp: + .ascii "Far jumping to load protected mode code segment\n" + len_ap_s_prot_far_jmp = . - ap_s_prot_far_jmp +.global ap_s_lgdt +ap_s_lgdt: + .ascii "Loading GDT\n" + .set len_ap_s_lgdt, . - ap_s_lgdt +.global ap_s_segm +ap_s_segm: + .ascii "Loaded segment registers\n" + .set len_ap_s_segm, . - ap_s_segm +.global ap_s_ljmp +ap_s_ljmp: + .ascii "Far jmp to trampoline to load protected mode code segment\n" + .set len_ap_s_ljmp, . - ap_s_ljmp +.global ap_s_apstartup32 +ap_s_apstartup32: + .ascii "Jumping to apstartup32\n" + .set len_ap_s_apstartup32, . - ap_s_apstartup32 + + +# To be filled in by the BSP +.global ap_kernel_cr3 +.type ap_kernel_cr3, @object +ap_kernel_cr3: + .skip 4 + + +.global ap_gdt32_ptr +.type ap_gdt32_ptr, @object +ap_gdt32_ptr: + .skip 2 # uint16 limit + .skip 4 # uint32 addr + + +.global ap_gdt32 +.type ap_gdt32, @object +ap_gdt32: + .skip 0x70 + + +.global ap_idt_ptr +ap_idt_ptr: + .word 2048 # uint16 limit + .int ap_idt # uint32 addr +.global ap_idt +.type ap_idt, @object +ap_idt: + .skip 2048 + + .align 0x20 +ap_pdpt: + .skip 0x20 + +.align 0x1000 +.global ap_startup_stack # Use space between gdt and paging root for stack +.type ap_startup_stack, @object +ap_startup_stack: + +.global ap_paging_root # PDPT +.type ap_paging_root, @object +ap_paging_root: +.skip 0x20 +ap_paging_root_end: +.global ap_paging_root_end + + + +.code32 diff --git a/arch/x86/32/pae/source/init_boottime_pagetables.cpp b/arch/x86/32/pae/source/init_boottime_pagetables.cpp index 4b6ea6777..84e3a219f 100644 --- a/arch/x86/32/pae/source/init_boottime_pagetables.cpp +++ b/arch/x86/32/pae/source/init_boottime_pagetables.cpp @@ -1,12 +1,15 @@ -#include "types.h" -#include "paging-definitions.h" -#include "offsets.h" +#include "kprintf.h" #include "multiboot.h" +#include "offsets.h" +#include "paging-definitions.h" + #include "ArchCommon.h" -#include "assert.h" -#include "kprintf.h" #include "ArchMemory.h" +#include "types.h" + +#include "assert.h" + extern void* kernel_end_address; extern "C" void initialiseBootTimePaging() @@ -16,30 +19,32 @@ extern "C" void initialiseBootTimePaging() PageDirPointerTableEntry *pdpt_start = (PageDirPointerTableEntry*) VIRTUAL_TO_PHYSICAL_BOOT( (pointer )kernel_page_directory_pointer_table); PageDirEntry *pde_start = (PageDirEntry*) VIRTUAL_TO_PHYSICAL_BOOT((pointer )kernel_page_directory); + + VAddr k_start{ArchCommon::getKernelStartAddress()}; + for (i = 0; i < 4; ++i) { - pdpt_start[i].page_directory_ppn = ((uint32) pde_start) / PAGE_SIZE + i; + pdpt_start[i].page_ppn = ((uint32) pde_start) / PAGE_SIZE + i; pdpt_start[i].present = 1; } PageTableEntry *pte_start = (PageTableEntry*) VIRTUAL_TO_PHYSICAL_BOOT((pointer )kernel_page_tables); - uint32 kernel_last_page = VIRTUAL_TO_PHYSICAL_BOOT((pointer)&kernel_end_address) / PAGE_SIZE; // we do not have to clear the pde since its in the bss for (i = 0; i < 10; ++i) { - pde_start[i].page.present = 1; + pde_start[i].page.page_ppn = i; pde_start[i].page.writeable = 1; pde_start[i].page.size = 1; - pde_start[i].page.page_ppn = i; + pde_start[i].page.present = 1; } for (i = 0; i < 8; ++i) { pde_start[i + 1024].pt.present = 1; pde_start[i + 1024].pt.writeable = 1; - pde_start[i + 1024].pt.page_table_ppn = ((pointer) &pte_start[PAGE_TABLE_ENTRIES * i]) / PAGE_SIZE; + pde_start[i + 1024].pt.page_ppn = ((pointer) &pte_start[PAGE_TABLE_ENTRIES * i]) / PAGE_SIZE; } // ok, we currently only fill in mappings for the first 4 megs (aka one page table) @@ -49,22 +54,29 @@ extern "C" void initialiseBootTimePaging() // update, from now on, all pages up to the last page containing only rodata // will be write protected. + extern uint32 text_start_address; extern uint32 ro_data_end_address; + extern uint32 apstartup_text_load_begin; + extern uint32 apstartup_text_load_end; - uint32 last_ro_data_page = VIRTUAL_TO_PHYSICAL_BOOT((pointer)&ro_data_end_address) / PAGE_SIZE; - - // ppns are 1mb = 256 pages after vpns... - for (i = 0; i < last_ro_data_page - 256; ++i) - { - pte_start[i].present = 1; - pte_start[i].writeable = 0; - pte_start[i].page_ppn = i + 256; - } - for (; i < kernel_last_page - 256; ++i) + // Map kernel pages + for(VAddr a{ArchCommon::getKernelStartAddress()}; a.addr < ArchCommon::getKernelEndAddress(); a.addr += PAGE_SIZE) { - pte_start[i].present = 1; - pte_start[i].writeable = 1; - pte_start[i].page_ppn = i + 256; + size_t pti = (a.pdpti - k_start.pdpti)*PAGE_DIRECTORY_ENTRIES*PAGE_DIRECTORY_POINTER_TABLE_ENTRIES + (a.pdi - k_start.pdi)*PAGE_TABLE_ENTRIES + a.pti; + assert(pti < sizeof(kernel_page_tables)/sizeof(kernel_page_tables[0])); + pte_start[pti].page_ppn = VIRTUAL_TO_PHYSICAL_BOOT(a.addr)/PAGE_SIZE; + // AP startup pages need to be writeable to fill in the GDT, ... + size_t ap_text_start = ((size_t)&apstartup_text_load_begin/PAGE_SIZE)*PAGE_SIZE; + size_t ap_text_end = (size_t)&apstartup_text_load_end; + + pte_start[pti].writeable = + (((a.addr >= (pointer)&text_start_address) && + (a.addr < (pointer)&ro_data_end_address)) && + !((a.addr >= ap_text_start) && + (a.addr < ap_text_end)) + ? 0 + : 1); + pte_start[pti].present = 1; } if (ArchCommon::haveVESAConsole(0)) diff --git a/arch/x86/32/pae/utils/kernel-ld-script.ld b/arch/x86/32/pae/utils/kernel-ld-script.ld index 8b1c2053e..9472de208 100644 --- a/arch/x86/32/pae/utils/kernel-ld-script.ld +++ b/arch/x86/32/pae/utils/kernel-ld-script.ld @@ -2,25 +2,61 @@ * this linker script tells ld how to link and which symbols to add to the * kernel.x binary */ - + /* let the linker use its 'native' format (ELF/COFF/PE) */ OUTPUT_FORMAT("elf32-i386") -/* no leading underscore for symbols handled in asm: */ ENTRY(entry) LS_Phys = 0x100000; /* 1 meg */ -/*LS_Virt = 0x100000; /* 1 meg */ -/* use this line instead when paging works properly! */ LS_Virt = 0x80000000; /* 2 gig + 1 meg due to 4m pages for kernel area*/ + +/* AP_STARTUP = 0x0000; */ + SECTIONS { - .text LS_Virt : AT(LS_Phys) + .mboot ALIGN(4096): AT(LS_Phys) { - PROVIDE(kernel_start_address = ABSOLUTE(.)); + KEEP(*(.mboot)) + } + + . = 0; + + /* . = AP_STARTUP; */ + .text.apstartup ALIGN(4096) : AT(LS_Phys + SIZEOF(.mboot)) + { + apstartup_text_begin = .; + *(.text.apstartup*) + apstartup_text_end = .; + } + + . = LS_Virt; + LS_START = .; + PROVIDE(kernel_start_address = ABSOLUTE(.)); + + mboot_load_begin = .; + . += SIZEOF(.mboot); + mboot_load_end = .; + + /* .mboot.load ALIGN(4096) (INFO) : AT(0x0) */ + /* { */ + /* . += SIZEOF(.mboot); */ + /* } */ + + apstartup_text_load_begin = .; + . += SIZEOF(.text.apstartup); + apstartup_text_load_end = .; + + .text ALIGN(4096) : AT(LS_Phys + (LS_Code - LS_START)) + { LS_Code = .; text_start_address = .; - *(.mboot) + /* KEEP(*(.mboot)) */ + /* . = ALIGN(4096); */ + /* apstartup_text_begin = .; */ + /* *(.text.apstartup*) */ + /* apstartup_text_end = .; */ + /* . = ALIGN(4096); */ *(.text) *(.text.*) text_end_address = .; @@ -28,33 +64,93 @@ SECTIONS ro_data_start_address = .; *(.rodata*) ro_data_end_address = .; + *(.eh_frame) + *(.eh_frame_hdr) . = ALIGN(4096); + } + + /* .text.apstartup.load ALIGN(4096) : AT(LS_Phys + (LS_apstartup_load - LS_START)) */ + /* { */ + /* LS_apstartup_load = .; */ + /* apstartup_text_load_begin = .; */ + /* . += SIZEOF(.text.apstartup); */ + /* apstartup_text_load_end = .; */ + /* . = ALIGN(4096); */ + /* } */ + /* .eh_frame ALIGN(4096) : AT(LS_Phys + (LS_eh_frame - LS_START)) */ + /* { */ + /* LS_eh_frame = .; */ + /* *(.eh_frame) */ + /* *(.eh_frame_hdr) */ + /* } */ + + .tdata ALIGN(4096) : AT(LS_Phys + (LS_tdata - LS_START)) + { + LS_tdata = .; + cls_start = .; + tdata_start = .; + *(.tdata) + tdata_end = .; } - .data ALIGN(4096) : AT(LS_Phys + (LS_Data - LS_Code)) + .tbss ALIGN(4096) : AT(LS_Phys + (LS_tbss - LS_START)) + { + LS_tbss = .; + tbss_start = .; + *(.tbss) + . = ALIGN(4) + 4; /* For C++11 tread_local init-on-first-use flag */ + tbss_end = .; + cls_end = .; + . = ALIGN(4) + 4; + } + + .data ALIGN(4096) : AT(LS_Phys + (LS_Data - LS_START)) { LS_Data = .; data_start_address = .; *(.data) . = ALIGN(4096); *(.gdt_stuff) + . = ALIGN(4096); + *(COMMON) /* common symbols, usually placed in .bss */ data_end_address = .; . = ALIGN(4096); } - .bss : AT(LS_Phys + (LS_Bss - LS_Code)) + .init_array : AT(LS_Phys + (__init_array_start - LS_START)) + { + __init_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + __init_array_end = .; + } + + .preinit_array : AT(LS_Phys + (__preinit_array_start - LS_START)) + { + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } + + .fini_array : AT(LS_Phys + (__fini_array_start - LS_START)) + { + __fini_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + __fini_array_end = .; + } + + .bss ALIGN(4096) : AT(LS_Phys + (LS_Bss - LS_START)) { LS_Bss = .; bss_start_address = .; *(.bss) . = ALIGN(4096); - *(COMMON) /* common symbols, usually placed in .bss */ bss_end_address = .; - } - .stab : AT(LS_Phys + (LS_Stab - LS_Code)) + .stab : AT(LS_Phys + (LS_Stab - LS_START)) { LS_Stab = .; stab_start_address_nr = .; @@ -62,9 +158,9 @@ SECTIONS stab_end_address_nr = .; . = ALIGN(4096); } - - .stabstr : AT(LS_Phys + (LS_Stabstr - LS_Code)) + + .stabstr : AT(LS_Phys + (LS_Stabstr - LS_START)) { LS_Stabstr = .; stabstr_start_address_nr = .; @@ -75,4 +171,4 @@ SECTIONS PROVIDE(kernel_end_address = .); } -} \ No newline at end of file +} diff --git a/arch/x86/32/source/ArchMemory.cpp b/arch/x86/32/source/ArchMemory.cpp index b8ebba82d..66ae1b979 100644 --- a/arch/x86/32/source/ArchMemory.cpp +++ b/arch/x86/32/source/ArchMemory.cpp @@ -1,131 +1,273 @@ #include "ArchMemory.h" -#include "kprintf.h" -#include "assert.h" + #include "PageManager.h" +#include "kprintf.h" #include "kstring.h" +#include "offsets.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMulticore.h" + +#include "assert.h" + +// Also see x86/common/source/ArchMemory.cpp for common functionality PageDirEntry kernel_page_directory[PAGE_DIRECTORY_ENTRIES] __attribute__((aligned(0x1000))); PageTableEntry kernel_page_tables[4 * PAGE_TABLE_ENTRIES] __attribute__((aligned(0x1000))); ArchMemory::ArchMemory() { - page_dir_page_ = PageManager::instance()->allocPPN(); + page_dir_page_ = PageManager::instance().allocPPN(); PageDirEntry *new_page_directory = (PageDirEntry*) getIdentAddressOfPPN(page_dir_page_); memcpy(new_page_directory, kernel_page_directory, PAGE_SIZE); memset(new_page_directory, 0, PAGE_SIZE / 2); // should be zero, this is just for safety } +ArchMemory::ArchMemory(size_t page_dir_ppn) : + page_dir_page_(page_dir_ppn) +{ +} + // only free pte's < PAGE_TABLE_ENTRIES/2 because we do NOT want to free Kernel Pages ArchMemory::~ArchMemory() { - debug(A_MEMORY, "ArchMemory::~ArchMemory(): Freeing page directory %x\n", page_dir_page_); - PageDirEntry *page_directory = (PageDirEntry *) getIdentAddressOfPPN(page_dir_page_); - for (uint32 pde_vpn = 0; pde_vpn < PAGE_TABLE_ENTRIES / 2; ++pde_vpn) - { - if (page_directory[pde_vpn].pt.present) + debug(A_MEMORY, "~ArchMemory(): Free PD %x\n", page_dir_page_); + + size_t cr3 = 0; + asm("mov %%cr3, %[cr3]\n" : [cr3]"=g"(cr3)); + assert(cr3 != getValueForCR3() && "thread deletes its own arch memory"); + + PageDirEntry* pd = (PageDirEntry*) getIdentAddressOfPPN(page_dir_page_); + for (size_t pdi = 0; pdi < PAGE_TABLE_ENTRIES/2; ++pdi) { - assert(!page_directory[pde_vpn].page.size); // only 4 KiB pages allowed - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - for (uint32 pte_vpn = 0; pte_vpn < PAGE_TABLE_ENTRIES; ++pte_vpn) - { - if (pte_base[pte_vpn].present) + if (pd[pdi].pt.present) { - unmapPage(pde_vpn * PAGE_TABLE_ENTRIES + pte_vpn); - if(!page_directory[pde_vpn].pt.present) - break; + assert(!pd[pdi].page.size); // only 4 KiB pages allowed + auto pt_ppn = pd[pdi].pt.page_ppn; + PageTableEntry *pt = (PageTableEntry *) getIdentAddressOfPPN(pt_ppn); + for (size_t pti = 0; pti < PAGE_TABLE_ENTRIES; ++pti) + { + if (pt[pti].present) + { + auto page_ppn = pt[pti].page_ppn; + removeEntry(pt, pti); + PageManager::instance().freePPN(page_ppn); + } + } + removeEntry(&pd->pt, pdi); + PageManager::instance().freePPN(pt_ppn); } - } } - } - PageManager::instance()->freePPN(page_dir_page_); + PageManager::instance().freePPN(page_dir_page_); +} + + +void ArchMemory::printMappedPages() +{ + printMappedPages(page_dir_page_); } -void ArchMemory::checkAndRemovePT(uint32 pde_vpn) +void ArchMemory::printMappedPages(uint32 page_dir_page) { - PageDirEntry *page_directory = (PageDirEntry *) getIdentAddressOfPPN(page_dir_page_); - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - assert(page_directory[pde_vpn].page.size == 0); + debug(A_MEMORY, "ArchMemory::print mapped pages for PD %x\n", page_dir_page); + + + PageDirEntry *pd = (PageDirEntry *) getIdentAddressOfPPN(page_dir_page); + for (uint32 pdi = 0; pdi < PAGE_TABLE_ENTRIES; ++pdi) + { + if (pd[pdi].pt.present) + { + if(!pd[pdi].pt.size) + { + PageTableEntry *pt = (PageTableEntry *) getIdentAddressOfPPN(pd[pdi].pt.page_ppn); + for (uint32 pti = 0; pti < PAGE_TABLE_ENTRIES; ++pti) + { + if (pt[pti].present) + { + VAddr a{}; + a.pdi = pdi; + a.pti = pti; + debug(A_MEMORY, "[%zx - %zx] -> {%zx - %zx} (%u, %u) U: %u, W: %u, T: %u, C: %u\n", + a.addr, a.addr + PAGE_SIZE, + pt[pti].page_ppn*PAGE_SIZE, pt[pti].page_ppn*PAGE_SIZE + PAGE_SIZE, + pdi, pti, + pt[pti].user_access, pt[pti].writeable, pt[pti].write_through, pt[pti].cache_disabled); + } + } + } + else + { + VAddr a{}; + a.pdi = pdi; + debug(A_MEMORY, "[%zx - %zx] -> {%zx - %zx} (%u) LARGE PAGE U: %u, W: %u, T: %u, C: %u\n", + a.addr, a.addr + PAGE_SIZE*PAGE_TABLE_ENTRIES, + pd[pdi].page.page_ppn*PAGE_SIZE, pd[pdi].page.page_ppn*PAGE_SIZE + PAGE_SIZE*PAGE_TABLE_ENTRIES, + pdi, + pd[pdi].page.user_access, pd[pdi].page.writeable, pd[pdi].page.write_through, pd[pdi].page.cache_disabled); + } + } + } +} - assert(page_directory[pde_vpn].pt.present); - assert(!page_directory[pde_vpn].page.size); +template +bool ArchMemory::tableEmpty(T* table) +{ + for (size_t i = 0; i < NUM_ENTRIES; i++) + { + if (table[i].present) + return false; + } + return true; +} - for (uint32 pte_vpn = 0; pte_vpn < PAGE_TABLE_ENTRIES; ++pte_vpn) - if (pte_base[pte_vpn].present > 0) - return; //not empty -> do nothing +template +void ArchMemory::removeEntry(T* table, size_t index) +{ + assert(table[index].present); - //else: - page_directory[pde_vpn].pt.present = 0; - PageManager::instance()->freePPN(page_directory[pde_vpn].pt.page_table_ppn); - ((uint32*)page_directory)[pde_vpn] = 0; // for easier debugging + table[index].present = 0; + memset(&table[index], 0, sizeof(table[index])); } void ArchMemory::unmapPage(uint32 virtual_page) { - RESOLVEMAPPING(page_dir_page_, virtual_page); + ArchMemoryMapping m = resolveMapping(virtual_page); + debug(A_MEMORY, "Unmap %zx => pd: %x, pt: %x, page: %x\n", + virtual_page, m.pd_ppn, m.pt_ppn, m.page_ppn); + assert(m.page && m.page_size == PAGE_SIZE); - assert(page_directory[pde_vpn].pt.present); - assert(!page_directory[pde_vpn].page.size); // only 4 KiB pages allowed + removeEntry(m.pt, m.pti); - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - assert(pte_base[pte_vpn].present); + bool pt_empty = tableEmpty(m.pt); + if (pt_empty) + { + removeEntry((PageDirPageTableEntry*)m.pd, m.pdi); + } - pte_base[pte_vpn].present = 0; - PageManager::instance()->freePPN(pte_base[pte_vpn].page_ppn); - ((uint32*)pte_base)[pte_vpn] = 0; // for easier debugging + flushAllTranslationCaches(virtual_page * PAGE_SIZE); // Needs to happen after page table entries have been modified but before PPNs are freed - checkAndRemovePT(pde_vpn); + PageManager::instance().freePPN(m.page_ppn); + if(pt_empty) { PageManager::instance().freePPN(m.pt_ppn); } } -void ArchMemory::insertPT(uint32 pde_vpn, uint32 physical_page_table_page) +template +void ArchMemory::insert(T* table, size_t index, ppn_t ppn, bool user_access, bool writeable) { - PageDirEntry *page_directory = (PageDirEntry *) getIdentAddressOfPPN(page_dir_page_); - assert(!page_directory[pde_vpn].pt.present); - memset((void*) getIdentAddressOfPPN(physical_page_table_page), 0, PAGE_SIZE); - page_directory[pde_vpn].pt.writeable = 1; - page_directory[pde_vpn].pt.size = 0; - page_directory[pde_vpn].pt.page_table_ppn = physical_page_table_page; - page_directory[pde_vpn].pt.user_access = 1; - page_directory[pde_vpn].pt.present = 1; + if (A_MEMORY & OUTPUT_ADVANCED) + debug(A_MEMORY, "%s: page %p index %zx ppn %x user_access %u, writeable %u\n", + __PRETTY_FUNCTION__, table, index, ppn, user_access, writeable); + + assert(((uint32*)table)[index] == 0); + + table[index].writeable = writeable; + table[index].user_access = user_access; + table[index].page_ppn = ppn; + table[index].present = 1; } -bool ArchMemory::mapPage(uint32 virtual_page, uint32 physical_page, uint32 user_access) +bool ArchMemory::mapPage(vpn_t virtual_page, ppn_t physical_page, bool user_access) { - RESOLVEMAPPING(page_dir_page_, virtual_page); + ArchMemoryMapping m = resolveMapping(virtual_page); + debug(A_MEMORY, "Map %zx => pd: %x, pt: %x, page: %x, user: %u\n", + virtual_page, m.pd_ppn, m.pt_ppn, physical_page, user_access); + assert((m.page_size == 0) || (m.page_size == PAGE_SIZE)); - if(page_directory[pde_vpn].pt.present == 0) - { - insertPT(pde_vpn, PageManager::instance()->allocPPN()); - } - assert(!page_directory[pde_vpn].page.size); + if (m.pt == 0) + { + m.pt_ppn = PageManager::instance().allocPPN(); + m.pt = (PageTableEntry*) getIdentAddressOfPPN(m.pt_ppn); + insert((PageDirPageTableEntry*)m.pd, m.pdi, m.pt_ppn, user_access, true); + } + + if (m.page == 0) + { + insert(m.pt, m.pti, physical_page, user_access, true); + return true; + } + + return false; +} +pointer ArchMemory::checkAddressValid(uint32 vaddress_to_check) const +{ + return checkAddressValid(page_dir_page_, vaddress_to_check); +} - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - if(pte_base[pte_vpn].present == 0) +pointer ArchMemory::checkAddressValid(ppn_t pd, uint32 vaddress_to_check) +{ + ArchMemoryMapping m = resolveMapping(pd, vaddress_to_check / PAGE_SIZE); + if (m.page != 0) { - pte_base[pte_vpn].writeable = 1; - pte_base[pte_vpn].user_access = user_access; - pte_base[pte_vpn].page_ppn = physical_page; - pte_base[pte_vpn].present = 1; - return true; + debug(A_MEMORY, "checkAddressValid, pd: %#zx, vaddr: %#zx -> true\n", pd, vaddress_to_check); + return m.page | (vaddress_to_check % m.page_size); } + else + { + debug(A_MEMORY, "checkAddressValid, pd: %#zx, vaddr: %#zx -> false\n", pd, vaddress_to_check); + return 0; + } +} - return false; +const ArchMemoryMapping ArchMemory::resolveMapping(size_t vpage) const +{ + return resolveMapping(page_dir_page_, vpage); } -pointer ArchMemory::checkAddressValid(uint32 vaddress_to_check) +const ArchMemoryMapping ArchMemory::resolveMapping(ppn_t pd, vpn_t vpage) { - uint32 virtual_page = vaddress_to_check / PAGE_SIZE; - RESOLVEMAPPING(page_dir_page_, virtual_page); - if (page_directory[pde_vpn].pt.present) + assert(pd); + ArchMemoryMapping m; + + VAddr a{vpage*PAGE_SIZE}; + + m.pti = a.pti; + m.pdi = a.pdi; + + if(A_MEMORY & OUTPUT_ADVANCED) { - if (page_directory[pde_vpn].page.size) - return getIdentAddressOfPPN(page_directory[pde_vpn].page.page_ppn,PAGE_SIZE * PAGE_TABLE_ENTRIES) | (vaddress_to_check % (PAGE_SIZE * PAGE_TABLE_ENTRIES)); + debug(A_MEMORY, "resolveMapping, vpn: %#zx, pdi: %zx(%zu), pti: %zx(%zu)\n", vpage, m.pdi, m.pdi, m.pti, m.pti); + } + + assert(pd < PageManager::instance().getTotalNumPages()); + m.pd = (PageDirEntry*) getIdentAddressOfPPN(pd); + m.pt = 0; + m.page = 0; + m.pd_ppn = pd; + m.pt_ppn = 0; + m.page_ppn = 0; + m.page_size = 0; - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - if (pte_base[pte_vpn].present) - return getIdentAddressOfPPN(pte_base[pte_vpn].page_ppn) | (vaddress_to_check % PAGE_SIZE); + if(m.pd[m.pdi].pt.present && !m.pd[m.pdi].pt.size) + { + m.pt_ppn = m.pd[m.pdi].pt.page_ppn; + assert(m.pt_ppn < PageManager::instance().getTotalNumPages()); + m.pt = (PageTableEntry*) getIdentAddressOfPPN(m.pt_ppn); + if(m.pt[m.pti].present) + { + m.page_size = PAGE_SIZE; + m.page_ppn = m.pt[m.pti].page_ppn; + m.page = getIdentAddressOfPPN(m.page_ppn); + } + } + else if(m.pd[m.pdi].pt.present && m.pd[m.pdi].pt.size) + { + m.page_size = PAGE_SIZE * PAGE_TABLE_ENTRIES; + m.page_ppn = m.pd[m.pdi].page.page_ppn; + m.page = getIdentAddressOfPPN(m.page_ppn); + } + + if(A_MEMORY & OUTPUT_ADVANCED) + { + debug(A_MEMORY, "resolveMapping, vpn: %#zx, pd[%s]: %#zx, pt[%s]: %#zx, page[%s]: %#zx, size: %#zx\n", + vpage, + (m.pd ? "P" : "-"), m.pd_ppn, + (m.pt ? "P" : "-"), m.pt_ppn, + (m.page ? "P" : "-"), m.page_ppn, + m.page_size); } - return 0; + + return m; } uint32 ArchMemory::get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, uint32 *physical_page, @@ -145,8 +287,8 @@ uint32 ArchMemory::get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, uint32 * else { if (physical_pte_page) - *physical_pte_page = page_directory[pde_vpn].pt.page_table_ppn; - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); + *physical_pte_page = page_directory[pde_vpn].pt.page_ppn; + PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_ppn); if (pte_base[pte_vpn].present) { if (physical_page) @@ -161,50 +303,72 @@ uint32 ArchMemory::get_PPN_Of_VPN_In_KernelMapping(uint32 virtual_page, uint32 * return 0; } -void ArchMemory::mapKernelPage(uint32 virtual_page, uint32 physical_page) +bool ArchMemory::mapKernelPage(vpn_t virtual_page, ppn_t physical_page, bool can_alloc_pages, bool memory_mapped_io) { - PageDirEntry *page_directory = kernel_page_directory; - uint32 pde_vpn = virtual_page / PAGE_TABLE_ENTRIES; - uint32 pte_vpn = virtual_page % PAGE_TABLE_ENTRIES; - assert(page_directory[pde_vpn].pt.present && page_directory[pde_vpn].pt.size == 0); - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - assert(!pte_base[pte_vpn].present); - pte_base[pte_vpn].writeable = 1; - pte_base[pte_vpn].page_ppn = physical_page; - pte_base[pte_vpn].present = 1; - asm volatile ("movl %%cr3, %%eax; movl %%eax, %%cr3;" ::: "%eax"); + debug(A_MEMORY, "Map kernel page %#zx -> PPN %#zx, alloc new pages: %u, mmio: %u\n", virtual_page, physical_page, can_alloc_pages, memory_mapped_io); + ArchMemoryMapping m = resolveMapping(((uint64) VIRTUAL_TO_PHYSICAL_BOOT(getKernelPagingStructureRootVirt()) / PAGE_SIZE), virtual_page); + + if (m.page_size) + { + return false; // Page already mapped + } + + m.pd = getKernelPagingStructureRootVirt(); + + assert(m.pt || can_alloc_pages); + if((!m.pt) && can_alloc_pages) + { + m.pt_ppn = PageManager::instance().allocPPN(); + m.pt = (PageTableEntry*) getIdentAddressOfPPN(m.pt_ppn); + + m.pd[m.pdi].pt.page_ppn = m.pt_ppn; + m.pd[m.pdi].pt.writeable = 1; + m.pd[m.pdi].pt.present = 1; + } + + assert(!m.pt[m.pti].present); + + if(memory_mapped_io) + { + m.pt[m.pti].write_through = 1; + m.pt[m.pti].cache_disabled = 1; + } + + m.pt[m.pti].page_ppn = physical_page; + m.pt[m.pti].writeable = 1; + m.pt[m.pti].present = 1; + + asm volatile ("movl %%cr3, %%eax; movl %%eax, %%cr3;" ::: "%eax"); // TODO: flushing caches after mapping a fresh page is pointless + + return true; } -void ArchMemory::unmapKernelPage(uint32 virtual_page) +void ArchMemory::unmapKernelPage(vpn_t virtual_page, bool free_page) { - PageDirEntry *page_directory = kernel_page_directory; - uint32 pde_vpn = virtual_page / PAGE_TABLE_ENTRIES; - uint32 pte_vpn = virtual_page % PAGE_TABLE_ENTRIES; - assert(page_directory[pde_vpn].pt.present && page_directory[pde_vpn].pt.size == 0); - PageTableEntry *pte_base = (PageTableEntry *) getIdentAddressOfPPN(page_directory[pde_vpn].pt.page_table_ppn); - assert(pte_base[pte_vpn].present); - pte_base[pte_vpn].present = 0; - pte_base[pte_vpn].writeable = 0; - PageManager::instance()->freePPN(pte_base[pte_vpn].page_ppn); + ArchMemoryMapping m = resolveMapping(((uint64) VIRTUAL_TO_PHYSICAL_BOOT(getKernelPagingStructureRootVirt()) / PAGE_SIZE), virtual_page); + assert(m.page && (m.page_size == PAGE_SIZE)); + + memset(&m.pt[m.pti], 0, sizeof(m.pt[m.pti])); + + if(free_page) + { + PageManager::instance().freePPN(m.page_ppn); + } asm volatile ("movl %%cr3, %%eax; movl %%eax, %%cr3;" ::: "%eax"); } -uint32 ArchMemory::getRootOfPagingStructure() +size_t ArchMemory::getPagingStructureRootPhys() const { - return page_dir_page_; + return page_dir_page_ * PAGE_SIZE; } -PageDirEntry* ArchMemory::getRootOfKernelPagingStructure() +PageDirEntry* ArchMemory::getKernelPagingStructureRootVirt() { return kernel_page_directory; } -uint32 ArchMemory::getValueForCR3() -{ - return page_dir_page_ * PAGE_SIZE; -} - -pointer ArchMemory::getIdentAddressOfPPN(uint32 ppn, uint32 page_size /* optional */) +ArchMemory& ArchMemory::kernelArchMemory() { - return (3U * 1024U * 1024U * 1024U) + (ppn * page_size); + static ArchMemory kernel_arch_mem((size_t)ArchMemory::getKernelPagingStructureRootPhys()/PAGE_SIZE); + return kernel_arch_mem; } diff --git a/arch/x86/32/source/CMakeLists.txt b/arch/x86/32/source/CMakeLists.txt index d59323abe..fdaff2d4f 100644 --- a/arch/x86/32/source/CMakeLists.txt +++ b/arch/x86/32/source/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC ../../common/include ) add_project_library(arch_x86_32) diff --git a/arch/x86/32/source/arch_apstartup.S b/arch/x86/32/source/arch_apstartup.S new file mode 100644 index 000000000..d91bf953c --- /dev/null +++ b/arch/x86/32/source/arch_apstartup.S @@ -0,0 +1,179 @@ +# https://www.airs.com/blog/archives/518 +.section .note.GNU-stack,"",@progbits + +# Startup code for APs + +# TODO: Pass these as arguments from the BSP instead of hardcoding +.set KERNEL_CS, 8*3 +.set KERNEL_DS, 8*2 + +.code16gcc +.section .text.apstartup, "ax", @progbits + +.macro ap_print string + leal \string, %esi + movw $0xE9, %dx +1: + lodsb + testb %al, %al + jz 2f + outb %al, %dx + jmp 1b +2: + .endm + +.macro ap_printn string len + leal \string, %esi + movl $\len, %ecx + movw $0xE9, %dx + rep outsb +.endm + + +.macro ap_dump addr num + movl $\addr, %ebx + movl $\num, %ecx +1: + testl %ecx, %ecx + jz 2f + + movb (%ebx), %al + movw $0xE9, %dx + outb %al, %dx + addl $1, %ebx + subl $1, %ecx + jmp 1b +2: +.endm + +# APs start in real mode +.global apstartup +apstartup: + cli + + movw %cs, %ax + movw %ax, %ds + movw %ax, %ss + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + leal (ap_startup_stack - apstartup), %esp + + ap_printn (ap_s_start - apstartup), len_ap_s_start + + ap_printn (ap_s_lgdt - apstartup), len_ap_s_lgdt +ap_load_gdt: + leal (ap_gdt32_ptr - apstartup), %eax + lgdt (%eax) + +ap_load_idt: + lidt ap_idt_ptr + + ap_printn (ap_s_pse - apstartup), len_ap_s_pse + + # Enable PSE + movl %cr4, %eax + orl $0x10,%eax + movl %eax, %cr4 + + ap_printn (ap_s_cr3 - apstartup), len_ap_s_cr3 + + # Load CR3 + leal (ap_kernel_cr3 - apstartup), %eax + movl (%eax), %eax + movl %eax, %cr3 + + ap_printn (ap_s_prot - apstartup), len_ap_s_prot + +ap_prot_enable: + # Enable protected mode, write protection in kernel mode and paging + movl %cr0, %eax + orl $0x80010001,%eax + movl %eax, %cr0 + + ljmpl $KERNEL_CS, $apstartup32 + hlt +.code32 + +.global ap_s_start +ap_s_start: + .ascii "AP started\n" + .set len_ap_s_start, . - ap_s_start +.global ap_s_pse +ap_s_pse: + .ascii "Enabling PSE\n" + len_ap_s_pse = . - ap_s_pse +.global ap_s_cr3 +ap_s_cr3: + .ascii "Loading CR3\n" + .set len_ap_s_cr3, . - ap_s_cr3 +.global ap_s_prot +ap_s_prot: + .ascii "Enabling protected mode, write protection in kernel mode and paging\n" + len_ap_s_prot = . - ap_s_prot +ap_s_prot_far_jmp: + .ascii "Far jumping to load protected mode code segment\n" + len_ap_s_prot_far_jmp = . - ap_s_prot_far_jmp +.global ap_s_lgdt +ap_s_lgdt: + .ascii "Loading GDT\n" + .set len_ap_s_lgdt, . - ap_s_lgdt +.global ap_s_segm +ap_s_segm: + .ascii "Loaded segment registers\n" + .set len_ap_s_segm, . - ap_s_segm +.global ap_s_ljmp +ap_s_ljmp: + .ascii "Far jmp to trampoline to load protected mode code segment\n" + .set len_ap_s_ljmp, . - ap_s_ljmp +.global ap_s_apstartup32 +ap_s_apstartup32: + .ascii "Jumping to apstartup32\n" + .set len_ap_s_apstartup32, . - ap_s_apstartup32 + + +# To be filled in by the BSP +.global ap_kernel_cr3 +.type ap_kernel_cr3, @object +ap_kernel_cr3: + .skip 4 + + +.global ap_gdt32_ptr +.type ap_gdt32_ptr, @object +ap_gdt32_ptr: + .skip 2 # uint16 limit + .skip 4 # uint32 addr + + +.global ap_gdt32 +.type ap_gdt32, @object +ap_gdt32: + .skip 0x70 + + +.global ap_idt_ptr +ap_idt_ptr: + .word 2048 # uint16 limit + .int ap_idt # uint32 addr +.global ap_idt +.type ap_idt, @object +ap_idt: + .skip 2048 + + +.align 0x1000 +.global ap_startup_stack # Use space between gdt and pml4 for stack +.type ap_startup_stack, @object +ap_startup_stack: + +.global ap_paging_root # PD +.type ap_paging_root, @object +ap_paging_root: +.skip 0x1000 +ap_paging_root_end: +.global ap_paging_root_end + + +.code32 diff --git a/arch/x86/32/source/init_boottime_pagetables.cpp b/arch/x86/32/source/init_boottime_pagetables.cpp index 731488f2f..bbfd63b9f 100644 --- a/arch/x86/32/source/init_boottime_pagetables.cpp +++ b/arch/x86/32/source/init_boottime_pagetables.cpp @@ -1,64 +1,72 @@ -#include "types.h" -#include "paging-definitions.h" -#include "offsets.h" +#include "debug_bochs.h" #include "multiboot.h" +#include "offsets.h" +#include "paging-definitions.h" + #include "ArchCommon.h" -#include "debug_bochs.h" #include "ArchMemory.h" -extern void* kernel_end_address; +#include "types.h" + +#include "assert.h" extern "C" void initialiseBootTimePaging() { uint32 i; PageDirEntry *pde_start = (PageDirEntry*) VIRTUAL_TO_PHYSICAL_BOOT((pointer )kernel_page_directory); - PageTableEntry *pte_start = (PageTableEntry*) VIRTUAL_TO_PHYSICAL_BOOT((pointer )kernel_page_tables); - uint32 kernel_last_page = VIRTUAL_TO_PHYSICAL_BOOT((pointer)&kernel_end_address) / PAGE_SIZE; - // we do not have to clear the pde since its in the bss + VAddr k_start{ArchCommon::getKernelStartAddress()}; + VAddr k_end{ArchCommon::getKernelEndAddress()}; + + // Boot time ident mapping for (i = 0; i < 5; ++i) { - pde_start[i].page.present = 1; + pde_start[i].page.page_ppn = i; pde_start[i].page.writeable = 1; pde_start[i].page.size = 1; - pde_start[i].page.page_ppn = i; + pde_start[i].page.present = 1; } - for (i = 0; i < 4; ++i) - { - pde_start[i + 512].pt.present = 1; - pde_start[i + 512].pt.writeable = 1; - pde_start[i + 512].pt.page_table_ppn = ((pointer) &pte_start[1024 * i]) / PAGE_SIZE; - } - // ok, we currently only fill in mappings for the first 4 megs (aka one page table) - // we do not have to zero out the other page tables since they're already empty - // thanks to the bss clearance. - // update, from now on, all pages up to the last page containing only rodata - // will be write protected. + // Map kernel page tables + for (size_t pdi = k_start.pdi; pdi <= k_end.pdi; ++pdi) + { + size_t pdi_offset = pdi - k_start.pdi; + pde_start[pdi].pt.page_ppn = ((pointer) &pte_start[PAGE_TABLE_ENTRIES * pdi_offset]) / PAGE_SIZE; + pde_start[pdi].pt.writeable = 1; + pde_start[pdi].pt.present = 1; + } + extern uint32 text_start_address; extern uint32 ro_data_end_address; + extern uint32 apstartup_text_load_begin; + extern uint32 apstartup_text_load_end; - uint32 last_ro_data_page = VIRTUAL_TO_PHYSICAL_BOOT((pointer)&ro_data_end_address) / PAGE_SIZE; - - // ppns are 1mb = 256 pages after vpns... - for (i = 0; i < last_ro_data_page - 256; ++i) + // Map kernel page tables + for(VAddr a{ArchCommon::getKernelStartAddress()}; a.addr < ArchCommon::getKernelEndAddress(); a.addr += PAGE_SIZE) { - pte_start[i].present = 1; - pte_start[i].writeable = 0; - pte_start[i].page_ppn = i + 256; - } - for (; i < kernel_last_page - 256; ++i) - { - pte_start[i].present = 1; - pte_start[i].writeable = 1; - pte_start[i].page_ppn = i + 256; + size_t pti = (a.pdi - k_start.pdi)*PAGE_TABLE_ENTRIES + a.pti; + assert(pti < sizeof(kernel_page_tables)/sizeof(kernel_page_tables[0])); + pte_start[pti].page_ppn = VIRTUAL_TO_PHYSICAL_BOOT(a.addr)/PAGE_SIZE; + // AP startup pages need to be writeable to fill in the GDT, ... + size_t ap_text_start = ((size_t)&apstartup_text_load_begin/PAGE_SIZE)*PAGE_SIZE; + size_t ap_text_end = (size_t)&apstartup_text_load_end; + + pte_start[pti].writeable = + (((a.addr >= (pointer)&text_start_address) && + (a.addr < (pointer)&ro_data_end_address)) && + !((a.addr >= ap_text_start) && + (a.addr < ap_text_end)) + ? 0 + : 1); + pte_start[pti].present = 1; } + if (ArchCommon::haveVESAConsole(0)) { for (i = 0; i < 4; ++i) @@ -73,7 +81,7 @@ extern "C" void initialiseBootTimePaging() } // identity mapping - for (i = 0; i < 256; ++i) + for (i = 0; i < 1024-768; ++i) { pde_start[i + 768].page.present = 1; pde_start[i + 768].page.writeable = 1; diff --git a/arch/x86/32/utils/kernel-ld-script.ld b/arch/x86/32/utils/kernel-ld-script.ld index 66efd7e6c..9472de208 100644 --- a/arch/x86/32/utils/kernel-ld-script.ld +++ b/arch/x86/32/utils/kernel-ld-script.ld @@ -2,22 +2,61 @@ * this linker script tells ld how to link and which symbols to add to the * kernel.x binary */ - + /* let the linker use its 'native' format (ELF/COFF/PE) */ OUTPUT_FORMAT("elf32-i386") ENTRY(entry) LS_Phys = 0x100000; /* 1 meg */ LS_Virt = 0x80000000; /* 2 gig + 1 meg due to 4m pages for kernel area*/ + +/* AP_STARTUP = 0x0000; */ + SECTIONS { - .text LS_Virt : AT(LS_Phys) + .mboot ALIGN(4096): AT(LS_Phys) { - PROVIDE(kernel_start_address = ABSOLUTE(.)); + KEEP(*(.mboot)) + } + + . = 0; + + /* . = AP_STARTUP; */ + .text.apstartup ALIGN(4096) : AT(LS_Phys + SIZEOF(.mboot)) + { + apstartup_text_begin = .; + *(.text.apstartup*) + apstartup_text_end = .; + } + + . = LS_Virt; + LS_START = .; + PROVIDE(kernel_start_address = ABSOLUTE(.)); + + mboot_load_begin = .; + . += SIZEOF(.mboot); + mboot_load_end = .; + + /* .mboot.load ALIGN(4096) (INFO) : AT(0x0) */ + /* { */ + /* . += SIZEOF(.mboot); */ + /* } */ + + apstartup_text_load_begin = .; + . += SIZEOF(.text.apstartup); + apstartup_text_load_end = .; + + .text ALIGN(4096) : AT(LS_Phys + (LS_Code - LS_START)) + { LS_Code = .; text_start_address = .; - *(.mboot) + /* KEEP(*(.mboot)) */ + /* . = ALIGN(4096); */ + /* apstartup_text_begin = .; */ + /* *(.text.apstartup*) */ + /* apstartup_text_end = .; */ + /* . = ALIGN(4096); */ *(.text) *(.text.*) text_end_address = .; @@ -25,10 +64,48 @@ SECTIONS ro_data_start_address = .; *(.rodata*) ro_data_end_address = .; + *(.eh_frame) + *(.eh_frame_hdr) . = ALIGN(4096); } - .data ALIGN(4096) : AT(LS_Phys + (LS_Data - LS_Code)) + /* .text.apstartup.load ALIGN(4096) : AT(LS_Phys + (LS_apstartup_load - LS_START)) */ + /* { */ + /* LS_apstartup_load = .; */ + /* apstartup_text_load_begin = .; */ + /* . += SIZEOF(.text.apstartup); */ + /* apstartup_text_load_end = .; */ + /* . = ALIGN(4096); */ + /* } */ + + /* .eh_frame ALIGN(4096) : AT(LS_Phys + (LS_eh_frame - LS_START)) */ + /* { */ + /* LS_eh_frame = .; */ + /* *(.eh_frame) */ + /* *(.eh_frame_hdr) */ + /* } */ + + .tdata ALIGN(4096) : AT(LS_Phys + (LS_tdata - LS_START)) + { + LS_tdata = .; + cls_start = .; + tdata_start = .; + *(.tdata) + tdata_end = .; + } + + .tbss ALIGN(4096) : AT(LS_Phys + (LS_tbss - LS_START)) + { + LS_tbss = .; + tbss_start = .; + *(.tbss) + . = ALIGN(4) + 4; /* For C++11 tread_local init-on-first-use flag */ + tbss_end = .; + cls_end = .; + . = ALIGN(4) + 4; + } + + .data ALIGN(4096) : AT(LS_Phys + (LS_Data - LS_START)) { LS_Data = .; data_start_address = .; @@ -41,17 +118,39 @@ SECTIONS . = ALIGN(4096); } - .bss : AT(LS_Phys + (LS_Bss - LS_Code)) + .init_array : AT(LS_Phys + (__init_array_start - LS_START)) + { + __init_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + __init_array_end = .; + } + + .preinit_array : AT(LS_Phys + (__preinit_array_start - LS_START)) + { + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } + + .fini_array : AT(LS_Phys + (__fini_array_start - LS_START)) + { + __fini_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + __fini_array_end = .; + } + + .bss ALIGN(4096) : AT(LS_Phys + (LS_Bss - LS_START)) { LS_Bss = .; bss_start_address = .; *(.bss) . = ALIGN(4096); bss_end_address = .; - } - .stab : AT(LS_Phys + (LS_Stab - LS_Code)) + .stab : AT(LS_Phys + (LS_Stab - LS_START)) { LS_Stab = .; stab_start_address_nr = .; @@ -59,9 +158,9 @@ SECTIONS stab_end_address_nr = .; . = ALIGN(4096); } - - .stabstr : AT(LS_Phys + (LS_Stabstr - LS_Code)) + + .stabstr : AT(LS_Phys + (LS_Stabstr - LS_START)) { LS_Stabstr = .; stabstr_start_address_nr = .; @@ -72,4 +171,4 @@ SECTIONS PROVIDE(kernel_end_address = .); } -} \ No newline at end of file +} diff --git a/arch/x86/64/CMakeLists.compiler b/arch/x86/64/CMakeLists.compiler index 8280627fb..717a71518 100644 --- a/arch/x86/64/CMakeLists.compiler +++ b/arch/x86/64/CMakeLists.compiler @@ -12,3 +12,9 @@ find_program(LD_EXECUTABLE x86_64-linux-gnu-gcc) find_program(OBJCOPY_EXECUTABLE x86_64-linux-gnu-objcopy) endif(CMAKE_CROSSCOMPILING) + + +# set(CMAKE_CXX_CLANG_TIDY +# clang-tidy; +# -checks=-*,clang-analyzer-*,clang-analyzer-cplusplus*; +# ) diff --git a/arch/x86/64/CMakeLists.include b/arch/x86/64/CMakeLists.include index abd204132..bf8de41d4 100644 --- a/arch/x86/64/CMakeLists.include +++ b/arch/x86/64/CMakeLists.include @@ -1,32 +1,61 @@ +cmake_minimum_required(VERSION 3.13) + +include(CheckCCompilerFlag) + set(KERNEL_BINARY kernel64.x) -if (CMAKE_COMPILER_IS_GNUCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 8 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8)) +check_c_compiler_flag(-fcf-protection=none HAS_FCF_PROTECTION_NONE) +if (HAS_FCF_PROTECTION_NONE) set(FCF_PROTECTION_FLAG -fcf-protection=none) else() set(FCF_PROTECTION_FLAG ) endif() -set(ARCH_X86_64_KERNEL_CFLAGS -m64 -O0 -gdwarf-2 -Wall -Wextra -Werror -Wno-error=format -Wno-nonnull-compare -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -ffreestanding -mcmodel=kernel -mno-red-zone -mgeneral-regs-only -mno-mmx -mno-sse2 -mno-sse3 -mno-3dnow ${FCF_PROTECTION_FLAG} ${NOPICFLAG}) +set(ARCH_X86_64_KERNEL_CFLAGS -O0 -gdwarf-4 -Wall -Wextra -Werror -Wno-error=format -Wno-nonnull-compare -Wno-address-of-packed-member -nostdinc -nostdlib -nostartfiles -nodefaultlibs -fno-builtin -fno-exceptions -fno-stack-protector -ffreestanding -mcmodel=kernel -mno-red-zone -mgeneral-regs-only -mno-mmx -mno-sse2 -mno-sse3 -mno-3dnow -fno-sync-libcalls ${FCF_PROTECTION_FLAG} ${NOPICFLAG}) -set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++17 -nostdinc++ -fno-rtti ${ARCH_X86_64_KERNEL_CFLAGS}) +set(KERNEL_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -std=gnu++20 -nostdinc++ -fno-rtti ${ARCH_X86_64_KERNEL_CFLAGS}) set(KERNEL_CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -std=gnu11 ${ARCH_X86_64_KERNEL_CFLAGS}) -set(ARCH_LD_ARGUMENTS -m64 -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -Wl,-melf_x86_64 -nostdinc -nostdlib -nodefaultlibs) -set(KERNEL_LD_ARGUMENT ${ARCH_LD_ARGUMENTS} -mcmodel=kernel ${NOPIEFLAG}) -set(ARCH_APPEND_LD_ARGUMENTS ) + +target_compile_options(arch_options INTERFACE + -m64) + +target_link_options(arch_options INTERFACE + -m64 -Wl,--build-id=none -Wl,-z,max-page-size=0x1000 -Wl,-melf_x86_64 -nostdinc -nostdlib -nodefaultlibs) + + +target_link_options(kernel_options INTERFACE + -mcmodel=kernel ${NOPIEFLAG}) + +target_compile_options(kernel_options INTERFACE + $<$:${KERNEL_CMAKE_CXX_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> + ) function(ARCH2OBJ ARCHOBJ_LIBNAME LIBRARY_NAME) - file(GLOB arch_files "*.32.C") + file(GLOB arch_files CONFIGURE_DEPENDS "*.32.C") if(arch_files) set(ARCHOBJS_TARGET ${LIBRARY_NAME}_archobjs) add_library(${ARCHOBJS_TARGET} OBJECT ${arch_files}) + target_include_directories(${ARCHOBJS_TARGET} + PRIVATE + "$" + ) + + target_include_directories(${ARCHOBJS_TARGET} + PRIVATE + "$" + ) + target_compile_options(${ARCHOBJS_TARGET} PRIVATE $<$:${KERNEL_CMAKE_CXX_FLAGS}> $<$:${KERNEL_CMAKE_C_FLAGS}> + $<$:${KERNEL_CMAKE_C_FLAGS}> -m32 -g0 -mcmodel=32 -mgeneral-regs-only -momit-leaf-frame-pointer -Wa,--64 -fno-toplevel-reorder ${FCF_PROTECTION_FLAG} ) @@ -34,33 +63,45 @@ function(ARCH2OBJ ARCHOBJ_LIBNAME LIBRARY_NAME) endif(arch_files) endfunction(ARCH2OBJ) -set(KERNEL_IMAGE_OBJCOPY COMMAND ${OBJCOPY_EXECUTABLE} ${PROJECT_BINARY_DIR}/kernel.x --strip-unneeded ${PROJECT_BINARY_DIR}/kernel.x) + if ("${DEBUG}" STREQUAL "1") - set(KERNEL_IMAGE_OBJCOPY ) + set(STRIP_DEBUG_INFO "") +else() + set(STRIP_DEBUG_INFO ${OBJCOPY_EXECUTABLE} $ --strip-unneeded $) endif() -set(KERNEL_IMAGE_OBJCOPY - COMMAND ${PROJECT_BINARY_DIR}/add-dbg ${PROJECT_BINARY_DIR}/kernel.x ${PROJECT_BINARY_DIR}/kernel.dbg - ${KERNEL_IMAGE_OBJCOPY} - COMMAND mv ${PROJECT_BINARY_DIR}/kernel.x ${PROJECT_BINARY_DIR}/kernel64.x && ${OBJCOPY_EXECUTABLE} -O elf32-i386 ${PROJECT_BINARY_DIR}/kernel64.x ${PROJECT_BINARY_DIR}/kernel.x - ) + +add_custom_command(TARGET kernel + POST_BUILD + BYPRODUCTS ${KERNEL_BINARY} kernel.dbg kernel.unstripped + COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_BINARY_DIR}/${KERNEL_BINARY} + COMMAND ${CMAKE_COMMAND} -E copy $ ${PROJECT_BINARY_DIR}/kernel.unstripped + COMMAND ${PROJECT_BINARY_DIR}/add-dbg $ ${PROJECT_BINARY_DIR}/kernel.dbg + COMMAND ${STRIP_DEBUG_INFO} + COMMAND ${OBJCOPY_EXECUTABLE} -O elf32-i386 $ $) + +add_dependencies(kernel add-dbg) + set(AVAILABLE_MEMORY 8M) +set(NUM_CPUS 4) + +set(DISK_MOUNT_ARG -drive file=${HDD_IMAGE},index=0,media=disk,id=sweb-hdd,if=ide) set(QEMU_BIN qemu-system-x86_64) -set(QEMU_FLAGS_COMMON -m ${AVAILABLE_MEMORY} -drive file=${HDD_IMAGE},index=0,media=disk -debugcon stdio -no-reboot) +set(QEMU_FLAGS_COMMON -name SWEB -m ${AVAILABLE_MEMORY} ${DISK_MOUNT_ARG} -smp ${NUM_CPUS} -debugcon stdio -no-reboot -no-shutdown -s -d guest_errors) string(REPLACE ";" " " QEMU_FLAGS_COMMON_STR "${QEMU_FLAGS_COMMON}") # kvm: Run kvm in non debugging mode add_custom_target(kvm - COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -enable-kvm -cpu host - COMMENT "Executing `${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -enable-kvm -cpu host`" - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - COMMAND reset -I - ) + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -enable-kvm -cpu host + COMMENT "Executing `${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -enable-kvm -cpu host`" + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMAND reset -I + ) # qemu: Run qemu in non debugging mode add_custom_target(qemu - COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -cpu qemu64 | tee output.log + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -cpu qemu64 2>&1 | tee output.log COMMENT "Executing `${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -cpu qemu64`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I @@ -68,23 +109,23 @@ add_custom_target(qemu # qemugdb: Run qemu in debugging mode add_custom_target(qemugdb - COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -s -S | tee output.log - COMMENT "Executing `gdb ${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -s -S on localhost:1234`" + COMMAND ${QEMU_BIN} ${QEMU_FLAGS_COMMON} -S 2>&1 | tee output.log + COMMENT "Executing `gdb ${QEMU_BIN} ${QEMU_FLAGS_COMMON_STR} -S on localhost:1234`" WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND reset -I ) # qemutacos: Run qemu in pipe monitor mode for tacos add_custom_target(qemutacos - COMMAND ${QEMU_BIN} -hda ${HDD_IMAGE} -m ${AVAILABLE_MEMORY} -snapshot -monitor pipe:qemu -nographic -debugcon stdio - COMMENT "Executing `${QEMU_BIN} -hda ${HDD_IMAGE} -m 8M -snapshot -monitor pipe:qemu -nographic -debugcon stdio`" - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - ) + COMMAND ${QEMU_BIN} -hda ${HDD_IMAGE} -m ${AVAILABLE_MEMORY} -snapshot -monitor pipe:qemu -nographic -debugcon stdio + COMMENT "Executing `${QEMU_BIN} -hda ${HDD_IMAGE} -m 8M -snapshot -monitor pipe:qemu -nographic -debugcon stdio`" + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + ) # net-init: Initializes br0 for target "net" add_custom_target(net-init - COMMAND sudo ${PROJECT_SOURCE_DIR}/utils/netinit.sh - COMMENT "Executing `sudo utils/netinit.sh`" - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - COMMAND reset -I - ) + COMMAND sudo ${PROJECT_SOURCE_DIR}/utils/netinit.sh + COMMENT "Executing `sudo utils/netinit.sh`" + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + COMMAND reset -I + ) diff --git a/arch/x86/64/CMakeLists.txt b/arch/x86/64/CMakeLists.txt index 07ed29d0f..790b38a4b 100644 --- a/arch/x86/64/CMakeLists.txt +++ b/arch/x86/64/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC ../../common/include ../common/include include diff --git a/arch/x86/64/CMakeLists.userspace b/arch/x86/64/CMakeLists.userspace index 45bd93822..b443d6c02 100644 --- a/arch/x86/64/CMakeLists.userspace +++ b/arch/x86/64/CMakeLists.userspace @@ -1,2 +1,10 @@ -set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -std=gnu11 -g -gdwarf-4 -O0 -m64 -static -nostdinc -fno-builtin -nostdlib -nodefaultlibs -fno-stack-protector -fno-common -Werror=implicit-function-declaration -Wno-error=unused-variable -fno-stack-clash-protection) -set(ARCH_USERSPACE_LINKER_OPTIONS -static) +set(ARCH_USERSPACE_COMPILE_OPTIONS -Wall -Werror -g -gdwarf-4 -O0 -static -nostdinc -fno-builtin -nostdlib -nodefaultlibs -nolibc -fno-stack-protector -fno-common -Wno-error=unused-variable -fno-stack-clash-protection) + +target_link_options(userspace_options INTERFACE + -static) + +target_compile_options(userspace_options INTERFACE + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu++20 -nostdinc++> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS} -std=gnu11 -Werror=implicit-function-declaration> + $<$:${ARCH_USERSPACE_COMPILE_OPTIONS}> +) diff --git a/arch/x86/64/include/ArchCpuLocalStorage.h b/arch/x86/64/include/ArchCpuLocalStorage.h new file mode 100644 index 000000000..3fdff331a --- /dev/null +++ b/arch/x86/64/include/ArchCpuLocalStorage.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#define cpu_local thread_local +#define __cpu __thread + +namespace CpuLocalStorage +{ + void initCpuLocalStorage(); + size_t getClsSize(); + + char* allocCls(); + void setCls(char* cls); + bool ClsInitialized(); +}; diff --git a/arch/x86/64/include/ArchMemory.h b/arch/x86/64/include/ArchMemory.h index 2bfbbd38c..f14762237 100644 --- a/arch/x86/64/include/ArchMemory.h +++ b/arch/x86/64/include/ArchMemory.h @@ -1,60 +1,78 @@ #pragma once -#include "types.h" #include "offsets.h" #include "paging-definitions.h" +#include "types.h" +#include +#include + +#include "EASTL/atomic.h" + +extern PageMapLevel4Entry kernel_page_map_level_4[PAGE_MAP_LEVEL_4_ENTRIES] __attribute__((aligned(PAGE_SIZE))); +extern PageDirPointerTableEntry kernel_page_directory_pointer_table[2 * PAGE_DIR_POINTER_TABLE_ENTRIES] __attribute__((aligned(PAGE_SIZE))); +extern PageDirEntry kernel_page_directory[2 * PAGE_DIR_ENTRIES] __attribute__((aligned(PAGE_SIZE))); +extern PageTableEntry kernel_page_table[8 * PAGE_TABLE_ENTRIES] __attribute__((aligned(PAGE_SIZE))); + + struct ArchMemoryMapping { - PageMapLevel4Entry* pml4; - PageDirPointerTableEntry* pdpt; - PageDirEntry* pd; - PageTableEntry* pt; - pointer page; - - uint64 pml4_ppn; - uint64 pdpt_ppn; - uint64 pd_ppn; - uint64 pt_ppn; - uint64 page_ppn; - - uint64 page_size; - - uint64 pml4i; - uint64 pdpti; - uint64 pdi; - uint64 pti; + PageMapLevel4Entry* pml4; + PageDirPointerTableEntry* pdpt; + PageDirEntry* pd; + PageTableEntry* pt; + pointer page; + + ppn_t pml4_ppn; + ppn_t pdpt_ppn; + ppn_t pd_ppn; + ppn_t pt_ppn; + ppn_t page_ppn; + + size_t page_size; + + uint64_t pml4i; + uint64_t pdpti; + uint64_t pdi; + uint64_t pti; }; class ArchMemory { - public: +public: ArchMemory(); + ArchMemory(ppn_t pml4_ppn); ~ArchMemory(); - uint64 page_map_level_4_; - static constexpr size_t RESERVED_START = 0xFFFFFFFF80000ULL; - static constexpr size_t RESERVED_END = 0xFFFFFFFFC0000ULL; + /// Prevent accidental copying/assignment, can be implemented if needed + ArchMemory(const ArchMemory& src) = delete; + ArchMemory& operator=(const ArchMemory& src) = delete; /** * Maps a virtual page to a physical page and creates the upper paging-hierarchy tables on demand. - * @param virtual_page The virtual page to map - * @param physical_page The physical page to which the virtual page shall be mapped - * @param user_access PTE flag indicating whether the virtual page shall be accessible by threads in user-mode - * @return True if successful, false otherwise (the PT entry already exists) + * @param virtual_page The virtual page number (VPN) to map (not the full address) + * @param physical_page The physical page number (PPN) which the virtual page should be mapped to + * @param user_access page table entry flag indicating whether the virtual page should be accessible in user-mode + * @return true if successful, false otherwise (the PT entry already exists) */ - [[nodiscard]] bool mapPage(uint64 virtual_page, uint64 physical_page, uint64 user_access); + [[nodiscard]] bool mapPage(vpn_t virtual_page, ppn_t physical_page, bool user_access); /** - * Removes the mapping to a virtual_page by marking its PTE entry as non-valid and frees the underlying physical page. - * Potentially de-allocates the upper paging-hierarchy tables, depending on their occupancy. - * @param virtual_page The virtual page which shall be unmapped + * Removes the mapping to a virtual_page by marking its page table entry as non-present and frees the mapped physical page. + * Unmaps and de-allocates the upper paging-hierarchy tables if they are no longer required. + * @param virtual_page The virtual page number (VPN) to be unmapped (not the full address) * @return Currently always returns true */ - bool unmapPage(uint64 virtual_page); + bool unmapPage(vpn_t virtual_page); + + [[nodiscard]] static bool mapKernelPage(vpn_t virtual_page, ppn_t physical_page, bool can_alloc_pages = false, bool memory_mapped_io = false); + static void unmapKernelPage(vpn_t virtual_page, bool free_page = true); /** - * Takes a physical page number (PPN) and returns a virtual address that can be used to access given physical page. + * Takes a physical page number (PPN) and returns a virtual address that can be used to access the given physical page. + * At kernel startup, all physical memory is mapped into a contiguous block of virtual memory in kernel space at a fixed offset. + * This allows access to a physical addresses via the virtual address (IDENT_MAPPING_START + ). + * This technique is called identity mapping, 1:1 mapping or direct mapping (Linux) * https://wiki.osdev.org/Identity_Paging * @param ppn The physical page number * @param page_size Optional, defaults to 4k pages, but you need to set it to @@ -62,64 +80,125 @@ class ArchMemory * @return Virtual Address above KERNEL_START pointing to the start of a memory segment that * acts as a 1:1 mapping to the given physical page */ - static pointer getIdentAddressOfPPN(uint64 ppn, uint32 page_size=PAGE_SIZE) - { - return 0xFFFFF00000000000ULL | (ppn * page_size); - } + static pointer getIdentAddressOfPPN(ppn_t ppn, size_t page_size = PAGE_SIZE); + static pointer getIdentAddress(size_t address); // TODO: rename to distinguish it from getIdentAddressOfPPN /** * Checks whether the given virtual address is valid and mapped to physical memory. - * @param vaddress_to_check The virtual address to check - * @return True if mapping exists, false otherwise (accessing it would result in a pagefault) + * @param vaddress_to_check The virtual address to check (full address, not just the VPN) + * @return physical address corresponding to the the virtual address if it is mapped, zero otherwise + */ + pointer checkAddressValid(size_t vaddress_to_check) const; + /** + * Checks whether the given virtual address is valid and mapped to physical memory. + * @param pml4 pml4 ppn to use as page table root for the check + * @param vaddress_to_check The virtual address to check (full address, not VPN) + * @return physical address (full address, not PPN) corresponding to the the virtual address if it is mapped, zero otherwise */ - pointer checkAddressValid(uint64 vaddress_to_check); + static pointer checkAddressValid(ppn_t pml4, size_t vaddress_to_check); /** - * Fills out an ArchMemoryMapping object by translating the given - * virtual page and collecting infos from the page tables along the way. + * Look at the page tables for the given virtual page (VPN) and collect information about the memory mapping. + * Returns information about the final page table entry as well as the intermediate paging levels. * @param pml4 The pml4 of the ArchMemory object for which to translate the vpage - * @param vpage The virtual page to resolve - * @return Returns a completely or partially filled out ArchMemoryMapping object + * @param vpage The virtual page (VPN) to resolve + * @return an ArchMemoryMapping object containing information about the memory mapping of the given virtual page */ - static const ArchMemoryMapping resolveMapping(uint64 pml4, uint64 vpage); + const ArchMemoryMapping resolveMapping(vpn_t vpage) const; + /** + * Retrieve information about the page table mapping for a given virtual address (see resolveMapping above). + * This variant allows you to specify the pml4 to use as paging root. + * @param pml4 the pml4 PPN to use as page table root + * @param vpage The virtual page (VPN) to resolve + * @return an ArchMemoryMapping object containing information about the memory mapping of the given virtual page + */ + static const ArchMemoryMapping resolveMapping(ppn_t pml4, vpn_t vpage); - const ArchMemoryMapping resolveMapping(uint64 vpage); + static size_t get_PPN_Of_VPN_In_KernelMapping(vpn_t virtual_page, ppn_t* physical_page, ppn_t* physical_pte_page=0); - static uint64 get_PPN_Of_VPN_In_KernelMapping(uint64 virtual_page, - uint64 *physical_page, uint64 *physical_pte_page=nullptr); + /** + * Get the value that should be stored in the %cr3 register in order to use this ArchMemory virtual address space + * + * @return value to be stored in cr3 + */ + size_t getValueForCR3() const; + + size_t getPagingStructureRootPhys() const; + + static PageMapLevel4Entry* getKernelPagingStructureRootVirt(); + static size_t getKernelPagingStructureRootPhys(); + + /** + * Load the %cr3 register with the given value to switch the virtual address space used by the CPU + * + * @param cr3_value new %cr3 value to be used + */ + static void loadPagingStructureRoot(size_t cr3_value); - static void mapKernelPage(uint64 virtual_page, uint64 physical_page); + /** + * Flush TLB entries for the given virtual address (full address, not VPN) + * on the current CPU + * + * @param addr virtual address to flush from TLB + */ + static void flushLocalTranslationCaches(size_t addr); + /** + * Flush TLB entries for the given virtual address (full address, not VPN) + * on all CPUs by sending TLB shootdown requests + * + * @param addr virtual address to flush from TLB + */ + static void flushAllTranslationCaches(size_t addr); - static void unmapKernelPage(uint64 virtual_page); + /** + * Get the ArchMemory object used by kernel threads + * (kernel space is the same for all threads) + * + * @return reference to the kernel ArchMemory object + */ + static ArchMemory& kernelArchMemory(); - static PageMapLevel4Entry* getRootOfKernelPagingStructure(); +private: - /// Prevents accidental copying/assignment, can be implemented if needed - ArchMemory(ArchMemory const &src) = delete; - ArchMemory &operator=(ArchMemory const &src) = delete; + /** + * Check if the given page table is empty (no entries in use) + * + * @tparam T Page table type (level) + * @tparam NUM_ENTRIES Number of entries in the page table + * @param table virtual address of the page table + * @return true if the table is empty + * @return false if the table is still in use + */ + template + static bool tableEmpty(T* table); - private: /** - * Adds a PML4Entry, PDPTEntry, PDEntry or PTEntry to the given PML4, PDPT, PD or PT respectively. - * (In other words, adds the reference to a new page table to a given page directory, for example.) + * Clear an entry in a page table by setting it to non-present and clearing all other page table entry bits. * - * @param map_ptr identity address of the physical page containing the target page table. - * @param index Index of the PML4Entry, PDPTEntry, PDEntry or PTEntry to be added. - * @param ppn physical page number of the new PDPT, PD, PT or page. - * @param bzero if true, the content of the newly inserted physical page is set to 0 - * @param size if true, a 1G or 2M page is inserted, otherwise a PD or PT respectively. - * @param user_access if true, threads in user mode may access the inserted page. Otherwise, a kernel page is mapped - * @param writable if true, the inserted pages are writable + * @tparam T Page table type (level) + * @param table virtual address of the page table + * @param index index of the page table entry to clear */ - template static void insert(pointer map_ptr, uint64 index, uint64 ppn, uint64 bzero, uint64 size, uint64 user_access, uint64 writeable); + template + static void removeEntry(T* table, size_t index); /** - * Removes a PML4Entry, PDPTEntry, PDEntry or PTEntry from a given PML4, PDPT, PD or PT if it is present - * in the first place. This is done by setting the entry to present = 0 and clearing all other bits. + * Fill a page table entry. Sets the corresponding page table entry fields and marks the entry as present. * - * @param map_ptr identity address of the physical page containing the target page table. - * @param index Index of the PML4Entry, PDPTEntry, PDEntry or PTEntry to be removed. - * @return True if the table map_ptr is full of zeroes and thus able to be freed. + * @tparam T Page table type (level) + * @param table virtual address of the page table + * @param index index of the page table entry to fill + * @param ppn physical page number (PPN) the entry should map to + * @param user_access whether the virtual page corresponding to the entry should be accessible in user mode + * @param writeable whether the virtual page corresponding to the entry should be writeable + * @param memory_mapped_io disable the cache and mark it as write-through for this page to prevent it from interfering with memory mapped hardware I/O */ - template static bool checkAndRemove(pointer map_ptr, uint64 index); -}; \ No newline at end of file + template + static void insert(T* table, size_t index, ppn_t ppn, bool user_access, bool writeable, bool memory_mapped_io); + + + /** + * The PPN of the pml4 root page table of this virtual address space + */ + ppn_t page_map_level_4_; +}; diff --git a/arch/x86/64/include/ArchMulticore.h b/arch/x86/64/include/ArchMulticore.h new file mode 100644 index 000000000..77208431c --- /dev/null +++ b/arch/x86/64/include/ArchMulticore.h @@ -0,0 +1,77 @@ +#pragma once + +#include "APIC.h" +#include "AtomicMpScQueue.h" +#include "Cpu.h" +#include "IdleThread.h" +#include "Mutex.h" +#include "RemoteFunctionCall.h" +#include "SMP.h" +#include "SegmentUtils.h" +#include "X2Apic.h" +#include "paging-definitions.h" + +#include "ArchCpuLocalStorage.h" + +#include "types.h" + +#include "EASTL/atomic.h" +#include "EASTL/vector.h" + +#define CPU_STACK_SIZE 4*PAGE_SIZE + +#define STOP_INT_VECTOR 90 +#define MESSAGE_INT_VECTOR 101 + + +class ArchCpu : public Cpu +{ +public: + ArchCpu(); + + Apic* lapic; + + IrqDomain& rootIrqDomain(); + +private: + IrqDomain** root_domain_ptr; +}; + + +extern cpu_local Apic* cpu_lapic; +extern cpu_local char cpu_stack[CPU_STACK_SIZE]; +extern cpu_local TSS cpu_tss; +extern cpu_local IdleThread* idle_thread; + +constexpr size_t AP_STARTUP_PADDR = 0x00; + +class Allocator; + +class ArchMulticore +{ + public: + static void initialize(); + + static void reservePages(Allocator& alloc); + + static void startOtherCPUs(); + static void stopAllCpus(); + static void stopOtherCpus(); + + static void startAP(uint8_t apic_id, size_t entry_addr); + + static void sendFunctionCallMessage(ArchCpu& cpu, RemoteFunctionCallMessage* fcall_message); + static void notifyMessageAvailable(ArchCpu& cpu); + + static void initApplicationProcessorCpu(); + static void initCpuLocalData(bool boot_cpu = false); + + static char* cpuStackTop(); + + private: + static void initCpuLocalGDT(GDT& template_gdt); + static void initCpuLocalTSS(size_t boot_stack_top); + static void prepareAPStartup(size_t entry_addr); + + [[noreturn]] static void waitForSystemStart(); +}; diff --git a/arch/x86/64/include/ArchThreads.h b/arch/x86/64/include/ArchThreads.h index 1a2afc038..a104b63be 100644 --- a/arch/x86/64/include/ArchThreads.h +++ b/arch/x86/64/include/ArchThreads.h @@ -1,7 +1,11 @@ #pragma once +#include "Scheduler.h" + #include "types.h" +#include "EASTL/unique_ptr.h" + /** * The flag for full barrier synchronization. */ @@ -11,45 +15,50 @@ struct ArchThreadRegisters { - uint64 rip; // 0 - uint64 cs; // 8 - uint64 rflags; // 16 - uint64 rax; // 24 - uint64 rcx; // 32 - uint64 rdx; // 40 - uint64 rbx; // 48 - uint64 rsp; // 56 - uint64 rbp; // 64 - uint64 rsi; // 72 - uint64 rdi; // 80 - uint64 r8; // 88 - uint64 r9; // 96 - uint64 r10; // 104 - uint64 r11; // 112 - uint64 r12; // 120 - uint64 r13; // 128 - uint64 r14; // 136 - uint64 r15; // 144 - uint64 ds; // 152 - uint64 es; // 160 - uint64 fs; // 168 - uint64 gs; // 176 - uint64 ss; // 184 - uint64 rsp0; // 192 - uint64 cr3; // 200 - uint32 fpu[28]; // 208 + uint64 rip; + uint64 cs; + uint64 rflags; + uint64 rax; + uint64 rcx; + uint64 rdx; + uint64 rbx; + uint64 rsp; + uint64 rbp; + uint64 rsi; + uint64 rdi; + uint64 r8; + uint64 r9; + uint64 r10; + uint64 r11; + uint64 r12; + uint64 r13; + uint64 r14; + uint64 r15; + uint64 ds; + uint64 es; + uint64 fs; + uint64 gs; + uint64 ss; + uint64 rsp0; + uint64 cr3; + uint64 fsbase; + uint32 fpu[28]; + + void setKernelStack(size_t k_stack) + { + rsp0 = k_stack; + } + + void setStack(size_t stack) + { + rsp = stack; + rbp = stack; + } }; class Thread; class ArchMemory; -/** - * this is where the thread info for task switching is stored - * - */ -extern ArchThreadRegisters *currentThreadRegisters; -extern Thread *currentThread; - /** * Collection of architecture dependant code concerning Task Switching * @@ -58,6 +67,8 @@ class ArchThreads { public: + [[noreturn]] static void startThreads(Thread* init_thread); + /** * allocates space for the currentThreadRegisters */ @@ -69,43 +80,52 @@ class ArchThreads * @param start_function instruction pointer is set so start function * @param stack stackpointer */ - static void createKernelRegisters(ArchThreadRegisters *&info, void* start_function, void* kernel_stack); + static eastl::unique_ptr createKernelRegisters(void* start_function, + void* kernel_stack); -/** - * creates the ArchThreadRegisters for a user thread - * @param info where the ArchThreadRegisters is saved - * @param start_function instruction pointer is set so start function - * @param user_stack pointer to the userstack - * @param kernel_stack pointer to the kernel stack - */ - static void createUserRegisters(ArchThreadRegisters *&info, void* start_function, void* user_stack, void* kernel_stack); + /** + * creates the ArchThreadRegisters for a user thread + * @param info where the ArchThreadRegisters is saved + * @param start_function instruction pointer is set so start function + * @param user_stack pointer to the userstack + * @param kernel_stack pointer to the kernel stack + */ + static eastl::unique_ptr createUserRegisters(void* start_function, + void* user_stack, + void* kernel_stack); -/** - * changes an existing ArchThreadRegisters so that execution will start / continue - * at the function specified - * it does not change anything else, and if the thread info / thread was currently - * executing something else this will lead to a lot of problems - * USE WITH CARE, or better, don't use at all if you're a student - * @param the ArchThreadRegisters that we are going to mangle - * @param start_function instruction pointer for the next instruction that gets executed - */ - static void changeInstructionPointer(ArchThreadRegisters *info, void* function); + /** + * changes an existing ArchThreadRegisters so that execution will start / continue + * at the function specified + * it does not change anything else, and if the thread info / thread was currently + * executing something else this will lead to a lot of problems + * USE WITH CARE, or better, don't use at all if you're a student + * @param info the ArchThreadRegisters that we are going to mangle + * @param start_function instruction pointer for the next instruction that gets executed + */ + static void changeInstructionPointer(ArchThreadRegisters& info, void* function); + + static void* getInstructionPointer(ArchThreadRegisters& info); + + static void setInterruptEnableFlag(ArchThreadRegisters& info, bool interrupts_enabled); + static bool getInterruptEnableFlag(ArchThreadRegisters& info); /** - * * on x86: invokes int65, whose handler facilitates a task switch - * */ static void yield(); /** * sets a threads page map level 4 * - * @param *thread Pointer to Thread Object + * @param thread Pointer to Thread Object * @param arch_memory the arch memory object for the address space */ static void setAddressSpace(Thread *thread, ArchMemory& arch_memory); + static void switchToAddressSpace(Thread* thread); + static void switchToAddressSpace(ArchMemory& arch_memory); + /** * uninterruptable locked operation * exchanges value in variable lock with new_value and returns the old_value @@ -114,17 +134,35 @@ class ArchThreads * @param new_value to set variable lock to * @returns old_value of variable lock */ - static size_t testSetLock(size_t &lock, size_t new_value); + template + static T testSetLock(volatile T &lock, T new_value) + { + return __sync_lock_test_and_set(&lock, new_value); + } + + /** + * Counterpart to testSetLock() + * Writes 0 to the lock variable and provides a memory release barrier + * (ensures all previous memory stores are visible) + */ + template + static void syncLockRelease(volatile T &lock) + { + __sync_lock_release(&lock); + } /** - * atomically increments or decrements value by increment + * atomically increments or decrements target by increment * - * @param &value Reference to value + * @param &target Reference to target * @param increment can be positive or negative - * @returns old value of value + * @returns old value of target */ - static uint64 atomic_add(uint64 &value, int64 increment); - static int64 atomic_add(int64 &value, int64 increment); + template + static T atomic_add(T& target, T increment) + { + return __atomic_fetch_add(&target, increment, __ATOMIC_SEQ_CST); + } /** * Atomically set a target to another value. @@ -132,10 +170,11 @@ class ArchThreads * @param target The target which shall be set * @param value The value which shall be set */ - static void atomic_set(uint32 &target, uint32 value); - static void atomic_set(int32 &target, int32 value); - static void atomic_set(uint64 &target, uint64 value); - static void atomic_set(int64 &target, int64 value); + template + static void atomic_set(T& target, T value) + { + __atomic_store_n(&target, value, __ATOMIC_SEQ_CST); + } /** * @@ -159,6 +198,15 @@ class ArchThreads * @param start_function instruction pointer is set to start function * @param stack stackpointer */ - static void createBaseThreadRegisters(ArchThreadRegisters *&info, void* start_function, void* stack); + static eastl::unique_ptr createBaseThreadRegisters(void* start_function, void* stack); }; +class WithAddressSpace +{ +public: + WithAddressSpace(Thread* thread); + WithAddressSpace(ArchMemory& arch_memory); + ~WithAddressSpace(); +private: + size_t prev_addr_space_; +}; diff --git a/arch/x86/64/include/InterruptDescriptorTable.h b/arch/x86/64/include/InterruptDescriptorTable.h new file mode 100644 index 000000000..a4ded24c2 --- /dev/null +++ b/arch/x86/64/include/InterruptDescriptorTable.h @@ -0,0 +1,133 @@ +#pragma once + +#include "types.h" +#include +#include + +#include "EASTL/array.h" + +using handler_func_t = void (*)(); + +static constexpr size_t NUM_INTERRUPTS = 256; + +// https://wiki.osdev.org/Exceptions +constexpr bool interruptHasErrorcode(size_t N) +{ + if ((N == 8) || (10 <= N && N <= 14) || (N == 17) || (N == 21) || (N == 29) || + (N == 30)) + return true; + + return false; +} + +constexpr int interruptPrivilegeLevel(size_t interrupt_number) +{ + if (interrupt_number == 0x80) + return DPL_USER; + else + return DPL_KERNEL; +} + +struct [[gnu::packed]] InterruptGateDesc +{ + uint16_t offset_ld_lw : 16; // low word / low dword of handler entry point's address + uint16_t segment_selector : 16; // (code) segment the handler resides in + uint8_t ist : 3; // interrupt stack table index + uint8_t zeros : 5; // set to zero + uint8_t type : 4; // set to TYPE_TRAP_GATE or TYPE_INTERRUPT_GATE + uint8_t zero_1 : 1; // unsued - set to zero + uint8_t dpl : 2; // descriptor protection level + uint8_t present : 1; // present- flag - set to 1 + uint16_t offset_ld_hw : 16; // high word / low dword of handler entry point's address + uint32_t offset_hd : 32; // high dword of handler entry point's address + uint32_t reserved : 32; + + constexpr InterruptGateDesc() = default; + InterruptGateDesc(handler_func_t offset, uint8_t dpl); + constexpr bool operator==(const InterruptGateDesc&) const = default; + + void setOffset(handler_func_t); + handler_func_t offset(); + + enum Type + { + // interrupt gate => IF flag *is* cleared + // trap gate => IF flag is *not* cleared + TASK = 0x5, + INTERRUPT_16BIT = 0x6, + TRAP_16BIT = 0x7, + INTERRUPT = 0xE, + TRAP = 0xF, + }; +}; + +struct [[gnu::packed]] IDTR +{ + uint16_t limit; + uintptr_t base; + + void load(); +}; + +/** + * The main interrupt handler. Defined in arch_interrupts.S + */ +extern void arch_interruptHandler(); + +/** Interrupt entry stub. + * Pushes interrupt number onto the stack before jumping to the main + * interrupt hander arch_interruptHandler(). Also pushes a fake error code if + * not already done by the processor itself to ensure stack layout consistency. + * + * @tparam N interrupt number + */ +template +[[gnu::naked, noreturn]] void interruptEntry() +{ + // compile time constexpr if -> push instruction is only generated if required. No check at runtime + if constexpr (!interruptHasErrorcode(N)) + asm volatile("pushq $0\n"); + + // Main interrupt handler code is responsible for saving registers, etc... + asm volatile("pushq %[num]\n" + "jmp arch_interruptHandler\n" + ::[num] "i"(N)); +} + +template +auto make_element() +{ + return InterruptGateDesc{&interruptEntry, interruptPrivilegeLevel(I)}; +} + +template +constexpr auto generate_impl(eastl::index_sequence) -> eastl::array +{ + return {make_element()...}; +} + +template +constexpr eastl::array generate() +{ + return generate_impl(eastl::make_index_sequence()); +} + +struct InterruptDescriptorTable +{ + constexpr InterruptDescriptorTable() : + entries(generate()) + { + } + + static InterruptDescriptorTable& instance() + { + static InterruptDescriptorTable idt; + return idt; + } + + constexpr IDTR idtr() { return {sizeof(entries) - 1, (uintptr_t)&entries}; } + + eastl::array entries; + + static_assert(sizeof(entries) == sizeof(InterruptGateDesc) * NUM_INTERRUPTS); +}; diff --git a/arch/x86/64/include/InterruptUtils.h b/arch/x86/64/include/InterruptUtils.h new file mode 100644 index 000000000..8505fd5bb --- /dev/null +++ b/arch/x86/64/include/InterruptUtils.h @@ -0,0 +1,77 @@ +#pragma once + +#include "InterruptDescriptorTable.h" + +#include + +namespace InterruptVector +{ + static constexpr size_t NUM_VECTORS = 256; + static constexpr size_t NUM_ISA_INTERRUPTS = 16; + + static constexpr uint8_t REMAP_OFFSET = 32; + + static constexpr uint8_t YIELD = 65; + static constexpr uint8_t IPI_HALT_CPU = 90; + static constexpr uint8_t APIC_ERROR = 91; + static constexpr uint8_t APIC_SPURIOUS = 100; + static constexpr uint8_t IPI_REMOTE_FCALL = 101; + static constexpr uint8_t APIC_TIMER = 127; + static constexpr uint8_t SYSCALL = 0x80; +}; + +// Standard ISA IRQs +enum class ISA_IRQ : uint8_t +{ + PIT = 0, // 0 Programmable Interrupt Timer Interrupt + KEYBOARD = 1, // 1 Keyboard Interrupt + PIC_8259_CASCADE = 2, // 2 Cascade (used internally by the two PICs. never raised) + COM2 = 3, // 3 COM2 (if enabled) + COM1 = 4, // 4 COM1 (if enabled) + LPT2 = 5, // 5 LPT2 (if enabled) + FLOPPY_DISK = 6, // 6 Floppy Disk + LPT1 = 7, // 7 LPT1 / Unreliable "spurious" interrupt (usually) + RTC = 8, // 8 CMOS real-time clock (if enabled) + // 9 Free for peripherals / legacy SCSI / NIC + // 10 Free for peripherals / SCSI / NIC + // 11 Free for peripherals / SCSI / NIC + PS2_MOUSE = 12, // 12 PS2 Mouse + FPU = 13, // 13 FPU / Coprocessor / Inter-processor + ATA_PRIMARY = 14, // 14 Primary ATA Hard Disk + ATA_SECONDARY = 15 // 15 Secondary ATA Hard Disk +}; + +union [[gnu::packed]] PagefaultExceptionErrorCode +{ + uint32_t u32; + struct [[gnu::packed]] + { + uint32_t present : 1; + uint32_t write : 1; + uint32_t user : 1; + uint32_t reserved_write : 1; + uint32_t instruction_fetch : 1; + uint32_t protection_key : 1; + uint32_t shadow_stack : 1; + uint32_t reserved : 8; + uint32_t sgx : 1; + uint32_t reserved2 : 16; + }; +}; + +static_assert(sizeof(PagefaultExceptionErrorCode) == sizeof(uint32_t)); + +struct ArchThreadRegisters; + +void interruptHandler(size_t interrupt_num, uint64_t error, ArchThreadRegisters* saved_registers); + +void int32_handler_PIT_irq0(); +void int65_handler_swi_yield(); +void int90_handler_halt_cpu(); +void int91_handler_APIC_error(); +void int100_handler_APIC_spurious(); +void int101_handler_cpu_fcall(); +void int127_handler_APIC_timer(); +void syscallHandler(); +void pageFaultHandler(uint64_t address, uint64_t error, uint64_t ip); +void errorHandler(size_t num, size_t eip, size_t cs, size_t spurious); diff --git a/arch/x86/64/include/SegmentUtils.h b/arch/x86/64/include/SegmentUtils.h new file mode 100644 index 000000000..e1e1d990f --- /dev/null +++ b/arch/x86/64/include/SegmentUtils.h @@ -0,0 +1,117 @@ +#pragma once + +#include "types.h" + +struct [[gnu::packed]] SegmentDescriptor +{ + uint16 limitL; + uint16 baseLL; + + uint8 baseLM; + uint8 typeL; + uint8 limitH : 4; + uint8 typeH : 4; + uint8 baseLH; + + uint32 baseH; + uint32 reserved; +}; + +struct [[gnu::packed]] GDT +{ + SegmentDescriptor entries[7]; +}; + +struct [[gnu::packed]] GDT32Ptr +{ + uint16 limit; + uint32 addr; + + GDT32Ptr() = default; + GDT32Ptr(uint16 limit, uint32 addr); + explicit GDT32Ptr(GDT& gdt); + void load(); +}; + +struct [[gnu::packed]] GDT64Ptr +{ + uint16 limit; + uint64 addr; + + GDT64Ptr() = default; + GDT64Ptr(uint16 limit, uint64 addr); + explicit GDT64Ptr(GDT& gdt); + void load(); +}; + +struct [[gnu::packed]] TSSSegmentDescriptor +{ + uint32 limitL : 16; + uint32 baseLL : 16; + + uint32 baseLM : 8; + uint32 type : 4; + uint32 zero : 1; + uint32 dpl : 2; + uint32 present : 1; + uint32 limitH : 4; + uint32 avl_to_software : 1; + uint32 ignored : 2; + uint32 granularity : 1; + uint32 baseLH : 8; + + uint32 baseH; + + uint32 reserved; +}; + +void setTSSSegmentDescriptor(TSSSegmentDescriptor* descriptor, uint32 baseH, uint32 baseL, uint32 limit, uint8 dpl); + +struct [[gnu::packed]] TSS +{ + uint32 reserved_0; + + union + { + uint64 rsp0; + + struct + { + uint32 rsp0_l; + uint32 rsp0_h; + }; + }; + + uint32 rsp1_l; + uint32 rsp1_h; + uint32 rsp2_l; + uint32 rsp2_h; + uint32 reserved_1; + uint32 reserved_2; + + union + { + uint64 ist0; + + struct + { + uint32 ist0_l; + uint32 ist0_h; + }; + }; + + uint32 reserved_3[14]; + uint16 reserved_4; + uint16 iobp; + + void setTaskStack(size_t stack_top); +}; + +void setFSBase(size_t fs_base); +void setGSBase(size_t fs_base); +size_t getFSBase(); +size_t getGSBase(); +size_t getGSKernelBase(); +void* getSavedFSBase(); +void restoreSavedFSBase(); +void setSWAPGSKernelBase(size_t swapgs_base); diff --git a/arch/x86/64/include/offsets.h b/arch/x86/64/include/offsets.h index 344d37889..c4e8a597f 100644 --- a/arch/x86/64/include/offsets.h +++ b/arch/x86/64/include/offsets.h @@ -17,6 +17,8 @@ */ #define VIRTUAL_TO_PHYSICAL_BOOT(x) ((void*)(~PHYSICAL_TO_VIRTUAL_OFFSET & ((uint64)x))) +#define TRUNCATE(X) ({ volatile unsigned int x = (unsigned int)(((char*)X)+0x7FFFFFFF); (char*)(x+1); }) + /** * Use only the lower canonical half for userspace */ @@ -26,3 +28,6 @@ * End of the non-canonical space, start of kernel space */ #define KERNEL_START 0xffff800000000000ULL + +#define IDENT_MAPPING_START 0xFFFFF00000000000ULL +#define IDENT_MAPPING_END 0xFFFFF00040000000ULL diff --git a/arch/x86/64/include/paging-definitions.h b/arch/x86/64/include/paging-definitions.h index b3edd76f4..0ae6c583e 100644 --- a/arch/x86/64/include/paging-definitions.h +++ b/arch/x86/64/include/paging-definitions.h @@ -18,7 +18,7 @@ #define PAGE_FAULT_WRITEABLE 0x00000002 #define PAGE_FAULT_PRESENT 0x00000001 -typedef struct +struct [[gnu::packed]] PageMapLevel4Entry { uint64 present :1; uint64 writeable :1; @@ -33,11 +33,13 @@ typedef struct uint64 reserved_1 :12; // must be 0 uint64 ignored_1 :11; uint64 execution_disabled :1; -} __attribute__((__packed__)) PageMapLevel4Entry; +}; static_assert(sizeof(PageMapLevel4Entry) == 8, "PageMapLevel4Entry is not 64 bit"); -struct PageDirPointerTablePageDirEntry +using PageMapLevel4 = PageMapLevel4Entry[PAGE_MAP_LEVEL_4_ENTRIES]; + +struct [[gnu::packed]] PageDirPointerTablePageDirEntry { uint64 present :1; uint64 writeable :1; @@ -52,11 +54,11 @@ struct PageDirPointerTablePageDirEntry uint64 reserved_1 :12; // must be 0 uint64 ignored_1 :11; uint64 execution_disabled :1; -} __attribute__((__packed__)); +}; static_assert(sizeof(PageDirPointerTablePageDirEntry) == 8, "PageDirPointerTablePageDirEntry is not 64 bit"); -struct PageDirPointerTablePageEntry +struct [[gnu::packed]] PageDirPointerTablePageEntry { uint64 present :1; uint64 writeable :1; @@ -74,17 +76,19 @@ struct PageDirPointerTablePageEntry uint64 reserved_1 :12; // must be 0 uint64 ignored_1 :11; uint64 execution_disabled :1; -} __attribute__((__packed__)); +}; static_assert(sizeof(PageDirPointerTablePageEntry) == 8, "PageDirPointerTablePageEntry is not 64 bit"); -typedef union +union [[gnu::packed]] PageDirPointerTableEntry { struct PageDirPointerTablePageDirEntry pd; struct PageDirPointerTablePageEntry page; -} __attribute__((__packed__)) PageDirPointerTableEntry; +}; + +using PageDirPointerTable = PageDirPointerTableEntry[PAGE_DIR_POINTER_TABLE_ENTRIES]; -struct PageDirPageTableEntry +struct [[gnu::packed]] PageDirPageTableEntry { uint64 present :1; uint64 writeable :1; @@ -99,11 +103,11 @@ struct PageDirPageTableEntry uint64 reserved_1 :12; // must be 0 uint64 ignored_1 :11; uint64 execution_disabled :1; -} __attribute__((__packed__)); +}; static_assert(sizeof(PageDirPageTableEntry) == 8, "PageDirPageTableEntry is not 64 bit"); -struct PageDirPageEntry +struct [[gnu::packed]] PageDirPageEntry { uint64 present :1; uint64 writeable :1; @@ -121,17 +125,19 @@ struct PageDirPageEntry uint64 reserved_1 :12; // must be 0 uint64 ignored_1 :11; uint64 execution_disabled :1; -} __attribute__((__packed__)); +}; static_assert(sizeof(PageDirPageEntry) == 8, "PageDirPageEntry is not 64 bit"); -typedef union +union [[gnu::packed]] PageDirEntry { struct PageDirPageTableEntry pt; struct PageDirPageEntry page; -} __attribute__((__packed__)) PageDirEntry; +}; -typedef struct +using PageDir = PageDirEntry[PAGE_DIR_ENTRIES]; + +struct [[gnu::packed]] PageTableEntry { uint64 present :1; uint64 writeable :1; @@ -140,13 +146,34 @@ typedef struct uint64 cache_disabled :1; uint64 accessed :1; uint64 dirty :1; - uint64 size :1; + uint64 size :1; // PAT bit uint64 global :1; uint64 ignored_2 :3; uint64 page_ppn :28; uint64 reserved_1 :12; // must be 0 - uint64 ignored_1 :11; + uint64 ignored_1 :7; + uint64 protection_key :4; // https://www.kernel.org/doc/html/next/core-api/protection-keys.html uint64 execution_disabled :1; -} __attribute__((__packed__)) PageTableEntry; +}; static_assert(sizeof(PageTableEntry) == 8, "PageTableEntry is not 64 bit"); + +using PageTable = PageTableEntry[PAGE_TABLE_ENTRIES]; + +struct [[gnu::packed]] VAddr +{ + union [[gnu::packed]] + { + uint64 addr; + + struct [[gnu::packed]] + { + uint64 offset : 12; + uint64 pti : 9; + uint64 pdi : 9; + uint64 pdpti : 9; + uint64 pml4i : 9; + uint64 reserved : 16; + }; + }; +}; diff --git a/arch/x86/64/include/print.32.h b/arch/x86/64/include/print.32.h new file mode 100644 index 000000000..f37aacebb --- /dev/null +++ b/arch/x86/64/include/print.32.h @@ -0,0 +1,31 @@ +#pragma once + +#include "types.h" + +void memset(char* block, char c, size_t length); + +void setFBrow(uint8 row); +void setFBcol(uint8 col); + +uint8 getFBrow(); +uint8 getFBcol(); + +uint8 getNextFBrow(); + +void clearFB(); + +char* getFBAddr(uint8 row, uint8 col, bool is_paging_set_up); + +void clearFBrow(uint8 row, bool is_paging_set_up); + +void FBnewline(bool is_paging_set_up); + +void putc(const char c); + +void puts(const char* string); + +uint8 nibbleToASCII(char nibble); + +void putHex8(char c); + +void putHex32(uint32 v); diff --git a/arch/x86/64/include/types.h b/arch/x86/64/include/types.h index f9c51bc12..13bc08383 100644 --- a/arch/x86/64/include/types.h +++ b/arch/x86/64/include/types.h @@ -1,56 +1,49 @@ #pragma once -typedef char int8; -typedef unsigned char uint8; +#include "stdint.h" +#include "klibc/sys/types.h" -typedef short int16; -typedef unsigned short uint16; +typedef int8_t int8; +typedef uint8_t uint8; -typedef int int32; -typedef unsigned int uint32; +typedef int16_t int16; +typedef uint16_t uint16; -typedef long unsigned int uint64; -typedef long int int64; +typedef int32_t int32; +typedef uint32_t uint32; -typedef uint64 pointer; +typedef uint64_t uint64; +typedef int64_t int64; -typedef uint64 l_off_t; +typedef uint64_t l_off_t; -typedef uint64 mode_t; -typedef uint64 uid_t; -typedef uint64 gid_t; -typedef uint64 size_t; -typedef int64 ssize_t; +typedef uint64_t mode_t; +typedef uint64_t uid_t; +typedef uint64_t gid_t; -#pragma GCC poison double float +typedef __SIZE_TYPE__ size_t; + +typedef uint64_t ppn_t; +typedef size_t vpn_t; + +typedef size_t pointer; + + +/* #pragma GCC poison double float */ #define Min(x,y) (((x)<(y))?(x):(y)) #define Max(x,y) (((x)>(y))?(x):(y)) #define KERNEL_CS 0x10 #define KERNEL_DS 0x20 -#define KERNEL_SS 0x20 +#define KERNEL_SS KERNEL_DS #define KERNEL_TSS 0x50 #define DPL_KERNEL 0 #define DPL_USER 3 -#define USER_CS (0x30|DPL_USER) +#define USER_CS ((0x30)|DPL_USER) #define USER_DS ((0x40)|DPL_USER) -#define USER_SS ((0x40)|DPL_USER) +#define USER_SS USER_DS #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) #define unreachable() __builtin_unreachable() - -typedef struct -{ - uint16 limitL; - uint16 baseLL; - uint8 baseLM; - uint8 typeL; - uint8 limitH :4; - uint8 typeH :4; - uint8 baseLH; - uint32 baseH; - uint32 reserved; -}__attribute__((__packed__)) SegmentDescriptor; - diff --git a/arch/x86/64/source/ArchCommon.cpp b/arch/x86/64/source/ArchCommon.cpp index 1b654088e..1659ec62e 100644 --- a/arch/x86/64/source/ArchCommon.cpp +++ b/arch/x86/64/source/ArchCommon.cpp @@ -1,21 +1,43 @@ #include "ArchCommon.h" -#include "multiboot.h" + +#include "ACPI.h" +#include "FrameBufferConsole.h" +#include "IDEDriver.h" +#include "KernelMemoryManager.h" +#include "PageManager.h" +#include "PlatformBus.h" +#include "ProgrammableIntervalTimer.h" +#include "SMP.h" +#include "SWEBDebugInfo.h" +#include "Scheduler.h" +#include "SegmentUtils.h" +#include "SerialManager.h" +#include "TextConsole.h" #include "debug_bochs.h" -#include "offsets.h" #include "kprintf.h" #include "kstring.h" -#include "ArchMemory.h" -#include "FrameBufferConsole.h" -#include "TextConsole.h" +#include "multiboot.h" +#include "offsets.h" #include "ports.h" -#include "SWEBDebugInfo.h" -#include "PageManager.h" -#include "KernelMemoryManager.h" +#include "ArchMemory.h" +#include "ArchMulticore.h" + +void puts(const char* string); + +#if (A_BOOT == A_BOOT | OUTPUT_ENABLED) +#define PRINT(X) writeLine2Bochs((const char*)VIRTUAL_TO_PHYSICAL_BOOT(X)) +#else +#define PRINT(X) +#endif + +RangeAllocator<> mmio_addr_allocator; + +extern void* kernel_start_address; extern void* kernel_end_address; -multiboot_info_t* multi_boot_structure_pointer = (multiboot_info_t*)0xDEADDEAD; // must not be in bss segment -static struct multiboot_remainder mbr __attribute__ ((section (".data"))); // must not be in bss segment +__attribute__ ((section (".data"))) multiboot_info_t* multi_boot_structure_pointer = (multiboot_info_t*)0xDEADDEAD; // must not be in bss segment +__attribute__ ((section (".data"))) static struct multiboot_remainder mbr; // must not be in bss segment extern "C" void parseMultibootHeader() { @@ -23,9 +45,31 @@ extern "C" void parseMultibootHeader() multiboot_info_t *mb_infos = *(multiboot_info_t**)VIRTUAL_TO_PHYSICAL_BOOT( (pointer)&multi_boot_structure_pointer); struct multiboot_remainder &orig_mbr = (struct multiboot_remainder &)(*((struct multiboot_remainder*)VIRTUAL_TO_PHYSICAL_BOOT((pointer)&mbr))); + PRINT("Bootloader: "); + writeLine2Bochs((char*)(pointer)(mb_infos->boot_loader_name)); + PRINT("\n"); + + if (mb_infos && mb_infos->f_cmdline) + { + const char* cmdline = (char*)(uintptr_t)mb_infos->cmdline; + size_t len = strlen(cmdline); + if (len+1 <= sizeof(orig_mbr.cmdline)) + { + memcpy(orig_mbr.cmdline, cmdline, len+1); + } + } + + if (mb_infos && mb_infos->f_fb) + { + orig_mbr.have_framebuffer = true; + orig_mbr.framebuffer = mb_infos->framebuffer; + } + if (mb_infos && mb_infos->f_vbe) { - struct vbe_mode* mode_info = (struct vbe_mode*)(uint64)mb_infos->vbe_mode_info; + orig_mbr.have_vbe = true; + orig_mbr.vbe = mb_infos->vbe; + struct vbe_mode* mode_info = (struct vbe_mode*)(uint64)mb_infos->vbe.vbe_mode_info; orig_mbr.have_vesa_console = 1; orig_mbr.vesa_lfb_pointer = mode_info->phys_base; orig_mbr.vesa_x_res = mode_info->x_resolution; @@ -67,6 +111,17 @@ extern "C" void parseMultibootHeader() ++i; } } + + if (mb_infos && mb_infos->f_elf_shdr) + { + orig_mbr.have_elf_sec_hdr = true; + orig_mbr.elf_sec = mb_infos->elf_sec; + } +} + +pointer ArchCommon::getKernelStartAddress() +{ + return (pointer)&kernel_start_address; } pointer ArchCommon::getKernelEndAddress() @@ -84,7 +139,14 @@ pointer ArchCommon::getFreeKernelMemoryStart() pointer ArchCommon::getFreeKernelMemoryEnd() { - return KernelMemoryManager::instance()->getKernelBreak(); + if (KernelMemoryManager::isReady()) + { + return KernelMemoryManager::instance()->getKernelBreak(); + } + else + { + return ArchCommon::getKernelEndAddress(); + } } @@ -110,7 +172,18 @@ size_t ArchCommon::getNumModules(size_t is_paging_set_up) } } -size_t ArchCommon::getModuleStartAddress(size_t num,size_t is_paging_set_up) +const char* ArchCommon::getModuleName(size_t num, size_t is_paging_set_up) +{ + if (is_paging_set_up) + return (char*)((size_t)mbr.module_maps[num].name | PHYSICAL_TO_VIRTUAL_OFFSET); + else + { + struct multiboot_remainder &orig_mbr = (struct multiboot_remainder &)(*((struct multiboot_remainder*)VIRTUAL_TO_PHYSICAL_BOOT((pointer)&mbr))); + return (char*)orig_mbr.module_maps[num].name; + } +} + +size_t ArchCommon::getModuleStartAddress(size_t num, size_t is_paging_set_up) { if (is_paging_set_up) return mbr.module_maps[num].start_address | PHYSICAL_TO_VIRTUAL_OFFSET; @@ -121,7 +194,7 @@ size_t ArchCommon::getModuleStartAddress(size_t num,size_t is_paging_set_up) } } -size_t ArchCommon::getModuleEndAddress(size_t num,size_t is_paging_set_up) +size_t ArchCommon::getModuleEndAddress(size_t num, size_t is_paging_set_up) { if (is_paging_set_up) return mbr.module_maps[num].end_address | PHYSICAL_TO_VIRTUAL_OFFSET; @@ -161,6 +234,26 @@ pointer ArchCommon::getFBPtr(size_t is_paging_set_up) return 0xB8000ULL; } +size_t ArchCommon::getFBWidth() +{ + return 80; +} + +size_t ArchCommon::getFBHeight() +{ + return 25; +} + +size_t ArchCommon::getFBBitsPerCharacter() +{ + return 16; +} + +size_t ArchCommon::getFBSize() +{ + return getFBWidth() * getFBHeight() * getFBBitsPerCharacter()/8; +} + size_t ArchCommon::getVESAConsoleBitsPerPixel() { return mbr.vesa_bits_per_pixel; @@ -177,7 +270,7 @@ size_t ArchCommon::getNumUseableMemoryRegions() return i; } -size_t ArchCommon::getUsableMemoryRegion(size_t region, pointer &start_address, pointer &end_address, size_t &type) +size_t ArchCommon::getUseableMemoryRegion(size_t region, pointer &start_address, pointer &end_address, size_t &type) { if (region >= MAX_MEMORY_MAPS) return 1; @@ -201,38 +294,60 @@ Console* ArchCommon::createConsole(size_t count) } -#if (A_BOOT == A_BOOT | OUTPUT_ENABLED) -#define PRINT(X) writeLine2Bochs((const char*)VIRTUAL_TO_PHYSICAL_BOOT(X)) -#else -#define PRINT(X) -#endif - -extern SegmentDescriptor gdt[7]; +extern GDT gdt; extern "C" void startup(); extern "C" void initialisePaging(); extern uint8 boot_stack[0x4000]; -struct GDTPtr -{ - uint16 limit; - uint64 addr; -}__attribute__((__packed__)) gdt_ptr; +GDT64Ptr gdt_ptr; -extern "C" void entry64() +extern "C" [[noreturn]] void entry64() { PRINT("Parsing Multiboot Header...\n"); parseMultibootHeader(); PRINT("Initializing Kernel Paging Structures...\n"); initialisePaging(); PRINT("Setting CR3 Register...\n"); - asm("mov %%rax, %%cr3" : : "a"(VIRTUAL_TO_PHYSICAL_BOOT(ArchMemory::getRootOfKernelPagingStructure()))); + asm("mov %%rax, %%cr3" : : "a"(VIRTUAL_TO_PHYSICAL_BOOT(ArchMemory::getKernelPagingStructureRootVirt()))); + kprintf("Paging initialized\n"); PRINT("Switch to our own stack...\n"); + kprintf("Switch to our own stack...\n"); asm("mov %[stack], %%rsp\n" "mov %[stack], %%rbp\n" : : [stack]"i"(boot_stack + 0x4000)); PRINT("Loading Long Mode Segments...\n"); + kprintf("Loading Long Mode Segments...\n"); + + if (A_BOOT & OUTPUT_ADVANCED) + { + kprintf("GDT: %p\n", &gdt); + kprintf("GDT[0]: %lx\n", *(uint64*)&gdt.entries[0]); + kprintf("GDT[0]: %lx\n", *(uint64*)&gdt.entries[1]); + kprintf("GDT[2]: %lx\n", *(uint64*)&gdt.entries[2]); + kprintf("GDT[3]: %lx\n", *(uint64*)&gdt.entries[3]); + kprintf("GDT: %p\n", VIRTUAL_TO_PHYSICAL_BOOT(&gdt)); + kprintf("GDT[0]: %lx\n", *(uint64*)&(((GDT*)VIRTUAL_TO_PHYSICAL_BOOT(&gdt))->entries[0])); + kprintf("GDT[0]: %lx\n", *(uint64*)&(((GDT*)VIRTUAL_TO_PHYSICAL_BOOT(&gdt))->entries[1])); + kprintf("GDT[2]: %lx\n", *(uint64*)&(((GDT*)VIRTUAL_TO_PHYSICAL_BOOT(&gdt))->entries[2])); + kprintf("GDT[3]: %lx\n", *(uint64*)&(((GDT*)VIRTUAL_TO_PHYSICAL_BOOT(&gdt))->entries[3])); + kprintf("PML4[0]: %lx\n", *(uint64*)&kernel_page_map_level_4[0]); + kprintf("PDPT[0]: %lx\n", *(uint64*)&kernel_page_directory_pointer_table[0].pd); + kprintf("PD[0]: %lx\n", *(uint64*)&kernel_page_directory[0].page); + kprintf("PD[1]: %lx\n", *(uint64*)&kernel_page_directory[1].page); + kprintf("PD[2]: %lx\n", *(uint64*)&kernel_page_directory[2].page); + } + + assert(kernel_page_directory[0].page.present); + assert(kernel_page_directory[0].page.size); + assert(kernel_page_directory[0].page.page_ppn == 0); + assert(kernel_page_directory[1].page.present); + assert(kernel_page_directory[1].page.size); + assert(kernel_page_directory[1].page.page_ppn == 1); + assert(*(uint64*)&gdt.entries[0] == 0); + assert(*(uint64*)&gdt.entries[1] != 0); + assert(*(uint64*)&gdt.entries[2] != 0); gdt_ptr.limit = sizeof(gdt) - 1; - gdt_ptr.addr = (uint64)gdt; + gdt_ptr.addr = (uint64)&gdt; asm("lgdt (%%rax)" : : "a"(&gdt_ptr)); asm("mov %%ax, %%ds\n" "mov %%ax, %%es\n" @@ -240,34 +355,58 @@ extern "C" void entry64() "mov %%ax, %%fs\n" "mov %%ax, %%gs\n" : : "a"(KERNEL_DS)); + PRINT("Reloading TSS...\n"); + kprintf("Reloading TSS...\n"); asm("ltr %%ax" : : "a"(KERNEL_TSS)); + + + extern char cls_start; + extern char cls_end; + debug(A_MULTICORE, "Setting temporary CLS for boot processor [%p, %p)\n", &cls_start, &cls_end); + CpuLocalStorage::setCls(&cls_start); + currentThread = nullptr; + PRINT("Calling startup()...\n"); + kprintf("Calling startup()...\n"); asm("jmp *%[startup]" : : [startup]"r"(startup)); + assert(false && "Returned from startup"); while (1); } class Stabs2DebugInfo; -Stabs2DebugInfo const *kernel_debug_info = 0; +const Stabs2DebugInfo* kernel_debug_info = 0; void ArchCommon::initDebug() { + debug(A_COMMON, "initDebug\n"); for (size_t i = 0; i < getNumModules(); ++i) { - if (memcmp("SWEBDBG1",(char const *)getModuleStartAddress(i),8) == 0) - kernel_debug_info = new SWEBDebugInfo((char const *)getModuleStartAddress(i), - (char const *)getModuleEndAddress(i)); + debug(A_COMMON, "Checking module from [%zx -> %zx)\n", getModuleStartAddress(i), getModuleEndAddress(i)); + if ((getModuleStartAddress(i) < getModuleEndAddress(i)) && + (memcmp("SWEBDBG1", (const char*)getModuleStartAddress(i), 8) == 0)) + { + kernel_debug_info = new SWEBDebugInfo((const char*)getModuleStartAddress(i), + (const char*)getModuleEndAddress(i)); + } } if (!kernel_debug_info) + { kernel_debug_info = new SWEBDebugInfo(0, 0); + } + debug(A_COMMON, "initDebug done\n"); } void ArchCommon::idle() +{ + halt(); +} + +void ArchCommon::halt() { asm volatile("hlt"); } #define STATS_OFFSET 22 -#define FREE_PAGES_OFFSET STATS_OFFSET + 11*2 void ArchCommon::drawStat() { const char* text = "Free pages F9 MemInfo F10 Locks F11 Stacktrace F12 Threads"; @@ -277,27 +416,149 @@ void ArchCommon::drawStat() { size_t i = 0; while(text[i]) { fb[i * 2 + STATS_OFFSET] = text[i]; - fb[i * 2 + STATS_OFFSET + 1] = (char)(color[i] == 'x' ? 0x80 : 0x08); + fb[i * 2 + STATS_OFFSET + 1] = (char)(color[i] == 'x' ? ((CONSOLECOLOR::BLACK) | (CONSOLECOLOR::DARK_GREY << 4)) : + ((CONSOLECOLOR::DARK_GREY) | (CONSOLECOLOR::BLACK << 4))); i++; } - char itoa_buffer[33]; + char itoa_buffer[80]; + +#define STATS_FREE_PAGES_START (STATS_OFFSET + 11*2) + memset(fb + STATS_FREE_PAGES_START, 0, 4*2); memset(itoa_buffer, '\0', sizeof(itoa_buffer)); - itoa(PageManager::instance()->getNumFreePages(), itoa_buffer, 10); + itoa(PageManager::instance().getNumFreePages(), itoa_buffer, 10); + for(size_t i = 0; (i < sizeof(itoa_buffer)) && (itoa_buffer[i] != '\0'); ++i) + { + fb[STATS_FREE_PAGES_START + i * 2] = itoa_buffer[i]; + fb[STATS_FREE_PAGES_START + i * 2 + 1] = ((CONSOLECOLOR::WHITE) | (CONSOLECOLOR::BLACK << 4)); + } + +#define STATS_FREE_PAGES_PERCENT_START (STATS_OFFSET + 80*2 + 11*2) + size_t total_pages = PageManager::instance().getTotalNumPages(); + size_t free_pages_percent = total_pages ? (PageManager::instance().getNumFreePages()*100)/total_pages : 0; + memset(fb + STATS_FREE_PAGES_PERCENT_START, 0, 4*2); + memset(itoa_buffer, '\0', sizeof(itoa_buffer)); + itoa(free_pages_percent, itoa_buffer, 10); + size_t free_pp_len = strlen(itoa_buffer); + itoa_buffer[free_pp_len] = '%'; + for(size_t i = 0; (i < sizeof(itoa_buffer)) && (itoa_buffer[i] != '\0'); ++i) + { + fb[STATS_FREE_PAGES_PERCENT_START + i * 2] = itoa_buffer[i]; + fb[STATS_FREE_PAGES_PERCENT_START + i * 2 + 1] = ((CONSOLECOLOR::WHITE) | (CONSOLECOLOR::BLACK << 4)); + } +#define STATS_NUM_THREADS_START (80*2 + 73*2) + memset(fb + STATS_NUM_THREADS_START, 0, 4*2); + memset(itoa_buffer, '\0', sizeof(itoa_buffer)); + itoa(Scheduler::instance()->num_threads, itoa_buffer, 10); + for(size_t i = 0; (i < sizeof(itoa_buffer)) && (itoa_buffer[i] != '\0'); ++i) + { + fb[STATS_NUM_THREADS_START + i * 2] = itoa_buffer[i]; + fb[STATS_NUM_THREADS_START + i * 2 + 1] = ((CONSOLECOLOR::WHITE) | (CONSOLECOLOR::BLACK << 4)); + } + + + size_t STATS_SCHED_LOCK_CONTENTION_START = (80*2 + SMP::numRunningCpus()*2); + // calc fixnum xxx.xxx% + size_t sched_lock_free = Scheduler::instance()->scheduler_lock_count_free; + size_t sched_lock_blocked = Scheduler::instance()->scheduler_lock_count_blocked; + size_t sched_lock_total = sched_lock_free + sched_lock_blocked; + size_t sched_lock_contention_percent = sched_lock_total ? (sched_lock_blocked*100)/sched_lock_total : 0; + size_t sched_lock_contention_2 = sched_lock_total ? ((sched_lock_blocked*100000)/sched_lock_total) % 1000 : 0; + + memset(itoa_buffer, '\0', sizeof(itoa_buffer)); + itoa(sched_lock_contention_percent, itoa_buffer, 10); + size_t slc_len = strlen(itoa_buffer); + itoa_buffer[slc_len++] = '.'; + if (sched_lock_contention_2 < 100) + itoa_buffer[slc_len++] = '0'; + if (sched_lock_contention_2 < 10) + itoa_buffer[slc_len++] = '0'; + itoa(sched_lock_contention_2, itoa_buffer + slc_len, 10); + slc_len = strlen(itoa_buffer); + itoa_buffer[slc_len] = '%'; + + memset(fb + STATS_SCHED_LOCK_CONTENTION_START, 0, 7*2); for(size_t i = 0; (i < sizeof(itoa_buffer)) && (itoa_buffer[i] != '\0'); ++i) { - fb[i * 2 + FREE_PAGES_OFFSET] = itoa_buffer[i]; + fb[STATS_SCHED_LOCK_CONTENTION_START + i * 2] = itoa_buffer[i]; + fb[STATS_SCHED_LOCK_CONTENTION_START + i * 2 + 1] = ((CONSOLECOLOR::WHITE) | (CONSOLECOLOR::BLACK << 4)); } } +void updateStatsThreadColor() +{ + char* fb = (char*)ArchCommon::getFBPtr(); + fb[1 + SMP::currentCpuId()*2] = + (((currentThread ? currentThread->console_color : + CONSOLECOLOR::BLACK) << 4) | + CONSOLECOLOR::BRIGHT_WHITE); +} + +cpu_local size_t heart_beat_value = 0; + void ArchCommon::drawHeartBeat() { + drawStat(); + const char* clock = "/-\\|"; - static uint32 heart_beat_value = 0; char* fb = (char*)getFBPtr(); - fb[0] = clock[heart_beat_value++ % 4]; - fb[1] = (char)0x9f; + size_t cpu_id = SMP::currentCpuId(); + fb[0 + cpu_id*2] = clock[heart_beat_value++ % 4]; + updateStatsThreadColor(); +} - drawStat(); + +void ArchCommon::postBootInit() +{ + initACPI(); +} + +void ArchCommon::initPlatformDrivers() +{ + PlatformBus::instance().registerDriver(PITDriver::instance()); + PlatformBus::instance().registerDriver(SerialManager::instance()); +} + +void ArchCommon::initBlockDeviceDrivers() +{ + PlatformBus::instance().registerDriver(IDEControllerDriver::instance()); +} + +[[noreturn]] void ArchCommon::callWithStack(char* stack, void (*func)()) +{ + asm volatile("movq %[stack], %%rsp\n" + "callq *%[func]\n" + ::[stack]"r"(stack), + [func]"r"(func)); + assert(false); +} + + +uint64 ArchCommon::cpuTimestamp() +{ + uint64 low, high; + asm volatile("rdtsc\n" + :"=a"(low), "=d"(high)); + return (high << 32) | low; +} + +void ArchCommon::spinlockPause() +{ + asm volatile("pause\n"); +} + +void ArchCommon::reservePagesPreKernelInit(Allocator &alloc) +{ + ArchMulticore::reservePages(alloc); +} + +void ArchCommon::initKernelVirtualAddressAllocator() +{ + mmio_addr_allocator.setUseable(KERNEL_START, (size_t)-1); + mmio_addr_allocator.setUnuseable(getKernelStartAddress(), getKernelEndAddress()); + mmio_addr_allocator.setUnuseable(KernelMemoryManager::instance()->getKernelHeapStart(), KernelMemoryManager::instance()->getKernelHeapMaxEnd()); + mmio_addr_allocator.setUnuseable(IDENT_MAPPING_START, IDENT_MAPPING_END); + debug(MAIN, "Usable MMIO ranges:\n"); + mmio_addr_allocator.printUsageInfo(); } diff --git a/arch/x86/64/source/ArchCpuLocalStorage.cpp b/arch/x86/64/source/ArchCpuLocalStorage.cpp new file mode 100644 index 000000000..2471501bb --- /dev/null +++ b/arch/x86/64/source/ArchCpuLocalStorage.cpp @@ -0,0 +1,62 @@ +#include "ArchCpuLocalStorage.h" + +#include "SegmentUtils.h" + +#include + +#include "debug.h" + +extern char cls_start; +extern char cls_end; +extern char tbss_start; +extern char tbss_end; +extern char tdata_start; +extern char tdata_end; + +void CpuLocalStorage::initCpuLocalStorage() +{ + setCls(allocCls()); +} + +size_t CpuLocalStorage::getClsSize() +{ + return &cls_end - &cls_start; +} + +char* CpuLocalStorage::allocCls() +{ + debug(A_MULTICORE, "Allocating CPU local storage\n"); + + size_t cls_size = getClsSize(); + size_t tbss_size = &tbss_end - &tbss_start; + size_t tdata_size = &tdata_end - &tdata_start; + debug(A_MULTICORE, "cls_base: [%p, %p), size: %zx\n", &cls_start, &cls_end, cls_size); + debug(A_MULTICORE, "tbss: [%p, %p), size: %zx\n", &tbss_start, &tbss_end, tbss_size); + debug(A_MULTICORE, "tdata: [%p, %p), size: %zx\n", &tdata_start, &tdata_end, tdata_size); + + char* cls_base = new char[cls_size + sizeof(void*)]{}; + debug(A_MULTICORE, "Allocated new cls_base at [%p, %p)\n", cls_base, cls_base + cls_size + sizeof(void*)); + + debug(A_MULTICORE, "Initializing tdata at [%p, %p) and tbss at [%p, %p)\n", + cls_base + (&tdata_start - &cls_start), cls_base + (&tdata_start - &cls_start) + tdata_size, + cls_base + (&tbss_start - &cls_start), cls_base + (&tbss_start - &cls_start) + tbss_size); + memcpy(cls_base + (&tdata_start - &cls_start), &tdata_start, tdata_size); + + return cls_base; +} + +void CpuLocalStorage::setCls(char* cls) +{ + debug(A_MULTICORE, "Set CLS: %p\n", cls); + void** fs_base = (void**)(cls + getClsSize()); + *fs_base = fs_base; + setFSBase((size_t)fs_base); // %fs base needs to point to end of CLS, not the start. %fs:0 = pointer to %fs base + setGSBase((size_t)fs_base); + setSWAPGSKernelBase((size_t)fs_base); +} + +bool CpuLocalStorage::ClsInitialized() +{ + bool init = (getFSBase() != 0); + return init; +} diff --git a/arch/x86/64/source/ArchInterrupts.cpp b/arch/x86/64/source/ArchInterrupts.cpp index 271655e63..1e0f6a169 100644 --- a/arch/x86/64/source/ArchInterrupts.cpp +++ b/arch/x86/64/source/ArchInterrupts.cpp @@ -1,62 +1,222 @@ #include "ArchInterrupts.h" + #include "8259.h" -#include "ports.h" +#include "APIC.h" +#include "CPUID.h" +#include "InterruptDescriptorTable.h" #include "InterruptUtils.h" +#include "IoApic.h" +#include "IrqDomain.h" +#include "KeyboardManager.h" +#include "PageManager.h" +#include "PlatformBus.h" +#include "ProgrammableIntervalTimer.h" +#include "Scheduler.h" +#include "SystemState.h" +#include "Thread.h" +#include "X2Apic.h" +#include "ports.h" + +#include "ArchMemory.h" +#include "ArchMulticore.h" #include "ArchThreads.h" + #include "assert.h" -#include "Thread.h" +#include "debug.h" + +cpu_local IrqDomain cpu_irq_vector_domain_("CPU interrupt vector", InterruptVector::NUM_VECTORS); +cpu_local IrqDomain* cpu_root_irq_domain_ = &cpu_irq_vector_domain_; + +IrqDomain& ArchInterrupts::currentCpuRootIrqDomain() +{ + assert(cpu_root_irq_domain_); + return *cpu_root_irq_domain_; +} + +IrqDomain& ArchInterrupts::isaIrqDomain() +{ + static IrqDomain isa_irq_domain("ISA IRQ", InterruptVector::NUM_ISA_INTERRUPTS); + return isa_irq_domain; +} + +static void initInterruptDescriptorTable() +{ + auto& idt = InterruptDescriptorTable::instance(); + idt.idtr().load(); + + if (A_INTERRUPTS & OUTPUT_ENABLED & OUTPUT_ADVANCED) + { + for (size_t i = 0; i < idt.entries.size(); ++i) + { + debug(A_INTERRUPTS, + "%3zu -- offset: %p, ist: %x, present: %x, segment_selector: %x, " + "type: %x, dpl: %x\n", + i, idt.entries[i].offset(), idt.entries[i].ist, idt.entries[i].present, + idt.entries[i].segment_selector, idt.entries[i].type, + idt.entries[i].dpl); + } + } +} + +void initCpuLocalInterruptHandlers() +{ + debug(A_INTERRUPTS, "Initializing interrupt handlers\n"); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::YIELD).useHandler(int65_handler_swi_yield); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::IPI_HALT_CPU).useHandler(int90_handler_halt_cpu); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::APIC_ERROR).useHandler(int91_handler_APIC_error); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::APIC_SPURIOUS).useHandler(int100_handler_APIC_spurious); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::IPI_REMOTE_FCALL).useHandler(int101_handler_cpu_fcall); + ArchInterrupts::currentCpuRootIrqDomain().irq(InterruptVector::SYSCALL).useHandler(syscallHandler); +} + +void initInterruptControllers() +{ + debug(A_INTERRUPTS, "Initializing interrupt controllers\n"); + assert(CpuLocalStorage::ClsInitialized()); + + PlatformBus::instance().registerDriver(ApicDriver::instance()); + PlatformBus::instance().registerDriver(ApicTimerDriver::instance()); + PlatformBus::instance().registerDriver(IoApicDriver::instance()); + PlatformBus::instance().registerDriver(PIC8259Driver::instance()); + + assert(cpu_root_irq_domain_); + debug(A_INTERRUPTS, "Interrupt controllers initialized\n"); +} void ArchInterrupts::initialise() { - uint16 i; - disableInterrupts(); - initialise8259s(); - InterruptUtils::initialise(); - for (i=0;i<16;++i) - disableIRQ(i); + initInterruptDescriptorTable(); + initInterruptControllers(); + initCpuLocalInterruptHandlers(); } void ArchInterrupts::enableTimer() { - enableIRQ(0); + if(cpu_lapic->isInitialized() && cpu_lapic->usingAPICTimer()) + { + debug(A_INTERRUPTS, "Enabling xApic %x timer\n", cpu_lapic->apicId()); + enableIRQ(cpu_lapic->timer_interrupt_controller.irq()); + } + else + { + debug(A_INTERRUPTS, "Enabling PIT timer IRQ\n"); + enableIRQ(PIT::instance().irq()); + } +} + +void ArchInterrupts::disableTimer() +{ + if(cpu_lapic->isInitialized() && cpu_lapic->usingAPICTimer()) + { + debug(A_INTERRUPTS, "Disabling xApic %x timer \n", cpu_lapic->apicId()); + disableIRQ(cpu_lapic->timer_interrupt_controller.irq()); + } + else + { + debug(A_INTERRUPTS, "Disabling PIT timer IRQ\n"); + disableIRQ(PIT::instance().irq()); + } } void ArchInterrupts::setTimerFrequency(uint32 freq) { + debug(A_INTERRUPTS, "Set timer frequency %u\n", freq); + + PIT::setOperatingMode(PIT::OperatingMode::SQUARE_WAVE); + uint16_t divisor; if(freq < (uint32)(1193180. / (1 << 16) + 1)) { divisor = 0; } else { divisor = (uint16)(1193180 / freq); } - outportb(0x43, 0x36); - outportb(0x40, divisor & 0xFF); - outportb(0x40, divisor >> 8); -} -void ArchInterrupts::disableTimer() -{ - disableIRQ(0); + PIT::setFrequencyDivisor(divisor); } + void ArchInterrupts::enableKBD() { - enableIRQ(1); - enableIRQ(9); + debug(A_INTERRUPTS, "Enable keyboard irq\n"); + + enableIRQ(KeyboardManager::instance().irq()); } void ArchInterrupts::disableKBD() { - disableIRQ(1); + enableIRQ(KeyboardManager::instance().irq(), false); +} + +void ArchInterrupts::enableIRQ(const IrqDomain::DomainIrqHandle& irq_handle, bool enable) +{ + debug(A_INTERRUPTS, "[Cpu %zu] %s %s IRQ %zu\n", SMP::currentCpuId(), + enable ? "Enable" : "Disable", irq_handle.domain().name().c_str(), + irq_handle.irq()); + + for (auto&& domain_irq : irq_handle.forwardMappingChain()) + { + domain_irq.activateInDomain(enable); + } +} + +void ArchInterrupts::disableIRQ(const IrqDomain::DomainIrqHandle& irq_handle) +{ + enableIRQ(irq_handle, false); +} + +void ArchInterrupts::startOfInterrupt(const IrqDomain::DomainIrqHandle& irq_handle) +{ + for (auto&& [domain, local_irqnum] : irq_handle.reverseMappingTree()) + { + if (domain->controller()) + { + domain->controller()->irqStart(local_irqnum); + } + } +} + +void ArchInterrupts::startOfInterrupt(uint16 irqnum) +{ + debugAdvanced(A_INTERRUPTS, "[Cpu %zu] Start of IRQ %u\n", SMP::currentCpuId(), irqnum); + + startOfInterrupt(currentCpuRootIrqDomain().irq(irqnum)); +} + +void ArchInterrupts::endOfInterrupt(const IrqDomain::DomainIrqHandle& irq_handle) +{ + for (auto&& [domain, local_irqnum] : irq_handle.reverseMappingTree()) + { + if (domain->controller()) + { + domain->controller()->ack(local_irqnum); + } + } +} + +void ArchInterrupts::endOfInterrupt(uint16 irqnum) +{ + debugAdvanced(A_INTERRUPTS, "[Cpu %zu] Sending EOI for IRQ %u\n", + SMP::currentCpuId(), irqnum); + + endOfInterrupt(currentCpuRootIrqDomain().irq(irqnum)); +} + +void ArchInterrupts::handleInterrupt(const IrqDomain::DomainIrqHandle& irq_handle) +{ + for (auto&& domain_irq : irq_handle.reverseMappingTree()) + { + domain_irq.handleInDomain(); + } } -void ArchInterrupts::EndOfInterrupt(uint16 number) +void ArchInterrupts::handleInterrupt(uint16_t irqnum) { - sendEOI(number); + handleInterrupt(currentCpuRootIrqDomain().irq(irqnum)); } void ArchInterrupts::enableInterrupts() { - asm("sti"); + asm("sti\n" + "nop\n"); } bool ArchInterrupts::disableInterrupts() @@ -66,7 +226,8 @@ bool ArchInterrupts::disableInterrupts() "popq %0\n" "cli" : "=a"(ret_val)); - return (ret_val & (1 << 9)); //testing IF Flag + bool previous_state = (ret_val & (1 << 9)); + return previous_state; //testing IF Flag } bool ArchInterrupts::testIFSet() @@ -91,8 +252,10 @@ void ArchInterrupts::yieldIfIFSet() } } - -struct context_switch_registers { +struct [[gnu::packed]] context_switch_registers +{ + uint64 fsbase_low; + uint64 fsbase_high; uint64 ds; uint64 es; uint64 r15; @@ -111,9 +274,12 @@ struct context_switch_registers { uint64 rcx; uint64 rax; uint64 rsp; + uint64_t interrupt_num; + uint64_t error_code; }; -struct interrupt_registers { +struct [[gnu::packed]] interrupt_registers +{ uint64 rip; uint64 cs; uint64 rflags; @@ -123,12 +289,27 @@ struct interrupt_registers { #include "kprintf.h" -extern "C" void arch_saveThreadRegisters(uint64* base, uint64 error) +struct [[gnu::packed]] SavedContextSwitchRegisters +{ + context_switch_registers registers; + interrupt_registers iregisters; +}; + +struct [[gnu::packed]] SavedContextSwitchRegistersWithError { - struct context_switch_registers* registers; - registers = (struct context_switch_registers*) base; - struct interrupt_registers* iregisters; - iregisters = (struct interrupt_registers*) (base + sizeof(struct context_switch_registers)/sizeof(uint64) + error); + context_switch_registers registers; + uint64 error; + interrupt_registers iregisters; +} __attribute__((packed)); + +extern "C" ArchThreadRegisters* arch_saveThreadRegisters(void* base, uint64 error) +{ + context_switch_registers* registers = error ? &((SavedContextSwitchRegistersWithError*)base)->registers : + &((SavedContextSwitchRegisters*)base)->registers; + interrupt_registers* iregisters = error ? &((SavedContextSwitchRegistersWithError*)base)->iregisters : + &((SavedContextSwitchRegisters*)base)->iregisters; + + restoreSavedFSBase(); ArchThreadRegisters* info = currentThreadRegisters; asm("fnsave %[fpu]\n" "frstor %[fpu]\n" @@ -156,33 +337,73 @@ extern "C" void arch_saveThreadRegisters(uint64* base, uint64 error) info->rcx = registers->rcx; info->rax = registers->rax; info->rbp = registers->rbp; + info->fsbase = (registers->fsbase_high << 32) | registers->fsbase_low; assert(!currentThread || currentThread->isStackCanaryOK()); + + return info; +} + +extern "C" void genericInterruptEntry(SavedContextSwitchRegisters* regs) +{ + // Take registers previously saved on the stack via assembly and store them in the + // saved registers of the thread + auto saved_regs = arch_saveThreadRegisters(&(regs->registers), 0); + + debugAdvanced(A_INTERRUPTS, "[Cpu %zu] Interrupt entry %zu\n", + SMP::currentCpuId(), regs->registers.interrupt_num); + + interruptHandler(regs->registers.interrupt_num, regs->registers.error_code, saved_regs); } -typedef struct { - uint32 padding; - uint64 rsp0; // actually the TSS has more fields, but we don't need them -} __attribute__((__packed__))TSS; -extern TSS g_tss; -extern "C" void arch_contextSwitch() +extern "C" [[noreturn]] void contextSwitch(Thread* target_thread, ArchThreadRegisters* target_registers) { - if(outstanding_EOIs) + target_thread = target_thread ? : currentThread; + target_registers = target_registers ? : currentThreadRegisters; + assert(target_thread); + + if(A_INTERRUPTS & OUTPUT_ADVANCED) + { + debug(A_INTERRUPTS, "[Cpu %zu] Context switch to thread %s (%p) at rip %p\n", SMP::currentCpuId(), target_thread->getName(), target_thread, (void*)target_registers->rip); + } + + + assert(target_registers); + assert(!currentThread || currentThread->currently_scheduled_on_cpu_ == SMP::currentCpuId()); + + if((SMP::currentCpuId() == 0) && PIC8259::outstanding_EOIs_) // TODO: Check local APIC for pending EOIs { - debug(A_INTERRUPTS, "%zu outstanding End-Of-Interrupt signal(s) on context switch. Probably called yield in the wrong place (e.g. in the scheduler)\n", outstanding_EOIs); - assert(!outstanding_EOIs); + debug(A_INTERRUPTS, "%zu pending End-Of-Interrupt signal(s) on context switch. Probably called yield in the wrong place (e.g. in the scheduler)\n", PIC8259::outstanding_EOIs_); + assert(!((SMP::currentCpuId() == 0) && PIC8259::outstanding_EOIs_)); } - if (currentThread->switch_to_userspace_) + if (target_thread->switch_to_userspace_) { - assert(currentThread->holding_lock_list_ == 0 && "Never switch to userspace when holding a lock! Never!"); - assert(currentThread->lock_waiting_on_ == 0 && "How did you even manage to execute code while waiting for a lock?"); + assert(target_thread->holding_lock_list_ == 0 && "Never switch to userspace when holding a lock! Never!"); + assert(target_thread->lock_waiting_on_ == 0 && "How did you even manage to execute code while waiting for a lock?"); + if ((target_registers->cs & 3) != 3) + { + debugAlways( + A_INTERRUPTS, + "Incorrect ring level for switch to userspace, expected 3, cs to restore is: %lx\n", + target_registers->cs); + } + assert((target_registers->cs & 3) == 3 && "Incorrect ring level for switch to userspace"); } - assert(currentThread->isStackCanaryOK() && "Kernel stack corruption detected."); - ArchThreadRegisters info = *currentThreadRegisters; // optimization: local copy produces more efficient code in this case - g_tss.rsp0 = info.rsp0; + assert(target_thread->isStackCanaryOK() && "Kernel stack corruption detected."); - asm volatile("frstor %[fpu]\n" + currentThread = target_thread; + currentThreadRegisters = target_registers; + currentThread->currently_scheduled_on_cpu_ = SMP::currentCpuId(); + + ArchThreadRegisters info = *target_registers; + assert(info.rip >= PAGE_SIZE); // debug + assert(info.rsp0 >= USER_BREAK); + cpu_tss.setTaskStack(info.rsp0); + size_t new_fsbase = target_thread->switch_to_userspace_ ? target_registers->fsbase : (uint64)getSavedFSBase(); + setFSBase(new_fsbase); // Don't use CLS after this line + asm volatile( + "frstor %[fpu]\n" "mov %[cr3], %%cr3\n" "push %[ss]\n" "push %[rsp]\n" @@ -206,11 +427,17 @@ extern "C" void arch_contextSwitch() "mov %[rbx], %%rbx\n" "mov %[rax], %%rax\n" "mov %[rbp], %%rbp\n" - "iretq" :: - [fpu]"m"(info.fpu), [cr3]"r"(info.cr3), [ss]"m"(info.ss), [rsp]"m"(info.rsp), [rflags]"m"(info.rflags), - [cs]"m"(info.cs), [rip]"m"(info.rip), [rsi]"m"(info.rsi), [rdi]"m"(info.rdi), [es]"m"(info.es), [ds]"m"(info.ds), - [r8]"m"(info.r8), [r9]"m"(info.r9), [r10]"m"(info.r10), [r11]"m"(info.r11),[r12]"m"(info.r12), [r13]"m"(info.r13), - [r14]"m"(info.r14), [r15]"m"(info.r15), [rdx]"m"(info.rdx),[rcx]"m"(info.rcx), [rbx]"m"(info.rbx), - [rax]"m"(info.rax), [rbp]"m"(info.rbp) : "memory"); + // Check %cs in iret frame on stack whether we're returning to userspace + "testl $3, 8(%%rsp)\n" + "jz 1f\n" + "swapgs\n" + "1: iretq\n" + : + : [fpu]"m"(info.fpu), [cr3]"r"(info.cr3), [ss]"m"(info.ss), [rsp]"m"(info.rsp), [rflags]"m"(info.rflags), + [cs]"m"(info.cs), [rip]"m"(info.rip), [rsi]"m"(info.rsi), [rdi]"m"(info.rdi), [es]"m"(info.es), [ds]"m"(info.ds), + [r8]"m"(info.r8), [r9]"m"(info.r9), [r10]"m"(info.r10), [r11]"m"(info.r11), [r12]"m"(info.r12), [r13]"m"(info.r13), + [r14]"m"(info.r14), [r15]"m"(info.r15), [rdx]"m"(info.rdx), [rcx]"m"(info.rcx), [rbx]"m"(info.rbx), + [rax]"m"(info.rax), [rbp]"m"(info.rbp) + : "memory"); assert(false && "This line should be unreachable"); } diff --git a/arch/x86/64/source/ArchMemory.cpp b/arch/x86/64/source/ArchMemory.cpp index 11588811d..e03ce98ff 100644 --- a/arch/x86/64/source/ArchMemory.cpp +++ b/arch/x86/64/source/ArchMemory.cpp @@ -1,10 +1,18 @@ #include "ArchMemory.h" -#include "ArchInterrupts.h" -#include "kprintf.h" + #include "PageManager.h" +#include "Thread.h" +#include "kprintf.h" #include "kstring.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMulticore.h" #include "ArchThreads.h" -#include "Thread.h" + +#include "assert.h" + +// Also see x86/common/source/ArchMemory.cpp for common functionality PageMapLevel4Entry kernel_page_map_level_4[PAGE_MAP_LEVEL_4_ENTRIES] __attribute__((aligned(PAGE_SIZE))); PageDirPointerTableEntry kernel_page_directory_pointer_table[2 * PAGE_DIR_POINTER_TABLE_ENTRIES] __attribute__((aligned(PAGE_SIZE))); @@ -13,100 +21,128 @@ PageTableEntry kernel_page_table[8 * PAGE_TABLE_ENTRIES] __attribute__((aligned( ArchMemory::ArchMemory() { - page_map_level_4_ = PageManager::instance()->allocPPN(); + page_map_level_4_ = PageManager::instance().allocPPN(); PageMapLevel4Entry* new_pml4 = (PageMapLevel4Entry*) getIdentAddressOfPPN(page_map_level_4_); memcpy((void*) new_pml4, (void*) kernel_page_map_level_4, PAGE_SIZE); memset(new_pml4, 0, PAGE_SIZE / 2); // should be zero, this is just for safety } +ArchMemory::ArchMemory(ppn_t pml4_ppn) : + page_map_level_4_(pml4_ppn) +{ +} + +template +bool ArchMemory::tableEmpty(T* table) +{ + for (size_t i = 0; i < NUM_ENTRIES; i++) + { + if (table[i].present) + return false; + } + return true; +} + template -bool ArchMemory::checkAndRemove(pointer map_ptr, uint64 index) +void ArchMemory::removeEntry(T* table, size_t index) { - T* map = (T*) map_ptr; - debug(A_MEMORY, "%s: page %p index %zx\n", __PRETTY_FUNCTION__, map, index); - ((uint64*) map)[index] = 0; - for (uint64 i = 0; i < PAGE_DIR_ENTRIES; i++) - { - if (map[i].present != 0) - return false; - } - return true; + assert(table[index].present); + + table[index].present = 0; + memset(&table[index], 0, sizeof(table[index])); } -bool ArchMemory::unmapPage(uint64 virtual_page) +bool ArchMemory::unmapPage(vpn_t virtual_page) { ArchMemoryMapping m = resolveMapping(virtual_page); + debug(A_MEMORY, "Unmap %zx => pml4: %lx, pdpt: %lx, pd: %lx, pt: %lx, page: %lx\n", + virtual_page, m.pml4_ppn, m.pdpt_ppn, m.pd_ppn, m.pt_ppn, m.page_ppn); + assert(m.page && m.page_size == PAGE_SIZE); + + removeEntry(m.pt, m.pti); - assert(m.page_ppn != 0 && m.page_size == PAGE_SIZE && m.pt[m.pti].present); - m.pt[m.pti].present = 0; - PageManager::instance()->freePPN(m.page_ppn); - ((uint64*)m.pt)[m.pti] = 0; // for easier debugging - bool empty = checkAndRemove(getIdentAddressOfPPN(m.pt_ppn), m.pti); - if (empty) + bool pdpt_empty = false; + bool pd_empty = false; + bool pt_empty = tableEmpty(m.pt); + + if (pt_empty) { - empty = checkAndRemove(getIdentAddressOfPPN(m.pd_ppn), m.pdi); - PageManager::instance()->freePPN(m.pt_ppn); + removeEntry(&m.pd->pt, m.pdi); + pd_empty = tableEmpty(&m.pd->pt); } - if (empty) + if (pd_empty) { - empty = checkAndRemove(getIdentAddressOfPPN(m.pdpt_ppn), m.pdpti); - PageManager::instance()->freePPN(m.pd_ppn); + removeEntry(&m.pdpt->pd, m.pdpti); + pdpt_empty = tableEmpty(&m.pdpt->pd); } - if (empty) + if (pdpt_empty) { - checkAndRemove(getIdentAddressOfPPN(m.pml4_ppn), m.pml4i); - PageManager::instance()->freePPN(m.pdpt_ppn); + removeEntry(m.pml4, m.pml4i); } + + flushAllTranslationCaches(virtual_page * PAGE_SIZE); // Needs to happen after page table entries have been modified but before PPNs are freed + + PageManager::instance().freePPN(m.page_ppn); + if(pt_empty) { PageManager::instance().freePPN(m.pt_ppn); } + if(pd_empty) { PageManager::instance().freePPN(m.pd_ppn); } + if(pdpt_empty) { PageManager::instance().freePPN(m.pdpt_ppn); } + return true; } template -void ArchMemory::insert(pointer map_ptr, uint64 index, uint64 ppn, uint64 bzero, uint64 size, uint64 user_access, - uint64 writeable) +void ArchMemory::insert(T* table, size_t index, ppn_t ppn, bool user_access, bool writeable, bool memory_mapped_io) { - assert(map_ptr & ~0xFFFFF00000000000ULL); - T* map = (T*) map_ptr; - debug(A_MEMORY, "%s: page %p index %zx ppn %zx user_access %zx size %zx\n", __PRETTY_FUNCTION__, map, index, ppn, - user_access, size); - if (bzero) + debugAdvanced(A_MEMORY, "%s: page %p index %zx ppn %lx user_access %u\n", + __PRETTY_FUNCTION__, table, index, ppn, user_access); + + assert((size_t)table & ~0xFFFFF00000000000ULL); + assert(((uint64*)table)[index] == 0); + + if(memory_mapped_io) { - memset((void*) getIdentAddressOfPPN(ppn), 0, PAGE_SIZE); - assert(((uint64* )map)[index] == 0); + table[index].write_through = 1; + table[index].cache_disabled = 1; } - map[index].size = size; - map[index].writeable = writeable; - map[index].page_ppn = ppn; - map[index].user_access = user_access; - map[index].present = 1; + + table[index].size = 0; + table[index].writeable = writeable; + table[index].page_ppn = ppn; + table[index].user_access = user_access; + table[index].present = 1; } -bool ArchMemory::mapPage(uint64 virtual_page, uint64 physical_page, uint64 user_access) +bool ArchMemory::mapPage(vpn_t virtual_page, ppn_t physical_page, bool user_access) { - debug(A_MEMORY, "%zx %zx %zx %zx\n", page_map_level_4_, virtual_page, physical_page, user_access); - ArchMemoryMapping m = resolveMapping(page_map_level_4_, virtual_page); + ArchMemoryMapping m = resolveMapping(virtual_page); + debug(A_MEMORY, "Map %zx => pml4: %lx, pdpt: %lx, pd: %lx, pt: %lx, page: %lx, user: %u\n", + virtual_page, m.pml4_ppn, m.pdpt_ppn, m.pd_ppn, m.pt_ppn, physical_page, user_access); assert((m.page_size == 0) || (m.page_size == PAGE_SIZE)); - if (m.pdpt_ppn == 0) + if (!m.pdpt) { - m.pdpt_ppn = PageManager::instance()->allocPPN(); - insert((pointer) m.pml4, m.pml4i, m.pdpt_ppn, 1, 0, 1, 1); + m.pdpt_ppn = PageManager::instance().allocPPN(); + m.pdpt = (PageDirPointerTableEntry*) getIdentAddressOfPPN(m.pdpt_ppn); + insert(m.pml4, m.pml4i, m.pdpt_ppn, true, true, false); } - if (m.pd_ppn == 0) + if (!m.pd) { - m.pd_ppn = PageManager::instance()->allocPPN(); - insert(getIdentAddressOfPPN(m.pdpt_ppn), m.pdpti, m.pd_ppn, 1, 0, 1, 1); + m.pd_ppn = PageManager::instance().allocPPN(); + m.pd = (PageDirEntry*) getIdentAddressOfPPN(m.pd_ppn); + insert(&m.pdpt->pd, m.pdpti, m.pd_ppn, true, true, false); } - if (m.pt_ppn == 0) + if (!m.pt) { - m.pt_ppn = PageManager::instance()->allocPPN(); - insert(getIdentAddressOfPPN(m.pd_ppn), m.pdi, m.pt_ppn, 1, 0, 1, 1); + m.pt_ppn = PageManager::instance().allocPPN(); + m.pt = (PageTableEntry*) getIdentAddressOfPPN(m.pt_ppn); + insert(&m.pd->pt, m.pdi, m.pt_ppn, true, true, false); } - if (m.page_ppn == 0) + if (!m.page) { - insert(getIdentAddressOfPPN(m.pt_ppn), m.pti, physical_page, 0, 0, user_access, 1); + insert(m.pt, m.pti, physical_page, user_access, true, false); return true; } @@ -115,97 +151,116 @@ bool ArchMemory::mapPage(uint64 virtual_page, uint64 physical_page, uint64 user_ ArchMemory::~ArchMemory() { - assert(currentThread->kernel_registers_->cr3 != page_map_level_4_ * PAGE_SIZE && "thread deletes its own arch memory"); + debug(A_MEMORY, "~ArchMemory(): Free PML4 %lx\n", page_map_level_4_); - PageMapLevel4Entry* pml4 = (PageMapLevel4Entry*) getIdentAddressOfPPN(page_map_level_4_); - for (uint64 pml4i = 0; pml4i < PAGE_MAP_LEVEL_4_ENTRIES / 2; pml4i++) // free only lower half - { - if (pml4[pml4i].present) + size_t cr3 = 0; + asm("mov %%cr3, %[cr3]\n" : [cr3]"=g"(cr3)); + assert(cr3 != getValueForCR3() && "thread deletes its own arch memory"); + + PageMapLevel4Entry* pml4 = (PageMapLevel4Entry*) getIdentAddressOfPPN(page_map_level_4_); + for (size_t pml4i = 0; pml4i < PAGE_MAP_LEVEL_4_ENTRIES / 2; pml4i++) // free only lower half { - PageDirPointerTableEntry* pdpt = (PageDirPointerTableEntry*) getIdentAddressOfPPN(pml4[pml4i].page_ppn); - for (uint64 pdpti = 0; pdpti < PAGE_DIR_POINTER_TABLE_ENTRIES; pdpti++) - { - if (pdpt[pdpti].pd.present) + if (pml4[pml4i].present) { - assert(pdpt[pdpti].pd.size == 0); - PageDirEntry* pd = (PageDirEntry*) getIdentAddressOfPPN(pdpt[pdpti].pd.page_ppn); - for (uint64 pdi = 0; pdi < PAGE_DIR_ENTRIES; pdi++) - { - if (pd[pdi].pt.present) + auto pdpt_ppn = pml4[pml4i].page_ppn; + PageDirPointerTableEntry* pdpt = (PageDirPointerTableEntry*) getIdentAddressOfPPN(pdpt_ppn); + for (size_t pdpti = 0; pdpti < PAGE_DIR_POINTER_TABLE_ENTRIES; pdpti++) { - assert(pd[pdi].pt.size == 0); - PageTableEntry* pt = (PageTableEntry*) getIdentAddressOfPPN(pd[pdi].pt.page_ppn); - for (uint64 pti = 0; pti < PAGE_TABLE_ENTRIES; pti++) - { - if (pt[pti].present) + if (pdpt[pdpti].pd.present) { - pt[pti].present = 0; - PageManager::instance()->freePPN(pt[pti].page_ppn); + assert(pdpt[pdpti].pd.size == 0); + auto pd_ppn = pdpt[pdpti].pd.page_ppn; + PageDirEntry* pd = (PageDirEntry*) getIdentAddressOfPPN(pd_ppn); + for (size_t pdi = 0; pdi < PAGE_DIR_ENTRIES; pdi++) + { + if (pd[pdi].pt.present) + { + assert(pd[pdi].pt.size == 0); + auto pt_ppn = pd[pdi].pt.page_ppn; + PageTableEntry* pt = (PageTableEntry*) getIdentAddressOfPPN(pt_ppn); + for (size_t pti = 0; pti < PAGE_TABLE_ENTRIES; pti++) + { + if (pt[pti].present) + { + auto page_ppn = pt[pti].page_ppn; + removeEntry(pt, pti); + PageManager::instance().freePPN(page_ppn); + } + } + removeEntry(&pd->pt, pdi); + PageManager::instance().freePPN(pt_ppn); + } + } + removeEntry(&pdpt->pd, pdpti); + PageManager::instance().freePPN(pd_ppn); } - } - pd[pdi].pt.present = 0; - PageManager::instance()->freePPN(pd[pdi].pt.page_ppn); } - } - pdpt[pdpti].pd.present = 0; - PageManager::instance()->freePPN(pdpt[pdpti].pd.page_ppn); + removeEntry(pml4, pml4i); + PageManager::instance().freePPN(pdpt_ppn); } - } - pml4[pml4i].present = 0; - PageManager::instance()->freePPN(pml4[pml4i].page_ppn); } - } - PageManager::instance()->freePPN(page_map_level_4_); + PageManager::instance().freePPN(page_map_level_4_); } -pointer ArchMemory::checkAddressValid(uint64 vaddress_to_check) +pointer ArchMemory::checkAddressValid(size_t vaddress_to_check) const { - ArchMemoryMapping m = resolveMapping(page_map_level_4_, vaddress_to_check / PAGE_SIZE); + return checkAddressValid(page_map_level_4_, vaddress_to_check); +} + +pointer ArchMemory::checkAddressValid(ppn_t pml4, size_t vaddress_to_check) +{ + ArchMemoryMapping m = resolveMapping(pml4, vaddress_to_check / PAGE_SIZE); if (m.page != 0) { - debug(A_MEMORY, "checkAddressValid %zx and %zx -> true\n", page_map_level_4_, vaddress_to_check); + debug(A_MEMORY, "checkAddressValid, pml4: %#zx, vaddr: %#zx -> true\n", (size_t)pml4, vaddress_to_check); return m.page | (vaddress_to_check % m.page_size); } else { - debug(A_MEMORY, "checkAddressValid %zx and %zx -> false\n", page_map_level_4_, vaddress_to_check); + debug(A_MEMORY, "checkAddressValid, pml4: %#zx, vaddr: %#zx -> false\n", (size_t)pml4, vaddress_to_check); return 0; } } -const ArchMemoryMapping ArchMemory::resolveMapping(uint64 vpage) +const ArchMemoryMapping ArchMemory::resolveMapping(vpn_t vpage) const { return resolveMapping(page_map_level_4_, vpage); } -const ArchMemoryMapping ArchMemory::resolveMapping(uint64 pml4, uint64 vpage) +const ArchMemoryMapping ArchMemory::resolveMapping(ppn_t pml4, vpn_t vpage) { assert((vpage * PAGE_SIZE < USER_BREAK || vpage * PAGE_SIZE >= KERNEL_START) && "This is not a valid vpn! Did you pass an address to resolveMapping?"); ArchMemoryMapping m; - m.pti = vpage; - m.pdi = m.pti / PAGE_TABLE_ENTRIES; + m.pti = vpage; + m.pdi = m.pti / PAGE_TABLE_ENTRIES; m.pdpti = m.pdi / PAGE_DIR_ENTRIES; m.pml4i = m.pdpti / PAGE_DIR_POINTER_TABLE_ENTRIES; - m.pti %= PAGE_TABLE_ENTRIES; - m.pdi %= PAGE_DIR_ENTRIES; + m.pti %= PAGE_TABLE_ENTRIES; + m.pdi %= PAGE_DIR_ENTRIES; m.pdpti %= PAGE_DIR_POINTER_TABLE_ENTRIES; m.pml4i %= PAGE_MAP_LEVEL_4_ENTRIES; - assert(pml4 < PageManager::instance()->getTotalNumPages()); - m.pml4 = (PageMapLevel4Entry*) getIdentAddressOfPPN(pml4); - m.pdpt = 0; - m.pd = 0; - m.pt = 0; - m.page = 0; - m.pml4_ppn = pml4; - m.pdpt_ppn = 0; - m.pd_ppn = 0; - m.pt_ppn = 0; - m.page_ppn = 0; + if(A_MEMORY & OUTPUT_ADVANCED) + { + debug(A_MEMORY, "resolveMapping, vpn: %zx, pml4i: %lx(%lu), pdpti: %lx(%lu), pdi: %lx(%lu), pti: %lx(%lu)\n", vpage, m.pml4i, m.pml4i, m.pdpti, m.pdpti, m.pdi, m.pdi, m.pti, m.pti); + } + + assert(pml4 < PageManager::instance().getTotalNumPages()); + m.pml4 = (PageMapLevel4Entry*) getIdentAddressOfPPN(pml4); + m.pdpt = 0; + m.pd = 0; + m.pt = 0; + m.page = 0; + m.pml4_ppn = pml4; + m.pdpt_ppn = 0; + m.pd_ppn = 0; + m.pt_ppn = 0; + m.page_ppn = 0; m.page_size = 0; + if (m.pml4[m.pml4i].present) { m.pdpt_ppn = m.pml4[m.pml4i].page_ppn; @@ -213,22 +268,21 @@ const ArchMemoryMapping ArchMemory::resolveMapping(uint64 pml4, uint64 vpage) if (m.pdpt[m.pdpti].pd.present && !m.pdpt[m.pdpti].pd.size) // 1gb page ? { m.pd_ppn = m.pdpt[m.pdpti].pd.page_ppn; - if (m.pd_ppn > PageManager::instance()->getTotalNumPages()) + if (m.pd_ppn > PageManager::instance().getTotalNumPages()) { - debug(A_MEMORY, "%zx\n", m.pd_ppn); + debug(A_MEMORY, "%lx\n", m.pd_ppn); } - assert(m.pd_ppn < PageManager::instance()->getTotalNumPages()); + assert(m.pd_ppn < PageManager::instance().getTotalNumPages()); m.pd = (PageDirEntry*) getIdentAddressOfPPN(m.pdpt[m.pdpti].pd.page_ppn); if (m.pd[m.pdi].pt.present && !m.pd[m.pdi].pt.size) // 2mb page ? { m.pt_ppn = m.pd[m.pdi].pt.page_ppn; - assert(m.pt_ppn < PageManager::instance()->getTotalNumPages()); + assert(m.pt_ppn < PageManager::instance().getTotalNumPages()); m.pt = (PageTableEntry*) getIdentAddressOfPPN(m.pd[m.pdi].pt.page_ppn); if (m.pt[m.pti].present) { m.page = getIdentAddressOfPPN(m.pt[m.pti].page_ppn); m.page_ppn = m.pt[m.pti].page_ppn; - assert(m.page_ppn < PageManager::instance()->getTotalNumPages()); m.page_size = PAGE_SIZE; } } @@ -243,18 +297,28 @@ const ArchMemoryMapping ArchMemory::resolveMapping(uint64 pml4, uint64 vpage) { m.page_size = PAGE_SIZE * PAGE_TABLE_ENTRIES * PAGE_DIR_ENTRIES; m.page_ppn = m.pdpt[m.pdpti].page.page_ppn; - assert(m.page_ppn < PageManager::instance()->getTotalNumPages()); + //assert(m.page_ppn < PageManager::instance().getTotalNumPages()); m.page = getIdentAddressOfPPN(m.pdpt[m.pdpti].page.page_ppn); } } + + if(A_MEMORY & OUTPUT_ADVANCED) + { + debug(A_MEMORY, "resolveMapping, vpn: %zx, pml4: %lx, pdpt[%s]: %lx, pd[%s]: %lx, pt[%s]: %lx, page[%s]: %lx\n", + vpage, + m.pml4_ppn, + (m.pdpt ? "P" : "-"), m.pdpt_ppn, + (m.pd ? "P" : "-"), m.pd_ppn, + (m.pt ? "P" : "-"), m.pt_ppn, + (m.page ? "P" : "-"), m.page_ppn); + } return m; } -uint64 ArchMemory::get_PPN_Of_VPN_In_KernelMapping(size_t virtual_page, size_t *physical_page, - size_t *physical_pte_page) +size_t ArchMemory::get_PPN_Of_VPN_In_KernelMapping(vpn_t virtual_page, ppn_t *physical_page, + ppn_t *physical_pte_page) { - ArchMemoryMapping m = resolveMapping(((uint64) VIRTUAL_TO_PHYSICAL_BOOT(kernel_page_map_level_4) / PAGE_SIZE), - virtual_page); + ArchMemoryMapping m = resolveMapping(getKernelPagingStructureRootPhys() / PAGE_SIZE, virtual_page); if (physical_page) *physical_page = m.page_ppn; if (physical_pte_page) @@ -262,43 +326,74 @@ uint64 ArchMemory::get_PPN_Of_VPN_In_KernelMapping(size_t virtual_page, size_t * return m.page_size; } -void ArchMemory::mapKernelPage(size_t virtual_page, size_t physical_page) +bool ArchMemory::mapKernelPage(vpn_t virtual_page, ppn_t physical_page, bool can_alloc_pages, bool memory_mapped_io) { - ArchMemoryMapping mapping = resolveMapping(((uint64) VIRTUAL_TO_PHYSICAL_BOOT(kernel_page_map_level_4) / PAGE_SIZE), - virtual_page); - PageMapLevel4Entry* pml4 = kernel_page_map_level_4; - assert(pml4[mapping.pml4i].present); - PageDirPointerTableEntry *pdpt = (PageDirPointerTableEntry*) getIdentAddressOfPPN(pml4[mapping.pml4i].page_ppn); - assert(pdpt[mapping.pdpti].pd.present); - PageDirEntry *pd = (PageDirEntry*) getIdentAddressOfPPN(pdpt[mapping.pdpti].pd.page_ppn); - assert(pd[mapping.pdi].pt.present); - PageTableEntry *pt = (PageTableEntry*) getIdentAddressOfPPN(pd[mapping.pdi].pt.page_ppn); - assert(!pt[mapping.pti].present); - pt[mapping.pti].writeable = 1; - pt[mapping.pti].page_ppn = physical_page; - pt[mapping.pti].present = 1; - asm volatile ("movq %%cr3, %%rax; movq %%rax, %%cr3;" ::: "%rax"); + ArchMemoryMapping m = resolveMapping(getKernelPagingStructureRootPhys()/PAGE_SIZE, virtual_page); + debug(A_MEMORY, "Map (kernel) %zx => pml4: %lx, pdpt: %lx, pd: %lx, pt: %lx, page: %lx\n", + virtual_page, m.pml4_ppn, m.pdpt_ppn, m.pd_ppn, m.pt_ppn, physical_page); + + assert(m.pdpt || can_alloc_pages); + if(!m.pdpt && can_alloc_pages) + { + m.pdpt_ppn = PageManager::instance().allocPPN(); + m.pdpt = (PageDirPointerTableEntry*) getIdentAddressOfPPN(m.pdpt_ppn); + insert(m.pml4, m.pml4i, m.pdpt_ppn, false, true, false); + } + + assert(m.pd || can_alloc_pages); + if(!m.pd && can_alloc_pages) + { + m.pd_ppn = PageManager::instance().allocPPN(); + m.pd = (PageDirEntry*) getIdentAddressOfPPN(m.pd_ppn); + insert(&m.pdpt->pd, m.pdpti, m.pd_ppn, false, true, false); + } + + assert(m.pt || can_alloc_pages); + if(!m.pt && can_alloc_pages) + { + m.pt_ppn = PageManager::instance().allocPPN(); + m.pt = (PageTableEntry*) getIdentAddressOfPPN(m.pt_ppn); + insert(&m.pd->pt, m.pdi, m.pt_ppn, false, true, false); + } + + if (!m.page) + { + insert(m.pt, m.pti, physical_page, false, true, memory_mapped_io); + return true; + } + + return false; // already mapped } -void ArchMemory::unmapKernelPage(size_t virtual_page) +void ArchMemory::unmapKernelPage(size_t virtual_page, bool free_page) { - ArchMemoryMapping mapping = resolveMapping(((uint64) VIRTUAL_TO_PHYSICAL_BOOT(kernel_page_map_level_4) / PAGE_SIZE), - virtual_page); - PageMapLevel4Entry* pml4 = kernel_page_map_level_4; - assert(pml4[mapping.pml4i].present); - PageDirPointerTableEntry *pdpt = (PageDirPointerTableEntry*) getIdentAddressOfPPN(pml4[mapping.pml4i].page_ppn); - assert(pdpt[mapping.pdpti].pd.present); - PageDirEntry *pd = (PageDirEntry*) getIdentAddressOfPPN(pdpt[mapping.pdpti].pd.page_ppn); - assert(pd[mapping.pdi].pt.present); - PageTableEntry *pt = (PageTableEntry*) getIdentAddressOfPPN(pd[mapping.pdi].pt.page_ppn); - assert(pt[mapping.pti].present); - pt[mapping.pti].present = 0; - pt[mapping.pti].writeable = 0; - PageManager::instance()->freePPN(pt[mapping.pti].page_ppn); + ArchMemoryMapping m = resolveMapping(getKernelPagingStructureRootPhys() / PAGE_SIZE, virtual_page); + + assert(m.page && (m.page_size == PAGE_SIZE)); + + memset(&m.pt[m.pti], 0, sizeof(m.pt[m.pti])); + + flushAllTranslationCaches(virtual_page * PAGE_SIZE); // Needs to happen after page table entries have been modified but before PPNs are freed + + if(free_page) + { + PageManager::instance().freePPN(m.pt[m.pti].page_ppn); + } asm volatile ("movq %%cr3, %%rax; movq %%rax, %%cr3;" ::: "%rax"); } -PageMapLevel4Entry* ArchMemory::getRootOfKernelPagingStructure() +size_t ArchMemory::getPagingStructureRootPhys() const +{ + return page_map_level_4_ * PAGE_SIZE; +} + +PageMapLevel4Entry* ArchMemory::getKernelPagingStructureRootVirt() +{ + return kernel_page_map_level_4; +} + +ArchMemory& ArchMemory::kernelArchMemory() { - return kernel_page_map_level_4; + static ArchMemory kernel_arch_mem((size_t)ArchMemory::getKernelPagingStructureRootPhys()/PAGE_SIZE); + return kernel_arch_mem; } diff --git a/arch/x86/64/source/ArchMulticore.cpp b/arch/x86/64/source/ArchMulticore.cpp new file mode 100644 index 000000000..261bf3cd2 --- /dev/null +++ b/arch/x86/64/source/ArchMulticore.cpp @@ -0,0 +1,223 @@ +#include "ArchMulticore.h" + +#include "APIC.h" +#include "Allocator.h" +#include "CPUID.h" +#include "InterruptUtils.h" +#include "PlatformBus.h" +#include "Scheduler.h" +#include "ScopeLock.h" +#include "SystemState.h" +#include "Thread.h" +#include "offsets.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMemory.h" +#include "ArchThreads.h" + +#include "EASTL/atomic.h" + +#include "assert.h" +#include "debug.h" + +cpu_local GDT cpu_gdt; +cpu_local TSS cpu_tss; + +cpu_local XApic cpu_lapic_impl; +cpu_local Apic* cpu_lapic = &cpu_lapic_impl; + +cpu_local char cpu_stack[CPU_STACK_SIZE]; + + +eastl::atomic ap_started = false; + + +extern eastl::atomic running_cpus; + +extern GDT32Ptr ap_gdt32_ptr; +extern GDT ap_gdt32; + +extern GDT gdt; + +extern char apstartup_text_begin; +extern char apstartup_text_end; +extern "C" void apstartup(); + +extern uint32 ap_kernel_cr3; +extern char ap_pml4[PAGE_SIZE]; + +static uint8 ap_boot_stack[PAGE_SIZE]; + + +void ArchMulticore::initCpuLocalData(bool boot_cpu) +{ + initCpuLocalGDT(boot_cpu ? gdt : ap_gdt32); + initCpuLocalTSS((size_t)ArchMulticore::cpuStackTop()); + + // The constructor of objects declared as cpu_local will be called automatically the first time the cpu_local object is used. Other cpu_local objects _may or may not_ also be initialized at the same time. + debug(A_MULTICORE, "Initializing CPU local objects for CPU %zu\n", SMP::currentCpuId()); + + idle_thread = new IdleThread(); + debug(A_MULTICORE, "CPU %zu: %s initialized\n", SMP::currentCpuId(), idle_thread->getName()); + idle_thread->pinned_to_cpu = SMP::currentCpuId(); + Scheduler::instance()->addNewThread(idle_thread); +} + + + +void ArchMulticore::initCpuLocalGDT(GDT& template_gdt) +{ + cpu_gdt = template_gdt; + + debug(A_MULTICORE, "CPU switching to own GDT at: %p\n", &cpu_gdt); + GDT64Ptr(cpu_gdt).load(); + __asm__ __volatile__("mov %%ax, %%ds\n" + "mov %%ax, %%es\n" + "mov %%ax, %%ss\n" + ::"a"(KERNEL_DS)); +} + +void ArchMulticore::initCpuLocalTSS(size_t cpu_stack_top) +{ + debug(A_MULTICORE, "CPU init TSS at %p\n", &cpu_tss); + setTSSSegmentDescriptor((TSSSegmentDescriptor*)((char*)&cpu_gdt + KERNEL_TSS), (size_t)&cpu_tss >> 32, (size_t)&cpu_tss, sizeof(TSS) - 1, 0); + + cpu_tss.setTaskStack(cpu_stack_top); + + __asm__ __volatile__("ltr %%ax" : : "a"(KERNEL_TSS)); +} + +void ArchMulticore::initialize() +{ + assert(running_cpus == 0); + running_cpus = 1; + + CpuLocalStorage::initCpuLocalStorage(); + ArchMulticore::initCpuLocalData(true); +} + +void ArchMulticore::prepareAPStartup(size_t entry_addr) +{ + size_t apstartup_size = (size_t)(&apstartup_text_end - &apstartup_text_begin); + debug(A_MULTICORE, "apstartup_text_begin: %p, apstartup_text_end: %p, size: %zx\n", + &apstartup_text_begin, &apstartup_text_end, apstartup_size); + + debug(A_MULTICORE, "apstartup %p, phys: %zx\n", &apstartup, (size_t)VIRTUAL_TO_PHYSICAL_BOOT(entry_addr)); + + + pointer paddr0 = ArchMemory::getIdentAddressOfPPN(entry_addr/PAGE_SIZE) + (entry_addr % PAGE_SIZE); + auto m = ArchMemory::kernelArchMemory().resolveMapping(paddr0/PAGE_SIZE); + assert(m.page && "Page for application processor entry not mapped in kernel"); // TODO: Map if not present + assert((m.page_ppn == entry_addr/PAGE_SIZE) && "PPN in ident mapping doesn't match expected ppn for AP entry"); + + // Init AP gdt + debug(A_MULTICORE, "Init AP GDT at %p\n", &ap_gdt32); + auto m_ap_gdt = ArchMemory::kernelArchMemory().resolveMapping(((size_t)&ap_gdt32)/PAGE_SIZE); + assert(m_ap_gdt.page && "AP GDT virtual address not mapped in kernel"); + debug(A_MULTICORE, "AP GDT mapped on ppn %#lx\n", m_ap_gdt.page_ppn); + + memcpy(&ap_gdt32, &gdt, sizeof(ap_gdt32)); + ap_gdt32_ptr.addr = entry_addr + ((size_t)&ap_gdt32 - (size_t)&apstartup_text_begin); + ap_gdt32_ptr.limit = sizeof(ap_gdt32) - 1; + + // Init AP PML4 + debug(A_MULTICORE, "Init AP PML4\n"); + memcpy(&ap_pml4, &kernel_page_map_level_4, sizeof(ap_pml4)); + ap_kernel_cr3 = (size_t)VIRTUAL_TO_PHYSICAL_BOOT(kernel_page_map_level_4); // TODO: shouldn't this be ap_pml4? + + debug(A_MULTICORE, "Copying apstartup from virt [%p,%p] -> %p (phys: %zx), size: %zx\n", (void*)&apstartup_text_begin, (void*)&apstartup_text_end, (void*)paddr0, (size_t)entry_addr, (size_t)(&apstartup_text_end - &apstartup_text_begin)); + memcpy((void*)paddr0, (void*)&apstartup_text_begin, apstartup_size); +} + +void ArchMulticore::startOtherCPUs() +{ + if(cpu_lapic->isInitialized()) + { + debug(A_MULTICORE, "Starting other CPUs\n"); + + prepareAPStartup(AP_STARTUP_PADDR); + + for(auto& other_cpu_lapic : Apic::local_apic_list_) + { + if(other_cpu_lapic.flags.enabled && (other_cpu_lapic.apic_id != cpu_lapic->apicId())) + { + startAP(other_cpu_lapic.apic_id, AP_STARTUP_PADDR); + debug(A_MULTICORE, "BSP waiting for AP %x startup to be complete\n", other_cpu_lapic.apic_id); + while(!ap_started); + ap_started = false; + debug(A_MULTICORE, "AP %u startup complete, BSP continuing\n", other_cpu_lapic.apic_id); + } + } + + ScopeLock l(SMP::cpuListLock()); + for(auto& cpu : SMP::cpuList()) + { + debug(A_MULTICORE, "CPU %zu running\n", cpu->id()); + } + } + else + { + debug(A_MULTICORE, "No local APIC. Cannot start other CPUs\n"); + } +} + +extern "C" __attribute__((naked)) void apstartup64() +{ + // Stack is not set up yet, no function prologue! + // Load long mode data segments + __asm__ __volatile__( + "movw %[K_DS], %%ax\n" + "movw %%ax, %%ds\n" + "movw %%ax, %%ss\n" + "movw %%ax, %%es\n" + "movw %%ax, %%fs\n" + "movw %%ax, %%gs\n" + "movq %[stack], %%rsp\n" + "movq %[stack], %%rbp\n" + : + : [K_DS]"i"(KERNEL_DS), + [stack]"i"(ap_boot_stack + sizeof(ap_boot_stack))); + + ++running_cpus; + debug(A_MULTICORE, "AP startup 64\n"); + debug(A_MULTICORE, "AP switched to stack %p\n", ap_boot_stack + sizeof(ap_boot_stack)); + + // Stack variables are messed up in this function because we skipped the function prologue. Should be fine once we've entered another function. + ArchMulticore::initApplicationProcessorCpu(); +} + +extern void initCpuLocalInterruptHandlers(); + +void ArchMulticore::initApplicationProcessorCpu() +{ + debug(A_MULTICORE, "AP switching from temp kernel page tables to main kernel page tables: %zx\n", (size_t)ArchMemory::kernelArchMemory().getPagingStructureRootPhys()); + ArchMemory::loadPagingStructureRoot(ArchMemory::kernelArchMemory().getValueForCR3()); + + InterruptDescriptorTable::instance().idtr().load(); + + extern char cls_start; + extern char cls_end; + debug(A_MULTICORE, "Setting temporary CLS for AP [%p, %p)\n", &cls_start, &cls_end); + CpuLocalStorage::setCls(&cls_start); + currentThread = nullptr; + + CpuLocalStorage::initCpuLocalStorage(); + + ApicDriver::instance().cpuLocalInit(); + ApicTimerDriver::instance().cpuLocalInit(); + + initCpuLocalInterruptHandlers(); + + assert(cpu_lapic->apicId() == CPUID::localApicId()); + ArchMulticore::initCpuLocalData(); + + ArchThreads::initialise(); + + debug(A_MULTICORE, "Enable AP timer\n"); + assert(cpu_lapic->isInitialized() && cpu_lapic->usingAPICTimer() && "Use of local APIC timer is required for SMP"); + ArchInterrupts::enableTimer(); + + debug(A_MULTICORE, "Switching to CPU local stack at %p\n", ArchMulticore::cpuStackTop()); + ArchCommon::callWithStack(ArchMulticore::cpuStackTop(), waitForSystemStart); +} diff --git a/arch/x86/64/source/ArchThreads.cpp b/arch/x86/64/source/ArchThreads.cpp index fe65b24fe..2243d3ad7 100644 --- a/arch/x86/64/source/ArchThreads.cpp +++ b/arch/x86/64/source/ArchThreads.cpp @@ -1,134 +1,163 @@ #include "ArchThreads.h" -#include "ArchMemory.h" -#include "kprintf.h" -#include "paging-definitions.h" -#include "offsets.h" -#include "assert.h" + +#include "Scheduler.h" #include "Thread.h" +#include "kprintf.h" #include "kstring.h" +#include "offsets.h" +#include "paging-definitions.h" -extern PageMapLevel4Entry kernel_page_map_level_4[]; +#include "ArchMemory.h" +#include "ArchMulticore.h" + +#include "EASTL/unique_ptr.h" + +#include "assert.h" void ArchThreads::initialise() { - currentThreadRegisters = new ArchThreadRegisters{}; + // Required for interrupts + static ArchThreadRegisters boot_thread_registers{}; + currentThreadRegisters = &boot_thread_registers; - /** Enable SSE for floating point instructions in long mode **/ - asm volatile ("movq %%cr0, %%rax\n" - "and $0xFFFB, %%ax\n" - "or $0x2, %%ax\n" - "movq %%rax, %%cr0\n" - "movq %%cr4, %%rax\n" - "orq $0x200, %%rax\n" - "movq %%rax, %%cr4\n" : : : "rax"); + /** Enable SSE for floating point instructions in long mode **/ + asm volatile("movq %%cr0, %%rax\n" + "and $0xFFFB, %%ax\n" + "or $0x2, %%ax\n" + "movq %%rax, %%cr0\n" + "movq %%cr4, %%rax\n" + "orq $0x200, %%rax\n" + "movq %%rax, %%cr4\n" + : + : + : "rax"); } + void ArchThreads::setAddressSpace(Thread *thread, ArchMemory& arch_memory) { - assert(arch_memory.page_map_level_4_); - thread->kernel_registers_->cr3 = arch_memory.page_map_level_4_ * PAGE_SIZE; + assert(arch_memory.getPagingStructureRootPhys()); + thread->kernel_registers_->cr3 = arch_memory.getValueForCR3(); if (thread->user_registers_) - thread->user_registers_->cr3 = arch_memory.page_map_level_4_ * PAGE_SIZE; + thread->user_registers_->cr3 = arch_memory.getValueForCR3(); if(thread == currentThread) { - asm volatile("movq %[new_cr3], %%cr3\n" - ::[new_cr3]"r"(arch_memory.page_map_level_4_ * PAGE_SIZE)); + switchToAddressSpace(arch_memory); } } -void ArchThreads::createBaseThreadRegisters(ArchThreadRegisters *&info, void* start_function, void* stack) +void ArchThreads::switchToAddressSpace(Thread* thread) { - info = new ArchThreadRegisters{}; - pointer pml4 = (pointer)VIRTUAL_TO_PHYSICAL_BOOT(((pointer)ArchMemory::getRootOfKernelPagingStructure())); - - info->rflags = 0x200; - info->cr3 = pml4; - info->rsp = (size_t)stack; - info->rbp = (size_t)stack; - info->rip = (size_t)start_function; - - /* fpu (=fninit) */ - info->fpu[0] = 0xFFFF037F; - info->fpu[1] = 0xFFFF0000; - info->fpu[2] = 0xFFFFFFFF; - info->fpu[3] = 0x00000000; - info->fpu[4] = 0x00000000; - info->fpu[5] = 0x00000000; - info->fpu[6] = 0xFFFF0000; + ArchMemory::loadPagingStructureRoot(thread->kernel_registers_->cr3); } -void ArchThreads::createKernelRegisters(ArchThreadRegisters *&info, void* start_function, void* kernel_stack) +void ArchThreads::switchToAddressSpace(ArchMemory& arch_memory) { - createBaseThreadRegisters(info, start_function, kernel_stack); - - info->cs = KERNEL_CS; - info->ds = KERNEL_DS; - info->es = KERNEL_DS; - info->ss = KERNEL_SS; - assert(info->cr3); + ArchMemory::loadPagingStructureRoot(arch_memory.getValueForCR3()); } -void ArchThreads::createUserRegisters(ArchThreadRegisters *&info, void* start_function, void* user_stack, void* kernel_stack) + +WithAddressSpace::WithAddressSpace(Thread* thread) : + prev_addr_space_(0) { - createBaseThreadRegisters(info, start_function, user_stack); + if (thread) + { + prev_addr_space_ = thread->kernel_registers_->cr3; + ArchThreads::switchToAddressSpace(thread); + } +} - info->cs = USER_CS; - info->ds = USER_DS; - info->es = USER_DS; - info->ss = USER_SS; - info->rsp0 = (size_t)kernel_stack; - assert(info->cr3); +WithAddressSpace::WithAddressSpace(ArchMemory& arch_memory) +{ + prev_addr_space_ = arch_memory.getValueForCR3(); + ArchThreads::switchToAddressSpace(arch_memory); } -void ArchThreads::changeInstructionPointer(ArchThreadRegisters *info, void* function) +WithAddressSpace::~WithAddressSpace() { - info->rip = (size_t)function; + if (prev_addr_space_) + { + ArchMemory::loadPagingStructureRoot(prev_addr_space_); + } } -void ArchThreads::yield() +eastl::unique_ptr ArchThreads::createBaseThreadRegisters( + void* start_function, void* stack) { - __asm__ __volatile__("int $65"); + auto regs = eastl::make_unique(); + + setInterruptEnableFlag(*regs, true); + regs->cr3 = ArchMemory::kernelArchMemory().getValueForCR3(); + regs->rsp = (size_t)stack; + regs->rbp = (size_t)stack; + regs->rip = (size_t)start_function; + + /* fpu (=fninit) */ + regs->fpu[0] = 0xFFFF037F; + regs->fpu[1] = 0xFFFF0000; + regs->fpu[2] = 0xFFFFFFFF; + regs->fpu[3] = 0x00000000; + regs->fpu[4] = 0x00000000; + regs->fpu[5] = 0x00000000; + regs->fpu[6] = 0xFFFF0000; + + return regs; } -size_t ArchThreads::testSetLock(size_t &lock, size_t new_value) +eastl::unique_ptr ArchThreads::createKernelRegisters( + void* start_function, void* kernel_stack) { - return __sync_lock_test_and_set(&lock,new_value); + auto kregs = createBaseThreadRegisters(start_function, kernel_stack); + + kregs->cs = KERNEL_CS; + kregs->ds = KERNEL_DS; + kregs->es = KERNEL_DS; + kregs->ss = KERNEL_SS; + kregs->rsp0 = (size_t)kernel_stack; + assert(kregs->cr3); + return kregs; } -uint64 ArchThreads::atomic_add(uint64 &value, int64 increment) +eastl::unique_ptr ArchThreads::createUserRegisters( + void* start_function, void* user_stack, void* kernel_stack) { - int64 ret=increment; - __asm__ __volatile__( - "lock; xadd %0, %1;" - :"=a" (ret), "=m" (value) - :"a" (ret) - :); - return ret; + auto uregs = createBaseThreadRegisters(start_function, user_stack); + + uregs->cs = USER_CS; + uregs->ds = USER_DS; + uregs->es = USER_DS; + uregs->ss = USER_SS; + uregs->rsp0 = (size_t)kernel_stack; + assert(uregs->cr3); + return uregs; } -int64 ArchThreads::atomic_add(int64 &value, int64 increment) +void ArchThreads::changeInstructionPointer(ArchThreadRegisters& info, void* function) { - return (int64) ArchThreads::atomic_add((uint64 &) value, increment); + info.rip = (size_t)function; } -void ArchThreads::atomic_set(uint32& target, uint32 value) +void* ArchThreads::getInstructionPointer(ArchThreadRegisters& info) { - __atomic_store_n(&(target), value, __ATOMIC_SEQ_CST); + return (void*)info.rip; } -void ArchThreads::atomic_set(int32& target, int32 value) +void ArchThreads::setInterruptEnableFlag(ArchThreadRegisters& info, bool interrupts_enabled) { - __atomic_store_n(&(target), value, __ATOMIC_SEQ_CST); + if (interrupts_enabled) + info.rflags |= 0x200; + else + info.rflags &= ~0x200; } -void ArchThreads::atomic_set(uint64& target, uint64 value) +bool ArchThreads::getInterruptEnableFlag(ArchThreadRegisters& info) { - __atomic_store_n(&(target), value, __ATOMIC_SEQ_CST); + return info.rflags & 0x200; } -void ArchThreads::atomic_set(int64& target, int64 value) +void ArchThreads::yield() { - __atomic_store_n(&(target), value, __ATOMIC_SEQ_CST); + __asm__ __volatile__("int $65"); } void ArchThreads::printThreadRegisters(Thread *thread, bool verbose) @@ -139,7 +168,7 @@ void ArchThreads::printThreadRegisters(Thread *thread, bool verbose) void ArchThreads::printThreadRegisters(Thread *thread, size_t userspace_registers, bool verbose) { - ArchThreadRegisters *info = userspace_registers?thread->user_registers_:thread->kernel_registers_; + ArchThreadRegisters *info = userspace_registers ? thread->user_registers_.get() : thread->kernel_registers_.get(); if (!info) { kprintfd("%sThread: %18p, has no %s registers. %s\n",userspace_registers?" User":"Kernel",thread,userspace_registers?"User":"Kernel",userspace_registers?"":"This should never(!) occur. How did you do that?"); @@ -147,14 +176,14 @@ void ArchThreads::printThreadRegisters(Thread *thread, size_t userspace_register else if (verbose) { kprintfd("\t\t%sThread: %18p, info: %18p\n"\ - "\t\t\t rax: %18zx rbx: %18zx rcx: %18zx rdx: %18zx\n"\ - "\t\t\t rsp: %18zx rbp: %18zx rsp0 %18zx rip: %18zx\n"\ - "\t\t\trflg: %18zx cr3: %18zx\n", + "\t\t\t rax: %18lx rbx: %18lx rcx: %18lx rdx: %18lx\n"\ + "\t\t\t rsp: %18lx rbp: %18lx rsp0 %18lx rip: %18lx\n"\ + "\t\t\trflg: %18lx cr3: %18lx\n", userspace_registers?" User":"Kernel",thread,info,info->rax,info->rbx,info->rcx,info->rdx,info->rsp,info->rbp,info->rsp0,info->rip,info->rflags,info->cr3); } else { - kprintfd("%sThread: %18p, info: %18p -- rax: %18zx rbx: %18zx rcx: %18zx rdx: %18zx -- rsp: %18zx rbp: %18zx rsp0 %18zx -- rip: %18zx rflg: %18zx cr3: %zx\n", + kprintfd("%sThread: %18p, info: %18p -- rax: %18lx rbx: %18lx rcx: %18lx rdx: %18lx -- rsp: %18lx rbp: %18lx rsp0 %18lx -- rip: %18lx rflg: %18lx cr3: %lx\n", userspace_registers?" User":"Kernel",thread,info,info->rax,info->rbx,info->rcx,info->rdx,info->rsp,info->rbp,info->rsp0,info->rip,info->rflags,info->cr3); } } @@ -164,10 +193,9 @@ extern "C" void threadStartHack(); void ArchThreads::debugCheckNewThread(Thread* thread) { assert(currentThread); - ArchThreads::printThreadRegisters(currentThread,false); - ArchThreads::printThreadRegisters(thread,false); + ArchThreads::printThreadRegisters(currentThread, false); + ArchThreads::printThreadRegisters(thread, false); assert(thread->kernel_registers_ != 0 && thread->kernel_registers_ != currentThread->kernel_registers_ && "all threads need to have their own register sets"); - assert(thread->kernel_registers_->rsp0 == 0 && "kernel register set needs no backup of kernel esp"); assert(thread->kernel_registers_->rsp == thread->kernel_registers_->rbp && "new kernel stack must be empty"); assert(thread->kernel_registers_->rsp != currentThread->kernel_registers_->rsp && thread->kernel_registers_->rbp != currentThread->kernel_registers_->rbp && "all threads need their own stack"); assert(thread->kernel_registers_->cr3 < 0x80000000 && "cr3 contains the physical page dir address"); @@ -182,3 +210,10 @@ void ArchThreads::debugCheckNewThread(Thread* thread) return; assert(currentThread->user_registers_->rsp0 != thread->user_registers_->rsp0 && "no 2 threads may have the same esp0 value"); } + + + +[[noreturn]] void ArchThreads::startThreads(Thread* init_thread) +{ + contextSwitch(init_thread, init_thread->kernel_registers_.get()); +} diff --git a/arch/x86/64/source/InterruptDescriptorTable.cpp b/arch/x86/64/source/InterruptDescriptorTable.cpp new file mode 100644 index 000000000..2b60853b2 --- /dev/null +++ b/arch/x86/64/source/InterruptDescriptorTable.cpp @@ -0,0 +1,40 @@ +#include "InterruptDescriptorTable.h" + +#include "debug.h" + +#define LO_WORD(x) (((uint32)(x)) & 0x0000FFFFULL) +#define HI_WORD(x) ((((uint32)(x)) >> 16) & 0x0000FFFFULL) +#define LO_DWORD(x) (((uint64)(x)) & 0x00000000FFFFFFFFULL) +#define HI_DWORD(x) ((((uint64)(x)) >> 32) & 0x00000000FFFFFFFFULL) + +InterruptGateDesc::InterruptGateDesc(handler_func_t offset, uint8 dpl) : + segment_selector(KERNEL_CS), + ist(0), + zeros(0), + type(INTERRUPT), + zero_1(0), + dpl(dpl), + present(1), + reserved(0) +{ + setOffset(offset); +} + +void InterruptGateDesc::setOffset(handler_func_t offset) +{ + offset_ld_lw = LO_WORD(LO_DWORD((uintptr_t)offset)); + offset_ld_hw = HI_WORD(LO_DWORD((uintptr_t)offset)); + offset_hd = HI_DWORD((uintptr_t)offset); +} + +handler_func_t InterruptGateDesc::offset() +{ + return (handler_func_t)(((uintptr_t)offset_hd << 32) | + ((uintptr_t)offset_ld_hw << 16) | ((uintptr_t)offset_ld_lw)); +} + +void IDTR::load() +{ + debug(A_INTERRUPTS, "Loading IDT, base: %zx, limit: %x\n", base, limit); + asm volatile("lidt (%0) " : : "q"(this)); +} diff --git a/arch/x86/64/source/InterruptUtils.cpp b/arch/x86/64/source/InterruptUtils.cpp index 66bb05c8c..aa2983c4c 100644 --- a/arch/x86/64/source/InterruptUtils.cpp +++ b/arch/x86/64/source/InterruptUtils.cpp @@ -1,313 +1,290 @@ #include "InterruptUtils.h" -#include "ArchSerialInfo.h" +#include "8259.h" #include "BDManager.h" -#include "new.h" -#include "ports.h" -#include "ArchMemory.h" -#include "ArchThreads.h" -#include "ArchCommon.h" #include "Console.h" -#include "Terminal.h" -#include "kprintf.h" +#include "ErrorHandlers.h" +#include "KeyboardManager.h" +#include "Loader.h" +#include "PageFaultHandler.h" +#include "SWEBDebugInfo.h" #include "Scheduler.h" -#include "debug_bochs.h" -#include "offsets.h" -#include "kstring.h" - #include "SerialManager.h" -#include "KeyboardManager.h" -#include "panic.h" - +#include "Syscall.h" +#include "Terminal.h" #include "Thread.h" -#include "ArchInterrupts.h" +#include "TimerTickHandler.h" #include "backtrace.h" - -#include "SWEBDebugInfo.h" -#include "Loader.h" -#include "Syscall.h" +#include "debug_bochs.h" +#include "kprintf.h" +#include "kstring.h" +#include "new.h" +#include "offsets.h" #include "paging-definitions.h" -#include "PageFaultHandler.h" - -#include "8259.h" - -#define LO_WORD(x) (((uint32)(x)) & 0x0000FFFFULL) -#define HI_WORD(x) ((((uint32)(x)) >> 16) & 0x0000FFFFULL) -#define LO_DWORD(x) (((uint64)(x)) & 0x00000000FFFFFFFFULL) -#define HI_DWORD(x) ((((uint64)(x)) >> 32) & 0x00000000FFFFFFFFULL) - -#define TYPE_TRAP_GATE 15 // trap gate, i.e. IF flag is *not* cleared -#define TYPE_INTERRUPT_GATE 14 // interrupt gate, i.e. IF flag *is* cleared - -#define DPL_KERNEL_SPACE 0 // kernelspace's protection level -#define DPL_USER_SPACE 3 // userspaces's protection level - -#define SYSCALL_INTERRUPT 0x80 // number of syscall interrupt - - -// --- Pagefault error flags. -// PF because/in/caused by/... - -#define FLAG_PF_PRESENT 0x01 // =0: pt/page not present - // =1: of protection violation +#include "ports.h" -#define FLAG_PF_RDWR 0x02 // =0: read access - // =1: write access +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMemory.h" +#include "ArchMulticore.h" +#include "ArchSerialInfo.h" +#include "ArchThreads.h" -#define FLAG_PF_USER 0x04 // =0: supervisormode (CPL < 3) - // =1: usermode (CPL == 3) +#include "assert.h" +#include "debug.h" -#define FLAG_PF_RSVD 0x08 // =0: not a reserved bit - // =1: a reserved bit +extern const SWEBDebugInfo* kernel_debug_info; +extern eastl::atomic_flag assert_print_lock; -#define FLAG_PF_INSTR_FETCH 0x10 // =0: not an instruction fetch - // =1: an instruction fetch (need PAE for that) -struct GateDesc +// generic interrupt handler -> dispatches to registered handlers for invoked interrupt number +void interruptHandler(size_t interrupt_num, + uint64_t error_code, + ArchThreadRegisters* saved_registers) { - uint16 offset_ld_lw : 16; // low word / low dword of handler entry point's address - uint16 segment_selector : 16; // (code) segment the handler resides in - uint8 ist : 3; // interrupt stack table index - uint8 zeros : 5; // set to zero - uint8 type : 4; // set to TYPE_TRAP_GATE or TYPE_INTERRUPT_GATE - uint8 zero_1 : 1; // unsued - set to zero - uint8 dpl : 2; // descriptor protection level - uint8 present : 1; // present- flag - set to 1 - uint16 offset_ld_hw : 16; // high word / low dword of handler entry point's address - uint32 offset_hd : 32; // high dword of handler entry point's address - uint32 reserved : 32; -}__attribute__((__packed__)); - - -extern "C" void arch_dummyHandler(); -extern "C" void arch_dummyHandlerMiddle(); - -uint64 InterruptUtils::pf_address; -uint64 InterruptUtils::pf_address_counter; - -void InterruptUtils::initialise() -{ - uint32 num_handlers = 0; - for (uint32 i = 0; handlers[i].offset != 0; ++i) - num_handlers = handlers[i].number; - ++num_handlers; - // allocate some memory for our handlers - GateDesc *interrupt_gates = new GateDesc[num_handlers]; - size_t dummy_handler_sled_size = (((size_t) arch_dummyHandlerMiddle) - (size_t) arch_dummyHandler); - assert((dummy_handler_sled_size % 128) == 0 && "cannot handle weird padding in the kernel binary"); - dummy_handler_sled_size /= 128; - - uint32 j = 0; - for (uint32 i = 0; i < num_handlers; ++i) - { - while (handlers[j].number < i && handlers[j].offset != 0) - ++j; - interrupt_gates[i].offset_ld_lw = LO_WORD(LO_DWORD((handlers[j].number == i && handlers[j].offset != 0) ? (size_t)handlers[j].offset : (((size_t)arch_dummyHandler)+i*dummy_handler_sled_size))); - interrupt_gates[i].offset_ld_hw = HI_WORD(LO_DWORD((handlers[j].number == i && handlers[j].offset != 0) ? (size_t)handlers[j].offset : (((size_t)arch_dummyHandler)+i*dummy_handler_sled_size))); - interrupt_gates[i].offset_hd = HI_DWORD((handlers[j].number == i && handlers[j].offset != 0) ? (size_t)handlers[j].offset : (((size_t)arch_dummyHandler)+i*dummy_handler_sled_size)); - interrupt_gates[i].ist = 0; // we could provide up to 7 different indices here - 0 means legacy stack switching - interrupt_gates[i].present = 1; - interrupt_gates[i].segment_selector = KERNEL_CS; - interrupt_gates[i].type = TYPE_INTERRUPT_GATE; - interrupt_gates[i].zero_1 = 0; - interrupt_gates[i].zeros = 0; - interrupt_gates[i].reserved = 0; - interrupt_gates[i].dpl = ((i == SYSCALL_INTERRUPT && handlers[j].number == i) ? DPL_USER_SPACE : DPL_KERNEL_SPACE); - debug(A_INTERRUPTS, - "%x -- offset = %p, offset_ld_lw = %x, offset_ld_hw = %x, offset_hd = %x, ist = %x, present = %x, segment_selector = %x, type = %x, dpl = %x\n", i, handlers[i].offset, - interrupt_gates[i].offset_ld_lw, interrupt_gates[i].offset_ld_hw, - interrupt_gates[i].offset_hd, interrupt_gates[i].ist, - interrupt_gates[i].present, interrupt_gates[i].segment_selector, - interrupt_gates[i].type, interrupt_gates[i].dpl); - } - IDTR idtr; + debugAdvanced(A_INTERRUPTS, "[CPU %zu] Interrupt handler %zu, error: %lx\n", + SMP::currentCpuId(), interrupt_num, error_code); + assert(interrupt_num < 256); + + if (interrupt_num == 0xE) + { + uintptr_t pagefault_addr = 0; + asm volatile("movq %%cr2, %[cr2]\n" : [cr2] "=r"(pagefault_addr)); + pageFaultHandler(pagefault_addr, error_code, saved_registers->rip); + } + else if (interrupt_num < 32) + { + errorHandler(interrupt_num, saved_registers->rip, saved_registers->cs, 0); + } + else + { + ArchInterrupts::startOfInterrupt(interrupt_num); + ArchInterrupts::handleInterrupt(interrupt_num); + ArchInterrupts::endOfInterrupt(interrupt_num); + } - idtr.base = (pointer) interrupt_gates; - idtr.limit = sizeof(GateDesc) * num_handlers - 1; - lidt(&idtr); - pf_address = 0xdeadbeef; - pf_address_counter = 0; + debugAdvanced(A_INTERRUPTS, "Interrupt handler %zu end\n", interrupt_num); } -void InterruptUtils::lidt(IDTR *idtr) +/** + * Interrupt handler for Programmable Interval Timer (PIT) + * ISA IRQ 0 -> remapped to interrupt vector 32 via PIC 8259/IoApic + */ +void int32_handler_PIT_irq0() { - asm volatile("lidt (%0) ": :"q" (idtr)); + debugAdvanced(A_INTERRUPTS, "[CPU %zu] Interrupt vector %u (ISA IRQ %u) called\n", + SMP::currentCpuId(), InterruptVector::REMAP_OFFSET + (uint8_t)ISA_IRQ::PIT, 0); + + ArchCommon::callWithStack( + ArchMulticore::cpuStackTop(), + []() + { + TimerTickHandler::handleTimerTick(); + + ((char*)ArchCommon::getFBPtr())[1 + SMP::currentCpuId() * 2] = + ((currentThread->console_color << 4) | CONSOLECOLOR::BRIGHT_WHITE); + + // Signal end of interrupt here since we don't return normally + ArchInterrupts::endOfInterrupt(InterruptVector::REMAP_OFFSET + (uint8_t)ISA_IRQ::PIT); + contextSwitch(); + assert(false); + }); } -void InterruptUtils::countPageFault(uint64 address) +/** + * Interrupt handler for APIC timer + */ +void int127_handler_APIC_timer() { - if ((address ^ (uint64)currentThread) == pf_address) - { - pf_address_counter++; - } - else - { - pf_address = address ^ (uint64)currentThread; - pf_address_counter = 0; - } - if (pf_address_counter >= 10) - { - kprintfd("same pagefault from the same thread for 10 times in a row. most likely you have an error in your code\n"); - asm("hlt"); - } + debugAdvanced(A_INTERRUPTS, "[CPU %zu] Interrupt vector %u called\n", + SMP::currentCpuId(), InterruptVector::APIC_TIMER); + + ArchCommon::callWithStack(ArchMulticore::cpuStackTop(), + []() + { + TimerTickHandler::handleTimerTick(); + + ((char*)ArchCommon::getFBPtr())[1 + SMP::currentCpuId()*2] = + ((currentThread->console_color << 4) | + CONSOLECOLOR::BRIGHT_WHITE); + + // Signal end of interrupt here since we don't return normally + ArchInterrupts::endOfInterrupt(InterruptVector::APIC_TIMER); + contextSwitch(); + assert(false); + }); } -extern SWEBDebugInfo const *kernel_debug_info; - - -extern "C" void arch_contextSwitch(); - -extern ArchThreadRegisters *currentThreadRegisters; -extern Thread *currentThread; -extern "C" void arch_irqHandler_0(); -extern "C" void irqHandler_0() +/** + * Interrupt handler for software interrupt 65 used by yield() + */ +void int65_handler_swi_yield() { - ++outstanding_EOIs; - ArchCommon::drawHeartBeat(); - - Scheduler::instance()->incTicks(); - - Scheduler::instance()->schedule(); - - //kprintfd("irq0: Going to leave irq Handler 0\n"); - ArchInterrupts::EndOfInterrupt(0); - arch_contextSwitch(); + debugAdvanced(A_INTERRUPTS, "[CPU %zu] Interrupt vector %u called\n", + SMP::currentCpuId(), InterruptVector::YIELD); + + ArchCommon::callWithStack( + ArchMulticore::cpuStackTop(), + []() + { + Scheduler::instance()->schedule(); + + ((char*)ArchCommon::getFBPtr())[1 + SMP::currentCpuId() * 2] = + ((currentThread->console_color << 4) | CONSOLECOLOR::BRIGHT_WHITE); + + // Signal end of interrupt here since we don't return normally + ArchInterrupts::endOfInterrupt(InterruptVector::YIELD); + contextSwitch(); + assert(false); + }); } -extern "C" void arch_irqHandler_65(); -extern "C" void irqHandler_65() +/** + * Handler for pagefault exception + * + * @param address accessed address that caused the pagefault + * @param error information about the pagefault provided by the CPU + * @param ip CPU instruction pointer at the pagefault + */ +void pageFaultHandler(uint64_t address, uint64_t error, uint64_t ip) { - Scheduler::instance()->schedule(); - arch_contextSwitch(); + auto ®s = *(currentThread->switch_to_userspace_ ? currentThread->user_registers_ : + currentThread->kernel_registers_); + + // dirty hack due to qemu invoking the pf handler when accessing non canonical addresses + if (address >= USER_BREAK && address < KERNEL_START) + { + errorHandler(0xd, regs.rip, regs.cs, 0); + assert(false && "thread should not survive a GP fault"); + } + PagefaultExceptionErrorCode error_code{static_cast(error)}; + assert(!error_code.reserved_write && "Reserved bit set in page table entry"); + + assert(ArchThreads::getInterruptEnableFlag(regs) && "PF with interrupts disabled. PF handler will enable interrupts soon. Better panic now"); + + PageFaultHandler::enterPageFault(address, ip, error_code.user, error_code.present, + error_code.write, error_code.instruction_fetch); + if (currentThread->switch_to_userspace_) + contextSwitch(); + else + asm volatile ("movq %%cr3, %%rax; movq %%rax, %%cr3;" ::: "%rax"); } -extern "C" void errorHandler(size_t num, size_t eip, size_t cs, size_t spurious); -extern "C" void arch_pageFaultHandler(); -extern "C" void pageFaultHandler(uint64 address, uint64 error) +/** + * Handler for Inter-Processor-Interrupt vector 90. + * Used to stop other CPUs when a kernel panic occurs. + */ +void int90_handler_halt_cpu() { - auto ®s = *(currentThread->switch_to_userspace_ ? currentThread->user_registers_ : currentThread->kernel_registers_); + while (assert_print_lock.test_and_set(eastl::memory_order_acquire)); - if (address >= USER_BREAK && address < KERNEL_START) { // dirty hack due to qemu invoking the pf handler when accessing non canonical addresses - errorHandler(0xd, regs.rip, regs.cs, 0); - assert(0 && "thread should not survive a GP fault"); - } - assert(!(error & FLAG_PF_RSVD) && "Reserved bit set in page table entry"); + debugAlways(A_INTERRUPTS, "Interrupt %u called, CPU %zu halting\n", InterruptVector::IPI_HALT_CPU, SMP::currentCpuId()); + if (currentThread) + { + debug(BACKTRACE, "CPU %zu backtrace:\n", SMP::currentCpuId()); + currentThread->printBacktrace(false); + } - assert((regs.rflags&(1ULL<<9)) && "PF with interrupts disabled. PF handler will enable interrupts soon. Better panic now"); + assert_print_lock.clear(eastl::memory_order_release); - PageFaultHandler::enterPageFault(address, error & FLAG_PF_USER, - error & FLAG_PF_PRESENT, - error & FLAG_PF_RDWR, - error & FLAG_PF_INSTR_FETCH); - if (currentThread->switch_to_userspace_) - arch_contextSwitch(); - else - asm volatile ("movq %%cr3, %%rax; movq %%rax, %%cr3;" ::: "%rax"); + while(true) + { + ArchCommon::halt(); + } } -extern "C" void arch_irqHandler_1(); -extern "C" void irqHandler_1() +/** + * APIC error interrupt handler. + * Invoked by the APIC when it encounters an error condition. + */ +void int91_handler_APIC_error() { - ++outstanding_EOIs; - KeyboardManager::instance()->serviceIRQ( ); - ArchInterrupts::EndOfInterrupt(1); -} + auto error = cpu_lapic->readRegister(); + debugAlways(APIC, "Internal APIC error: %x\n", *(uint32_t*)&error); -extern "C" void arch_irqHandler_3(); -extern "C" void irqHandler_3() -{ - kprintfd( "IRQ 3 called\n" ); - ++outstanding_EOIs; - SerialManager::getInstance()->service_irq( 3 ); - ArchInterrupts::EndOfInterrupt(3); - kprintfd( "IRQ 3 ended\n" ); -} + assert(!"Internal APIC error"); -extern "C" void arch_irqHandler_4(); -extern "C" void irqHandler_4() -{ - kprintfd( "IRQ 4 called\n" ); - ++outstanding_EOIs; - SerialManager::getInstance()->service_irq( 4 ); - ArchInterrupts::EndOfInterrupt(4); - kprintfd( "IRQ 4 ended\n" ); + cpu_lapic->writeRegister({}); } -extern "C" void arch_irqHandler_6(); -extern "C" void irqHandler_6() +/** + * Handler for spurious APIC interrupts. + * Spurious interrupts can be triggered under special conditions when an interrupt signal + * becomes masked at the same time it is raised. Can safely be ignored. + */ +void int100_handler_APIC_spurious() { - kprintfd( "IRQ 6 called\n" ); - kprintfd( "IRQ 6 ended\n" ); + debug(A_INTERRUPTS, "IRQ %u called by CPU %zu, spurious APIC interrupt\n", InterruptVector::APIC_SPURIOUS, SMP::currentCpuId()); } -extern "C" void arch_irqHandler_9(); -extern "C" void irqHandler_9() +/** + * Handler for Inter-Processor-Interrupt vector 100. + * Used to send remote function call requests to other CPUs (e.g. TLB flush requests) + */ +void int101_handler_cpu_fcall() { - kprintfd( "IRQ 9 called\n" ); - ++outstanding_EOIs; - BDManager::getInstance()->serviceIRQ( 9 ); - ArchInterrupts::EndOfInterrupt(9); -} + debug(A_INTERRUPTS, "IRQ %u called by CPU %zu\n", InterruptVector::IPI_REMOTE_FCALL, SMP::currentCpuId()); -extern "C" void arch_irqHandler_11(); -extern "C" void irqHandler_11() -{ - kprintfd( "IRQ 11 called\n" ); - ++outstanding_EOIs; - BDManager::getInstance()->serviceIRQ( 11 ); - ArchInterrupts::EndOfInterrupt(11); -} + auto funcdata = SMP::currentCpu().fcall_queue.takeAll(); + while (funcdata != nullptr) + { + debug(A_INTERRUPTS, "CPU %zu: Function call request from CPU %zu\n", SMP::currentCpuId(), funcdata->orig_cpu); -extern "C" void arch_irqHandler_14(); -extern "C" void irqHandler_14() -{ - //kprintfd( "IRQ 14 called\n" ); - ++outstanding_EOIs; - BDManager::getInstance()->serviceIRQ( 14 ); - ArchInterrupts::EndOfInterrupt(14); -} + funcdata->received.store(true, eastl::memory_order_release); -extern "C" void arch_irqHandler_15(); -extern "C" void irqHandler_15() -{ - //kprintfd( "IRQ 15 called\n" ); - ++outstanding_EOIs; - BDManager::getInstance()->serviceIRQ( 15 ); - ArchInterrupts::EndOfInterrupt(15); + assert(funcdata->target_cpu == SMP::currentCpuId()); + assert(funcdata->func); + + funcdata->func(); + + auto next = funcdata->next.load(); + funcdata->done.store(true, eastl::memory_order_release); // funcdata object is invalid as soon as it is acknowledged + funcdata = next; + } } -extern "C" void arch_syscallHandler(); -extern "C" void syscallHandler() +/** + * Handler for syscall software interrupts from userspace. + * Marks thread as being in kernel mode, reenables interrupts, and passes syscall + * parameters stored in user registers to the generic syscall handler. + */ +void syscallHandler() { - currentThread->switch_to_userspace_ = 0; - currentThreadRegisters = currentThread->kernel_registers_; - ArchInterrupts::enableInterrupts(); - - auto ret = Syscall::syscallException(currentThread->user_registers_->rax, - currentThread->user_registers_->rbx, - currentThread->user_registers_->rcx, - currentThread->user_registers_->rdx, - currentThread->user_registers_->rsi, - currentThread->user_registers_->rdi); - - currentThread->user_registers_->rax = ret; - - ArchInterrupts::disableInterrupts(); - currentThread->switch_to_userspace_ = 1; - currentThreadRegisters = currentThread->user_registers_; - arch_contextSwitch(); -} + currentThread->switch_to_userspace_ = 0; + currentThreadRegisters = currentThread->kernel_registers_.get(); + ArchInterrupts::enableInterrupts(); + + auto ret = Syscall::syscallException(currentThread->user_registers_->rax, + currentThread->user_registers_->rbx, + currentThread->user_registers_->rcx, + currentThread->user_registers_->rdx, + currentThread->user_registers_->rsi, + currentThread->user_registers_->rdi); + currentThread->user_registers_->rax = ret; -extern const char* errors[]; -extern "C" void arch_errorHandler(); -extern "C" void errorHandler(size_t num, size_t eip, size_t cs, size_t spurious) + ArchInterrupts::disableInterrupts(); + currentThread->switch_to_userspace_ = 1; + currentThreadRegisters = currentThread->user_registers_.get(); + contextSwitch(); + assert(false); +} + +/** + * Handler for exception interrupts raised by the CPU (e.g. invalid opcode, divide by zero, general protection fault, ...) + * + * @param num raised exception number + * @param rip instruction pointer when the exception was raised + * @param cs selected code segment when the exception was raised + * @param spurious whether the exception was a spurious interrupt rather than a CPU exception + */ +void errorHandler(size_t num, size_t rip, size_t cs, size_t spurious) { kprintfd("%zx\n",cs); if (spurious) { - assert(num < 128 && "there are only 128 interrupts"); + assert(num < 256 && "there are only 256 interrupts"); debug(CPU_ERROR, "Spurious Interrupt %zu (%zx)\n", num, num); } else @@ -316,27 +293,25 @@ extern "C" void errorHandler(size_t num, size_t eip, size_t cs, size_t spurious) debug(CPU_ERROR, "\033[1;31m%s\033[0;39m\n", errors[num]); } const bool userspace = (cs & 0x3); - debug(CPU_ERROR, "Instruction Pointer: %zx, Userspace: %d - currentThread: %p %zd" ":%s, switch_to_userspace_: %d\n", - eip, userspace, currentThread, + debug(CPU_ERROR, "Instruction Pointer: %zx, Userspace: %d - currentThread(): %p %zd" ":%s, switch_to_userspace_: %d\n", + rip, userspace, currentThread, currentThread ? currentThread->getTID() : -1UL, currentThread ? currentThread->getName() : 0, currentThread ? currentThread->switch_to_userspace_ : -1); const Stabs2DebugInfo* deb = kernel_debug_info; assert(currentThread && "there should be no fault before there is a current thread"); assert(currentThread->kernel_registers_ && "every thread needs kernel registers"); - ArchThreadRegisters* registers_ = currentThread->kernel_registers_; if (userspace) { assert(currentThread->loader_ && "User Threads need to have a Loader"); assert(currentThread->user_registers_ && (currentThread->user_registers_->cr3 == currentThread->kernel_registers_->cr3 && "User and Kernel CR3 register values differ, this most likely is a bug!")); deb = currentThread->loader_->getDebugInfos(); - registers_ = currentThread->user_registers_; } - if(deb && registers_->rip) + if(deb && rip) { debug(CPU_ERROR, "This Fault was probably caused by:"); - deb->printCallInformation(registers_->rip); + deb->printCallInformation(rip); } ArchThreads::printThreadRegisters(currentThread, false); currentThread->printBacktrace(true); @@ -344,20 +319,26 @@ extern "C" void errorHandler(size_t num, size_t eip, size_t cs, size_t spurious) if (spurious) { if (currentThread->switch_to_userspace_) - arch_contextSwitch(); + { + contextSwitch(); + assert(false); + } } else { currentThread->switch_to_userspace_ = false; - currentThreadRegisters = currentThread->kernel_registers_; + currentThreadRegisters = currentThread->kernel_registers_.get(); ArchInterrupts::enableInterrupts(); debug(CPU_ERROR, "Terminating process...\n"); if (currentThread->user_registers_) + { Syscall::exit(888); + } else + { currentThread->kill(); + } + + assert(false); } } - -#include "ErrorHandlers.h" // error handler definitions and irq forwarding definitions - diff --git a/arch/x86/64/source/SegmentUtils.cpp b/arch/x86/64/source/SegmentUtils.cpp new file mode 100644 index 000000000..d5a645ef6 --- /dev/null +++ b/arch/x86/64/source/SegmentUtils.cpp @@ -0,0 +1,119 @@ +#include "SegmentUtils.h" + +#include "MSR.h" +#include "kstring.h" + +#include "assert.h" +#include "debug.h" + +GDT32Ptr::GDT32Ptr(uint16 gdt_limit, uint32 gdt_addr) : + limit(gdt_limit), + addr(gdt_addr) +{} + +GDT32Ptr::GDT32Ptr(GDT& gdt) : + limit(sizeof(gdt.entries) - 1), + addr((uint32)(size_t)&gdt.entries) +{} + +void GDT32Ptr::load() +{ + asm("lgdt %[gdt_ptr]\n" + : + :[gdt_ptr]"m"(*this)); +} + +GDT64Ptr::GDT64Ptr(uint16 gdt_limit, uint64 gdt_addr) : + limit(gdt_limit), + addr(gdt_addr) +{} + +GDT64Ptr::GDT64Ptr(GDT& gdt) : + limit(sizeof(gdt.entries) - 1), + addr((uint64)(size_t)&gdt.entries) +{} + +void GDT64Ptr::load() +{ + asm("lgdt %[gdt_ptr]\n" + : + :[gdt_ptr]"m"(*this)); +} + + +void setTSSSegmentDescriptor(TSSSegmentDescriptor* descriptor, uint32 baseH, uint32 baseL, uint32 limit, uint8 dpl) +{ + debug(A_MULTICORE, "setTSSSegmentDescriptor at %p, baseH: %x, baseL: %x, limit: %x, dpl: %x\n", descriptor, baseH, baseL, limit, dpl); + memset(descriptor, 0, sizeof(TSSSegmentDescriptor)); + descriptor->baseLL = (uint16) (baseL & 0xFFFF); + descriptor->baseLM = (uint8) ((baseL >> 16U) & 0xFF); + descriptor->baseLH = (uint8) ((baseL >> 24U) & 0xFF); + descriptor->baseH = baseH; + descriptor->limitL = (uint16) (limit & 0xFFFF); + descriptor->limitH = (uint8) (((limit >> 16U) & 0xF)); + descriptor->type = 0b1001; + descriptor->dpl = dpl; + descriptor->granularity = 0; + descriptor->present = 1; +} + +void TSS::setTaskStack(size_t stack_top) +{ + ist0 = stack_top; + rsp0 = stack_top; +} + + +size_t getGSBase() +{ + size_t gs_base; + MSR::getMSR(MSR_GS_BASE, (uint32*)&gs_base, ((uint32*)&gs_base) + 1); + return gs_base; +} + +size_t getGSKernelBase() +{ + size_t gs_base; + MSR::getMSR(MSR_KERNEL_GS_BASE, (uint32*)&gs_base, ((uint32*)&gs_base) + 1); + return gs_base; +} + +size_t getFSBase() +{ + size_t fs_base; + MSR::getMSR(MSR_FS_BASE, (uint32*)&fs_base, ((uint32*)&fs_base) + 1); + return fs_base; +} + + +void setGSBase(size_t gs_base) +{ + MSR::setMSR(MSR_GS_BASE, gs_base, gs_base >> 32); +} + +void setFSBase(size_t fs_base) +{ + MSR::setMSR(MSR_FS_BASE, fs_base, fs_base >> 32); +} + +void setSWAPGSKernelBase(size_t swapgs_base) +{ + MSR::setMSR(MSR_KERNEL_GS_BASE, swapgs_base, swapgs_base >> 32); +} + +void* getSavedFSBase() +{ + void* fs_base; + __asm__ __volatile__("movq %%gs:0, %%rax\n" + "movq %%rax, %[fs_base]\n" + : [fs_base]"=m"(fs_base) + : + : "rax"); + assert(fs_base != 0); + return fs_base; +} + +void restoreSavedFSBase() +{ + setFSBase((uint64)getSavedFSBase()); +} diff --git a/arch/x86/64/source/arch_apstartup.S b/arch/x86/64/source/arch_apstartup.S new file mode 100644 index 000000000..b1c55a8e2 --- /dev/null +++ b/arch/x86/64/source/arch_apstartup.S @@ -0,0 +1,174 @@ +# https://www.airs.com/blog/archives/518 +.section .note.GNU-stack,"",@progbits + +# Startup code for APs + +# TODO: Pass these as arguments from the BSP instead of hardcoding +.equ KERNEL_CS, 0x10 +.equ KERNEL_DS, 0x20 + +.code16gcc +.section .text.apstartup, "ax", @progbits + +.macro ap_print string + movl $\string, %ebx +1: + movb (%ebx), %al + testb %al, %al + jz 2f + movw $0xE9, %dx + outb %al, %dx + addl $1, %ebx + jmp 1b +2: +.endm + +.macro ap_print64 string + movq $\string, %rbx +1: + movb (%rbx), %al + testb %al, %al + jz 2f + movw $0xE9, %dx + outb %al, %dx + addq $1, %rbx + jmp 1b +2: +.endm + +.macro ap_dump addr num + movl $\addr, %ebx + movl $\num, %ecx +1: + testl %ecx, %ecx + jz 2f + + movb (%ebx), %al + movw $0xE9, %dx + outb %al, %dx + addl $1, %ebx + subl $1, %ecx + jmp 1b +2: +.endm + +# APs start in real mode and directly enter long mode without going to protected mode first +.global apstartup +.type apstartup, @function +apstartup: + cli + + movw %cs, %ax + movw %ax, %ds + movw %ax, %ss + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + ap_print (ap_s_start - apstartup) + + ap_print (ap_s_pae - apstartup) + + # Enable PAE + PSE + movl %cr4, %eax + orl $0x30,%eax + movl %eax, %cr4 + + ap_print (ap_s_cr3 - apstartup) + + # Load CR3 + movl $(ap_kernel_cr3 - apstartup), %eax + movl (%eax), %eax + movl %eax, %cr3 + + ap_print (ap_s_efer - apstartup) + + # Enable EFER.LME and EFER.NXE + mov $0xC0000080, %ecx + rdmsr + orl $0x900, %eax + wrmsr + + ap_print (ap_s_prot - apstartup) + + # Enable protected mode, write protection in kernel mode and paging + movl %cr0, %eax + orl $0x80010001,%eax + movl %eax, %cr0 + + ap_print (ap_s_lgdt - apstartup) + + movl $(ap_gdt32_ptr - apstartup), %eax + lgdt (%eax) + + ap_print (ap_s_ljmp - apstartup) + + ljmp $KERNEL_CS, $(apstartup64_trampoline - apstartup) + hlt + +.code64 +.global apstartup64_trampoline +.type apstartup64_trampoline, @function +apstartup64_trampoline: + ap_print64 ap_s_apstartup64 + +.extern apstartup 64 + movq $apstartup64, %rax + jmp *%rax + hlt + +.global ap_s_start +ap_s_start: + .asciz "AP started\n" +.global ap_s_pae +ap_s_pae: + .asciz "Enabling PAE\n" +.global ap_s_cr3 +ap_s_cr3: + .asciz "Loading CR3\n" +.global ap_s_efer +ap_s_efer: + .asciz "Enabling EFER.LME and EFER.NXE\n" +.global ap_s_prot +ap_s_prot: + .asciz "Enabling protected mode, write protection in kernel mode and paging\n" +.global ap_s_lgdt +ap_s_lgdt: + .asciz "Loading GDT\n" +.global ap_s_segm +ap_s_segm: + .asciz "Loading segment registers\n" +.global ap_s_ljmp +ap_s_ljmp: + .asciz "Far jmp to trampoline to load long mode code segment\n" +.global ap_s_apstartup64 +ap_s_apstartup64: + .asciz "Jumping to apstartup64\n" + +# To be filled in by the BSP +.global ap_kernel_cr3 +.type ap_kernel_cr3, @object +ap_kernel_cr3: + .skip 4 + +.global ap_gdt32_ptr +.type ap_gdt32_ptr, @object +ap_gdt32_ptr: + .skip 2 # uint16 limit + .skip 4 # uint32 addr + +.global ap_gdt32 +.type ap_gdt32, @object +ap_gdt32: + .skip 0x70 + + +.align 0x1000 +.global ap_pml4 +.type ap_pml4, @object +ap_pml4: +.skip 0x1000 + + + +.code64 diff --git a/arch/x86/64/source/arch_backtrace.cpp b/arch/x86/64/source/arch_backtrace.cpp index c10691cc4..dbdfdbba7 100644 --- a/arch/x86/64/source/arch_backtrace.cpp +++ b/arch/x86/64/source/arch_backtrace.cpp @@ -1,14 +1,14 @@ -#include "kprintf.h" -#include "Thread.h" -#include "backtrace.h" #include "InterruptUtils.h" -#include "ArchThreads.h" #include "KernelMemoryManager.h" // for use of "kernel_end_address" #include "Loader.h" -#include "umap.h" +#include "Thread.h" +#include "backtrace.h" +#include "kprintf.h" + #include "ArchCommon.h" +#include "ArchThreads.h" -extern Thread* currentThread; +#include "EASTL/map.h" struct StackFrame { diff --git a/arch/x86/64/source/arch_interrupts.S b/arch/x86/64/source/arch_interrupts.S index 9c33c2604..a0d5c847a 100644 --- a/arch/x86/64/source/arch_interrupts.S +++ b/arch/x86/64/source/arch_interrupts.S @@ -1,10 +1,42 @@ -# ok, this is our main interrupt handling stuff +/* https://www.airs.com/blog/archives/518 */ +.section .note.GNU-stack,"",@progbits .code64 .text +.extern arch_saveThreadRegisters +.extern genericInterruptHandler + .equ KERNEL_DS, 0x20 +.macro swapgsIfPrivChange cs_offset=8 + testl $3, \cs_offset(%rsp) + jz 1f + + /* + # # debug write to emulator io console port + # movw $0xE9, %dx + # xorq %rax, %rax + # movb 8(%rsp), %al + # andl $3, %eax + # addl $0x41, %eax + # outb %al, %dx + # hlt + */ + + swapgs +1: +.endm + +.macro loadKernelSegments + movw $KERNEL_DS, %ax + movw %ax,%ss + movw %ax,%ds + movw %ax,%es + /* movw %ax,%fs */ + /* movw %ax,%gs */ +.endm + .macro pushAll pushq %rsp pushq %rax @@ -26,15 +58,30 @@ pushq %rax movw %ds,%ax pushq %rax - movw $KERNEL_DS, %ax - movw %ax,%ss - movw %ax,%ds - movw %ax,%es - movw %ax,%fs - movw %ax,%gs + + /* push fs base */ + movq $0xC0000100, %rcx + rdmsr + pushq %rdx + pushq %rax + + loadKernelSegments .endm .macro popAll + + /* pop fs base (only relevant when there's no contextSwitch) */ + movq $0xC0000100, %rcx + popq %rax + popq %rdx + + /* Only restore fs base when returning to user mode (user fs is thread specific, kernel fs is _CPU_ specific and changes depending on which cpu the thread is scheduled on, so the saved fs base is incorrect when the thread is re-scheduled on another cpu) */ + /* Test for user mode by looking at the privilege level of the data segment selector that's next on the stack */ + testl $3, (%rsp) + jz 1f + wrmsr +1: + popq %rax movw %ax,%ds popq %rax @@ -57,122 +104,38 @@ popq %rsp .endm -.extern arch_saveThreadRegisters -.macro irqhandler num -.global arch_irqHandler_\num -.extern irqHandler_\num -arch_irqHandler_\num: - pushAll - movq %rsp,%rdi - movq $0,%rsi - call arch_saveThreadRegisters - call irqHandler_\num - popAll - iretq -.endm -dummyhandlerscratchvariable: - .long 0 - .long 0 - -.extern dummyHandler -.global arch_dummyHandler -arch_dummyHandler: -.rept 128 - decq dummyhandlerscratchvariable -.endr -.extern dummyHandlerMiddle -.global arch_dummyHandlerMiddle -arch_dummyHandlerMiddle: - pushAll - movq %rsp,%rdi - movq $0,%rsi - call arch_saveThreadRegisters - movq $1,%rcx - movq 152(%rsp),%rdx - movq 144(%rsp), %rsi - movq dummyhandlerscratchvariable,%rdi - addq $128, %rdi - movq $0,dummyhandlerscratchvariable - call errorHandler - popAll - iretq - hlt - - -.extern errorHandler -.macro errorhandler num -.global arch_errorHandler_\num -arch_errorHandler_\num: - pushAll - movq %rsp,%rdi - movq $0,%rsi - call arch_saveThreadRegisters - movq $0,%rcx - movq 152(%rsp),%rdx - movq 144(%rsp), %rsi - movq $\num,%rdi - call errorHandler - popAll - iretq - hlt -.endm +/* + Called by interrupt_entrystub(), which pushes the interrupt number + as well as a fake error code onto the stack if it wasn't generated by the cpu +*/ +.global arch_interruptHandler +.type arch_interruptHandler, @function +arch_interruptHandler: + swapgsIfPrivChange 24 + pushAll -.macro errorhandlerWithCode num -.global arch_errorHandler_\num -arch_errorHandler_\num: - pushAll - movq %rsp,%rdi - movq $1,%rsi - call arch_saveThreadRegisters - movq $0,%rcx - movq 160(%rsp),%rdx - movq 152(%rsp), %rsi - movq $\num,%rdi - call errorHandler - popAll - addq $8,%rsp - iretq - hlt -.endm + movq %rsp, %rdi -.text + movq 176(%rsp), %rax /* rip at interrupt, depends on size of saved registers! */ + + /* set up fake call stack for debugger backtrace */ + pushq %rax /* fake return address = rip at interrupt */ +.global arch_interruptHandler_backtrace_fix +.type arch_interruptHandler_backtrace_fix, @function +arch_interruptHandler_backtrace_fix: + pushq %rbp + movq %rsp, %rbp + + call genericInterruptEntry + + leave + popq %rdx /* pop simulated return address */ + + popAll + addq $16, %rsp /* pop interrupt number and error code */ -.extern pageFaultHandler -.global arch_pageFaultHandler -arch_pageFaultHandler: - pushAll - movq %rsp,%rdi - movq $1,%rsi - call arch_saveThreadRegisters - movq 144(%rsp),%rsi - movq %cr2, %rdi - call pageFaultHandler - popAll - addq $8,%rsp - iretq - hlt - - -.irp num,0,1,3,4,6,9,11,14,15,65 -irqhandler \num -.endr - -.irp num,8,10,11,12,13,14,17 -errorhandlerWithCode \num -.endr - -.irp num,0,4,5,6,7,9,16,18,19 -errorhandler \num -.endr - -.global arch_syscallHandler -.extern syscallHandler -arch_syscallHandler: - pushAll - movq %rsp,%rdi - movq $0,%rsi - call arch_saveThreadRegisters - call syscallHandler + swapgsIfPrivChange + iretq hlt diff --git a/arch/x86/64/source/boot.32.C b/arch/x86/64/source/boot.32.C index 9722093be..96a199ad9 100644 --- a/arch/x86/64/source/boot.32.C +++ b/arch/x86/64/source/boot.32.C @@ -4,6 +4,8 @@ asm(".equ PHYS_BASE,0xFFFFFFFF00000000"); #include "types.h" #include "offsets.h" #include "multiboot.h" +#include "print.32.h" +#include "SegmentUtils.h" #if A_BOOT == A_BOOT | OUTPUT_ENABLED #define PRINT(X) print(TRUNCATE(X)) @@ -11,120 +13,155 @@ asm(".equ PHYS_BASE,0xFFFFFFFF00000000"); #define PRINT(X) #endif -#define TRUNCATE(X) ({ volatile unsigned int x = (unsigned int)(((char*)X)+0x7FFFFFFF); (char*)(x+1); }) #define MULTIBOOT_PAGE_ALIGN (1<<0) #define MULTIBOOT_MEMORY_INFO (1<<1) #define MULTIBOOT_WANT_VESA (1<<2) -#define MULTIBOOT_HEADER_MAGIC (0x1BADB002) +#define MULTIBOOT_HEADER_MAGIC (0x1BADB002U) #define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_WANT_VESA) #define MULTIBOOT_CHECKSUM (-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)) extern uint32 bss_start_address; extern uint32 bss_end_address; extern uint8 boot_stack[]; -extern PageMapLevel4Entry kernel_page_map_level_4[]; -extern uint32 tss_selector; +extern PageMapLevel4Entry kernel_page_map_level_4[PAGE_MAP_LEVEL_4_ENTRIES] __attribute__((aligned(PAGE_SIZE))); +extern PageDirPointerTableEntry kernel_page_directory_pointer_table[2 * PAGE_DIR_POINTER_TABLE_ENTRIES] __attribute__((aligned(PAGE_SIZE))); +extern PageDirEntry kernel_page_directory[2 * PAGE_DIR_ENTRIES] __attribute__((aligned(PAGE_SIZE))); +extern PageTableEntry kernel_page_table[8 * PAGE_TABLE_ENTRIES] __attribute__((aligned(PAGE_SIZE))); -SegmentDescriptor gdt[7]; -struct GDT32Ptr -{ - uint16 limit; - uint32 addr; -}__attribute__((__packed__)); +extern uint32 tss_selector; -typedef struct -{ - uint32 reserved_0; - uint32 rsp0_l; - uint32 rsp0_h; - uint32 rsp1_l; - uint32 rsp1_h; - uint32 rsp2_l; - uint32 rsp2_h; - uint32 reserved_1; - uint32 reserved_2; - uint32 ist0_l; - uint32 ist0_h; - uint32 reserved_3[14]; - uint16 reserved_4; - uint16 iobp; -}__attribute__((__packed__)) TSS; +GDT gdt; TSS g_tss; -static const struct +__attribute__((section (".mboot"))) static const multiboot_header mboot = +{ + .magic = MULTIBOOT_HEADER_MAGIC, + .flags = MULTIBOOT_HEADER_FLAGS, + .checksum = MULTIBOOT_CHECKSUM, + + .header_addr = 0, + .load_addr = 0, + .load_end_addr = 0, + .bss_end_addr = 0, + .entry_addr = 0, + + .mode_type = 1, + .width = 80, + .height = 25, + .depth = 16, +}; + +static void writeLine2Bochs(const char* p) { - uint32 magic = MULTIBOOT_HEADER_MAGIC; - uint32 flags = MULTIBOOT_HEADER_FLAGS; - uint32 checksum = MULTIBOOT_CHECKSUM; - uint32 mode = 0; - uint32 widht = 800; - uint32 height = 600; - uint32 depth = 32; -} mboot __attribute__ ((section (".mboot"))); - -void print(const char* p) + while (*p) + { + asm volatile ("outb %b0, %w1" : : "a"(*p++), "d"(0xe9)); + } +} + + +void setSegmentBase(SegmentDescriptor* descr, uint32 baseL, uint32 baseH) { - while (*p) - asm volatile ("outb %b0, %w1" : : "a"(*p++), "d"(0xe9)); + descr->baseLL = (uint16) (baseL & 0xFFFF); + descr->baseLM = (uint8) ((baseL >> 16U) & 0xFF); + descr->baseLH = (uint8) ((baseL >> 24U) & 0xFF); + descr->baseH = baseH; } -static void memset(char* block, char c, size_t length) +void setSegmentLimit(SegmentDescriptor* descr, uint32 limit) { - for (size_t i = 0; i < length; ++i) - block[i] = c; + descr->limitL = (uint16) (limit & 0xFFFF); + descr->limitH = (uint8) (((limit >> 16U) & 0xF)); } -static void setSegmentDescriptor(uint32 index, uint32 baseH, uint32 baseL, uint32 limit, uint8 dpl, uint8 code, - uint8 tss) +static void setSegmentDescriptor(uint32 index, uint32 baseH, uint32 baseL, uint32 limit, uint8 dpl, uint8 code, uint8 tss) { - SegmentDescriptor* gdt_p = (SegmentDescriptor*) TRUNCATE(&gdt); - gdt_p[index].baseLL = (uint16) (baseL & 0xFFFF); - gdt_p[index].baseLM = (uint8) ((baseL >> 16U) & 0xFF); - gdt_p[index].baseLH = (uint8) ((baseL >> 24U) & 0xFF); - gdt_p[index].baseH = baseH; - gdt_p[index].limitL = (uint16) (limit & 0xFFFF); - gdt_p[index].limitH = (uint8) (((limit >> 16U) & 0xF)); - gdt_p[index].typeH = tss ? 0 : (code ? 0xA : 0xC); // 4kb + 64bit + auto gdt_p = (SegmentDescriptor*) TRUNCATE(&gdt); + + setSegmentBase(gdt_p + index, baseL, baseH); + setSegmentLimit(gdt_p + index, limit); + + // code segment: granularity = 4kb, long mode = 1, default size = reserved, has to be 0 + // data segment: granularity = 4kb, long mode = 1, default size = 32 + // tss: granularity = 1 byte + gdt_p[index].typeH = tss ? 0 : (code ? 0xA : 0xC); gdt_p[index].typeL = (tss ? 0x89 : 0x92) | ((dpl & 0x3) << 5) | (code ? 0x8 : 0); // present bit + memory expands upwards + code } +static void print(const char* p) +{ + writeLine2Bochs(p); + puts(p); +} + +extern multiboot_info_t* multi_boot_structure_pointer; + extern "C" void entry() { - asm volatile("mov %ebx,multi_boot_structure_pointer - BASE"); + // Save pointer to multiboot protocol information handed to us by the bootloader + asm volatile("mov %ebx, multi_boot_structure_pointer - BASE"); + + // Bootloader already put us in protected mode + PRINT("Booting...\n"); PRINT("Clearing Framebuffer...\n"); - memset((char*) 0xB8000, 0, 80 * 25 * 2); + clearFB(); PRINT("Clearing BSS...\n"); char* bss_start = TRUNCATE(&bss_start_address); memset(bss_start, 0, TRUNCATE(&bss_end_address) - bss_start); + puts(TRUNCATE("Multiboot structure pointer: ")); + putHex32(*(uint32*)TRUNCATE(&multi_boot_structure_pointer)); putc('\n'); + PRINT("Initializing Kernel Paging Structures...\n"); - asm volatile("movl $kernel_page_directory_pointer_table - BASE + 3, kernel_page_map_level_4 - BASE\n" - "movl $0, kernel_page_map_level_4 - BASE + 4\n"); - asm volatile("movl $kernel_page_directory - BASE + 3, kernel_page_directory_pointer_table - BASE\n" - "movl $0, kernel_page_directory_pointer_table - BASE + 4\n"); - asm volatile("movl $0x83, kernel_page_directory - BASE\n" - "movl $0, kernel_page_directory - BASE + 4\n"); - - PRINT("Enable PSE and PAE...\n"); - asm volatile("mov %cr4,%eax\n" - "or $0x20, %eax\n" + puts(TRUNCATE("Kernel PML4: ")); + putHex32((uint32)TRUNCATE(&kernel_page_map_level_4)); putc('\n'); + puts(TRUNCATE("Kernel PDPT: ")); + putHex32((uint32)TRUNCATE(&kernel_page_directory_pointer_table)); putc('\n'); + puts(TRUNCATE("Kernel PD: ")); + putHex32((uint32)TRUNCATE(&kernel_page_directory)); putc('\n'); + puts(TRUNCATE("Kernel PT: ")); + putHex32((uint32)TRUNCATE(&kernel_page_table)); putc('\n'); + + + // Map as uncachable for now to avoid problems with memory mapped I/O -> remapped later + asm volatile( + // Set PML4[0] -> PDPT + "movl $kernel_page_directory_pointer_table - BASE + 3, kernel_page_map_level_4 - BASE\n" + "movl $0, kernel_page_map_level_4 - BASE + 4\n" + // Set PDPT[0] -> PD + "movl $kernel_page_directory - BASE + 3, kernel_page_directory_pointer_table - BASE\n" + "movl $0, kernel_page_directory_pointer_table - BASE + 4\n" + // Set PD[0] present, writeable, cache disabled, size (2MB large page), ppn = 0 + "movl $0x93, kernel_page_directory - BASE\n" + "movl $0, kernel_page_directory - BASE + 4\n" + // Set PD[1] present, writeable, cache disabled, size (2MB large page), ppn = 1 + "movl $0x200093, kernel_page_directory - BASE + 8\n" + "movl $0, kernel_page_directory - BASE + 12\n" + ); + + PRINT("Enable PAE and PSE...\n"); + asm("mov %cr4,%eax\n" + "or $0x30, %eax\n" "mov %eax,%cr4\n"); PRINT("Setting CR3 Register...\n"); asm volatile("mov %[pd],%%cr3" : : [pd]"r"(TRUNCATE(kernel_page_map_level_4))); + // Enter compatibility mode (while long mode code segment is not yet loaded) + // Compatibility mode uses 4 level paging but ignores linear address bits 63:32 and treats them as 0 PRINT("Enable EFER.LME and EFER.NXE...\n"); asm volatile("mov $0xC0000080,%ecx\n" "rdmsr\n" "or $0x900,%eax\n" "wrmsr\n"); + PRINT("Clear flags...\n"); asm volatile("push $2\n" "popf\n"); @@ -142,19 +179,37 @@ extern "C" void entry() g_tss_p->iobp = -1; PRINT("Setup Segments...\n"); - setSegmentDescriptor(1, 0, 0, 0xFFFFFFFF, 0, 1, 0); - setSegmentDescriptor(2, 0, 0, 0xFFFFFFFF, 0, 0, 0); - setSegmentDescriptor(3, 0, 0, 0xFFFFFFFF, 3, 1, 0); - setSegmentDescriptor(4, 0, 0, 0xFFFFFFFF, 3, 0, 0); + setSegmentDescriptor(1, 0, 0, 0xFFFFFFFF, 0, 1, 0); // kernel code + setSegmentDescriptor(2, 0, 0, 0xFFFFFFFF, 0, 0, 0); // kernel data + setSegmentDescriptor(3, 0, 0, 0xFFFFFFFF, 3, 1, 0); // user code + setSegmentDescriptor(4, 0, 0, 0xFFFFFFFF, 3, 0, 0); // user data setSegmentDescriptor(5, -1U, (uint32) TRUNCATE(&g_tss) | 0x80000000, sizeof(TSS) - 1, 0, 0, 1); PRINT("Loading Long Mode GDT...\n"); - - struct GDT32Ptr gdt32_ptr; + GDT32Ptr gdt32_ptr; gdt32_ptr.limit = sizeof(gdt) - 1; - gdt32_ptr.addr = (uint32) TRUNCATE(gdt); + gdt32_ptr.addr = (uint32) TRUNCATE(&gdt); + puts(TRUNCATE("GDT addr: ")); + putHex32(gdt32_ptr.addr); putc('\n'); + + puts(TRUNCATE("GDT[KERNEL_DS] addr: ")); + putHex32(gdt32_ptr.addr + (KERNEL_DS >> 3)*(sizeof(SegmentDescriptor)/2)); putc('\n'); + asm volatile("lgdt %[gdt_ptr]" : : [gdt_ptr]"m"(gdt32_ptr)); - asm volatile("mov %%ax, %%ds\n" : : "a"(KERNEL_DS)); + + puts(TRUNCATE("Kernel data segment selector: ")); + putHex8(KERNEL_DS); putc('\n'); + + + puts(TRUNCATE("GDT[")); + putHex8(KERNEL_DS); + puts(TRUNCATE("]=")); + + for(uint8 i = 1; i <= sizeof(SegmentDescriptor); ++i) + { + putHex8(*((char*)((SegmentDescriptor*)TRUNCATE(&gdt) + ((KERNEL_DS >> 3)/2) + 1) - i)); + } + putc('\n'); PRINT("Setting Long Mode Segment Selectors...\n"); asm volatile("mov %%ax, %%ds\n" @@ -164,6 +219,8 @@ extern "C" void entry() "mov %%ax, %%gs\n" : : "a"(KERNEL_DS)); + // Long jump loads the long mode code segment selector from the GDT + // After this, we are in proper 64-bit long mode and no longer in compatibility mode PRINT("Calling entry64()...\n"); asm volatile("ljmp %[cs],$entry64-BASE\n" : : [cs]"i"(KERNEL_CS)); diff --git a/arch/x86/64/source/init_boottime_pagetables.cpp b/arch/x86/64/source/init_boottime_pagetables.cpp index b7c716efe..1e69853a8 100644 --- a/arch/x86/64/source/init_boottime_pagetables.cpp +++ b/arch/x86/64/source/init_boottime_pagetables.cpp @@ -1,20 +1,23 @@ -#include "types.h" -#include "paging-definitions.h" -#include "offsets.h" +#include "kprintf.h" #include "multiboot.h" +#include "offsets.h" +#include "paging-definitions.h" + #include "ArchCommon.h" -#include "kprintf.h" +#include "ArchMemory.h" -extern void* kernel_end_address; +#include "types.h" -extern PageDirPointerTableEntry kernel_page_directory_pointer_table[]; -extern PageDirEntry kernel_page_directory[]; -extern PageTableEntry kernel_page_table[]; -extern PageMapLevel4Entry kernel_page_map_level_4[]; +extern void* kernel_end_address; extern "C" void initialisePaging() { - uint32 i; + extern void* kernel_start_address; + extern void* kernel_end_address; + extern size_t text_start_address; + extern size_t ro_data_end_address; + extern size_t apstartup_text_begin; + extern size_t apstartup_text_end; PageMapLevel4Entry *pml4 = (PageMapLevel4Entry*)VIRTUAL_TO_PHYSICAL_BOOT(kernel_page_map_level_4); PageDirPointerTableEntry *pdpt1 = (PageDirPointerTableEntry*)VIRTUAL_TO_PHYSICAL_BOOT((pointer)kernel_page_directory_pointer_table); @@ -24,19 +27,27 @@ extern "C" void initialisePaging() PageTableEntry *pt = (PageTableEntry*)VIRTUAL_TO_PHYSICAL_BOOT((pointer)kernel_page_table); + VAddr k_start{(size_t)&kernel_start_address}; + VAddr k_end{(size_t)&kernel_end_address}; + // Note: the only valid address ranges are currently: // * 0000 0000 0 to * 7FFF FFFF F // binary: 011 111 111 means pml4i <= 255 // * 8000 0000 0 to * FFFF FFFF F // binary: 100 000 000 means pml4i >= 256 - // map the first and the last PM4L entry and one for the identity mapping + // map the first and the last PML4 entry and one for the identity mapping + // boot time ident mapping pml4[0].page_ppn = (uint64)pdpt1 / PAGE_SIZE; pml4[0].writeable = 1; pml4[0].present = 1; + + // ident mapping pml4[480].page_ppn = (uint64)pdpt1 / PAGE_SIZE; pml4[480].writeable = 1; pml4[480].present = 1; + + // kernel pml4[511].page_ppn = (uint64)pdpt2 / PAGE_SIZE; pml4[511].writeable = 1; pml4[511].present = 1; @@ -45,52 +56,78 @@ extern "C" void initialisePaging() // ident mapping 0x* F000 0000 0000 <-> 0x0 --> pml4i = 480, pdpti = 0 // ident mapping 0x* FFFF 8000 0000 <-> 0x0 --> pml4i = 511, pdpti = 510 + // pdpt for ident mapping pdpt1[0].pd.page_ppn = (uint64) pd1 / PAGE_SIZE; pdpt1[0].pd.writeable = 1; pdpt1[0].pd.present = 1; + // 1 GiB for the kernel pdpt2[510].pd.page_ppn = (uint64) pd2 / PAGE_SIZE; pdpt2[510].pd.writeable = 1; pdpt2[510].pd.present = 1; - // identity map - for (i = 0; i < PAGE_DIR_ENTRIES; ++i) + + // set up ident mapping + for (size_t i = 0; i < PAGE_DIR_ENTRIES; ++i) { pd2[i].page.present = 0; + pd1[i].page.page_ppn = i; pd1[i].page.size = 1; pd1[i].page.writeable = 1; + pd1[i].page.cache_disabled = 0; pd1[i].page.present = 1; } - // Map 8 page directories (8*512*4kb = max 16mb) - for (i = 0; i < 8; ++i) + + // Map 8 page directory entries for kernel (8*512*4kb = max 16mb) + for (size_t pdi = k_start.pdi; pdi <= k_end.pdi; ++pdi) { - pd2[i].pt.writeable = 1; - pd2[i].pt.present = 1; - pd2[i].pt.page_ppn = ((pointer)&pt[512*i])/PAGE_SIZE;; + assert(pdi < PAGE_DIR_ENTRIES); + size_t pdi_offset = pdi - k_start.pdi; + + pd2[pdi].pt.writeable = 1; + pd2[pdi].pt.present = 1; + pd2[pdi].pt.page_ppn = ((pointer)&pt[PAGE_TABLE_ENTRIES*pdi_offset])/PAGE_SIZE; } - size_t kernel_last_page = (size_t)VIRTUAL_TO_PHYSICAL_BOOT((pointer)&kernel_end_address) / PAGE_SIZE; - extern size_t ro_data_end_address; - size_t last_ro_data_page = (size_t)VIRTUAL_TO_PHYSICAL_BOOT((pointer)&ro_data_end_address) / PAGE_SIZE; + // AP startup pages need to be writeable to fill in the GDT, ... + size_t ap_text_start = ((size_t)&apstartup_text_begin/PAGE_SIZE)*PAGE_SIZE; + size_t ap_text_end = (size_t)&apstartup_text_end; // Map the kernel page tables (first 640kib = 184 pages are unused) - for (i = 184; i < last_ro_data_page - 256; ++i) + assert(k_start.addr + PAGE_SIZE < k_end.addr); + for(VAddr a{k_start}; a.addr < k_end.addr; a.addr += PAGE_SIZE) { - pt[i].present = 1; - pt[i].writeable = 0; - pt[i].page_ppn = i; + size_t pti = (a.pdi - k_start.pdi)*PAGE_TABLE_ENTRIES + a.pti; + assert(pti < sizeof(kernel_page_table)/sizeof(kernel_page_table[0])); + + pt[pti].page_ppn = (uint64)VIRTUAL_TO_PHYSICAL_BOOT(a.addr)/PAGE_SIZE; + pt[pti].writeable = + (((a.addr >= (pointer)&text_start_address) && + (a.addr < (pointer)&ro_data_end_address)) && + !((a.addr >= ap_text_start) && + (a.addr < ap_text_end)) + ? 0 + : 1); + pt[pti].present = 1; } - for (; i < kernel_last_page; ++i) + + // Map framebuffer + VAddr framebuffer_start{ArchCommon::getFBPtr()}; + VAddr framebuffer_end{ArchCommon::getFBPtr() + ArchCommon::getFBSize()}; + for(VAddr a{framebuffer_start}; a.addr < framebuffer_end.addr; a.addr += PAGE_SIZE) { - pt[i].present = 1; - pt[i].writeable = 1; - pt[i].page_ppn = i; + size_t pti = (a.pdi - framebuffer_start.pdi)*PAGE_TABLE_ENTRIES + a.pti; + + pt[pti].page_ppn = (uint64)VIRTUAL_TO_PHYSICAL_BOOT(a.addr)/PAGE_SIZE; + pt[pti].writeable = 1; + pt[pti].write_through = 1; + pt[pti].present = 1; } if (ArchCommon::haveVESAConsole(0)) { - for (i = 0; i < 8; ++i) // map the 16 MiB (8 pages) framebuffer + for (size_t i = 0; i < 8; ++i) // map the 16 MiB (8 pages) framebuffer { pd2[504+i].page.present = 1; pd2[504+i].page.writeable = 1; diff --git a/arch/x86/64/source/print.32.C b/arch/x86/64/source/print.32.C new file mode 100644 index 000000000..64ce078e3 --- /dev/null +++ b/arch/x86/64/source/print.32.C @@ -0,0 +1,126 @@ +asm(".code32"); +#include "offsets.h" + +#include "types.h" + +#define FB_ADDR ((char*)0xB8000) +#define FB_COLS 80 +#define FB_ROWS 25 + +uint8 fb_row = 0; +uint8 fb_col = 0; + +void memset(char* block, char c, size_t length) +{ + for (size_t i = 0; i < length; ++i) + { + block[i] = c; + } +} + +void setFBrow(uint8 row) +{ + *(uint8*)TRUNCATE(&fb_row) = row; +} + +void setFBcol(uint8 col) +{ + *(uint8*)TRUNCATE(&fb_col) = col; +} + +uint8 getFBrow() +{ + return *(uint8*)TRUNCATE(&fb_row); +} + +uint8 getFBcol() +{ + return *(uint8*)TRUNCATE(&fb_col); +} + +uint8 getNextFBrow() +{ + return (getFBrow() == FB_ROWS - 1 ? 0 : getFBrow() + 1); +} + +void clearFB() +{ + memset(FB_ADDR, 0, FB_COLS * FB_ROWS * 2); + setFBrow(0); + setFBcol(0); +} + +char* getFBAddr(uint8 row, uint8 col) +{ + return FB_ADDR + ((row * FB_COLS + col) * 2); +} + +void clearFBrow(uint8 row) +{ + memset(getFBAddr(row, 0), 0, FB_COLS * 2); +} + +void FBnewline() +{ + uint8 next_row = getNextFBrow(); + clearFBrow(next_row); + setFBrow(next_row); + setFBcol(0); +} + +void putc(const char c) +{ + if (c == '\n') + { + FBnewline(); + } + else + { + if (getFBcol() == FB_COLS) + { + FBnewline(); + } + + uint32 row = getFBrow(); + uint32 col = getFBcol(); + + char* fb_pos = getFBAddr(row, col); + fb_pos[0] = c; + fb_pos[1] = 0x02; + + setFBcol(getFBcol() + 1); + } +} + +void puts(const char* string) +{ + while (*string != '\0') + { + putc(*string); + ++string; + } +} + +uint8 nibbleToASCII(char nibble) +{ + nibble &= 0xF; + return nibble < 10 ? '0' + nibble : 'A' + nibble - 10; +} + +void putHex8(char c) +{ + char nibble_l = c & 0xF; + char nibble_h = c >> 4; + putc(nibbleToASCII(nibble_h)); + putc(nibbleToASCII(nibble_l)); +} + +void putHex32(uint32 v) +{ + for (uint8 i = 1; i <= sizeof(v); ++i) + { + putHex8(*((char*)&v + sizeof(v) - i)); + } +} + +asm(".code64"); diff --git a/arch/x86/64/userspace/CMakeLists.txt b/arch/x86/64/userspace/CMakeLists.txt index 07c5172fc..07ab147ad 100644 --- a/arch/x86/64/userspace/CMakeLists.txt +++ b/arch/x86/64/userspace/CMakeLists.txt @@ -1,5 +1,8 @@ -FILE(GLOB userspace_libc_SOURCES ${CMAKE_CURRENT_LIST_DIR}/*.c) +cmake_minimum_required(VERSION 3.11) + +FILE(GLOB userspace_libc_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_LIST_DIR}/*.c) target_sources(userspace_libc PRIVATE - ${userspace_libc_SOURCES}) + ${userspace_libc_SOURCES} + ) diff --git a/arch/x86/64/utils/kernel-ld-script.ld b/arch/x86/64/utils/kernel-ld-script.ld index 30e803a63..cf075e5ab 100644 --- a/arch/x86/64/utils/kernel-ld-script.ld +++ b/arch/x86/64/utils/kernel-ld-script.ld @@ -2,17 +2,19 @@ * this linker script tells ld how to link and which symbols to add to the * kernel.x binary */ - + OUTPUT_FORMAT(elf64-x86-64) ENTRY(entry) LS_Phys = 0x100000; LS_Virt = 0xFFFFFFFF80000000; UpperToLower = 0xFFFFFFFF00000000; + SECTIONS { . = LS_Virt + LS_Phys; + LS_START = .; - .text : AT(ADDR(.text) - LS_Virt) + .text ALIGN(4096) : AT(LS_Phys + (LS_Code - LS_START)) { PROVIDE(kernel_start_address = ABSOLUTE(.)); @@ -20,6 +22,11 @@ SECTIONS text_start_address = .; *(.mboot) + . = ALIGN(4096); + apstartup_text_begin = .; + *(.text.apstartup*) + apstartup_text_end = .; + . = ALIGN(4096); *(.text) *(.text.*) text_end_address = .; @@ -29,26 +36,71 @@ SECTIONS ro_data_end_address = .; } - .data ALIGN(4096) : AT(LS_Phys + (LS_Data - LS_Code)) + .tdata ALIGN(4096) : AT(LS_Phys + (LS_tdata - LS_START)) + { + LS_tdata = .; + cls_start = .; + tdata_start = .; + *(.tdata) + *(.tdata.*) + tdata_end = .; + } + + .tbss ALIGN(4096) : AT(LS_Phys + (LS_tbss - LS_START)) + { + LS_tbss = .; + tbss_start = .; + *(.tbss) + *(.tbss.*) + . = ALIGN(8) + 8; /* For C++11 thread_local init-on-first-use flag */ + tbss_end = .; + cls_end = .; + . = ALIGN(8) + 8; + } + + .data ALIGN(4096) : AT(LS_Phys + (LS_Data - LS_START)) { LS_Data = .; data_start_address = .; *(.data) + *(.data.*) . = ALIGN(4096); *(.gdt_stuff) data_end_address = .; } - .bss ALIGN(4096) : AT(LS_Phys + (LS_Bss - LS_Code)) + .init_array : AT(LS_Phys + (__init_array_start - LS_START)) + { + __init_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + __init_array_end = .; + } + + .preinit_array : AT(LS_Phys + (__preinit_array_start - LS_START)) + { + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } + + .fini_array : AT(LS_Phys + (__fini_array_start - LS_START)) + { + __fini_array_start = .; + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + __fini_array_end = .; + } + + .bss ALIGN(4096) : AT(LS_Phys + (LS_Bss - LS_START)) { LS_Bss = .; bss_start_address = .; *(.bss) + *(.bss.*) *(COMMON) /* common symbols, usually placed in .bss */ . = ALIGN(4096); bss_end_address = .; - PROVIDE(kernel_end_address = .); } - } diff --git a/arch/x86/CMakeLists.txt b/arch/x86/CMakeLists.txt index a5e85a070..061dbe046 100644 --- a/arch/x86/CMakeLists.txt +++ b/arch/x86/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories( +target_include_directories(kernel PUBLIC common/include ../${ARCH}/include ../${ARCH}/common/include @@ -7,4 +7,3 @@ include_directories( ) add_subdirectory(common/source) - diff --git a/arch/x86/common/include/8259.h b/arch/x86/common/include/8259.h index d3737b931..74e20cd17 100644 --- a/arch/x86/common/include/8259.h +++ b/arch/x86/common/include/8259.h @@ -1,33 +1,54 @@ #pragma once -#include "types.h" +#include "Device.h" +#include "DeviceDriver.h" +#include "IrqDomain.h" #include "ports.h" -#define PIC_1_CONTROL_PORT 0x20 -#define PIC_2_CONTROL_PORT 0xA0 -#define PIC_1_DATA_PORT 0x21 -#define PIC_2_DATA_PORT 0xA1 -extern volatile size_t outstanding_EOIs; +#include "types.h" + +class PIC8259 : public InterruptController, public IrqDomain, public Device +{ +public: + static bool exists; + static bool enabled; + + PIC8259(); + + static PIC8259& instance(); + + void init(); + + + bool mask(irqnum_t irq, bool mask) override; + bool ack(irqnum_t irq) override; + bool irqStart(irqnum_t irq) override; + bool isMasked(irqnum_t irq) override; /** * sends the initialisation and operational command words to CPU * */ -void initialise8259s(); + static void initialise8259s(); + -extern uint32 cached_mask; + static uint16 cached_mask; /** * enables the interrupt Request with the given number 0 to 15 * */ -void enableIRQ(uint16 number); + static void enableIRQ(uint16 number); /** * disables the interrupt Request with the given number 0 to 15 * */ -void disableIRQ(uint16 number); + static void disableIRQ(uint16 number); + + static void setIrqMask(uint16 mask); + + static bool isIRQEnabled(uint16 number); /** * sends the EOI signal to a Programmable Interrupt Controller (PIC) @@ -35,5 +56,35 @@ void disableIRQ(uint16 number); * interrupt. * */ -void sendEOI(uint16 number); + static void sendEOI(uint16 number); + + static size_t outstanding_EOIs_; + +private: + void setupIrqMappings(); + + enum class IoPorts : uint16_t + { + PIC_1_CONTROL_PORT = 0x20, + PIC_1_DATA_PORT = 0x21, + PIC_2_CONTROL_PORT = 0xA0, + PIC_2_DATA_PORT = 0xA1, + }; + + using PIC_1_CONTROL_PORT = IoPort::StaticIoRegister<(uint16_t)IoPorts::PIC_1_CONTROL_PORT, uint8_t, false, true>; + using PIC_2_CONTROL_PORT = IoPort::StaticIoRegister<(uint16_t)IoPorts::PIC_2_CONTROL_PORT, uint8_t, false, true>; + using PIC_1_DATA_PORT = IoPort::StaticIoRegister<(uint16_t)IoPorts::PIC_1_DATA_PORT, uint8_t, false, true>; + using PIC_2_DATA_PORT = IoPort::StaticIoRegister<(uint16_t)IoPorts::PIC_2_DATA_PORT, uint8_t, false, true>; +}; + +class PIC8259Driver : public BasicDeviceDriver, public Driver +{ +public: + PIC8259Driver(); + ~PIC8259Driver() override = default; + + static PIC8259Driver& instance(); + void doDeviceDetection() override; +private: +}; diff --git a/arch/x86/common/include/ACPI.h b/arch/x86/common/include/ACPI.h new file mode 100644 index 000000000..ea96e64e4 --- /dev/null +++ b/arch/x86/common/include/ACPI.h @@ -0,0 +1,191 @@ +#pragma once + +#include "types.h" + +struct RSDPDescriptor { + char Signature[8]; + uint8 Checksum; + char OEMID[6]; + uint8 Revision; + uint32 RsdtAddress; + + bool checksumValid(); +} __attribute__ ((packed)); + +struct RSDPDescriptor20 { + RSDPDescriptor firstPart; + + uint32 Length; + uint64 XsdtAddress; + uint8 ExtendedChecksum; + uint8 reserved[3]; + + bool checksumValid(); +} __attribute__ ((packed)); + + +struct ACPISDTHeader { + char Signature[4]; + uint32 Length; + uint8 Revision; + uint8 Checksum; + char OEMID[6]; + char OEMTableID[8]; + uint32 OEMRevision; + uint32 CreatorID; + uint32 CreatorRevision; + + bool checksumValid(); +} __attribute__ ((packed)); + +struct RSDT +{ + ACPISDTHeader h; + + size_t numEntries(); + ACPISDTHeader* getEntry(size_t i); +}; + +struct XSDT +{ + ACPISDTHeader h; + + size_t numEntries(); + ACPISDTHeader* getEntry(size_t i); +}; + +struct MADTExtendedHeader +{ + uint32 local_apic_addr; + struct Flags + { + uint32_t PCAT_COMPAT : 1; + uint32_t reserved : 31; + } __attribute__((packed)) flags; +} __attribute__ ((packed)); + +struct ACPI_MADTHeader +{ + ACPISDTHeader std_header; + MADTExtendedHeader ext_header; + + void parse(); +} __attribute__ ((packed)); + +struct MADTEntryDescriptor +{ + uint8 type; + uint8 length; + + enum TYPE + { + PROCESSOR_LOCAL_APIC = 0x0, + IO_APIC = 0x1, + INTERRUPT_SOURCE_OVERRIDE = 0x2, + NMI_SOURCE = 0x3, + LOCAL_APIC_NMI = 0x4, + LOCAL_APIC_ADDR_OVERRIDE = 0x5, + IO_SAPIC = 0x6, + LOCAL_SAPIC = 0x7, + PLATFORM_INTERRUPT_SOURCS = 0x8, + PROCESSOR_LOCAL_X2APIC = 0x9, + LOCAL_X2APIC_NMI = 0xA, + GIC_CPU_INTERFACE = 0xB, + GIC_DISTRIBUTOR = 0xC, + GIC_MSI_FRAME = 0xD, + GIC_REDISTRIBUTOR = 0xE, + GIC_INTERRUPT_TRANSLATION = 0xF, + MULTIPROCESSOR_WAKEUP = 0x10, + }; +} __attribute__ ((packed)); + +struct MADTProcLocalAPIC +{ + uint8 proc_id; + uint8 apic_id; + struct + { + uint32 enabled : 1; + uint32 online_capable : 1; + uint32 reserved : 30; + } __attribute__ ((packed)) flags; +} __attribute__ ((packed)); + +struct MADT_IO_APIC +{ + uint8 id; + uint8 reserved; + uint32 address; + uint32 global_system_interrupt_base; +} __attribute__ ((packed)); + +struct MADTInterruptSourceOverride +{ + uint8 bus_source; + uint8 irq_source; + uint32 g_sys_int; + struct + { + uint16 polarity : 2; // 00: conforms to bus specifications, 01: active high, 10: reserved, 11: active low + uint16 trigger_mode : 2; // 00: conforms to bus specifications, 01: edge triggered, 10: reserved, 11: level triggered + uint16 reserved : 12; + } __attribute__ ((packed)) flags; +} __attribute__ ((packed)); + +enum +{ + ACPI_MADT_POLARITY_CONFORMS = 0, + ACPI_MADT_POLARITY_ACTIVE_HIGH = 1, + ACPI_MADT_POLARITY_RESERVED = 2, + ACPI_MADT_POLARITY_ACTIVE_LOW = 3, +}; + +enum +{ + ACPI_MADT_TRIGGER_CONFORMS = 0, + ACPI_MADT_TRIGGER_EDGE = 1, + ACPI_MADT_TRIGGER_RESERVED = 2, + ACPI_MADT_TRIGGER_LEVEL = 3, +}; + +struct MADTNonMaskableInterruptsSource +{ + uint16 flags; + uint32 g_sys_int; +} __attribute__ ((packed)); + +struct MADTNonMaskableInterrupts +{ + uint8 processor_id; + uint16 flags; + uint8 lint_num; +} __attribute__ ((packed)); + +struct MADTLocalAPICAddressOverride +{ + uint16 reserved; + uint64 local_apic_addr; +} __attribute__ ((packed)); + +struct MADTProcLocalx2APIC +{ + uint16 reserved; + uint32 x2apic_id; + struct + { + uint32 enabled : 1; + uint32 reserved : 31; + } __attribute__ ((packed)) flags; + uint32 processor_uid; +} __attribute__ ((packed)); + + + +void initACPI(); +RSDPDescriptor* checkForRSDP(char* start, char* end); +RSDPDescriptor* locateRSDP(); + +void handleSDT(ACPISDTHeader*); + +extern RSDPDescriptor* RSDP; +extern size_t ACPI_version; diff --git a/arch/x86/common/include/APIC.h b/arch/x86/common/include/APIC.h new file mode 100644 index 000000000..99905dca0 --- /dev/null +++ b/arch/x86/common/include/APIC.h @@ -0,0 +1,537 @@ +#pragma once + +#include "ACPI.h" +#include "CPUID.h" +#include "Device.h" +#include "DeviceDriver.h" +#include "IrqDomain.h" +#include "ports.h" + +#include "types.h" +#include + +#include "EASTL/bit.h" +#include "EASTL/string.h" +#include "EASTL/vector.h" + + +class Apic : public InterruptController, public IrqDomain, public Device +{ +public: + + // ########################################## + // Register value types + + enum class IPIDestination : uint32_t + { + TARGET = 0, + SELF = 1, + ALL = 2, + OTHERS = 3, + }; + + enum class IPIType : uint32_t + { + FIXED = 0, + LOW_PRIORITY = 1, + SMI = 2, + NMI = 4, + INIT = 5, + SIPI = 6, + }; + + enum class IPIDestinationMode : uint32_t + { + PHYSICAL = 0, + LOGICAL = 1, + }; + + enum class IPILevel : uint32_t + { + DEASSERT = 0, + ASSERT = 1, + }; + + enum class IntPinPolarity : uint32_t + { + ACTIVE_HIGH = 0, + ACTIVE_LOW = 1, + }; + + enum class IntTriggerMode : uint32_t + { + EDGE = 0, + LEVEL = 1, + }; + + enum class DeliveryStatus : uint32_t + { + IDLE = 0, + PENDING = 1, + }; + + enum class Mask : uint32_t + { + UNMASKED = 0, + MASKED = 1, + }; + + enum class TimerMode : uint32_t + { + ONESHOT = 0, + PERIODIC = 1, + TSC_DEADLINE = 2, + }; + + struct [[gnu::packed]] InterruptCommandRegisterLow + { + uint32_t vector : 8; // 0-7 + uint32_t delivery_mode : 3; // 8-10 + uint32_t destination_mode : 1; // 11 + uint32_t delivery_status : 1; // 12 + uint32_t reserved : 1; // 13 + uint32_t level : 1; // 14 + uint32_t trigger_mode : 1; // 15 + uint32_t reserved2 : 2; // 16-17 + uint32_t destination_shorthand : 2; // 18-19 + uint32_t reserved3 : 12; // 20-31 + }; + static_assert(sizeof(InterruptCommandRegisterLow) == 4, "Invalid size for InterruptCommandRegisterLow"); + + struct [[gnu::packed]] InterruptCommandRegisterHigh + { + union + { + struct + { + uint32_t reserved : 24; // 0-23 + uint32_t xapic_destination : 8; // 24-31 + }; + uint32_t x2apic_destination; + }; + }; + static_assert(sizeof(InterruptCommandRegisterHigh) == 4, "Invalid size for InterruptCommandRegisterHigh"); + + struct [[gnu::packed]] InterruptCommandRegister + { + InterruptCommandRegisterLow l; + InterruptCommandRegisterHigh h; + }; + static_assert(sizeof(InterruptCommandRegister) == 8, "Invalid size for InterruptCommandRegister"); + + struct [[gnu::packed]] SpuriousInterruptVectorRegister + { + uint32_t vector : 8; + uint32_t enable : 1; + uint32_t focus_checking : 1; + uint32_t reserved : 22; + + void setSpuriousInterruptNumber(uint8_t num); + }; + static_assert(sizeof(SpuriousInterruptVectorRegister) == 4, "Invalid size for SpuriousInterruptVectorRegister"); + + struct [[gnu::packed]] IdRegister + { + union [[gnu::packed]] + { + struct [[gnu::packed]] + { + uint32_t reserved : 24; + uint32_t xapic_id : 8; + }; + uint32_t x2apic_id; + }; + + }; + static_assert(sizeof(IdRegister) == 4, "Invalid size for IdRegister"); + + struct [[gnu::packed]] VersionRegister + { + uint32_t version : 8; + uint32_t reserved1 : 8; + uint32_t max_lvt_entry : 8; + uint32_t eoi_broadcast_suppression_supported : 1; + uint32_t reserved2 : 7; + }; + static_assert(sizeof(VersionRegister) == 4, "Invalid size for VersionRegister"); + + struct [[gnu::packed]] LVT_TimerRegister + { + uint32_t vector : 8; + uint32_t reserved1 : 4; + uint32_t delivery_status : 1; + uint32_t reserved2 : 3; + uint32_t mask : 1; + uint32_t timer_mode : 2; // 0: one-shot, 1: periodic, 3: TSC-Deadline + uint32_t reserved3 : 13; + + void setVector(uint8_t vector); + void setMode(TimerMode mode); + void setMask(bool mask); + }; + static_assert(sizeof(LVT_TimerRegister) == 4, "Invalid size for LVT_TimerRegister"); + + struct [[gnu::packed]] LVT_LINTRegister + { + uint32_t vector : 8; + uint32_t delivery_mode : 3; + uint32_t reserved1 : 1; + uint32_t delivery_status : 1; + uint32_t pin_polarity : 1; + uint32_t remote_irr : 1; + uint32_t trigger_mode : 1; + uint32_t mask : 1; + uint32_t reserved3 : 15; + }; + static_assert(sizeof(LVT_LINTRegister) == 4, "Invalid size for LVT_LINTRegister"); + + struct [[gnu::packed]] LVT_ErrorRegister + { + uint32_t vector : 8; + uint32_t reserved1 : 4; + uint32_t delivery_status : 1; + uint32_t reserved2 : 3; + uint32_t mask : 1; + uint32_t reserved3 : 15; + }; + static_assert(sizeof(LVT_ErrorRegister) == 4, "Invalid size for LVT_ErrorRegister"); + + struct [[gnu::packed]] ErrorStatusRegister + { + uint32_t illegal_register_access : 1; + uint32_t recv_illegal_vector : 1; + uint32_t send_illegal_vector : 1; + uint32_t redirectable_ipi : 1; + uint32_t recv_accept_error : 1; + uint32_t send_accept_error : 1; + uint32_t recv_checksum_error : 1; + uint32_t send_checksum_error : 1; + uint32_t reserved : 24; + }; + static_assert(sizeof(ErrorStatusRegister) == 4, "Invalid size for ErrorStatusRegister"); + + struct [[gnu::packed]] TimerDivideConfigRegister + { + uint32_t divisor_l : 2; + uint32_t reserved1 : 1; + uint32_t divisor_h : 1; + uint32_t reserved2 : 28; + + void setTimerDivisor(uint8_t divisor); + }; + static_assert(sizeof(TimerDivideConfigRegister) == 4, "Invalid size for TimerDivideConfigRegister"); + + struct [[gnu::packed]] PriorityRegister + { + uint32_t priority_sub_class : 4; + uint32_t priority_class : 4; + uint32_t reserved : 24; + }; + static_assert(sizeof(PriorityRegister) == 4, "Invalid size for PriorityRegister"); + + // ########################################## + // Helpers for type safe access + + enum class ApicRegisterOffset : unsigned int + { + ID = 0x20, + VERSION = 0x30, + TASK_PRIORITY = 0x80, + PROCESSOR_PRIORITY = 0xA0, + EOI = 0xB0, + LOGICAL_DESTINATION = 0xD0, + SPURIOUS_INTERRUPT_VECTOR = 0xF0, + ISR_31_0 = 0x100, + ISR_63_32 = 0x110, + ISR_95_64 = 0x120, + ISR_127_96 = 0x130, + ISR_159_128 = 0x140, + ISR_191_160 = 0x150, + ISR_223_192 = 0x160, + ISR_255_224 = 0x170, + TMR_31_0 = 0x180, + TMR_63_32 = 0x190, + TMR_95_64 = 0x1A0, + TMR_127_96 = 0x1B0, + TMR_159_128 = 0x1C0, + TMR_191_160 = 0x1D0, + TMR_223_192 = 0x1E0, + TMR_255_224 = 0x1F0, + IRR_31_0 = 0x200, + IRR_63_32 = 0x210, + IRR_95_64 = 0x220, + IRR_127_96 = 0x230, + IRR_159_128 = 0x240, + IRR_191_160 = 0x250, + IRR_223_192 = 0x260, + IRR_255_224 = 0x270, + ERROR_STATUS = 0x280, + LVT_CMCI = 0x2F0, + INTERRUPT_COMMAND = 0x300, + INTERRUPT_COMMAND_H = 0x310, + LVT_TIMER = 0x320, + LVT_THERMAL_SENSOR = 0x330, + LVT_PERFMON = 0x340, + LVT_LINT0 = 0x350, + LVT_LINT1 = 0x360, + LVT_ERROR = 0x370, + TIMER_INITIAL_COUNT = 0x380, + TIMER_CURRENT_COUNT = 0x390, + TIMER_DIVIDE_CONFIG = 0x3E0, + SELF_IPI = 0x3F0, + }; + + struct Register + { + template + struct ApicRegister + { + using value_type = T; + static constexpr ApicRegisterOffset reg_offset = reg; + static constexpr bool readable = readable_; + static constexpr bool writeable = writeable_; + }; + + using ID = ApicRegister; + using VERSION = ApicRegister; + using TASK_PRIORITY = ApicRegister; + using PROCESSOR_PRIORITY = ApicRegister; + using EOI = ApicRegister; + using LOGICAL_DESTINATION = ApicRegister; + using SPURIOUS_INTERRUPT_VECTOR = ApicRegister; + using ISR_31_0 = ApicRegister; + using ISR_63_32 = ApicRegister; + using ISR_95_64 = ApicRegister; + using ISR_127_96 = ApicRegister; + using ISR_159_128 = ApicRegister; + using ISR_191_160 = ApicRegister; + using ISR_223_192 = ApicRegister; + using ISR_255_224 = ApicRegister; + using TMR_31_0 = ApicRegister; + using TMR_63_32 = ApicRegister; + using TMR_95_64 = ApicRegister; + using TMR_127_96 = ApicRegister; + using TMR_159_128 = ApicRegister; + using TMR_191_160 = ApicRegister; + using TMR_223_192 = ApicRegister; + using TMR_255_224 = ApicRegister; + using IRR_31_0 = ApicRegister; + using IRR_63_32 = ApicRegister; + using IRR_95_64 = ApicRegister; + using IRR_127_96 = ApicRegister; + using IRR_159_128 = ApicRegister; + using IRR_191_160 = ApicRegister; + using IRR_223_192 = ApicRegister; + using IRR_255_224 = ApicRegister; + using ERROR_STATUS = ApicRegister; + using LVT_CMCI = ApicRegister; + using INTERRUPT_COMMAND = ApicRegister; + using LVT_TIMER = ApicRegister; + using LVT_THERMAL_SENSOR = ApicRegister; + using LVT_PERFMON = ApicRegister; + using LVT_LINT0 = ApicRegister; + using LVT_LINT1 = ApicRegister; + using LVT_ERROR = ApicRegister; + using TIMER_INITIAL_COUNT = ApicRegister; + using TIMER_CURRENT_COUNT = ApicRegister; + using TIMER_DIVIDE_CONFIG = ApicRegister; + using SELF_IPI = ApicRegister; + }; + + + + // ########################################## + + static constexpr uint8_t TIMER_DIVISOR = 16; + + Apic(const eastl::string& name = "APIC"); + ~Apic() override = default; + Apic(const Apic &) = delete; + Apic &operator=(const Apic &) = delete; + + template void writeRegister(const typename R::value_type &v) { + static_assert(sizeof(v) == 4 || sizeof(v) == 8); + if constexpr (sizeof(v) == 8) + { + writeRegisterImpl(R::reg_offset, eastl::bit_cast(v)); + } + else + { + writeRegisterImpl(R::reg_offset, eastl::bit_cast(v)); + } + } + + template + typename R::value_type readRegister() + { + static_assert(sizeof(typename R::value_type) == 4 || sizeof(typename R::value_type) == 8); + union + { + uint32_t u32; + uint64_t u64; + } v; + + v.u64 = readRegisterImpl(R::reg_offset); + if constexpr (sizeof(typename R::value_type) == 8) + { + return eastl::bit_cast(v.u64); + } + else + { + return eastl::bit_cast(v.u32); + } + } + + [[nodiscard]] uint32_t apicId() const; + [[nodiscard]] bool isInitialized() const; + [[nodiscard]] virtual bool isX2Apic(); + + virtual void init() = 0; + + static void setIMCRMode(IMCRData mode); + + static void globalEnable(bool = true); + void enable(bool = true); + + bool mask(irqnum_t irq, bool mask) override; + bool ack(irqnum_t irq) override; + bool irqStart(irqnum_t irq) override; + bool isMasked(irqnum_t irq) override; + + + void sendEOI(size_t num); + + bool checkIRR(uint8_t num); + bool checkISR(uint8_t num); + + struct ApicTimer : public InterruptController, public IrqDomain, public Device + { + explicit ApicTimer(Apic& apic); + + bool mask(irqnum_t irq, bool mask) override; + bool ack(irqnum_t irq) override; + + bool isMasked(irqnum_t irq) override; + bool isMasked(); + + private: + Apic* apic_; + bool masked_ = true; + }; + + void initTimer(); + void setTimerPeriod(uint32_t count); + virtual uint32_t readId() = 0; + + void sendIPI(uint8_t vector, IPIDestination dest_type = IPIDestination::ALL, + size_t target = -1, IPIType ipi_type = IPIType::FIXED, + bool wait_for_delivery = false); + void sendIPI(uint8_t vector, const Apic& target, bool wait_for_delivery = false); + + [[nodiscard]] bool usingAPICTimer() const; + void setUsingAPICTimer(bool using_apic_timer); + + void setSpuriousInterruptNumber(uint8_t num); + void setErrorInterruptVector(uint8_t vector); + + static eastl::vector local_apic_list_; + static void addLocalAPICToList(const MADTProcLocalAPIC&); + + size_t outstanding_EOIs_ = 0; + + ApicTimer timer_interrupt_controller; + +protected: + virtual void writeRegisterImpl(ApicRegisterOffset offset, uint64_t v) = 0; + virtual uint64_t readRegisterImpl(ApicRegisterOffset offset) = 0; + + virtual void writeIcr(InterruptCommandRegisterLow icr, uint32_t dest) = 0; + + virtual void waitIpiDelivered() = 0; + + uint32_t id_ = 0; + bool initialized_ = false; + bool use_apic_timer_ = false; +}; + +class XApic : public Apic +{ +public: + XApic() : + Apic(eastl::string("xAPIC ") + eastl::to_string(CPUID::localApicId())) + { + } + + ~XApic() override = default; + XApic(const XApic &) = delete; + XApic &operator=(const XApic &) = delete; + + static void foundLocalAPIC(void *reg_phys_addr, + MADTExtendedHeader::Flags flags); + + static void mapAt(size_t addr); + + void init() override; + + uint32_t readId() override; + + static void setPhysicalAddress(void* paddr) + { + reg_paddr_ = paddr; + } + + static void* physicalAddress() + { + return reg_paddr_; + } + + static void* virtualAddress() + { + return reg_vaddr_; + } + + static void* readMsrPhysAddr(); + + static bool apicSupported(); + +protected: + void writeRegisterImpl(ApicRegisterOffset offset, uint64_t v) override; + uint64_t readRegisterImpl(ApicRegisterOffset offset) override; + + void writeIcr(InterruptCommandRegisterLow icr, uint32_t dest) override; + + void waitIpiDelivered() override; + +private: + + static void* reg_paddr_; + static void* reg_vaddr_; +}; + +class ApicDriver : public BasicDeviceDriver, public Driver +{ +public: + ApicDriver(); + ~ApicDriver() override = default; + + static ApicDriver& instance(); + + void doDeviceDetection() override; + void cpuLocalInit() override; + +private: +}; + +class ApicTimerDriver : public BasicDeviceDriver, public Driver +{ +public: + ApicTimerDriver(); + ~ApicTimerDriver() override = default; + + static ApicTimerDriver& instance(); + + void doDeviceDetection() override; + void cpuLocalInit() override; + +private: +}; diff --git a/arch/x86/common/include/ATACommands.h b/arch/x86/common/include/ATACommands.h new file mode 100644 index 000000000..239d1a7b9 --- /dev/null +++ b/arch/x86/common/include/ATACommands.h @@ -0,0 +1,71 @@ +#pragma once + +namespace ATACommand +{ + namespace Other + { + enum + { + NOP = 0x00, + SET_MULTIPLE_MODE = 0xC6, + FLUSH_CACHE = 0xE7, + SET_FEATURES = 0xEF, + }; + }; + + namespace PIO + { + enum + { + READ_SECTORS = 0x20, + WRITE_SECTORS = 0x30, + READ_MULTIPLE = 0xC4, + IDENTIFY_DEVICE = 0xEC, + }; + }; + + namespace DMA + { + enum + { + READ = 0xC8, + WRITE = 0xCA, + }; + }; + + // 48-bit commands + namespace EXT + { + namespace Other + { + enum + { + DATA_SET_MANAGEMENT = 0x06, + CONFIGURE_STREAM = 0x51, + SET_DATE_TIME = 0x77, + }; + }; + + namespace PIO + { + enum + { + READ_SECTORS = 0x24, + READ_MULTIPLE = 0x29, + READ_STREAM = 0x2B, + READ_LOG = 0x2F, + }; + }; + + namespace DMA + { + enum + { + READ = 0x25, + WRITE = 0x35, + READ_LOG = 0x47, + READ_STREAM = 0x2A, + }; + }; + }; +}; // namespace ATACommand diff --git a/arch/x86/common/include/ATADriver.h b/arch/x86/common/include/ATADriver.h index 5a2ca91a9..f010e2d88 100644 --- a/arch/x86/common/include/ATADriver.h +++ b/arch/x86/common/include/ATADriver.h @@ -1,18 +1,35 @@ #pragma once #include "BDDriver.h" +#include "BDManager.h" +#include "BDVirtualDevice.h" +#include "Device.h" +#include "DeviceBus.h" +#include "DeviceDriver.h" +#include "IDEDriver.h" +#include "IrqDomain.h" #include "Mutex.h" +#include "NonBlockingQueue.h" +#include "ports.h" + +#include "EASTL/span.h" class BDRequest; +class IDEControllerChannel; -class ATADriver : public BDDriver +class ATADrive : public BDDriver, public Device { - public: - - typedef enum BD_ATA_MODE_ +public: + enum class BD_ATA_MODE { - BD_PIO_NO_IRQ, BD_PIO, BD_DMA, BD_UDMA - } BD_ATA_MODES; + BD_PIO_NO_IRQ, + BD_PIO, + BD_DMA, + BD_UDMA + }; + + ATADrive(IDEControllerChannel& controller, uint16 drive_num); + ~ATADrive() override = default; /** * adds the given request to a list and checkes the type of the @@ -20,20 +37,14 @@ class ATADriver : public BDDriver * or the function returns otherwise. * */ - uint32 addRequest(BDRequest* br); - void nextRequest(BDRequest* br); - ATADriver(uint16 baseport, uint16 getdrive, uint16 irqnum); - virtual ~ATADriver() - { - } - ; + uint32 addRequest(BDRequest* br) override; /** * sets the current mode to BD_PIO_NO_IRQ while the readSector * function is being executed * */ - int32 rawReadSector(uint32, uint32, void *); + int32 rawReadSector(uint32, uint32, void*); /** * @param 1 sector where it should be started to read @@ -41,7 +52,7 @@ class ATADriver : public BDDriver * @param 3 buffer where to save all that was read * */ - int32 readSector(uint32, uint32, void *); + int32 readSector(uint32, uint32, void*) override; /** * @param 1 sector where it should be started to write @@ -49,19 +60,13 @@ class ATADriver : public BDDriver * @param 3 buffer, which content should be written to the sectors * */ - int32 writeSector(uint32, uint32, void *); + int32 writeSector(uint32, uint32, void*) override; - uint32 getNumSectors() - { - return numsec; - } - ; - uint32 getSectorSize() - { - return 512; - } - ; - void serviceIRQ(); + uint32 getNumSectors() override { return numsec; } + + uint32 getSectorSize() override { return sector_word_size * WORD_SIZE; } + + void serviceIRQ() override; /** * tests if there is an Interrupt Request waiting @@ -69,31 +74,57 @@ class ATADriver : public BDDriver */ void testIRQ(); - /** - * tests if the Controller is available - * false if it is not or the time is elapsed - * - */ - bool waitForController(bool resetIfFailed); + void printIdentifyInfo(eastl::span id) const; uint32 HPC, SPT; // HEADS PER CYLINDER and SECTORS PER TRACK - private: - +protected: int32 selectSector(uint32 start_sector, uint32 num_sectors); + void pioReadData(eastl::span buffer); + void pioWriteData(eastl::span buffer); + +private: + IDEControllerChannel& controller; + uint8_t drive_num; + uint32 numsec; + // 256 * uint16_t = 512 bytes + uint32 sector_word_size = 256; + static constexpr size_t WORD_SIZE = sizeof(uint16_t); - uint16 port; - uint16 drive; + bool lba; + bool lba_48bit; - uint32 jiffies; + size_t jiffies = 0; - BD_ATA_MODES mode; // mode see enum BD_ATA_MODES + BD_ATA_MODE mode; - BDRequest *request_list_; - BDRequest *request_list_tail_; + IrqDomain irq_domain; + + NonBlockingQueue request_list_; Mutex lock_; }; +struct IDEDeviceDescription; + +class PATADeviceDriver : public BasicDeviceDriver, + public Driver, + public IDEControllerChannel::bus_device_driver_type +{ +public: + PATADeviceDriver(); + ~PATADeviceDriver() override = default; + + static PATADeviceDriver& instance(); + + // Check if driver is compatible with device discovered during IDE bus enumeration + // If yes, create an actual device based on the description + bool probe(const IDEDeviceDescription&) override; + + static constexpr IDEDeviceDescription::Signature PATA_DRIVE_SIGNATURE{0x01, 0x01, + 0x00, 0x00}; + +private: +}; diff --git a/arch/x86/common/include/CPUID.h b/arch/x86/common/include/CPUID.h new file mode 100644 index 000000000..ef463c739 --- /dev/null +++ b/arch/x86/common/include/CPUID.h @@ -0,0 +1,230 @@ +#pragma once + +#include "ArchCpuLocalStorage.h" + +#include + +#include "EASTL/bitset.h" + +namespace CPUID +{ + void cpuid(uint32_t selector, uint32_t subselector, uint32_t& eax, uint32_t& ebx, uint32_t& ecx, uint32_t& edx); + + uint32_t highestSupportedLeaf(); + uint32_t highestSupportedExtendedLeaf(); + uint32_t localApicId(); + uint32_t localX2ApicId(); +}; + +class CpuFeatures +{ +public: + enum X86Feature + { + // cpuid eax=1 edx + FPU, + VME, + DEBUGGING_EXTENSIONS, + PSE, + TSC, + MSR, + PAE, + MACHINE_CHECK_EXCEPTION, + CMPXCHG8, + APIC, + SYSENTER_SYSEXIT, + MTRR, + PGE, + MCA, + CMOV, + PAT, + PSE36, + PSN, + CLFLUSH, + DEBUG_STORE, + ACPI_THERMAL_MSR, + MMX, + FXSAVE_FXRESTOR, + SSE, + SSE2, + CACHE_SELF_SNOOP, + HYPERTHREADING, + THERMAL_MONITOR, + IA64, + PBE, + + // cpuid eax=1 ecx + SSE3, + PCMULQDQ, + DTES64, + MONITOR, + DS_CPL, + VMX, + SMX, + ENHANCED_SPEED_STEP, + THERMAL_MONITOR_2, + SSSE3, + L1_CONTEXT_ID, + SILICON_DEBUG_INTERFACE, + FMA3, + CMPXCHG16B, + TASK_PRIORITY_MESSAGE_DISABLE, + PERFMON_DEBUG_CAPABILITY, + PCID, + DMA_DIRECT_CACHE_ACCESS, + SSE4_1, + SSE4_2, + X2APIC, + MOVBE, + POPCNT, + TSC_DEADLINE, + AES, + XSAVE, + OS_XSAVE, + AVX, + F16C, + RDRAND, + HYPERVISOR, + + // cpuid eax=7 ebx + FSGSBASE, + TSC_ADJUST_MSR, + SGX, + BMI1, + TSX_HLE, + AVX2, + SMEP, + BMI2, + ENHANCED_REP_MOVSB, + INVPCID, + TSX_RTM, + RESOURCE_DIRECTOR_MONITORING, + FPU_CS_DS_DEPRECATED, + MPX, + RESOURCE_DIRECTOR_ALLOCATION, + AVX512_F, + AVX512_DQ, + RDSEED, + ADX, + SMAP, + AVX512_IFMA, + CLFLUSHOPT, + CLWB, + PROCESSOR_TRACE, + AVX512_PF, + AVX512_ER, + AVX512_CD, + SHA, + AVX512_BW, + AVX512_VL, + + // cpuid eax=7 ecx + PREFETCHW1, + AVX512_VBMI, + UMIP, + PKU, + OS_PKU, + WAITPKG, + AVX512_VBMI2, + CET_SS, + GFNI, + VAES, + CLMUL, + AVX512_VNNI, + AVX512_BITALG, + TME, + AVX512_VPOPCNTDQ, + FIVE_LEVEL_PAGING, + RDPID, + PKS, + + // cpuid eax=7 edx + AVX512_4VNNIW, + AVX512_4FMAPS, + FSRM, + UINTR, + AVX512_VP2INTERSECT, + SPECIAL_REGISTER_BUFFER_DATA_SAMPLING_MITIGATIONS, + VERW_CPU_BUFFER_CLEAR, + TSX_ALWAYS_ABORT, + TSX_FORCE_ABORT_MSR, + SERIALIZE, + HYBRID, + TSXLDTRK, + PCONFIG, + ARCH_LAST_BRANCH_RECORDS, + CET_IBT, + AMX_BF16, + AVX512_FP16, + AMX_TILE, + AMX_INT8, + IBRS_IBPB, + STIBP, + FLUSH_CMD_MSR, + ARCH_CAPABILITIES_MSR, + CORE_CAPABILITIES_MSR, + SSBD, + + // AMD cpuid eax=80000001h edx + SYSCALL_SYSRET, + MULTIPROCESSOR_CAPABLE, + NX, + MMX_EXT, + FXSAVE_FXRESTOR_OPT, + PDPE1GB, + RDTSCP, + LONG_MODE, + THREE_D_NOW_EXT, + THREE_D_NOW, + + // AMD cpuid eax=80000001h ecx + LAHF_SAHF_LONG_MODE, + SVM, + EXTAPIC, + CR8_LEGACY, + LZCNT, + SSE4A, + SSE_MISALIGNED, + PREFETCH_PREFETCHW, + INSTRUCTION_BASED_SAMPLING, + XOP, + SKINIT_STGI, + WATCHDOG_TIMER, + LIGHT_WEIGHT_PROFILING, + FMA4, + TRANSLATION_CACHE_EXTENSION, + NODE_ID_MSR, + TBM, + TOPOEXT, + PERFCTR_CORE, + PERFCTR_NB, + DBX, + PERFTSC, + PCX_L2I, + MONITORX, + + // AMD cpuid eax=8000001Fh eax + SME, + SEV, + PAGE_FLUSH_MSR, + SEV_ES, + SEV_SNP, + VM_PERM_LVL, + VTE, + + FEATURE_COUNT // dummy value to get enum size for bitset + }; + + CpuFeatures(); + + void initCpuFeatures(); + [[nodiscard]] bool cpuHasFeature(X86Feature feature) const; + [[nodiscard]] bool cpuFeatureIsForceDisabled(X86Feature feature) const; + + void setFeatureForceDisabled(X86Feature feature); +private: + eastl::bitset features_; + eastl::bitset force_disabled_features_; +}; + +extern cpu_local CpuFeatures cpu_features; diff --git a/arch/x86/common/include/ErrorHandlers.h b/arch/x86/common/include/ErrorHandlers.h index efe8b69c3..699dcaa9b 100644 --- a/arch/x86/common/include/ErrorHandlers.h +++ b/arch/x86/common/include/ErrorHandlers.h @@ -1,16 +1,10 @@ -#ifdef _DUMMY_HANDLERS_H_ -#error This file may not be included more than once -#else -#define _ERROR_HANDLERS_H_ +#pragma once - - - -const char* errors[32] = { +constexpr const char* errors[32] = { "#DE: Divide by Zero", - "", - "", - "", + "#DB: Debug", + "NMI: Non-maskable Interrupt", + "#BP: Breakpoint", "#OF: Overflow (INTO Instruction)", "#BR: Bound Range Exceeded", "#OP: Invalid OP Code", @@ -21,66 +15,22 @@ const char* errors[32] = { "#NP: Segment Not Present (WTF ?)", "#SS: Stack Segment Fault", "#GF: General Protection Fault", - "", - "", + "#PF: Page Fault", + "reserved", "#MF: Floting Point Error", "#AC: Alignment Error (Unaligned Memory Reference)", "#MC: Machine Check Error", - "#XF: SIMD Floting Point Error" + "#XF: SIMD Floting Point Error", + "#VE: Virtualization Exception", + "#CP: Control Protection Exception", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "#HV: Hypervisor Injection Exception", + "#VC: VMM Communication Exception", + "#SX: Security Exception", + "reserved" }; - -#define ERROR_HANDLER(x) extern "C" void arch_errorHandler_##x(); - -ERROR_HANDLER(0) -ERROR_HANDLER(4) -ERROR_HANDLER(5) -ERROR_HANDLER(6) -ERROR_HANDLER(7) -ERROR_HANDLER(8) -ERROR_HANDLER(9) -ERROR_HANDLER(10) -ERROR_HANDLER(11) -ERROR_HANDLER(12) -ERROR_HANDLER(13) -ERROR_HANDLER(16) -ERROR_HANDLER(17) -ERROR_HANDLER(18) -ERROR_HANDLER(19) - -extern ArchThreadRegisters *currentThreadRegisters; -extern Thread *currentThread; - -#define ERRORHANDLER(X) {X, &arch_errorHandler_##X}, -#define IRQHANDLER(X) {X + 32, &arch_irqHandler_##X}, -InterruptHandlers InterruptUtils::handlers[] = { - ERRORHANDLER(0) - ERRORHANDLER(4) - ERRORHANDLER(5) - ERRORHANDLER(6) - ERRORHANDLER(7) - ERRORHANDLER(8) - ERRORHANDLER(9) - ERRORHANDLER(10) - ERRORHANDLER(11) - ERRORHANDLER(12) - ERRORHANDLER(13) - {14, &arch_pageFaultHandler}, - ERRORHANDLER(16) - ERRORHANDLER(17) - ERRORHANDLER(18) - ERRORHANDLER(19) - IRQHANDLER(0) - IRQHANDLER(1) - IRQHANDLER(3) - IRQHANDLER(4) - IRQHANDLER(6) - IRQHANDLER(9) - IRQHANDLER(11) - IRQHANDLER(14) - IRQHANDLER(15) - {65, &arch_irqHandler_65}, - {128, &arch_syscallHandler}, - {0,0} -}; - -#endif diff --git a/arch/x86/common/include/IDEDriver.h b/arch/x86/common/include/IDEDriver.h new file mode 100644 index 000000000..eaa1ec754 --- /dev/null +++ b/arch/x86/common/include/IDEDriver.h @@ -0,0 +1,290 @@ +#pragma once + +#include "DeviceBus.h" +#include "DeviceDriver.h" +#include "IrqDomain.h" +#include "ports.h" +#include "source_location.h" + +#include "ArchInterrupts.h" + +#include + +#include "EASTL/array.h" +#include "EASTL/tuple.h" +#include "EASTL/vector.h" + +class BDDriver; +class IDEControllerChannel; + +// Description of an IDE device discovered during IDE bus device enumeration +// Used to find compatible IDE bus device drivers (e.g. PATADeviceDriver for PATA devices) +// Compatible drivers then create appropriate devices based on this description +struct IDEDeviceDescription +{ + IDEControllerChannel* controller; + uint8_t device_num; + + struct Signature + { + uint8_t r_count; + uint8_t r_lba_low; + uint8_t r_lba_mid; + uint8_t r_lba_high; + + friend bool operator==(const Signature& lhs, const Signature& rhs) = default; + } signature; +}; + +class IDEControllerChannel : public DeviceBus, + public IrqDomain +{ +public: + + struct IoRegister + { + struct [[gnu::packed]] DriveHead + { + union [[gnu::packed]] + { + struct [[gnu::packed]] + { + uint8_t chs_head_0_3 : 4; + uint8_t drive_num : 1; + uint8_t always_set_0 : 1; + uint8_t use_lba : 1; + uint8_t always_set_1 : 1; + }; + + struct [[gnu::packed]] + { + // alternative for chs_head_0_3 (union inside bitfield struct always + // uses at least one byte, so we have to do it the other way around + // instead) + uint8_t lba_24_27 : 4; + uint8_t _ : 4; // see above struct + }; + + uint8_t u8; + }; + }; + + static_assert(sizeof(DriveHead) == 1); + + struct [[gnu::packed]] Status + { + union [[gnu::packed]] + { + struct [[gnu::packed]] + { + uint8_t error : 1; + // Always zero + uint8_t index : 1; + // Always zero + uint8_t corrected_data : 1; + // Drive has PIO data ready to transfer or is ready to accept PIO data + uint8_t data_ready : 1; + uint8_t srv : 1; + uint8_t drive_fault_error : 1; + // 0 = error or drive spun down + uint8_t ready : 1; + uint8_t busy : 1; + }; + + uint8_t u8; + }; + }; + + static_assert(sizeof(Status) == 1); + + struct [[gnu::packed]] Error + { + union [[gnu::packed]] + { + struct [[gnu::packed]] + { + // Illegal length indicator, command completion time out, cfa error bit + uint8_t illegal_length : 1; + uint8_t end_of_media : 1; + uint8_t command_aborted : 1; + uint8_t media_change_request : 1; + // invalid address + uint8_t id_not_found : 1; + uint8_t media_changed : 1; + uint8_t uncorrectable_data_error : 1; + // CRC error + uint8_t bad_block_detected : 1; + }; + + uint8_t u8; + }; + }; + + static_assert(sizeof(Error) == 1); + + using DATA_t = IoPort::IoPortDescription<0, uint16_t, true, true>; + using ERROR_t = IoPort::IoPortDescription<1, Error, true, false>; + using FEATURES_t = IoPort::IoPortDescription<1, uint8_t, false, true>; + using SECTOR_COUNT_t = IoPort::IoPortDescription<2, uint8_t, true, true>; + using SECTOR_NUMBER_t = IoPort::IoPortDescription<3, uint8_t, true, true>; + using LBA_LOW_t = SECTOR_NUMBER_t; + using CYLINDER_LOW_t = IoPort::IoPortDescription<4, uint8_t, true, true>; + using LBA_MID_t = CYLINDER_LOW_t; + using CYLINDER_HIGH_t = IoPort::IoPortDescription<5, uint8_t, true, true>; + using LBA_HIGH_t = CYLINDER_HIGH_t; + using DRIVE_HEAD_t = IoPort::IoPortDescription<6, DriveHead, true, true>; + using STATUS_t = IoPort::IoPortDescription<7, Status, true, false>; + using COMMAND_t = IoPort::IoPortDescription<7, uint8_t, false, true>; + + static constexpr DATA_t DATA{}; + static constexpr ERROR_t ERROR{}; + static constexpr FEATURES_t FEATURES{}; + static constexpr SECTOR_COUNT_t SECTOR_COUNT{}; + static constexpr SECTOR_NUMBER_t SECTOR_NUMBER{}; + static constexpr LBA_LOW_t LBA_LOW{}; + static constexpr CYLINDER_LOW_t CYLINDER_LOW{}; + static constexpr LBA_MID_t LBA_MID{}; + static constexpr CYLINDER_HIGH_t CYLINDER_HIGH{}; + static constexpr LBA_HIGH_t LBA_HIGH{}; + static constexpr DRIVE_HEAD_t DRIVE_HEAD{}; + static constexpr STATUS_t STATUS{}; + static constexpr COMMAND_t COMMAND{}; + }; + + struct ControlRegister + { + struct [[gnu::packed]] DeviceControl + { + union [[gnu::packed]] + { + struct [[gnu::packed]] + { + uint8_t always_zero : 1; + // 0 = interrupts enabled, 1 = interrupts disabled + uint8_t interrupt_disable : 1; + // Set to 1 for 5 us, then clear to 0 to reset all ATA drives on the + // bus + uint8_t reset : 1; + uint8_t reserved : 4; + uint8_t lba48_high_order_byte_readback : 1; + }; + + uint8_t u8; + }; + }; + + static_assert(sizeof(DeviceControl) == 1); + + struct [[gnu::packed]] DriveAddress + { + union [[gnu::packed]] + { + struct [[gnu::packed]] + { + // 0 when drive 0 is selected + uint8_t drive0_select : 1; + // 0 when drive 1 is selected + uint8_t drive1_select : 1; + // ones-complement of currently selected head + uint8_t head_select : 4; + // 0 when write is in progress + uint8_t write_gate : 1; + uint8_t reserved : 1; + }; + + uint8_t u8; + }; + }; + + static_assert(sizeof(DriveAddress) == 1); + + using ALT_STATUS_t = IoPort::IoPortDescription<0, IoRegister::Status, true, false>; + using DEVICE_CONTROL_t = IoPort::IoPortDescription<0, DeviceControl, false, true>; + using DRIVE_ADDRESS_t = IoPort::IoPortDescription<1, DriveAddress, true, false>; + + static constexpr ALT_STATUS_t ALT_STATUS{}; + static constexpr DEVICE_CONTROL_t DEVICE_CONTROL{}; + static constexpr DRIVE_ADDRESS_t DRIVE_ADDRESS{}; + }; + +public: + IDEControllerChannel(const eastl::string& name, uint16_t io_reg_base, uint16_t control_reg_base, uint16_t isa_irqnum); + + ~IDEControllerChannel() override = default; + + static bool detectChannel(uint16_t io_reg_base, uint16_t control_reg_base); + + void doDeviceDetection(); + + void detectDrive(uint8_t drive_num); + + void selectDrive(uint8_t drive, bool force = false); + + uint8_t selectedDrive(); + + void sendCommand(uint8_t); + + // Resets BOTH drives on the channel + void reset(); + + [[nodiscard]] bool isDataReady(); + + bool waitNotBusy(source_location loc = source_location::current()); + bool waitDataReady(source_location loc = source_location::current()); + bool waitDriveReady(source_location loc = source_location::current()); + + [[nodiscard]] constexpr uint8_t isaIrqNumber() const { return isa_irqnum; } + + + IoPort::IoRegisterSet io_regs; + IoPort::IoRegisterSet control_regs; + uint8_t isa_irqnum; + size_t controller_id; + static size_t num_ide_controllers; + + uint8_t selected_drive = 0; +}; + +class IDEController : public Device +{ +public: + IDEController(const eastl::string& name = "IDE Controller"); + ~IDEController() override = default; + + void doDeviceDetection(); + + struct DefaultPorts + { + static constexpr uint16_t PRIMARY_IO = 0x1F0; + static constexpr uint16_t PRIMARY_CONTROL = 0x3F6; + static constexpr uint8_t PRIMARY_ISA_IRQ = 14; + + static constexpr uint16_t SECONDARY_IO = 0x170; + static constexpr uint16_t SECONDARY_CONTROL = 0x376; + static constexpr uint8_t SECONDARY_ISA_IRQ = 15; + + static constexpr uint16_t TERNARY_IO = 0x1E8; + static constexpr uint16_t TERNARY_CONTROL = 0x3EE; + static constexpr uint8_t TERNARY_ISA_IRQ = 11; + + static constexpr uint16_t QUATERNARY_IO = 0x168; + static constexpr uint16_t QUATERNARY_CONTROL = 0x36E; + static constexpr uint8_t QUATERNARY_ISA_IRQ = 10; + }; + +private: + eastl::vector channels; +}; + +class IDEControllerDriver : public BasicDeviceDriver, + public Driver +{ +public: + IDEControllerDriver(); + IDEControllerDriver(const IDEControllerDriver&) = delete; + ~IDEControllerDriver() override = default; + + static IDEControllerDriver& instance(); + + void doDeviceDetection() override; +}; diff --git a/arch/x86/common/include/InterruptUtils.h b/arch/x86/common/include/InterruptUtils.h deleted file mode 100644 index 794f39a5f..000000000 --- a/arch/x86/common/include/InterruptUtils.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "types.h" - -typedef struct { - uint32 number; // handler number - void (*offset)(); // pointer to handler function -} __attribute__((__packed__)) InterruptHandlers; - - -typedef struct { - uint16 limit; - size_t base; -} __attribute__((__packed__)) IDTR ; - - -class InterruptUtils -{ -public: - static void initialise(); - - static void lidt(IDTR *idtr); - - static void countPageFault(uint64 address); - -private: - static InterruptHandlers handlers[]; - static uint64 pf_address; - static uint64 pf_address_counter; -}; - diff --git a/arch/x86/common/include/IoApic.h b/arch/x86/common/include/IoApic.h new file mode 100644 index 000000000..3d10ca01a --- /dev/null +++ b/arch/x86/common/include/IoApic.h @@ -0,0 +1,217 @@ +#pragma once + +#include "ACPI.h" +#include "IrqDomain.h" + +#include "ArchMulticore.h" + +#include + +#include "EASTL/bit.h" +#include "EASTL/vector.h" + +#include "debug.h" + +class IoApic : public InterruptController, public IrqDomain, public Device +{ +public: + + enum IOAPICRegisterOffsets + { + IOAPICID = 0x00, + IOAPICVER = 0x01, + IOAPICARB = 0x02, + }; + + struct IOAPIC_r_ID + { + union + { + volatile uint32_t word; + struct + { + volatile uint32_t reserved1 : 24; // 0-23 + volatile uint32_t io_apic_id : 4; // 24-27 + volatile uint32_t reserved2 : 4; // 28-31 + } __attribute__((packed)); + }; + } __attribute__((packed)); + + struct IOAPIC_r_VER + { + union + { + volatile uint32_t word; + struct + { + volatile uint32_t version : 8; // 0- 7 + volatile uint32_t reserved1 : 8; // 8-15 + volatile uint32_t max_redir : 8; // 16-23 + volatile uint32_t reserved2 : 8; // 24-31 + } __attribute__((packed)); + }; + } __attribute__((packed)); + + struct IOAPIC_r_ARB + { + union + { + volatile uint32_t word; + struct + { + volatile uint32_t reserved1 : 24; // 0-23 + volatile uint32_t arbitration_priority : 4; // 24-27 + volatile uint32_t reserved2 : 4; // 27-31 + } __attribute__((packed)); + }; + } __attribute__((packed)); + + struct IOAPIC_redir_entry + { + union + { + volatile uint32_t word_l; + struct + { + volatile uint32_t interrupt_vector : 8; // Allowed values: 0x10-0xFE + volatile uint32_t delivery_mode : 3; + volatile uint32_t destination_mode : 1; + volatile uint32_t pending_busy : 1; + volatile uint32_t polarity : 1; // 0: active high, 1: active low + volatile uint32_t lvl_trig_recvd : 1; + volatile uint32_t trigger_mode : 1; // 0: edge, 1: level + volatile uint32_t mask : 1; + volatile uint32_t reserved1 : 15; + }; + }; + + union + { + volatile uint32_t word_h; + struct + { + volatile uint32_t reserved2 : 24; + volatile uint32_t destination : 8; + }; + }; + } __attribute__((packed)); + + + struct IOAPIC_MMIORegs + { + volatile uint32_t io_reg_sel; + char padding[12]; + volatile uint32_t io_win; + } __attribute__((packed)); + + struct Register + { + template + struct IoApicRegister + { + using value_type = T; + static constexpr IOAPICRegisterOffsets reg_offset = reg; + static constexpr bool readable = readable_; + static constexpr bool writeable = writeable_; + }; + + using ID = IoApicRegister; + using VERSION = IoApicRegister; + using ARB = IoApicRegister; + }; + + + + bool mask(irqnum_t irq, bool mask) override; + bool irqStart(irqnum_t irq) override; + bool ack(irqnum_t irq) override; + bool isMasked(irqnum_t irq) override; + + + static void addIOAPIC(uint32_t id, IOAPIC_MMIORegs* regs, uint32_t g_sys_int_base); + static void addIRQSourceOverride(const MADTInterruptSourceOverride&); + static eastl::tuple findSourceOverrideForGSysInt(uint8_t g_sys_int); + static eastl::tuple findSourceOverrideForIrq(uint8_t irq); + uint8_t gSysIntToVector(uint8_t g_sys_int); + static void initAll(); + + static uint32_t findGSysIntForIRQ(uint8_t irq); + + static void setIRQMask(uint32_t irq_num, bool value); + static void setGSysIntMask(uint32_t g_sys_int, bool value); + + static bool getIRQMask(uint32_t irq_num); + static bool getGSysIntMask(uint32_t g_sys_int); + + static IoApic* findIOAPICforIRQ(uint8_t irq); + static IoApic* findIOAPICforGlobalInterrupt(uint32_t g_int); + + + static eastl::vector& IoApicList(); + static eastl::vector& IrqSourceOverrideList(); + + + IoApic(uint32_t id, IOAPIC_MMIORegs* regs, uint32_t g_sys_int_base); + +private: + void init(); + void initRedirections(); + void setupIsaIrqMappings(); + + void mapAt(size_t addr); + + uint32_t read(uint8_t offset); + void write(uint8_t offset, uint32_t value); + + template + typename R::value_type read() + { + return eastl::bit_cast(read(R::reg_offset)); + } + + template + typename R::value_type write(const typename R::value_type& v) + { + return write(R::reg_offset, eastl::bit_cast(v)); + } + + uint8_t redirEntryOffset(uint32_t entry_no); + + IOAPIC_redir_entry readRedirEntry(uint32_t entry_no); + void writeRedirEntry(uint32_t entry_no, const IOAPIC_redir_entry& value); + + uint32_t getGlobalInterruptBase(); + uint32_t getMaxRedirEntry(); + + + IOAPIC_MMIORegs* reg_paddr_; + IOAPIC_MMIORegs* reg_vaddr_; + + uint32_t id_; + uint32_t max_redir_; + uint32_t g_sys_int_base_; + + eastl::vector redir_entry_cache_; +}; + + +class IoApicDriver : public BasicDeviceDriver, public Driver +{ +public: + IoApicDriver() : + BasicDeviceDriver("I/O Apic driver") + { + } + + ~IoApicDriver() override = default; + + static IoApicDriver& instance() + { + static IoApicDriver i; + return i; + } + + void doDeviceDetection() override; + +private: +}; diff --git a/arch/x86/common/include/MSR.h b/arch/x86/common/include/MSR.h new file mode 100644 index 000000000..aac93ea97 --- /dev/null +++ b/arch/x86/common/include/MSR.h @@ -0,0 +1,117 @@ +#pragma once + +#include "types.h" +#include + +#include "EASTL/bit.h" +#include "EASTL/type_traits.h" + +namespace MSR +{ + +#define MSR_FS_BASE 0xC0000100 +#define MSR_GS_BASE 0xC0000101 +#define MSR_KERNEL_GS_BASE 0xC0000102 +#define MSR_IA32_APIC_BASE 0x1B +#define MSR_IA32_PAT 0x277 + + + void getMSR(uint32_t msr, uint32_t* lo, uint32_t* hi); + void setMSR(uint32_t msr, uint32_t lo, uint32_t hi); + + template + class MSR + { + public: + static constexpr uint32_t MSR_ID = msr; + using value_type = RegT; + + static_assert(sizeof(value_type) == sizeof(uint32_t) || sizeof(value_type) == sizeof(uint64_t)); + + template + static value_type read() requires(readable) + { + static_assert(sizeof(value_type) == 4 || sizeof(value_type) == 8); + + union + { + uint32_t u32; + uint64_t u64; + } v; + + if constexpr (sizeof(value_type) == 8) + { + getMSR(msr, (uint32_t*)&v.u32, ((uint32_t*)&v.u32) + 1); + return eastl::bit_cast(v.u64); + } + else + { + uint32_t ignored; + getMSR(msr, (uint32_t*)&v.u32, &ignored); + return eastl::bit_cast(v.u32); + } + } + + template + static void write(value_type val) requires(writeable) + { + if constexpr (sizeof(value_type) == 8) + { + setMSR(msr, *(uint32_t*)&val, *(((uint32_t*)&val) + 1)); + } + else + { + setMSR(msr, *(uint32_t*)&val, 0); + } + } + private: + }; + + struct IA32_APIC_BASE_t + { + union + { + struct + { + uint64_t reserved1 : 8; + uint64_t bsp : 1; + uint64_t reserved2 : 1; + uint64_t x2apic_enable : 1; + uint64_t enable : 1; + uint64_t apic_base : 24; + uint64_t reserved3 : 28; + }; + struct + { + uint32_t value32_l; + uint32_t value32_h; + }; + uint64_t value64; + }; + }; + static_assert(sizeof(IA32_APIC_BASE_t) == 8); + + enum class PAT_TYPE : uint8 + { + UNCACHEABLE = 0, + WRITE_COMBINING = 1, + WRITE_THROUGH = 4, + WRITE_PROTECTED = 5, + WRITE_BACK = 6, + UNCACHED_MINUS = 7, + }; + + struct IA32_PAT_t + { + union + { + PAT_TYPE pat[8]; + uint64_t u64; + }; + }; + + static_assert(sizeof(IA32_PAT_t) == 8); + + using IA32_APIC_BASE = MSR; + using IA32_PAT = MSR; +}; diff --git a/arch/x86/common/include/ProgrammableIntervalTimer.h b/arch/x86/common/include/ProgrammableIntervalTimer.h new file mode 100644 index 000000000..74d79bcfd --- /dev/null +++ b/arch/x86/common/include/ProgrammableIntervalTimer.h @@ -0,0 +1,94 @@ +#pragma once + +#include "Device.h" +#include "DeviceDriver.h" +#include "IrqDomain.h" +#include "ports.h" + +#include "types.h" + +class PIT : public Device, public IrqDomain +{ +public: + PIT(); + ~PIT() override = default; + + PIT(const PIT&) = delete; + PIT& operator=(const PIT&) = delete; + + enum PITIoPort + { + PIT_PORT_CH_0_DATA = 0x40, + PIT_PORT_CH_1_DATA = 0x41, + PIT_PORT_CH_2_DATA = 0x42, + PIT_PORT_COMMAND = 0x43, + }; + + enum class OperatingMode : uint8_t + { + ONESHOT = 0, + HW_RETRIGGERABLE_ONESHOT = 1, + RATE_GENERATOR = 2, + SQUARE_WAVE = 3, + SOFTWARE_STROBE = 4, + HARDWARE_STROBE = 5, + }; + + enum class AccessMode : uint8_t + { + LATCH_COUNT_VALUE = 0, + LOW_BYTE_ONLY = 1, + HIGH_BYTE_ONLY = 2, + LOW_BYTE_HIGH_BYTE = 3, + }; + + struct [[gnu::packed]] PITCommandRegister + { + union + { + uint8 value; + + struct [[gnu::packed]] + { + uint8 bcd_mode : 1; + OperatingMode operating_mode : 3; + AccessMode access_mode : 2; + uint8 channel : 2; + }; + }; + }; + static_assert(sizeof(PITCommandRegister) == 1); + + + static void init(PITCommandRegister command, uint16 divisor); + + static OperatingMode setOperatingMode(OperatingMode mode); + static OperatingMode operatingMode(); + + static void sendCommand(PITCommandRegister command); + + static uint16_t setFrequencyDivisor(uint16 reload_value); + static uint16_t frequencyDivisor(); + + static PIT& instance(); + + +private: + static OperatingMode operating_mode; + static uint16_t frequency_divisor; + + using COMMAND_PORT = IoPort::StaticIoRegister; + using CH0_DATA_PORT = IoPort::StaticIoRegister; +}; + +class PITDriver : public BasicDeviceDriver, public Driver +{ +public: + PITDriver(); + ~PITDriver() override = default; + + static PITDriver& instance(); + + void doDeviceDetection() override; +private: +}; diff --git a/arch/x86/common/include/X2Apic.h b/arch/x86/common/include/X2Apic.h new file mode 100644 index 000000000..830beb25b --- /dev/null +++ b/arch/x86/common/include/X2Apic.h @@ -0,0 +1,102 @@ +#pragma once + +#include "APIC.h" +#include "CPUID.h" +#include "MSR.h" + +#include + +#include "debug.h" + +class X2Apic : public Apic +{ +public: + // TODO: x2apic flag set in cpuid page 1, but x2apic id page 0xB not supported on x86-32 bit in QEMU + X2Apic() : + Apic(eastl::string("x2APIC ") + eastl::to_string(CPUID::localX2ApicId())) + { + } + ~X2Apic() override = default; + + static bool x2ApicSupported(); + static void enableX2ApicMode(); + static bool isEnabled(); + + void init() override; + uint32_t readId() override; + + bool isX2Apic() override + { + return true; + } + +private: + constexpr static unsigned int x2ApicOffset2Msr(ApicRegisterOffset reg) + { + return 0x800 + (static_cast(reg) >> 4); + } + + struct X2ApicRegisters + { + template + struct X2ApicRegister : public MSR::MSR{}; + + using ID = X2ApicRegister; + using VERSION = X2ApicRegister; + using TASK_PRIORITY = X2ApicRegister; + using PROCESSOR_PRIORITY = X2ApicRegister; + using EOI = X2ApicRegister; + using LOGICAL_DESTINATION = X2ApicRegister; + using SPURIOUS_INTERRUPT_VECTOR = X2ApicRegister; + using ISR_31_0 = X2ApicRegister; + using ISR_63_32 = X2ApicRegister; + using ISR_95_64 = X2ApicRegister; + using ISR_127_96 = X2ApicRegister; + using ISR_159_128 = X2ApicRegister; + using ISR_191_160 = X2ApicRegister; + using ISR_223_192 = X2ApicRegister; + using ISR_255_224 = X2ApicRegister; + using TMR_31_0 = X2ApicRegister; + using TMR_63_32 = X2ApicRegister; + using TMR_95_64 = X2ApicRegister; + using TMR_127_96 = X2ApicRegister; + using TMR_159_128 = X2ApicRegister; + using TMR_191_160 = X2ApicRegister; + using TMR_223_192 = X2ApicRegister; + using TMR_255_224 = X2ApicRegister; + using IRR_31_0 = X2ApicRegister; + using IRR_63_32 = X2ApicRegister; + using IRR_95_64 = X2ApicRegister; + using IRR_127_96 = X2ApicRegister; + using IRR_159_128 = X2ApicRegister; + using IRR_191_160 = X2ApicRegister; + using IRR_223_192 = X2ApicRegister; + using IRR_255_224 = X2ApicRegister; + using ERROR_STATUS = X2ApicRegister; + using LVT_CMCI = X2ApicRegister; + using INTERRUPT_COMMAND = X2ApicRegister; + using LVT_TIMER = X2ApicRegister; + using LVT_THERMAL_SENSOR = X2ApicRegister; + using LVT_PERFMON = X2ApicRegister; + using LVT_LINT0 = X2ApicRegister; + using LVT_LINT1 = X2ApicRegister; + using LVT_ERROR = X2ApicRegister; + using TIMER_INITIAL_COUNT = X2ApicRegister; + using TIMER_CURRENT_COUNT = X2ApicRegister; + using TIMER_DIVIDE_CONFIG = X2ApicRegister; + using SELF_IPI = X2ApicRegister; + }; + +protected: + void writeRegisterImpl(ApicRegisterOffset offset, uint64_t v) override; + uint64_t readRegisterImpl(ApicRegisterOffset offset) override; + + void writeIcr(InterruptCommandRegisterLow icr_l, uint32_t dest) override; + + void waitIpiDelivered() override + { + // Do nothing since delivered bit was removed for x2apic + } + +private: +}; diff --git a/arch/x86/common/include/atkbd.h b/arch/x86/common/include/atkbd.h index b815ec675..e1da34ae2 100644 --- a/arch/x86/common/include/atkbd.h +++ b/arch/x86/common/include/atkbd.h @@ -2,6 +2,7 @@ #include "types.h" #include "ports.h" + #define ATKBD_DATA 0x60 #define ATKBD_CTRL 0x64 diff --git a/arch/x86/common/include/multiboot.h b/arch/x86/common/include/multiboot.h index 24955372c..7ee5574e3 100644 --- a/arch/x86/common/include/multiboot.h +++ b/arch/x86/common/include/multiboot.h @@ -74,20 +74,73 @@ struct vbe_mode uint8 reserved3[189]; } __attribute__ ((packed)); +enum class MultibootFramebufferType : uint8 +{ + INDEXED_COLOR = 0, + RGB = 1, + EGA_TEXT_MODE = 2, // wdith+height in characters, not in pixels, pitch = bytes per text line, bpp = 16 +}; + +struct [[gnu::packed]] multiboot_framebuffer_t +{ + uint64 framebuffer_addr; + uint32 framebuffer_pitch; + uint32 framebuffer_width; + uint32 framebuffer_height; + uint8 framebuffer_bpp; // bits per pixel + MultibootFramebufferType framebuffer_type; + union [[gnu::packed]] + { + // type == 0 + struct [[gnu::packed]] + { + uint32 framebuffer_palette_addr; + uint16 framebuffer_palette_num_colors; + } indexed_color; + // type == 1 + struct [[gnu::packed]] + { + uint8 framebuffer_red_field_position; + uint8 framebuffer_red_mask_size; + uint8 framebuffer_green_field_position; + uint8 framebuffer_green_mask_size; + uint8 framebuffer_blue_field_position; + uint8 framebuffer_blue_mask_size; + } rgb; + } color_info; +}; + +struct [[gnu::packed]] color_descriptor +{ + uint8 red_value; + uint8 green_value; + uint8 blue_value; +}; + +struct [[gnu::packed]] multiboot_vbe_t +{ + uint32 vbe_control_info : 32; + uint32 vbe_mode_info : 32; + uint32 vbe_mode : 32; + uint32 vbe_interface_seg : 32; + uint32 vbe_interface_off : 32; + uint32 vbe_interface_len : 32; +}; + typedef struct multiboot_header { - uint32 magic : 32; - uint32 flags : 32; - uint32 checksum : 32; + uint32 magic : 32; + uint32 flags : 32; + uint32 checksum : 32; uint32 header_addr : 32; - uint32 load_addr : 32; - uint32 load_end_addr : 32; - uint32 bss_end_addr : 32; + uint32 load_addr : 32; + uint32 load_end_addr : 32; + uint32 bss_end_addr : 32; uint32 entry_addr : 32; - uint32 mode_type : 32; - uint32 width : 32; - uint32 height : 32; - uint32 depth : 32; + uint32 mode_type : 32; + uint32 width : 32; + uint32 height : 32; + uint32 depth : 32; } __attribute__((__packed__)) multiboot_header_t; typedef struct elf_section_header_table @@ -135,12 +188,10 @@ typedef struct multiboot_info uint32 config_table : 32; uint32 boot_loader_name : 32; uint32 apm_table : 32; - uint32 vbe_control_info : 32; - uint32 vbe_mode_info : 32; - uint32 vbe_mode : 32; - uint32 vbe_interface_seg : 32; - uint32 vbe_interface_off : 32; - uint32 vbe_interface_len : 32; + // present if flags[11] f_vbe set + multiboot_vbe_t vbe; + // present if flags[12] f_fb set + multiboot_framebuffer_t framebuffer; } __attribute__((__packed__)) multiboot_info_t; typedef struct module @@ -191,10 +242,20 @@ struct multiboot_remainder uint8 name[256]; } __attribute__((__packed__)) module_maps[MAX_MODULE_MAPS]; + bool have_framebuffer = false; + multiboot_framebuffer_t framebuffer; + + bool have_elf_sec_hdr = false; + elf_section_header_table_t elf_sec; + + bool have_vbe = false; + multiboot_vbe_t vbe; + + char cmdline[256]; + }__attribute__((__packed__)); struct mb_offsets { uint64 phys; uint64 entry; } __attribute__((__packed__)); - diff --git a/arch/x86/common/include/ports.h b/arch/x86/common/include/ports.h index 776ff5b03..2fa0d2179 100644 --- a/arch/x86/common/include/ports.h +++ b/arch/x86/common/include/ports.h @@ -2,13 +2,14 @@ #include "types.h" +#include "EASTL/bit.h" /** * reads 1 byte from the selected I/O port * @param port the I/O port number which is read * */ -static inline uint8 inportb(uint16 port) +[[gnu::always_inline]] static inline uint8 inportb(uint16 port) { uint8 return_val; __asm__ __volatile__ ( @@ -24,7 +25,7 @@ static inline uint8 inportb(uint16 port) * @param port the I/O port number which is read * */ -static inline uint8 inportbp(uint16 port) +[[gnu::always_inline]] static inline uint8 inportbp(uint16 port) { uint8 _v; asm volatile ("inb %1, %0" : "=a" (_v) : "id" (port)); @@ -37,7 +38,7 @@ static inline uint8 inportbp(uint16 port) * @param port the I/O port number which is read * */ -static inline uint16 inportw(uint16 port) +[[gnu::always_inline]] static inline uint16 inportw(uint16 port) { uint16 _res; asm volatile ("inw %1, %0" : "=a" (_res) : "id" (port)); @@ -50,7 +51,7 @@ static inline uint16 inportw(uint16 port) * */ __attribute__((unused)) -static uint16 inportwp(uint16 port) +static inline uint16 inportwp(uint16 port) { uint16 _res; asm volatile ("inw %1, %0" : "=a" (_res) : "id" (port)); @@ -64,10 +65,10 @@ static uint16 inportwp(uint16 port) * @param val data value sent to I/O port * */ -static inline void outportb(uint16 port, uint8 val) +[[gnu::always_inline]] static inline void outportb(uint16 port, uint8 val) { __asm__ __volatile__ ( - "outb %b0, %w1" + "outb %b0, %w1\n" : : "a"(val), "d"(port)); } @@ -77,7 +78,7 @@ static inline void outportb(uint16 port, uint8 val) * @param port the I/O port number to send data to * @param val data value sent to I/O port */ -static inline void outportbp(uint16 port, uint8 value) +[[gnu::always_inline]] static inline void outportbp(uint16 port, uint8 value) { asm volatile ("outb %b0, %1" : : "a" (value), "id" (port)); asm volatile ("outb %al,$0x80"); @@ -89,7 +90,7 @@ static inline void outportbp(uint16 port, uint8 value) * @param val data value sent to I/O port * */ -static inline void outportw(uint16 port, uint16 value) +[[gnu::always_inline]] static inline void outportw(uint16 port, uint16 value) { asm volatile ("outw %w0, %1" : : "a" (value), "id" (port)); } @@ -99,9 +100,159 @@ static inline void outportw(uint16 port, uint16 value) * @param port the I/O port number to send data to * @param val data value sent to I/O port */ -static inline void outportwp(uint16 port, uint16 value) +[[gnu::always_inline]] static inline void outportwp(uint16 port, uint16 value) { asm volatile ("outw %w0, %1" : : "a" (value), "id" (port)); asm volatile ("outb %al,$0x80"); } +namespace IoPort +{ + template constexpr bool always_false = false; + + template + struct IoPortDescription + { + using value_type = T; + using port = eastl::integral_constant; + using readable = eastl::bool_constant; + using writeable = eastl::bool_constant; + }; + + template + concept io_port_description = requires(T x) + { + typename T::value_type; + typename T::port; + typename T::readable; + typename T::writeable; + }; + + template + [[gnu::always_inline]] static inline void write(uint16_t port, const T& v) + { + if constexpr (sizeof(T) == 1) + { + outportb(port, eastl::bit_cast(v)); + } + else if constexpr (sizeof(T) == 2) + { + outportw(port, eastl::bit_cast(v)); + } + else + { + static_assert(always_false, "Invalid size for I/O port write"); + } + } + + template + [[gnu::always_inline]] static inline T read(uint16_t port) + { + if constexpr (sizeof(T) == 1) + { + return eastl::bit_cast(inportb(port)); + } + else if constexpr (sizeof(T) == 2) + { + return eastl::bit_cast(inportw(port)); + } + else + { + static_assert(always_false, "Invalid size for I/O port read"); + } + } + + template + requires(PD::writeable::value) + [[gnu::always_inline]] static inline void write(const typename PD::value_type& v) + { + write(PD::port, v); + } + + template + requires(PD::readable::value) + [[gnu::always_inline]] static inline typename PD::value_type read() + { + return read(PD::port); + } + + template + struct StaticIoRegister + { + [[gnu::always_inline]] static inline void write(const T& v) requires(writeable) + { + IoPort::write(port, v); + } + + [[gnu::always_inline]] static inline T read() requires(writeable) + { + return IoPort::read(port); + } + }; + + template + using StaticIoRegister_ = StaticIoRegister; + + template + struct IoRegister + { + const uint16_t port; + + inline void write(const T& v) requires(writeable) { IoPort::write(port, v); } + + inline T read() requires(readable) { return IoPort::read(port); } + }; + + struct IoRegisterSet + { + const uint16_t base_port; + + template + requires(PD::writeable::value) + inline void write(const typename PD::value_type& v) + { + IoPort::write(base_port + PD::port::value, v); + } + + template + requires(PD::readable::value) + inline typename PD::value_type read() + { + return IoPort::read(base_port + PD::port::value); + } + + template + inline IoRegister + operator[](PD) + { + return {static_cast(base_port + PD::port::value)}; + } + }; +}; + +static constexpr uint16_t EMULATOR_DEBUGCONSOLE_PORTNUM = 0xE9; + +using EMULATOR_DEBUGCONSOLE = + IoPort::StaticIoRegister; + +enum class ICMRIoPorts : uint16_t +{ + IMCR_SELECT = 0x22, + IMCR_DATA = 0x23, +}; + +enum class IMCRSelect : uint8_t +{ + SELECT_IMCR = 0x70, +}; + +enum class IMCRData : uint8_t +{ + PIC_MODE = 0x0, + APIC_PASSTHROUGH = 0x1, +}; + +using IMCR_SELECT = + IoPort::StaticIoRegister<(uint16_t)ICMRIoPorts::IMCR_SELECT, IMCRSelect, false, true>; +using IMCR_DATA = + IoPort::StaticIoRegister<(uint16_t)ICMRIoPorts::IMCR_DATA, IMCRData, false, true>; diff --git a/arch/x86/common/source/8259.cpp b/arch/x86/common/source/8259.cpp index 4859227ce..7f000465c 100644 --- a/arch/x86/common/source/8259.cpp +++ b/arch/x86/common/source/8259.cpp @@ -1,67 +1,214 @@ #include "8259.h" + +#include "APIC.h" +#include "InterruptUtils.h" #include "ports.h" + +#include "ArchInterrupts.h" + +#include "assert.h" #include "debug.h" -volatile size_t outstanding_EOIs = 0; +bool PIC8259::exists = true; +bool PIC8259::enabled = true; +size_t PIC8259::outstanding_EOIs_ = 0; + +uint16 PIC8259::cached_mask = 0xFFFF; -uint32 cached_mask = 0xFFFF; +#define PIC_ICW1_INIT 0x11 +#define PIC_ICW2_OFFSET 0x20 // offset for IRQ -> interrupt vector mapping (i.e. IRQ 0 is mapped to vector 32) +#define PIC_ICW4_8086 0x01 + +#define PIC_EOI 0x20 + +PIC8259::PIC8259() : + IrqDomain("PIC 8259", 16, this), + Device("PIC 8259") +{ +} + +PIC8259& PIC8259::instance() +{ + static PIC8259 pic; + return pic; +} + +void PIC8259::init() +{ + PIC8259::initialise8259s(); + + if (enabled) + { + setupIrqMappings(); + } +} -void initialise8259s() +void PIC8259::setupIrqMappings() { - outportb(PIC_1_CONTROL_PORT, 0x11); /* ICW1 */ - outportb(PIC_1_DATA_PORT, 0x20); /* ICW2: route IRQs 0...7 to INTs 20h...27h */ - outportb(PIC_1_DATA_PORT, 0x04); /* ICW3 */ - outportb(PIC_1_DATA_PORT, 0x01); /* ICW4 */ + for (size_t isa_irq = 0; isa_irq < 16; ++isa_irq) + { + // There is no ISA IRQ 2 (used for pic cascade) + if (isa_irq == 2) + continue; - outportb(PIC_2_CONTROL_PORT, 0x11); - outportb(PIC_2_DATA_PORT, 0x28); /* ...IRQs 8...15 to INTs 28h...2Fh */ - outportb(PIC_2_DATA_PORT, 0x02); - outportb(PIC_2_DATA_PORT, 0x01); + // Map ISA interrupts to PIC (if not already mapped to I/O APIC) + IrqInfo* irq_info = ArchInterrupts::isaIrqDomain().irqInfo(isa_irq); + if (!irq_info || !irq_info->map_to.domain) + { + ArchInterrupts::isaIrqDomain().irq(isa_irq).mapTo(instance(), isa_irq); + } + } - uint16 i; - for (i=0;i<16;++i) - disableIRQ(i); + for (size_t i = 0; i < 16; ++i) + { + // IRQ 2 is never raised (internal PIC cascade) + if (i == 2) + continue; - for (i=0;i<16;++i) - sendEOI(i); + irq(i).mapTo(*cpu_root_irq_domain_, InterruptVector::REMAP_OFFSET + i); + } +} + +bool PIC8259::mask(irqnum_t irq, bool mask) +{ + assert(irq < 16); + if (mask) + disableIRQ(irq); + else + enableIRQ(irq); + return true; +} - outstanding_EOIs = 0; +bool PIC8259::isMasked(irqnum_t irqnum) +{ + return !isIRQEnabled(irqnum); +} + +bool PIC8259::irqStart([[maybe_unused]]irqnum_t irq) +{ + ++pending_EOIs; + ++outstanding_EOIs_; + return true; } -void enableIRQ(uint16 number) +bool PIC8259::ack(irqnum_t irq) { - uint32 mask = 1 << number; - cached_mask &= ~mask; - if (number & 8) + assert(irq < 16); + --pending_EOIs; + sendEOI(irq); + return true; +} + +void PIC8259::initialise8259s() +{ + // https://wiki.osdev.org/8259_PIC#Initialisation + debug(PIC_8259, "Init 8259 Programmable Interrupt Controller\n"); + PIC_1_CONTROL_PORT::write(PIC_ICW1_INIT); /* ICW1 */ + PIC_1_DATA_PORT::write(PIC_ICW2_OFFSET); /* ICW2: route IRQs 0...7 to INTs 20h...27h */ + PIC_1_DATA_PORT::write(0x04); /* ICW3 */ + PIC_1_DATA_PORT::write(PIC_ICW4_8086); /* PIC_ICW4 */ + + PIC_2_CONTROL_PORT::write(PIC_ICW1_INIT); + PIC_2_DATA_PORT::write(PIC_ICW2_OFFSET + 0x8); /* ...IRQs 8...15 to INTs 28h...2Fh */ + PIC_2_DATA_PORT::write(0x02); + PIC_2_DATA_PORT::write(PIC_ICW4_8086); + + // Mask all interrupts + setIrqMask(0xFFFF); + + for (size_t i=0; i < 16; ++i) + sendEOI(i); + + outstanding_EOIs_ = 0; +} + +bool PIC8259::isIRQEnabled(uint16 number) +{ + assert(number < 16); + return !(cached_mask & (1 << number)); +} + +void PIC8259::enableIRQ(uint16 number) +{ + debug(PIC_8259, "PIC8259, enable IRQ %x\n", number); + assert(number < 16); + cached_mask &= ~(1 << number); + if (number >= 8) { - outportb(PIC_2_DATA_PORT,((cached_mask>>8))); + PIC_2_DATA_PORT::write(cached_mask >> 8); } else { - outportb(PIC_1_DATA_PORT,(cached_mask%8)); + PIC_1_DATA_PORT::write(cached_mask % 8); + } + + if(!isIRQEnabled(2) && (number >= 8)) + { + enableIRQ(2); // Enable slave cascade } } -void disableIRQ(uint16 number) +void PIC8259::disableIRQ(uint16 number) { - uint32 mask = 1 << number; - cached_mask |= mask; - if (number & 8) + debug(PIC_8259, "PIC8259, disable IRQ %x\n", number); + assert(number < 16); + cached_mask |= (1 << number); + if (number >= 8) { - outportb(PIC_2_DATA_PORT,((cached_mask>>8))); + PIC_2_DATA_PORT::write(cached_mask >> 8); } else { - outportb(PIC_1_DATA_PORT,(cached_mask%8)); + PIC_1_DATA_PORT::write(cached_mask & 0xFF); + } + + if(((cached_mask & 0xFF00) == 0xFF00) && isIRQEnabled(2) && (number >= 8)) + { + disableIRQ(2); // Disable slave cascade } } -void sendEOI(uint16 number) +void PIC8259::setIrqMask(uint16 mask) +{ + debug(PIC_8259, "PIC8259, set IRQ mask %x\n", mask); + cached_mask = mask; + PIC_2_DATA_PORT::write(cached_mask >> 8); + PIC_1_DATA_PORT::write(cached_mask & 0xFF); +} + +void PIC8259::sendEOI(uint16 number) +{ + debugAdvanced(PIC_8259, "PIC8259, send EOI for IRQ %x\n", number); + + assert(number <= 16); + --outstanding_EOIs_; + + if (number >= 8) + { + PIC_2_CONTROL_PORT::write(PIC_EOI); + } + + PIC_1_CONTROL_PORT::write(PIC_EOI); +} + +PIC8259Driver::PIC8259Driver() : + BasicDeviceDriver("PIC8259 driver") +{ +} + +PIC8259Driver& PIC8259Driver::instance() +{ + static PIC8259Driver i; + return i; +} + +void PIC8259Driver::doDeviceDetection() { - --outstanding_EOIs; - debug(A_INTERRUPTS, "sendEOI, outstanding: %zu\n", outstanding_EOIs); - if (number > 7) - outportb(PIC_2_CONTROL_PORT,0x20); + debug(DRIVER, "Driver '%s' device detection\n", driverName().c_str()); - outportb(PIC_1_CONTROL_PORT,0x20); + if (PIC8259::exists) + { + PIC8259::instance().init(); + bindDevice(PIC8259::instance()); + } } diff --git a/arch/x86/common/source/ACPI.cpp b/arch/x86/common/source/ACPI.cpp new file mode 100644 index 000000000..8efe03fd5 --- /dev/null +++ b/arch/x86/common/source/ACPI.cpp @@ -0,0 +1,299 @@ +#include "ACPI.h" + +#include "APIC.h" +#include "IoApic.h" +#include "kstring.h" +#include "new.h" +#include "offsets.h" + +#include "ArchCommon.h" +#include "ArchMemory.h" + +#include "types.h" + +#include "assert.h" +#include "debug.h" + +RSDPDescriptor* RSDP = nullptr; +size_t ACPI_version = 0; + +RSDPDescriptor* checkForRSDP(char* start, char* end) +{ + debug(ACPI, "Search segment [%p, %p) for RSDP\n", start, end); + for(char* i = (char*)start; i < (char*)end; ++i) + { + if(memcmp(i, "RSD PTR ", 8) == 0) + { + debug(ACPI, "Found RSDP at %p\n", i); + return (RSDPDescriptor*)i; + } + } + return nullptr; +} + +RSDPDescriptor* locateRSDP() +{ + debug(ACPI, "locateRSDP\n"); + size_t num_mmaps = ArchCommon::getNumUseableMemoryRegions(); + for (size_t i = 0; i < num_mmaps; ++i) + { + pointer start_address = 0, end_address = 0, type = 0; + ArchCommon::getUseableMemoryRegion(i, start_address, end_address, type); + if(end_address == 0) + end_address = 0xFFFFFFFF; // TODO: Fix this (use full 64 bit addresses for memory detection) + assert(start_address <= end_address); + if((type == 2) && (end_address <= 0x100000)) + { + RSDPDescriptor* found = checkForRSDP((char*)start_address, (char*)end_address); + if(found) + { + return found; + } + } + } + return nullptr; +} + + + +void initACPI() +{ + debug(ACPI, "initACPI\n"); + + RSDP = locateRSDP(); + assert(RSDP && "Could not find RSDP"); + + assert(RSDP->checksumValid()); + ACPI_version = (RSDP->Revision == 0 ? 1 : RSDP->Revision); + debug(ACPI, "ACPI version: %zu\n", ACPI_version); + { + char oemid[7]; + memcpy(oemid, RSDP->OEMID, 6); + oemid[6] = '\0'; + debug(ACPI, "RSDP OEMID: %s\n", oemid); + } + + switch(RSDP->Revision) + { + case 0: + { + debug(ACPI, "RSDT address: %#x\n", RSDP->RsdtAddress); + + RSDT* RSDT_ptr = (RSDT*)(size_t)RSDP->RsdtAddress; + ArchMemoryMapping m = ArchMemory::kernelArchMemory().resolveMapping((size_t)RSDT_ptr / PAGE_SIZE); + assert(m.page_ppn != 0); + assert(RSDT_ptr->h.checksumValid()); + + size_t RSDT_entries = RSDT_ptr->numEntries(); + debug(ACPI, "RSDT entries: %zu\n", RSDT_entries); + for(size_t i = 0; i < RSDT_entries; ++i) + { + handleSDT(RSDT_ptr->getEntry(i)); + } + + break; + } + case 2: + { + RSDPDescriptor20* RSDP2 = (RSDPDescriptor20*)RSDP; + assert(RSDP2->checksumValid()); + + debug(ACPI, "XSDT address: %" PRIx64 "\n", RSDP2->XsdtAddress); + + XSDT* XSDT_ptr = (XSDT*)(size_t)RSDP2->XsdtAddress; + ArchMemoryMapping m = ArchMemory::kernelArchMemory().resolveMapping((size_t)XSDT_ptr / PAGE_SIZE); + bool unmap_again = false; + if(!m.page) + { + debug(ACPI, "XSDT page %zx not present, mapping\n", (size_t)XSDT_ptr/PAGE_SIZE); + assert(ArchMemory::mapKernelPage((size_t)XSDT_ptr / PAGE_SIZE, (size_t)XSDT_ptr / PAGE_SIZE, true)); + m = ArchMemory::kernelArchMemory().resolveMapping((size_t)XSDT_ptr / PAGE_SIZE); + } + assert(m.page != 0); + assert(XSDT_ptr->h.checksumValid()); + + size_t XSDT_entries = XSDT_ptr->numEntries(); + debug(ACPI, "XSDT entries: %zu\n", XSDT_entries); + for(size_t i = 0; i < XSDT_entries; ++i) + { + handleSDT(XSDT_ptr->getEntry(i)); + } + + if(unmap_again) + { + debug(ACPI, "Unmapping previously mapped XSDT page %zx again\n", (size_t)XSDT_ptr/PAGE_SIZE); + ArchMemory::unmapKernelPage((size_t)XSDT_ptr / PAGE_SIZE, false); + } + + break; + } + default: + debug(ACPI, "Invalid RSDP version %x\n", RSDP->Revision); + assert(false && "Invalid RDSP version"); + break; + } +} + +void handleSDT(ACPISDTHeader* entry_header) +{ + bool unmap_page_again = false; + ArchMemoryMapping m = ArchMemory::kernelArchMemory().resolveMapping((size_t)entry_header / PAGE_SIZE); + + if(!m.page) + { + debug(ACPI, "SDT page %zx not present, mapping\n", (size_t)entry_header/PAGE_SIZE); + assert(ArchMemory::mapKernelPage((size_t)entry_header/PAGE_SIZE, (size_t)entry_header/PAGE_SIZE, true)); + m = ArchMemory::kernelArchMemory().resolveMapping((size_t)entry_header / PAGE_SIZE); + unmap_page_again = true; + } + assert(m.page && "Page for ACPI SDT not mapped"); + + { + char sig[5]; + memcpy(sig, entry_header->Signature, 4); + sig[4] = '\0'; + debug(ACPI, "[%p] SDT Header signature: %s\n", entry_header, sig); + } + + if(memcmp(entry_header->Signature, "APIC", 4) == 0) + { + ACPI_MADTHeader* madt = (ACPI_MADTHeader*)entry_header; + madt->parse(); + } + + if(unmap_page_again) + { + debug(ACPI, "Unmapping previously mapped SDT page %zx again\n", (size_t)entry_header/PAGE_SIZE); + ArchMemory::unmapKernelPage((size_t)entry_header/PAGE_SIZE, false); + } +} + + +bool RSDPDescriptor::checksumValid() +{ + uint8 sum = 0; + for(char* i = (char*)this; i < ((char*)this) + sizeof(*this); ++i) + { + sum += *i; + } + if(ACPI & OUTPUT_ADVANCED) + debug(ACPI, "RSDP checksum %x\n", sum); + return sum == 0; +} + +bool RSDPDescriptor20::checksumValid() +{ + uint8 sum = 0; + for(char* i = (char*)this; i < ((char*)this) + sizeof(*this); ++i) + { + sum += *i; + } + if(ACPI & OUTPUT_ADVANCED) + debug(ACPI, "RSDP 2.0 checksum %x\n", sum); + return sum == 0; +} + +bool ACPISDTHeader::checksumValid() +{ + uint8 sum = 0; + for(char* i = (char*)this; i < ((char*)this) + Length; ++i) + { + sum += *i; + } + return sum == 0; +} + + +size_t RSDT::numEntries() +{ + size_t RSDT_entries = (h.Length - sizeof(*this)) / 4; + return RSDT_entries; +} + +size_t XSDT::numEntries() +{ + size_t XSDT_entries = (h.Length - sizeof(*this)) / 8; + return XSDT_entries; +} + +ACPISDTHeader* RSDT::getEntry(size_t i) +{ + ACPISDTHeader* entry_ptr = (ACPISDTHeader*)(size_t)(((uint32*)(this + 1))[i]); + return entry_ptr; +} + +ACPISDTHeader* XSDT::getEntry(size_t i) +{ + ACPISDTHeader* entry_ptr = (ACPISDTHeader*)(size_t)(((uint64*)(this + 1))[i]); + return entry_ptr; +} + + +void ACPI_MADTHeader::parse() +{ + XApic::foundLocalAPIC((void*)(size_t)ext_header.local_apic_addr, ext_header.flags); + + MADTEntryDescriptor* madt_entry = (MADTEntryDescriptor*)(this + 1); + while((size_t)madt_entry < (size_t)this + std_header.Length) + { + switch(madt_entry->type) + { + case MADTEntryDescriptor::PROCESSOR_LOCAL_APIC: + { + MADTProcLocalAPIC* entry = (MADTProcLocalAPIC*)(madt_entry + 1); + debug(ACPI, "[%p] Processor local APIC, ACPI Processor ID: %4x, APIC ID: %4x, enabled: %u, online capable: %u\n", entry, entry->proc_id, entry->apic_id, entry->flags.enabled, entry->flags.online_capable); + Apic::addLocalAPICToList(*entry); + break; + } + case MADTEntryDescriptor::IO_APIC: + { + MADT_IO_APIC* entry = (MADT_IO_APIC*)(madt_entry + 1); + debug(ACPI, "[%p] I/O APIC, id: %x, address: %x, g_sys_int base: %x\n", entry, entry->id, entry->address, entry->global_system_interrupt_base); + IoApic::addIOAPIC(entry->id, (IoApic::IOAPIC_MMIORegs*)(size_t)entry->address, (uint32)entry->global_system_interrupt_base); + break; + } + case MADTEntryDescriptor::INTERRUPT_SOURCE_OVERRIDE: + { + MADTInterruptSourceOverride* entry = (MADTInterruptSourceOverride*)(madt_entry + 1); + debug(ACPI, "[%p] Interrupt Source Override, bus_source: %x, irq_source: %3x, g_sys_int: %3x, polarity: %x, trigger mode: %x\n", entry, entry->bus_source, entry->irq_source, entry->g_sys_int, entry->flags.polarity, entry->flags.trigger_mode); + IoApic::addIRQSourceOverride(*entry); + break; + } + case MADTEntryDescriptor::NMI_SOURCE: + { + MADTNonMaskableInterruptsSource* entry = (MADTNonMaskableInterruptsSource*)(madt_entry + 1); + debug(ACPI, "[%p] NMI source, g_sys_int: %x, flags: %x\n", entry, entry->g_sys_int, entry->flags); + break; + } + case MADTEntryDescriptor::LOCAL_APIC_NMI: + { + MADTNonMaskableInterrupts* entry = (MADTNonMaskableInterrupts*)(madt_entry + 1); + debug(ACPI, "[%p] Local APIC NMI, proc_id: %x, flags: %x, lint_num: %x\n", entry, entry->processor_id, entry->flags, entry->lint_num); + break; + } + case MADTEntryDescriptor::LOCAL_APIC_ADDR_OVERRIDE: + { + MADTLocalAPICAddressOverride* entry = (MADTLocalAPICAddressOverride*)(madt_entry + 1); + debug(ACPI, "[%p] Local APIC address override, addr: %" PRIx64 "\n", entry, entry->local_apic_addr); + XApic::setPhysicalAddress((void*)entry->local_apic_addr); + break; + } + case MADTEntryDescriptor::PROCESSOR_LOCAL_X2APIC: + { + MADTProcLocalx2APIC* entry = (MADTProcLocalx2APIC*)(madt_entry + 1); + debug(ACPI, "[%p] Processor local x2APIC, x2APIC ID: %4x, enabled: %u, proc UID: %x\n", entry, entry->x2apic_id, entry->flags.enabled, entry->processor_uid); + break; + } + default: + debug(ACPI, "[%p] Unknown MADT entry type %x\n", madt_entry + 1, madt_entry->type); + break; + } + madt_entry = (MADTEntryDescriptor*)((size_t)madt_entry + madt_entry->length); + } +} + + +void Apic::addLocalAPICToList(const MADTProcLocalAPIC& entry) +{ + local_apic_list_.push_back(entry); +} diff --git a/arch/x86/common/source/APIC.cpp b/arch/x86/common/source/APIC.cpp new file mode 100644 index 000000000..02c82d35b --- /dev/null +++ b/arch/x86/common/source/APIC.cpp @@ -0,0 +1,631 @@ +#include "APIC.h" + +#include "8259.h" +#include "CPUID.h" +#include "Device.h" +#include "InterruptUtils.h" +#include "IrqDomain.h" +#include "MSR.h" +#include "PageManager.h" +#include "ProgrammableIntervalTimer.h" +#include "Scheduler.h" +#include "X2Apic.h" +#include "kstring.h" +#include "new.h" +#include "offsets.h" +#include "ports.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMemory.h" +#include "ArchMulticore.h" + +#include "assert.h" +#include "debug.h" + +void* XApic::reg_paddr_ = (void*)0xfee00000; +void* XApic::reg_vaddr_ = nullptr; + +eastl::vector Apic::local_apic_list_{}; + +extern volatile size_t outstanding_EOIs; + +Apic::Apic(const eastl::string& name) : + IrqDomain(name, InterruptVector::NUM_VECTORS, this), + Device(name), + timer_interrupt_controller(*this) +{ +} + +uint32 Apic::apicId() const +{ + return id_; +} + +bool Apic::isInitialized() const { return initialized_; } + +bool Apic::isX2Apic() { return false; } + +void Apic::setIMCRMode(IMCRData mode) +{ + // Intel MultiProcessor Specification chapter 3.6.2 + // https://pdos.csail.mit.edu/6.828/2008/readings/ia32/MPspec.pdf + debug(APIC, "Ensure IMCR is set to APIC passthrough/symmetric mode\n"); + // IMCR register might not actually exist, but attempting to write to it should be fine + IMCR_SELECT::write(IMCRSelect::SELECT_IMCR); + IMCR_DATA::write(mode); +} + +void Apic::globalEnable(bool enable) +{ + auto apic_base = MSR::IA32_APIC_BASE::read(); + apic_base.enable = enable; + MSR::IA32_APIC_BASE::write(apic_base); +} + +void Apic::enable(bool enable) +{ + debug(APIC, "%s APIC %x\n", (enable ? "Enabling" : "Disabling"), apicId()); + + WithInterrupts i(false); + auto siv = readRegister(); + siv.enable = enable; + writeRegister(siv); +} + +void Apic::initTimer() +{ + debug(APIC, "Init timer for APIC %x\n", apicId()); + assert(!ArchInterrupts::testIFSet()); + + setUsingAPICTimer(true); + + auto div = readRegister(); + div.setTimerDivisor(TIMER_DIVISOR); + writeRegister(div); + + auto timer_reg = readRegister(); + timer_reg.setVector(InterruptVector::APIC_TIMER); + timer_reg.setMode(TimerMode::PERIODIC); + timer_reg.setMask(true); + writeRegister(timer_reg); + + // Write to initial count register starts timer + setTimerPeriod(0x500000); +} + +void Apic::setTimerPeriod(uint32_t count) +{ + debug(APIC, "Set timer period %x\n", count); + writeRegister(count); +} + +void Apic::setSpuriousInterruptNumber(uint8_t num) +{ + auto siv = readRegister(); + siv.vector = num; + writeRegister(siv); +} + +void Apic::setErrorInterruptVector(uint8_t vector) +{ + LVT_ErrorRegister errorreg{}; + errorreg.vector = vector; + errorreg.mask = false; + writeRegister(errorreg); +} + +void Apic::setUsingAPICTimer(bool use_apic_timer) +{ + debug(APIC, "Set using APIC timer: %d\n", use_apic_timer); + + use_apic_timer_ = use_apic_timer; +} + +bool Apic::usingAPICTimer() const +{ + return use_apic_timer_; +} + +bool Apic::checkISR(uint8_t irqnum) +{ + uint8_t word_offset = irqnum/32; + uint8_t bit_offset = irqnum % 32; + assert(word_offset < 8); + + uint32_t isr = 0; + + switch (word_offset) + { + case 0: + isr = readRegister(); + break; + case 1: + isr = readRegister(); + break; + case 2: + isr = readRegister(); + break; + case 3: + isr = readRegister(); + break; + case 4: + isr = readRegister(); + break; + case 5: + isr = readRegister(); + break; + case 6: + isr = readRegister(); + break; + case 7: + isr = readRegister(); + break; + default: + assert(!"Invalid ISR word offset"); + } + return isr & (1 << bit_offset); +} + +bool Apic::checkIRR(uint8_t irqnum) +{ + uint8_t word_offset = irqnum/32; + uint8_t bit_offset = irqnum % 32; + assert(word_offset < 8); + + uint32_t irr = 0; + + switch (word_offset) + { + case 0: + irr = readRegister(); + break; + case 1: + irr = readRegister(); + break; + case 2: + irr = readRegister(); + break; + case 3: + irr = readRegister(); + break; + case 4: + irr = readRegister(); + break; + case 5: + irr = readRegister(); + break; + case 6: + irr = readRegister(); + break; + case 7: + irr = readRegister(); + break; + default: + assert(!"Invalid IRR word offset"); + } + return irr & (1 << bit_offset); +} + +void Apic::sendEOI(size_t num) +{ + assert(SMP::currentCpuId() == apicId()); + --outstanding_EOIs_; + if(APIC & OUTPUT_ADVANCED) + { + debug(APIC, "CPU %zu, Sending EOI for %zu\n", SMP::currentCpuId(), num); + for(size_t i = 0; i < 256; ++i) + { + if(checkISR(i)) + { + debug(APIC, "CPU %zx, interrupt %zu being serviced\n", SMP::currentCpuId(), i); + } + } + for(size_t i = 0; i < 256; ++i) + { + if(checkIRR(i)) + { + debug(APIC, "CPU %zx, interrupt %zu pending\n", SMP::currentCpuId(), i); + } + } + } + + assert(!ArchInterrupts::testIFSet() && "Attempted to send end of interrupt command while interrupts are enabled"); + assert(checkISR(num) && "Attempted to send end of interrupt command but interrupt is not actually being serviced"); + + writeRegister(0); +} + +bool Apic::mask(irqnum_t irq, bool mask) +{ + // Cannot mask interrupts in APIC + debug(APIC, "%s, mask Irq %zu = %u\n", name().c_str(), irq, mask); + auto info = irqInfo(irq); + assert(info && "No irq info found"); + assert(!info->mapped_by.empty() || info->handler); + + return false; +} + +bool Apic::isMasked([[maybe_unused]]irqnum_t irq) +{ + // Cannot mask interrupts in APIC + return false; +} + +bool Apic::ack(irqnum_t irq) +{ + debugAdvanced(APIC, "%s, ack Irq %zu\n", name().c_str(), irq); + auto info = irqInfo(irq); + assert(info && "No irq info found"); + assert(!info->mapped_by.empty() || info->handler); + + return false; +} + +bool Apic::irqStart(irqnum_t irq) +{ + debugAdvanced(APIC, "%s, start of Irq %zu\n", name().c_str(), irq); + auto info = irqInfo(irq); + if (!info) + debugAlways(APIC, "No irq info found for irqnum %zu in irq domain %s\n", irq, name().c_str()); + assert(info && "No irq info found"); + assert(!info->mapped_by.empty() || info->handler); + return false; +} + + + +void XApic::foundLocalAPIC(void* reg_phys_addr, MADTExtendedHeader::Flags flags) +{ + debug(APIC, "Local APIC at phys %p, PIC 8259: %u\n", reg_paddr_, flags.PCAT_COMPAT); + PIC8259::exists = flags.PCAT_COMPAT; + assert(reg_phys_addr == readMsrPhysAddr()); +} + +void XApic::mapAt(size_t addr) +{ + assert(addr); + assert(cpu_features.cpuHasFeature(CpuFeatures::APIC)); + + debug(APIC, "Map local APIC at phys %p to %p\n", reg_paddr_, (void*)addr); + + ArchMemoryMapping m = ArchMemory::kernelArchMemory().resolveMapping(addr/PAGE_SIZE); + if (!m.page) + { + assert(ArchMemory::mapKernelPage(addr/PAGE_SIZE, ((size_t)reg_paddr_)/PAGE_SIZE, true, true)); + } + else + { + debug(APIC, "Vaddr %p is already mapped to ppn %zx\n", (void*)addr, m.page_ppn); + assert(m.page_ppn == ((size_t)reg_paddr_)/PAGE_SIZE); + assert(m.pt[m.pti].write_through); + assert(m.pt[m.pti].cache_disabled); + } + reg_vaddr_ = (void*)addr; +} + +void XApic::init() +{ + debug(APIC, "Initializing local xAPIC\n"); + assert(!isInitialized()); + + id_ = readId(); + auto logical_dest_id = readRegister(); + debug(APIC, "Local xAPIC, id: %x, logical dest: %x\n", apicId(), logical_dest_id); + + setErrorInterruptVector(InterruptVector::APIC_ERROR); + setSpuriousInterruptNumber(InterruptVector::APIC_SPURIOUS); + + enable(true); + + initialized_ = true; + SMP::currentCpu().setId(id_); +} + +void Apic::LVT_TimerRegister::setVector(uint8_t num) +{ + debug(APIC, "Set timer interrupt number %u\n", num); + assert(num >= 32); + vector = num; +} + +void Apic::LVT_TimerRegister::setMode(TimerMode mode) +{ + debug(APIC, "Set timer mode %x\n", (uint32_t)mode); + timer_mode = (uint32_t)mode; +} + +void Apic::LVT_TimerRegister::setMask(bool new_mask) +{ + debug(APIC, "Set timer mask %u\n", new_mask); + mask = new_mask; +} + +void Apic::SpuriousInterruptVectorRegister::setSpuriousInterruptNumber(uint8_t num) +{ + debug(APIC, "Set spurious interrupt number %u\n", num); + vector = num; +} + +void Apic::TimerDivideConfigRegister::setTimerDivisor(uint8_t divisor) +{ + debug(APIC, "Set timer divisor %x\n", divisor); + + switch(divisor) + { + case 1: + divisor_l = 0b11; + divisor_h = 1; + break; + case 2: + divisor_l = 0b00; + divisor_h = 0; + break; + case 4: + divisor_l = 0b01; + divisor_h = 0; + break; + case 8: + divisor_l = 0b10; + divisor_h = 0; + break; + case 16: + divisor_l = 0b11; + divisor_h = 0; + break; + case 32: + divisor_l = 0b00; + divisor_h = 1; + break; + case 64: + divisor_l = 0b01; + divisor_h = 1; + break; + case 128: + divisor_l = 0b10; + divisor_h = 1; + break; + default: + assert(false); + break; + } +} + + + +uint32_t XApic::readId() +{ + auto id = readRegister(); + assert(id.xapic_id == CPUID::localApicId()); + return id.xapic_id; +} + +void XApic::waitIpiDelivered() +{ + InterruptCommandRegister icr{}; + while (icr.l.delivery_status == (uint32_t)DeliveryStatus::PENDING) + { + icr = readRegister(); + } +} + +void Apic::sendIPI(uint8_t vector, IPIDestination dest_type, size_t target, IPIType ipi_type, bool wait_for_delivery) +{ + assert(isInitialized()); + assert(!((ipi_type == IPIType::FIXED) && (vector < 32))); + + // Need to ensure this section of code runs on the same CPU and the APIC is not used for anything else in the meantime + WithInterrupts d(false); + + if (A_MULTICORE & OUTPUT_ADVANCED) + debug(APIC, "CPU %x Sending IPI, vector: %x\n", apicId(), vector); + + InterruptCommandRegisterLow icrl{}; + icrl.vector = vector; + icrl.delivery_mode = (uint32_t)ipi_type; + icrl.destination_mode = (uint32_t)IPIDestinationMode::PHYSICAL; + icrl.level = (uint32_t)IPILevel::ASSERT; + icrl.trigger_mode = (uint32_t)IntTriggerMode::EDGE; + icrl.destination_shorthand = (uint32_t)dest_type; + + writeIcr(icrl, dest_type == IPIDestination::TARGET ? target : 0); + + if (wait_for_delivery && !((dest_type == IPIDestination::TARGET) && (target == apicId()))) + { + debug(APIC, "CPU %zx waiting until IPI to %zx has been delivered\n", SMP::currentCpuId(), target); + waitIpiDelivered(); + } +} + +void Apic::sendIPI(uint8_t vector, const Apic& target, bool wait_for_delivery) +{ + assert(isInitialized()); + assert(target.isInitialized()); + sendIPI(vector, IPIDestination::TARGET, target.apicId(), IPIType::FIXED, wait_for_delivery); +} + + +void XApic::writeRegisterImpl(ApicRegisterOffset offset, uint64_t v) +{ + assert(offset != ApicRegisterOffset::SELF_IPI); + + WithInterrupts i{false}; + if (offset == ApicRegisterOffset::INTERRUPT_COMMAND) + { + volatile uint32_t* icr_h = (uint32_t*)((char*)reg_vaddr_ + static_cast(ApicRegisterOffset::INTERRUPT_COMMAND_H)); + *icr_h = (v >> 32); + } + + volatile uint32_t* reg_addr = (uint32_t*)((char*)reg_vaddr_ + static_cast(offset)); + uint32_t v32 = v; + *reg_addr = v32; +} + +uint64_t XApic::readRegisterImpl(ApicRegisterOffset offset) +{ + assert(offset != ApicRegisterOffset::SELF_IPI); + + uint64_t v = 0; + + WithInterrupts i{false}; + + if (offset == ApicRegisterOffset::INTERRUPT_COMMAND) + { + uint32_t* icr_h = (uint32_t*)((char*)reg_vaddr_ + static_cast(ApicRegisterOffset::INTERRUPT_COMMAND_H)); + + v = (uint64_t)*icr_h << 32; + } + + volatile uint32_t* reg_addr = (uint32_t*)((char*)reg_vaddr_ + static_cast(offset)); + v |= *reg_addr; + return v; +} + +void XApic::writeIcr(InterruptCommandRegisterLow icr_l, uint32_t dest) +{ + InterruptCommandRegister icr{}; + icr.h.xapic_destination = dest; + icr.l = icr_l; + + WithInterrupts d(false); + writeRegister(icr); +} + +void* XApic::readMsrPhysAddr() +{ + auto apic_base = MSR::IA32_APIC_BASE::read(); + return (void*)((uintptr_t)apic_base.apic_base*PAGE_SIZE); +} + +bool XApic::apicSupported() +{ + return cpu_features.cpuHasFeature(CpuFeatures::X86Feature::APIC); +} + +Apic::ApicTimer::ApicTimer(Apic& apic) : + IrqDomain("APIC Timer", 1, this), + Device("APIC timer", &apic), + apic_(&apic) +{ +} + +bool Apic::ApicTimer::mask(irqnum_t irq, bool mask) +{ + assert(irq == 0); + WithInterrupts i{false}; + auto timer_reg = apic_->readRegister(); + + debug(APIC, "[Cpu %u] %s mask IRQ %zu = %u -> %u\n", apic_->apicId(), name().c_str(), irq, timer_reg.mask, mask); + + timer_reg.mask = mask; + masked_ = mask; + apic_->writeRegister(timer_reg); + return true; +} + +bool Apic::ApicTimer::ack(irqnum_t irq) +{ + debugAdvanced(APIC, "[Cpu %u] %s ack IRQ %zu\n", apic_->apicId(), name().c_str(), irq); + assert(irq == 0); + --pending_EOIs; + apic_->sendEOI(InterruptVector::APIC_TIMER); + return true; +} + +bool Apic::ApicTimer::isMasked(irqnum_t irq) +{ + assert(irq == 0); + return isMasked(); +} + +bool Apic::ApicTimer::isMasked() { return masked_; } + +ApicDriver::ApicDriver() : + BasicDeviceDriver("Local APIC driver") +{ +} + +ApicDriver &ApicDriver::instance() +{ + static ApicDriver i; + return i; +} + +void ApicDriver::doDeviceDetection() +{ + debug(APIC, "Device detection\n"); + cpuLocalInit(); +} + +void ApicDriver::cpuLocalInit() +{ + if (cpu_features.cpuHasFeature(CpuFeatures::APIC)) + { + debug(APIC, "Init cpu local apic\n"); + Apic::setIMCRMode(IMCRData::APIC_PASSTHROUGH); + + Apic::globalEnable(); + if (X2Apic::x2ApicSupported()) + { + X2Apic::enableX2ApicMode(); + } + else + { + XApic::setPhysicalAddress(XApic::readMsrPhysAddr()); + if (!XApic::virtualAddress()) + { + auto apic_vaddr = mmio_addr_allocator.alloc(PAGE_SIZE, PAGE_SIZE); + debug(APIC, "Allocated MMIO addr for APIC: %zx\n", apic_vaddr); + assert(apic_vaddr != (size_t)-1 && "Unable to allocate virtual page for APIC MMIO"); + XApic::mapAt(apic_vaddr); + } + } + + cpu_root_irq_domain_ = cpu_lapic; + cpu_lapic->init(); + + SMP::currentCpu().addSubDevice(*cpu_lapic); + bindDevice(*cpu_lapic); + } + else + { + debug(APIC, "Local APIC not available\n"); + } +} + +ApicTimerDriver::ApicTimerDriver() : + BasicDeviceDriver("APIC timer driver") +{ +} + +ApicTimerDriver& ApicTimerDriver::instance() +{ + static ApicTimerDriver i; + return i; +} + +void ApicTimerDriver::doDeviceDetection() +{ + cpuLocalInit(); +} + +void ApicTimerDriver::cpuLocalInit() +{ + if (cpu_features.cpuHasFeature(CpuFeatures::APIC) && cpu_lapic) + { + auto& timer = cpu_lapic->timer_interrupt_controller; + timer.setDeviceName(eastl::string("APIC ") + eastl::to_string(cpu_lapic->apicId()) + " timer"); + cpu_lapic->initTimer(); + + timer.irq() + .mapTo(*cpu_lapic, InterruptVector::APIC_TIMER) + .useHandler(int127_handler_APIC_timer); + + bindDevice(timer); + } + else + { + debug(APIC, "Local APIC timer not available\n"); + } +} diff --git a/arch/x86/common/source/ATADriver.cpp b/arch/x86/common/source/ATADriver.cpp index 2250018ff..063eb0b30 100644 --- a/arch/x86/common/source/ATADriver.cpp +++ b/arch/x86/common/source/ATADriver.cpp @@ -1,99 +1,120 @@ #include "ATADriver.h" +#include "ATACommands.h" #include "BDManager.h" #include "BDRequest.h" -#include "ArchInterrupts.h" -#include "8259.h" - +#include "IDEDriver.h" +#include "MasterBootRecord.h" #include "Scheduler.h" +#include "Thread.h" #include "kprintf.h" +#include "offsets.h" +#include "kstring.h" -#include "Thread.h" +#include "ArchInterrupts.h" + +#include "debug.h" #define TIMEOUT_WARNING() do { kprintfd("%s:%d: timeout. THIS MIGHT CAUSE SERIOUS TROUBLE!\n", __PRETTY_FUNCTION__, __LINE__); } while (0) -#define TIMEOUT_CHECK(CONDITION,BODY) jiffies = 0;\ - while((CONDITION) && jiffies++ < IO_TIMEOUT)\ - ArchInterrupts::yieldIfIFSet();\ - if(jiffies >= IO_TIMEOUT)\ - {\ - BODY;\ - } +#define TIMEOUT_CHECK(CONDITION, BODY) \ + jiffies = 0; \ + while ((CONDITION) && jiffies++ < IO_TIMEOUT) \ + ArchInterrupts::yieldIfIFSet(); \ + if (jiffies >= IO_TIMEOUT) \ + { \ + BODY; \ + } -ATADriver::ATADriver( uint16 baseport, uint16 getdrive, uint16 irqnum ) : lock_("ATADriver::lock_") +ATADrive::ATADrive(IDEControllerChannel& ide_controller, uint16 drive_num) : + BDDriver(ide_controller.isaIrqNumber()), + Device(eastl::string("ATA disk ") + eastl::to_string(drive_num)), + controller(ide_controller), + drive_num(drive_num), + irq_domain(eastl::string("ATA disk ") + eastl::to_string(drive_num)), + lock_("ATADriver::lock_") { - debug(ATA_DRIVER, "ctor: Entered with irgnum %d and baseport %d!!\n", irqnum, baseport); + // Disable irq to ensure this part of the interrupt tree won't be used while we map a new interrupt + ArchInterrupts::disableIRQ(controller.irq()); + irq_domain.irq() + .mapTo(controller) + .useHandler([this]() { serviceIRQ(); }); - jiffies = 0; - port = baseport; - drive= (getdrive == 0 ? 0xA0 : 0xB0); + controller.selectDrive(drive_num); - debug(ATA_DRIVER, "ctor: Requesting disk geometry !!\n"); + IDEControllerChannel::ControlRegister::DeviceControl dc{}; + dc.interrupt_disable = 0; + controller.control_regs[IDEControllerChannel::ControlRegister::DEVICE_CONTROL].write(dc); - outportbp (port + 6, drive); // Get first drive - outportbp (port + 7, 0xEC); // Get drive info data - TIMEOUT_CHECK(inportbp(port + 7) != 0x58,TIMEOUT_WARNING(); return;); + controller.io_regs[IDEControllerChannel::IoRegister::COMMAND].write( + ATACommand::PIO::IDENTIFY_DEVICE); + controller.waitDataReady(); + eastl::array identify{}; + pioReadData(identify); - uint16 dd[256]; + if (ATA_DRIVER & OUTPUT_ADVANCED) + printIdentifyInfo(identify); - for (uint32 dd_off = 0; dd_off != 256; dd_off++) // Read "sector" 512 b - dd [dd_off] = inportw ( port ); + lba = identify[49] & (1 << 9); + numsec = *(uint32_t*)&identify[60]; - debug(ATA_DRIVER, "max. original PIO support: %x, PIO3 support: %x, PIO4 support: %x\n", (dd[51] >> 8), (dd[64] & 0x1) != 0, (dd[64] & 0x2) != 0); + HPC = identify[3]; + SPT = identify[6]; + uint32 CYLS = identify[1]; - debug(ATA_DRIVER, "ctor: Disk geometry read !!\n"); + if (!numsec) + numsec = CYLS * HPC * SPT; - HPC = dd[3]; - SPT = dd[6]; - uint32 CYLS = dd[1]; - numsec = CYLS * HPC * SPT; + uint32_t logical_sector_size = *(uint32_t*)&identify[117]; + sector_word_size = logical_sector_size ? logical_sector_size : 256; - bool interrupt_context = ArchInterrupts::disableInterrupts(); - ArchInterrupts::enableInterrupts(); + debug(ATA_DRIVER, "Using LBA: %u, # sectors: %u, sector size: %zu\n", lba, numsec, + sector_word_size * sizeof(uint16_t)); - enableIRQ( irqnum ); - if( irqnum > 8 ) - { - enableIRQ( 2 ); // cascade - } + // Clear pending interrupts by reading status register + [[maybe_unused]] auto status = controller.io_regs[IDEControllerChannel::IoRegister::STATUS].read(); - testIRQ( ); - if( !interrupt_context ) - ArchInterrupts::disableInterrupts(); - irq = irqnum; - debug(ATA_DRIVER, "ctor: mode: %d !!\n", mode ); + debug(ATA_DRIVER, "Enabling interrupts for ATA IRQ check\n"); + ArchInterrupts::enableIRQ(irq_domain.irq()); - request_list_ = 0; - request_list_tail_ = 0; + testIRQ(); - debug(ATA_DRIVER, "ctor: Driver created !!\n"); - return; + debug(ATA_DRIVER, "ctor: Using ATA mode: %d !!\n", (int)mode); + debug(ATA_DRIVER, "ctor: Drive created !!\n"); } -void ATADriver::testIRQ( ) +void ATADrive::testIRQ() { - mode = BD_PIO; + mode = BD_ATA_MODE::BD_PIO; + + BDManager::instance().probeIRQ.test_and_set(); + { + WithInterrupts intr(true); - BDManager::getInstance()->probeIRQ = true; - readSector( 0, 1, 0 ); + controller.selectDrive(drive_num); + char buf[getSectorSize()]; + readSector(0, 1, buf); - TIMEOUT_CHECK(BDManager::getInstance()->probeIRQ,mode = BD_PIO_NO_IRQ;); + debug(ATA_DRIVER, "Waiting for ATA IRQ\n"); + TIMEOUT_CHECK(BDManager::instance().probeIRQ.test_and_set(),mode = BD_ATA_MODE::BD_PIO_NO_IRQ;); + controller.reset(); + } } -int32 ATADriver::rawReadSector ( uint32 start_sector, uint32 num_sectors, void *buffer ) +int32 ATADrive::rawReadSector(uint32 start_sector, uint32 num_sectors, void* buffer) { - BD_ATA_MODES old_mode = mode; - mode = BD_PIO_NO_IRQ; - uint32 result = readSector ( start_sector, num_sectors, buffer ); + BD_ATA_MODE old_mode = mode; + mode = BD_ATA_MODE::BD_PIO_NO_IRQ; + uint32 result = readSector(start_sector, num_sectors, buffer); mode = old_mode; return result; } -int32 ATADriver::selectSector(uint32 start_sector, uint32 num_sectors) +int32 ATADrive::selectSector(uint32 start_sector, uint32 num_sectors) { - /* Wait for drive to clear BUSY */ - TIMEOUT_CHECK(inportbp(port + 7) & 0x80,TIMEOUT_WARNING(); return -1;); + if (!controller.waitNotBusy()) + return -1; //LBA: linear base address of the block //CYL: value of the cylinder CHS coordinate @@ -112,271 +133,502 @@ int32 ATADriver::selectSector(uint32 start_sector, uint32 num_sectors) uint8 high = cyls >> 8; uint8 lo = cyls & 0x00FF; - //debug(ATA_DRIVER, "readSector:(drive | head): %d, num_sectors: %d, sect: %d, lo: %d, high: %d!!\n",(drive | head),num_sectors,sect,lo,high); - outportbp(port + 6, (drive | head)); // drive and head selection - outportbp(port + 2, num_sectors); // number of sectors to read - outportbp(port + 3, sect); // starting sector - outportbp(port + 4, lo); // cylinder low - outportbp(port + 5, high); // cylinder high + IDEControllerChannel::IoRegister::DriveHead dh = + controller.io_regs[IDEControllerChannel::IoRegister::DRIVE_HEAD].read(); + dh.drive_num = drive_num; + dh.use_lba = lba; + dh.always_set_0 = 1; + dh.always_set_1 = 1; + + if (lba) + { + dh.lba_24_27 = (start_sector >> 24) & 0xF; + } + else + { + dh.chs_head_0_3 = head; + } + + controller.io_regs[IDEControllerChannel::IoRegister::DRIVE_HEAD].write(dh); + + controller.io_regs[IDEControllerChannel::IoRegister::SECTOR_COUNT].write(num_sectors); + + if (lba) + { + controller.io_regs[IDEControllerChannel::IoRegister::LBA_LOW].write(start_sector & + 0xFF); + controller.io_regs[IDEControllerChannel::IoRegister::LBA_MID].write( + (start_sector >> 8) & 0xFF); + controller.io_regs[IDEControllerChannel::IoRegister::LBA_HIGH].write( + (start_sector >> 16) & 0xFF); + } + else + { + controller.io_regs[IDEControllerChannel::IoRegister::SECTOR_NUMBER].write(sect); + controller.io_regs[IDEControllerChannel::IoRegister::CYLINDER_LOW].write(lo); + controller.io_regs[IDEControllerChannel::IoRegister::CYLINDER_HIGH].write(high); + } - /* Wait for drive to set DRDY */ - TIMEOUT_CHECK((!inportbp(port + 7)) & 0x40,TIMEOUT_WARNING(); return -1;); + if (!controller.waitDriveReady()) + return -1; return 0; } -int32 ATADriver::readSector ( uint32 start_sector, uint32 num_sectors, void *buffer ) +void ATADrive::pioReadData(eastl::span buffer) { + asm volatile("cld\n" // Ensure correct direction (low to high) + "rep insw\n" :: + "D"(buffer.data()), // RDI + "c"(buffer.size()), // RCX + "d"(controller.io_regs[IDEControllerChannel::IoRegister::DATA].port)); // RDX + + if (ATA_DRIVER & OUTPUT_ADVANCED) + { + auto chks = checksum((uint32_t*)buffer.data(), buffer.size()*sizeof(*buffer.data())); + debug(ATA_DRIVER, "PIO read data [%p, %p), size: %zx, checksum: %x\n", + buffer.data(), buffer.data() + buffer.size(), buffer.size()*sizeof(*buffer.data()), chks); + } +} + +void ATADrive::pioWriteData(eastl::span buffer) +{ + if (ATA_DRIVER & OUTPUT_ADVANCED) + { + auto chks = checksum((uint32_t*)buffer.data(), buffer.size()*sizeof(*buffer.data())); + debug(ATA_DRIVER, "PIO write data [%p, %p), size: %zx, checksum: %x\n", + buffer.data(), buffer.data() + buffer.size(), buffer.size()*sizeof(*buffer.data()), chks); + } + + // Don't use rep outsw here because of required delay after port write + for (const uint16_t& word : buffer) + { + controller.io_regs[IDEControllerChannel::IoRegister::DATA].write(word); + } +} + +int32 ATADrive::readSector(uint32 start_sector, uint32 num_sectors, void *buffer) +{ + debug(ATA_DRIVER, "read %u sectors [%x, %x) = [%x, %x) into buffer %p\n", + num_sectors, start_sector, start_sector + num_sectors, start_sector*getSectorSize(), (start_sector + num_sectors)*getSectorSize(), buffer); + + controller.selectDrive(drive_num); + assert(buffer || (start_sector == 0 && num_sectors == 1)); if (selectSector(start_sector, num_sectors) != 0) return -1; - for (int i = 0;; ++i) - { - /* Write the command code to the command register */ - outportbp(port + 7, 0x20); // command + if (!controller.waitNotBusy()) + return -1; - if (mode != BD_PIO_NO_IRQ) + controller.sendCommand(ATACommand::PIO::READ_SECTORS); + + if (mode != BD_ATA_MODE::BD_PIO_NO_IRQ) return 0; - jiffies = 0; - while (inportbp(port + 7) != 0x58 && jiffies++ < IO_TIMEOUT) - ArchInterrupts::yieldIfIFSet(); - if (jiffies >= IO_TIMEOUT) - { - if (i == 3) - { - TIMEOUT_WARNING(); - return -1; - } - } - else - break; - } + if (!controller.waitDataReady()) + return -1; if (buffer) { - uint32 counter; - uint16 *word_buff = (uint16 *) buffer; - for (counter = 0; counter != (256*num_sectors); counter++) // read sector - word_buff [counter] = inportw ( port ); + pioReadData({static_cast(buffer), sector_word_size*num_sectors}); } - /* Wait for drive to clear BUSY */ - TIMEOUT_CHECK(inportbp(port + 7) & 0x80,TIMEOUT_WARNING(); return -1;); - //debug(ATA_DRIVER, "readSector:Read successfull !!\n"); - return 0; + if (!controller.waitNotBusy()) + return -1; + + return 0; } -int32 ATADriver::writeSector ( uint32 start_sector, uint32 num_sectors, void * buffer ) +int32 ATADrive::writeSector(uint32 start_sector, uint32 num_sectors, void * buffer) { + debug(ATA_DRIVER, "write %u sectors [%x, %x) = [%x, %x) from buffer %p\n", + num_sectors, start_sector, start_sector + num_sectors, start_sector*getSectorSize(), (start_sector + num_sectors)*getSectorSize(), buffer); assert(buffer); + + controller.selectDrive(drive_num); + if (selectSector(start_sector, num_sectors) != 0) return -1; - uint16 *word_buff = (uint16 *) buffer; + controller.sendCommand(ATACommand::PIO::WRITE_SECTORS); - /* Write the command code to the command register */ - outportbp( port + 7, 0x30 ); // command + if (!controller.waitDataReady()) + return -1; - TIMEOUT_CHECK(inportbp(port + 7) != 0x58,TIMEOUT_WARNING(); return -1;); + uint32 count = (sector_word_size*num_sectors); + if( mode != BD_ATA_MODE::BD_PIO_NO_IRQ ) + count = sector_word_size; + pioWriteData({static_cast(buffer), count}); - uint32 count2 = (256*num_sectors); - if( mode != BD_PIO_NO_IRQ ) - count2 = 256; + if (!controller.waitNotBusy()) + return -1; - uint32 counter; - for (counter = 0; counter != count2; counter++) - outportw ( port, word_buff [counter] ); - - /* Wait for drive to clear BUSY */ - TIMEOUT_CHECK(inportbp(port + 7) & 0x80,TIMEOUT_WARNING(); return -1;); + controller.sendCommand(ATACommand::Other::FLUSH_CACHE); - /* Write flush code to the command register */ - outportbp (port + 7, 0xE7); - - /* Wait for drive to clear BUSY */ - TIMEOUT_CHECK(inportbp(port + 7) & 0x80,TIMEOUT_WARNING(); return -1;); + if (!controller.waitNotBusy()) + return -1; return 0; } -uint32 ATADriver::addRequest( BDRequest *br ) +uint32 ATADrive::addRequest(BDRequest* br) { ScopeLock lock(lock_); // this lock might serialize stuff too much... bool interrupt_context = false; - debug(ATA_DRIVER, "addRequest %d!\n", br->getCmd() ); - if( mode != BD_PIO_NO_IRQ ) + debug(ATA_DRIVER, "addRequest %zu, cmd = %d!\n", br->request_id, (int)br->getCmd()); + if (mode != BD_ATA_MODE::BD_PIO_NO_IRQ) { interrupt_context = ArchInterrupts::disableInterrupts(); - //Add request to the list protected by the cli - if( request_list_ == 0 ) - request_list_ = request_list_tail_ = br; - else - { - request_list_tail_->setNextRequest(br) ; - request_list_tail_ = br; - } + request_list_.pushFront(br); } int32 res = -1; - - switch( br->getCmd() ) + + switch(br->getCmd()) { - case BDRequest::BD_READ: - res = readSector( br->getStartBlock(), br->getNumBlocks(), br->getBuffer() ); + case BDRequest::BD_CMD::BD_READ: + res = readSector(br->getStartBlock(), br->getNumBlocks(), br->getBuffer()); break; - case BDRequest::BD_WRITE: - res = writeSector( br->getStartBlock(), br->getNumBlocks(), br->getBuffer() ); + case BDRequest::BD_CMD::BD_WRITE: + res = writeSector(br->getStartBlock(), br->getNumBlocks(), br->getBuffer()); break; - default: + default: res = -1; break; } - - if( res != 0 ) + + if (res != 0) { - br->setStatus( BDRequest::BD_ERROR ); + br->setStatus(BDRequest::BD_RESULT::BD_ERROR); debug(ATA_DRIVER, "Got out on error !!\n"); - if( interrupt_context ) - ArchInterrupts::enableInterrupts(); + if (interrupt_context) + ArchInterrupts::enableInterrupts(); return 0; } - if( mode == BD_PIO_NO_IRQ ) + if (mode == BD_ATA_MODE::BD_PIO_NO_IRQ) { - debug(ATA_DRIVER, "addRequest:No IRQ operation !!\n"); - br->setStatus( BDRequest::BD_DONE ); + debug(ATA_DRIVER, "addRequest: No IRQ operation !!\n"); + br->setStatus(BDRequest::BD_RESULT::BD_DONE); return 0; } if (currentThread) { if(interrupt_context) - ArchInterrupts::enableInterrupts(); + { + ArchInterrupts::enableInterrupts(); + } + jiffies = 0; - while (br->getStatus() == BDRequest::BD_QUEUED && jiffies++ < IO_TIMEOUT*10); + + debugAdvanced(ATA_DRIVER, "addRequest: Waiting for request %zu to be finished\n", br->request_id); + while (br->getStatus() == BDRequest::BD_RESULT::BD_QUEUED && jiffies++ < IO_TIMEOUT*10); + if (jiffies >= IO_TIMEOUT*10) - TIMEOUT_WARNING(); - if (br->getStatus() == BDRequest::BD_QUEUED) { + [[maybe_unused]] auto data_ready = controller.waitDataReady(); + + TIMEOUT_WARNING(); + auto status = controller.control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + auto error = controller.io_regs[IDEControllerChannel::IoRegister::ERROR].read(); + auto lba_l = controller.io_regs[IDEControllerChannel::IoRegister::LBA_LOW].read(); + auto lba_m = controller.io_regs[IDEControllerChannel::IoRegister::LBA_MID].read(); + auto lba_h = controller.io_regs[IDEControllerChannel::IoRegister::LBA_HIGH].read(); + + debug(ATA_DRIVER, "addRequest: id: %zu, Device status: %x, error: %x, lba_l: %x, lba_m: %x, lba_h: %x\n", br->request_id, status.u8, error.u8, lba_l, lba_m, lba_h); + } + + if (br->getStatus() == BDRequest::BD_RESULT::BD_QUEUED) + { + debug(ATA_DRIVER, "addRequest: Request %zu is still pending, going to sleep\n", br->request_id); ArchInterrupts::disableInterrupts(); - if (br->getStatus() == BDRequest::BD_QUEUED) + if (br->getStatus() == BDRequest::BD_RESULT::BD_QUEUED) { - currentThread->setState(Sleeping); + currentThread->setState(Thread::Sleeping); ArchInterrupts::enableInterrupts(); Scheduler::instance()->yield(); // this is necessary! setting state to sleep and continuing to run is a BAD idea + debug(ATA_DRIVER, "addRequest: Woke up after sleeping on request %zu, state = %d\n", br->request_id, (int)br->getStatus()); + assert(br->getStatus() != BDRequest::BD_RESULT::BD_QUEUED && "ATA request still not done after waking up from sleep!"); } ArchInterrupts::enableInterrupts(); } } + debugAdvanced(ATA_DRIVER, "addRequest: done\n"); return 0; } -bool ATADriver::waitForController( bool resetIfFailed = true ) +void ATADrive::serviceIRQ() { - uint32 jiffies = 0; - while( inportbp( port + 7 ) != 0x58 && jiffies++ < IO_TIMEOUT) - ArchInterrupts::yieldIfIFSet(); + // Need to read status register to acknowledge and unblock interrupts + auto status = controller.io_regs[IDEControllerChannel::IoRegister::STATUS].read(); + auto error = controller.io_regs[IDEControllerChannel::IoRegister::ERROR].read(); + auto lba_l = controller.io_regs[IDEControllerChannel::IoRegister::LBA_LOW].read(); + auto lba_m = controller.io_regs[IDEControllerChannel::IoRegister::LBA_MID].read(); + auto lba_h = controller.io_regs[IDEControllerChannel::IoRegister::LBA_HIGH].read(); - if(jiffies >= IO_TIMEOUT ) - { - debug(ATA_DRIVER, "waitForController: controler still not ready\n"); - if( resetIfFailed ) - { - debug(ATA_DRIVER, "waitForController: reseting\n"); - outportbp( port + 0x206, 0x04 ); - outportbp( port + 0x206, 0x00 ); // RESET - } - return false; - } - return true; -} + debugAdvanced(ATA_DRIVER, "serviceIRQ: Device status: %x, error: %x, lba_l: %x, lba_m: %x, lba_h: %x\n", status.u8, error.u8, lba_l, lba_m, lba_h); -void ATADriver::nextRequest(BDRequest* br) -{ - if(br->getThread()) - br->getThread()->setState(Running); - request_list_ = br->getNextRequest(); -} + BDManager::instance().probeIRQ.clear(); -void ATADriver::serviceIRQ() -{ - if( mode == BD_PIO_NO_IRQ ) - return; + if (mode == BD_ATA_MODE::BD_PIO_NO_IRQ) + return; - if( request_list_ == 0 ) - { - debug(ATA_DRIVER, "serviceIRQ: IRQ without request!!\n"); - outportbp( port + 0x206, 0x04 ); - outportbp( port + 0x206, 0x00 ); // RESET COTROLLER - debug(ATA_DRIVER, "serviceIRQ: Reset controller!!\n"); - return; // not my interrupt + BDRequest* br = request_list_.peekBack(); + + if (br == nullptr) + { + debug(ATA_DRIVER, + "serviceIRQ: IRQ without request! Device status: %x, error: %x, lba_l: %x, " + "lba_m: %x, lba_h: %x\n", + status.u8, error.u8, lba_l, lba_m, lba_h); + if (status.error || status.drive_fault_error) + { + debugAlways(ATA_DRIVER, "serviceIRQ: Device error, reset device! Status: %x, Error: %x\n", status.u8, error.u8); + controller.reset(); + } + return; // not my interrupt } - BDRequest* br = request_list_; - debug(ATA_DRIVER, "serviceIRQ: Found active request!!\n"); + Thread* requesting_thread = br->getThread(); + + debugAdvanced(ATA_DRIVER, "serviceIRQ: Found active request %zu!!\n", br->request_id); + assert(br); uint16* word_buff = (uint16*) br->getBuffer(); - uint32 counter; - uint32 blocks_done = br->getBlocksDone(); + size_t blocks_done = br->getBlocksDone(); + size_t bytes_requested = br->getNumBlocks()*sector_word_size*WORD_SIZE; + + // TODO: target thread may not yet be sleeping (this irq handler and section with disabled interrupts in addRequest may run simultaneously if running on other cpu core) - if( br->getCmd() == BDRequest::BD_READ ) + if (br->getCmd() == BDRequest::BD_CMD::BD_READ) { - if( !waitForController() ) + size_t bytes_done = blocks_done*sector_word_size*WORD_SIZE; + uint16_t* buf_ptr = word_buff + blocks_done*sector_word_size; + debugAdvanced(ATA_DRIVER, "serviceIRQ: Handling read request %zu, [%zu/%zu], buffer: %p\n", br->request_id, bytes_done, bytes_requested, word_buff); + // This IRQ handler may be run in the context of any arbitrary thread, so we must not attempt to access userspace + assert((size_t)word_buff >= USER_BREAK); + if (!controller.waitDataReady()) { - br->setStatus( BDRequest::BD_ERROR ); - nextRequest(br); + assert(request_list_.popBack() == br); + br->setStatus(BDRequest::BD_RESULT::BD_ERROR); // br may be deallocated as soon as status is set to error + debug(ATA_DRIVER, "serviceIRQ: Error while waiting for controller\n"); + if (requesting_thread) + requesting_thread->setState(Thread::Running); return; } - for(counter = blocks_done * 256; counter!=(blocks_done + 1) * 256; counter++ ) - word_buff [counter] = inportw ( port ); + pioReadData({buf_ptr, sector_word_size}); blocks_done++; - br->setBlocksDone( blocks_done ); + br->setBlocksDone(blocks_done); - if( blocks_done == br->getNumBlocks() ) + if (blocks_done == br->getNumBlocks()) { - br->setStatus( BDRequest::BD_DONE ); - nextRequest(br); + assert(request_list_.popBack() == br); + br->setStatus(BDRequest::BD_RESULT::BD_DONE); // br may be deallocated as soon as status is set to done + debugAdvanced(ATA_DRIVER, "serviceIRQ: Read finished [%zu/%zu], buffer: %p\n", bytes_requested, bytes_requested, word_buff); + if (requesting_thread) + requesting_thread->setState(Thread::Running); } } - else if( br->getCmd() == BDRequest::BD_WRITE ) + else if (br->getCmd() == BDRequest::BD_CMD::BD_WRITE) { blocks_done++; - if( blocks_done == br->getNumBlocks() ) + br->setBlocksDone(blocks_done); + + size_t bytes_done = blocks_done*sector_word_size*WORD_SIZE; + uint16_t* buf_ptr = word_buff + blocks_done*sector_word_size; + debugAdvanced(ATA_DRIVER, "serviceIRQ: Handling write request %zu, [%zu/%zu], buffer: %p\n", br->request_id, bytes_done, bytes_requested, word_buff); + + if (blocks_done == br->getNumBlocks()) { - debug(ATA_DRIVER, "serviceIRQ:All done!!\n"); - br->setStatus( BDRequest::BD_DONE ); - debug(ATA_DRIVER, "serviceIRQ:Waking up thread!!\n"); - nextRequest(br); + assert(request_list_.popBack() == br); + debugAdvanced(ATA_DRIVER, "serviceIRQ: Write finished [%zu/%zu], buffer: %p\n", bytes_requested, bytes_requested, word_buff); + br->setStatus( BDRequest::BD_RESULT::BD_DONE); // br may be deallocated as soon as status is set to done + if (requesting_thread) + requesting_thread->setState(Thread::Running); } else { - if( !waitForController() ) + // This IRQ handler may be run in the context of any arbitrary thread, so we must not attempt to access userspace + assert((size_t)word_buff >= USER_BREAK); + if (!controller.waitDataReady()) { - br->setStatus( BDRequest::BD_ERROR ); - nextRequest(br); + assert(request_list_.popBack() == br); + br->setStatus(BDRequest::BD_RESULT::BD_ERROR); // br may be deallocated as soon as status is set to error + if (requesting_thread) + requesting_thread->setState(Thread::Running); return; } - - for(counter = blocks_done*256; counter != (blocks_done + 1) * 256; counter++ ) - outportw ( port, word_buff [counter] ); - br->setBlocksDone( blocks_done ); + pioWriteData({buf_ptr, sector_word_size}); } } else { - blocks_done = br->getNumBlocks(); - br->setStatus( BDRequest::BD_ERROR ); - nextRequest(br); + assert(request_list_.popBack() == br); + br->setStatus(BDRequest::BD_RESULT::BD_ERROR); // br may be deallocated as soon as status is set to error + if (requesting_thread) + requesting_thread->setState(Thread::Running); } - debug(ATA_DRIVER, "serviceIRQ:Request handled!!\n"); + debugAdvanced(ATA_DRIVER, "serviceIRQ: Request %zu handled!!\n", br->request_id); +} + + +void ATADrive::printIdentifyInfo(eastl::span id) const +{ + uint16_t serialnum[11]{}; + uint16_t firmware_rev[5]{}; + uint16_t model_number[21]{}; + uint32_t num_sectors_28bit = 0; + uint64_t num_logical_sectors = 0; + // default 256 words / 512 bytes if 0 + uint32_t logical_sector_size = 0; + memcpy(serialnum, &id[10], 20); + memcpy(firmware_rev, &id[23], 8); + memcpy(model_number, &id[27], 40); + memcpy(&num_sectors_28bit, &id[60], 4); + memcpy(&num_logical_sectors, &id[100], 8); + memcpy(&logical_sector_size, &id[117], 4); + for (auto& x : serialnum) + x = __builtin_bswap16(x); + for (auto& x : firmware_rev) + x = __builtin_bswap16(x); + for (auto& x : model_number) + x = __builtin_bswap16(x); + + debug(ATA_DRIVER, "Device serial number: %s\n", (char*)serialnum); + debug(ATA_DRIVER, "Firmware revision: %s\n", (char*)firmware_rev); + debug(ATA_DRIVER, "Model number: %s\n", (char*)model_number); + debug(ATA_DRIVER, "Cylinder: %u, Head: %u, Sector: %u, C*H*S = %u\n", id[1], + id[3], id[6], id[1] * id[3] * id[6]); + debug(ATA_DRIVER, "DRQ data block size: %u\n", (id[49] & 0xFF)); + debug(ATA_DRIVER, "Capabilities: LBA %u, DMA %u, IORDY %u, IORDY DISABLE: %u\n", + !!(id[49] & (1 << 9)), !!(id[49] & (1 << 8)), + !!(id[49] & (1 << 11)), !!(id[49] & (1 << 10))); + debug(ATA_DRIVER, "#Sectors (28 bit): %u\n", *(uint32_t*)&num_sectors_28bit); + debug(ATA_DRIVER, + "Read/write multiple supported: %u, optimal multiple sector num: %u\n", + !!(id[59] & (1 << 8)), (id[59] & 0xFF)); + debug(ATA_DRIVER, "Multiword DMA support: mode 0: %u, mode 1: %u, mode 2: %u\n", + !!(id[63] & (1 << 0)), !!(id[63] & (1 << 1)), + !!(id[63] & (1 << 2))); + debug(ATA_DRIVER, "PIO support: max original: %u, PIO3: %u, PIO4: %u\n", (id[51] >> 8), !!(id[64] & (1 << 0)), + !!(id[64] & (1 << 1))); + debug(ATA_DRIVER, "Extended num of user addressable sectors supported: %u\n", + !!(id[69] & (1 << 3))); + debug(ATA_DRIVER, "Max queue depth: %u\n", (id[75] & 0b11111)); + debug(ATA_DRIVER, + "Major version: ATA/ATAPI5: %u, ATA/ATAPI6: %u, ATA/ATAPI7: %u, " + "ATA8-ACS: %u, ACS-2: %u, ACS-3: %u\n", + !!(id[80] & (1 << 5)), !!(id[80] & (1 << 6)), + !!(id[80] & (1 << 7)), !!(id[80] & (1 << 8)), + !!(id[80] & (1 << 9)), !!(id[80] & (1 << 10))); + debug(ATA_DRIVER, + "Command/feature support: SMART: %u, Security: %u, Power " + "management: %u, PACKET: %u, volatile write cache: %u, read " + "look-ahead: %u, DEVICE RESET: %u, WRITE BUFFER: %u, READ " + "BUFFER: %u, NOP: %u\n", + !!(id[82] & (1 << 0)), !!(id[82] & (1 << 1)), + !!(id[82] & (1 << 3)), !!(id[82] & (1 << 4)), + !!(id[82] & (1 << 5)), !!(id[82] & (1 << 6)), + !!(id[82] & (1 << 9)), !!(id[82] & (1 << 12)), + !!(id[82] & (1 << 13)), !!(id[82] & (1 << 14))); + debug(ATA_DRIVER, "48-bit address support: %u\n", !!(id[83] & (1 << 10))); + debug(ATA_DRIVER, + "UDMA support: m0: %u, m1: %u, m2: %u, m3: %u, m4: %u, m5: %u, " + "m6: %u\n", + !!(id[88] & (1 << 0)), !!(id[88] & (1 << 1)), + !!(id[88] & (1 << 2)), !!(id[88] & (1 << 3)), + !!(id[88] & (1 << 4)), !!(id[88] & (1 << 5)), + !!(id[88] & (1 << 6))); + debug(ATA_DRIVER, + "UDMA active: m0: %u, m1: %u, m2: %u, m3: %u, m4: %u, m5: %u, " + "m6: %u\n", + !!(id[88] & (1 << 8)), !!(id[88] & (1 << 9)), + !!(id[88] & (1 << 10)), !!(id[88] & (1 << 11)), + !!(id[88] & (1 << 12)), !!(id[88] & (1 << 13)), + !!(id[88] & (1 << 14))); + debug(ATA_DRIVER, "#Sectors: %" PRIu64 "\n", *(uint64_t*)&num_logical_sectors); + if (!(id[106] & (1 << 15)) && (id[106] & (1 << 14))) + { + debug(ATA_DRIVER, "Physical sector size: 2^%u*logical sectors\n", + (id[106] & 0xF)); + debug(ATA_DRIVER, + "Logical sector size > 512: %u, multiple logical sectors per " + "phys sectors: %u\n", + !!(id[106] & (1 << 12)), !!(id[106] & (1 << 13))); + debug(ATA_DRIVER, "Logical sector size (words): %u\n", + *(uint32_t*)&logical_sector_size); + } + + debug(ATA_DRIVER, "Transport type: %u (0 = parallel, 1 = serial)\n", + (id[222] & (0xF << 12))); + if ((id[222] & (0xF << 12)) >> 12 == 0) + { + debug(ATA_DRIVER, "Transport version: ATA8-APT: %u, ATA/ATAPI-7: %u\n", + !!(id[222] & (1 << 0)), !!(id[222] & (1 << 1))); + } + if ((id[222] & (0xF << 12)) >> 12 == 1) + { + debug(ATA_DRIVER, + "Transport version: ATA8-AST: %u, SATA 1.0a: %u, SATA II " + "Extensions: %u, SATA 2.5: %u, SATA 2.6: %u, SATA 3.0: %u, " + "SATA: 3.1: %u\n", + !!(id[222] & (1 << 0)), !!(id[222] & (1 << 1)), + !!(id[222] & (1 << 2)), !!(id[222] & (1 << 3)), + !!(id[222] & (1 << 4)), !!(id[222] & (1 << 5)), + !!(id[222] & (1 << 6))); + } + debug(ATA_DRIVER, "Integrity word: %x, validity indicator: %x\n", + (id[255] & (0xFF << 8)), (id[255] & 0xFF)); +} + +PATADeviceDriver::PATADeviceDriver() : + BasicDeviceDriver("PATA device driver") +{ +} + +PATADeviceDriver& PATADeviceDriver::instance() +{ + static PATADeviceDriver instance_; + return instance_; +} + +bool PATADeviceDriver::probe(const IDEDeviceDescription& descr) +{ + auto [controller, drive_num, signature] = descr; + if (signature == PATA_DRIVE_SIGNATURE) + { + debug(ATA_DRIVER, "Device description matches PATA device\n"); + + eastl::string disk_name{"ide"}; + // idea = first disk of primary controller + // ideb = second disk of primary controller + // idec = first disk of secondary controller + // ... + disk_name += 'a' + controller->controller_id * 2 + drive_num; + + debug(ATA_DRIVER, "Create block device %s\n", disk_name.c_str()); + auto drv = new ATADrive(*controller, drive_num); + bindDevice(*drv); + auto* bdv = new BDVirtualDevice(drv, 0, drv->getNumSectors(), + drv->getSectorSize(), disk_name.c_str(), true); + + BDManager::instance().addVirtualDevice(bdv); + + detectMBRPartitions(bdv, drv, 0, drv->SPT, disk_name.c_str()); + + return true; + } + + debug(ATA_DRIVER, "IDE device not compatible with PATA device driver\n"); + return false; } diff --git a/arch/x86/common/source/ArchMemory.cpp b/arch/x86/common/source/ArchMemory.cpp new file mode 100644 index 000000000..ddd0e825e --- /dev/null +++ b/arch/x86/common/source/ArchMemory.cpp @@ -0,0 +1,55 @@ +#include "ArchMemory.h" + +#include "SMP.h" +#include "offsets.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMulticore.h" + +#include "debug.h" + +// Common functionality for x86 memory management (x86_64 / x86_32 / x86_32_pae) + +pointer ArchMemory::getIdentAddressOfPPN(ppn_t ppn, size_t page_size /* optional */) +{ + return IDENT_MAPPING_START + (ppn * page_size); +} + +pointer ArchMemory::getIdentAddress(size_t address) +{ + return IDENT_MAPPING_START | (address); +} + +size_t ArchMemory::getKernelPagingStructureRootPhys() +{ + return (size_t)VIRTUAL_TO_PHYSICAL_BOOT(getKernelPagingStructureRootVirt()); +} + +size_t ArchMemory::getValueForCR3() const +{ + return getPagingStructureRootPhys(); +} + +void ArchMemory::loadPagingStructureRoot(size_t cr3_value) +{ + __asm__ __volatile__("mov %[cr3_value], %%cr3\n" + ::[cr3_value]"r"(cr3_value)); +} + +void ArchMemory::flushLocalTranslationCaches(size_t addr) +{ + if(A_MEMORY & OUTPUT_ADVANCED) + { + debug(A_MEMORY, "CPU %zx flushing translation caches for address %zx\n", SMP::currentCpuId(), addr); + } + __asm__ __volatile__("invlpg %[addr]\n" + ::[addr]"m"(*(char*)addr)); +} + +void ArchMemory::flushAllTranslationCaches(size_t addr) +{ + flushLocalTranslationCaches(addr); + + SMP::callOnOtherCpus([addr]{ flushLocalTranslationCaches(addr); }); +} diff --git a/arch/x86/common/source/ArchMulticore.cpp b/arch/x86/common/source/ArchMulticore.cpp new file mode 100644 index 000000000..f671be7d5 --- /dev/null +++ b/arch/x86/common/source/ArchMulticore.cpp @@ -0,0 +1,151 @@ +#include "ArchMulticore.h" + +#include "Allocator.h" +#include "InterruptUtils.h" +#include "ProgrammableIntervalTimer.h" +#include "SMP.h" +#include "SystemState.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" + +#include "EASTL/atomic.h" +#include "EASTL/finally.h" + +#include "debug.h" + +extern eastl::atomic ap_started; + +ArchCpu::ArchCpu() : + lapic(cpu_lapic) +{ + setId(lapic->isInitialized() ? lapic->apicId() : 0); + debug(A_MULTICORE, "Initializing ArchCpu %zu\n", id()); + SMP::addCpuToList(this); + root_domain_ptr = &cpu_root_irq_domain_; +} + +IrqDomain& ArchCpu::rootIrqDomain() +{ + assert(*root_domain_ptr); + return **root_domain_ptr; +} + +void ArchMulticore::notifyMessageAvailable(ArchCpu& cpu) +{ + cpu_lapic->sendIPI(MESSAGE_INT_VECTOR, *cpu.lapic, true); +} + +void ArchMulticore::sendFunctionCallMessage(ArchCpu& cpu, RemoteFunctionCallMessage* fcall_message) +{ + cpu.enqueueFunctionCallMessage(fcall_message); + notifyMessageAvailable(cpu); +} + +void ArchMulticore::stopAllCpus() +{ + if(CpuLocalStorage::ClsInitialized() && cpu_lapic->isInitialized()) + { + cpu_lapic->sendIPI(STOP_INT_VECTOR); + } +} + +void ArchMulticore::stopOtherCpus() +{ + if(CpuLocalStorage::ClsInitialized() && cpu_lapic->isInitialized()) + { + cpu_lapic->sendIPI(STOP_INT_VECTOR, Apic::IPIDestination::OTHERS); + } +} + +[[noreturn]] void ArchMulticore::waitForSystemStart() +{ + kprintf("CPU %zu initialized, waiting for system start\n", SMP::currentCpuId()); + debug(A_MULTICORE, "CPU %zu initialized, waiting for system start\n", SMP::currentCpuId()); + assert(CpuLocalStorage::ClsInitialized()); + ap_started = true; + + while(system_state != RUNNING); + + ArchInterrupts::enableInterrupts(); + + while(true) + { + debug(A_MULTICORE, "CPU %zu halting\n", SMP::currentCpuId()); + ArchCommon::halt(); + } + assert(false); +} + +char* ArchMulticore::cpuStackTop() +{ + return cpu_stack + sizeof(cpu_stack); +} + +void ArchMulticore::reservePages(Allocator& allocator) +{ + // Physical pages 0 + 1 are used for AP startup code + // Technically we could free them after starting all other CPUs, but a lot + // of the rest of SWEB treats PPN == 0 as invalid (even though it's a perfectly usable page). + // Therefore we cannot actually free them without causing problems elsewhere. + // PPN 0 was previously used for the kernel heap and was never returned from allocPPN(). + size_t ap_boot_code_range = allocator.alloc(PAGE_SIZE*2, PAGE_SIZE); + debug(A_MULTICORE, "Allocated mem for ap boot code: [%zx, %zx)\n", + ap_boot_code_range, ap_boot_code_range + PAGE_SIZE*2); + assert(ap_boot_code_range == 0); +} + +void ArchMulticore::startAP(uint8_t apic_id, size_t entry_addr) +{ + debug(A_MULTICORE, "Sending init IPI to AP local APIC %u, AP entry function: %zx\n", apic_id, entry_addr); + + assert((entry_addr % PAGE_SIZE) == 0); + assert((entry_addr/PAGE_SIZE) <= 0xFF); + + eastl::atomic_flag delay{true}; + assert(delay.test()); + + ArchInterrupts::disableIRQ(PIT::instance().irq()); + auto old_pit_handler = PIT::instance().irq().handler(); + auto old_pit_mode = PIT::operatingMode(); + auto old_pit_freq = PIT::frequencyDivisor(); + + // Restore old PIT state at end of function + eastl::finally f{[&]{ + PIT::instance().irq().useHandler(old_pit_handler); + PIT::setOperatingMode(old_pit_mode); + PIT::setFrequencyDivisor(old_pit_freq); + }}; + + PIT::instance().irq().useHandler([&delay]{ + debugAdvanced(A_MULTICORE, "Delay interrupt called\n"); + delay.clear(); + }); + PIT::setOperatingMode(PIT::OperatingMode::ONESHOT); + + cpu_lapic->sendIPI(0, Apic::IPIDestination::TARGET, apic_id, Apic::IPIType::INIT); + + { + // Enable PIT interrupts for this section + ArchInterrupts::enableIRQ(PIT::instance().irq()); + eastl::finally f_disable_pit_irq{[]{ ArchInterrupts::disableIRQ(PIT::instance().irq()); }}; + WithInterrupts i{true}; + + // 10ms delay + PIT::setFrequencyDivisor(1193182 / 100); + debugAdvanced(A_MULTICORE, "Waiting for delay 1\n"); + while(delay.test_and_set()); + + cpu_lapic->sendIPI(entry_addr/PAGE_SIZE, Apic::IPIDestination::TARGET, apic_id, Apic::IPIType::SIPI); + + // 200us delay + PIT::setFrequencyDivisor(1193182 / 100); + debugAdvanced(A_MULTICORE, "Waiting for delay 2\n"); + while(delay.test_and_set()); + } + + // Second SIPI just in case the first one didn't work + cpu_lapic->sendIPI(entry_addr/PAGE_SIZE, Apic::IPIDestination::TARGET, apic_id, Apic::IPIType::SIPI); + + debugAdvanced(A_MULTICORE, "Finished sending IPI to AP local APIC\n"); +} diff --git a/arch/x86/common/source/CMakeLists.txt b/arch/x86/common/source/CMakeLists.txt index d9d29a0ce..52cf54150 100644 --- a/arch/x86/common/source/CMakeLists.txt +++ b/arch/x86/common/source/CMakeLists.txt @@ -1 +1,4 @@ add_project_library(arch_x86_common) + +target_include_directories(kernel PUBLIC + ../include/) diff --git a/arch/x86/common/source/CPUID.cpp b/arch/x86/common/source/CPUID.cpp new file mode 100644 index 000000000..ac1d6e774 --- /dev/null +++ b/arch/x86/common/source/CPUID.cpp @@ -0,0 +1,274 @@ +#include "CPUID.h" + +#include "debug.h" + +cpu_local CpuFeatures cpu_features; + +void CPUID::cpuid(uint32_t selector, uint32_t subselector, uint32_t& eax, uint32_t& ebx, uint32_t& ecx, uint32_t& edx) +{ + asm("cpuid\n" + :"=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + :"a"(selector), "c"(subselector)); +} + +uint32_t CPUID::highestSupportedLeaf() +{ + uint32_t unused; + uint32_t eax = 0; + cpuid(0x00, 0, eax, unused, unused, unused); + return eax; +} + +uint32_t CPUID::highestSupportedExtendedLeaf() +{ + uint32_t unused; + uint32_t eax = 0; + cpuid(0x80000000, 0, eax, unused, unused, unused); + return eax; +} + +uint32_t CPUID::localApicId() +{ + uint32_t unused; + uint32_t ebx = 0; + cpuid(0x01, 0, unused, ebx, unused, unused); + return ebx >> 24; +} + +uint32_t CPUID::localX2ApicId() +{ + uint32_t unused; + uint32_t edx = 0; + cpuid(0x0B, 0, unused, unused, unused, edx); + return edx; +} + +CpuFeatures::CpuFeatures() +{ + initCpuFeatures(); +} + +bool CpuFeatures::cpuHasFeature(X86Feature feature) const +{ + return features_.test(feature) && !cpuFeatureIsForceDisabled(feature); +} + +bool CpuFeatures::cpuFeatureIsForceDisabled(X86Feature feature) const +{ + return force_disabled_features_.test(feature); +} + +void CpuFeatures::setFeatureForceDisabled(X86Feature feature) +{ + force_disabled_features_.set(feature); +} + +void CpuFeatures::initCpuFeatures() +{ + uint32_t eax, ebx, ecx, edx; + + uint32_t highest_base_cpuid_param; + uint32_t highest_ext_cpuid_param; + char manufacturer_id[13]; + memset(manufacturer_id, 0, sizeof(manufacturer_id)); + CPUID::cpuid(0, 0, highest_base_cpuid_param, *(uint32_t*)manufacturer_id, *((uint32_t*)manufacturer_id + 2), *((uint32_t*)manufacturer_id + 1)); + highest_ext_cpuid_param = CPUID::highestSupportedExtendedLeaf(); + debug(A_MULTICORE, "CPU manufaturer id: %s, highest cpuid param: %x / %x\n", manufacturer_id, highest_base_cpuid_param, highest_ext_cpuid_param); + + CPUID::cpuid(1, 0, eax, ebx, ecx, edx); + + features_.set(FPU, edx & (1 << 0)); + features_.set(VME, edx & (1 << 1)); + features_.set(DEBUGGING_EXTENSIONS, edx & (1 << 2)); + features_.set(PSE, edx & (1 << 3)); + features_.set(TSC, edx & (1 << 4)); + features_.set(MSR, edx & (1 << 5)); + features_.set(PAE, edx & (1 << 6)); + features_.set(MACHINE_CHECK_EXCEPTION, edx & (1 << 7)); + features_.set(CMPXCHG8, edx & (1 << 8)); + features_.set(APIC, edx & (1 << 9)); + features_.set(SYSENTER_SYSEXIT, edx & (1 << 11)); + features_.set(MTRR, edx & (1 << 12)); + features_.set(PGE, edx & (1 << 13)); + features_.set(MCA, edx & (1 << 14)); + features_.set(CMOV, edx & (1 << 15)); + features_.set(PAT, edx & (1 << 16)); + features_.set(PSE36, edx & (1 << 17)); + features_.set(PSN, edx & (1 << 18)); + features_.set(CLFLUSH, edx & (1 << 19)); + features_.set(DEBUG_STORE, edx & (1 << 21)); + features_.set(ACPI_THERMAL_MSR, edx & (1 << 22)); + features_.set(MMX, edx & (1 << 23)); + features_.set(FXSAVE_FXRESTOR, edx & (1 << 24)); + features_.set(SSE, edx & (1 << 25)); + features_.set(SSE2, edx & (1 << 26)); + features_.set(CACHE_SELF_SNOOP, edx & (1 << 27)); + features_.set(HYPERTHREADING, edx & (1 << 28)); + features_.set(THERMAL_MONITOR, edx & (1 << 29)); + features_.set(IA64, edx & (1 << 30)); + features_.set(PBE, edx & (1 << 31)); + + features_.set(SSE3, ecx & (1 << 0)); + features_.set(PCMULQDQ, ecx & (1 << 1)); + features_.set(DTES64, ecx & (1 << 3)); + features_.set(MONITOR, ecx & (1 << 4)); + features_.set(DS_CPL, ecx & (1 << 5)); + features_.set(VMX, ecx & (1 << 5)); + features_.set(SMX, ecx & (1 << 6)); + features_.set(ENHANCED_SPEED_STEP, ecx & (1 << 7)); + features_.set(THERMAL_MONITOR_2, ecx & (1 << 8)); + features_.set(SSSE3, ecx & (1 << 9)); + features_.set(L1_CONTEXT_ID, ecx & (1 << 10)); + features_.set(SILICON_DEBUG_INTERFACE, ecx & (1 << 11)); + features_.set(FMA3, ecx & (1 << 12)); + features_.set(CMPXCHG16B, ecx & (1 << 13)); + features_.set(TASK_PRIORITY_MESSAGE_DISABLE, ecx & (1 << 14)); + features_.set(PERFMON_DEBUG_CAPABILITY, ecx & (1 << 15)); + features_.set(PCID, ecx & (1 << 17)); + features_.set(DMA_DIRECT_CACHE_ACCESS, ecx & (1 << 18)); + features_.set(SSE4_1, ecx & (1 << 19)); + features_.set(SSE4_2, ecx & (1 << 20)); + features_.set(X2APIC, ecx & (1 << 21)); + features_.set(MOVBE, ecx & (1 << 22)); + features_.set(POPCNT, ecx & (1 << 23)); + features_.set(TSC_DEADLINE, ecx & (1 << 24)); + features_.set(AES, ecx & (1 << 25)); + features_.set(XSAVE, ecx & (1 << 26)); + features_.set(OS_XSAVE, ecx & (1 << 27)); + features_.set(AVX, ecx & (1 << 28)); + features_.set(F16C, ecx & (1 << 29)); + features_.set(RDRAND, ecx & (1 << 30)); + features_.set(HYPERVISOR, ecx & (1 << 31)); + + CPUID::cpuid(7, 0, eax, ebx, ecx, edx); + + features_.set(FSGSBASE, ebx & (1 << 0)); + features_.set(TSC_ADJUST_MSR, ebx & (1 << 1)); + features_.set(SGX, ebx & (1 << 2)); + features_.set(BMI1, ebx & (1 << 3)); + features_.set(TSX_HLE, ebx & (1 << 4)); + features_.set(AVX2, ebx & (1 << 5)); + features_.set(SMEP, ebx & (1 << 7)); + features_.set(BMI2, ebx & (1 << 8)); + features_.set(ENHANCED_REP_MOVSB, ebx & (1 << 9)); + features_.set(INVPCID, ebx & (1 << 10)); + features_.set(TSX_RTM, ebx & (1 << 11)); + features_.set(RESOURCE_DIRECTOR_MONITORING, ebx & (1 << 12)); + features_.set(FPU_CS_DS_DEPRECATED, ebx & (1 << 13)); + features_.set(MPX, ebx & (1 << 14)); + features_.set(RESOURCE_DIRECTOR_ALLOCATION, ebx & (1 << 15)); + features_.set(AVX512_F, ebx & (1 << 16)); + features_.set(AVX512_DQ, ebx & (1 << 17)); + features_.set(RDSEED, ebx & (1 << 18)); + features_.set(ADX, ebx & (1 << 19)); + features_.set(SMAP, ebx & (1 << 20)); + features_.set(AVX512_IFMA, ebx & (1 << 21)); + features_.set(CLFLUSHOPT, ebx & (1 << 23)); + features_.set(CLWB, ebx & (1 << 24)); + features_.set(PROCESSOR_TRACE, ebx & (1 << 25)); + features_.set(AVX512_PF, ebx & (1 << 26)); + features_.set(AVX512_ER, ebx & (1 << 27)); + features_.set(AVX512_CD, ebx & (1 << 28)); + features_.set(SHA, ebx & (1 << 29)); + features_.set(AVX512_BW, ebx & (1 << 30)); + features_.set(AVX512_VL, ebx & (1 << 31)); + + features_.set(PREFETCHW1, ecx & (1 << 0)); + features_.set(AVX512_VBMI, ecx & (1 << 1)); + features_.set(UMIP, ecx & (1 << 2)); + features_.set(PKU, ecx & (1 << 3)); + features_.set(OS_PKU, ecx & (1 << 4)); + features_.set(WAITPKG, ecx & (1 << 5)); + features_.set(AVX512_VBMI2, ecx & (1 << 6)); + features_.set(CET_SS, ecx & (1 << 7)); + features_.set(GFNI, ecx & (1 << 8)); + features_.set(VAES, ecx & (1 << 9)); + features_.set(CLMUL, ecx & (1 << 10)); + features_.set(AVX512_VNNI, ecx & (1 << 11)); + features_.set(AVX512_BITALG, ecx & (1 << 12)); + features_.set(TME, ecx & (1 << 13)); + features_.set(AVX512_VPOPCNTDQ, ecx & (1 << 14)); + features_.set(FIVE_LEVEL_PAGING, ecx & (1 << 15)); + features_.set(RDPID, ecx & (1 << 22)); + features_.set(PKS, ecx & (1 << 31)); + + features_.set(AVX512_4VNNIW, edx & (1 << 2)); + features_.set(AVX512_4FMAPS, edx & (1 << 3)); + features_.set(FSRM, edx & (1 << 4)); + features_.set(UINTR, edx & (1 << 5)); + features_.set(AVX512_VP2INTERSECT, edx & (1 << 8)); + features_.set(SPECIAL_REGISTER_BUFFER_DATA_SAMPLING_MITIGATIONS, edx & (1 << 9)); + features_.set(VERW_CPU_BUFFER_CLEAR, edx & (1 << 10)); + features_.set(TSX_ALWAYS_ABORT, edx & (1 << 11)); + features_.set(TSX_FORCE_ABORT_MSR, edx & (1 << 13)); + features_.set(SERIALIZE, edx & (1 << 14)); + features_.set(HYBRID, edx & (1 << 15)); + features_.set(TSXLDTRK, edx & (1 << 16)); + features_.set(PCONFIG, edx & (1 << 18)); + features_.set(ARCH_LAST_BRANCH_RECORDS, edx & (1 << 19)); + features_.set(CET_IBT, edx & (1 << 20)); + features_.set(AMX_BF16, edx & (1 << 22)); + features_.set(AVX512_FP16, edx & (1 << 23)); + features_.set(AMX_TILE, edx & (1 << 24)); + features_.set(AMX_INT8, edx & (1 << 25)); + features_.set(IBRS_IBPB, edx & (1 << 26)); + features_.set(STIBP, edx & (1 << 27)); + features_.set(FLUSH_CMD_MSR, edx & (1 << 28)); + features_.set(ARCH_CAPABILITIES_MSR, edx & (1 << 29)); + features_.set(CORE_CAPABILITIES_MSR, edx & (1 << 30)); + features_.set(SSBD, edx & (1 << 31)); + + if (highest_ext_cpuid_param >= 0x80000001) + { + CPUID::cpuid(0x80000001, 0, eax, ebx, ecx, edx); + + features_.set(SYSCALL_SYSRET, edx & (1 << 11)); + features_.set(MULTIPROCESSOR_CAPABLE, edx & (1 << 19)); + features_.set(NX, edx & (1 << 20)); + features_.set(MMX_EXT, edx & (1 << 21)); + features_.set(FXSAVE_FXRESTOR_OPT, edx & (1 << 25)); + features_.set(PDPE1GB, edx & (1 << 26)); + features_.set(RDTSCP, edx & (1 << 27)); + features_.set(LONG_MODE, edx & (1 << 29)); + features_.set(THREE_D_NOW_EXT, edx & (1 << 30)); + features_.set(THREE_D_NOW, edx & (1 << 31)); + + features_.set(LAHF_SAHF_LONG_MODE, ecx & (1 << 0)); + features_.set(SVM, ecx & (1 << 2)); + features_.set(EXTAPIC, ecx & (1 << 3)); + features_.set(CR8_LEGACY, ecx & (1 << 4)); + features_.set(LZCNT, ecx & (1 << 5)); + features_.set(SSE4A, ecx & (1 << 6)); + features_.set(SSE_MISALIGNED, ecx & (1 << 7)); + features_.set(PREFETCH_PREFETCHW, ecx & (1 << 8)); + features_.set(INSTRUCTION_BASED_SAMPLING, ecx & (1 << 10)); + features_.set(XOP, ecx & (1 << 11)); + features_.set(SKINIT_STGI, ecx & (1 << 12)); + features_.set(WATCHDOG_TIMER, ecx & (1 << 13)); + features_.set(LIGHT_WEIGHT_PROFILING, ecx & (1 << 15)); + features_.set(FMA4, ecx & (1 << 16)); + features_.set(TRANSLATION_CACHE_EXTENSION, ecx & (1 << 17)); + features_.set(NODE_ID_MSR, ecx & (1 << 19)); + features_.set(TBM, ecx & (1 << 21)); + features_.set(TOPOEXT, ecx & (1 << 22)); + features_.set(PERFCTR_CORE, ecx & (1 << 23)); + features_.set(PERFCTR_NB, ecx & (1 << 24)); + features_.set(DBX, ecx & (1 << 26)); + features_.set(PERFTSC, ecx & (1 << 27)); + features_.set(PCX_L2I, ecx & (1 << 28)); + features_.set(MONITORX, ecx & (1 << 29)); + } + + if (highest_ext_cpuid_param >= 0x8000001F) + { + CPUID::cpuid(0x8000001F, 0, eax, ebx, ecx, edx); + + features_.set(SME, eax & (1 << 0)); + features_.set(SEV, eax & (1 << 1)); + features_.set(PAGE_FLUSH_MSR, eax & (1 << 2)); + features_.set(SEV_ES, eax & (1 << 3)); + features_.set(SEV_SNP, eax & (1 << 4)); + features_.set(VM_PERM_LVL, eax & (1 << 5)); + features_.set(VTE, eax & (1 << 16)); + } +} diff --git a/arch/x86/common/source/IDEDriver.cpp b/arch/x86/common/source/IDEDriver.cpp index d02ae6139..777a8bac4 100644 --- a/arch/x86/common/source/IDEDriver.cpp +++ b/arch/x86/common/source/IDEDriver.cpp @@ -1,247 +1,401 @@ #include "IDEDriver.h" +#include "ATACommands.h" +#include "ATADriver.h" #include "BDManager.h" #include "BDVirtualDevice.h" -#include "ATADriver.h" -#include "ports.h" +#include "MasterBootRecord.h" +#include "kprintf.h" #include "kstring.h" +#include "ports.h" +#include "source_location.h" + #include "ArchInterrupts.h" -#include "kprintf.h" -uint32 IDEDriver::doDeviceDetection() +size_t IDEControllerChannel::num_ide_controllers = 0; + +IDEControllerDriver::IDEControllerDriver() : + BasicDeviceDriver("IDE Driver") { - uint32 jiffies = 0; - uint16 base_port = 0x1F0; - uint16 base_regport = 0x3F6; - uint8 cs = 0; - uint8 sc; - uint8 sn; - uint8 devCtrl; +} - uint8 ata_irqs[4] = - { - 14, 15, 11, 9 - }; +IDEControllerDriver& IDEControllerDriver::instance() +{ + static IDEControllerDriver instance_; + return instance_; +} + +void IDEControllerDriver::doDeviceDetection() +{ + // Assume we have an IDE controller + // Normally detcted via PCI bus enumeration + auto ide_controller = new IDEController("IDE Controller"); + bindDevice(*ide_controller); +} - // setup register values - devCtrl = 0x00; // please use interrupts +IDEControllerChannel::IDEControllerChannel(const eastl::string& name, + uint16_t io_reg_base, + uint16_t control_reg_base, + uint16_t isa_irqnum) : + DeviceBus(name), + IrqDomain(name), + io_regs({io_reg_base}), + control_regs({control_reg_base}), + isa_irqnum(isa_irqnum), + controller_id(num_ide_controllers++) +{ + debug(IDE_DRIVER, "Init IDE Controller Channel %zu - %s\n", controller_id, deviceName().c_str()); - // assume there are no devices - debug(IDE_DRIVER, "doDetection:%d\n", cs); + registerDriver(PATADeviceDriver::instance()); - for (cs = 0; cs < 4; cs++) - { - char name[5]; - name[0] = 'i'; - name[1] = 'd'; - name[2] = 'e'; - name[3] = cs + 'a'; - name[4] = '\0'; + IrqDomain::irq() + .mapTo(ArchInterrupts::isaIrqDomain(), isa_irqnum); - debug(IDE_DRIVER, "doDetection:Detecting IDE DEV: %s\n", name); + doDeviceDetection(); +} - if (cs > 1) +void IDEControllerChannel::reset() +{ + debug(IDE_DRIVER, "Reset controller\n"); + ControlRegister::DeviceControl dc{}; + dc.reset = 1; + control_regs[ControlRegister::DEVICE_CONTROL].write(dc); + dc.reset = 0; + control_regs[ControlRegister::DEVICE_CONTROL].write(dc); + + waitNotBusy(); + + // Drive 0 is normally automatically selected after reset, but this might not automatically be done by QEMU + selectDrive(0, true); +} + +[[nodiscard]] bool IDEControllerChannel::isDataReady() +{ + auto status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + return status.data_ready && !status.error && !status.drive_fault_error; +} + +bool IDEControllerChannel::waitDriveReady(source_location loc) +{ + int jiffies = 0; + auto status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + debugAdvanced(IDE_DRIVER, "Wait for drive to be ready. Status: %x\n", + status.u8); + while ((status.busy || !status.ready) && jiffies++ < IO_TIMEOUT) { - base_port = 0x170; - base_regport = 0x376; + status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + ++jiffies; } - outportbp(base_regport, devCtrl); // init the device with interupts + if (status.error) + { + auto error = io_regs[IDEControllerChannel::IoRegister::ERROR].read(); - uint8 value = (cs % 2 == 0 ? 0xA0 : 0xB0); - uint16 bpp6 = base_port + 6; - uint16 bpp2 = base_port + 2; - uint16 bpp3 = base_port + 3; + debugAlways(IDE_DRIVER, + "Drive reported error while waiting for drive to be ready. " + "Status: %x, error: %x. " + "At %s:%u %s\n" + "\n", + status.u8, error.u8, loc.file_name(), loc.line(), + loc.function_name()); - outportb(bpp6, value); - outportb(0x80, 0x00); + return false; + } - outportb(bpp2, 0x55); - outportb(0x80, 0x00); - outportb(bpp3, 0xAA); - outportb(0x80, 0x00); - outportb(bpp2, 0xAA); - outportb(0x80, 0x00); - outportb(bpp3, 0x55); - outportb(0x80, 0x00); - outportb(bpp2, 0x55); - outportb(0x80, 0x00); - outportb(bpp3, 0xAA); - outportb(0x80, 0x00); + if (jiffies >= IO_TIMEOUT) + { + debugAlways(IDE_DRIVER, + "ERROR: Timeout while waiting for IDE drive to be ready. " + "Status: %x. " + "At %s:%u %s\n", + status.u8, loc.file_name(), loc.line(), loc.function_name()); + } - sc = inportb(bpp2); - outportb(0x80, 0x00); - sn = inportb(bpp3); - outportb(0x80, 0x00); + return status.ready; +} - if ((sc == 0x55) && (sn == 0xAA)) +bool IDEControllerChannel::waitNotBusy(source_location loc) +{ + int jiffies = 0; + auto status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + debugAdvanced(IDE_DRIVER, "Wait until drive is not busy. Status: %x\n", status.u8); + while (status.busy && jiffies++ < IO_TIMEOUT) { - outportbp(base_regport, devCtrl | 0x04); // RESET - outportbp(base_regport, devCtrl); + status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + ++jiffies; + } - jiffies = 0; - while (!(inportbp(base_port + 7) & 0x58) && jiffies++ < IO_TIMEOUT) - ArchInterrupts::yieldIfIFSet(); + if (status.error) + { + auto error = io_regs[IDEControllerChannel::IoRegister::ERROR].read(); - if (jiffies >= IO_TIMEOUT) - debug(IDE_DRIVER, "doDetection: Still busy after reset!\n"); - else - { - outportbp(base_port + 6, (cs % 2 == 0 ? 0xA0 : 0xB0)); + debugAlways(IDE_DRIVER, + "Drive reported error while waiting until drive is not busy. " + "Status: %x, error: %x.\n" + "At %s:%u %s\n", + status.u8, error.u8, loc.file_name(), loc.line(), + loc.function_name()); - jiffies = 0; - while(inportbp(base_port + 7) & 0x80 && jiffies++ < IO_TIMEOUT); + return false; + } - uint8 c1 = inportbp(base_port + 2); - uint8 c2 = inportbp(base_port + 3); + if (jiffies >= IO_TIMEOUT) + { + debugAlways(IDE_DRIVER, + "ERROR: Timeout while waiting until drive is not busy. " + "Status: %x.\n" + "At %s:%u %s\n", + status.u8, loc.file_name(), loc.line(), loc.function_name()); + } - debug(IDE_DRIVER, "c1 = %x, c2 = %x\n", c1, c2); - if (c1 != 0x01 && c2 != 0x01) - debug(IDE_DRIVER, "doDetection: Not found after reset ! \n"); - else - { - uint8 c3 = inportbp(base_port + 7); - uint8 c4 = inportbp(base_port + 4); - uint8 c5 = inportbp(base_port + 5); - - if (((c4 == 0x14) && (c5 == 0xEB)) || ((c4 == 0x69) && (c5 == 0x96))) - { - debug(IDE_DRIVER, "doDetection: Found ATAPI ! \n"); - debug(IDE_DRIVER, "doDetection: port: %4X, drive: %d \n", base_port, cs % 2); - - debug(IDE_DRIVER, "doDetection: CDROM not supported \n"); - - // CDROM hook goes here - // - // char *name = "ATAX0"; - // name[3] = cs + '0'; - // drv = new CROMDriver ( base_port, cs % 2 ); - // BDVirtualDevice *bdv = new - // BDVirtualDevice( drv, 0, drv->getNumSectors(), - // drv->getSectorSize(), name, true); - // BDManager::getInstance()->addDevice( bdv ); - - } - else - { - if (c3 != 0) - { - if ((c4 == 0x00) && (c5 == 0x00)) - { - debug(IDE_DRIVER, "doDetection: Found PATA ! \n"); - debug(IDE_DRIVER, "doDetection: port: %4X, drive: %d \n", base_port, cs % 2); + return !status.busy; +} + +bool IDEControllerChannel::waitDataReady(source_location loc) +{ + int jiffies = 0; + auto status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + debugAdvanced(IDE_DRIVER, "Wait for data ready. Status: %x\n", status.u8); + while (status.busy && jiffies++ < IO_TIMEOUT) + { + status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + ++jiffies; + } + + if (status.error) + { + auto error = io_regs[IDEControllerChannel::IoRegister::ERROR].read(); + + debugAlways(IDE_DRIVER, + "Drive reported error while waiting for data to be available. " + "Status: %x, error: %x\n", + status.u8, error.u8); + + return false; + } + if (!status.data_ready) + { + auto error = io_regs[IDEControllerChannel::IoRegister::ERROR].read(); + + debugAlways(IDE_DRIVER, "Data not ready. Status: %x, error: %x\n", status.u8, + error.u8); + } + + if (jiffies >= IO_TIMEOUT) + { + debugAlways(IDE_DRIVER, + "ERROR: Timeout while waiting for IDE controller data ready.\n" + "At %s:%u %s\n", + loc.file_name(), loc.line(), loc.function_name()); + } + + return status.data_ready; +} + + +void IDEControllerChannel::sendCommand(uint8_t cmd) +{ + debugAdvanced(IDE_DRIVER, "Send command: %x\n", cmd); + io_regs[IoRegister::COMMAND].write(cmd); +} + +uint8_t IDEControllerChannel::selectedDrive() +{ + return selected_drive; +} + +void IDEControllerChannel::selectDrive(uint8_t drive, bool force) +{ + assert(drive <= 1); - ATADriver *drv = new ATADriver(base_port, cs % 2, ata_irqs[cs]); - BDVirtualDevice *bdv = new BDVirtualDevice(drv, 0, drv->getNumSectors(), drv->getSectorSize(), name, - true); + if (force || selected_drive != drive) + { + IoRegister::DriveHead dh{}; + dh.drive_num = drive; + dh.use_lba = 1; + dh.always_set_0 = 1; + dh.always_set_1 = 1; + debugAdvanced(ATA_DRIVER, "Select %s drive %u\n", deviceName().c_str(), drive); + io_regs[IoRegister::DRIVE_HEAD].write(dh); + + // Delay so drive select is registered before next command + for (size_t i = 0; i < 15; ++i) + control_regs[ControlRegister::ALT_STATUS].read(); + } - BDManager::getInstance()->addVirtualDevice(bdv); - processMBR(drv, 0, drv->SPT, name); - } - else if ((c4 == 0x3C) && (c5 == 0xC3)) - { - debug(IDE_DRIVER, "doDetection: Found SATA device! \n"); - debug(IDE_DRIVER, "doDetection: port: %4X, drive: %d \n", base_port, cs % 2); + selected_drive = drive; +} - // SATA hook - // drv = new SATADriver ( base_port, cs % 2 ); +bool IDEControllerChannel::detectChannel(uint16_t io_reg_base, + uint16_t control_reg_base) +{ + IoPort::IoRegisterSet io_regs{io_reg_base}; - debug(IDE_DRIVER, "doDetection: Running SATA device as PATA in compatibility mode! \n"); + debug(IDE_DRIVER, "IDE Channel %x:%x detection\n", io_reg_base, control_reg_base); + auto status = io_regs[IoRegister::STATUS].read(); + if (status.u8 == 0xFF) + { + debug(IDE_DRIVER, "Floating bus detected, channel empty\n"); + return false; + } - ATADriver *drv = new ATADriver(base_port, cs % 2, ata_irqs[cs]); + // Detect presence of I/O ports by writing to them and trying to read back + // the written value + constexpr uint8_t test_sc = 0x55; + constexpr uint8_t test_sn = 0xAA; + io_regs[IoRegister::SECTOR_COUNT].write(test_sc); + io_regs[IoRegister::SECTOR_NUMBER].write(test_sn); + + auto sc = io_regs[IoRegister::SECTOR_COUNT].read(); + auto sn = io_regs[IoRegister::SECTOR_NUMBER].read(); + if (sc == test_sc && sn == test_sn) + { + debug(IDE_DRIVER, "Channel %x:%x exists\n", io_reg_base, control_reg_base); + return true; + } + else + { + debug(IDE_DRIVER, "Channel %x:%x does not exists\n", io_reg_base, + control_reg_base); + return false; + } +} + + +void IDEControllerChannel::doDeviceDetection() +{ + debug(IDE_DRIVER, "IDE Channel device detection\n"); + + for (uint8_t disk = 0; disk < 2; ++disk) + { + detectDrive(disk); + } +} + +void IDEControllerChannel::detectDrive(uint8_t drive_num) +{ + debug(ATA_DRIVER, "Reset %s drives\n", deviceName().c_str()); + reset(); + + // Select drive again after reset + selectDrive(drive_num); + + // Send IDENTIFY command + debug(ATA_DRIVER, "Send IDENTIFY command\n"); + io_regs[IoRegister::COMMAND].write(ATACommand::PIO::IDENTIFY_DEVICE); + + auto status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + if (status.u8 == 0) + { + debug(ATA_DRIVER, "IDENTIFY: Drive %u does not exist\n", drive_num); + } + else + { + debug(ATA_DRIVER, "IDENTIFY: Drive %u exists\n", drive_num); + waitNotBusy(); + status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); + + auto lba_mid = io_regs[IoRegister::LBA_MID].read(); + auto lba_high = io_regs[IoRegister::LBA_HIGH].read(); + if (!status.error && (lba_mid != 0 || lba_high != 0)) + { + debug(ATA_DRIVER, + "IDENTIFY: Non spec-conforming ATAPI device found, aborting\n"); + return; + } - BDVirtualDevice *bdv = new BDVirtualDevice(drv, 0, drv->getNumSectors(), drv->getSectorSize(), name, - true); + waitDataReady(); + status = control_regs[IDEControllerChannel::ControlRegister::ALT_STATUS].read(); - BDManager::getInstance()->addVirtualDevice(bdv); + /* + See ATA command set https://people.freebsd.org/~imp/asiabsdcon2015/works/d2161r5-ATAATAPI_Command_Set_-_3.pdf table 206 for device signatures + */ + auto count = io_regs[IoRegister::SECTOR_COUNT].read(); + auto lba_low = io_regs[IoRegister::LBA_LOW].read(); + lba_mid = io_regs[IoRegister::LBA_MID].read(); + lba_high = io_regs[IoRegister::LBA_HIGH].read(); - processMBR(drv, 0, drv->SPT, name); - } + debug(ATA_DRIVER, "IDENTIFY command signature: status: %x, count: %x, lba h: %x, lba m: %x, lba l: %x\n", status.u8, count, lba_high, lba_mid, lba_low); + + bool found_driver = probeDrivers(IDEDeviceDescription{this, drive_num, {count, lba_low, lba_mid, lba_high}}); + if (found_driver) + { + debug(ATA_DRIVER, "Found driver for device: %u\n", found_driver); + } + else + { + debug(ATA_DRIVER, "Could not find driver for device\n"); + } + + if (status.error) + { + debug(ATA_DRIVER, "IDENTIFY command aborted: Not an ATA device\n"); + + if (count == 0x01 && lba_low == 0x01 && lba_mid == 0x14 && lba_high == 0xEB) + { + debug(ATA_DRIVER, "IDENTIFY command aborted: ATAPI device found\n"); + } + else if (count == 0x01 && lba_low == 0x01 && + ((lba_mid == 0x3C && lba_high == 0xC3) || + (lba_mid == 0x69 && lba_high == 0x96))) + { + debug(ATA_DRIVER, "IDENTIFY command aborted: SATA device found\n"); + } + else if (count == 0x01 && lba_low == 0x01 && lba_mid == 0xCE && + lba_high == 0xAA) + { + debug(ATA_DRIVER, "IDENTIFY command aborted: Obsolete device identifier\n"); + } + else + { + debug(ATA_DRIVER, + "IDENTIFY command aborted: unknown device type identifier\n"); + } + } + else + { + if (count == 0x01 && lba_low == 0x01 && lba_mid == 0x00 && lba_high == 0x00) + { + debug(ATA_DRIVER, "IDENTIFY: PATA device found\n"); } else { - debug(IDE_DRIVER, "doDetection: Unknown harddisk!\n"); + debug(ATA_DRIVER, "IDENTIFY: Command succeeded but signature does not match ATA device\n"); } - } } - } } - else +} + +IDEController::IDEController(const eastl::string& name) : + Device(name) +{ + debug(IDE_DRIVER, "Init %s\n", deviceName().c_str()); + doDeviceDetection(); +} + +void IDEController::doDeviceDetection() +{ + // Assume default io registers + // Normally detected via PCI bus enumeration + eastl::array, 4> + default_channels = { + {{"Primary IDE Channel", DefaultPorts::PRIMARY_IO, + DefaultPorts::PRIMARY_CONTROL, DefaultPorts::PRIMARY_ISA_IRQ}, + {"Secondary IDE Channel", DefaultPorts::SECONDARY_IO, + DefaultPorts::SECONDARY_CONTROL, DefaultPorts::SECONDARY_ISA_IRQ}, + {"Ternary IDE Channel", DefaultPorts::TERNARY_IO, + DefaultPorts::TERNARY_CONTROL, DefaultPorts::TERNARY_ISA_IRQ}, + {"Quaternary IDE Channel", DefaultPorts::QUATERNARY_IO, + DefaultPorts::QUATERNARY_CONTROL, DefaultPorts::QUATERNARY_ISA_IRQ}}}; + + for (auto& [name, io_reg_base, control_reg_base, isa_irqnum] : default_channels) { - debug(IDE_DRIVER, "doDetection: Not found!\n"); - } - - } - - // TODO : verify if the device is ATA and not ATAPI or SATA - return 0; -} - -int32 IDEDriver::processMBR(BDDriver* drv, uint32 sector, uint32 SPT, const char *name) -{ - uint32 offset = 0, numsec = 0; - uint16 buff[256]; // read buffer - debug(IDE_DRIVER, "processMBR:reading MBR\n"); - - static uint32 part_num = 0; -// char part_num_str[2]; -// char part_name[10]; - - uint32 read_res = ((ATADriver*)drv)->rawReadSector(sector, 1, (void *) buff); - - if (read_res != 0) - { - debug(IDE_DRIVER, "processMBR: drv returned BD_ERROR\n"); - return -1; - } - - MBR *mbr = (MBR *) buff; - - if (mbr->signature == 0xAA55) - { - debug(IDE_DRIVER, "processMBR: | Valid PC MBR | \n"); - FP * fp = (FP *) mbr->parts; - uint32 i; - for (i = 0; i < 4; i++, fp++) - { - switch (fp->systid) - { - case 0x00: - // do nothing - break; - case 0x05: // DOS extended partition - case 0x0F: // Windows extended partition - case 0x85: // linux extended partition - debug(IDE_DRIVER, "ext. part. at: %d \n", fp->relsect); - if (processMBR(drv, sector + fp->relsect, SPT, name) == -1) - processMBR(drv, sector + fp->relsect - SPT, SPT, name); - break; - default: - // offset = fp->relsect - SPT; - offset = fp->relsect; - numsec = fp->numsect; - - char part_name[6]; - strncpy(part_name, name, 4); - part_name[4] = part_num + '0'; - part_name[5] = 0; - part_num++; - BDVirtualDevice *bdv = new BDVirtualDevice(drv, offset, numsec, drv->getSectorSize(), part_name, true); - - // set Partition Type (FileSystem identifier) - bdv->setPartitionType(fp->systid); - - BDManager::getInstance()->addVirtualDevice(bdv); - break; - } - } - } - else - { - debug(IDE_DRIVER, "processMBR: | Invalid PC MBR %d | \n", mbr->signature); - return -1; - } - - debug(IDE_DRIVER, "processMBR:, done with partitions \n"); - return 0; + if (IDEControllerChannel::detectChannel(io_reg_base, control_reg_base)) + { + auto c = + new IDEControllerChannel{name, io_reg_base, control_reg_base, isa_irqnum}; + channels.push_back(c); + addSubDevice(*c); + } + } } diff --git a/arch/x86/common/source/IoApic.cpp b/arch/x86/common/source/IoApic.cpp new file mode 100644 index 000000000..c82556ba8 --- /dev/null +++ b/arch/x86/common/source/IoApic.cpp @@ -0,0 +1,361 @@ +#include "IoApic.h" + +#include "8259.h" +#include "CPUID.h" +#include "InterruptUtils.h" +#include "SMP.h" +#include "offsets.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMemory.h" +#include "ArchMulticore.h" + +#include "debug.h" + +IoApic::IoApic(uint32_t id, IOAPIC_MMIORegs* regs, uint32_t g_sys_int_base) : + IrqDomain("I/O APIC", 24, this), + Device("I/O APIC"), + reg_paddr_(regs), + reg_vaddr_(regs), + id_(id), + g_sys_int_base_(g_sys_int_base) +{ + debug(APIC, "IOAPIC %x at phys %p, g_sys_int_base: %x\n", id_, reg_paddr_, + g_sys_int_base_); + assert(reg_paddr_); +} + +void IoApic::initAll() +{ + for (auto& io_apic : IoApicList()) + { + auto ioapic_vaddr = mmio_addr_allocator.alloc(PAGE_SIZE, PAGE_SIZE); + debug(APIC, "Allocated MMIO addr for IoAPIC: %zx\n", ioapic_vaddr); + io_apic.mapAt(ioapic_vaddr); + assert((size_t)io_apic.reg_vaddr_ >= USER_BREAK); + io_apic.init(); + } +} + +void IoApic::init() +{ + debug(A_INTERRUPTS, "Initializing I/O APIC\n"); + + PIC8259::enabled = false; + + IOAPIC_r_ID id = read(); + IOAPIC_r_VER version = read(); + max_redir_ = version.max_redir; + IrqDomain::setNumIrqs(max_redir_ + 1); + debug(APIC, "IOAPIC id: %u, version: %#x, g_sys_ints: [%u, %u)\n", id.io_apic_id, + version.version, getGlobalInterruptBase(), + getGlobalInterruptBase() + getMaxRedirEntry()); + redir_entry_cache_.resize(max_redir_ + 1); + + setupIsaIrqMappings(); + initRedirections(); +} + +void IoApic::setupIsaIrqMappings() +{ + size_t ioapic_irq_start = getGlobalInterruptBase(); + size_t ioapic_irq_end = getGlobalInterruptBase() + getMaxRedirEntry(); + + for (size_t isa_irq = ioapic_irq_start; isa_irq < ioapic_irq_end && isa_irq < 16; + ++isa_irq) + { + // IRQ 2 is never raised (internal PIC cascade) + if (isa_irq == 2) + continue; + + auto [have_irq_override, ovr] = findSourceOverrideForIrq(isa_irq); + auto g_sys_int = have_irq_override ? ovr.g_sys_int : isa_irq; + ArchInterrupts::isaIrqDomain().irq(isa_irq).mapTo(*this, g_sys_int); + } +} + +void IoApic::initRedirections() +{ + for (uint32_t i = 0; i <= max_redir_; ++i) + { + auto g_sys_int = getGlobalInterruptBase() + i; + IOAPIC_redir_entry r = readRedirEntry(i); + + r.interrupt_vector = InterruptVector::REMAP_OFFSET + g_sys_int; + + auto [have_override, entry] = findSourceOverrideForGSysInt(g_sys_int); + + if (have_override) + { + debug(APIC, + "Found override for global system interrupt %2u -> IRQ SRC %u, trigger " + "mode: %u, polarity: %u\n", + entry.g_sys_int, entry.irq_source, entry.flags.trigger_mode, + entry.flags.polarity); + r.interrupt_vector = InterruptVector::REMAP_OFFSET + entry.irq_source; + r.polarity = (entry.flags.polarity == ACPI_MADT_POLARITY_ACTIVE_HIGH); + r.trigger_mode = (entry.flags.trigger_mode == ACPI_MADT_TRIGGER_LEVEL); + r.destination = cpu_lapic->apicId(); + } + + writeRedirEntry(i, r); + auto target_cpu = SMP::cpu(r.destination); + // Don't create an irq mapping if the irq source is connected to a different I/O + // APIC global system interrupt + if (auto [have_override, ovr] = findSourceOverrideForIrq(g_sys_int); + !have_override || ovr.irq_source == ovr.g_sys_int) + { + irq(g_sys_int).mapTo(target_cpu->rootIrqDomain(), r.interrupt_vector); + } + } + + if (APIC & OUTPUT_ENABLED) + { + for (uint32_t i = 0; i <= max_redir_; ++i) + { + IOAPIC_redir_entry r = readRedirEntry(i); + debug(APIC, + "IOAPIC redir entry: IRQ %2u -> vector %u, dest mode: %u, dest APIC: " + "%u, mask: %u, pol: %u, trig: %u\n", + getGlobalInterruptBase() + i, r.interrupt_vector, r.destination_mode, + r.destination, r.mask, r.polarity, r.trigger_mode); + } + } + debug(APIC, "IO APIC redirections initialized\n"); +} + +void IoApic::mapAt(size_t addr) +{ + debug(APIC, "Map IOAPIC %u at phys %p to %p\n", id_, reg_paddr_, (void*)addr); + assert(addr); + + assert(ArchMemory::mapKernelPage(addr / PAGE_SIZE, ((size_t)reg_paddr_) / PAGE_SIZE, + true, true)); + reg_vaddr_ = (IOAPIC_MMIORegs*)addr; +} + +uint32_t IoApic::read(uint8_t offset) +{ + WithInterrupts i(false); + uint32_t retval = 0; + asm volatile("movl %[offset], %[io_reg_sel]\n" + "movl %[io_win], %[retval]\n" + : [io_reg_sel] "=m"(reg_vaddr_->io_reg_sel), [retval] "=r"(retval) + : [offset] "r"((uint32)offset), [io_win] "m"(reg_vaddr_->io_win)); + return retval; +} + +void IoApic::write(uint8_t offset, uint32_t value) +{ + WithInterrupts i(false); + asm volatile( + "movl %[offset], %[io_reg_sel]\n" + "movl %[value], %[io_win]\n" + : [io_reg_sel] "=m"(reg_vaddr_->io_reg_sel), [io_win] "=m"(reg_vaddr_->io_win) + : [offset] "r"((uint32)offset), [value] "r"(value)); +} + +uint8_t IoApic::redirEntryOffset(uint32_t entry_no) +{ + return (0x10 + 2 * entry_no); +} + +IoApic::IOAPIC_redir_entry IoApic::readRedirEntry(uint32_t entry_no) +{ + debug(APIC, "IoApic, read redir entry %u\n", entry_no); + assert(entry_no <= max_redir_); + assert(entry_no < redir_entry_cache_.size()); + uint8_t offset = redirEntryOffset(entry_no); + + IOAPIC_redir_entry temp; + temp.word_l = read(offset); + temp.word_h = read(offset + 1); + + redir_entry_cache_[entry_no] = temp; + + return temp; +} + +void IoApic::writeRedirEntry(uint32_t entry_no, const IoApic::IOAPIC_redir_entry& value) +{ + assert(entry_no <= max_redir_); + assert(entry_no < redir_entry_cache_.size()); + + redir_entry_cache_[entry_no] = value; + + uint8_t offset = redirEntryOffset(entry_no); + + write(offset + 1, value.word_h); + write(offset, value.word_l); +} + +uint32_t IoApic::getGlobalInterruptBase() +{ + return g_sys_int_base_; +} + +uint32_t IoApic::getMaxRedirEntry() +{ + return max_redir_; +} + +void IoApic::setGSysIntMask(uint32_t g_sys_int, bool value) +{ + debug(APIC, "Set G Sys Int %x mask: %u\n", g_sys_int, value); + IoApic* io_apic = findIOAPICforGlobalInterrupt(g_sys_int); + uint32_t entry_offset = g_sys_int - io_apic->getGlobalInterruptBase(); + IOAPIC_redir_entry r = io_apic->readRedirEntry(entry_offset); + r.mask = (value ? 1 : 0); + io_apic->writeRedirEntry(entry_offset, r); +} + +bool IoApic::getGSysIntMask(uint32_t g_sys_int) +{ + IoApic* io_apic = findIOAPICforGlobalInterrupt(g_sys_int); + uint32_t entry_offset = g_sys_int - io_apic->getGlobalInterruptBase(); + return io_apic->redir_entry_cache_.at(entry_offset).mask; +} + +void IoApic::setIRQMask(uint32_t irq_num, bool value) +{ + debug(APIC, "Set IRQ %x mask: %u\n", irq_num, value); + setGSysIntMask(findGSysIntForIRQ(irq_num), value); +} + +bool IoApic::getIRQMask(uint32_t irq_num) +{ + return getGSysIntMask(findGSysIntForIRQ(irq_num)); +} + +IoApic* IoApic::findIOAPICforGlobalInterrupt(uint32_t g_int) +{ + for (auto& io_apic : IoApicList()) + { + uint32_t base = io_apic.getGlobalInterruptBase(); + if ((base <= g_int) && (g_int < base + io_apic.getMaxRedirEntry())) + { + debugAdvanced(APIC, "Found IOAPIC for global interrupt %u: %p\n", g_int, + &io_apic); + + return &io_apic; + } + } + + debugAdvanced(APIC, "Couldn't find IOAPIC for global interrupt %u\n", g_int); + + return nullptr; +} + +uint32_t IoApic::findGSysIntForIRQ(uint8_t irq) +{ + uint8_t g_sys_int = irq; + + for (auto& entry : IrqSourceOverrideList()) + { + if (irq == entry.irq_source) + { + g_sys_int = entry.g_sys_int; + break; + } + } + + debugAdvanced(APIC, "IRQ %u -> g sys int %u\n", irq, g_sys_int); + return g_sys_int; +} + +IoApic* IoApic::findIOAPICforIRQ(uint8_t irq) +{ + debugAdvanced(APIC, "Find IOAPIC for IRQ %u\n", irq); + + return findIOAPICforGlobalInterrupt(findGSysIntForIRQ(irq)); +} + +void IoApic::addIOAPIC(uint32_t id, IOAPIC_MMIORegs* regs, uint32_t g_sys_int_base) +{ + IoApicList().emplace_back(id, regs, g_sys_int_base); +} + +void IoApic::addIRQSourceOverride(const MADTInterruptSourceOverride& entry) +{ + IrqSourceOverrideList().push_back(entry); +} + +eastl::tuple +IoApic::findSourceOverrideForGSysInt(uint8_t g_sys_int) +{ + for (auto o : IrqSourceOverrideList()) + { + if (o.g_sys_int == g_sys_int) + return {true, o}; + } + return {false, {}}; +} + +eastl::tuple +IoApic::findSourceOverrideForIrq(uint8_t irq) +{ + for (auto o : IrqSourceOverrideList()) + { + if (o.irq_source == irq) + return {true, o}; + } + return {false, {}}; +} + +uint8_t IoApic::gSysIntToVector(uint8_t g_sys_int) +{ + assert(g_sys_int < redir_entry_cache_.size()); + return redir_entry_cache_[g_sys_int].interrupt_vector; +} + +eastl::vector& IoApic::IoApicList() +{ + static eastl::vector io_apic_list_; + return io_apic_list_; +} + +eastl::vector& IoApic::IrqSourceOverrideList() +{ + static eastl::vector irq_source_override_list_; + return irq_source_override_list_; +} + +bool IoApic::mask(irqnum_t irq, bool mask) +{ + debug(APIC, "IoApic, mask global system interrupt %zu = %u\n", irq, mask); + setGSysIntMask(irq, mask); + return true; +} + +bool IoApic::irqStart(irqnum_t irq) +{ + debugAdvanced(APIC, "IoApic, start global system interrupt %zu\n", irq); + ++pending_EOIs; + return true; +} + +bool IoApic::ack(irqnum_t irq) +{ + debugAdvanced(APIC, "IoApic, ack global system interrupt %zu\n", irq); + --pending_EOIs; + cpu_lapic->sendEOI(gSysIntToVector(irq)); + return true; +} + +bool IoApic::isMasked(irqnum_t irq) +{ + return getGSysIntMask(irq); +} + +void IoApicDriver::doDeviceDetection() +{ + if (cpu_features.cpuHasFeature(CpuFeatures::APIC)) + { + IoApic::initAll(); + for (auto& ioapic : IoApic::IoApicList()) + { + bindDevice(ioapic); + } + } +} diff --git a/arch/x86/common/source/KeyboardManager.cpp b/arch/x86/common/source/KeyboardManager.cpp index 56953d4c8..41b2f88ec 100644 --- a/arch/x86/common/source/KeyboardManager.cpp +++ b/arch/x86/common/source/KeyboardManager.cpp @@ -1,22 +1,23 @@ #include "KeyboardManager.h" -#include "kprintf.h" + #include "Console.h" +#include "kprintf.h" #include "ports.h" -uint32 const KeyboardManager::STANDARD_KEYMAP[KEY_MAPPING_SIZE] = STANDARD_KEYMAP_DEF; +#include "ArchInterrupts.h" -uint32 const KeyboardManager::E0_KEYS[KEY_MAPPING_SIZE] = E0_KEYS_DEF; +const uint32 KeyboardManager::STANDARD_KEYMAP[KEY_MAPPING_SIZE] = STANDARD_KEYMAP_DEF; -KeyboardManager *KeyboardManager::instance_ = 0; +const uint32 KeyboardManager::E0_KEYS[KEY_MAPPING_SIZE] = E0_KEYS_DEF; KeyboardManager::KeyboardManager() : + IrqDomain("Keyboard"), keyboard_buffer_(256), extended_scancode(0), keyboard_status_(0) { emptyKbdBuffer(); -} - -KeyboardManager::~KeyboardManager() -{ + IrqDomain::irq() + .mapTo(ArchInterrupts::isaIrqDomain(), 1) + .useHandler([this]{ serviceIRQ(); }); } void KeyboardManager::kb_wait() @@ -40,7 +41,7 @@ void KeyboardManager::send_cmd(uint8 cmd, uint8 port) outportbp(port, cmd); } -void KeyboardManager::serviceIRQ(void) +void KeyboardManager::serviceIRQ() { send_cmd(0xAD); // disable the keyboard kb_wait(); @@ -140,7 +141,6 @@ void KeyboardManager::modifyKeyboardStatus(uint8 sc) else keyboard_status_ |= control_key; } - return; } void KeyboardManager::emptyKbdBuffer() @@ -149,7 +149,7 @@ void KeyboardManager::emptyKbdBuffer() kbdGetScancode(); } -void KeyboardManager::setLEDs(void) +void KeyboardManager::setLEDs() { static uint32 last_leds = 0; uint32 leds = 0; diff --git a/arch/x86/common/source/MSR.cpp b/arch/x86/common/source/MSR.cpp new file mode 100644 index 000000000..cae0d6b4d --- /dev/null +++ b/arch/x86/common/source/MSR.cpp @@ -0,0 +1,15 @@ +#include "MSR.h" + +#include + +#include "debug.h" + +void MSR::getMSR(uint32_t msr, uint32_t *lo, uint32_t *hi) +{ + asm volatile("rdmsr" : "=a"(*lo), "=d"(*hi) : "c"(msr)); +} + +void MSR::setMSR(uint32_t msr, uint32_t lo, uint32_t hi) +{ + asm volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr)); +} diff --git a/arch/x86/common/source/ProgrammableIntervalTimer.cpp b/arch/x86/common/source/ProgrammableIntervalTimer.cpp new file mode 100644 index 000000000..da213f5bd --- /dev/null +++ b/arch/x86/common/source/ProgrammableIntervalTimer.cpp @@ -0,0 +1,98 @@ +#include "ProgrammableIntervalTimer.h" + +#include "InterruptUtils.h" +#include "ports.h" + +#include "ArchInterrupts.h" +#include "ArchMulticore.h" + +#include "assert.h" + +PIT::OperatingMode PIT::operating_mode = PIT::OperatingMode::SQUARE_WAVE; +uint16_t PIT::frequency_divisor = 0; + +PIT::PIT() : + Device("Programmable Interval Timer"), + IrqDomain("Programmable Interval Timer") +{ +} + +PIT& PIT::instance() +{ + static PIT i; + return i; +} + +void PIT::init(PITCommandRegister command, uint16 divisor) +{ + assert(command.access_mode == AccessMode::LOW_BYTE_HIGH_BYTE); + sendCommand(command); + setFrequencyDivisor(divisor); +} + +void PIT::sendCommand(PITCommandRegister command) +{ + operating_mode = command.operating_mode; + COMMAND_PORT::write(command); +} + +PIT::OperatingMode PIT::setOperatingMode(PIT::OperatingMode mode) +{ + auto old_mode = operating_mode; + + sendCommand(PITCommandRegister{ + .bcd_mode = 0, + .operating_mode = mode, + .access_mode = PIT::AccessMode::LOW_BYTE_HIGH_BYTE, // send low + high byte of reload value/divisor + .channel = 0, + }); + + return old_mode; +} + +uint16_t PIT::setFrequencyDivisor(uint16 reload_value) +{ + auto old_freq = frequency_divisor; + frequency_divisor = reload_value; + assert(!(operating_mode == PIT::OperatingMode::SQUARE_WAVE && reload_value == 1)); + + WithInterrupts i{false}; + CH0_DATA_PORT::write(reload_value & 0xFF); + CH0_DATA_PORT::write(reload_value >> 8); + + return old_freq; +} + +PIT::OperatingMode PIT::operatingMode() +{ + return operating_mode; +} + +uint16_t PIT::frequencyDivisor() +{ + return frequency_divisor; +} + +PITDriver::PITDriver() : + BasicDeviceDriver("PIT driver") +{ +} + +PITDriver& PITDriver::instance() +{ + static PITDriver i; + return i; +} + +void PITDriver::doDeviceDetection() +{ + bindDevice(PIT::instance()); + + PIT::setOperatingMode(PIT::OperatingMode::SQUARE_WAVE); + PIT::setFrequencyDivisor(0); + + PIT::instance() + .irq() + .mapTo(ArchInterrupts::isaIrqDomain(), (uint8_t)ISA_IRQ::PIT) + .useHandler(int32_handler_PIT_irq0); +} diff --git a/arch/x86/common/source/SerialManager.cpp b/arch/x86/common/source/SerialManager.cpp index e75c2a6da..3fa71ffb7 100644 --- a/arch/x86/common/source/SerialManager.cpp +++ b/arch/x86/common/source/SerialManager.cpp @@ -1,29 +1,68 @@ -#include "ArchSerialInfo.h" #include "SerialManager.h" -#include "kstring.h" +#include "8259.h" +#include "APIC.h" #include "debug_bochs.h" #include "kprintf.h" -#include "8259.h" +#include "kstring.h" -SerialManager * SerialManager::instance_ = 0; +#include "ArchInterrupts.h" +#include "ArchSerialInfo.h" -SerialManager::SerialManager() : num_ports( 0 ) +SerialManager::SerialManager() : + BasicDeviceDriver("Serial Port Driver"), + num_ports(0) { + debug(A_SERIALPORT, "Init serial port driver\n"); } -SerialManager::~SerialManager() +uint32 SerialManager::get_num_ports() const { + return num_ports; } -uint32 SerialManager::get_num_ports() +void SerialManager::doDeviceDetection() { - return num_ports; + do_detection(false); } -uint32 SerialManager::do_detection( uint32 is_paging_set_up ) +UART_TYPE identifyUartDevice(uint16_t base_port) { - uint16 * bios_sp_table; + SerialPort::write_UART(base_port, SerialPortRegister::FCR, 0xE7); + + SerialPort_InterruptIdentificationRegister iir{}; + iir.u8 = SerialPort::read_UART(base_port, SerialPortRegister::IIR); + + switch (iir.fifo_info) + { + case IIR_fifo_info::NO_FIFO: + debug(A_SERIALPORT, "Serial port has no fifo -> 8250\n"); + return UART_TYPE::UART_8250; + break; + case IIR_fifo_info::FIFO_NON_FUNCTIONAL: + debug(A_SERIALPORT, "Serial port has non functional fifo -> 16550\n"); + return UART_TYPE::UART_16650; + break; + case IIR_fifo_info::FIFO_ENABLED: + if (iir.fifo_64b_enabled) + { + debug(A_SERIALPORT, "Serial port has functional 64 byte fifo -> 16750\n"); + return UART_TYPE::UART_16750; + } + else + { + debug(A_SERIALPORT, "Serial port has functional fifo -> 16550A\n"); + return UART_TYPE::UART_16650A; + } + break; + }; + + return UART_TYPE::UART_8250; +} + +uint32 SerialManager::do_detection(uint32 is_paging_set_up) +{ + uint16* bios_sp_table; if (is_paging_set_up) bios_sp_table = (uint16 *) 0xC0000400; @@ -31,21 +70,23 @@ uint32 SerialManager::do_detection( uint32 is_paging_set_up ) bios_sp_table = (uint16 *) 0x00000400; uint32 i = 0; - for( ; i < SC::MAX_ARCH_PORTS; i++, bios_sp_table++ ) + for (; i < SC::MAX_ARCH_PORTS; i++, bios_sp_table++) { - if( *bios_sp_table != 0x00 ) + if (*bios_sp_table != 0x00) { uint8 sp_name[] = { 's', 'p', (uint8) (num_ports + '1'), '\0' }; - ArchSerialInfo * archInfo = new ArchSerialInfo(); - archInfo->base_port = *bios_sp_table; - archInfo->uart_type = SC::UART_OLD; - // UART type detection still missing - archInfo->irq_num = 4 - i%2; - serial_ports[ num_ports ] = new SerialPort( (char*) sp_name, *archInfo ); - enableIRQ( archInfo->irq_num ); + ArchSerialInfo archInfo{}; + archInfo.base_port = *bios_sp_table; + archInfo.uart_type = static_cast(identifyUartDevice(archInfo.base_port)); + archInfo.irq_num = 4 - i%2; + auto s_port = new SerialPort((char*)sp_name, archInfo); + serial_ports[ num_ports ] = s_port; + bindDevice(*s_port); + ArchInterrupts::enableIRQ(s_port->irq()); num_ports++; } } + return num_ports; } diff --git a/arch/x86/common/source/SerialPort.cpp b/arch/x86/common/source/SerialPort.cpp index 6cc2b77d1..d3c045ebb 100644 --- a/arch/x86/common/source/SerialPort.cpp +++ b/arch/x86/common/source/SerialPort.cpp @@ -1,34 +1,40 @@ -#include "ArchSerialInfo.h" -#include "ports.h" +#include "8259.h" #include "SerialManager.h" +#include "kprintf.h" +#include "ports.h" #include "ArchInterrupts.h" +#include "ArchSerialInfo.h" #include "ArchThreads.h" -#include "kprintf.h" -#include "8259.h" +#include "debug.h" -SerialPort::SerialPort ( char *name, ArchSerialInfo port_info ) : CharacterDevice( name ) +SerialPort::SerialPort(const char* name, const ArchSerialInfo& port_info) : + CharacterDevice(name), + IrqDomain(eastl::string("Serial Port ") + name) { this->port_info_ = port_info; WriteLock = 0; SerialLock = 0; - - setup_port( BR_9600, DATA_8, STOP_ONE, NO_PARITY ); -} -SerialPort::~SerialPort () -{ + setup_port(BR_9600, DATA_8, STOP_ONE, NO_PARITY); + + auto irqnum = get_info().irq_num; + IrqDomain::irq() + .mapTo(ArchInterrupts::isaIrqDomain(), irqnum) + .useHandler([this] { irq_handler(); }); + + debug(DRIVER, "New serial port device, io port: %x, irq: %u\n", get_info().base_port, irqnum); } -SerialPort::SRESULT SerialPort::setup_port( BAUD_RATE_E baud_rate, DATA_BITS_E data_bits, STOP_BITS_E stop_bits, PARITY_E parity ) +SerialPort::SRESULT SerialPort::setup_port(BAUD_RATE_E baud_rate, DATA_BITS_E data_bits, STOP_BITS_E stop_bits, PARITY_E parity) { - write_UART( SC::IER , 0x00); // turn off interupts - + write_UART(SerialPortRegister::IER, 0x00); // turn off interupts + uint8 divisor = 0x0C; - - switch( baud_rate ) + + switch(baud_rate) { case BR_14400: case BR_19200: @@ -42,145 +48,279 @@ SerialPort::SRESULT SerialPort::setup_port( BAUD_RATE_E baud_rate, DATA_BITS_E d break; case BR_115200: divisor = 0x01; - break; + break; default: case BR_9600: divisor = 0x0C; break; } - write_UART( SC::LCR , 0x80); // activate DL + SerialPort_LineControlRegister lcr{}; + lcr.divisor_latch = 1; + + write_UART(SerialPortRegister::LCR, lcr.u8); // activate DL - write_UART( 0 , divisor ); // DL low byte - write_UART( SC::IER , 0x00); // DL high byte - - uint8 data_bit_reg = 0x03; - - switch( data_bits ) + write_UART(SerialPortRegister::DLL, divisor); // DL low byte + write_UART(SerialPortRegister::DLH, 0x00); // DL high byte + + lcr.divisor_latch = 0; + + switch(data_bits) { case DATA_8: - data_bit_reg = 0x03; + lcr.word_length = LCR_word_length::BITS_8; break; case DATA_7: - data_bit_reg = 0x02; + lcr.word_length = LCR_word_length::BITS_7; break; } - - uint8 par = 0x00; - - switch( parity ) + + + switch(parity) { case NO_PARITY: - par = 0x00; + lcr.parity = LCR_parity::NO_PARITY; break; case EVEN_PARITY: - par = 0x18; + lcr.parity = LCR_parity::EVEN_PARITY; break; case ODD_PARITY: - par = 0x08; + lcr.parity = LCR_parity::ODD_PARITY; break; } - - uint8 stopb = 0x00; - - switch( stop_bits ) + + + switch(stop_bits) { case STOP_ONE: - stopb = 0x00; + lcr.stop_bits = LCR_stop_bits::ONE_STOP_BIT; break; case STOP_TWO: case STOP_ONEANDHALF: - stopb = 0x04; + lcr.stop_bits = LCR_stop_bits::TWO_STOP_BITS; break; } - - write_UART( SC::LCR , data_bit_reg | par | stopb ); // deact DL and set params - - write_UART( SC::FCR , 0xC7); - write_UART( SC::MCR , 0x0B); - - write_UART( SC::IER , 0x0F); - + + write_UART(SerialPortRegister::LCR, lcr.u8); // deact DL and set params + + SerialPort_FifoControlRegister fcr{}; + fcr.enable_fifos = 1; + fcr.clear_receive_fifo = 1; + fcr.clear_transmit_fifo = 1; + fcr.enable_64_byte_fifo = 1; + fcr.trigger_level = FIFO_TRIGGER_LEVEL::TRIGGER_16_OR_56_BYTES; + + write_UART(SerialPortRegister::FCR, fcr.u8); + + SerialPort_ModemControlRegister mcr{}; + mcr.data_terminal_ready = 1; + mcr.request_to_send = 1; + mcr.aux2 = 1; + + write_UART(SerialPortRegister::MCR, mcr.u8); + + SerialPort_InterruptEnableRegister ier{}; + ier.received_data_available_int_enable = 1; + // ier.transmitter_holding_reg_empty_int_enable = 1; // Polling used instead + ier.receiver_line_status_int_enable = 1; + ier.modem_status_int_enable = 1; + + write_UART(SerialPortRegister::IER, ier.u8); + return SR_OK; } -int32 SerialPort::writeData(uint32 offset, uint32 num_bytes, const char*buffer) +size_t SerialPort::writeTransmitBuffer(const char* buffer, size_t size) { - if( offset != 0 ) + size_t nwritten = 0; + while (nwritten < size && eastl::bit_cast( + read_UART(SerialPortRegister::LSR)) + .empty_transmitter_holding_reg) + { + char b = *(buffer + nwritten); + debugAdvanced(A_SERIALPORT, "Write char to serial port: %c (%x)\n", b, b); + write_UART(SerialPortRegister::THR, b); + ++nwritten; + } + + return nwritten; +} + +int32 SerialPort::writeData(uint32 offset, uint32 num_bytes, const char* buffer) +{ + debug(A_SERIALPORT, "Write serial port, buffer: %p, size: %u\n", buffer, num_bytes); + if(offset != 0) return -1; - + size_t jiffies = 0, bytes_written = 0; - - while( ArchThreads::testSetLock( SerialLock ,1 ) && jiffies++ < IO_TIMEOUT ) + + while(ArchThreads::testSetLock(SerialLock , (size_t)1) && jiffies++ < IO_TIMEOUT) ArchInterrupts::yieldIfIFSet(); - - if( jiffies == IO_TIMEOUT ) + + if(jiffies == IO_TIMEOUT) { WriteLock = 0; return -1; } - - WriteLock = bytes_written = 0; - - while( num_bytes -- ) - { + + WriteLock = 0; + bytes_written = 0; + + while(bytes_written < num_bytes) + { jiffies = 0; - - while( !(read_UART( SC::LSR ) & 0x40) && jiffies++ < IO_TIMEOUT ) - ArchInterrupts::yieldIfIFSet(); - - if( jiffies == IO_TIMEOUT) + + size_t nwritten_chunk = writeTransmitBuffer(buffer + bytes_written, num_bytes - bytes_written); + bytes_written += nwritten_chunk; + + if (bytes_written < num_bytes) { - SerialLock = 0; - WriteLock = 0; - return -1; - } - - write_UART( 0, *(buffer++) ); - bytes_written++; + while (!eastl::bit_cast( + read_UART(SerialPortRegister::LSR)) + .empty_transmitter_holding_reg && + jiffies++ < IO_TIMEOUT) + ArchInterrupts::yieldIfIFSet(); + + if (jiffies == IO_TIMEOUT) + { + SerialLock = 0; + WriteLock = 0; + return -1; + } + } } - + SerialLock = 0; return bytes_written; } +void SerialPort::readReceiveBuffers() +{ + SerialPort_LineStatusRegister lsr{}; + lsr.u8 = read_UART(SerialPortRegister::LSR); + while (lsr.data_ready) + { + auto b = read_UART(SerialPortRegister::RBR); + debug(A_SERIALPORT, "Read char from serial port: %c (%x)\n", b, b); + // TODO: FIFO uses a mutex internally -> deadlock when used in interrupt handler + // (this has been broken since the beginning) + in_buffer_.put(b); + lsr.u8 = read_UART(SerialPortRegister::LSR); + } +} + + void SerialPort::irq_handler() { - debug(A_SERIALPORT, "irq_handler: Entered SerialPort IRQ handler"); + debug(A_SERIALPORT, "irq_handler: Entered SerialPort IRQ handler\n"); - uint8 int_id_reg = read_UART( SC::IIR ); - - if( int_id_reg & 0x01 ) - return; // it is not my IRQ or IRQ is handled + SerialPort_InterruptIdentificationRegister int_id_reg{}; + int_id_reg.u8 = read_UART(SerialPortRegister::IIR); + debug(A_SERIALPORT, "irq_handler: IIR: %x\n", int_id_reg.u8); - uint8 int_id = (int_id_reg & 0x06) >> 1; - - switch( int_id ) + if (int_id_reg.int_pending) { - case 0: // Modem status changed - break; - case 1: // Output buffer is empty - WriteLock = 0; - break; - case 2: // Data is available - int_id = read_UART( 0 ); - in_buffer_.put( int_id ); - break; - case 3: // Line status changed - break; + debug(A_SERIALPORT, "Nothing (more) to do here\n"); + return; // it is not my IRQ or IRQ is handled + } + + + switch (int_id_reg.int_status) + { + case IIR_int::MODEM_STATUS: // Modem status changed + { + debug(A_SERIALPORT, "Modem status IRQ\n"); + SerialPort_ModemStatusRegister msr{}; + msr.u8 = read_UART(SerialPortRegister::MSR); + debug(A_SERIALPORT, "Modem status: %x\n", msr.u8); + break; + } + case IIR_int::TRANSMITTER_HOLDING_REG_EMPTY: // Output buffer is empty + { + debug(A_SERIALPORT, "Transmitter holding reg empty IRQ\n"); + WriteLock = 0; + break; + } + case IIR_int::RECEIVED_DATA_AVAILABLE: // Data is available + { + debug(A_SERIALPORT, "Received data available IRQ\n"); + readReceiveBuffers(); + break; + } + case IIR_int::RECEIVER_LINE_STATUS: // Line status changed + { + debug(A_SERIALPORT, "Receiver line status IRQ\n"); + SerialPort_LineStatusRegister lsr{}; + lsr.u8 = read_UART(SerialPortRegister::LSR); + debug(A_SERIALPORT, "Line status: %x\n", lsr.u8); + + if (lsr.overrun_error) + { + debugAlways(A_SERIALPORT, "Overrun error! Receive buffer is full, dropping incoming data\n"); + } + if (lsr.parity_error) + { + debugAlways(A_SERIALPORT, "Parity error!\n"); + } + if (lsr.framing_error) + { + debugAlways(A_SERIALPORT, "Framing error!\n"); + } + if (lsr.break_interrupt) + { + debug(A_SERIALPORT, "Break interrupt\n"); + } + if (lsr.empty_transmitter_holding_reg) + { + debug(A_SERIALPORT, "Empty transmitter holding register\n"); + } + if (lsr.empty_data_holding_reg) + { + debug(A_SERIALPORT, "Empty data holding register\n"); + } + if (lsr.fifo_receive_error) + { + debugAlways(A_SERIALPORT, "FIFO receive error! Clearing FIFO\n"); + SerialPort_FifoControlRegister fcr{}; + fcr.enable_fifos = 1; + fcr.clear_receive_fifo = 1; + fcr.enable_64_byte_fifo = 1; + fcr.trigger_level = FIFO_TRIGGER_LEVEL::TRIGGER_16_OR_56_BYTES; + + write_UART(SerialPortRegister::FCR, fcr.u8); + } + break; + } + case IIR_int::TIMEOUT: + { + debug(A_SERIALPORT, "Timeout IRQ\n"); + readReceiveBuffers(); + break; + } default: // This will never be executed - break; + { + debug(A_SERIALPORT, "Unknown serial port IRQ\n"); + break; } - - return; + } +} + +void SerialPort::write_UART(uint16_t base_port, SerialPortRegister reg, uint8 what) +{ + outportb(base_port + static_cast(reg), what); +} + +uint8 SerialPort::read_UART(uint16_t base_port, SerialPortRegister reg) +{ + return inportb(base_port + static_cast(reg)); } -void SerialPort::write_UART( uint32 reg, uint8 what ) +void SerialPort::write_UART(SerialPortRegister reg, uint8 what) { - outportb( this->port_info_.base_port + reg, what ); + write_UART(port_info_.base_port, reg, what); } -uint8 SerialPort::read_UART( uint32 reg ) +uint8 SerialPort::read_UART(SerialPortRegister reg) { - return inportb( this->port_info_.base_port + reg ); + return read_UART(port_info_.base_port, reg); } diff --git a/arch/x86/common/source/X2Apic.cpp b/arch/x86/common/source/X2Apic.cpp new file mode 100644 index 000000000..b0c3a5c31 --- /dev/null +++ b/arch/x86/common/source/X2Apic.cpp @@ -0,0 +1,87 @@ +#include "X2Apic.h" + +#include "CPUID.h" +#include "InterruptUtils.h" +#include "MSR.h" +#include "SMP.h" + +#include "ArchCpuLocalStorage.h" +#include "ArchInterrupts.h" +#include "ArchMulticore.h" + +#include + +#include "debug.h" + +cpu_local X2Apic cpu_x2apic_impl; + +bool X2Apic::x2ApicSupported() +{ + return cpu_features.cpuHasFeature(CpuFeatures::X86Feature::X2APIC); +} + +void X2Apic::enableX2ApicMode() +{ + debug(APIC, "Enabling x2APIC mode\n"); + assert(x2ApicSupported()); + auto msr = MSR::IA32_APIC_BASE::read(); + assert(msr.enable); + msr.x2apic_enable = 1; + MSR::IA32_APIC_BASE::write(msr); + cpu_lapic = &cpu_x2apic_impl; +} + +bool X2Apic::isEnabled() +{ + auto msr = MSR::IA32_APIC_BASE::read(); + debug(APIC, "x2apic enabled: %u, apic enabled: %u\n", msr.x2apic_enable, msr.enable); + return msr.enable && msr.x2apic_enable; +} + +void X2Apic::writeRegisterImpl(ApicRegisterOffset offset, uint64_t v) +{ + uint32_t vl = v; + uint32_t vh = v >> 32; + MSR::setMSR(x2ApicOffset2Msr(offset), vl, vh); +} + +uint64_t X2Apic::readRegisterImpl(ApicRegisterOffset offset) +{ + assert(offset != ApicRegisterOffset::INTERRUPT_COMMAND_H); + uint64_t v = 0; + MSR::getMSR(x2ApicOffset2Msr(offset), (uint32_t*)&v, ((uint32_t*)&v)+1); + return v; +} + +void X2Apic::writeIcr(InterruptCommandRegisterLow icr_l, uint32_t dest) +{ + InterruptCommandRegister icr{}; + icr.h.x2apic_destination = dest; + icr.l = icr_l; + + writeRegister(icr); +} + +void X2Apic::init() +{ + debug(APIC, "Initializing Local x2APIC\n"); + assert(!initialized_); + assert(isEnabled()); + + id_ = readId(); + auto logical_dest_id = readRegister(); + debug(APIC, "Local x2APIC, id: %x, logical dest: %x\n", apicId(), logical_dest_id); + + setErrorInterruptVector(InterruptVector::APIC_ERROR); + setSpuriousInterruptNumber(100); + + enable(true); + + initialized_ = true; + SMP::currentCpu().setId(id_); +} + +uint32_t X2Apic::readId() +{ + return X2ApicRegisters::ID::read().x2apic_id; +} diff --git a/arch/x86/common/source/arch_serial_constants.cpp b/arch/x86/common/source/arch_serial_constants.cpp index 71af433a4..524835b6e 100644 --- a/arch/x86/common/source/arch_serial_constants.cpp +++ b/arch/x86/common/source/arch_serial_constants.cpp @@ -1,16 +1,16 @@ #include "ArchSerialInfo.h" -uint32 SC::IER = 1; -uint32 SC::IIR = 2; -uint32 SC::FCR = 2; -uint32 SC::LCR = 3; -uint32 SC::MCR = 4; -uint32 SC::LSR = 5; -uint32 SC::MSR = 6; +// uint32 SC::IER = 1; +// uint32 SC::IIR = 2; +// uint32 SC::FCR = 2; +// uint32 SC::LCR = 3; +// uint32 SC::MCR = 4; +// uint32 SC::LSR = 5; +// uint32 SC::MSR = 6; -uint32 SC::MAX_ARCH_PORTS = 4; +// uint32 SC::MAX_ARCH_PORTS = 4; -uint32 SC::UART_16650A = 2; -uint32 SC::UART_16650 = 1; -uint32 SC::UART_OLD = 0; +// uint32 SC::UART_16650A = 2; +// uint32 SC::UART_16650 = 1; +// uint32 SC::UART_OLD = 0; diff --git a/arch/x86/common/source/assert.cpp b/arch/x86/common/source/assert.cpp index 9c3322ff3..6c854e57a 100644 --- a/arch/x86/common/source/assert.cpp +++ b/arch/x86/common/source/assert.cpp @@ -1,16 +1,20 @@ #include "assert.h" -#include "kprintf.h" -#include "panic.h" -#include "debug_bochs.h" + +#include "SMP.h" +#include "Scheduler.h" +#include "SystemState.h" #include "Thread.h" -#include "ArchInterrupts.h" +#include "debug_bochs.h" +#include "kprintf.h" -extern Thread* currentThread; +#include "ArchCpuLocalStorage.h" +#include "ArchInterrupts.h" +#include "ArchMulticore.h" __attribute__((noreturn)) void pre_new_sweb_assert(const char* condition, uint32 line, const char* file) { system_state = KPANIC; - char const *error_string = "KERNEL PANIC: Assertion Failed in File: on Line: "; + const char* error_string = "KERNEL PANIC: Assertion Failed in File: on Line: "; char line_string[5]; ArchInterrupts::disableInterrupts(); writeChar2Bochs('\n'); @@ -20,7 +24,7 @@ __attribute__((noreturn)) void pre_new_sweb_assert(const char* condition, uint32 writeChar2Bochs('\n'); writeLine2Bochs(file); writeChar2Bochs('\n'); - if (currentThread != 0) + if (currentThread) currentThread->printBacktrace(false); uint8 * fb = (uint8*)0xC00B8000; uint32 s=0; @@ -59,25 +63,53 @@ __attribute__((noreturn)) void pre_new_sweb_assert(const char* condition, uint32 } writeLine2Bochs(line_string); writeChar2Bochs('\n'); - while(1); + while(true) + ArchCommon::halt(); unreachable(); } +eastl::atomic_flag assert_print_lock; +__cpu bool in_assert = false; +bool in_assert_pre_cls = false; -void sweb_assert(const char *condition, uint32 line, const char* file) +[[noreturn]] void sweb_assert(const char *condition, uint32 line, const char* file, const char* function) { - ArchInterrupts::disableInterrupts(); - static bool in_assert = false; + bool prev_intr = ArchInterrupts::disableInterrupts(); system_state = KPANIC; - kprintfd("KERNEL PANIC: Assertion %s failed in File %s on Line %d\n",condition, file, line); - if (in_assert) { - kprintfd("PANIC LOOP: How did we get here?\n"); - while(1); - unreachable(); + debug_print_to_fb = false; + + Thread* calling_thread = CpuLocalStorage::ClsInitialized() ? currentThread : nullptr; + bool* in_assert_p = CpuLocalStorage::ClsInitialized() ? &in_assert : &in_assert_pre_cls; + + if (*in_assert_p) { + assert_print_lock.clear(eastl::memory_order_release); + kprintfd("PANIC LOOP: How did we get here?\n"); + while(true) + ArchCommon::halt(); + unreachable(); } - in_assert = true; - if (currentThread != 0) - currentThread->printBacktrace(false); - while(1); + *in_assert_p = true; + + if (SMP::numRunningCpus() > 1) + { + ArchMulticore::stopOtherCpus(); + } + + while (assert_print_lock.test_and_set(eastl::memory_order_acquire)); + + if (calling_thread) + { + debug(BACKTRACE, "CPU %zu backtrace:\n", SMP::currentCpuId()); + if (!prev_intr) + debug(BACKTRACE, "CAUTION: Assertion occurred in interrupt handler that is potentially unrelated to normal thread execution\n"); + calling_thread->printBacktrace(false); + } + + kprintfd("KERNEL PANIC: Assertion %s failed in File %s, Function %s on Line %d, CPU %zd\n", condition, file, function, line, SMP::currentCpuId()); + kprintf("KERNEL PANIC: Assertion %s failed in File %s, Function %s on Line %d, CPU %zd\n", condition, file, function, line, SMP::currentCpuId()); + + assert_print_lock.clear(eastl::memory_order_release); + while(true) + ArchCommon::halt(); unreachable(); } diff --git a/arch/x86/common/source/atkbd.cpp b/arch/x86/common/source/atkbd.cpp index f1a3d02ed..36ca417f0 100644 --- a/arch/x86/common/source/atkbd.cpp +++ b/arch/x86/common/source/atkbd.cpp @@ -24,9 +24,9 @@ void updateKbdLights(uint8 status) inportb(ATKBD_DATA); // should be ACK } -bool kbd_light_numlock = 0; -bool kbd_light_capslock = 0; -bool kbd_light_scrolllock = 0; +bool kbd_light_numlock = false; +bool kbd_light_capslock = false; +bool kbd_light_scrolllock = false; void kbdSetNumlock(bool on) { diff --git a/arch/x86/common/source/debug_bochs.cpp b/arch/x86/common/source/debug_bochs.cpp index 3b67966c7..a5a04e33b 100644 --- a/arch/x86/common/source/debug_bochs.cpp +++ b/arch/x86/common/source/debug_bochs.cpp @@ -1,19 +1,19 @@ #include "debug_bochs.h" -#include "ports.h" +#include "ports.h" -void writeChar2Bochs( char char2Write ) +void writeChar2Bochs(char char2Write) { - outportb( 0xE9, char2Write ); + EMULATOR_DEBUGCONSOLE::write(char2Write); } -void writeLine2Bochs( const char * line2Write ) +void writeLine2Bochs(const char * line2Write) { uint8 counter = 0; while (*line2Write && counter++ < 250) { - writeChar2Bochs( *line2Write ); + writeChar2Bochs(*line2Write); ++line2Write; } } diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 48c73604f..3d016137e 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,4 +1,6 @@ -include_directories( +add_subdirectory(source) + +target_include_directories(kernel PUBLIC include ../arch/common/include ../arch/${ARCH}/include @@ -7,16 +9,14 @@ include_directories( ../arch/${ARCH}/../../common/include include/console include/kernel - include/drivers include/fs include/fs/devicefs + include/fs/devicefs/devices include/fs/minixfs include/fs/pseudofs include/fs/ramfs include/ipc include/mm + include/mm/allocators include/util - include/ustl ) - -add_subdirectory(source) diff --git a/common/include/console/Console.h b/common/include/console/Console.h index 6ffac3471..eec7e18f4 100644 --- a/common/include/console/Console.h +++ b/common/include/console/Console.h @@ -1,61 +1,37 @@ #pragma once -#include "types.h" -#include #include "Mutex.h" - #include "Thread.h" +#include "types.h" + +#include "EASTL/vector.h" + class Terminal; class Console : public Thread { friend class Terminal; - friend class ConsoleManager; - - public: - - enum CONSOLECOLOR - { - BLACK = 0, - BLUE, - GREEN, - CYAN, - RED, - MAGENTA, - BROWN, - WHITE, - DARK_GREY, - BRIGHT_BLUE, - BRIGHT_GREEN, - BRIGHT_CYAN, - PINK, - BRIGHT_MAGENTA, - YELLOW, - BRIGHT_WHITE - }; - - Console(uint32 num_terminals, const char *name); + +public: + Console(uint32 num_terminals, const char* name); + ~Console() override = default; /** * Writes input from the keyboard to the active terminal */ - virtual void Run(); + void Run() override; /** * Checks if the given key is displayable. * @param key the key to check * @return true if displayable */ - bool isDisplayable(uint32 key); + [[nodiscard]] bool isDisplayable(uint32 key) const; - virtual ~Console() - { - } - - uint32 getNumTerminals() const; - Terminal* getActiveTerminal(); - Terminal* getTerminal(uint32 term); + [[nodiscard]] uint32 getNumTerminals() const; + [[nodiscard]] Terminal* getActiveTerminal() const; + [[nodiscard]] Terminal* getTerminal(uint32 term) const; /** * Sets the terminal with the given number active. @@ -67,17 +43,7 @@ class Console : public Thread void lockConsoleForDrawing(); void unLockConsoleForDrawing(); - /** - * Checks if all Console locks are free. - * @return true if all locks are free - */ - bool areLocksFree() - { - return (!(system_state == RUNNING) || (console_lock_.isFree() && locked_for_drawing_ == 0)); - } - - protected: - +protected: /** * Handles special non displayable keys: * F-keys for switching active terminals @@ -87,21 +53,23 @@ class Console : public Thread */ void handleKey(uint32 key); - virtual void consoleClearScreen() =0; - virtual uint32 consoleSetCharacter(uint32 const &row, uint32 const&column, uint8 const &character, - uint8 const &state) =0; - virtual uint32 consoleGetNumRows() const=0; - virtual uint32 consoleGetNumColumns() const=0; - virtual void consoleScrollUp(uint8 const &state) =0; + virtual void consoleClearScreen() = 0; + virtual uint32 consoleSetCharacter(const uint32& row, + const uint32& column, + const uint8& character, + const uint8& state) = 0; + + [[nodiscard]] virtual uint32 consoleGetNumRows() const = 0; + [[nodiscard]] virtual uint32 consoleGetNumColumns() const = 0; - ustl::list terminals_; + virtual void consoleScrollUp(const uint8& state) = 0; + + eastl::vector terminals_; Mutex console_lock_; Mutex set_active_lock_; uint8 locked_for_drawing_; uint32 active_terminal_; - }; extern Console* main_console; - diff --git a/common/include/console/FrameBufferConsole.h b/common/include/console/FrameBufferConsole.h index ad53cdacf..58ca226c6 100644 --- a/common/include/console/FrameBufferConsole.h +++ b/common/include/console/FrameBufferConsole.h @@ -4,26 +4,28 @@ class FrameBufferConsole : public Console { - public: +public: FrameBufferConsole(uint32 num_terminals); + ~FrameBufferConsole() override = default; - virtual uint32 consoleSetCharacter(uint32 const &row, uint32 const&column, uint8 const &character, - uint8 const &state); + uint32 consoleSetCharacter(const uint32& row, + const uint32& column, + const uint8& character, + const uint8& state) override; - private: - virtual void consoleClearScreen(); - virtual uint32 consoleGetNumRows() const; - virtual uint32 consoleGetNumColumns() const; - virtual void consoleScrollUp(uint8 const &state); +private: + void consoleClearScreen() override; + [[nodiscard]] uint32 consoleGetNumRows() const override; + [[nodiscard]] uint32 consoleGetNumColumns() const override; + void consoleScrollUp(const uint8& state) override; void setPixel(uint32 x, uint32 y, uint8 r, uint8 g, uint8 b); - uint16 convertConsoleColor(CONSOLECOLOR color); - void colorsFromState(uint8 const &state, CONSOLECOLOR &fg, CONSOLECOLOR &bg); + static uint16 convertConsoleColor(CONSOLECOLOR color); + static void colorsFromState(const uint8& state, CONSOLECOLOR& fg, CONSOLECOLOR& bg); uint32 x_res_; uint32 y_res_; uint32 bits_per_pixel_; uint32 bytes_per_pixel_; }; - diff --git a/common/include/console/KprintfFlushingThread.h b/common/include/console/KprintfFlushingThread.h new file mode 100644 index 000000000..102c190e2 --- /dev/null +++ b/common/include/console/KprintfFlushingThread.h @@ -0,0 +1,15 @@ +#pragma once + +#include "RingBuffer.h" +#include "Thread.h" + +class KprintfFlushingThread : public Thread +{ +public: + KprintfFlushingThread(RingBuffer* rb); + + void Run() override; + +private: + RingBuffer* rb; +}; diff --git a/common/include/console/Terminal.h b/common/include/console/Terminal.h index 2e3e1e1a6..64e857133 100644 --- a/common/include/console/Terminal.h +++ b/common/include/console/Terminal.h @@ -1,9 +1,11 @@ #pragma once -#include "types.h" #include "Console.h" +#include "VgaColors.h" #include "chardev.h" +#include "types.h" + class Terminal : public CharacterDevice { @@ -16,7 +18,7 @@ class Terminal : public CharacterDevice EN = 0, DE = 1 }; - static uint32 const TERMINAL_BUFFER_SIZE = 256; + static const uint32 TERMINAL_BUFFER_SIZE = 256; /** * Constructor creates the Terminal Character Device. @@ -38,14 +40,14 @@ class Terminal : public CharacterDevice * Writes a string to the terminal. * @param string the string to write */ - void writeString(char const *string); + void writeString(const char* string); /** * Writes a buffer with the given length to the terminal. * @param buffer the buffer to write * @param len the buffer's length */ - void writeBuffer(char const *buffer, size_t len); + void writeBuffer(const char* buffer, size_t len); /** * Writes Data starting at the offset from the buffer with the given length to the terminal. @@ -54,10 +56,10 @@ class Terminal : public CharacterDevice * @param buffer the buffer to write * @return the size */ - virtual int32 writeData(uint32 offset, uint32 size, const char*buffer); + int32 writeData(uint32 offset, uint32 size, const char*buffer) override; - void setForegroundColor(Console::CONSOLECOLOR const &color); - void setBackgroundColor(Console::CONSOLECOLOR const &color); + void setForegroundColor(CONSOLECOLOR const &color); + void setBackgroundColor(CONSOLECOLOR const &color); /** * Reads one character.from the input @@ -94,7 +96,7 @@ class Terminal : public CharacterDevice void clearBuffer(); void putInBuffer(uint32 key); - void initTerminalColors(Console::CONSOLECOLOR fg, Console::CONSOLECOLOR bg); + void initTerminalColors(CONSOLECOLOR fg, CONSOLECOLOR bg); void backspace(); @@ -107,11 +109,6 @@ class Terminal : public CharacterDevice void setLayout(Terminal::LAYOUTS layout); - bool isLockFree() - { - return mutex_.isFree(); - } - protected: void setAsActiveTerminal(); @@ -126,14 +123,14 @@ class Terminal : public CharacterDevice */ void writeInternal(char character); - uint32 getNumRows() const; - uint32 getNumColumns() const; + [[nodiscard]] uint32 getNumRows() const; + [[nodiscard]] uint32 getNumColumns() const; uint32 setCharacter(uint32 row, uint32 column, uint8 character); void scrollUp(); - bool isLetter(uint32 key); - bool isNumber(uint32 key); + static bool isLetter(uint32 key); + static bool isNumber(uint32 key); void clearScreen(); void fullRedraw(); @@ -155,4 +152,3 @@ class Terminal : public CharacterDevice LAYOUTS layout_; }; - diff --git a/common/include/console/TextConsole.h b/common/include/console/TextConsole.h index 48eba21d8..3508385e3 100644 --- a/common/include/console/TextConsole.h +++ b/common/include/console/TextConsole.h @@ -4,16 +4,16 @@ class TextConsole : public Console { - public: - TextConsole ( uint32 num_terminals ); - - private: +public: + TextConsole(uint32 num_terminals); + ~TextConsole() override = default; +private: /** * Clears the console screen. * @pre console should be locked for drawing */ - virtual void consoleClearScreen(); + void consoleClearScreen() override; /** * Sets the given character to the given position on the console. @@ -24,24 +24,26 @@ class TextConsole : public Console * @param state not implemented - should change the output color * @return 0 */ - virtual uint32 consoleSetCharacter ( uint32 const &row, uint32 const&column, uint8 const &character, uint8 const &state ); + uint32 consoleSetCharacter(const uint32& row, + const uint32& column, + const uint8& character, + const uint8& state) override; /** * Returns the console's number of rows. * @return the number of rows */ - virtual uint32 consoleGetNumRows() const; + [[nodiscard]] uint32 consoleGetNumRows() const override; /** * Returns the console's number of Columns. * @return the number of columns */ - virtual uint32 consoleGetNumColumns() const; + [[nodiscard]] uint32 consoleGetNumColumns() const override; /** * Scrolls up the console. * @pre console should be locked for drawing */ - virtual void consoleScrollUp(uint8 const &state); + void consoleScrollUp(const uint8& state) override; }; - diff --git a/common/include/console/VgaColors.h b/common/include/console/VgaColors.h new file mode 100644 index 000000000..eff1dcda1 --- /dev/null +++ b/common/include/console/VgaColors.h @@ -0,0 +1,21 @@ +#pragma once + +enum CONSOLECOLOR +{ + BLACK = 0, + BLUE, + GREEN, + CYAN, + RED, + MAGENTA, + BROWN, + WHITE, + DARK_GREY, + BRIGHT_BLUE, + BRIGHT_GREEN, + BRIGHT_CYAN, + PINK, + BRIGHT_MAGENTA, + YELLOW, + BRIGHT_WHITE +}; diff --git a/common/include/console/debug.h b/common/include/console/debug.h index df50d37bf..551321a8f 100644 --- a/common/include/console/debug.h +++ b/common/include/console/debug.h @@ -4,6 +4,7 @@ enum AnsiColor { + Ansi_Black = 30, Ansi_Red = 31, Ansi_Green = 32, Ansi_Yellow = 33, @@ -11,48 +12,95 @@ enum AnsiColor Ansi_Magenta = 35, Ansi_Cyan = 36, Ansi_White = 37, + Ansi_BrightBlack = 90, + Ansi_BrightRed = 91, + Ansi_BrightGreen = 92, + Ansi_BrightYellow = 93, + Ansi_BrightBlue = 94, + Ansi_BrightMagenta = 95, + Ansi_BrightCyan = 96, + Ansi_BrightWhite = 97, }; -#define OUTPUT_ENABLED 0x80000000 -#define OUTPUT_ADVANCED 0x70000000 + +#define DEBUG_COLOR 1 +#define DEBUG_TO_FB 0 + + +#define __STRINGIFY2(x) #x +#define __STRINGIFY(x) __STRINGIFY2(x) +#define DEBUG_STR_HERE2(file, line) " at " file ":" __STRINGIFY(line) +#define DEBUG_STR_HERE DEBUG_STR_HERE2(__FILE__, __LINE__) + +#define OUTPUT_ENABLED 0x80000000 +#define OUTPUT_ADVANCED 0x40000000 #define OUTPUT_FLAGS (OUTPUT_ENABLED | OUTPUT_ADVANCED) -#ifndef NOCOLOR -#define DEBUG_FORMAT_STRING "\033[1;%zum[%-11s]\033[0;39m" -#define COLOR_PARAM(flag) (flag & ~OUTPUT_FLAGS), #flag +#define DEBUG_FORMAT_STRING_COLOR "\033[1;%zum[%-11s]\033[0;39m" +#define DEBUG_FORMAT_STRING_NOCOLOR "[%-11s]" + +#define FLAG_PARAM_COLOR(flag) (flag & ~OUTPUT_FLAGS), #flag +#define FLAG_PARAM_NOCOLOR(flag) #flag + +#if DEBUG_COLOR +#define DEBUG_FORMAT_STRING DEBUG_FORMAT_STRING_COLOR +#define FLAG_PARAM FLAG_PARAM_COLOR #else -#define DEBUG_FORMAT_STRING "[%-11s]" -#define COLOR_PARAM(flag) #flag +#define DEBUG_FORMAT_STRING DEBUG_FORMAT_STRING_NOCOLOR +#define FLAG_PARAM FLAG_PARAM_NOCOLOR #endif +#define DEBUG_PRINT_FSTRING_CONCAT(printfunc, flag_fstring, flag_params, fstring, ...) \ + printfunc(flag_fstring fstring, flag_params, ##__VA_ARGS__) + +extern bool debug_print_to_fb; + #ifndef EXE2MINIXFS -#define debug(flag, ...) do { if (flag & OUTPUT_ENABLED) { kprintfd(DEBUG_FORMAT_STRING, COLOR_PARAM(flag)); kprintfd(__VA_ARGS__); } } while (0) +#define debugAlways(flag, ...) do { \ + DEBUG_PRINT_FSTRING_CONCAT(kprintfd, DEBUG_FORMAT_STRING, FLAG_PARAM(flag), ##__VA_ARGS__); \ + if(debug_print_to_fb) { \ + DEBUG_PRINT_FSTRING_CONCAT(kprintf, DEBUG_FORMAT_STRING_NOCOLOR, FLAG_PARAM_NOCOLOR(flag), ##__VA_ARGS__); \ + } \ + } while (0) + +#define debug(flag, ...) do { \ + if (flag & OUTPUT_ENABLED) { \ + debugAlways(flag, ##__VA_ARGS__); \ + } } while (0) + +#define debugAdvanced(flag, ...) do { \ + if (flag & OUTPUT_ENABLED && flag & OUTPUT_ADVANCED) { \ + debugAlways(flag, ##__VA_ARGS__); \ + } } while (0) #endif -//group Block Device -const size_t BD_MANAGER = Ansi_Yellow; -const size_t BD_VIRT_DEVICE = Ansi_Yellow; + + //group Console -const size_t KPRINTF = Ansi_Yellow; +const size_t KPRINTF = Ansi_Yellow | OUTPUT_ENABLED; +const size_t CONSOLE = Ansi_Yellow | OUTPUT_ENABLED; +const size_t TERMINAL = Ansi_Yellow; //group kernel const size_t LOCK = Ansi_Yellow | OUTPUT_ENABLED; const size_t LOADER = Ansi_White | OUTPUT_ENABLED; const size_t SCHEDULER = Ansi_Yellow | OUTPUT_ENABLED; +const size_t SCHEDULER_LOCK = Ansi_Red; const size_t SYSCALL = Ansi_Blue | OUTPUT_ENABLED; const size_t MAIN = Ansi_Red | OUTPUT_ENABLED; const size_t THREAD = Ansi_Magenta | OUTPUT_ENABLED; const size_t USERPROCESS = Ansi_Cyan | OUTPUT_ENABLED; +const size_t INITTHREAD = Ansi_Red | OUTPUT_ENABLED; const size_t PROCESS_REG = Ansi_Yellow | OUTPUT_ENABLED; const size_t BACKTRACE = Ansi_Cyan | OUTPUT_ENABLED; const size_t USERTRACE = Ansi_Red | OUTPUT_ENABLED; //group memory management -const size_t PM = Ansi_Green | OUTPUT_ENABLED; -const size_t PAGEFAULT = Ansi_Green | OUTPUT_ENABLED; -const size_t CPU_ERROR = Ansi_Red | OUTPUT_ENABLED; +const size_t PM = Ansi_Green | OUTPUT_ENABLED; +const size_t PAGEFAULT = Ansi_Green | OUTPUT_ENABLED; +const size_t CPU_ERROR = Ansi_Red | OUTPUT_ENABLED; const size_t KMM = Ansi_Yellow; //group driver @@ -60,14 +108,24 @@ const size_t DRIVER = Ansi_Yellow; const size_t ATA_DRIVER = Ansi_Yellow; const size_t IDE_DRIVER = Ansi_Yellow; const size_t MMC_DRIVER = Ansi_Yellow; +const size_t RAMDISK = Ansi_Yellow; + +//group Block Device +const size_t BD_MANAGER = Ansi_Yellow; +const size_t BD_VIRT_DEVICE = Ansi_Yellow; //group arch -const size_t A_BOOT = Ansi_Yellow | OUTPUT_ENABLED; -const size_t A_COMMON = Ansi_Yellow; +const size_t A_BOOT = Ansi_Yellow | OUTPUT_ENABLED; +const size_t A_COMMON = Ansi_Yellow | OUTPUT_ENABLED; const size_t A_MEMORY = Ansi_Yellow; const size_t A_SERIALPORT = Ansi_Yellow; const size_t A_KB_MANAGER = Ansi_Yellow; const size_t A_INTERRUPTS = Ansi_Yellow; +const size_t A_MULTICORE = Ansi_Yellow | OUTPUT_ENABLED; + +const size_t ACPI = Ansi_Red | OUTPUT_ENABLED; +const size_t APIC = Ansi_Yellow | OUTPUT_ENABLED; +const size_t PIC_8259 = Ansi_Yellow | OUTPUT_ENABLED; //group file system const size_t FS = Ansi_Yellow; diff --git a/common/include/console/kprintf.h b/common/include/console/kprintf.h index a95c84462..882e74248 100644 --- a/common/include/console/kprintf.h +++ b/common/include/console/kprintf.h @@ -4,6 +4,7 @@ #include "stdarg.h" #include "types.h" + #include "debug.h" /** diff --git a/common/include/console/panic.h b/common/include/console/panic.h deleted file mode 100644 index 8d3d5f267..000000000 --- a/common/include/console/panic.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "types.h" - -#define S1(x) #x -#define S2(x) S1(x) -#define LOCATION __FILE__ " : " S2(__LINE__) - - -/** - * kernel panic function writes the message and the stack trace to the screen and - * dies after that - * - */ -void kpanict( const char *message ); diff --git a/common/include/drivers/Device.h b/common/include/drivers/Device.h new file mode 100644 index 000000000..f163893e0 --- /dev/null +++ b/common/include/drivers/Device.h @@ -0,0 +1,88 @@ +#pragma once + +#include "ranges.h" + +#include "EASTL/string.h" +#include "EASTL/vector.h" + +#include "debug.h" + +class DeviceDriver; +class Inode; + +struct Device +{ + Device(const eastl::string& device_type, Device* parent = nullptr, DeviceDriver* driver = nullptr) : + parent_(parent), + driver_(driver), + device_name_(device_type) + { + debug(DRIVER, "Create device '%s'\n", deviceName().c_str()); + if (parent) + { + parent->addSubDevice(*this); + } + } + virtual ~Device() = default; + + + Device& setParent(Device& parent) + { + parent_ = &parent; + return *this; + } + + Device* parent() + { + return parent_; + } + + DeviceDriver* driver() + { + return driver_; + } + + virtual Inode* deviceInode() { return nullptr; } + + Device& setDriver(DeviceDriver& driver) + { + driver_ = &driver; + return *this; + } + + virtual const eastl::string& deviceName() { return device_name_; } + + void setDeviceName(const eastl::string& name) { device_name_ = name; } + + auto subdevices() { return ranges::subrange(subdevices_); } + + virtual void addSubDevice(Device& subdevice) + { + debug(DRIVER, "Adding sub device %s to %s\n", subdevice.deviceName().c_str(), + deviceName().c_str()); + if (subdevice.parent()) + { + debug(DRIVER, "ERROR: subdevice added to %s already has parent %s\n", deviceName().c_str(), subdevice.parent()->deviceName().c_str()); + } + assert(!subdevice.parent() || subdevice.parent() == this); + subdevice.setParent(*this); + subdevices_.push_back(&subdevice); + } + + void printSubDevices(int level = 0) + { + kprintfd("%*s- %s\n", level*2, "", deviceName().c_str()); + for (auto&& x : subdevices()) + { + x->printSubDevices(level + 1); + } + } + + +protected: + + Device* parent_; // Usually a bus or controller, NULL for top level devices + DeviceDriver* driver_; + eastl::string device_name_; + eastl::vector subdevices_; +}; diff --git a/common/include/drivers/DeviceBus.h b/common/include/drivers/DeviceBus.h new file mode 100644 index 000000000..9d9f254f3 --- /dev/null +++ b/common/include/drivers/DeviceBus.h @@ -0,0 +1,85 @@ +#pragma once + +#include "Device.h" +#include "DeviceDriver.h" + +#include "EASTL/string.h" +#include "EASTL/vector.h" + +#include "debug.h" + +template +class DeviceBus : public Device +{ + using device_description_type = DeviceDescriptionType_; +private: + + class BusDeviceDriver : public virtual DeviceDriver + { + public: + ~BusDeviceDriver() override = default; + virtual bool probe(const device_description_type&) = 0; + }; + +public: + + // Directly use DeviceDriver instead of BusDeviceDriver (which does not work) + using bus_device_driver_type = eastl:: + conditional_t, + BusDeviceDriver, + DeviceDriver>; + + DeviceBus(const eastl::string bus_name) : + Device(bus_name) + { + debug(DRIVER, "Init '%s' bus\n", deviceName().c_str()); + } + + ~DeviceBus() override = default; + + void addSubDevice(Device& device) override + { + debug(DRIVER, "Add '%s' device to '%s' bus\n", device.deviceName().c_str(), + deviceName().c_str()); + Device::addSubDevice(device); + + if (!device.driver()) + { + debug(DRIVER, "Added device '%s' without driver\n", + device.deviceName().c_str()); + } + } + + void registerDriver(bus_device_driver_type& driver) + { + debug(DRIVER, "Add driver '%s' to '%s' bus\n", driver.driverName().c_str(), + deviceName().c_str()); + + drivers_.push_back(&driver); + driver.setParentDevice(*this); + driver.doDeviceDetection(); + } + + const eastl::vector& drivers() { return drivers_; } + + bool probeDrivers(auto&& device_description) + { + debug(DRIVER, "Probe '%s' drivers for device compatibility\n", deviceName().c_str()); + for (auto& d : drivers_) + { + if (d->probe(device_description)) + { + debug(DRIVER, "Found compatible driver '%s' for device description\n", d->driverName().c_str()); + return true; + } + } + + debug(DRIVER, "Could not find compatible driver for device description\n"); + return false; + } + +private: + eastl::vector drivers_; +}; + +DeviceBus<>& deviceTreeRoot(); diff --git a/common/include/drivers/DeviceDriver.h b/common/include/drivers/DeviceDriver.h new file mode 100644 index 000000000..aff82f099 --- /dev/null +++ b/common/include/drivers/DeviceDriver.h @@ -0,0 +1,100 @@ +#pragma once + +#include "Device.h" +#include "IrqDomain.h" + +#include "ranges.h" +#include "transform.h" + +#include "EASTL/string.h" +#include "EASTL/vector.h" + +#include "debug.h" + +class IrqDomain; + +// Abstract interface for device drivers +class DeviceDriver +{ +public: + virtual ~DeviceDriver() = default; + + virtual const eastl::string& driverName() = 0; + virtual void initDriver() = 0; + virtual void doDeviceDetection() = 0; + virtual void cpuLocalInit() = 0; + virtual Device* parentDevice() = 0; + virtual void setParentDevice(Device& device) = 0; +}; + +// Provides default implementation for DeviceDriver functions +class BasicDeviceDriver : public virtual DeviceDriver +{ +public: + BasicDeviceDriver(const eastl::string& name) : + driver_name_(name) + { + debug(DRIVER, "Driver '%s' created\n", driverName().c_str()); + } + + ~BasicDeviceDriver() override = default; + + const eastl::string& driverName() override + { + return driver_name_; + } + + void initDriver() override + { + debug(DRIVER, "Init device driver '%s'\n", driverName().c_str()); + } + + void doDeviceDetection() override + { + debug(DRIVER, "Driver '%s' device detection\n", driverName().c_str()); + } + + // Allow drivers to initialize cpu local data/devices + // Do nothing by default + void cpuLocalInit() override { } + + Device* parentDevice() override + { + return parent_device_; + } + + void setParentDevice(Device& device) override + { + parent_device_ = &device; + } + +private: + eastl::string driver_name_; + Device* parent_device_; +}; + +// Additional driver functions for concrete device types +template +class Driver : public virtual DeviceDriver +{ +public: + using device_type = T; + + Driver() = default; + ~Driver() override = default; + +protected: + void bindDevice(T& device) + { + device.setDriver(*this); + devices_.push_back(&device); + if (parentDevice() && !device.parent()) + { + parentDevice()->addSubDevice(device); + } + } + + eastl::vector devices_; + +private: +}; diff --git a/common/include/drivers/platform/PlatformBus.h b/common/include/drivers/platform/PlatformBus.h new file mode 100644 index 000000000..3345c6229 --- /dev/null +++ b/common/include/drivers/platform/PlatformBus.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Device.h" +#include "DeviceBus.h" + +struct PlatformBus : public DeviceBus<> +{ + PlatformBus() : + DeviceBus("Platform") + { + } + + ~PlatformBus() override = default; + + static PlatformBus& instance(); + + static void initPlatformBus(); +}; diff --git a/common/include/fs/BDManager.h b/common/include/fs/BDManager.h index 16dda7837..c7637aff7 100644 --- a/common/include/fs/BDManager.h +++ b/common/include/fs/BDManager.h @@ -1,6 +1,9 @@ #pragma once -#include +#include + +#include "EASTL/atomic.h" +#include "EASTL/list.h" class BDRequest; class BDVirtualDevice; @@ -9,18 +12,13 @@ class BDManager { public: BDManager(); - ~BDManager(); + ~BDManager() = default; /** * returns singleton instance * @return the block device manager instance */ - static BDManager *getInstance(); - - /** - * detects all devices present - */ - void doDeviceDetection(); + static BDManager& instance(); /** * adds the given device to the manager @@ -33,43 +31,41 @@ class BDManager * @param dev_num the device number * @return the device */ - BDVirtualDevice *getDeviceByNumber(uint32 dev_num); + BDVirtualDevice* getDeviceByNumber(uint32_t dev_num); /** * returns the device with the given name * @param dev_name the device name * @return the device */ - BDVirtualDevice *getDeviceByName(const char *dev_name); + BDVirtualDevice* getDeviceByName(const char *dev_name); /** * returns the number of devices in the bd manager * @return the number of devices */ - uint32 getNumberOfDevices(); + uint32_t getNumberOfDevices() const; /** * adds the given request to the device given in the request * @param bdr the request */ - void addRequest(BDRequest *bdr); + void addRequest(BDRequest* bdr); /** * calls seviceIRQ on the device the irq with the given number is on * after that probeIRQ is false * @param irq_num the irq number */ - void serviceIRQ(uint32 irq_num); + void serviceIRQ(uint32_t irq_num); /** * gets false when the irq is serviced */ - bool probeIRQ; + eastl::atomic_flag probeIRQ{true}; - ustl::list device_list_; + eastl::list device_list_; protected: - static BDManager *instance_; + static BDManager* instance_; }; - - diff --git a/common/include/fs/BDVirtualDevice.h b/common/include/fs/BDVirtualDevice.h index 53ebec195..859b87280 100644 --- a/common/include/fs/BDVirtualDevice.h +++ b/common/include/fs/BDVirtualDevice.h @@ -1,11 +1,11 @@ #pragma once #include "types.h" -#include "ulist.h" -#include "ustring.h" -class BDDriver; -class BDRequest; +#include "EASTL/list.h" +#include "EASTL/string.h" + +#include "assert.h" class BDDriver; class BDRequest; @@ -13,8 +13,7 @@ class BDRequest; class BDVirtualDevice { public: - BDVirtualDevice(BDDriver *driver, uint32 offset, uint32 num_sectors, uint32 sector_size, const char *name, - bool writable); + BDVirtualDevice(BDDriver *driver, uint32 offset, uint32 num_sectors, uint32 sector_size, const char *name, bool writable); void addRequest(BDRequest *command); @@ -59,7 +58,7 @@ class BDVirtualDevice * @param buffer data, that should be written * */ - [[nodiscard]] virtual int32 writeData(uint32 offset, uint32 size, char *buffer); + [[nodiscard]] virtual int32 writeData(uint32 offset, uint32 size, const char *buffer); /** * the PartitionType is a 8bit field in the PartitionTable of a MBR @@ -96,6 +95,5 @@ class BDVirtualDevice bool writable_; BDDriver* driver_; uint8 partition_type_; - ustl::string name_; + eastl::string name_; }; - diff --git a/common/include/fs/Dentry.h b/common/include/fs/Dentry.h index 2d50d89b7..88e33b546 100644 --- a/common/include/fs/Dentry.h +++ b/common/include/fs/Dentry.h @@ -1,10 +1,12 @@ #pragma once -#include "types.h" -#include #include "kstring.h" -#include -#include "ustring.h" + +#include "types.h" + +#include "EASTL/algorithm.h" +#include "EASTL/list.h" +#include "EASTL/string.h" class Inode; @@ -34,7 +36,7 @@ class Dentry /** * This list_head is used to link together all the children of the dentry. */ - ustl::list d_child_; + eastl::list d_child_; /** * For a directory that has had a file-system mounted on it, this points to @@ -117,6 +119,8 @@ class Dentry */ bool emptyChild(); + bool emptyChild(std::initializer_list exceptDentries); + /** * get the number of the child * @return the number of childs @@ -134,7 +138,7 @@ class Dentry * return the name of the dentry * @return the dentry's name */ - const char* getName(); + const char* getName() const; /** * This should compare the name with the all names of the d_child_ list. @@ -158,8 +162,7 @@ class Dentry public: Dentry(Inode* inode); // root dentry - Dentry(Inode* inode, Dentry* parent, const ustl::string& name); // named dentry + Dentry(Inode* inode, Dentry* parent, const eastl::string& name); // named dentry virtual ~Dentry(); - ustl::string d_name_; + eastl::string d_name_; }; - diff --git a/common/include/fs/File.h b/common/include/fs/File.h index 01fa0c405..fb5b713ed 100644 --- a/common/include/fs/File.h +++ b/common/include/fs/File.h @@ -1,7 +1,8 @@ #pragma once #include "types.h" -#include "ulist.h" + +#include "EASTL/list.h" class Superblock; class Inode; @@ -73,7 +74,7 @@ class File /** * List of open file descriptors */ - ustl::list f_fds_; + eastl::list f_fds_; public: /** @@ -118,10 +119,10 @@ class File * @param origin is the on off SEEK_SET, SEEK_CUR and SEEK_END. * @returns the offset from the start off the file or -1 on failure. */ - l_off_t lseek(l_off_t offset, uint8 origin); + virtual l_off_t lseek(l_off_t offset, uint8 origin); /** - * not implemented here + * not implemented here (do nothing by default) * reads from the file * @param buffer is the buffer where the data is written to * @param count is the number of bytes to read. @@ -133,7 +134,7 @@ class File } /** - * not implemented here + * not implemented here (do nothing by default) * write to the file * @param buffer is the buffer where the data is read from * @param count is the number of bytes to write. @@ -145,7 +146,7 @@ class File } /** - * Opens the file + * Opens the file (do nothing by default) * @param inode is the inode the read the file from. */ virtual int32 open(uint32) @@ -154,7 +155,7 @@ class File } /** - * not implemented here + * not implemented here (do nothing by default) * Close the file * @param inode is close, the superblock has the information, that this * inode is not use anymore. @@ -165,14 +166,41 @@ class File } /** - * not implemented here + * not implemented here (do nothing by default) * Flush all off the file's write operations. The File will be written to disk. * @return is the error code of the flush operation. */ - virtual int32 flush() - { - return 0; - } + virtual int32 flush(); virtual uint32 getSize(); }; + + +/** + * Base class for simple files with standard read/write behaviour + */ +class SimpleFile : public File +{ +public: + SimpleFile(Inode* inode, Dentry* dentry, uint32 flag); + ~SimpleFile() override = default; + + int32 read(char* buffer, size_t count, l_off_t offset) override; + int32 write(const char* buffer, size_t count, l_off_t offset) override; +private: +}; + +/** + * Base class for simple files with no automatic offset advance + */ +class NoOffsetFile : public File +{ +public: + NoOffsetFile(Inode* inode, Dentry* dentry, uint32 flag); + ~NoOffsetFile() override = default; + + int32 read(char* buffer, size_t count, l_off_t offset) override; + int32 write(const char* buffer, size_t count, l_off_t offset) override; + +private: +}; diff --git a/common/include/fs/FileDescriptor.h b/common/include/fs/FileDescriptor.h index 5ed96bc4b..6b35751d7 100644 --- a/common/include/fs/FileDescriptor.h +++ b/common/include/fs/FileDescriptor.h @@ -1,10 +1,12 @@ #pragma once -#include "types.h" -#include "ulist.h" -#include "umap.h" #include "Mutex.h" +#include "types.h" + +#include "EASTL/list.h" +#include "EASTL/map.h" + class File; class FileDescriptor; class FileDescriptorList; @@ -16,10 +18,10 @@ class FileDescriptor File* file_; public: - FileDescriptor ( File* file ); + FileDescriptor(File* file); virtual ~FileDescriptor(); - uint32 getFd() { return fd_; } - File* getFile() { return file_; } + [[nodiscard]] uint32 getFd() const { return fd_; } + [[nodiscard]] File* getFile() const { return file_; } friend File; }; @@ -32,11 +34,11 @@ class FileDescriptorList int add(FileDescriptor* fd); int remove(FileDescriptor* fd); - FileDescriptor* getFileDescriptor(uint32 fd); + [[nodiscard]] FileDescriptor* getFileDescriptor(uint32 fd); + + static FileDescriptorList& globalFdList(); private: - ustl::list fds_; + eastl::list fds_; Mutex fd_lock_; }; - -extern FileDescriptorList global_fd_list; diff --git a/common/include/fs/FileSystemInfo.h b/common/include/fs/FileSystemInfo.h index 9f6967256..75eabe0eb 100644 --- a/common/include/fs/FileSystemInfo.h +++ b/common/include/fs/FileSystemInfo.h @@ -1,9 +1,11 @@ #pragma once -#include "types.h" -#include "ustring.h" #include "Path.h" +#include "types.h" + +#include "EASTL/string.h" + class Dentry; class VfsMount; @@ -23,7 +25,7 @@ class FileSystemInfo public: FileSystemInfo(); - ~FileSystemInfo(); + ~FileSystemInfo() = default; FileSystemInfo(const FileSystemInfo& fsi); /** @@ -67,4 +69,3 @@ extern FileSystemInfo* default_working_dir; // you use a different getcwd() method depending on where your cpp is being compiled // (it can come either from Thread.cpp or from exe2minixfs.cpp) FileSystemInfo* getcwd(); - diff --git a/common/include/fs/FileSystemType.h b/common/include/fs/FileSystemType.h index e2cfab455..5c1d04809 100644 --- a/common/include/fs/FileSystemType.h +++ b/common/include/fs/FileSystemType.h @@ -19,7 +19,7 @@ class FileSystemType public: FileSystemType(const char *fs_name); - virtual ~FileSystemType(); + virtual ~FileSystemType() = default; FileSystemType const &operator =(FileSystemType const &instance) { diff --git a/common/include/fs/Inode.h b/common/include/fs/Inode.h index 021c79f1d..e1e8e1257 100644 --- a/common/include/fs/Inode.h +++ b/common/include/fs/Inode.h @@ -1,10 +1,13 @@ #pragma once -#include "types.h" -#include "kprintf.h" -#include -#include #include "Dentry.h" +#include "kprintf.h" + +#include "types.h" + +#include "EASTL/atomic.h" +#include "EASTL/list.h" + #include "assert.h" class File; @@ -41,12 +44,12 @@ class Superblock; class Inode { protected: - ustl::list i_dentrys_; + eastl::list i_dentrys_; /** * The (open) file of this inode. */ - ustl::list i_files_; + eastl::list i_files_; /** * the number of Dentry links to this inode. @@ -74,7 +77,7 @@ class Inode * There are three possible inode state bits: I_DIRTY, I_LOCK, I_UNUSED. */ uint32 i_state_; - + /** * The inodes permission flag */ @@ -137,7 +140,7 @@ class Inode */ virtual int32 symlink(Inode */*inode*/, Dentry */*dentry*/, const char */*link_name*/) { - return 0; + return -1; } /** @@ -256,7 +259,7 @@ class Inode return i_type_; } - ustl::list& getDentrys() + eastl::list& getDentrys() { return i_dentrys_; } @@ -276,7 +279,7 @@ class Inode return i_mode_; } - int32 flush() + virtual int32 flush() { return 0; } diff --git a/common/include/fs/MasterBootRecord.h b/common/include/fs/MasterBootRecord.h new file mode 100644 index 000000000..0817818a6 --- /dev/null +++ b/common/include/fs/MasterBootRecord.h @@ -0,0 +1,154 @@ +#pragma once + +#include + +class BDDriver; +class BDVirtualDevice; + +struct MasterBootRecord +{ + struct PartitionEntry + { + enum class BootableStatus : uint8_t + { + NOT_BOOTABLE = 0, + BOOTABLE = 0x80, + }; + + struct CHSAddress + { + uint8_t head; + uint8_t sect; + uint8_t cyl; + }; + + BootableStatus bootable; + CHSAddress first_sector; + uint8_t type; + CHSAddress last_sector; + uint32_t first_sector_lba; + uint32_t num_sectors; + } __attribute__((packed)); + + static_assert(sizeof(PartitionEntry) == 16); + + static constexpr uint16_t PC_MBR_SIGNATURE = 0xAA55; + + uint8_t bootinst[446]; // GRUB space + PartitionEntry parts[4]; + uint16_t signature; // set to 0xAA55 for PC MBR +} __attribute__((packed)); + +static_assert(sizeof(MasterBootRecord) == 512); + +/* + Partition types (taken from fdisk) + + 00 Empty 27 Hidden NTFS Win 82 Linux swap / So c1 DRDOS/sec (FAT- + 01 FAT12 39 Plan 9 83 Linux c4 DRDOS/sec (FAT- + 02 XENIX root 3c PartitionMagic 84 OS/2 hidden or c6 DRDOS/sec (FAT- + 03 XENIX usr 40 Venix 80286 85 Linux extended c7 Syrinx + 04 FAT16 <32M 41 PPC PReP Boot 86 NTFS volume set da Non-FS data + 05 Extended 42 SFS 87 NTFS volume set db CP/M / CTOS / . + 06 FAT16 4d QNX4.x 88 Linux plaintext de Dell Utility + 07 HPFS/NTFS/exFAT 4e QNX4.x 2nd part 8e Linux LVM df BootIt + 08 AIX 4f QNX4.x 3rd part 93 Amoeba e1 DOS access + 09 AIX bootable 50 OnTrack DM 94 Amoeba BBT e3 DOS R/O + 0a OS/2 Boot Manag 51 OnTrack DM6 Aux 9f BSD/OS e4 SpeedStor + 0b W95 FAT32 52 CP/M a0 IBM Thinkpad hi ea Linux extended + 0c W95 FAT32 (LBA) 53 OnTrack DM6 Aux a5 FreeBSD eb BeOS fs + 0e W95 FAT16 (LBA) 54 OnTrackDM6 a6 OpenBSD ee GPT + 0f W95 Ext'd (LBA) 55 EZ-Drive a7 NeXTSTEP ef EFI (FAT-12/16/ + 10 OPUS 56 Golden Bow a8 Darwin UFS f0 Linux/PA-RISC b + 11 Hidden FAT12 5c Priam Edisk a9 NetBSD f1 SpeedStor + 12 Compaq diagnost 61 SpeedStor ab Darwin boot f4 SpeedStor + 14 Hidden FAT16 <3 63 GNU HURD or Sys af HFS / HFS+ f2 DOS secondary + 16 Hidden FAT16 64 Novell Netware b7 BSDI fs f8 EBBR protective + 17 Hidden HPFS/NTF 65 Novell Netware b8 BSDI swap fb VMware VMFS + 18 AST SmartSleep 70 DiskSecure Mult bb Boot Wizard hid fc VMware VMKCORE + 1b Hidden W95 FAT3 75 PC/IX bc Acronis FAT32 L fd Linux raid auto + 1c Hidden W95 FAT3 80 Old Minix be Solaris boot fe LANstep + 1e Hidden W95 FAT1 81 Minix / old Lin bf Solaris ff BBT +*/ + +enum PartitionType +{ + EMPTY = 0x00, + FAT16 = 0x04, + DOS_EXTENDED_CHS = 0x05, + NTFS = 0x07, + FAT32_CHS = 0x0B, + FAT32_LBA = 0x0C, + FAT16B_LBA = 0x0E, + WINDOWS_EXTENDED_LBA = 0x0F, + PLAN9 = 0x39, + MINIXFS_ALT = 0x41, // Same as 0x81 + SWAP_ALT = 0x42, // Same as 0x82 + MINIXFS_OLD = 0x80, + MINIXFS = 0x81, + SWAP = 0x82, + LINUX_ANY_NATIVE = 0x83, + LINUX_EXTENDED = 0x85, + LINUX_LVM = 0x8E, + HFS = 0xAF, + LINUX_LUKS = 0xE8, + GPT_PROTECTIVE_MBR = 0xEE, + EFI_SYSTEM = 0xEF, + LINUX_LVM_OLD = 0xFE, +}; + +constexpr const char* partitionTypeName(uint8_t type) +{ + switch (type) + { + case EMPTY: + return "empty"; + case FAT16: + return "FAT16"; + case DOS_EXTENDED_CHS: + return "DOS Extended"; + case NTFS: + return "NTFS"; + case FAT32_CHS: + return "FAT32 (CHS)"; + case FAT32_LBA: + return "FAT32 (LBA)"; + case FAT16B_LBA: + return "FAT16B (LBA)"; + case WINDOWS_EXTENDED_LBA: + return "Windows Extended (LBA)"; + case PLAN9: + return "Plan9"; + case MINIXFS_ALT: + return "Minixfs (alternative)"; + case SWAP_ALT: + return "Swap (alternative)"; + case MINIXFS_OLD: + return "Minix FS (old)"; + case MINIXFS: + return "Minix FS"; + case SWAP: + return "Swap"; + case LINUX_ANY_NATIVE: + return "Linux (any native FS)"; + case LINUX_EXTENDED: + return "Linux Extended"; + case LINUX_LVM: + return "Linux LVM"; + case HFS: + return "HFS/HFS+"; + case LINUX_LUKS: + return "Linux LUKS"; + case GPT_PROTECTIVE_MBR: + return "GPT Protective MBR"; + case EFI_SYSTEM: + return "EFI System"; + case LINUX_LVM_OLD: + return "Linux LVM (old)"; + default: + return "unknown"; + } +} + + +int detectMBRPartitions(BDVirtualDevice* bdv, BDDriver* drv, uint32_t sector, uint32_t SPT, const char* name); diff --git a/common/include/fs/Path.h b/common/include/fs/Path.h index 6cd47d936..b58dc359d 100644 --- a/common/include/fs/Path.h +++ b/common/include/fs/Path.h @@ -1,7 +1,8 @@ #pragma once #include "types.h" -#include "ustring.h" + +#include "EASTL/string.h" class Dentry; class VfsMount; @@ -16,9 +17,9 @@ class Path bool operator==(const Path&) const; Path parent(const Path* global_root = nullptr) const; - int child(const ustl::string& name, Path& out) const; + int child(const eastl::string& name, Path& out) const; - ustl::string getAbsolutePath(const Path* global_root = nullptr) const; + eastl::string getAbsolutePath(const Path* global_root = nullptr) const; bool isGlobalRoot(const Path* global_root = nullptr) const; bool isMountRoot() const; diff --git a/common/include/fs/PathWalker.h b/common/include/fs/PathWalker.h index 5cea19b20..40f92c595 100644 --- a/common/include/fs/PathWalker.h +++ b/common/include/fs/PathWalker.h @@ -1,7 +1,8 @@ #pragma once #include "types.h" -#include "ustring.h" + +#include "EASTL/string.h" class Dentry; class VfsMount; @@ -62,8 +63,8 @@ class PathWalker static int32 pathWalk(const char* pathname, FileSystemInfo* fs_info, Path& out, Path* parent_dir = nullptr); - static ustl::string pathPrefix(const ustl::string& path); - static ustl::string lastPathSegment(const ustl::string& path, bool ignore_separator_at_end = false); + static eastl::string pathPrefix(const eastl::string& path); + static eastl::string lastPathSegment(const eastl::string& path, bool ignore_separator_at_end = false); private: static size_t getNextPartLen(const char* path); @@ -72,5 +73,3 @@ class PathWalker PathWalker(); ~PathWalker(); }; - - diff --git a/common/include/fs/RamDiskDriver.h b/common/include/fs/RamDiskDriver.h new file mode 100644 index 000000000..1f9973ced --- /dev/null +++ b/common/include/fs/RamDiskDriver.h @@ -0,0 +1,34 @@ +#pragma once + +#include "BDDriver.h" + +#include +#include + +class BDRequest; +class BDVirtualDevice; + +class RamDiskDriver : public BDDriver +{ +public: + RamDiskDriver(void* start_vaddr, size_t size); + ~RamDiskDriver() override; + + uint32_t addRequest(BDRequest*) override; + + int32_t readSector(uint32_t start_sector, uint32_t num_sectors, void* buffer) override; + + int32_t writeSector(uint32_t start_sector, uint32_t num_sectors, void* buffer) override; + + uint32_t getNumSectors() override; + + uint32_t getSectorSize() override; + + void serviceIRQ() override; + + static BDVirtualDevice* createRamDisk(void* start_vaddr, size_t size, const char* name); + +private: + void* start_vaddr_; + size_t size_; +}; diff --git a/common/include/fs/Superblock.h b/common/include/fs/Superblock.h index eebd09794..c2a630de9 100644 --- a/common/include/fs/Superblock.h +++ b/common/include/fs/Superblock.h @@ -1,7 +1,8 @@ #pragma once #include "types.h" -#include + +#include "EASTL/list.h" class Iattr; class Statfs; @@ -60,24 +61,24 @@ class Superblock /** * A list of dirty inodes. */ - ustl::list dirty_inodes_; + eastl::list dirty_inodes_; /** * A list of used inodes. It is only used to open-file. */ - ustl::list used_inodes_; + eastl::list used_inodes_; /** * inodes of the superblock. */ - ustl::list all_inodes_; + eastl::list all_inodes_; /** * This is a list of files (linked on f_list) of open files on this * file-system. It is used, for example, to check if there are any files * open for write before remounting the file-system as read-only. */ - ustl::list s_files_; + eastl::list s_files_; public: @@ -158,5 +159,3 @@ class Superblock */ FileSystemType *getFSType(); }; - - diff --git a/common/include/fs/VfsMount.h b/common/include/fs/VfsMount.h index 18e66011d..b4c7f38b6 100644 --- a/common/include/fs/VfsMount.h +++ b/common/include/fs/VfsMount.h @@ -1,8 +1,9 @@ #pragma once -#include "types.h" #include "VirtualFileSystem.h" +#include "types.h" + class Superblock; class Dentry; diff --git a/common/include/fs/VfsSyscall.h b/common/include/fs/VfsSyscall.h index 7360d0794..bbf397952 100644 --- a/common/include/fs/VfsSyscall.h +++ b/common/include/fs/VfsSyscall.h @@ -1,7 +1,12 @@ #pragma once +#include #include "types.h" +#ifndef EXE2MINIXFS +#include "Mutex.h" +#endif + class Dirent; class Dentry; class VfsMount; @@ -24,15 +29,21 @@ class VfsSyscall */ static int32 mkdir(const char* pathname, int32 /*type*/); + typedef struct + { + unsigned long long d_offs_next; // offset to the next entry + unsigned char d_type; // file type + char d_name[]; // file name (null terminated) + } __attribute__((packed)) user_dirent; + /** - * The readdir() displays or saves the names from all childs into buffer and returns a pointer - * to a Dirent. - * @param pathname the destination-directory. - * @param buffer the buffer the output is saved to - * @param size the size of buffer in bytes - * @return the dirent + * Write the directory entries of a directory into a buffer + * @param fd The directory file descriptor to read direntries from + * @param buffer The buffer to write the direntries to + * @param buffer_size Size of the buffer in bytes + * @return Number of bytes written or -1 on error */ - static Dirent* readdir(const char* pathname, char* buffer = 0, size_t size = 0); + static ssize_t getdents(int fd, char* buffer, size_t buffer_size); /** * chdir() changes the current directory to the specified directory. @@ -143,8 +154,9 @@ class VfsSyscall */ static FileDescriptor* getFileDescriptor(uint32 fd); - private: +public: + +private: VfsSyscall(); ~VfsSyscall(); }; - diff --git a/common/include/fs/VirtualFileSystem.h b/common/include/fs/VirtualFileSystem.h index 12226dc11..74dde750a 100644 --- a/common/include/fs/VirtualFileSystem.h +++ b/common/include/fs/VirtualFileSystem.h @@ -1,7 +1,8 @@ #pragma once #include "types.h" -#include + +#include "EASTL/list.h" /** * File system flag indicating if the system in question requires an device. @@ -23,14 +24,14 @@ class FileSystemInfo; class VirtualFileSystem { protected: - ustl::list superblocks_; - ustl::list mounts_; - ustl::list file_system_types_; + eastl::list superblocks_; + eastl::list mounts_; + eastl::list file_system_types_; public: void initialize(); - VirtualFileSystem(); - ~VirtualFileSystem(); + VirtualFileSystem() = default; + ~VirtualFileSystem() = default; /** * register the file-system-type to the vfs @@ -103,4 +104,3 @@ class VirtualFileSystem }; extern VirtualFileSystem vfs; - diff --git a/common/include/fs/devicefs/DeviceFSSuperblock.h b/common/include/fs/devicefs/DeviceFSSuperblock.h index 5eb485796..e8b2009c3 100644 --- a/common/include/fs/devicefs/DeviceFSSuperblock.h +++ b/common/include/fs/devicefs/DeviceFSSuperblock.h @@ -7,6 +7,7 @@ class Inode; class Superblock; class CharacterDevice; class DeviceFSType; +class Device; class DeviceFSSuperBlock : public RamFSSuperblock { @@ -14,20 +15,24 @@ class DeviceFSSuperBlock : public RamFSSuperblock static const char ROOT_NAME[]; static const char DEVICE_ROOT_NAME[]; - virtual ~DeviceFSSuperBlock(); + ~DeviceFSSuperBlock() override; /** * addsa new device to the superblock - * @param inode the inode of the device to add - * @param node_name the device name + * @param device_inode the inode of the device to add + * @param device_name the device name */ - void addDevice(Inode* inode, const char* node_name); + void addDevice(Inode* device_inode, const char* device_name); /** * Access method to the singleton instance */ static DeviceFSSuperBlock* getInstance(); + void addBlockDeviceInodes(); + + void addDeviceInodes(Device& device_root); + private: /** @@ -41,4 +46,3 @@ class DeviceFSSuperBlock : public RamFSSuperblock static DeviceFSSuperBlock* instance_; }; - diff --git a/common/include/fs/devicefs/DeviceFSType.h b/common/include/fs/devicefs/DeviceFSType.h index 0e9e29d9e..47b7696f4 100644 --- a/common/include/fs/devicefs/DeviceFSType.h +++ b/common/include/fs/devicefs/DeviceFSType.h @@ -5,10 +5,10 @@ class DeviceFSType : public RamFSType { - public: +public: DeviceFSType(); - virtual ~DeviceFSType(); + ~DeviceFSType() override = default; /** * Reads the superblock from the device. @@ -16,7 +16,7 @@ class DeviceFSType : public RamFSType * @param data is the data given to the mount system call. * @return is a pointer to the resulting superblock. */ - virtual Superblock *readSuper(Superblock *superblock, void *data) const; + Superblock* readSuper(Superblock* superblock, void* data) const override; /** * Creates an Superblock object for the actual file system type. @@ -24,11 +24,10 @@ class DeviceFSType : public RamFSType * @param s_dev the device number of the new superblock * @return a pointer to the Superblock object */ - virtual Superblock *createSuper(uint32 s_dev); + Superblock* createSuper(uint32 s_dev) override; static DeviceFSType* getInstance(); protected: static DeviceFSType* instance_; }; - diff --git a/common/include/fs/devicefs/devices/BlockDeviceInode.h b/common/include/fs/devicefs/devices/BlockDeviceInode.h new file mode 100644 index 000000000..e33b28d4d --- /dev/null +++ b/common/include/fs/devicefs/devices/BlockDeviceInode.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Inode.h" + +class BDVirtualDevice; + +class BlockDeviceInode : public Inode +{ +public: + BlockDeviceInode(BDVirtualDevice* device); + ~BlockDeviceInode() override = default; + + File* open(Dentry* dentry, uint32 /*flag*/) override; + + int32 readData(uint32 /*offset*/, uint32 /*size*/, char */*buffer*/) override; + int32 writeData(uint32 /*offset*/, uint32 /*size*/, const char*/*buffer*/) override; +private: + + BDVirtualDevice* device_; +}; diff --git a/common/include/fs/minixfs/MinixFSFile.h b/common/include/fs/minixfs/MinixFSFile.h index 921ebceb6..54e01de0d 100644 --- a/common/include/fs/minixfs/MinixFSFile.h +++ b/common/include/fs/minixfs/MinixFSFile.h @@ -2,10 +2,9 @@ #include "File.h" -class MinixFSFile : public File +class MinixFSFile : public SimpleFile { - public: - +public: /** * constructor * @param inode the inode of the file @@ -14,30 +13,11 @@ class MinixFSFile : public File */ MinixFSFile(Inode* inode, Dentry* dentry, uint32 flag); - virtual ~MinixFSFile(); - - /** - * reads from the file - * @param buffer the buffer where the data is written to - * @param count the number of bytes to read - * @param offset the offset to read from counted from the current file position - * @return the number of bytes read - */ - virtual int32 read(char *buffer, size_t count, l_off_t offset); - - /** - * writes to the file - * @param buffer the buffer where the data is read from - * @param count the number of bytes to write - * @param offset the offset to write from counted from the current file position - * @return the number of bytes written - */ - virtual int32 write(const char *buffer, size_t count, l_off_t offset); + ~MinixFSFile() override = default; /** * writes all data to disc * @return 0 on success */ - virtual int32 flush(); + int32 flush() override; }; - diff --git a/common/include/fs/minixfs/MinixFSInode.h b/common/include/fs/minixfs/MinixFSInode.h index 4586d9c45..e772db0d3 100644 --- a/common/include/fs/minixfs/MinixFSInode.h +++ b/common/include/fs/minixfs/MinixFSInode.h @@ -1,21 +1,22 @@ #pragma once -#include "types.h" -#include "kstring.h" #include "Inode.h" #include "MinixFSZone.h" -#include +#include "kstring.h" + +#include "types.h" + +#include "EASTL/list.h" class MinixFSInode : public Inode { friend class MinixFSSuperblock; - protected: - +protected: /** * the zones storing the addresses of memory of this inode */ - MinixFSZone *i_zones_; + MinixFSZone* i_zones_; /** * the inode number (the first inode has the i_num 1 on a minix file system) @@ -27,14 +28,13 @@ class MinixFSInode : public Inode */ virtual void loadChildren(); - public: - +public: /** * basic constructor * @param super_block the superblock the inode is on * @param inode_type the inode type (I_FILE, I_DIR) */ - MinixFSInode(Superblock *super_block, uint32 inode_type); + MinixFSInode(Superblock* super_block, uint32 inode_type); /** * constructor of an inode existing on disc with all data given @@ -45,8 +45,13 @@ class MinixFSInode : public Inode * @param i_zones the first 9 zone addresses * @param i_num the inode number */ - MinixFSInode(Superblock *super_block, uint16 i_mode, uint32 i_size, uint16 i_nlinks, uint32* i_zones, uint32 i_num); - virtual ~MinixFSInode(); + MinixFSInode(Superblock* super_block, + uint16 i_mode, + uint32 i_size, + uint16 i_nlinks, + uint32* i_zones, + uint32 i_num); + ~MinixFSInode() override; /** * lookup checks if that name (given by the char-array) exists in the @@ -56,43 +61,42 @@ class MinixFSInode : public Inode * @param name the name to look for * @return the dentry found or NULL otherwise */ - virtual Dentry* lookup(const char *name); + Dentry* lookup(const char* name) override; /** * Called when a file is opened */ - virtual File* open(Dentry* dentry, uint32 /*flag*/); + File* open(Dentry* dentry, uint32 /*flag*/) override; /** * creates a directory with the given dentry. It is only used to with directory. * @param dentry the dentry to create with * @return 0 on success */ - virtual int32 mkdir(Dentry *dentry); + int32 mkdir(Dentry* dentry) override; - virtual int32 link(Dentry* dentry); - virtual int32 unlink(Dentry* dentry); + int32 link(Dentry* dentry) override; + int32 unlink(Dentry* dentry) override; /** * removes the directory (if it is empty) * @return 0 on success */ - virtual int32 rmdir(Dentry* dentry); - + int32 rmdir(Dentry* dentry) override; /** * creates a directory with the given dentry. * @param dentry the dentry * @return 0 on success */ - virtual int32 mknod(Dentry *dentry); // no dir no file + int32 mknod(Dentry* dentry) override; // no dir no file /** * creates a file with the given dentry. * @param dentry the dentry * @return 0 on success */ - virtual int32 mkfile(Dentry *dentry); + int32 mkfile(Dentry* dentry) override; /** * read the data from the inode @@ -101,7 +105,7 @@ class MinixFSInode : public Inode * @param buffer the dest char-array to store the data * @return the number of bytes read */ - virtual int32 readData(uint32 offset, uint32 size, char *buffer); + int32 readData(uint32 offset, uint32 size, char* buffer) override; /** * write the data to the inode @@ -110,7 +114,7 @@ class MinixFSInode : public Inode * @param buffer the src char-array * @return the number of bytes written */ - virtual int32 writeData(uint32 offset, uint32 size, const char *buffer); + int32 writeData(uint32 offset, uint32 size, const char* buffer) override; /** * flushes the inode to the file system @@ -118,7 +122,31 @@ class MinixFSInode : public Inode */ virtual int32 flush(); - private: + struct [[gnu::packed]] MinixFSInodeOnDiskDataV1 + { + uint16 i_mode; + uint16 i_uid; + uint32 i_size; + uint32 i_time; + uint8 i_gid; + uint8 i_nlinks; + uint16 i_zone[9]; + }; + + struct [[gnu::packed]] MinixFSInodeOnDiskDataV3 + { + uint16 i_mode; + uint16 i_nlinks; + uint16 i_uid; + uint16 i_gid; + uint32 i_size; + uint32 i_atime; + uint32 i_mtime; + uint32 i_ctime; + uint32 i_zone[10]; + }; + +private: /** * writes the inode dentry to disc * @param dest_i_num the inode number to write the dentry to diff --git a/common/include/fs/minixfs/MinixFSSuperblock.h b/common/include/fs/minixfs/MinixFSSuperblock.h index 32587f929..77a050d01 100644 --- a/common/include/fs/minixfs/MinixFSSuperblock.h +++ b/common/include/fs/minixfs/MinixFSSuperblock.h @@ -1,8 +1,9 @@ #pragma once -#include "Superblock.h" #include "MinixStorageManager.h" -#include "umap.h" +#include "Superblock.h" + +#include "EASTL/map.h" class Inode; class MinixFSInode; @@ -11,39 +12,39 @@ class MinixFSType; class MinixFSSuperblock : public Superblock { - public: +public: friend class MinixFSInode; friend class MinixFSZone; friend class MinixStorageManager; - MinixFSSuperblock(MinixFSType* fs_type, size_t s_dev, uint64 offset); - virtual ~MinixFSSuperblock(); + MinixFSSuperblock(MinixFSType* fs_type, size_t s_dev, uint64 offset, uint64 partition_size); + ~MinixFSSuperblock() override; /** * creates one new inode of the superblock * @param type the file type of the new inode (I_DIR, I_FILE) * @return the new inode */ - virtual Inode* createInode(uint32 type); + Inode* createInode(uint32 type) override; /** * reads one inode from the mounted file system * @param inode the inode to read * @return 0 on success */ - virtual int32 readInode(Inode* inode); + int32 readInode(Inode* inode) override; /** * writes the inode from the mounted file system * @param inode the inode to write */ - virtual void writeInode(Inode* inode); + void writeInode(Inode* inode) override; /** * removes one inode from the file system and frees all its resources * @param inode the inode to delete */ - virtual void deleteInode(Inode* inode); + void deleteInode(Inode* inode) override; /** * add an inode to the all_inodes_ data structures @@ -69,14 +70,43 @@ class MinixFSSuperblock : public Superblock */ virtual void freeZone(uint16 index); - protected: - + struct [[gnu::packed]] MinixFSSuperblockOnDiskDataV1 + { + uint16 s_num_inodes; + uint16 s_num_zones; + uint16 s_imap_blocks; + uint16 s_zmap_blocks; + uint16 s_firstdatazone; + uint16 s_log_zone_size; + uint32 s_max_file_size; + uint16 s_magic; + uint16 s_mount_state; + }; + + struct [[gnu::packed]] MinixFSSuperblockOnDiskDataV3 + { + uint32 s_num_inodes; + uint16 padding0; + uint16 s_imap_blocks; + uint16 s_zmap_blocks; + uint16 s_firstdatazone; + uint16 s_log_zone_size; + uint16 padding1; + uint32 s_max_file_size; + uint32 s_num_zones; + uint16 s_magic; + uint16 padding2; + uint16 s_blocksize; + uint8 s_disk_version; + }; + +protected: /** * creates an Inode object with the given number from the file system * @param i_num the inode number * @return the Inode object */ - MinixFSInode *getInode(uint16 i_num); + MinixFSInode* getInode(uint16 i_num); /** * creates an Inode object with the given number from the file system @@ -87,14 +117,14 @@ class MinixFSSuperblock : public Superblock * @param is_already_loaded should be set to true if already loaded * @return the Inode object */ - MinixFSInode *getInode(uint16 i_num, bool &is_already_loaded); + MinixFSInode* getInode(uint16 i_num, bool& is_already_loaded); /** * reads one Zone from the file system to the given buffer * @param zone the zone index to read * @param buffer the buffer to write in */ - void readZone(uint16 zone, char *buffer); + void readZone(uint16 zone, char* buffer); /** * reads the given number of blocks from the file system to the given buffer @@ -102,22 +132,22 @@ class MinixFSSuperblock : public Superblock * @param num_blocks the number of blcoks to read * @param buffer the buffer to write in */ - void readBlocks(uint16 block, uint32 num_blocks, char *buffer); + void readBlocks(uint16 block, uint32 num_blocks, char* buffer); /** * writes one zone from the given buffer to the file system * @param zone the zone index to write * @param buffer the buffer to write */ - void writeZone(uint16 zone, char *buffer); + void writeZone(uint16 zone, char* buffer); /** - * writes the given number of blcoks to the file system from the given buffer + * writes the given number of blocks to the file system from the given buffer * @param block the index of the first block to write * @param num_blocks the number of blocks to write * @param buffer the buffer to write */ - void writeBlocks(uint16 block, uint32 num_blocks, char *buffer); + void writeBlocks(uint16 block, uint32 num_blocks, char* buffer); /** * writes the given number of bytes to the filesystem @@ -128,7 +158,7 @@ class MinixFSSuperblock : public Superblock * @param buffer the buffer with the bytes to write * @return the number of bytes written */ - int32 writeBytes(uint32 block, uint32 offset, uint32 size, char *buffer); + int32 writeBytes(uint32 block, uint32 offset, uint32 size, char* buffer); /** * reads the given number of bytes from the disc @@ -139,14 +169,14 @@ class MinixFSSuperblock : public Superblock * @param buffer the buffer to write to * @return the number of bytes read */ - int32 readBytes(uint32 block, uint32 offset, uint32 size, char *buffer); + int32 readBytes(uint32 block, uint32 offset, uint32 size, char* buffer); /** * reads the fs header */ void readHeader(); - private: +private: /** * reads the root inode and its children from the filesystem */ @@ -185,8 +215,7 @@ class MinixFSSuperblock : public Superblock MinixStorageManager* storage_manager_; - - ustl::map all_inodes_set_; + eastl::map all_inodes_set_; /** * pointer to self for compatability @@ -194,8 +223,12 @@ class MinixFSSuperblock : public Superblock Superblock* superblock_; /** - * offset in the image file (in image util) + * offset in the partition */ uint64 offset_; -}; + /** + * partition size in bytes + */ + uint64 size_; +}; diff --git a/common/include/fs/minixfs/MinixFSType.h b/common/include/fs/minixfs/MinixFSType.h index 90d2d42fd..69f787663 100644 --- a/common/include/fs/minixfs/MinixFSType.h +++ b/common/include/fs/minixfs/MinixFSType.h @@ -4,9 +4,9 @@ class MinixFSType : public FileSystemType { - public: +public: MinixFSType(); - virtual ~MinixFSType(); + ~MinixFSType() override = default; /** * reads the superblock from the device @@ -14,13 +14,12 @@ class MinixFSType : public FileSystemType * @param data the data given to the mount system call * @return the superblock */ - virtual Superblock *readSuper(Superblock *superblock, void *data) const; + Superblock* readSuper(Superblock* superblock, void* data) const override; /** * creates an Superblock object for the actual file system type * @param root the root dentry * @param s_dev the device number */ - virtual Superblock *createSuper(uint32 s_dev); + Superblock* createSuper(uint32 s_dev) override; }; - diff --git a/common/include/fs/minixfs/MinixFSZone.h b/common/include/fs/minixfs/MinixFSZone.h index 13c9e4dd0..58cefddfd 100644 --- a/common/include/fs/minixfs/MinixFSZone.h +++ b/common/include/fs/minixfs/MinixFSZone.h @@ -25,6 +25,7 @@ class MinixFSZone void flush(uint32 inode_num); void freeZones(); + void printZones(); private: MinixFSSuperblock *superblock_; diff --git a/common/include/fs/minixfs/MinixStorageManager.h b/common/include/fs/minixfs/MinixStorageManager.h index b043858b1..9b76f141c 100644 --- a/common/include/fs/minixfs/MinixStorageManager.h +++ b/common/include/fs/minixfs/MinixStorageManager.h @@ -1,15 +1,15 @@ #pragma once #include "StorageManager.h" -#include "types.h" #include "minix_fs_consts.h" +#include "types.h" + class MinixFSSuperblock; class MinixStorageManager : public StorageManager { - public: - +public: /** * constructor * @param bm_buffer the buffer with the inode and zone bitmaps from disc @@ -18,28 +18,27 @@ class MinixStorageManager : public StorageManager * @param num_inodes the max number of inodes * @param num_zones the max number of zones */ - MinixStorageManager(char *bm_buffer, uint16 num_inode_bm_blocks, uint16 num_zone_bm_blocks, uint16 num_inodes, + MinixStorageManager(char* bm_buffer, + uint16 num_inode_bm_blocks, + uint16 num_zone_bm_blocks, + uint16 num_inodes, uint16 num_zones); - virtual ~MinixStorageManager(); + ~MinixStorageManager() override; virtual size_t allocZone(); virtual size_t allocInode(); - virtual void freeZone(size_t index); - virtual void freeInode(size_t index); - virtual bool isInodeSet(size_t index); + void freeZone(size_t index) override; + void freeInode(size_t index) override; + bool isInodeSet(size_t index) override; virtual uint32 getNumUsedInodes(); - void flush(MinixFSSuperblock *superblock); + void flush(MinixFSSuperblock* superblock); void printBitmap(); - private: - +private: size_t curr_zone_pos_; size_t curr_inode_pos_; uint32 num_inode_bm_blocks_; uint32 num_zone_bm_blocks_; - }; - - diff --git a/common/include/fs/minixfs/StorageManager.h b/common/include/fs/minixfs/StorageManager.h index 9c9fa5a83..c4e99b35c 100644 --- a/common/include/fs/minixfs/StorageManager.h +++ b/common/include/fs/minixfs/StorageManager.h @@ -1,6 +1,7 @@ #pragma once #include "Bitmap.h" + #include "types.h" class StorageManager @@ -14,7 +15,7 @@ class StorageManager */ StorageManager(uint16 num_inodes, uint16 num_zones); - virtual ~StorageManager(); + virtual ~StorageManager() = default; /** * frees the zone at the given index diff --git a/common/include/fs/minixfs/minix_fs_consts.h b/common/include/fs/minixfs/minix_fs_consts.h index a6b97eb4f..6cd41fc9d 100644 --- a/common/include/fs/minixfs/minix_fs_consts.h +++ b/common/include/fs/minixfs/minix_fs_consts.h @@ -1,5 +1,7 @@ #pragma once +#include "MinixFSInode.h" + #define V3_OFFSET ((superblock_->s_magic_==MINIX_V3) ? 1 : 0) #define V3_ARRAY(PTR,IDX) ((superblock_->s_magic_==MINIX_V3) ? ((uint32*)(PTR))[(IDX)] : ((uint16*)(PTR))[(IDX)]) #define SET_V3_ARRAY(PTR,IDX,VAL) do { if (superblock_->s_magic_==MINIX_V3) ((uint32*)(PTR))[(IDX)] = VAL; else ((uint16*)(PTR))[(IDX)] = VAL; } while (0) @@ -7,10 +9,10 @@ #define ZONE_SIZE 1024U #define BLOCK_SIZE 1024U #define INODE_BYTES ((superblock_->s_magic_==MINIX_V3) ? 4 : 2) -#define INODE_SIZE ((superblock_->s_magic_==MINIX_V3) ? 64 : 32) -#define INODES_PER_BLOCK ((superblock_->s_magic_==MINIX_V3) ? 16 : 32) +#define INODE_SIZE ((superblock_->s_magic_==MINIX_V3) ? sizeof(MinixFSInode::MinixFSInodeOnDiskDataV3) : sizeof(MinixFSInode::MinixFSInodeOnDiskDataV1)) +#define INODES_PER_BLOCK (BLOCK_SIZE / INODE_SIZE) #define DENTRY_SIZE ((superblock_->s_magic_==MINIX_V3) ? 64 : 32) #define MAX_NAME_LENGTH ((superblock_->s_magic_==MINIX_V3) ? 60 : 30) #define NUM_ZONES ((superblock_->s_magic_==MINIX_V3) ? 10 : 9) #define MINIX_V3 0x4d5a - +#define MINIX_V1 0x138f diff --git a/common/include/fs/ramfs/RamFSFile.h b/common/include/fs/ramfs/RamFSFile.h index 5c07a9d7f..63aa109f0 100644 --- a/common/include/fs/ramfs/RamFSFile.h +++ b/common/include/fs/ramfs/RamFSFile.h @@ -2,60 +2,12 @@ #include "fs/File.h" - -class RamFSFile: public File +// No special operations by default +class RamFSFile : public SimpleFile { public: - RamFSFile ( Inode* inode, Dentry* dentry, uint32 flag ); - - virtual ~RamFSFile(); - - /** - * Sets the file position relative to the start of the file, the end of - * the file or the current file position. - * @param offset is the offset to set. - * @param origin is the on off SEEK_SET, SEEK_CUR and SEEK_END. - * @return the offset from the start off the file or -1 on failure. - */ - l_off_t llSeek ( l_off_t offset, uint8 origin ); - - - /** - * reads from the file - * @param buffer is the buffer where the data is written to - * @param count is the number of bytes to read. - * @param offset is the offset to read from counted from the start of the file. - * @return the number of bytes read - */ - virtual int32 read ( char *buffer, size_t count, l_off_t offset ); + RamFSFile(Inode* inode, Dentry* dentry, uint32 flag); - /** - * writes to the file - * @param buffer is the buffer where the data is read from - * @param count is the number of bytes to write. - * @param offset is the offset to write from counted from the start of the file. - * @return the number of bytes written - */ - virtual int32 write ( const char *buffer, size_t count, l_off_t offset ); - - /** - * Opens the file - * @param flag how to open the file - * @return the filedescriptor - */ - virtual int32 open ( uint32 flag ); - - /** - * Closes the file - * @return 0 on success - */ - virtual int32 close(); - - /** - * Flushes all off the file's write operations. The File will be written to disk. - * @return is the error code of the flush operation. - */ - virtual int32 flush(); + virtual ~RamFSFile() = default; }; - diff --git a/common/include/fs/ramfs/RamFSInode.h b/common/include/fs/ramfs/RamFSInode.h index ccef9ac81..2d581e4bd 100644 --- a/common/include/fs/ramfs/RamFSInode.h +++ b/common/include/fs/ramfs/RamFSInode.h @@ -1,40 +1,48 @@ #pragma once -#include "types.h" #include "fs/Inode.h" +#include "types.h" + class RamFSInode : public Inode { - protected: +protected: char* data_; - public: +public: /** * constructor * @param super_block the superblock to create the inode on * @param inode_type the inode type */ - RamFSInode ( Superblock *super_block, uint32 inode_type ); - virtual ~RamFSInode(); + RamFSInode(Superblock* super_block, uint32 inode_type); + ~RamFSInode() override; + + /** + * creates a directory with the given dentry + * @param dentry the dentry to create the directory with + * @return 0 on success + */ + int32 mkdir(Dentry* dentry) override; + + int32 rmdir(Dentry* dentry) override; /** * Called when a file is opened */ - virtual File* open(Dentry* dentry, uint32 /*flag*/); + File* open(Dentry* dentry, uint32 /*flag*/) override; /// read the data from the inode /// @param offset offset byte /// @param size the size of data that read from this inode /// @buffer the dest char-array to store the data /// @return On successe, return 0. On error, return -1. - virtual int32 readData ( uint32 offset, uint32 size, char *buffer ); + int32 readData(uint32 offset, uint32 size, char* buffer) override; /// write the data to the inode /// @param offset offset byte /// @param size the size of data that write to this inode (data_) /// @buffer the src char-array /// @return On successe, return 0. On error, return -1. - virtual int32 writeData ( uint32 offset, uint32 size, const char *buffer ); - + int32 writeData(uint32 offset, uint32 size, const char* buffer) override; }; - diff --git a/common/include/fs/ramfs/RamFSSuperblock.h b/common/include/fs/ramfs/RamFSSuperblock.h index 1dba92c96..39f39b033 100644 --- a/common/include/fs/ramfs/RamFSSuperblock.h +++ b/common/include/fs/ramfs/RamFSSuperblock.h @@ -8,14 +8,14 @@ class RamFSType; class RamFSSuperblock : public Superblock { - public: +public: /** * constructor * @param s_root the root dentry of the new filesystem * @param s_dev the device number of the new filesystem */ - RamFSSuperblock (RamFSType* type, uint32 s_dev ); - virtual ~RamFSSuperblock(); + RamFSSuperblock(RamFSType* type, uint32 s_dev); + ~RamFSSuperblock() override; /** * create a new Inode of the superblock, mknod with dentry, add in the list. @@ -23,21 +23,21 @@ class RamFSSuperblock : public Superblock * @param type the inode type * @return the inode */ - virtual Inode* createInode (uint32 type ); + Inode* createInode(uint32 type) override; /** * This method is called to read a specific inode from a mounted file-system. * @param inode the inode to read * @return 0 on success */ - virtual int32 readInode ( Inode* inode ); + int32 readInode(Inode* inode) override; /** * This method is called to write a specific inode to a mounted file-system, * and gets called on inodes which have been marked dirty. * @param inode the inode to write */ - virtual void writeInode ( Inode* inode ); + void writeInode(Inode* inode) override; /** * This method is called whenever the reference count on an inode reaches 0, @@ -47,7 +47,7 @@ class RamFSSuperblock : public Superblock * used. * @param inode the inode to delete */ - virtual void deleteInode ( Inode* inode ); + void deleteInode(Inode* inode) override; }; -//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- diff --git a/common/include/fs/ramfs/RamFSType.h b/common/include/fs/ramfs/RamFSType.h index 9d9b6e930..1c18022a4 100644 --- a/common/include/fs/ramfs/RamFSType.h +++ b/common/include/fs/ramfs/RamFSType.h @@ -6,7 +6,7 @@ class RamFSType : public FileSystemType { public: RamFSType(); - virtual ~RamFSType(); + ~RamFSType() override = default; /** * Reads the superblock from the device. @@ -14,12 +14,11 @@ class RamFSType : public FileSystemType * @param data is the data given to the mount system call. * @return is a pointer to the resulting superblock. */ - virtual Superblock *readSuper(Superblock *superblock, void *data) const; + Superblock *readSuper(Superblock *superblock, void *data) const override; /** * Creates an Superblock object for the actual file system type. * @return a pointer to the Superblock object */ - virtual Superblock *createSuper(uint32 s_dev); + Superblock *createSuper(uint32 s_dev) override; }; - diff --git a/common/include/kernel/BasicSpinLock.h b/common/include/kernel/BasicSpinLock.h new file mode 100644 index 000000000..38d31ff73 --- /dev/null +++ b/common/include/kernel/BasicSpinLock.h @@ -0,0 +1,27 @@ +#pragma once + +#include "EASTL/atomic.h" + +class Thread; + +class BasicSpinLock +{ +public: + BasicSpinLock(); + BasicSpinLock(const BasicSpinLock&) = delete; + BasicSpinLock& operator=(const BasicSpinLock&) = delete; + + virtual ~BasicSpinLock() = default; + + void acquire(bool yield = true); + bool acquireNonBlocking(); + + void release(); + + Thread* heldBy(); + bool isHeldBy(Thread* t); + +protected: + eastl::atomic_flag lock_; + Thread* held_by_ = nullptr; +}; diff --git a/common/include/kernel/BootloaderModules.h b/common/include/kernel/BootloaderModules.h new file mode 100644 index 000000000..7b4ac7a08 --- /dev/null +++ b/common/include/kernel/BootloaderModules.h @@ -0,0 +1,13 @@ +#pragma once + +class BDVirtualDevice; +class Allocator; + +namespace BootloaderModules +{ + void reserveModulePages(Allocator& allocator); + void mapModules(); + + BDVirtualDevice* createRamDiskFromModule(int module_num, const char* name); + void loadInitrdIfExists(); +}; diff --git a/common/include/kernel/CleanupThread.h b/common/include/kernel/CleanupThread.h index 996a08cec..37d03ce04 100644 --- a/common/include/kernel/CleanupThread.h +++ b/common/include/kernel/CleanupThread.h @@ -6,8 +6,8 @@ class CleanupThread : public Thread { public: CleanupThread(); - virtual ~CleanupThread(); - virtual void kill(); - virtual void Run(); -}; + ~CleanupThread() override; + void kill() override; + void Run() override; +}; diff --git a/common/include/kernel/Condition.h b/common/include/kernel/Condition.h index ffe46c015..2c5caf499 100644 --- a/common/include/kernel/Condition.h +++ b/common/include/kernel/Condition.h @@ -2,6 +2,8 @@ #include "Lock.h" +#include "types.h" + class Thread; class Mutex; @@ -10,46 +12,47 @@ class Condition : public Lock public: /** * Constructor - * A Condition needs a Mutex for creation. Only one thread can acquire this Mutex + * A Condition requires an associated Mutex which must be held when the condition is used. * and enter the critical section * @param mutex the Mutex * @param name the name of the condition */ Condition ( Mutex *mutex, const char* name); - ~Condition(); + ~Condition() override; - Condition(Condition const &) = delete; - Condition &operator=(Condition const &) = delete; + Condition(const Condition&) = delete; + Condition& operator=(const Condition&) = delete; - /** - * Only possible if the current Thread has acquired the Mutex. - * The Thread is put on the list of sleepers, releases the Mutex and goes to sleep - * Acquires the Mutex when waking up. - * @param re_acquire_mutex Automatically re-acquire the mutex after the wake-up. - * In case the mutex (or even the condition) does not exists any longer after wake-up, - * this flag has to be set to false. Then the mutex is not accessed any longer after wake up. - * @param called_by A pointer to the call point of this function. - * Can be set in case this method is called by a wrapper function. - * - */ - void wait(bool re_acquire_mutex = true, pointer called_by = 0); - void waitAndRelease(pointer called_by = 0); + /** + * Only possible if the current Thread has acquired the Mutex. + * The Thread is put on the list of sleepers, releases the Mutex and goes to sleep + * Acquires the Mutex when waking up. + * @param re_acquire_mutex Automatically re-acquire the mutex after the wake-up. + * In case the mutex (or even the condition) does not exists any longer after wake-up, + * this flag has to be set to false. Then the mutex is not accessed any longer after wake + * up. + * @param called_by A pointer to the call point of this function. + * Can be set in case this method is called by a wrapper function. + * + */ + void wait(bool re_acquire_mutex = true, pointer called_by = 0); + void waitAndRelease(pointer called_by = 0); - /** - * Wakes up the first Thread on the sleepers list. - * If the list is empty, signal is being lost. - * @param called_by A pointer to the call point of this function. - * Can be set in case this method is called by a wrapper function. - */ - void signal(pointer called_by = 0, bool broadcast = false); + /** + * Wakes up the first Thread on the sleepers list. + * If the list is empty, signal is being lost. + * @param called_by A pointer to the call point of this function. + * Can be set in case this method is called by a wrapper function. + */ + void signal(pointer called_by = 0, bool broadcast = false); - /** - * Wakes up all Threads on the sleepers list. - * If the list is empty, signal is being lost. - * @param called_by A pointer to the call point of this function. - * Can be set in case this method is called by a wrapper function. - */ - void broadcast(pointer called_by = 0); + /** + * Wakes up all Threads on the sleepers list. + * If the list is empty, signal is being lost. + * @param called_by A pointer to the call point of this function. + * Can be set in case this method is called by a wrapper function. + */ + void broadcast(pointer called_by = 0); private: /** diff --git a/common/include/kernel/CpuExclusiveLock.h b/common/include/kernel/CpuExclusiveLock.h new file mode 100644 index 000000000..cb0b3aaae --- /dev/null +++ b/common/include/kernel/CpuExclusiveLock.h @@ -0,0 +1,28 @@ +#pragma once + +#include "types.h" +#include + +#include "EASTL/atomic.h" + +class CpuExclusiveLock +{ +public: + CpuExclusiveLock(const char* name); + + CpuExclusiveLock(const CpuExclusiveLock&) = delete; + virtual ~CpuExclusiveLock() = default; + + virtual void acquire(pointer called_by = 0); + virtual void release(pointer called_by = 0); + + bool isHeldBy(size_t cpu_id) const; + size_t heldBy() const; + + const char* getName() const; + +protected: + eastl::atomic held_by_cpu_; + + const char* name_; +}; diff --git a/common/include/kernel/IdleThread.h b/common/include/kernel/IdleThread.h index 001efb2ea..3b3ede45b 100644 --- a/common/include/kernel/IdleThread.h +++ b/common/include/kernel/IdleThread.h @@ -6,7 +6,7 @@ class IdleThread : public Thread { public: IdleThread(); + ~IdleThread() override; - virtual void Run(); + void Run() override; }; - diff --git a/common/include/kernel/InitThread.h b/common/include/kernel/InitThread.h new file mode 100644 index 000000000..01350e544 --- /dev/null +++ b/common/include/kernel/InitThread.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Thread.h" + +class InitThread : public Thread +{ +public: + ~InitThread() override; + + static void init(FileSystemInfo *root_fs_info, char const *progs[]); + static InitThread* instance(); + + void Run() override; + +private: + InitThread(FileSystemInfo *root_fs_info, char const *progs[]); + + static InitThread* instance_; + char const **progs_; +}; diff --git a/common/include/kernel/IrqDomain.h b/common/include/kernel/IrqDomain.h new file mode 100644 index 000000000..7cbe9bf20 --- /dev/null +++ b/common/include/kernel/IrqDomain.h @@ -0,0 +1,389 @@ +#pragma once + +#include "ranges.h" +#include + +#include "EASTL/atomic.h" +#include "EASTL/fixed_function.h" +#include "EASTL/intrusive_list.h" +#include "EASTL/iterator.h" +#include "EASTL/map.h" +#include "EASTL/string.h" +#include "EASTL/type_traits.h" +#include "EASTL/vector.h" + +using irqnum_t = size_t; + +struct InterruptController +{ + virtual bool mask(irqnum_t irq, bool mask) = 0; + virtual bool ack(irqnum_t irq) = 0; + virtual bool isMasked(irqnum_t) = 0; + + virtual bool irqStart([[maybe_unused]] irqnum_t irq) + { + ++pending_EOIs; + return true; + } + + size_t pending_EOIs = 0; +}; + +class IrqDomain +{ +public: + struct IrqMappingTarget : public eastl::intrusive_list_node + { + constexpr IrqMappingTarget() = default; + + constexpr IrqMappingTarget(IrqDomain* domain, + irqnum_t irqnum, + IrqMappingTarget* reverse_ref) : + domain(domain), + irqnum(irqnum), + reverse_ref(reverse_ref) + { + } + + constexpr IrqMappingTarget(const IrqMappingTarget&) = default; + constexpr IrqMappingTarget(IrqMappingTarget&&) = default; + constexpr IrqMappingTarget& operator=(const IrqMappingTarget&) = default; + constexpr IrqMappingTarget& operator=(IrqMappingTarget&&) = default; + + friend constexpr bool operator==(const IrqMappingTarget& l, + const IrqMappingTarget& r) + { + return l.domain == r.domain && l.irqnum == r.irqnum; + } + + IrqDomain* domain; + irqnum_t irqnum; + IrqMappingTarget* reverse_ref; + }; + + struct IrqInfo + { + using handler_func_t = eastl::fixed_function<16, void(void)>; + + IrqMappingTarget map_to; + eastl::intrusive_list mapped_by; + handler_func_t handler = nullptr; + }; + + IrqDomain(const eastl::string& name, size_t num_irqs = 1, InterruptController* controller = nullptr); + virtual ~IrqDomain() = default; + + [[nodiscard]] constexpr const eastl::string& name() const { return name_; } + + [[nodiscard]] constexpr InterruptController* controller() const + { + return controller_; + } + + class DomainIrqHandle + { + public: + class iterator + { + private: + IrqDomain* domain_ = nullptr; + irqnum_t irq_ = 0; + + public: + using value_type = DomainIrqHandle; + using difference_type = size_t; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = eastl::forward_iterator_tag; + + constexpr iterator() = default; + constexpr iterator(const iterator& other) = default; + constexpr iterator(iterator&& other) = default; + + constexpr iterator(IrqDomain* domain, irqnum_t irq) : + domain_(domain), + irq_(irq) + { + } + + constexpr iterator(const DomainIrqHandle& handle) : + iterator(handle.domain_, handle.irq_) + { + } + + class sentinel_t + { + }; + + friend constexpr bool operator==(const iterator& lhs, sentinel_t) + { + return lhs.domain_ == nullptr; + } + + constexpr bool operator==(const iterator& lhs) const = default; + + constexpr DomainIrqHandle operator*() const { return {*domain_, irq_}; } + + const iterator& operator++() + { + auto info = domain_->irqInfo(irq_); + if (info) + { + domain_ = info->map_to.domain; + irq_ = info->map_to.irqnum; + } + else + { + domain_ = nullptr; + irq_ = 0; + } + + return *this; + } + + iterator operator++(int) + { + auto tmp = *this; + ++(*this); + return tmp; + } + }; + + class tree_iterator + { + private: + IrqDomain* domain_ = nullptr; + irqnum_t irq_ = 0; + + public: + size_t level = 0; + + using value_type = DomainIrqHandle; + using difference_type = size_t; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = eastl::forward_iterator_tag; + + constexpr tree_iterator() = default; + constexpr tree_iterator(const tree_iterator& other) = default; + constexpr tree_iterator(tree_iterator&& other) = default; + + constexpr tree_iterator(IrqDomain* domain, irqnum_t irq) : + domain_(domain), + irq_(irq) + { + } + + constexpr tree_iterator(const DomainIrqHandle& handle) : + tree_iterator(handle.domain_, handle.irq_) + { + } + + friend constexpr bool operator==(const tree_iterator& lhs, + const tree_iterator& rhs) + { + return lhs.domain_ == rhs.domain_ && lhs.irq_ == rhs.irq_; + } + + class sentinel_t { }; + + friend constexpr bool operator==(const tree_iterator& lhs, sentinel_t) + { + return lhs.domain_ == nullptr; + } + + constexpr DomainIrqHandle operator*() const { return {*domain_, irq_}; } + + const tree_iterator& operator++() + { + auto info = domain_->irqInfo(irq_); + if (!info) + { + domain_ = nullptr; + irq_ = 0; + } + else if (info->mapped_by.empty()) + { + // Need to backtrack + while (true) + { + auto& map_to = info->map_to; + if (map_to.domain) + { + assert(map_to.reverse_ref); + // find next sibling in parent + auto map_to_info = map_to.domain->irqInfo(map_to.irqnum); + assert(map_to_info); + auto it = map_to_info->mapped_by.locate(*map_to.reverse_ref); + assert(it != map_to_info->mapped_by.end()); + ++it; + if (it != map_to_info->mapped_by.end()) + { + // found next sibling + domain_ = it->domain; + irq_ = it->irqnum; + break; + } + else + { + // Reached last sibling in parent, backtrack to parent of + // parent + info = map_to_info; + --level; + } + } + else + { + // Nothing to backtrack to, reached end of tree + domain_ = nullptr; + irq_ = 0; + break; + } + } + } + else + { + domain_ = info->mapped_by.front().domain; + irq_ = info->mapped_by.front().irqnum; + ++level; + } + + return *this; + } + + tree_iterator operator++(int) + { + auto tmp = *this; + ++(*this); + return tmp; + } + }; + + constexpr DomainIrqHandle(IrqDomain& domain, irqnum_t irq) : + domain_(&domain), + irq_(irq) + { + } + + [[nodiscard]] constexpr IrqDomain& domain() const + { + assert(domain_); + return *domain_; + } + + [[nodiscard]] constexpr irqnum_t irq() const + { + assert(domain_); + return irq_; + } + + DomainIrqHandle& mapTo(IrqDomain& target_domain, irqnum_t target_irq = 0) + { + domain_->mapIrqTo(irq_, target_domain, target_irq); + return *this; + } + + DomainIrqHandle& removeMapping() + { + domain_->removeIrq(irq_); + return *this; + } + + DomainIrqHandle& useHandler(const IrqInfo::handler_func_t& handler) + { + domain_->setIrqHandler(irq_, handler); + return *this; + } + + [[nodiscard]] auto handler() const { return domain_->getIrqHandler(irq_); } + + DomainIrqHandle& activateInDomain(bool activate) + { + domain_->activateIrq(irq_, activate); + return *this; + } + + DomainIrqHandle& handleInDomain() + { + domain_->handleIrq(irq_); + return *this; + } + + [[nodiscard]] auto forwardMappingChain() const + { + iterator it{domain_, irq_}; + return ranges::subrange{it, iterator::sentinel_t{}}; + } + + [[nodiscard]] auto reverseMappingTree() const + { + return ranges::subrange{tree_iterator(*this), tree_iterator::sentinel_t{}}; + } + + // Enable structured decomposition + template + requires(N < 2) + constexpr auto get() const + { + if constexpr (N == 0) + return domain_; + else if constexpr (N == 1) + return irq_; + } + + private: + IrqDomain* domain_; + irqnum_t irq_; + }; + + [[nodiscard]] constexpr DomainIrqHandle irq(irqnum_t irq = 0) { return {*this, irq}; } + + void mapIrqTo(irqnum_t irq, IrqDomain& target_domain, irqnum_t target_irq); + void removeIrq(irqnum_t irq); + + void irqMappedBy(irqnum_t irq, + IrqDomain& source_domain, + irqnum_t source_irq, + IrqMappingTarget& reverse_ref); + void removeIrqMappedBy(irqnum_t irq, IrqDomain& source_domain, irqnum_t source_irq); + + void setIrqHandler(irqnum_t irq, const IrqInfo::handler_func_t& handler); + IrqInfo::handler_func_t getIrqHandler(irqnum_t irq); + + void handleIrq(irqnum_t irqnum); + void activateIrq(irqnum_t irq, bool activate); + + [[nodiscard]] IrqInfo* irqInfo(irqnum_t irqnum); + + void printReverseMappingTree(irqnum_t irq); + void printAllReverseMappings(); + +protected: + void setNumIrqs(size_t num_irqs); + +private: + using container_type = eastl::vector; + + InterruptController* controller_ = nullptr; + container_type irqs_; + eastl::string name_; +}; + +// Allow structured binding decomposition for DomainIrqHandle +namespace std +{ + template<> + struct tuple_size : eastl::integral_constant + { + }; + + template<> struct tuple_element<0, IrqDomain::DomainIrqHandle> + { + using type = IrqDomain*; + }; + + template<> struct tuple_element<1, IrqDomain::DomainIrqHandle> + { + using type = irqnum_t; + }; +} // namespace std diff --git a/common/include/kernel/Loader.h b/common/include/kernel/Loader.h index c32e5e179..f5a306401 100644 --- a/common/include/kernel/Loader.h +++ b/common/include/kernel/Loader.h @@ -1,67 +1,75 @@ #pragma once -#include "types.h" +#include "ElfFormat.h" #include "Mutex.h" + #include "ArchMemory.h" -#include "ElfFormat.h" -#include -#include + +#include "types.h" + +#include "EASTL/vector.h" class Stabs2DebugInfo; +/** + * Responsible for managing the executable file for a process and loading data on demand + */ class Loader { public: + /** + * Create a new ELF executable loader with the given file descriptor + * + * @param fd an open file descriptor corresponding to the ELF executable + */ Loader(ssize_t fd); ~Loader(); /** - * Loads the ehdr and phdrs from the executable and (optionally) loads debug info. + * Loads the ELF headers from the executable and loads debug info if available. * @return true if this was successful, false otherwise */ bool loadExecutableAndInitProcess(); /** - * Loads one binary page by its virtual address: - * Gets a free physical page, copies the contents of the binary to it, and then maps it. - * @param virtual_address virtual address where to find the page to load + * Load the data for the given virtual page from the ELF executable binary. + * Allocates a new physical page, fills it with data from the executable and maps it into the virtual address space. + * @param virtual_address virtual address that should be loaded */ void loadPage(pointer virtual_address); - Stabs2DebugInfo const* getDebugInfos() const; + const Stabs2DebugInfo* getDebugInfos() const; + /** + * Get the address of the entry function of the ELF executable + */ void* getEntryFunction() const; - ArchMemory arch_memory_; + + ArchMemory arch_memory_; // Object managing the virtual address space and page mappings of the process private: /** - *reads ELF-headers from the executable - * @return true if this was successful, false otherwise + * Read ELF-headers from the executable + * @return true on success, false otherwise */ bool readHeaders(); - /** - * clean up and sort the elf headers for faster access. + * Clean up and sort the elf headers for faster access. * @return true in case the headers could be prepared */ bool prepareHeaders(); - bool loadDebugInfoIfAvailable(); + bool readFromBinary(char* buffer, l_off_t position, size_t length); - bool readFromBinary (char* buffer, l_off_t position, size_t length); - - - size_t fd_; - Elf::Ehdr *hdr_; - ustl::list phdrs_; + size_t fd_; // file descriptor corresponding to the opened executable + Elf::Ehdr *hdr_; // ELF header read from the executable file + eastl::vector phdrs_; // ELF program headers describing the virtual address space layout of the program and file offsets where data should be loaded from Mutex program_binary_lock_; Stabs2DebugInfo *userspace_debug_info_; - }; - diff --git a/common/include/kernel/Lock.h b/common/include/kernel/Lock.h index 7fbbdea77..b140f66e7 100644 --- a/common/include/kernel/Lock.h +++ b/common/include/kernel/Lock.h @@ -58,6 +58,14 @@ class Lock return next_lock_on_holding_list_; } + /** + * Print the lock status. + */ + void printStatus(); + + bool debug_lock = false; + + protected: /** @@ -130,7 +138,7 @@ class Lock * Lock the waiters list, so it may be modified. * The lock may not be held in case the list is read out in some special cases. */ - void lockWaitersList(); + void lockWaitersList(bool yield = true); /** * unlock the waiters list. @@ -160,15 +168,10 @@ class Lock */ void pushFrontCurrentThreadToWaitersList(); - /** - * Print the lock status. - */ - void printStatus(); - /** * Release the lock, and set the thread to sleeping. */ - void sleepAndRelease(); + void sleepAndRelease(bool should_yield = true); private: @@ -204,6 +207,4 @@ class Lock * @param starting The thread which shall be started at */ void printOutCircularDeadLock(Thread* starting); - }; - diff --git a/common/include/kernel/Mutex.h b/common/include/kernel/Mutex.h index 1a3b49466..568614b55 100644 --- a/common/include/kernel/Mutex.h +++ b/common/include/kernel/Mutex.h @@ -1,8 +1,10 @@ #pragma once -#include "types.h" -#include "ScopeLock.h" #include "Lock.h" +#include "ScopeLock.h" + +#include "types.h" + class Thread; class Mutex: public Lock @@ -14,8 +16,8 @@ class Mutex: public Lock Mutex(const char* name); - Mutex(Mutex const &) = delete; - Mutex &operator=(Mutex const&) = delete; + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; /** * like acquire, but instead of blocking the currentThread until the Lock is free @@ -24,10 +26,10 @@ class Mutex: public Lock * @param called_by A pointer to the call point of this function. * Can be set in case this method is called by a wrapper function. */ - bool acquireNonBlocking(pointer called_by = 0); + bool acquireNonBlocking(pointer called_by = 0, bool do_checks = true); void acquire(pointer called_by = 0); - void release(pointer called_by = 0); + void release(pointer called_by = 0, bool do_checks = true); /** * allows us to check if the Lock is set or not. @@ -38,7 +40,7 @@ class Mutex: public Lock * * @return true if lock is set, false otherwise */ - bool isFree(); + bool isFree() const; private: diff --git a/common/include/kernel/ProcessRegistry.h b/common/include/kernel/ProcessRegistry.h index cd8722ca0..7b73321b6 100644 --- a/common/include/kernel/ProcessRegistry.h +++ b/common/include/kernel/ProcessRegistry.h @@ -1,24 +1,24 @@ #pragma once -#include "Thread.h" -#include "Mutex.h" #include "Condition.h" +#include "FileSystemInfo.h" +#include "Mutex.h" + +#include +#include -class ProcessRegistry : public Thread +class ProcessRegistry { public: /** * Constructor - * @param root_fs_info the FileSystemInfo - * @param progs a string-array of the userprograms which should be executed + * @param root_fs_info the default working directory to be used for new processes */ - ProcessRegistry ( FileSystemInfo *root_fs_info, char const *progs[] ); - ~ProcessRegistry(); + ProcessRegistry(FileSystemInfo* root_fs_info); + virtual ~ProcessRegistry() = default; - /** - * Mounts the Minix-Partition with user-programs and creates processes - */ - virtual void Run(); + static ProcessRegistry* instance(); + static void init(FileSystemInfo* root_fs_info); /** * Tells us that a userprocess is being destroyed @@ -35,15 +35,15 @@ class ProcessRegistry : public Thread */ size_t processCount(); - static ProcessRegistry* instance(); - void createProcess(const char* path); + void waitAllKilled(); + + int createProcess(const char* path); private: - char const **progs_; - uint32 progs_running_; + FileSystemInfo* default_working_dir_; + uint32_t progs_running_; Mutex counter_lock_; Condition all_processes_killed_; static ProcessRegistry* instance_; }; - diff --git a/common/include/kernel/Scheduler.h b/common/include/kernel/Scheduler.h index c8fa74bf7..3386907da 100644 --- a/common/include/kernel/Scheduler.h +++ b/common/include/kernel/Scheduler.h @@ -1,22 +1,46 @@ #pragma once -#include "types.h" -#include -#include "IdleThread.h" #include "CleanupThread.h" +#include "IdleThread.h" +#include "SMP.h" +#include "SchedulerLock.h" +#include "Thread.h" + +#include "ArchCommon.h" +#include "ArchCpuLocalStorage.h" +#include "ArchInterrupts.h" +#include "ArchMulticore.h" + +#include "types.h" + +#include "EASTL/atomic.h" +#include "EASTL/list.h" +#include "EASTL/set.h" +#include "EASTL/vector_multiset.h" + +#include "debug.h" class Thread; class Mutex; class SpinLock; class Lock; +class PreemptProtect; + +extern __cpu Thread* currentThread; +extern __cpu ArchThreadRegisters* currentThreadRegisters; + +extern cpu_local IdleThread* idle_thread; + +extern __cpu eastl::atomic preempt_protect_count_; class Scheduler { public: static Scheduler *instance(); + static bool isInitialized(); void addNewThread(Thread *thread); - void sleep(); + void sleep(bool yield = true); void wake(Thread *thread_to_wake); void yield(); void printThreadList(); @@ -24,8 +48,8 @@ class Scheduler void printLockingInformation(); bool isSchedulingEnabled(); bool isCurrentlyCleaningUp(); - void incTicks(); - size_t getTicks(); + void incCpuTimerTicks(); + size_t getCpuTimerTicks() const; /** * NEVER EVER EVER CALL THIS METHOD OUTSIDE OF AN INTERRUPT CONTEXT @@ -38,9 +62,22 @@ class Scheduler protected: friend class IdleThread; friend class CleanupThread; + friend class CpuLocalScheduler; + friend class PreemptProtect; void cleanupDeadThreads(); + struct ThreadVruntimeLess + { + constexpr bool operator()(const Thread* lhs, const Thread* rhs) const + { + return lhs->vruntime < rhs->vruntime; + } + }; + + // vector_multiset does not alloc on erase/insert ! + using ThreadList = eastl::vector_multiset; + private: Scheduler(); @@ -49,23 +86,58 @@ class Scheduler * locks the thread-list against concurrent access by prohibiting a thread switch * don't call this from an Interrupt-Handler, since Atomicity won't be guaranteed */ - void lockScheduling(); + void lockScheduling(const char* called_at); /** * Scheduler internal lock abstraction method * unlocks the thread-list */ - void unlockScheduling(); + void unlockScheduling(const char* called_at); + + uint64 descheduleThread(Thread* t, uint64 deschedule_time); static Scheduler *instance_; - typedef ustl::list ThreadList; - ThreadList threads_; + SchedulerLock scheduler_lock_; - size_t block_scheduling_; + volatile char* locked_at_ = nullptr; size_t ticks_; - IdleThread idle_thread_; CleanupThread cleanup_thread_; + +public: + ThreadList threads_; + + eastl::atomic num_threads; + eastl::atomic scheduler_lock_count_free; + eastl::atomic scheduler_lock_count_blocked; + + Thread* minVruntimeThread(); + Thread* maxVruntimeThread(); + uint64 updateVruntime(Thread* t, uint64 now); + ThreadList::iterator setThreadVruntime(Thread* t, uint64 new_vruntime); + ThreadList::iterator setThreadVruntime(Scheduler::ThreadList::iterator it, uint64 new_vruntime); +}; + +class PreemptProtect +{ +public: + PreemptProtect() : + intr(false) + { + ++preempt_protect_count_; + size_t cpu_id = SMP::currentCpuId(); + ((char*)ArchCommon::getFBPtr())[80*2 + cpu_id*2 + 1] = CONSOLECOLOR::WHITE | (CONSOLECOLOR::RED << 4); + } + + ~PreemptProtect() + { + size_t cpu_id = SMP::currentCpuId(); + ((char*)ArchCommon::getFBPtr())[80*2 + cpu_id*2 + 1] = CONSOLECOLOR::WHITE | (CONSOLECOLOR::BLACK << 4); + --preempt_protect_count_; + } + +private: + WithInterrupts intr; }; diff --git a/common/include/kernel/SchedulerLock.h b/common/include/kernel/SchedulerLock.h new file mode 100644 index 000000000..486839ba8 --- /dev/null +++ b/common/include/kernel/SchedulerLock.h @@ -0,0 +1,21 @@ +#pragma once + +#include "CpuExclusiveLock.h" + +#include "types.h" + +class Thread; + +class SchedulerLock : public CpuExclusiveLock +{ +public: + SchedulerLock(); + + SchedulerLock(const SchedulerLock&) = delete; + + void acquire(pointer called_by = 0) override; + void release(pointer called_by = 0) override; +private: + + volatile Thread* scheduling_locked_by_ = nullptr; +}; diff --git a/common/include/kernel/ScopeLock.h b/common/include/kernel/ScopeLock.h index f322c7a29..260f1a90b 100644 --- a/common/include/kernel/ScopeLock.h +++ b/common/include/kernel/ScopeLock.h @@ -1,19 +1,21 @@ #pragma once +#include "backtrace.h" + #include "types.h" class Mutex; class ScopeLock { - public: - ScopeLock(Mutex &m); +public: + ScopeLock(Mutex& m, bool b = true, pointer called_by = getCalledBefore(0)); ~ScopeLock(); - ScopeLock(ScopeLock const&) = delete; - ScopeLock &operator=(ScopeLock const&) = delete; + ScopeLock(const ScopeLock&) = delete; + ScopeLock& operator=(const ScopeLock&) = delete; - private: - Mutex &mutex_; +private: + Mutex& mutex_; + bool use_mutex_; }; - diff --git a/common/include/kernel/SpinLock.h b/common/include/kernel/SpinLock.h index d321cd2f0..9488990f5 100644 --- a/common/include/kernel/SpinLock.h +++ b/common/include/kernel/SpinLock.h @@ -1,8 +1,10 @@ #pragma once -#include "types.h" #include "Lock.h" +#include "types.h" +#include + class Thread; class SpinLock: public Lock @@ -11,10 +13,10 @@ class SpinLock: public Lock SpinLock(const char* name); - SpinLock(SpinLock const &) = delete; - SpinLock &operator=(SpinLock const&) = delete; + SpinLock(const SpinLock&) = delete; + SpinLock& operator=(const SpinLock&) = delete; - void acquire(pointer called_by = 0); + void acquire(pointer called_by = 0, bool yield = true); /** * Try to acquire the spinlock. If the spinlock is held by another thread at the moment, @@ -33,7 +35,7 @@ class SpinLock: public Lock * trust the return value only if the SpinLock can't be acquired or releases * when you're not locking. (= only use in atomic state) */ - bool isFree(); + bool isFree() const; private: /** @@ -41,4 +43,3 @@ class SpinLock: public Lock */ size_t lock_; }; - diff --git a/common/include/kernel/Syscall.h b/common/include/kernel/Syscall.h index 8088db193..43b2b6ec2 100644 --- a/common/include/kernel/Syscall.h +++ b/common/include/kernel/Syscall.h @@ -1,22 +1,30 @@ #pragma once +#include #include -class Syscall +namespace Syscall { - public: - static size_t syscallException(size_t syscall_number, size_t arg1, size_t arg2, size_t arg3, size_t arg4, size_t arg5); + size_t syscallException(size_t syscall_number, + size_t arg1, + size_t arg2, + size_t arg3, + size_t arg4, + size_t arg5); - static void exit(size_t exit_code); - static void outline(size_t port, pointer text); + [[noreturn]] void exit(size_t exit_code); + void outline(size_t port, pointer text); - static size_t write(size_t fd, pointer buffer, size_t size); - static size_t read(size_t fd, pointer buffer, size_t count); - static size_t close(size_t fd); - static size_t open(size_t path, size_t flags); - static void pseudols(const char *pathname, char *buffer, size_t size); + size_t write(size_t fd, pointer buffer, size_t size); + size_t read(size_t fd, pointer buffer, size_t count); + size_t close(size_t fd); + size_t open(size_t path, size_t flags); + size_t lseek(int fd, off_t offset, int whence); + void pseudols(const char* pathname, char* buffer, size_t size); + ssize_t getdents(int fd, char* buffer, size_t size); - static size_t createprocess(size_t path, size_t sleep); - static void trace(); -}; + size_t createprocess(size_t path, size_t sleep); + void trace(); + int getcpu(size_t* cpu, size_t* node, void* tcache); +}; // namespace Syscall diff --git a/common/include/kernel/SystemState.h b/common/include/kernel/SystemState.h new file mode 100644 index 000000000..d9baea7f8 --- /dev/null +++ b/common/include/kernel/SystemState.h @@ -0,0 +1,4 @@ +#pragma once + +enum SystemState { BOOTING, RUNNING, KPANIC }; +extern SystemState system_state; diff --git a/common/include/kernel/Thread.h b/common/include/kernel/Thread.h index 641124427..2f6a5563c 100644 --- a/common/include/kernel/Thread.h +++ b/common/include/kernel/Thread.h @@ -1,29 +1,33 @@ #pragma once -#include "types.h" +#include "VgaColors.h" #include "fs/FileSystemInfo.h" +#include "types.h" + +#include "EASTL/string.h" +#include "EASTL/unique_ptr.h" + #define STACK_CANARY ((uint32)0xDEADDEAD ^ (uint32)(size_t)this) -enum ThreadState{Running, Sleeping, ToBeDestroyed}; -enum SystemState {BOOTING, RUNNING, KPANIC}; -extern SystemState system_state; class Thread; -class ArchThreadRegisters; +struct ArchThreadRegisters; class Loader; class Terminal; class Mutex; class Lock; -extern Thread* currentThread; - class Thread { friend class Scheduler; - public: + friend class CpuLocalScheduler; +public: - static const char* threadStatePrintable[3]; + enum ThreadState { Running, Sleeping, ToBeDestroyed }; + static constexpr const char* threadStatePrintable[3] = { + "Running", "Sleeping", "ToBeDestroyed" + }; enum TYPE { KERNEL_THREAD, USER_THREAD }; @@ -33,100 +37,156 @@ class Thread * @param name The name of the thread * @param type The type of the thread (user or kernel thread) */ - Thread(FileSystemInfo* working_dir, ustl::string name, Thread::TYPE type); + Thread(FileSystemInfo* working_dir, eastl::string name, Thread::TYPE type); virtual ~Thread(); + Thread(const Thread& src) = delete; + Thread& operator=(const Thread& src) = delete; + /** - * Marks the thread to be deleted by the scheduler. + * Marks the thread to be deleted by the CleanupThread. * DO Not use new / delete in this Method, as it sometimes called from an Interrupt Handler with Interrupts disabled */ virtual void kill(); /** * runs whatever the user wants it to run; + * only used for kernel threads since user threads start in userspace */ virtual void Run() = 0; - void* getKernelStackStartPointer(); - - bool isStackCanaryOK(); - - static bool currentThreadIsStackCanaryOK(); + [[nodiscard]] const char* getName() const; - const char* getName(); + [[nodiscard]] size_t getTID() const; - size_t getTID(); + [[nodiscard]] Terminal* getTerminal() const; + void setTerminal(Terminal* my_term); - Terminal* getTerminal(); + [[nodiscard]] FileSystemInfo* getWorkingDirInfo() const; + void setWorkingDirInfo(FileSystemInfo* working_dir); - void setTerminal(Terminal *my_term); + /** + * Get a pointer to the start (highest address) of the kernel stack + */ + [[nodiscard]] void* getKernelStackStartPointer() const; - FileSystemInfo* getWorkingDirInfo(); + [[nodiscard]] bool isStackCanaryOK() const; + [[nodiscard]] static bool currentThreadIsStackCanaryOK(); - void setWorkingDirInfo(FileSystemInfo* working_dir); /** * Prints a backtrace (i.e. the call stack) to the debug output. - * @param use_stored_thread_info determines whether to use the stored or the current thread registers */ void printBacktrace(); + /** + * Prints a backtrace (i.e. the call stack) to the debug output. + * @param use_stored_registers whether to use the stored or the current thread registers + */ void printBacktrace(bool use_stored_registers); /** - * Tells the scheduler if this thread is ready for scheduling + * Tells the scheduler if this thread can be scheduled * @return true if ready for scheduling */ bool schedulable(); - - - uint32 kernel_stack_[2048]; - ArchThreadRegisters* kernel_registers_; - ArchThreadRegisters* user_registers_; - uint32 switch_to_userspace_; + /** + * Check if the thread is allowed to run on the given CPU + * + * @param cpu_id id of the CPU + * @return true if the thread is allowed to run on the given CPU, false otherwise + */ + [[nodiscard]] bool canRunOnCpu(size_t cpu_id) const; - Loader* loader_; + /** + * Check if the thread is currently scheduled on any CPU + */ + [[nodiscard]] bool isCurrentlyScheduled() const; + /** + * Check if the thread is currently scheduled on the given CPU + */ + [[nodiscard]] bool isCurrentlyScheduledOnCpu(size_t cpu_id) const; + /** + * Set the scheduling start timestamp. Used by the scheduler to track thread runtime. + */ + void setSchedulingStartTimestamp(uint64 timestamp); + /** + * Get the scheduling start timestamp. + */ + [[nodiscard]] uint64 schedulingStartTimestamp() const; + // If you think you need to use this, you probably don't. + // Ensuring proper synchronization here is not trivial and chances are you'll introduce a race condition. + // This function is intended for internal low level use by locking mechanisms. + // Use the provided higher level mechanisms instead (e.g. Condition, Mutex, ...) void setState(ThreadState state); + + uint32 kernel_stack_[2048]; // Stack used by the thread while it is running in the kernel + + eastl::unique_ptr kernel_registers_; // Stores thread context for user mode + eastl::unique_ptr user_registers_; // Stores thread context for kernel mode + + uint32 switch_to_userspace_; // Whether the thread is running in userspace or in kernel (determines which register set is used on context switch) + + Loader* loader_ = nullptr; + /** * A part of the single-chained waiters list for the locks. * It references to the next element of the list. - * In case of a spinlock it is a busy-waiter, else usually it is a sleeper ^^. */ - Thread* next_thread_in_lock_waiters_list_; + Thread* next_thread_in_lock_waiters_list_ = nullptr; /** - * The information which lock the thread is currently waiting on. + * Pointer to the lock the thread is currently waiting on (if any) */ - Lock* lock_waiting_on_; + Lock* lock_waiting_on_ = nullptr; /** * A single chained list containing all the locks held by the thread at the moment. - * This list is not locked. It may only be accessed by the thread himself, - * or by other threads in case they can ENSURE that this thread is not able to run at this moment. - * Changing the list has to be done atomic, else it cannot be ensured that the list is valid at any moment! + * This list is not locked. It may only safely be accessed by the thread itself, + * or by other threads if they can ENSURE that this thread is not able to run at this moment. + * Changing the list has to be done atomic as it needs to be valid at all times! */ - Lock* holding_lock_list_; + Lock* holding_lock_list_ = nullptr; - private: - Thread(Thread const &src); - Thread &operator=(Thread const &src); + volatile size_t currently_scheduled_on_cpu_ = -1; // Id of the CPU the thread is currently scheduled on (-1 if not scheduled) + volatile size_t pinned_to_cpu = -1; // Id of the CPU the thread is pinned to (-1 if allowed to run on all CPUs) - volatile ThreadState state_; + CONSOLECOLOR console_color; // Color used for the debug spinner background at the top left of the SWEB console window while the thread is running - size_t tid_; + bool* kprintfd_recursion_detected = nullptr; // Used detect recursive calls to kprintfd and prevent deadlocks (e.g. pagefault inside kprintfd) - Terminal* my_terminal_; +private: + void initKernelStackCanary(); - protected: - ThreadState getState() const; + volatile ThreadState state_; // Run state of the thread. Can change at any time. + + size_t tid_; // Thread id + + Terminal* my_terminal_ = nullptr; + +protected: + // Only valid for currentThread (but why do you need to ask in this case?) or if + // you can be sure the thread won't change state (e.g. a thread will never change out of the ToBeDestroyed state) + [[nodiscard]] ThreadState getState() const; FileSystemInfo* working_dir_; - ustl::string name_; + eastl::string name_; -}; + /** + * Virtual runtime of the thread. Used by scheduler to determine which thread to schedule next. + * Does **NOT** correspond to the actual time a thread has been running. Only useful for the scheduler itself. + */ + uint64 vruntime = 0; + /** + * Timestamp when the thread has been scheduled. Used by scheduler to track thread runtime. + */ + uint64 sched_start = 0; + bool prev_schedulable = false; // Used by scheduler to ensure correct timeslot distribution when a thread is blocked and cannot be scheduled + bool yielded = false; // Used by scheduler to adjust thread vruntime and prioritize other threads if a thread yields its timeslot +}; diff --git a/common/include/kernel/TimerTickHandler.h b/common/include/kernel/TimerTickHandler.h new file mode 100644 index 000000000..a1c24ad4c --- /dev/null +++ b/common/include/kernel/TimerTickHandler.h @@ -0,0 +1,9 @@ +#pragma once + +class TimerTickHandler +{ +public: + static void handleTimerTick(); + +private: +}; diff --git a/common/include/kernel/UserProcess.h b/common/include/kernel/UserProcess.h index 2c6c94147..291333568 100644 --- a/common/include/kernel/UserProcess.h +++ b/common/include/kernel/UserProcess.h @@ -2,23 +2,29 @@ #include "Thread.h" +#include + +#include "EASTL/string.h" + class UserProcess : public Thread { - public: +public: /** * Constructor - * @param minixfs_filename filename of the file in minixfs to execute - * @param fs_info filesysteminfo-object to be used + * @param executable_path path to the file to execute + * @param working_dir working directory for the process * @param terminal_number the terminal to run in (default 0) * */ - UserProcess(ustl::string minixfs_filename, FileSystemInfo *fs_info, uint32 terminal_number = 0); + UserProcess(const eastl::string& executable_path, + FileSystemInfo* working_dir, + uint32_t terminal_number, + int& creation_status); virtual ~UserProcess(); virtual void Run(); // not used - private: - int32 fd_; +private: + int32_t fd_; }; - diff --git a/common/include/kernel/smp/Cpu.h b/common/include/kernel/smp/Cpu.h new file mode 100644 index 000000000..929034d0d --- /dev/null +++ b/common/include/kernel/smp/Cpu.h @@ -0,0 +1,32 @@ +#pragma once + +#include "AtomicMpScQueue.h" +#include "Device.h" +#include "RemoteFunctionCall.h" + +#include "ArchCpuLocalStorage.h" + +#include + +// Information concerning one cpu in the system +// Base functionality for all architectures +class Cpu : public Device +{ +public: + Cpu(); + Cpu(const Cpu&) = delete; + Cpu& operator=(const Cpu&) = delete; + + size_t id(); + void setId(size_t id); + + void enqueueFunctionCallMessage(RemoteFunctionCallMessage* fcall_message); + + AtomicMpScQueue fcall_queue; + +private: + // pointer to thread local cpu_id variable so we can read the id from other cpus + size_t* cpu_id_; + + cpu_local static size_t cpu_id; +}; diff --git a/common/include/kernel/smp/RemoteFunctionCall.h b/common/include/kernel/smp/RemoteFunctionCall.h new file mode 100644 index 000000000..6b893bdee --- /dev/null +++ b/common/include/kernel/smp/RemoteFunctionCall.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "EASTL/atomic.h" +#include "EASTL/fixed_function.h" + +struct RemoteFunctionCallMessage +{ + using function_t = eastl::fixed_function<8, void (void)>; + + function_t func; + + eastl::atomic received; + eastl::atomic done; + + size_t target_cpu; + size_t orig_cpu; + + eastl::atomic next; +}; diff --git a/common/include/kernel/smp/SMP.h b/common/include/kernel/smp/SMP.h new file mode 100644 index 000000000..528826fee --- /dev/null +++ b/common/include/kernel/smp/SMP.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Mutex.h" +#include "RemoteFunctionCall.h" + +#include "ArchCpuLocalStorage.h" +#include "ArchMulticore.h" + +#include + +#include "EASTL/atomic.h" +#include "EASTL/vector.h" + +class ArchCpu; + +extern cpu_local ArchCpu current_cpu; + + +// Information and functions concerning multiple cpus +class SMP +{ +public: + + static ArchCpu& currentCpu(); + static size_t currentCpuId(); + + static size_t numRunningCpus(); + + /** + * Call a function on all other CPUs + * Functions run in an interrupt handler and therefore need to be fast and not use any locks + */ + static void callOnOtherCpus(const RemoteFunctionCallMessage::function_t& func); + + + static void initialize(); + + static void addCpuToList(ArchCpu* cpu); + static ArchCpu* cpu(size_t cpu_id); + + static eastl::vector& cpuList(); + static Mutex& cpuListLock(); +}; diff --git a/common/include/kernel/syscall-definitions.h b/common/include/kernel/syscall-definitions.h index dd99d1978..c4240b28d 100644 --- a/common/include/kernel/syscall-definitions.h +++ b/common/include/kernel/syscall-definitions.h @@ -11,9 +11,10 @@ #define sc_open 5 #define sc_close 6 #define sc_lseek 19 -#define sc_pseudols 43 +#define sc_getdents 44 #define sc_outline 105 #define sc_sched_yield 158 #define sc_createprocess 191 #define sc_trace 252 +#define sc_getcpu 300 diff --git a/common/include/kernel/user_progs.h b/common/include/kernel/user_progs.h index 656172747..7a9f53f30 100644 --- a/common/include/kernel/user_progs.h +++ b/common/include/kernel/user_progs.h @@ -3,7 +3,6 @@ // DO NOT CHANGE THE NAME OR THE TYPE OF THE user_progs VARIABLE! char const *user_progs[] = { // for reasons of automated testing - "/usr/shell.sweb", - 0 - }; - + "/usr/shell.sweb", + 0 +}; diff --git a/common/include/klibc/assert.h b/common/include/klibc/assert.h new file mode 100644 index 000000000..e7915bd33 --- /dev/null +++ b/common/include/klibc/assert.h @@ -0,0 +1,31 @@ +#pragma once + +#include "stdint.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifndef assert +#define assert(cond) do { (cond) ? (void)0 : sweb_assert( #cond ,__LINE__,__FILE__, __PRETTY_FUNCTION__); } while (0) +#endif + + /** + * called when assertion is used and true + * @param condition assertion that has to be checked + * @param line the function which to jump to + * @param file where the assertion is called + * @param function where the assertion is called + */ + __attribute__((noreturn)) void sweb_assert(const char *condition, uint32_t line, const char* file, const char* function); + + +#ifdef __cplusplus +} +#endif + +#if defined __USE_ISOC11 && !defined __cplusplus +# undef static_assert +# define static_assert _Static_assert +#endif diff --git a/common/include/klibc/cctype b/common/include/klibc/cctype new file mode 100644 index 000000000..a212af9be --- /dev/null +++ b/common/include/klibc/cctype @@ -0,0 +1,85 @@ +#pragma once + +// -*- C++ -*- forwarding header. + +// Copyright (C) 1997-2021 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file include/cctype + * This is a Standard C++ Library file. You should @c \#include this file + * in your programs, rather than any of the @a *.h implementation files. + * + * This is the C++ version of the Standard C Library header @c ctype.h, + * and its contents are (mostly) the same as that header, but are all + * contained in the namespace @c std (except for names which are defined + * as macros in C). + */ + +// +// ISO C++ 14882: +// + +#include + + +// Get rid of those macros defined in in lieu of real functions. +#undef isalnum +#undef isalpha +#undef iscntrl +#undef isdigit +#undef isgraph +#undef islower +#undef isprint +#undef ispunct +#undef isspace +#undef isupper +#undef isxdigit +#undef tolower +#undef toupper + +namespace std +{ + using ::isalnum; + using ::isalpha; + using ::iscntrl; + using ::isdigit; + using ::isgraph; + using ::islower; + using ::isprint; + using ::ispunct; + using ::isspace; + using ::isupper; + using ::isxdigit; + using ::tolower; + using ::toupper; +} // namespace std + +#if __cplusplus >= 201103L + +#undef isblank + +namespace std +{ + using ::isblank; +} // namespace std + +#endif // C++11 diff --git a/common/include/klibc/cfloat b/common/include/klibc/cfloat new file mode 100644 index 000000000..919c17c79 --- /dev/null +++ b/common/include/klibc/cfloat @@ -0,0 +1,12 @@ +#pragma once + +#include "float.h" + +#if __cplusplus >= 201103L +# ifndef DECIMAL_DIG +# define DECIMAL_DIG __DECIMAL_DIG__ +# endif +# ifndef FLT_EVAL_METHOD +# define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ +# endif +#endif diff --git a/common/include/klibc/cinttypes b/common/include/klibc/cinttypes new file mode 100644 index 000000000..89785818e --- /dev/null +++ b/common/include/klibc/cinttypes @@ -0,0 +1,4 @@ +#pragma once + +#include "cstdint" +#include "inttypes.h" diff --git a/common/include/klibc/climits b/common/include/klibc/climits new file mode 100644 index 000000000..20e1a7cb0 --- /dev/null +++ b/common/include/klibc/climits @@ -0,0 +1,15 @@ +#pragma once + +#include "limits.h" + +#ifndef LLONG_MIN +#define LLONG_MIN (-__LONG_LONG_MAX__ - 1) +#endif + +#ifndef LLONG_MAX +#define LLONG_MAX __LONG_LONG_MAX__ +#endif + +#ifndef ULLONG_MAX +#define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1) +#endif diff --git a/common/include/klibc/cmath b/common/include/klibc/cmath new file mode 100644 index 000000000..9e3d2dddb --- /dev/null +++ b/common/include/klibc/cmath @@ -0,0 +1,3 @@ +#pragma once + +#include "math.h" diff --git a/common/include/klibc/cstdarg b/common/include/klibc/cstdarg new file mode 100644 index 000000000..0f4649fa9 --- /dev/null +++ b/common/include/klibc/cstdarg @@ -0,0 +1,8 @@ +#pragma once + +#include "stdarg.h" + +namespace std +{ + using ::va_list; +} diff --git a/common/include/klibc/cstddef b/common/include/klibc/cstddef new file mode 100644 index 000000000..7f3ba3b04 --- /dev/null +++ b/common/include/klibc/cstddef @@ -0,0 +1,113 @@ +#pragma once + +#include "stddef.h" + +namespace std { + using ::ptrdiff_t; + using ::size_t; + using ::max_align_t; + using nullptr_t = decltype(nullptr); + + /// std::byte + enum class byte : unsigned char {}; + + template struct __byte_operand { }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; + template<> struct __byte_operand { using __type = byte; }; +#if defined(__GLIBCXX_TYPE_INT_N_0) + template<> struct __byte_operand<__GLIBCXX_TYPE_INT_N_0> + { using __type = byte; }; + template<> struct __byte_operand + { using __type = byte; }; +#endif +#if defined(__GLIBCXX_TYPE_INT_N_1) + template<> struct __byte_operand<__GLIBCXX_TYPE_INT_N_1> + { using __type = byte; }; + template<> struct __byte_operand + { using __type = byte; }; +#endif +#if defined(__GLIBCXX_TYPE_INT_N_2) + template<> struct __byte_operand<__GLIBCXX_TYPE_INT_N_2> + { using __type = byte; }; + template<> struct __byte_operand + { using __type = byte; }; +#endif + template + struct __byte_operand + : __byte_operand<_IntegerType> { }; + template + struct __byte_operand + : __byte_operand<_IntegerType> { }; + template + struct __byte_operand + : __byte_operand<_IntegerType> { }; + + template + using __byte_op_t = typename __byte_operand<_IntegerType>::__type; + + template + constexpr __byte_op_t<_IntegerType> + operator<<(byte __b, _IntegerType __shift) noexcept + { return (byte)(unsigned char)((unsigned)__b << __shift); } + + template + constexpr __byte_op_t<_IntegerType> + operator>>(byte __b, _IntegerType __shift) noexcept + { return (byte)(unsigned char)((unsigned)__b >> __shift); } + + constexpr byte + operator|(byte __l, byte __r) noexcept + { return (byte)(unsigned char)((unsigned)__l | (unsigned)__r); } + + constexpr byte + operator&(byte __l, byte __r) noexcept + { return (byte)(unsigned char)((unsigned)__l & (unsigned)__r); } + + constexpr byte + operator^(byte __l, byte __r) noexcept + { return (byte)(unsigned char)((unsigned)__l ^ (unsigned)__r); } + + constexpr byte + operator~(byte __b) noexcept + { return (byte)(unsigned char)~(unsigned)__b; } + + template + constexpr __byte_op_t<_IntegerType>& + operator<<=(byte& __b, _IntegerType __shift) noexcept + { return __b = __b << __shift; } + + template + constexpr __byte_op_t<_IntegerType>& + operator>>=(byte& __b, _IntegerType __shift) noexcept + { return __b = __b >> __shift; } + + constexpr byte& + operator|=(byte& __l, byte __r) noexcept + { return __l = __l | __r; } + + constexpr byte& + operator&=(byte& __l, byte __r) noexcept + { return __l = __l & __r; } + + constexpr byte& + operator^=(byte& __l, byte __r) noexcept + { return __l = __l ^ __r; } + + template + [[nodiscard]] + constexpr _IntegerType + to_integer(__byte_op_t<_IntegerType> __b) noexcept + { return _IntegerType(__b); } +} diff --git a/common/include/klibc/cstdint b/common/include/klibc/cstdint new file mode 100644 index 000000000..c77e79694 --- /dev/null +++ b/common/include/klibc/cstdint @@ -0,0 +1,42 @@ +#pragma once + +#include "stdint.h" + + +namespace std { + using ::int8_t; + using ::int16_t; + using ::int32_t; + using ::int64_t; + + using ::int_fast8_t; + using ::int_fast16_t; + using ::int_fast32_t; + using ::int_fast64_t; + + using ::int_least8_t; + using ::int_least16_t; + using ::int_least32_t; + using ::int_least64_t; + + using ::intmax_t; + using ::intptr_t; + + using ::uint8_t; + using ::uint16_t; + using ::uint32_t; + using ::uint64_t; + + using ::uint_fast8_t; + using ::uint_fast16_t; + using ::uint_fast32_t; + using ::uint_fast64_t; + + using ::uint_least8_t; + using ::uint_least16_t; + using ::uint_least32_t; + using ::uint_least64_t; + + using ::uintmax_t; + using ::uintptr_t; +} diff --git a/common/include/klibc/cstring b/common/include/klibc/cstring new file mode 100644 index 000000000..6354e9680 --- /dev/null +++ b/common/include/klibc/cstring @@ -0,0 +1,29 @@ +#pragma once + +#include "string.h" + +namespace std +{ + using ::memchr; + using ::memcmp; + using ::memcpy; + using ::memmove; + using ::memset; + using ::strcat; + using ::strcmp; + // using ::strcoll; + using ::strcpy; + // using ::strcspn; + // using ::strerror; + using ::strlen; + using ::strncat; + using ::strncmp; + using ::strncpy; + // using ::strspn; + using ::strtok; + // using ::strxfrm; + using ::strchr; + // using ::strpbrk; + using ::strrchr; + // using ::strstr; +} diff --git a/common/include/klibc/cstring.h b/common/include/klibc/cstring.h new file mode 100644 index 000000000..f1315794a --- /dev/null +++ b/common/include/klibc/cstring.h @@ -0,0 +1,3 @@ +#pragma once + +#include "string.h" diff --git a/common/include/klibc/ctype.h b/common/include/klibc/ctype.h new file mode 100644 index 000000000..0b868b4e6 --- /dev/null +++ b/common/include/klibc/ctype.h @@ -0,0 +1,60 @@ +#pragma once + +/* + * NOTE! This ctype does not handle EOF like the standard C + * library is required to. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define _U 0x01 /* upper */ +#define _L 0x02 /* lower */ +#define _D 0x04 /* digit */ +#define _C 0x08 /* cntrl */ +#define _P 0x10 /* punct */ +#define _S 0x20 /* white space (space/lf/tab) */ +#define _X 0x40 /* hex digit */ +#define _SP 0x80 /* hard space (0x20) */ + +extern unsigned char _ctype[]; + +#define __ismask(x) (_ctype[(int)(unsigned char)(x)]) + +#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0) +#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0) +#define iscntrl(c) ((__ismask(c)&(_C)) != 0) +#define isdigit(c) ((__ismask(c)&(_D)) != 0) +#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0) +#define islower(c) ((__ismask(c)&(_L)) != 0) +#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0) +#define ispunct(c) ((__ismask(c)&(_P)) != 0) +#define isspace(c) ((__ismask(c)&(_S)) != 0) +#define isupper(c) ((__ismask(c)&(_U)) != 0) +#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0) + +#define isascii(c) (((unsigned char)(c))<=0x7f) +#define toascii(c) (((unsigned char)(c))&0x7f) + +static inline unsigned char __tolower(unsigned char c) +{ + if (isupper(c)) + c -= 'A'-'a'; + return c; +} + +static inline unsigned char __toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +#define tolower(c) __tolower(c) +#define toupper(c) __toupper(c) + + +#ifdef __cplusplus +} +#endif diff --git a/common/include/klibc/exception b/common/include/klibc/exception new file mode 100644 index 000000000..851ebf4e5 --- /dev/null +++ b/common/include/klibc/exception @@ -0,0 +1,41 @@ +#pragma once + +namespace std +{ + /** + * @defgroup exceptions Exceptions + * @ingroup diagnostics + * @since C++98 + * + * Classes and functions for reporting errors via exceptions. + * @{ + */ + + /** + * @brief Base class for all library exceptions. + * + * This is the base class for all exceptions thrown by the standard + * library, and by certain language expressions. You are free to derive + * your own %exception classes, or use a different hierarchy, or to + * throw non-class data (e.g., fundamental types). + */ + class exception + { + public: + exception() noexcept { } + virtual ~exception() noexcept; +#if __cplusplus >= 201103L + exception(const exception&) = default; + exception& operator=(const exception&) = default; + exception(exception&&) = default; + exception& operator=(exception&&) = default; +#endif + + /** Returns a C-style character string describing the general cause + * of the current error. */ + virtual const char* what() const noexcept; + }; + + /// @} + +} // namespace std diff --git a/common/include/klibc/float.h b/common/include/klibc/float.h new file mode 100644 index 000000000..331ec46e7 --- /dev/null +++ b/common/include/klibc/float.h @@ -0,0 +1,103 @@ +#pragma once + +#ifdef __FLT_RADIX__ +#define FLT_RADIX __FLT_RADIX__ +#endif + +#ifdef __FLT_MANT_DIG__ +#define FLT_MANT_DIG __FLT_MANT_DIG__ +#endif +#ifdef __DBL_MANT_DIG__ +#define DBL_MANT_DIG __DBL_MANT_DIG__ +#endif +#ifdef __LDBL_MANT_DIG__ +#define LDBL_MANT_DIG __LDBL_MANT_DIG__ +#endif + +#ifdef __FLT_DIG__ +#define FLT_DIG __FLT_DIG__ +#endif +#ifdef __DBL_DIG__ +#define DBL_DIG __DBL_DIG__ +#endif +#ifdef __LDBL_DIG__ +#define LDBL_DIG __LDBL_DIG__ +#endif + +#ifdef __FLT_MIN_EXP__ +#define FLT_MIN_EXP __FLT_MIN_EXP__ +#endif +#ifdef __DBL_MIN_EXP__ +#define DBL_MIN_EXP __DBL_MIN_EXP__ +#endif +#ifdef __LDBL_MIN_EXP__ +#define LDBL_MIN_EXP __LDBL_MIN_EXP__ +#endif + +#ifdef __FLT_MIN_10_EXP__ +#define FLT_MIN_10_EXP __FLT_MIN_10_EXP__ +#endif +#ifdef __DBL_MIN_10_EXP__ +#define DBL_MIN_10_EXP __DBL_MIN_10_EXP__ +#endif +#ifdef __LDBL_MIN_10_EXP__ +#define LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__ +#endif + +#ifdef __FLT_MAX_EXP__ +#define FLT_MAX_EXP __FLT_MAX_EXP__ +#endif +#ifdef __DBL_MAX_EXP__ +#define DBL_MAX_EXP __DBL_MAX_EXP__ +#endif +#ifdef __LDBL_MAX_EXP__ +#define LDBL_MAX_EXP __LDBL_MAX_EXP__ +#endif + +#ifdef __FLT_MAX_10_EXP__ +#define FLT_MAX_10_EXP __FLT_MAX_10_EXP__ +#endif +#ifdef __DBL_MAX_10_EXP__ +#define DBL_MAX_10_EXP __DBL_MAX_10_EXP__ +#endif +#ifdef __LDBL_MAX_10_EXP__ +#define LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__ +#endif + +#ifdef __FLT_MAX__ +#define FLT_MAX __FLT_MAX__ +#endif +#ifdef __DBL_MAX__ +#define DBL_MAX __DBL_MAX__ +#endif +#ifdef __LDBL_MAX__ +#define LDBL_MAX __LDBL_MAX__ +#endif + +#ifdef __FLT_EPSILON__ +#define FLT_EPSILON __FLT_EPSILON__ +#endif +#ifdef __DBL_EPSILON__ +#define DBL_EPSILON __DBL_EPSILON__ +#endif +#ifdef __LDBL_EPSILON__ +#define LDBL_EPSILON __LDBL_EPSILON__ +#endif + +#ifdef __FLT_MIN__ +#define FLT_MIN __FLT_MIN__ +#endif +#ifdef __DBL_MIN__ +#define DBL_MIN __DBL_MIN__ +#endif +#ifdef __LDBL_MIN__ +#define LDBL_MIN __LDBL_MIN__ +#endif + +#ifdef __FLT_EVAL_METHOD__ +#define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ +#endif + +#ifdef __DECIMAL_DIG__ +#define DECIMAL_DIG __DECIMAL_DIG__ +#endif diff --git a/common/include/klibc/inttypes.h b/common/include/klibc/inttypes.h new file mode 100644 index 000000000..9572f6130 --- /dev/null +++ b/common/include/klibc/inttypes.h @@ -0,0 +1,42 @@ +#pragma once + +# if __INT_WIDTH__ == 32 + #ifndef __PRI_32_PREFIX + #define __PRI_32_PREFIX "" + #endif +#endif + +#if __LONG_WIDTH__ == 32 + #ifndef __PRI_32_PREFIX + #define __PRI_32_PREFIX "l" + #endif +#elif __LONG_WIDTH__ == 64 + #ifndef __PRI_64_PREFIX + #define __PRI_64_PREFIX "l" + #endif +#else + #error "unexpected sizeof long" +#endif + +// g++ defines __LONG_LONG_WIDTH__, clang defines __LLONG_WIDTH__ +#if __LONG_LONG_WIDTH__ == 64 || __LLONG_WIDTH__ == 64 + #ifndef __PRI_64_PREFIX + #define __PRI_64_PREFIX "ll" + #endif +#else + #error "unexpected sizeof long long" +#endif + +#if !defined(__PRI_32_PREFIX) || !defined(__PRI_64_PREFIX) + #error undefined integer format prefix +#endif + +#define PRIx32 __PRI_32_PREFIX "x" +#define PRId32 __PRI_32_PREFIX "d" +#define PRIi32 __PRI_32_PREFIX "i" +#define PRIu32 __PRI_32_PREFIX "u" + +#define PRIx64 __PRI_64_PREFIX "x" +#define PRId64 __PRI_64_PREFIX "d" +#define PRIi64 __PRI_64_PREFIX "i" +#define PRIu64 __PRI_64_PREFIX "u" diff --git a/common/include/klibc/limits.h b/common/include/klibc/limits.h new file mode 100644 index 000000000..ad1ff3799 --- /dev/null +++ b/common/include/klibc/limits.h @@ -0,0 +1,136 @@ +#pragma once + +/* Copyright (C) 1991-2021 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* + * ISO C99 Standard: 7.10/5.2.4.2.1 Sizes of integer types + */ + +#ifndef _LIBC_LIMITS_H_ +#define _LIBC_LIMITS_H_ 1 + + +/* Maximum length of any multibyte character in any locale. + We define this value here since the gcc header does not define + the correct value. */ +#define MB_LEN_MAX 16 + + +/* These assume 8-bit `char's, 16-bit `short int's, + and 32-bit `int's and `long int's. */ + +/* Number of bits in a `char'. */ +# define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold. */ +# define SCHAR_MIN (-128) +# define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +# define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold. */ +# ifdef __CHAR_UNSIGNED__ +# define CHAR_MIN 0 +# define CHAR_MAX UCHAR_MAX +# else +# define CHAR_MIN SCHAR_MIN +# define CHAR_MAX SCHAR_MAX +# endif + +/* Minimum and maximum values a `signed short int' can hold. */ +# define SHRT_MIN (-32768) +# define SHRT_MAX 32767 + +/* Maximum value an `unsigned short int' can hold. (Minimum is 0.) */ +# define USHRT_MAX 65535 + +/* Minimum and maximum values a `signed int' can hold. */ +# define INT_MIN (-INT_MAX - 1) +# define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +# define UINT_MAX 4294967295U + +/* Minimum and maximum values a `signed long int' can hold. */ +# if __WORDSIZE == 64 +# define LONG_MAX 9223372036854775807L +# else +# define LONG_MAX 2147483647L +# endif +# define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long int' can hold. (Minimum is 0.) */ +# if __WORDSIZE == 64 +# define ULONG_MAX 18446744073709551615UL +# else +# define ULONG_MAX 4294967295UL +# endif + + +/* The files in some gcc versions don't define LLONG_MIN, + LLONG_MAX, and ULLONG_MAX. Instead only the values gcc defined for + ages are available. */ +# ifndef LLONG_MIN +# define LLONG_MIN (-LLONG_MAX-1) +# endif +# ifndef LLONG_MAX +# define LLONG_MAX __LONG_LONG_MAX__ +# endif +# ifndef ULLONG_MAX +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1) +# endif + +/* The integer width macros are not defined by GCC's before + GCC 7, or if _GNU_SOURCE rather than + __STDC_WANT_IEC_60559_BFP_EXT__ is used to enable this feature. */ +# ifndef CHAR_WIDTH +# define CHAR_WIDTH 8 +# endif +# ifndef SCHAR_WIDTH +# define SCHAR_WIDTH 8 +# endif +# ifndef UCHAR_WIDTH +# define UCHAR_WIDTH 8 +# endif +# ifndef SHRT_WIDTH +# define SHRT_WIDTH 16 +# endif +# ifndef USHRT_WIDTH +# define USHRT_WIDTH 16 +# endif +# ifndef INT_WIDTH +# define INT_WIDTH 32 +# endif +# ifndef UINT_WIDTH +# define UINT_WIDTH 32 +# endif +# ifndef LONG_WIDTH +# define LONG_WIDTH __WORDSIZE +# endif +# ifndef ULONG_WIDTH +# define ULONG_WIDTH __WORDSIZE +# endif +# ifndef LLONG_WIDTH +# define LLONG_WIDTH 64 +# endif +# ifndef ULLONG_WIDTH +# define ULLONG_WIDTH 64 +# endif + +#endif diff --git a/common/include/klibc/math.h b/common/include/klibc/math.h new file mode 100644 index 000000000..f8e3427f7 --- /dev/null +++ b/common/include/klibc/math.h @@ -0,0 +1,70 @@ +#pragma once + +#if defined(__FLT_EVAL_METHOD__) + #if(__FLT_EVAL_METHOD__== 0) + typedef float float_t; + typedef double double_t; + #elif(__FLT_EVAL_METHOD__ == 1) + typedef double float_t; + typedef double double_t; + #elif(__FLT_EVAL_METHOD__ == 2) + typedef long double float_t; + typedef long double double_t; + #endif +#else + typedef float float_t; + typedef double double_t; +#endif + +/* Value returned on overflow. With IEEE 754 floating point, this is + +Infinity, otherwise the largest representable positive value. */ +#define HUGE_VAL (__builtin_huge_val ()) +/* This may provoke compiler warnings, and may not be rounded to + +Infinity in all IEEE 754 rounding modes, but is the best that can + be done in ISO C while remaining a constant expression. 10,000 is + greater than the maximum (decimal) exponent for all supported + floating-point formats and widths. */ + +#define HUGE_VALF (__builtin_huge_valf ()) +#define HUGE_VALL (__builtin_huge_vall ()) + +#define HUGE_VAL_F16 (__builtin_huge_valf16 ()) +#define HUGE_VAL_F32 (__builtin_huge_valf32 ()) +#define HUGE_VAL_F64 (__builtin_huge_valf64 ()) +#define HUGE_VAL_F128 (__builtin_huge_valf128 ()) +#define HUGE_VAL_F32X (__builtin_huge_valf32x ()) +#define HUGE_VAL_F64X (__builtin_huge_valf64x ()) +#define HUGE_VAL_F128X (__builtin_huge_valf128x ()) + +#define INFINITY (__builtin_inff ()) + +#define NAN (__builtin_nanf ("")) + +#define SNANF (__builtin_nansf ("")) +#define SNAN (__builtin_nans ("")) +#define SNANL (__builtin_nansl ("")) +#define SNANF16 (__builtin_nansf16 ("")) +#define SNANF32 (__builtin_nansf32 ("")) +#define SNANF64 (__builtin_nansf64 ("")) +#define SNANF128 (__builtin_nansf128 ("")) +#define SNANF32X (__builtin_nansf32x ("")) +#define SNANF64X (__builtin_nansf64x ("")) +#define SNANF128X (__builtin_nansf128x ("")) + +#define M_E 2.7182818284590452354 /* e */ +#define M_LOG2E 1.4426950408889634074 /* log_2 e */ +#define M_LOG10E 0.43429448190325182765 /* log_10 e */ +#define M_LN2 0.69314718055994530942 /* log_e 2 */ +#define M_LN10 2.30258509299404568402 /* log_e 10 */ +#define M_PI 3.14159265358979323846 /* pi */ +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#define M_PI_4 0.78539816339744830962 /* pi/4 */ +#define M_1_PI 0.31830988618379067154 /* 1/pi */ +#define M_2_PI 0.63661977236758134308 /* 2/pi */ +#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ + +/* float ceilf( float arg ); */ +/* double ceil( double arg ); */ +/* long double ceill( long double arg ); */ diff --git a/common/include/klibc/mutex b/common/include/klibc/mutex new file mode 100644 index 000000000..f12ab8b34 --- /dev/null +++ b/common/include/klibc/mutex @@ -0,0 +1,24 @@ +#pragma once + +#include "Mutex.h" + +// Forward std::mutex to standard sweb mutex. Required by EASTL. +namespace std +{ + class mutex : public Mutex + { + public: + mutex() : Mutex("eastl::mutex") {} + + void lock() { acquire(); } + void unlock() { release(); } + bool try_lock() { return acquireNonBlocking(); } + + private: + }; +} + +namespace eastl +{ + using mutex = std::mutex; +} diff --git a/common/include/klibc/new b/common/include/klibc/new new file mode 100644 index 000000000..c7b9074fa --- /dev/null +++ b/common/include/klibc/new @@ -0,0 +1,240 @@ +#pragma once + +// The -*- C++ -*- dynamic memory management header. + +// Copyright (C) 1994-2021 Free Software Foundation, Inc. + +// This file is part of GCC. +// +// GCC is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3, or (at your option) +// any later version. +// +// GCC is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file new + * This is a Standard C++ Library header. + * + * The header @c new defines several functions to manage dynamic memory and + * handling memory allocation errors; see + * https://gcc.gnu.org/onlinedocs/libstdc++/manual/dynamic_memory.html + * for more. + */ + +#ifndef _NEW +#define _NEW + +#include +#include +#include "assert.h" + +#if defined(__has_attribute) + #if __has_attribute(__externally_visible__) + #define __EXTERNALLY_VISIBLE_ATTR__ __externally_visible__ + #else + #define __EXTERNALLY_VISIBLE_ATTR__ + #endif +#else + #define __EXTERNALLY_VISIBLE_ATTR__ +#endif + +extern "C++" { + +namespace std +{ + /** + * @brief Exception possibly thrown by @c new. + * @ingroup exceptions + * + * @c bad_alloc (or classes derived from it) is used to report allocation + * errors from the throwing forms of @c new. */ + class bad_alloc : public exception + { + public: + bad_alloc() throw() = default; + +#if __cplusplus >= 201103L + bad_alloc(const bad_alloc&) = default; + bad_alloc& operator=(const bad_alloc&) = default; +#endif + + // This declaration is not useless: + // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118 + ~bad_alloc() throw() override; + + // See comment in eh_exception.cc. + const char* what() const throw() override; + }; + +#if __cplusplus >= 201103L + class bad_array_new_length : public bad_alloc + { + public: + bad_array_new_length() throw() { } + + // This declaration is not useless: + // http://gcc.gnu.org/onlinedocs/gcc-3.0.2/gcc_6.html#SEC118 + ~bad_array_new_length() throw() override; + + // See comment in eh_exception.cc. + const char* what() const throw() override; + }; +#endif + +#if __cpp_aligned_new + enum class align_val_t: size_t {}; +#endif + + struct nothrow_t + { +#if __cplusplus >= 201103L + explicit nothrow_t() = default; +#endif + }; + + extern const nothrow_t nothrow; + + /** If you write your own error handler to be called by @c new, it must + * be of this type. */ + typedef void (*new_handler)(); + + /// Takes a replacement handler as the argument, returns the + /// previous handler. + new_handler set_new_handler(new_handler) throw(); + +#if __cplusplus >= 201103L + /// Return the current new handler. + new_handler get_new_handler() noexcept; +#endif +} // namespace std + +//@{ +/** These are replaceable signatures: + * - normal single new and delete (no arguments, throw @c bad_alloc on error) + * - normal array new and delete (same) + * - @c nothrow single new and delete (take a @c nothrow argument, return + * @c NULL on error) + * - @c nothrow array new and delete (same) + * + * Placement new and delete signatures (take a memory address argument, + * does nothing) may not be replaced by a user's program. +*/ +[[nodiscard]] void* operator new(std::size_t) + __attribute__((__EXTERNALLY_VISIBLE_ATTR__, __alloc_size__ (1), __malloc__)); +[[nodiscard]] void* operator new[](std::size_t) + __attribute__((__EXTERNALLY_VISIBLE_ATTR__, __alloc_size__ (1), __malloc__)); +void operator delete(void*) noexcept + __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); +void operator delete[](void*) noexcept + __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); + +#if __cpp_sized_deallocation +void operator delete(void*, std::size_t) noexcept + __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); +void operator delete[](void*, std::size_t) noexcept + __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); +#endif + +[[nodiscard]] void* operator new(std::size_t, const std::nothrow_t&) noexcept + __attribute__((__EXTERNALLY_VISIBLE_ATTR__, __alloc_size__ (1), __malloc__)); +[[nodiscard]] void* operator new[](std::size_t, const std::nothrow_t&) noexcept + __attribute__((__EXTERNALLY_VISIBLE_ATTR__, __alloc_size__ (1), __malloc__)); +void operator delete(void*, const std::nothrow_t&) noexcept + __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); +void operator delete[](void*, const std::nothrow_t&) noexcept + __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); + +#if __cpp_aligned_new +[[nodiscard]] void* operator new(std::size_t, std::align_val_t) + __attribute__((__EXTERNALLY_VISIBLE_ATTR__, __alloc_size__ (1), __malloc__)); +[[nodiscard]] void* operator new(std::size_t, std::align_val_t, const std::nothrow_t&) + noexcept __attribute__((__EXTERNALLY_VISIBLE_ATTR__, __alloc_size__ (1), __malloc__)); +void operator delete(void*, std::align_val_t) + noexcept __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); +void operator delete(void*, std::align_val_t, const std::nothrow_t&) + noexcept __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); +[[nodiscard]] void* operator new[](std::size_t, std::align_val_t) + __attribute__((__EXTERNALLY_VISIBLE_ATTR__, __alloc_size__ (1), __malloc__)); +[[nodiscard]] void* operator new[](std::size_t, std::align_val_t, const std::nothrow_t&) + noexcept __attribute__((__EXTERNALLY_VISIBLE_ATTR__, __alloc_size__ (1), __malloc__)); +void operator delete[](void*, std::align_val_t) + noexcept __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); +void operator delete[](void*, std::align_val_t, const std::nothrow_t&) + noexcept __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); + +#if __cpp_sized_deallocation +void operator delete(void*, std::size_t, std::align_val_t) + noexcept __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); +void operator delete[](void*, std::size_t, std::align_val_t) + noexcept __attribute__((__EXTERNALLY_VISIBLE_ATTR__)); +#endif // __cpp_sized_deallocation +#endif // __cpp_aligned_new + +// Default placement versions of operator new. +[[nodiscard]] [[gnu::nonnull]] inline void* operator new(std::size_t, void* __p) noexcept +{ assert(__p); return __p; } +[[nodiscard]] [[gnu::nonnull]] inline void* operator new[](std::size_t, void* __p) noexcept +{ assert(__p); return __p; } + +// Default placement versions of operator delete. +inline void operator delete (void*, void*) noexcept { } +inline void operator delete[](void*, void*) noexcept { } +//@} +} // extern "C++" + +#if __cplusplus >= 201703L +namespace std +{ + /// Pointer optimization barrier [ptr.launder] + template + [[nodiscard]] constexpr _Tp* + launder(_Tp* __p) noexcept + { return __builtin_launder(__p); } + + // The program is ill-formed if T is a function type or + // (possibly cv-qualified) void. + + template + void launder(_Ret (*)(_Args...) noexcept) = delete; + template + void launder(_Ret (*)(_Args......) noexcept) = delete; + + void launder(void*) = delete; + void launder(const void*) = delete; + void launder(volatile void*) = delete; + void launder(const volatile void*) = delete; +} +#endif // C++17 + +#if __cplusplus > 201703L +namespace std +{ + /// Tag type used to declare a class-specific operator delete that can + /// invoke the destructor before deallocating the memory. + struct destroying_delete_t + { + explicit destroying_delete_t() = default; + }; + /// Tag variable of type destroying_delete_t. + inline constexpr destroying_delete_t destroying_delete{}; +} +// Only define the feature test macro if the compiler supports the feature: +#if __cpp_impl_destroying_delete +# define __cpp_lib_destroying_delete 201806L +#endif +#endif // C++20 + +#endif diff --git a/common/include/klibc/stdarg.h b/common/include/klibc/stdarg.h new file mode 100644 index 000000000..e04c26709 --- /dev/null +++ b/common/include/klibc/stdarg.h @@ -0,0 +1,122 @@ +/* Copyright (C) 1989-2022 Free Software Foundation, Inc. +This file is part of GCC. +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* + * ISO C Standard: 7.15 Variable arguments + */ + +#ifndef _STDARG_H +#ifndef _ANSI_STDARG_H_ +#ifndef __need___va_list +#define _STDARG_H +#define _ANSI_STDARG_H_ +#endif /* not __need___va_list */ +#undef __need___va_list + +/* Define __gnuc_va_list. */ + +#ifndef __GNUC_VA_LIST +#define __GNUC_VA_LIST +typedef __builtin_va_list __gnuc_va_list; +#endif + +/* Define the standard macros for the user, + if this invocation was from the user program. */ +#ifdef _STDARG_H + +#define va_start(v,l) __builtin_va_start(v,l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v,l) __builtin_va_arg(v,l) +#if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L \ + || __cplusplus + 0 >= 201103L +#define va_copy(d,s) __builtin_va_copy(d,s) +#endif +#define __va_copy(d,s) __builtin_va_copy(d,s) + +/* Define va_list, if desired, from __gnuc_va_list. */ +/* We deliberately do not define va_list when called from + stdio.h, because ANSI C says that stdio.h is not supposed to define + va_list. stdio.h needs to have access to that data type, + but must not use that name. It should use the name __gnuc_va_list, + which is safe because it is reserved for the implementation. */ + +#ifdef _BSD_VA_LIST +#undef _BSD_VA_LIST +#endif + +#if defined(__svr4__) || (defined(_SCO_DS) && !defined(__VA_LIST)) +/* SVR4.2 uses _VA_LIST for an internal alias for va_list, + so we must avoid testing it and setting it here. + SVR4 uses _VA_LIST as a flag in stdarg.h, but we should + have no conflict with that. */ +#ifndef _VA_LIST_ +#define _VA_LIST_ +#ifdef __i860__ +#ifndef _VA_LIST +#define _VA_LIST va_list +#endif +#endif /* __i860__ */ +typedef __gnuc_va_list va_list; +#ifdef _SCO_DS +#define __VA_LIST +#endif +#endif /* _VA_LIST_ */ +#else /* not __svr4__ || _SCO_DS */ + +/* The macro _VA_LIST_ is the same thing used by this file in Ultrix. + But on BSD NET2 we must not test or define or undef it. + (Note that the comments in NET 2's ansi.h + are incorrect for _VA_LIST_--see stdio.h!) */ +#if !defined (_VA_LIST_) || defined (__BSD_NET2__) || defined (____386BSD____) || defined (__bsdi__) || defined (__sequent__) || defined (__FreeBSD__) || defined(WINNT) +/* The macro _VA_LIST_DEFINED is used in Windows NT 3.5 */ +#ifndef _VA_LIST_DEFINED +/* The macro _VA_LIST is used in SCO Unix 3.2. */ +#ifndef _VA_LIST +/* The macro _VA_LIST_T_H is used in the Bull dpx2 */ +#ifndef _VA_LIST_T_H +/* The macro __va_list__ is used by BeOS. */ +#ifndef __va_list__ +typedef __gnuc_va_list va_list; +#endif /* not __va_list__ */ +#endif /* not _VA_LIST_T_H */ +#endif /* not _VA_LIST */ +#endif /* not _VA_LIST_DEFINED */ +#if !(defined (__BSD_NET2__) || defined (____386BSD____) || defined (__bsdi__) || defined (__sequent__) || defined (__FreeBSD__)) +#define _VA_LIST_ +#endif +#ifndef _VA_LIST +#define _VA_LIST +#endif +#ifndef _VA_LIST_DEFINED +#define _VA_LIST_DEFINED +#endif +#ifndef _VA_LIST_T_H +#define _VA_LIST_T_H +#endif +#ifndef __va_list__ +#define __va_list__ +#endif + +#endif /* not _VA_LIST_, except on certain systems */ + +#endif /* not __svr4__ */ + +#endif /* _STDARG_H */ + +#endif /* not _ANSI_STDARG_H_ */ +#endif /* not _STDARG_H */ diff --git a/common/include/klibc/stdbool.h b/common/include/klibc/stdbool.h new file mode 100644 index 000000000..94d6c140c --- /dev/null +++ b/common/include/klibc/stdbool.h @@ -0,0 +1,19 @@ +#pragma once + +#ifndef __cplusplus + +#define bool _Bool +#if defined __STDC_VERSION__ && __STDC_VERSION__ > 201710L +#define true ((_Bool)+1u) +#define false ((_Bool)+0u) +#else +#define true 1 +#define false 0 +#endif + +#else /* __cplusplus */ + +/* Supporting _Bool in C++ is a GCC extension. */ +#define _Bool bool + +#endif /* __cplusplus */ diff --git a/common/include/klibc/stddef.h b/common/include/klibc/stddef.h new file mode 100644 index 000000000..53c6b4966 --- /dev/null +++ b/common/include/klibc/stddef.h @@ -0,0 +1,51 @@ +#pragma once + +#ifdef __GNUG__ +#define NULL __null +#elif defined(__cplusplus) +#define NULL 0 +#else +#define NULL ((void*)0) +#endif + + + + +#define offsetof(type, member) __builtin_offsetof (type, member) + + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef __SIZE_TYPE__ size_t; + typedef __PTRDIFF_TYPE__ ptrdiff_t; + typedef ptrdiff_t ssize_t; // Technically not defined in this header +#ifndef __cplusplus + typedef __WCHAR_TYPE__ wchar_t; +#endif + +#ifdef __cplusplus + typedef decltype(nullptr) nullptr_t; +#endif +#if defined(__STDC__) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202300L) + typedef typeof(nullptr) nullptr_t; +#endif + + typedef struct { + long long __max_align_ll __attribute__((__aligned__(__alignof__(long long)))); + // long double __max_align_ld __attribute__((__aligned__(__alignof__(long double)))); + /* _Float128 is defined as a basic type, so max_align_t must be + sufficiently aligned for it. This code must work in C++, so we + use __float128 here; that is only available on some + architectures, but only on i386 is extra alignment needed for + __float128. */ +#ifdef __i386__ + __float128 __max_align_f128 __attribute__((__aligned__(__alignof(__float128)))); +#endif + } max_align_t; + +#ifdef __cplusplus +} +#endif diff --git a/common/include/klibc/stdint.h b/common/include/klibc/stdint.h new file mode 100644 index 000000000..4a7a6b26a --- /dev/null +++ b/common/include/klibc/stdint.h @@ -0,0 +1,380 @@ +#pragma once + +/* Copyright (C) 2008-2022 Free Software Foundation, Inc. +This file is part of GCC. +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +. */ + +/* + * ISO C Standard: 7.18 Integer types + */ + +#ifndef _GCC_STDINT_H +#define _GCC_STDINT_H + +/* 7.8.1.1 Exact-width integer types */ + +/* + Special casing for u/int32_t because on arm 32 bit __UINT32_TYPE__ is + defined as (unsigned) long int which is just plain silly. Make sure it's always defined + as (unsigned) int if possible +*/ + +#ifdef __INT8_TYPE__ + typedef __INT8_TYPE__ int8_t; +#endif +#ifdef __INT16_TYPE__ + typedef __INT16_TYPE__ int16_t; +#endif +#if __INT_WIDTH__ == 32 + typedef int int32_t; +#else + #ifdef __INT32_TYPE__ + typedef __INT32_TYPE__ int32_t; + #endif +#endif +#ifdef __INT64_TYPE__ + typedef __INT64_TYPE__ int64_t; +#endif +#ifdef __UINT8_TYPE__ + typedef __UINT8_TYPE__ uint8_t; +#endif +#ifdef __UINT16_TYPE__ + typedef __UINT16_TYPE__ uint16_t; +#endif +#if __INT_WIDTH__ == 32 + typedef unsigned int uint32_t; +#else + #ifdef __UINT32_TYPE__ + typedef __UINT32_TYPE__ uint32_t; + #endif +#endif +#ifdef __UINT64_TYPE__ + typedef __UINT64_TYPE__ uint64_t; +#endif + +/* 7.8.1.2 Minimum-width integer types */ + +typedef __INT_LEAST8_TYPE__ int_least8_t; +typedef __INT_LEAST16_TYPE__ int_least16_t; +typedef __INT_LEAST32_TYPE__ int_least32_t; +typedef __INT_LEAST64_TYPE__ int_least64_t; +typedef __UINT_LEAST8_TYPE__ uint_least8_t; +typedef __UINT_LEAST16_TYPE__ uint_least16_t; +typedef __UINT_LEAST32_TYPE__ uint_least32_t; +typedef __UINT_LEAST64_TYPE__ uint_least64_t; + +/* 7.8.1.3 Fastest minimum-width integer types */ + +typedef __INT_FAST8_TYPE__ int_fast8_t; +typedef __INT_FAST16_TYPE__ int_fast16_t; +typedef __INT_FAST32_TYPE__ int_fast32_t; +typedef __INT_FAST64_TYPE__ int_fast64_t; +typedef __UINT_FAST8_TYPE__ uint_fast8_t; +typedef __UINT_FAST16_TYPE__ uint_fast16_t; +typedef __UINT_FAST32_TYPE__ uint_fast32_t; +typedef __UINT_FAST64_TYPE__ uint_fast64_t; + +/* 7.8.1.4 Integer types capable of holding object pointers */ + +#ifdef __INTPTR_TYPE__ +typedef __INTPTR_TYPE__ intptr_t; +#endif +#ifdef __UINTPTR_TYPE__ +typedef __UINTPTR_TYPE__ uintptr_t; +#endif + +/* 7.8.1.5 Greatest-width integer types */ + +typedef __INTMAX_TYPE__ intmax_t; +typedef __UINTMAX_TYPE__ uintmax_t; + +#if (!defined __cplusplus || __cplusplus >= 201103L \ + || defined __STDC_LIMIT_MACROS) + +/* 7.18.2 Limits of specified-width integer types */ + +#ifdef __INT8_MAX__ +# undef INT8_MAX +# define INT8_MAX __INT8_MAX__ +# undef INT8_MIN +# define INT8_MIN (-INT8_MAX - 1) +#endif +#ifdef __UINT8_MAX__ +# undef UINT8_MAX +# define UINT8_MAX __UINT8_MAX__ +#endif +#ifdef __INT16_MAX__ +# undef INT16_MAX +# define INT16_MAX __INT16_MAX__ +# undef INT16_MIN +# define INT16_MIN (-INT16_MAX - 1) +#endif +#ifdef __UINT16_MAX__ +# undef UINT16_MAX +# define UINT16_MAX __UINT16_MAX__ +#endif +#ifdef __INT32_MAX__ +# undef INT32_MAX +# define INT32_MAX __INT32_MAX__ +# undef INT32_MIN +# define INT32_MIN (-INT32_MAX - 1) +#endif +#ifdef __UINT32_MAX__ +# undef UINT32_MAX +# define UINT32_MAX __UINT32_MAX__ +#endif +#ifdef __INT64_MAX__ +# undef INT64_MAX +# define INT64_MAX __INT64_MAX__ +# undef INT64_MIN +# define INT64_MIN (-INT64_MAX - 1) +#endif +#ifdef __UINT64_MAX__ +# undef UINT64_MAX +# define UINT64_MAX __UINT64_MAX__ +#endif + +#undef INT_LEAST8_MAX +#define INT_LEAST8_MAX __INT_LEAST8_MAX__ +#undef INT_LEAST8_MIN +#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1) +#undef UINT_LEAST8_MAX +#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__ +#undef INT_LEAST16_MAX +#define INT_LEAST16_MAX __INT_LEAST16_MAX__ +#undef INT_LEAST16_MIN +#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1) +#undef UINT_LEAST16_MAX +#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__ +#undef INT_LEAST32_MAX +#define INT_LEAST32_MAX __INT_LEAST32_MAX__ +#undef INT_LEAST32_MIN +#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1) +#undef UINT_LEAST32_MAX +#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__ +#undef INT_LEAST64_MAX +#define INT_LEAST64_MAX __INT_LEAST64_MAX__ +#undef INT_LEAST64_MIN +#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1) +#undef UINT_LEAST64_MAX +#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__ + +#undef INT_FAST8_MAX +#define INT_FAST8_MAX __INT_FAST8_MAX__ +#undef INT_FAST8_MIN +#define INT_FAST8_MIN (-INT_FAST8_MAX - 1) +#undef UINT_FAST8_MAX +#define UINT_FAST8_MAX __UINT_FAST8_MAX__ +#undef INT_FAST16_MAX +#define INT_FAST16_MAX __INT_FAST16_MAX__ +#undef INT_FAST16_MIN +#define INT_FAST16_MIN (-INT_FAST16_MAX - 1) +#undef UINT_FAST16_MAX +#define UINT_FAST16_MAX __UINT_FAST16_MAX__ +#undef INT_FAST32_MAX +#define INT_FAST32_MAX __INT_FAST32_MAX__ +#undef INT_FAST32_MIN +#define INT_FAST32_MIN (-INT_FAST32_MAX - 1) +#undef UINT_FAST32_MAX +#define UINT_FAST32_MAX __UINT_FAST32_MAX__ +#undef INT_FAST64_MAX +#define INT_FAST64_MAX __INT_FAST64_MAX__ +#undef INT_FAST64_MIN +#define INT_FAST64_MIN (-INT_FAST64_MAX - 1) +#undef UINT_FAST64_MAX +#define UINT_FAST64_MAX __UINT_FAST64_MAX__ + +#ifdef __INTPTR_MAX__ +# undef INTPTR_MAX +# define INTPTR_MAX __INTPTR_MAX__ +# undef INTPTR_MIN +# define INTPTR_MIN (-INTPTR_MAX - 1) +#endif +#ifdef __UINTPTR_MAX__ +# undef UINTPTR_MAX +# define UINTPTR_MAX __UINTPTR_MAX__ +#endif + +#undef INTMAX_MAX +#define INTMAX_MAX __INTMAX_MAX__ +#undef INTMAX_MIN +#define INTMAX_MIN (-INTMAX_MAX - 1) +#undef UINTMAX_MAX +#define UINTMAX_MAX __UINTMAX_MAX__ + +/* 7.18.3 Limits of other integer types */ + +#undef PTRDIFF_MAX +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#undef PTRDIFF_MIN +#define PTRDIFF_MIN (-PTRDIFF_MAX - 1) + +#undef SIG_ATOMIC_MAX +#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__ +#undef SIG_ATOMIC_MIN +#define SIG_ATOMIC_MIN __SIG_ATOMIC_MIN__ + +#undef SIZE_MAX +#define SIZE_MAX __SIZE_MAX__ + +#undef WCHAR_MAX +#define WCHAR_MAX __WCHAR_MAX__ +#undef WCHAR_MIN +#define WCHAR_MIN __WCHAR_MIN__ + +#undef WINT_MAX +#define WINT_MAX __WINT_MAX__ +#undef WINT_MIN +#define WINT_MIN __WINT_MIN__ + +#endif /* (!defined __cplusplus || __cplusplus >= 201103L + || defined __STDC_LIMIT_MACROS) */ + +#if (!defined __cplusplus || __cplusplus >= 201103L \ + || defined __STDC_CONSTANT_MACROS) + +#ifndef __UINT64_C + #define __UINT64_C(c) c ## UL +#endif + +#undef INT8_C +#define INT8_C(c) __INT8_C(c) +#undef INT16_C +#define INT16_C(c) __INT16_C(c) +#undef INT32_C +#define INT32_C(c) __INT32_C(c) +#undef INT64_C +#define INT64_C(c) __INT64_C(c) +#undef UINT8_C +#define UINT8_C(c) __UINT8_C(c) +#undef UINT16_C +#define UINT16_C(c) __UINT16_C(c) +#undef UINT32_C +#define UINT32_C(c) __UINT32_C(c) +#undef UINT64_C +#define UINT64_C(c) __UINT64_C(c) +#undef INTMAX_C +#define INTMAX_C(c) __INTMAX_C(c) +#undef UINTMAX_C +#define UINTMAX_C(c) __UINTMAX_C(c) + +#endif /* (!defined __cplusplus || __cplusplus >= 201103L + || defined __STDC_CONSTANT_MACROS) */ + +#if (defined __STDC_WANT_IEC_60559_BFP_EXT__ \ + || (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L)) +/* TS 18661-1 / C2X widths of integer types. */ + +#ifdef __INT8_TYPE__ +# undef INT8_WIDTH +# define INT8_WIDTH 8 +#endif +#ifdef __UINT8_TYPE__ +# undef UINT8_WIDTH +# define UINT8_WIDTH 8 +#endif +#ifdef __INT16_TYPE__ +# undef INT16_WIDTH +# define INT16_WIDTH 16 +#endif +#ifdef __UINT16_TYPE__ +# undef UINT16_WIDTH +# define UINT16_WIDTH 16 +#endif +#ifdef __INT32_TYPE__ +# undef INT32_WIDTH +# define INT32_WIDTH 32 +#endif +#ifdef __UINT32_TYPE__ +# undef UINT32_WIDTH +# define UINT32_WIDTH 32 +#endif +#ifdef __INT64_TYPE__ +# undef INT64_WIDTH +# define INT64_WIDTH 64 +#endif +#ifdef __UINT64_TYPE__ +# undef UINT64_WIDTH +# define UINT64_WIDTH 64 +#endif + +#undef INT_LEAST8_WIDTH +#define INT_LEAST8_WIDTH __INT_LEAST8_WIDTH__ +#undef UINT_LEAST8_WIDTH +#define UINT_LEAST8_WIDTH __INT_LEAST8_WIDTH__ +#undef INT_LEAST16_WIDTH +#define INT_LEAST16_WIDTH __INT_LEAST16_WIDTH__ +#undef UINT_LEAST16_WIDTH +#define UINT_LEAST16_WIDTH __INT_LEAST16_WIDTH__ +#undef INT_LEAST32_WIDTH +#define INT_LEAST32_WIDTH __INT_LEAST32_WIDTH__ +#undef UINT_LEAST32_WIDTH +#define UINT_LEAST32_WIDTH __INT_LEAST32_WIDTH__ +#undef INT_LEAST64_WIDTH +#define INT_LEAST64_WIDTH __INT_LEAST64_WIDTH__ +#undef UINT_LEAST64_WIDTH +#define UINT_LEAST64_WIDTH __INT_LEAST64_WIDTH__ + +#undef INT_FAST8_WIDTH +#define INT_FAST8_WIDTH __INT_FAST8_WIDTH__ +#undef UINT_FAST8_WIDTH +#define UINT_FAST8_WIDTH __INT_FAST8_WIDTH__ +#undef INT_FAST16_WIDTH +#define INT_FAST16_WIDTH __INT_FAST16_WIDTH__ +#undef UINT_FAST16_WIDTH +#define UINT_FAST16_WIDTH __INT_FAST16_WIDTH__ +#undef INT_FAST32_WIDTH +#define INT_FAST32_WIDTH __INT_FAST32_WIDTH__ +#undef UINT_FAST32_WIDTH +#define UINT_FAST32_WIDTH __INT_FAST32_WIDTH__ +#undef INT_FAST64_WIDTH +#define INT_FAST64_WIDTH __INT_FAST64_WIDTH__ +#undef UINT_FAST64_WIDTH +#define UINT_FAST64_WIDTH __INT_FAST64_WIDTH__ + +#ifdef __INTPTR_TYPE__ +# undef INTPTR_WIDTH +# define INTPTR_WIDTH __INTPTR_WIDTH__ +#endif +#ifdef __UINTPTR_TYPE__ +# undef UINTPTR_WIDTH +# define UINTPTR_WIDTH __INTPTR_WIDTH__ +#endif + +#undef INTMAX_WIDTH +#define INTMAX_WIDTH __INTMAX_WIDTH__ +#undef UINTMAX_WIDTH +#define UINTMAX_WIDTH __INTMAX_WIDTH__ + +#undef PTRDIFF_WIDTH +#define PTRDIFF_WIDTH __PTRDIFF_WIDTH__ + +#undef SIG_ATOMIC_WIDTH +#define SIG_ATOMIC_WIDTH __SIG_ATOMIC_WIDTH__ + +#undef SIZE_WIDTH +#define SIZE_WIDTH __SIZE_WIDTH__ + +#undef WCHAR_WIDTH +#define WCHAR_WIDTH __WCHAR_WIDTH__ + +#undef WINT_WIDTH +#define WINT_WIDTH __WINT_WIDTH__ + +#endif + +#endif /* _GCC_STDINT_H */ diff --git a/common/include/klibc/stdio.h b/common/include/klibc/stdio.h new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/common/include/klibc/stdio.h @@ -0,0 +1 @@ +#pragma once diff --git a/common/include/klibc/stdlib.h b/common/include/klibc/stdlib.h new file mode 100644 index 000000000..d87f59a74 --- /dev/null +++ b/common/include/klibc/stdlib.h @@ -0,0 +1,3 @@ +#pragma once + +#include "stddef.h" diff --git a/common/include/klibc/string.h b/common/include/klibc/string.h new file mode 100644 index 000000000..9e426dfd3 --- /dev/null +++ b/common/include/klibc/string.h @@ -0,0 +1,48 @@ +#pragma once + +#include "stddef.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + char *strcpy(char* dest, const char* src); + char *strncpy(char* dest, const char* src, size_t size); + char *strcat(char* dest, const char* append); + char *strncat(char* dest, const char* append, size_t size); + // size_t strxfrm( char* dest, const char* src, size_t count ); + size_t strlen(const char* str); + int strcmp(const char* str1, const char* str2); + int strncmp(const char* str1, const char* str2, size_t n); + // int strcoll( const char* lhs, const char* rhs ); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" + char* strchr(const char* str, char c); + char* strrchr(const char* str, char c); +#pragma GCC diagnostic pop + // size_t strspn( const char* dest, const char* src ); + // size_t strcspn( const char *dest, const char *src ); + // const char* strpbrk( const char* dest, const char* breakset ); + // const char* strstr( const char* haystack, const char* needle ); + char* strtok(char* str, const char* delimiters); + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" + void* memchr(const void* block, char c, size_t size); +#pragma GCC diagnostic pop + + int memcmp(const void* region1, const void* region2, size_t size); + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" + void* memset(void* block, char c, size_t size); +#pragma GCC diagnostic pop + + void* memcpy(void* dest, const void* src, size_t length); + void* memmove(void* dest, const void* src, size_t length); + + +#ifdef __cplusplus +} +#endif diff --git a/common/include/klibc/sys/types.h b/common/include/klibc/sys/types.h new file mode 100644 index 000000000..128cc25f8 --- /dev/null +++ b/common/include/klibc/sys/types.h @@ -0,0 +1,6 @@ +#pragma once + +#include "stddef.h" + +typedef ptrdiff_t ssize_t; +typedef ssize_t off_t; diff --git a/common/include/klibc/tuple b/common/include/klibc/tuple new file mode 100644 index 000000000..809cbcef5 --- /dev/null +++ b/common/include/klibc/tuple @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace std +{ + +template +struct tuple_size; + +#if __cplusplus > 201402L +template +inline constexpr size_t tuple_size_v = tuple_size<_Tp>::value; +#endif + +template +struct tuple_element; + +/** + * Error case for tuple_element: invalid index. + */ + template + struct tuple_element<__i, Tp> + { + static_assert(__i < tuple_size::value, + "tuple index must be in range"); + }; + +} diff --git a/common/include/ustl/ustringformat.h b/common/include/klibc/vsnprintf.h similarity index 64% rename from common/include/ustl/ustringformat.h rename to common/include/klibc/vsnprintf.h index c2b7f66f9..23a1d45fb 100644 --- a/common/include/ustl/ustringformat.h +++ b/common/include/klibc/vsnprintf.h @@ -1,36 +1,35 @@ #pragma once -#include "utypes.h" #include "stdarg.h" -#include "kstring.h" +#include "stddef.h" #define MAXNBUF 40 #define hex2ascii(hex) (hex2ascii_data[hex]) #define hex2asciiupper(hex) (hex2ascii_data_upper[hex]) -char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz"; -char const hex2ascii_data_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const char hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +const char hex2ascii_data_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned long u_long; typedef unsigned int u_int; -/*typedef unsigned int size_t;*/ struct snprintf_arg { char *str; size_t remain; }; -extern int +extern "C" int vsnprintf(char *str, size_t size, const char *format, va_list ap); extern void snprintf_func(int ch, void *arg); extern int -kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_list ap); +kvprintf(const char* fmt, void (*func)(int, void*), void* arg, int radix, va_list ap); extern char * ksprintn(char* nbuf, u_long ul, int base, int* lenp, int upper); +int snprintf(char* buf, size_t bufsz, const char* format, ...) __attribute__((format(printf, 3, 4))); diff --git a/common/include/klibc/wchar.h b/common/include/klibc/wchar.h new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/common/include/klibc/wchar.h @@ -0,0 +1 @@ +#pragma once diff --git a/common/include/mm/KernelMemoryManager.h b/common/include/mm/KernelMemoryManager.h index 420b0a3d8..80adf4d85 100644 --- a/common/include/mm/KernelMemoryManager.h +++ b/common/include/mm/KernelMemoryManager.h @@ -1,7 +1,11 @@ #pragma once -#include "new.h" +#include "Mutex.h" #include "SpinLock.h" +#include "new.h" + +#include + #include "assert.h" class MallocSegment @@ -22,13 +26,12 @@ class MallocSegment freed_at_(0), alloc_at_(0) { - size_flag_ = (size & 0x7FFFFFFF); //size to max 2^31-1 - if (used) - size_flag_ |= 0x80000000; //this is the used flag - + assert(size <= 0x7FFFFFFF); + setSize(size); + setUsed(used); } - size_t getSize() + [[nodiscard]] size_t getSize() const { return (size_flag_ & 0x7FFFFFFF); } @@ -39,7 +42,7 @@ class MallocSegment size_flag_ |= (size & 0x7FFFFFFF); } - bool getUsed() + [[nodiscard]] bool getUsed() const { return (size_flag_ & 0x80000000); } @@ -51,7 +54,8 @@ class MallocSegment size_flag_ |= 0x80000000; //this is the used flag } - bool markerOk() + bool checkCanary(); + [[nodiscard]] bool markerOk() const { return marker_ == (0xdeadbeef00000000ull | (uint32) (size_t) this); } @@ -68,13 +72,17 @@ class MallocSegment size_t size_flag_; // = 0; //max size is 2^31-1 }; -extern void* kernel_end_address; - class KernelMemoryManager { public: + KernelMemoryManager() = delete; + KernelMemoryManager(const KernelMemoryManager&) = delete; + KernelMemoryManager& operator=(const KernelMemoryManager&) = delete; + static KernelMemoryManager *instance(); + static bool isReady(); + /** * allocateMemory is called by new * searches the MallocSegment-List for a free segment with size >= requested_size @@ -103,16 +111,13 @@ class KernelMemoryManager */ pointer reallocateMemory(pointer virtual_address, size_t new_size, pointer called_by); - SpinLock& getKMMLock(); + Mutex& getKMMLock(); - Thread* KMMLockHeldBy(); - - KernelMemoryManager() : lock_("") - { - assert(false && "dummy constructor - do not use!"); - }; + [[nodiscard]] Thread* KMMLockHeldBy() const; - pointer getKernelBreak() const; + [[nodiscard]] pointer getKernelBreak() const; + [[nodiscard]] pointer getKernelHeapStart() const; + [[nodiscard]] pointer getKernelHeapMaxEnd() const; size_t getUsedKernelMemory(bool show_allocs); void startTracing(); void stopTracing(); @@ -122,10 +127,12 @@ class KernelMemoryManager KernelMemoryManager(size_t min_heap_pages, size_t max_heap_pages); - static size_t pm_ready_; + static bool kmm_ready_; static KernelMemoryManager *instance_; + static void init(); + private: MallocSegment *findFreeSegment(size_t requested_size); @@ -137,20 +144,24 @@ class KernelMemoryManager */ void fillSegment(MallocSegment *this_one, size_t size, uint32 zero_check = 1); - void freeSegment(MallocSegment *this_one); - MallocSegment *getSegmentFromAddress(pointer virtual_address); - bool mergeWithFollowingFreeSegment(MallocSegment *this_one); + void freeSegment(MallocSegment *this_one, pointer called_by); + [[nodiscard]] MallocSegment *getSegmentFromAddress(pointer virtual_address) const; + + MallocSegment* mergeSegments(MallocSegment* s1, MallocSegment* s2); /** - * This really implements the allocateMemory behaviour, but + * This really implements the allocateMemory behaviour, but * does not lock the KMM, so we can also use it within the * reallocate method */ inline pointer private_AllocateMemory(size_t requested_size, pointer called_by); + static size_t calcNumHeapPages(); + static size_t mapKernelHeap(size_t max_heap_pages); + - pointer ksbrk(ssize_t size); + pointer ksbrk(ssize_t size); MallocSegment* first_; //first_ must _never_ be NULL MallocSegment* last_; @@ -163,9 +174,8 @@ class KernelMemoryManager void lockKMM(); void unlockKMM(); - SpinLock lock_; + Mutex lock_; uint32 segments_used_; uint32 segments_free_; - size_t approx_memory_free_; }; diff --git a/common/include/mm/PageFaultHandler.h b/common/include/mm/PageFaultHandler.h index dfcd940b8..16d3a70a0 100644 --- a/common/include/mm/PageFaultHandler.h +++ b/common/include/mm/PageFaultHandler.h @@ -5,12 +5,6 @@ class PageFaultHandler { private: - /** - * The border address at which it is assumed that - * a pagefault happened by dereferencing a null pointer. - */ - static const size_t null_reference_check_border_; - /** * Print out the pagefault information. Check if the pagefault is valid, or the thread state is corrupt. * Afterwards, load a the if necessary. @@ -35,6 +29,18 @@ class PageFaultHandler bool present, bool writing, bool fetch, bool switch_to_us); + static void countPageFault(size_t address); + + /** + * The border address at which it is assumed that + * a pagefault happened by dereferencing a null pointer. + */ + static const size_t null_reference_check_border_; + + static size_t pf_address; + static size_t pf_address_counter; + + public: /** * Enter a new pagefault. The pagefault is processed. @@ -45,7 +51,6 @@ class PageFaultHandler * @param writing true if the fault happened by writing to an address, else reading * @param fetch true in case the fault happened by an instruction fetch, else by an operand fetch */ - static void enterPageFault(size_t address, bool user, - bool present, bool writing, - bool fetch); + static void enterPageFault(size_t address, size_t ip, bool user, + bool present, bool writing, bool fetch); }; diff --git a/common/include/mm/PageManager.h b/common/include/mm/PageManager.h index 2ae9ba42d..d38c0e98f 100644 --- a/common/include/mm/PageManager.h +++ b/common/include/mm/PageManager.h @@ -1,41 +1,56 @@ #pragma once -#include "types.h" -#include "paging-definitions.h" +#include "Allocator.h" +#include "Bitmap.h" +#include "BootstrapRangeAllocator.h" #include "SpinLock.h" +#include "paging-definitions.h" -#define DYNAMIC_KMM (0) // Please note that this means that the KMM depends on the page manager -// and you will have a harder time implementing swapping. Pros only! +#include "types.h" -class Bitmap; +// Allow KernelMemoryManager to allocate new pages from PageManager when kernel heap is full +// Please note that this means that the KMM depends on the page manager +// and you will have a harder time implementing swapping. Pros only! +static constexpr bool DYNAMIC_KMM = false; +/** + * Responsible for allocation of physical memory pages + */ class PageManager { - public: - static PageManager *instance(); +public: + PageManager() = delete; + PageManager(const PageManager&) = delete; + PageManager& operator=(const PageManager&) = delete; + + PageManager(Allocator* allocator); + + static PageManager& instance(); + static void init(); + static bool isReady(); /** * Returns the total number of physical pages available to SWEB. - * @return Number of available physical pages + * @return Number of available physical pages (4k page size) */ uint32 getTotalNumPages() const; /** * Returns the number of currently free physical pages. - * @return Number of free physical pages + * @return Number of free physical pages (4k page size) */ - uint32 getNumFreePages() const; + size_t getNumFreePages() const; /** - * Marks the lowest free physical page as used and returns it. - * Always returns 4KB PPNs. - * @param page_size The requested page size, must be a multiple of PAGE_SIZE - * @return The allocated physical page PPN. + * Allocate a physical memory page + * @param page_size The requested page size, must be a multiple of PAGE_SIZE (default = 4k) + * @return The allocated physical page PPN */ + [[nodiscard("Discarding return value of allocPPN() leaks pages")]] uint32 allocPPN(uint32 page_size = PAGE_SIZE); /** - * Marks the given physical page as free + * Releases the given physical page number (PPN) and marks it as free * @param page_number The physical page PPN to free * @param page_size The page size to free, must be a multiple of PAGE_SIZE */ @@ -46,25 +61,26 @@ class PageManager return lock_.heldBy(); } - PageManager(); - - void printBitmap(); + void printUsageInfo() const + { + debug(PM, "Useable pages:\n"); + allocator_->printUsageInfo(); + } - uint32 getNumPagesForUser() const; +private: + static size_t initUsableMemoryRegions(Allocator& allocator); + static void reserveKernelPages(Allocator& allocator); + static void initFreePageCanaries(BootstrapRangeAllocator& allocator); - private: - bool reservePages(uint32 ppn, uint32 num = 1); + void switchToHeapBitmapAllocator(); - PageManager(PageManager const&); + Allocator* allocator_; - Bitmap* page_usage_table_; - uint32 number_of_pages_; - uint32 lowest_unreserved_page_; - uint32 num_pages_for_user_; + size_t number_of_pages_; SpinLock lock_; static PageManager* instance_; - size_t HEAP_PAGES; + static bool pm_ready_; }; diff --git a/common/include/mm/allocators/Allocator.h b/common/include/mm/allocators/Allocator.h new file mode 100644 index 000000000..cbc42399f --- /dev/null +++ b/common/include/mm/allocators/Allocator.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Bitmap.h" +#include "paging-definitions.h" + +#include "types.h" + +#include "EASTL/iterator.h" + +#include "assert.h" +#include "debug.h" + +struct HalfOpenInterval +{ + size_t start; + size_t end; + + bool contains(size_t addr) { return start <= addr && addr < end; } + + enum IntervalRelation + { + + }; +}; + +class Allocator +{ +public: + virtual ~Allocator() = default; + + virtual size_t alloc(size_t size, size_t alignment = 1) = 0; + virtual bool dealloc(size_t start, size_t size) = 0; + + virtual void setUseable(size_t start, size_t end) = 0; + virtual void setUnuseable(size_t start, size_t end) = 0; + + [[nodiscard]] virtual size_t numFree() const = 0; + [[nodiscard]] virtual size_t numFreeBlocks(size_t size, size_t alignment = 1) const = 0; + + virtual void printUsageInfo() const = 0; + +private: +}; diff --git a/common/include/mm/allocators/BitmapAllocator.h b/common/include/mm/allocators/BitmapAllocator.h new file mode 100644 index 000000000..1eb778f01 --- /dev/null +++ b/common/include/mm/allocators/BitmapAllocator.h @@ -0,0 +1,144 @@ +#pragma once + +#include "Allocator.h" + +template +class BitmapAllocator : public Allocator +{ +public: + BitmapAllocator(size_t size) : + bitmap_(size/BLOCK_SIZE) + { + assert(size % BLOCK_SIZE == 0); + } + + BitmapAllocator(size_t size, Allocator&& alloc_template) : + bitmap_(size/BLOCK_SIZE) + { + assert(size % BLOCK_SIZE == 0); + + setUnuseable(0, size); + alloc_template.printUsageInfo(); + debug(PM, "Num free pages: %zu\n", alloc_template.numFreeBlocks(PAGE_SIZE, PAGE_SIZE)); + size_t free_phys_page; + while((free_phys_page = alloc_template.alloc(PAGE_SIZE, PAGE_SIZE)) != (size_t)-1) + { + setUseable(free_phys_page, free_phys_page + PAGE_SIZE); + } + } + + virtual ~BitmapAllocator() = default; + + virtual size_t alloc(size_t size = BLOCK_SIZE, size_t alignment = BLOCK_SIZE) + { + assert(size % BLOCK_SIZE == 0); + assert(alignment && (alignment % BLOCK_SIZE == 0)); + + size_t aligned_start = lowest_unreserved_address_; + aligned_start += (aligned_start % alignment ? alignment - aligned_start % alignment : 0); + for(size_t i = aligned_start; i + size <= bitmap_.getSize()*BLOCK_SIZE; i += alignment) + { + if(isRangeFree(i, size)) + { + setUnuseable(i, i + size); + return i; + } + } + + return -1; + } + + virtual bool allocTarget([[maybe_unused]]size_t start, [[maybe_unused]]size_t size) + { + // TODO + return false; + } + + + virtual bool dealloc(size_t start, size_t size = BLOCK_SIZE) + { + assert(start % BLOCK_SIZE == 0); + assert(size % BLOCK_SIZE == 0); + + for(size_t i = start; i < (start + size); i += BLOCK_SIZE) + { + if(!bitmap_.getBit(i/BLOCK_SIZE)) + { + return false; + } + } + + setUseable(start, start + size); + return true; + } + + + virtual void setUseable(size_t start, size_t end) + { + assert(start % BLOCK_SIZE == 0); + assert(end % BLOCK_SIZE == 0); + + for(size_t i = start/BLOCK_SIZE; i < end/BLOCK_SIZE; ++i) + { + bitmap_.unsetBit(i); + } + + if(start < lowest_unreserved_address_) + { + lowest_unreserved_address_ = start; + } + } + + + virtual void setUnuseable(size_t start, size_t end) + { + assert(start % BLOCK_SIZE == 0); + assert(end % BLOCK_SIZE == 0); + + for(size_t i = start/BLOCK_SIZE; i < end/BLOCK_SIZE; ++i) + { + bitmap_.setBit(i); + } + + while((lowest_unreserved_address_ < bitmap_.getSize()*BLOCK_SIZE) && bitmap_.getBit(lowest_unreserved_address_/BLOCK_SIZE)) + { + lowest_unreserved_address_ += BLOCK_SIZE; + } + } + + virtual size_t numFree() const + { + return bitmap_.getNumFreeBits() * BLOCK_SIZE; + } + + virtual size_t numFreeBlocks(size_t size = BLOCK_SIZE, size_t alignment = BLOCK_SIZE) const + { + assert(size % BLOCK_SIZE == 0); + assert(alignment && (alignment % BLOCK_SIZE == 0)); + + return bitmap_.getNumFreeBits(); + } + + virtual void printUsageInfo() const + { + bitmap_.bmprint(); + } + +private: + bool isRangeFree(size_t start, size_t size) const + { + assert(start % BLOCK_SIZE == 0); + assert(size % BLOCK_SIZE == 0); + + for(size_t i = start/BLOCK_SIZE; i < (start + size)/BLOCK_SIZE; ++i) + { + if(bitmap_.getBit(i)) + return false; + } + + return true; + } + + Bitmap bitmap_; + size_t lowest_unreserved_address_; +}; diff --git a/common/include/mm/allocators/BootstrapRangeAllocator.h b/common/include/mm/allocators/BootstrapRangeAllocator.h new file mode 100644 index 000000000..6cbc4c606 --- /dev/null +++ b/common/include/mm/allocators/BootstrapRangeAllocator.h @@ -0,0 +1,20 @@ +#pragma once + +#include "RangeAllocator.h" + +#include + +/* This BootstrapRangeAllocator is 'good enough' for temporary use during initialization + * of the page manager, but should not really be used elsewhere + */ +class BootstrapRangeAllocator : public RangeAllocator +{ +public: + BootstrapRangeAllocator(); + BootstrapRangeAllocator(const BootstrapRangeAllocator&) = delete; + ~BootstrapRangeAllocator() override = default; + +private: + // Fixed buffer for 10 elements + decltype(iset_)::node_type iset_buffer_[10]; +}; diff --git a/common/include/mm/allocators/RangeAllocator.h b/common/include/mm/allocators/RangeAllocator.h new file mode 100644 index 000000000..360f4ff3b --- /dev/null +++ b/common/include/mm/allocators/RangeAllocator.h @@ -0,0 +1,163 @@ +#pragma once + +#include "Allocator.h" +#include "IntervalSet.h" +#include "ranges.h" + +#include + +template +class RangeAllocator : public Allocator +{ +public: + RangeAllocator() = default; + + template + RangeAllocator(U&& iset_allocator) : + iset_(eastl::forward(iset_allocator)) + { + } + + RangeAllocator(const RangeAllocator&) = delete; + ~RangeAllocator() override = default; + + size_t alloc(size_t size, size_t alignment = 1) override + { + for (const auto& iv : iset_) + { + size_t start = iv.first; + size_t align_offset = start % alignment; + start += (align_offset ? alignment - align_offset : 0); + + if(start + size <= iv.second) + { + iset_.erase({start, start + size}); + return start; + } + } + + return -1; + } + + bool dealloc(size_t start, size_t size) override + { + // RangeAllocator doesn't have error checking for deallocation + setUseable(start, start + size); + return true; + } + + void setUseable(size_t start, size_t end) override + { + debug(PM, "Set useable [%zx - %zx)\n", start, end); + assert(start <= end); + iset_.insert({start, end}); + } + + void setUnuseable(size_t start, size_t end) override + { + debug(PM, "Set unuseable [%zx - %zx)\n", start, end); + assert(start <= end); + iset_.erase({start, end}); + } + + [[nodiscard]] size_t numFree() const override + { + size_t num_free = 0; + for (const auto& iv : iset_) + { + num_free += iv.second - iv.first; + } + + return num_free; + } + + [[nodiscard]] size_t numFreeBlocks(size_t size, size_t alignment = 1) const override + { + assert(size > 0); + assert(alignment == size); + + size_t num_blocks = 0; + for (auto& iv : iset_) + { + size_t aligned_start = iv.first; + aligned_start += (aligned_start % alignment ? alignment - aligned_start % alignment : 0); + num_blocks += (iv.second - aligned_start)/size; + } + + return num_blocks; + } + + void printUsageInfo() const override + { + for (const auto& interval : iset_) + { + debug(PM, "[%zx - %zx)\n", interval.first, interval.second); + } + } + + [[nodiscard]] size_t nextFreeBlock(size_t size, size_t alignment, size_t start) const + { + for (auto& iv : iset_) + { + size_t check_start = start < iv.first ? iv.first : start; + size_t align_offset = check_start % alignment; + check_start += (align_offset ? alignment - align_offset : 0); + if(check_start + size <= iv.second) + { + return check_start; + } + } + + return -1; + } + + + class AllocBlockIterator : public eastl::iterator + { + public: + using iterator_category = eastl::input_iterator_tag; + using value_type = size_t; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + + AllocBlockIterator(const RangeAllocator* allocator, size_t size, size_t alignment, size_t start) : + allocator_(allocator), size_(size), alignment_(alignment), curr_(start) {} + + const_reference operator*() const { return curr_; } + const_pointer operator->() const { return &curr_; } + + AllocBlockIterator& operator++() { curr_ = allocator_->nextFreeBlock(size_, alignment_, curr_ + size_); return *this; } + + friend bool operator== (const AllocBlockIterator& a, const AllocBlockIterator& b) { return a.curr_ == b.curr_; } + friend bool operator!= (const AllocBlockIterator& a, const AllocBlockIterator& b) { return a.curr_ != b.curr_; } + + private: + const RangeAllocator* allocator_; + + size_t size_; + size_t alignment_; + size_t curr_; + }; + + [[nodiscard]] auto freeBlocks(size_t size, size_t alignment) const + { + return ranges::subrange{freeBlocksBegin(size, alignment), + freeBlocksEnd(size, alignment)}; + } + +protected: + [[nodiscard]] AllocBlockIterator freeBlocksBegin(size_t size, size_t alignment) const + { + return AllocBlockIterator(this, size, alignment, + nextFreeBlock(size, alignment, 0)); + } + + [[nodiscard]] AllocBlockIterator freeBlocksEnd(size_t size, size_t alignment) const + { + return AllocBlockIterator(this, size, alignment, -1); + } + + IntervalSet iset_; +}; diff --git a/common/include/mm/kmalloc.h b/common/include/mm/kmalloc.h index 7882b0935..6dc1fa4ea 100644 --- a/common/include/mm/kmalloc.h +++ b/common/include/mm/kmalloc.h @@ -1,6 +1,6 @@ #pragma once -#include "types.h" +#include "stddef.h" #ifdef __cplusplus extern "C" diff --git a/common/include/mm/new.h b/common/include/mm/new.h index 86933137f..039ffcbc7 100644 --- a/common/include/mm/new.h +++ b/common/include/mm/new.h @@ -1,18 +1,10 @@ #pragma once -#include "types.h" +#include -/** - * wrapper for placement new operator - */ -inline void* operator new(size_t, void* p) -{ - return p; -} +#include void __builtin_delete(void* address); void* __builtin_new(size_t size); void* __builtin_vec_new(size_t size); void __builtin_vec_delete(void* address); - - diff --git a/common/include/ustl/LICENSE b/common/include/ustl/LICENSE deleted file mode 100644 index ad459b2bd..000000000 --- a/common/include/ustl/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -License for the USTL, forked from https://github.com/msharov/ustl - - - The MIT License - -Copyright (c) 2005 by Mike Sharov - -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. diff --git a/common/include/ustl/cmemlink.h b/common/include/ustl/cmemlink.h deleted file mode 100644 index 3a2593e09..000000000 --- a/common/include/ustl/cmemlink.h +++ /dev/null @@ -1,98 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "assert.h" -#include "ualgobase.h" - -/// The ustl namespace contains all ustl classes and algorithms. -namespace ustl { - -class istream; -class ostream; -class ostringstream; - -/// \class cmemlink cmemlink.h ustl.h -/// \ingroup MemoryManagement -/// -/// \brief A read-only pointer to a sized block of memory. -/// -/// Use this class the way you would a const pointer to an allocated unstructured block. -/// The pointer and block size are available through member functions and cast operator. -/// -/// Example usage: -/// -/// \code -/// void* p = malloc (46721); -/// cmemlink a, b; -/// a.link (p, 46721); -/// assert (a.size() == 46721)); -/// b = a; -/// assert (b.size() == 46721)); -/// assert (b.DataAt(34) == a.DataAt(34)); -/// assert (0 == memcmp (a, b, 12)); -/// \endcode -/// -class cmemlink { -public: - typedef char value_type; - typedef const value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type reference; - typedef value_type const_reference; - typedef size_t size_type; - typedef uint32_t written_size_type; - typedef ptrdiff_t difference_type; - typedef const_pointer const_iterator; - typedef const_iterator iterator; - typedef const cmemlink& rcself_t; -public: - inline cmemlink (void) : _data (nullptr), _size (0) { } - inline cmemlink (const void* p, size_type n) : _data (const_pointer(p)), _size (n) { assert (p || !n); } - inline cmemlink (const cmemlink& l) : _data (l._data), _size (l._size) {} - inline virtual ~cmemlink (void) noexcept {} - void link (const void* p, size_type n); - inline void link (const cmemlink& l) { link (l.begin(), l.size()); } - inline void link (const void* first, const void* last) { link (first, distance (first, last)); } - inline void relink (const void* p, size_type n); - virtual void unlink (void) noexcept { _data = nullptr; _size = 0; } - inline rcself_t operator= (const cmemlink& l) { link (l); return *this; } - bool operator== (const cmemlink& l) const noexcept; - inline void swap (cmemlink& l) { ::ustl::swap (_data, l._data); ::ustl::swap (_size, l._size); } - inline size_type size (void) const { return _size; } - inline size_type max_size (void) const { return size(); } - inline size_type readable_size (void) const { return size(); } - inline bool empty (void) const { return !size(); } - inline const_pointer data (void) const { return _data; } - inline const_pointer cdata (void) const { return _data; } - inline iterator begin (void) const { return iterator (cdata()); } - inline iterator iat (size_type i) const { assert (i <= size()); return begin() + i; } - inline iterator end (void) const { return iat (size()); } - inline void resize (size_type n) { _size = n; } - inline void read (istream&) { assert (!"ustl::cmemlink is a read-only object."); } - void write (ostream& os) const; - size_type stream_size (void) const noexcept; - void text_write (ostringstream& os) const; - void write_file (const char* filename, int mode = 0644) const; -private: - const_pointer _data; ///< Pointer to the data block (const) - size_type _size; ///< size of the data block -}; - -//---------------------------------------------------------------------- - -/// A fast alternative to link which can be used when relinking to the same block (i.e. when it is resized) -inline void cmemlink::relink (const void* p, size_type n) -{ - _data = reinterpret_cast(p); - _size = n; -} - -//---------------------------------------------------------------------- - -/// Use with cmemlink-derived classes to link to a static array -#define static_link(v) link (VectorBlock(v)) - -} // namespace ustl diff --git a/common/include/ustl/config.h b/common/include/ustl/config.h deleted file mode 100644 index 1af61b7d2..000000000 --- a/common/include/ustl/config.h +++ /dev/null @@ -1,261 +0,0 @@ -// config.h - Generated from config.h.in by configure. -#pragma once - -// Define to the one symbol short name of this package. -#define USTL_NAME "ustl" -// Define to the full name and version of this package. -// More or less. Was manually merged and updated from version ustl v2.2-3-gf0abf84 with SWEB specific changes. config.h file was mostly left as it is and not updated -// Based on ustl upstream commit c19d8291a471c47c808b5ef303f163df166cd388 -#define USTL_STRING "ustl v2.6-2-gc19d829" -// Define to the version of this package. -#define USTL_VERSION 0x260 -// Define to the address where bug reports for this package should be sent. -#define USTL_BUGREPORT "Mike Sharov " - -/// Define to 1 if you want stream operations to throw exceptions on -/// insufficient data or insufficient space. All these errors should -/// be preventable in output code; the input code should verify the -/// data in a separate step. It slows down stream operations a lot, -/// but it is your decision. By default only debug builds throw. -/// -#define WANT_STREAM_BOUNDS_CHECKING 0 - -/// Define to 1 if you want backtrace symbols demangled. -/// This adds some 15k to the library size, and requires that you link it and -/// any executables you make with the -rdynamic flag (increasing library size -/// even more). By default only the debug build does this. -#undef WANT_NAME_DEMANGLING - -/// Define to 1 if you want to build without libstdc++ -#define WITHOUT_LIBSTDCPP 1 - -/// Define GNU extensions if unavailable. -#ifndef __GNUC__ - /// GCC (and some other compilers) define '__attribute__'; ustl is using this - /// macro to alert the compiler to flag inconsistencies in printf/scanf-like - /// function calls. Just in case '__attribute__' is undefined, make a dummy. - /// - #ifndef __attribute__ - #define __attribute__(p) - #endif -#endif -#define WEAKALIAS(sym) __attribute__((weak,alias(sym))) -#if __GNUC__ >= 4 - #define DLL_EXPORT __attribute__((visibility("default"))) - #define DLL_LOCAL __attribute__((visibility("hidden"))) - #define INLINE __attribute__((always_inline)) -#else - #define DLL_EXPORT - #define DLL_LOCAL - #define INLINE -#endif -#if __cplusplus >= 201103L && (!__GNUC__ || (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 2)) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #define HAVE_CPP11 1 - #if __cplusplus >= 201402 - #define HAVE_CPP14 1 - #endif -#endif -#if !HAVE_CPP11 - #define constexpr - #define override - #define final - #define nullptr NULL - #define noexcept throw() -#endif -#if __GNUC__ >= 3 && (__i386__ || __x86_64__) - /// GCC 3+ supports the prefetch directive, which some CPUs use to improve caching - #define prefetch(p,rw,loc) __builtin_prefetch(p,rw,loc) -#else - #define prefetch(p,rw,loc) -#endif -#if __GNUC__ < 3 - /// __alignof__ returns the recommended alignment for the type - #define __alignof__(v) min(sizeof(v), sizeof(void*)) - /// This macro returns 1 if the value of x is known at compile time. - #ifndef __builtin_constant_p - #define __builtin_constant_p(x) 0 - #endif -#endif - -// Define to empty if 'const' does not conform to ANSI C. -#undef const -// Define as '__inline' if that is what the C compiler calls it -#undef inline -// Define to 'long' if does not define. -#undef off_t -// Define to 'unsigned' if does not define. -#undef size_t - -/// gcc has lately decided that inline is just a suggestion -/// Define to 1 if when you say 'inline' you mean it! -#undef WANT_ALWAYS_INLINE -#if WANT_ALWAYS_INLINE - #define inline INLINE inline -#endif - -/// Define to 1 if you have the header file. -#undef HAVE_ASSERT_H - -/// Define to 1 if you have the header file. -#undef HAVE_CTYPE_H - -/// Define to 1 if you have the header file. -#undef HAVE_ERRNO_H - -/// Define to 1 if you have the header file. -#undef HAVE_FCNTL_H - -/// Define to 1 if you have the header file. -#undef HAVE_FLOAT_H - -/// Define to 1 if you have the header file. -#undef HAVE_INTTYPES_H - -/// Define to 1 if you have the header file. -#undef HAVE_LIMITS_H - -/// Define to 1 if you have the header file. -#undef HAVE_LOCALE_H - -// Define to 1 if you have the header file. -#undef HAVE_ALLOCA_H - -// Define to 1 if you have the header file. -#undef HAVE_SIGNAL_H - -// Define to 1 if you have the __va_copy function -#define HAVE_VA_COPY 1 - -// Define to 1 if you have the header file. -#undef HAVE_STDARG_H - -// Define to 1 if you have the header file. -#undef HAVE_STDDEF_H - -// Define to 1 if you have the header file. -#undef HAVE_STDINT_H - -// Define to 1 if you have the header file. -#undef HAVE_STDIO_H - -// Define to 1 if you have the header file. -#undef HAVE_STDLIB_H - -// Define to 1 if you have the header file. -#undef HAVE_STRING_H - -// Define to 1 if you have the 'strrchr' function. -#define HAVE_STRRCHR 1 - -// Define to 1 if you have the 'strsignal' function. -#define HAVE_STRSIGNAL 1 - -// Define to 1 if you have the header file. -#undef HAVE_SYS_STAT_H - -// Define to 1 if you have the header file. -#undef HAVE_SYS_TYPES_H - -// Define to 1 if you have the header file. -#undef HAVE_SYS_MMAN_H - -// Define to 1 if you have the header file. -#undef HAVE_TIME_H - -// Define to 1 if you have the header file. -#undef HAVE_UNISTD_H - -// Define to 1 if you have the header file. -#undef HAVE_MATH_H - -// Define to 1 if you have the header file. -#undef HAVE_EXECINFO_H - -// Define to 1 if you have the header file. -#if __GNUC__ >= 3 - #define HAVE_CXXABI_H 1 -#endif - -// Define to 1 if you have the rintf function. Will use rint otherwise. -#undef HAVE_RINTF - -// STDC_HEADERS is defined to 1 on sane systems. -#if HAVE_ASSERT_H && HAVE_CTYPE_H && HAVE_ERRNO_H && HAVE_FLOAT_H &&\ - HAVE_LIMITS_H && HAVE_LOCALE_H && HAVE_MATH_H && HAVE_SIGNAL_H &&\ - HAVE_STDARG_H && HAVE_STDDEF_H && HAVE_STDIO_H && HAVE_STDLIB_H &&\ - HAVE_STRING_H && HAVE_TIME_H - #define STDC_HEADERS 1 -#endif - -// STDC_HEADERS is defined to 1 on unix systems. -#if HAVE_FCNTL_H && HAVE_SYS_STAT_H && HAVE_UNISTD_H - #define STDUNIX_HEADERS 1 -#endif - -// Define to 1 if your compiler treats char as a separate type along with -// signed char and unsigned char. This will create overloads for char. -#undef HAVE_THREE_CHAR_TYPES - -// Define to 1 if you have 64 bit types available -#undef HAVE_INT64_T - -// Define to 1 if you have the long long type -#undef HAVE_LONG_LONG - -// Define to 1 if you want unrolled specializations for fill and copy -#define WANT_UNROLLED_COPY 1 - -// Define to 1 if you want to use MMX/SSE/3dNow! processor instructions -#define WANT_MMX 0 - -// Define to byte sizes of types -#undef SIZE_OF_CHAR -#undef SIZE_OF_SHORT -#undef SIZE_OF_INT -#undef SIZE_OF_LONG -#undef SIZE_OF_LONG_LONG -#undef SIZE_OF_POINTER -#undef SIZE_OF_SIZE_T -#undef SIZE_OF_BOOL -#undef SIZE_T_IS_LONG - -// Byte order macros, converted in utypes.h -#define USTL_LITTLE_ENDIAN 4321 -#define USTL_BIG_ENDIAN 1234 -#define USTL_BYTE_ORDER USTL_LITTLE_ENDIAN - -#if __i386__ || __x86_64__ -#define __x86__ 1 -#endif - -// Extended CPU capabilities -#undef CPU_HAS_FPU -#undef CPU_HAS_EXT_DEBUG -#undef CPU_HAS_TIMESTAMPC -#undef CPU_HAS_MSR -#undef CPU_HAS_CMPXCHG8 -#undef CPU_HAS_APIC -#undef CPU_HAS_SYSCALL -#undef CPU_HAS_MTRR -#undef CPU_HAS_CMOV -#undef CPU_HAS_FCMOV -#if WANT_MMX -#undef CPU_HAS_MMX -#undef CPU_HAS_FXSAVE -#undef CPU_HAS_SSE -#undef CPU_HAS_SSE2 -#undef CPU_HAS_SSE3 -#undef CPU_HAS_EXT_3DNOW -#undef CPU_HAS_3DNOW -#endif - -// GCC vector extensions -#if (CPU_HAS_MMX || CPU_HAS_SSE) && __GNUC__ >= 3 - #undef HAVE_VECTOR_EXTENSIONS -#endif - -#if CPU_HAS_SSE && __GNUC__ - #define __sse_align __attribute__((aligned(16))) -#else - #define __sse_align -#endif diff --git a/common/include/ustl/memblock.h b/common/include/ustl/memblock.h deleted file mode 100644 index e1a3ab829..000000000 --- a/common/include/ustl/memblock.h +++ /dev/null @@ -1,62 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "memlink.h" - -namespace ustl { - -/// \class memblock memblock.h ustl.h -/// \ingroup MemoryManagement -/// -/// \brief Allocated memory block. -/// -/// Adds memory management capabilities to memlink. Uses malloc and realloc to -/// maintain the internal pointer, but only if allocated using members of this class, -/// or if linked to using the Manage() member function. Managed memory is automatically -/// freed in the destructor. -/// -class memblock : public memlink { -public: - memblock (void) noexcept; - memblock (const void* p, size_type n); - explicit memblock (size_type n); - explicit memblock (const cmemlink& b); - explicit memblock (const memlink& b); - memblock (const memblock& b); - virtual ~memblock (void) noexcept; - virtual void unlink (void) noexcept override; - inline void assign (const cmemlink& l) { assign (l.cdata(), l.readable_size()); } - inline const memblock& operator= (const cmemlink& l) { assign (l); return *this; } - inline const memblock& operator= (const memlink& l) { assign (l); return *this; } - inline const memblock& operator= (const memblock& l) { assign (l); return *this; } - inline void swap (memblock& l) noexcept { memlink::swap (l); ::ustl::swap (_capacity, l._capacity); } - void assign (const void* p, size_type n); - void reserve (size_type newSize, bool bExact = false); - void resize (size_type newSize, bool bExact = true); - iterator insert (const_iterator start, size_type size); - iterator erase (const_iterator start, size_type size); - inline void clear (void) noexcept { resize (0); } - inline size_type capacity (void) const { return _capacity; } - inline bool is_linked (void) const { return !capacity(); } - inline size_type max_size (void) const { return is_linked() ? memlink::max_size() : SIZE_MAX; } - inline void manage (memlink& l) { manage (l.begin(), l.size()); } - void deallocate (void) noexcept; - void shrink_to_fit (void); - void manage (void* p, size_type n) noexcept; - void copy_link (void); - void read (istream& is); - void read_file (const char* filename); -#if HAVE_CPP11 - inline memblock (memblock&& b) : memlink(), _capacity(0) { swap (b); } - inline memblock& operator= (memblock&& b) { swap (b); return *this; } -#endif -protected: - virtual size_type minimumFreeCapacity (void) const noexcept __attribute__((const)); -private: - size_type _capacity; ///< Number of bytes allocated by Resize. -}; - -} // namespace ustl diff --git a/common/include/ustl/memlink.h b/common/include/ustl/memlink.h deleted file mode 100644 index 95b645040..000000000 --- a/common/include/ustl/memlink.h +++ /dev/null @@ -1,98 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "cmemlink.h" -#include "ualgo.h" - -namespace ustl { - -/// \class memlink memlink.h ustl.h -/// \ingroup MemoryManagement -/// -/// \brief Wrapper for pointer to block with size. -/// -/// Use this class the way you would a pointer to an allocated unstructured block. -/// The pointer and block size are available through member functions and cast operator. -/// -/// Example usage: -/// \code -/// void* p = malloc (46721); -/// memlink a, b; -/// a.link (p, 46721); -/// assert (a.size() == 46721)); -/// b = a; -/// assert (b.size() == 46721)); -/// assert (b.begin() + 34 == a.begin + 34); -/// assert (0 == memcmp (a, b, 12)); -/// a.fill (673, b, 42, 67); -/// b.erase (87, 12); -/// \endcode -/// -class memlink : public cmemlink { -public: - typedef value_type* pointer; - typedef cmemlink::pointer const_pointer; - typedef cmemlink::const_iterator const_iterator; - typedef pointer iterator; - typedef const memlink& rcself_t; -public: - inline memlink (void) : cmemlink() {} - inline memlink (void* p, size_type n) : cmemlink (p, n) {} - inline memlink (const void* p, size_type n) : cmemlink (p, n) {} - inline memlink (rcself_t l) : cmemlink (l) {} - inline explicit memlink (const cmemlink& l) : cmemlink (l) {} - inline pointer data (void) { return const_cast(cmemlink::data()); } - inline const_pointer data (void) const { return cmemlink::data(); } - inline iterator begin (void) { return iterator (data()); } - inline iterator iat (size_type i) { assert (i <= size()); return begin() + i; } - inline iterator end (void) { return iat (size()); } - inline const_iterator begin (void) const { return cmemlink::begin(); } - inline const_iterator end (void) const { return cmemlink::end(); } - inline const_iterator iat (size_type i) const { return cmemlink::iat (i); } - size_type writable_size (void) const { return size(); } - inline rcself_t operator= (const cmemlink& l) { cmemlink::operator= (l); return *this; } - inline rcself_t operator= (rcself_t l) { cmemlink::operator= (l); return *this; } - inline void link (const void* p, size_type n) { cmemlink::link (p, n); } - inline void link (void* p, size_type n) { cmemlink::link (p, n); } - inline void link (const cmemlink& l) { cmemlink::link (l); } - inline void link (memlink& l) { cmemlink::link (l); } - inline void link (const void* first, const void* last) { link (first, distance (first, last)); } - inline void link (void* first, void* last) { link (first, distance (first, last)); } - inline void relink (const void* p, size_type n) { cmemlink::relink (p, n); } - inline void relink (void* p, size_type n) { cmemlink::relink (p, n); } - inline void swap (memlink& l) { cmemlink::swap (l); } - void fill (const_iterator start, const void* p, size_type elsize, size_type elCount = 1) noexcept; - inline void insert (const_iterator start, size_type size); - inline void erase (const_iterator start, size_type size); - void read (istream& is); -}; - -/// Shifts the data in the linked block from \p start to \p start + \p n. -/// The contents of the uncovered bytes is undefined. -inline void memlink::insert (const_iterator cstart, size_type n) -{ - assert (data() || !n); - assert (cmemlink::begin() || !n); - assert (cstart >= begin() && cstart + n <= end()); - iterator start = const_cast(cstart); - rotate (start, end() - n, end()); -} - -/// Shifts the data in the linked block from \p start + \p n to \p start. -/// The contents of the uncovered bytes is undefined. -inline void memlink::erase (const_iterator cstart, size_type n) -{ - assert (data() || !n); - assert (cmemlink::begin() || !n); - assert (cstart >= begin() && cstart + n <= end()); - iterator start = const_cast(cstart); - rotate (start, start + n, end()); -} - -/// Use with memlink-derived classes to allocate and link to stack space. -#define alloca_link(m,n) (m).link (alloca (n), (n)) - -} // namespace ustl diff --git a/common/include/ustl/metamac.h b/common/include/ustl/metamac.h deleted file mode 100644 index 4b22a1aeb..000000000 --- a/common/include/ustl/metamac.h +++ /dev/null @@ -1,89 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. -// -/// \file metamac.h -/// \brief Macros for complex metaprogramming involving pseudoiteration. - -#pragma once - -//---------------------------------------------------------------------- -// Functors and general utilities. -//---------------------------------------------------------------------- - -/// Evaluates to itself -#define ITSELF(x) x - -/// Concatenates \p a and \p b -#define PASTE(a,b) a##b - -//---------------------------------------------------------------------- -// Lists and other iterators -//---------------------------------------------------------------------- - -/// The maximum number of elements in REPEAT, LIST, and COMMA_LIST -#define METAMAC_MAXN 9 - -/// Simple list with no separators. Repeats x N times. -/// @{ -#define REPEAT_1(x) x(1) -#define REPEAT_2(x) REPEAT_1(x) x(2) -#define REPEAT_3(x) REPEAT_2(x) x(3) -#define REPEAT_4(x) REPEAT_3(x) x(4) -#define REPEAT_5(x) REPEAT_4(x) x(5) -#define REPEAT_6(x) REPEAT_5(x) x(6) -#define REPEAT_7(x) REPEAT_6(x) x(7) -#define REPEAT_8(x) REPEAT_7(x) x(8) -#define REPEAT_9(x) REPEAT_8(x) x(9) -#define REPEAT(N,x) PASTE(REPEAT_,N)(x) -/// @} - -/// Simple separated list. Repeats x N times with sep in between. -/// @{ -#define LIST_1(x,sep) x(1) -#define LIST_2(x,sep) LIST_1(x,sep) sep x(2) -#define LIST_3(x,sep) LIST_2(x,sep) sep x(3) -#define LIST_4(x,sep) LIST_3(x,sep) sep x(4) -#define LIST_5(x,sep) LIST_4(x,sep) sep x(5) -#define LIST_6(x,sep) LIST_5(x,sep) sep x(6) -#define LIST_7(x,sep) LIST_6(x,sep) sep x(7) -#define LIST_8(x,sep) LIST_7(x,sep) sep x(8) -#define LIST_9(x,sep) LIST_8(x,sep) sep x(9) -#define LIST(N,x,sep) PASTE(LIST_,N)(x,sep) -/// @} - -/// Comma separated list. A special case of LIST needed because the preprocessor can't substitute commas. -/// @{ -#define COMMA_LIST_1(x) x(1) -#define COMMA_LIST_2(x) COMMA_LIST_1(x), x(2) -#define COMMA_LIST_3(x) COMMA_LIST_2(x), x(3) -#define COMMA_LIST_4(x) COMMA_LIST_3(x), x(4) -#define COMMA_LIST_5(x) COMMA_LIST_4(x), x(5) -#define COMMA_LIST_6(x) COMMA_LIST_5(x), x(6) -#define COMMA_LIST_7(x) COMMA_LIST_6(x), x(7) -#define COMMA_LIST_8(x) COMMA_LIST_7(x), x(8) -#define COMMA_LIST_9(x) COMMA_LIST_8(x), x(9) -#define COMMA_LIST(N,x) PASTE(COMMA_LIST_,N)(x) -/// @} - -//---------------------------------------------------------------------- -// Macros for defining LIST arguments. -//---------------------------------------------------------------------- - -/// Ignores N, producing lists of identically named arguments. -#define LARG_NONE(name,N) name - -/// Appends N to name. -#define LARG_NUMBER(name,N) name##N - -/// name is a reference type. -#define LARG_REF(name,N) name##N& - -/// Sequential parameter passed by value with sequential types. -#define LARG_MT_PARAM_BY_VALUE(type,name,N) type##N name##N - -/// Sequential parameter passed by reference with sequential types. -#define LARG_MT_PARAM_BY_REF(type,name,N) type##N& name##N - -//---------------------------------------------------------------------- diff --git a/common/include/ustl/mistream.h b/common/include/ustl/mistream.h deleted file mode 100644 index f80ee3b48..000000000 --- a/common/include/ustl/mistream.h +++ /dev/null @@ -1,295 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "memlink.h" -#include "strmsize.h" -#include "utf8.h" -#include "uios.h" -#if WANT_STREAM_BOUNDS_CHECKING - #include "typeinfo.h" -#endif - -namespace ustl { - -class ostream; -class memlink; -class string; - -/// \class istream mistream.h ustl.h -/// \ingroup BinaryStreams -/// -/// \brief Helper class to read packed binary streams. -/// -/// This class contains a set of functions to read integral types from an -/// unstructured memory block. Unpacking binary file data can be done this -/// way, for instance. aligning the data is your responsibility, and can -/// be accomplished by proper ordering of reads and by calling the align() -/// function. Unaligned access is usually slower by orders of magnitude and, -/// on some architectures, such as PowerPC, can cause your program to crash. -/// Therefore, all read functions have asserts to check alignment. -/// Overreading the end of the stream will also cause a crash (an assert in -/// debug builds). Oh, and don't be intimidated by the size of the inlines -/// here. In the assembly code the compiler will usually chop everything down -/// to five instructions each. -/// -/// Alignment rules for your objects: -/// - Assume your writes start off 4-byte aligned. -/// - After completion, \ref istream::align the stream to at least 4. -/// - If data portability between 32bit and 64bit platforms is important -/// (it often is not, in config files and the like), ensure you are always -/// using fixed-size types and are aligning to a fixed grain. Avoid writing -/// 8-byte types, and if you do, manually align before doing so. -/// - Non-default alignment is allowed if you plan to frequently write this -/// object in array form and alignment would be costly. For example, an -/// array of uint16_t-sized objects may leave the stream uint16_t aligned -/// as long as you know about it and will default-align the stream after -/// writing the array (note: \ref vector will already do this for you) -/// -/// Example code: -/// \code -/// memblock b; -/// b.read_file ("test.file"); -/// ostream is (b); -/// is >> boolVar >> ios::talign(); -/// is >> intVar >> floatVar; -/// is.read (binaryData, binaryDataSize); -/// is.align(); -/// \endcode -/// -class istream : public cmemlink, public ios_base { -public: - inline istream (void) : cmemlink(), _pos (0) {} - inline istream (const void* p, streamsize n) : cmemlink(p, n), _pos (0) {} - inline explicit istream (const cmemlink& source) : cmemlink (source), _pos (0) {} - explicit istream (const ostream& source) noexcept; - inline iterator end (void) const { return cmemlink::end(); } - inline void link (const void* p, streamsize n) { cmemlink::link (p, n); } - inline void link (const cmemlink& l) { cmemlink::link (l.cdata(), l.readable_size()); } - inline void link (const void* f, const void* l) { cmemlink::link (f, l); } - inline void relink (const void* p, streamsize n) { cmemlink::relink (p, n); _pos = 0; } - inline void relink (const cmemlink& l) { relink (l.cdata(), l.readable_size()); } - virtual void unlink (void) noexcept override; - virtual streamsize underflow (streamsize = 1); - inline uoff_t pos (void) const { return _pos; } - inline const_iterator ipos (void) const { return begin() + pos(); } - inline streamsize remaining (void) const { return size() - pos(); } - inline void seek (uoff_t newPos); - inline void iseek (const_iterator newPos); - inline void skip (streamsize nBytes); - inline bool aligned (streamsize grain = c_DefaultAlignment) const; - inline bool verify_remaining (const char* op, const char* type, streamsize n); - inline streamsize align_size (streamsize grain = c_DefaultAlignment) const; - inline void align (streamsize grain = c_DefaultAlignment); - inline void swap (istream& is); - inline void read (void* buffer, streamsize size); - inline void read (memlink& buf) { read (buf.begin(), buf.writable_size()); } - void read_strz (string& str); - streamsize readsome (void* s, streamsize n); - inline void read (istream&) { } - void write (ostream& os) const; - void text_write (ostringstream& os) const; - inline streamsize stream_size (void) const { return remaining(); } - template - inline void iread (T& v); - inline void ungetc (void) { seek (pos() - 1); } - inline off_t tellg (void) const { return pos(); } - inline void seekg (off_t p, seekdir d = beg); -private: - streamoff _pos; ///< The current read position. -}; - -//---------------------------------------------------------------------- - -template -inline streamsize required_stream_size (T, const Stream&) { return 1; } -template -inline streamsize required_stream_size (T v, const istream&) { return stream_size_of(v); } - -template -inline bool stream_at_eof (const Stream& stm) { return stm.eof(); } -template <> -inline bool stream_at_eof (const istream&) { return false; } - -/// \class istream_iterator -/// \ingroup BinaryStreamIterators -/// -/// \brief An iterator over an istream to use with uSTL algorithms. -/// -template -class istream_iterator { -public: - typedef T value_type; - typedef ptrdiff_t difference_type; - typedef const value_type* pointer; - typedef const value_type& reference; - typedef typename Stream::size_type size_type; - typedef input_iterator_tag iterator_category; -public: - istream_iterator (void) : _pis (nullptr), _v() {} - explicit istream_iterator (Stream& is) : _pis (&is), _v() { Read(); } - istream_iterator (const istream_iterator& i) : _pis (i._pis), _v (i._v) {} - /// Reads and returns the next value. - inline const T& operator* (void) { return _v; } - inline istream_iterator& operator++ (void) { Read(); return *this; } - inline istream_iterator& operator-- (void) { _pis->seek (_pis->pos() - 2 * stream_size_of(_v)); return operator++(); } - inline istream_iterator operator++ (int) { istream_iterator old (*this); operator++(); return old; } - inline istream_iterator operator-- (int) { istream_iterator old (*this); operator--(); return old; } - inline istream_iterator& operator+= (streamsize n) { while (n--) operator++(); return *this; } - inline istream_iterator& operator-= (streamsize n) { _pis->seek (_pis->pos() - (n + 1) * stream_size_of(_v)); return operator++(); } - inline istream_iterator operator- (streamoff n) const { istream_iterator result (*this); return result -= n; } - inline difference_type operator- (const istream_iterator& i) const { return distance (i._pis->pos(), _pis->pos()) / stream_size_of(_v); } - inline bool operator== (const istream_iterator& i) const { return (!_pis && !i._pis) || (_pis && i._pis && _pis->pos() == i._pis->pos()); } - inline bool operator< (const istream_iterator& i) const { return !i._pis || (_pis && _pis->pos() < i._pis->pos()); } -private: - void Read (void) - { - if (!_pis) - return; - const streamsize rs (required_stream_size (_v, *_pis)); - if (_pis->remaining() < rs && _pis->underflow (rs) < rs) { - _pis = nullptr; - return; - } - *_pis >> _v; - if (stream_at_eof (*_pis)) - _pis = nullptr; - } -private: - Stream* _pis; ///< The host stream. - T _v; ///< Last read value; cached to be returnable as a const reference. -}; - -//---------------------------------------------------------------------- - -/// Checks that \p n bytes are available in the stream, or else throws. -inline bool istream::verify_remaining (const char* op, const char* type, streamsize n) -{ - const streamsize rem = remaining(); - bool enough = n <= rem; - if (!enough) overrun (op, type, n, pos(), rem); - return enough; -} - -/// Sets the current read position to \p newPos -inline void istream::seek (uoff_t newPos) -{ -#if WANT_STREAM_BOUNDS_CHECKING - if (newPos > size()) - return overrun ("seekg", "byte", newPos, pos(), size()); -#else - assert (newPos <= size()); -#endif - _pos = newPos; -} - -/// Sets the current read position to \p newPos -inline void istream::iseek (const_iterator newPos) -{ - seek (distance (begin(), newPos)); -} - -/// Sets the current write position to \p p based on \p d. -inline void istream::seekg (off_t p, seekdir d) -{ - switch (d) { - case beg: seek (p); break; - case cur: seek (pos() + p); break; - case ios_base::end: seek (size() - p); break; - } -} - -/// Skips \p nBytes without reading them. -inline void istream::skip (streamsize nBytes) -{ - seek (pos() + nBytes); -} - -/// Returns the number of bytes to skip to be aligned on \p grain. -inline streamsize istream::align_size (streamsize grain) const -{ - return Align (pos(), grain) - pos(); -} - -/// Returns \c true if the read position is aligned on \p grain -inline bool istream::aligned (streamsize grain) const -{ - return pos() % grain == 0; -} - -/// aligns the read position on \p grain -inline void istream::align (streamsize grain) -{ - seek (Align (pos(), grain)); -} - -/// Reads type T from the stream via a direct pointer cast. -template -inline void istream::iread (T& v) -{ - assert (aligned (stream_align_of (v))); -#if WANT_STREAM_BOUNDS_CHECKING - if (!verify_remaining ("read", typeid(v).name(), sizeof(T))) - return; -#else - assert (remaining() >= sizeof(T)); -#endif - v = *reinterpret_cast(ipos()); - _pos += sizeof(T); -} - -/// Swaps contents with \p is -inline void istream::swap (istream& is) -{ - cmemlink::swap (is); - ::ustl::swap (_pos, is._pos); -} - -/// Reads \p n bytes into \p buffer. -inline void istream::read (void* buffer, size_type n) -{ -#if WANT_STREAM_BOUNDS_CHECKING - if (!verify_remaining ("read", "binary data", n)) - return; -#else - assert (remaining() >= n && "Reading past end of buffer. Make sure you are reading the right format."); -#endif - memcpy (reinterpret_cast(buffer), ipos(), n); - _pos += n; -} - -//---------------------------------------------------------------------- - -template struct object_reader { - inline void operator()(istream& is, T& v) const { v.read (is); } -}; -template struct integral_object_reader { - inline void operator()(istream& is, T& v) const { is.iread (v); } -}; -template -inline istream& operator>> (istream& is, T& v) { - typedef typename tm::Select ::is_integral, - integral_object_reader, object_reader >::Result object_reader_t; - object_reader_t()(is, v); - return is; -} -template -inline istream& operator>> (istream& is, const T& v) { v.read (is); return is; } - -//---------------------------------------------------------------------- - -typedef istream_iterator istream_iterator_for_utf8; -typedef utf8in_iterator utf8istream_iterator; - -/// Returns a UTF-8 adaptor reading from \p is. -inline utf8istream_iterator utf8in (istream& is) -{ - istream_iterator_for_utf8 si (is); - return utf8istream_iterator (si); -} - -//---------------------------------------------------------------------- - -} // namespace ustl diff --git a/common/include/ustl/mostream.h b/common/include/ustl/mostream.h deleted file mode 100644 index 950fb8d0a..000000000 --- a/common/include/ustl/mostream.h +++ /dev/null @@ -1,266 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "memlink.h" -#include "utf8.h" -#include "uios.h" -#include "strmsize.h" -#if WANT_STREAM_BOUNDS_CHECKING - #include "typeinfo.h" -#endif - -namespace ustl { - -class istream; -class string; - -/// \class ostream mostream.h ustl.h -/// \ingroup BinaryStreams -/// -/// \brief Helper class to write packed binary streams. -/// -/// This class contains a set of functions to write integral types into an -/// unstructured memory block. Packing binary file data can be done this -/// way, for instance. aligning the data is your responsibility, and can -/// be accomplished by proper ordering of writes and by calling align. -/// Unaligned access is usually slower by orders of magnitude and, -/// on some architectures, such as PowerPC, can cause your program to crash. -/// Therefore, all write functions have asserts to check alignment. -/// See \ref istream documentation for rules on designing your data format. -/// Overwriting the end of the stream will also cause a crash (an assert in -/// debug builds). Oh, and don't be intimidated by the size of the inlines -/// here. In the assembly code the compiler will usually chop everything down -/// to five instructions each. -/// -/// Example code: -/// \code -/// memblock b; -/// ostream os (b); -/// os << boolVar << ios::talign(); -/// os << intVar << floatVar; -/// os.write (binaryData, binaryDataSize); -/// os.align(); -/// b.resize (os.pos()); -/// b.write_file ("test.file"); -/// \endcode -/// -class ostream : public memlink, public ios_base { -public: - inline ostream (void) : memlink(), _pos(0) {} - inline ostream (void* p, streamsize n) : memlink (p, n), _pos (0) {} - inline explicit ostream (const memlink& source) : memlink (source), _pos (0) {} - inline iterator end (void) { return memlink::end(); } - inline const_iterator end (void) const { return memlink::end(); } - inline void seek (uoff_t newPos); - inline void iseek (const_iterator newPos); - inline void skip (streamsize nBytes); - inline uoff_t pos (void) const { return _pos; } - inline iterator ipos (void) { return begin() + pos(); } - inline const_iterator ipos (void) const { return begin() + pos(); } - inline streamsize remaining (void) const; - inline bool aligned (streamsize grain = c_DefaultAlignment) const; - bool verify_remaining (const char* op, const char* type, size_t n); - inline streamsize align_size (streamsize grain = c_DefaultAlignment) const; - void align (streamsize grain = c_DefaultAlignment); - inline void write (const void* buffer, streamsize size); - inline void write (const cmemlink& buf); - void write_strz (const char* str); - void read (istream& is); - inline void write (ostream& os) const { os.write (begin(), pos()); } - void text_write (ostringstream& os) const; - inline size_t stream_size (void) const { return pos(); } - void insert (iterator start, streamsize size); - void erase (iterator start, streamsize size); - inline void swap (ostream& os); - template - inline void iwrite (const T& v); - inline virtual ostream& flush (void) { return *this; } - inline virtual streamsize overflow (streamsize=1) { return remaining(); } - virtual void unlink (void) noexcept override; - inline void link (void* p, streamsize n) { memlink::link (p, n); } - inline void link (memlink& l) { memlink::link (l.data(), l.writable_size()); } - inline void link (void* f, void* l) { memlink::link (f, l); } - inline void relink (void* p, streamsize n) { memlink::relink (p, n); _pos = 0; } - inline void relink (memlink& l) { relink (l.data(), l.writable_size()); } - inline void seekp (off_t p, seekdir d = beg); - inline off_t tellp (void) const { return pos(); } -protected: - inline void SetPos (uoff_t newPos) { _pos = newPos; } -private: - streamoff _pos; ///< Current write position. -}; - -//---------------------------------------------------------------------- - -/// \class ostream_iterator mostream.h ustl.h -/// \ingroup BinaryStreamIterators -/// -/// \brief An iterator over an ostream to use with uSTL algorithms. -/// -template -class ostream_iterator { -public: - typedef T value_type; - typedef ptrdiff_t difference_type; - typedef value_type* pointer; - typedef value_type& reference; - typedef typename Stream::size_type size_type; - typedef output_iterator_tag iterator_category; -public: - inline explicit ostream_iterator (Stream& os) - : _os (os) {} - inline ostream_iterator (const ostream_iterator& iter) - : _os (iter._os) {} - /// Writes \p v into the stream. - inline ostream_iterator& operator= (const T& v) - { _os << v; return *this; } - inline ostream_iterator& operator* (void) { return *this; } - inline ostream_iterator& operator++ (void) { return *this; } - inline ostream_iterator operator++ (int) { return *this; } - inline ostream_iterator& operator+= (streamsize n) { _os.skip (n); return *this; } - inline bool operator== (const ostream_iterator& i) const - { return _os.pos() == i._os.pos(); } - inline bool operator< (const ostream_iterator& i) const - { return _os.pos() < i._os.pos(); } -private: - Stream& _os; -}; - -//---------------------------------------------------------------------- - -typedef ostream_iterator ostream_iterator_for_utf8; -typedef utf8out_iterator utf8ostream_iterator; - -/// Returns a UTF-8 adaptor writing to \p os. -inline utf8ostream_iterator utf8out (ostream& os) -{ - ostream_iterator_for_utf8 si (os); - return utf8ostream_iterator (si); -} - -//---------------------------------------------------------------------- - -/// Checks that \p n bytes are available in the stream, or else throws. -inline bool ostream::verify_remaining (const char* op, const char* type, size_t n) -{ - const size_t rem = remaining(); - bool enough = n <= rem; - if (!enough) overrun (op, type, n, pos(), rem); - return enough; -} - -/// Move the write pointer to \p newPos -inline void ostream::seek (uoff_t newPos) -{ -#if WANT_STREAM_BOUNDS_CHECKING - if (newPos > size()) - return overrun ("seekp", "byte", newPos, pos(), size()); -#else - assert (newPos <= size()); -#endif - SetPos (newPos); -} - -/// Sets the current write position to \p newPos -inline void ostream::iseek (const_iterator newPos) -{ - seek (distance (begin(), const_cast(newPos))); -} - -/// Sets the current write position to \p p based on \p d. -inline void ostream::seekp (off_t p, seekdir d) -{ - switch (d) { - case beg: seek (p); break; - case cur: seek (pos() + p); break; - case ios_base::end: seek (size() - p); break; - } -} - -/// Skips \p nBytes without writing anything. -inline void ostream::skip (streamsize nBytes) -{ - seek (pos() + nBytes); -} - -/// Returns number of bytes remaining in the write buffer. -inline streamsize ostream::remaining (void) const -{ - return size() - pos(); -} - -/// Returns \c true if the write pointer is aligned on \p grain -inline bool ostream::aligned (streamsize grain) const -{ - return pos() % grain == 0; -} - -/// Returns the number of bytes to skip to be aligned on \p grain. -inline streamsize ostream::align_size (streamsize grain) const -{ - return Align (pos(), grain) - pos(); -} - -/// Writes \p n bytes from \p buffer. -inline void ostream::write (const void* buffer, size_type n) -{ -#if WANT_STREAM_BOUNDS_CHECKING - if (!verify_remaining ("write", "binary data", n)) - return; -#else - assert (remaining() >= n && "Buffer overrun. Check your stream size calculations."); -#endif - memcpy (ipos(), const_iterator(buffer), n); - _pos += n; -} - -/// Writes the contents of \p buf into the stream as a raw dump. -inline void ostream::write (const cmemlink& buf) -{ - write (buf.begin(), buf.size()); -} - -/// Writes type T into the stream via a direct pointer cast. -template -inline void ostream::iwrite (const T& v) -{ - assert (aligned (stream_align_of (v))); -#if WANT_STREAM_BOUNDS_CHECKING - if (!verify_remaining ("write", typeid(v).name(), sizeof(T))) - return; -#else - assert (remaining() >= sizeof(T)); -#endif - *reinterpret_cast(ipos()) = v; - SetPos (pos() + sizeof(T)); -} - -/// Swaps with \p os -inline void ostream::swap (ostream& os) -{ - memlink::swap (os); - ::ustl::swap (_pos, os._pos); -} - -//---------------------------------------------------------------------- - -template struct object_writer { - inline void operator()(ostream& os, const T& v) const { v.write (os); } -}; -template struct integral_object_writer { - inline void operator()(ostream& os, const T& v) const { os.iwrite (v); } -}; -template -inline ostream& operator<< (ostream& os, const T& v) { - typedef typename tm::Select ::is_integral, - integral_object_writer, object_writer >::Result object_writer_t; - object_writer_t()(os, v); - return os; -} - -//---------------------------------------------------------------------- - -} // namespace ustl diff --git a/common/include/ustl/outerrstream.h b/common/include/ustl/outerrstream.h deleted file mode 100644 index 3bfa96850..000000000 --- a/common/include/ustl/outerrstream.h +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once - -#include "sostream.h" -#include "kprintf.h" -#include "uios.h" - -namespace ustl { - - -/// \class coutclass sostream.h ustl.h -/// \ingroup TextStreams -/// -/// \brief This stream writes textual data into a memory block. -/// -class coutclass : public ostream { - public: - static void init(); - coutclass(); - coutclass(void* p, size_t n); - coutclass(void(*m_kprintf)(const char*, ...)); - coutclass(void* p, size_t n, void(*m_kprintf)(const char*, ...)); - void iwrite (uint8_t v); - void iwrite (wchar_t v); - inline void iwrite (int v) { iformat (v); } - inline void iwrite (unsigned int v) { iformat (v); } - inline void iwrite (long int v) { iformat (v); } - inline void iwrite (unsigned long int v) { iformat (v); } - void iwrite (bool v); - inline void iwrite (const char* s) { write (s, strlen(s)); } - inline void iwrite (const string& v) { write (v.begin(), v.size()); } - inline void iwrite (fmtflags f); -#if HAVE_LONG_LONG - inline void iwrite (long long v) { iformat (v); } - inline void iwrite (unsigned long long v) { iformat (v); } -#endif - inline size_type max_size (void) const { return 0; } - inline coutclass& put (char c) { iwrite (uint8_t(c)); return (*this); } - int format (const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))); - inline void set_base (uint16_t b) { m_Base = b; } - inline void set_width (uint16_t w) { m_Width = w; } - inline void set_decimal_separator (char) { } - inline void set_thousand_separator (char) { } - inline void set_precision (uint16_t v) { m_Precision = v; } - void link (void* p, size_type n); - inline void link (memlink& l) { link (l.data(), l.writable_size()); } - void str (const string& s); - coutclass& write (const void* buffer, size_type size); - inline coutclass& write (const cmemlink& buf) { return (write (buf.begin(), buf.size())); } - inline coutclass& seekp (off_t p __attribute__((unused)), __attribute__((unused)) seekdir d =beg) { return (*this); } - coutclass& flush (void) { return (*this); } - virtual size_type overflow (size_type n = 1); -protected: - inline void reserve (__attribute__((unused)) size_type n) { } - inline size_type capacity (void) const { return 0; } -private: - inline void write_strz (const char*) { assert (!"Writing nul characters into a text stream is not allowed"); } - inline char* encode_dec (char* fmt, uint32_t n) const; - void fmtstring (char* fmt, const char* typestr, bool bInteger) const; - template - void iformat (T v); -private: - uint32_t m_Flags; ///< See ios_base::fmtflags. - uint16_t m_Width; ///< Field width. - uint8_t m_Base; ///< Numeric base for writing numbers. - uint8_t m_Precision; ///< Number of digits after the decimal separator. - void (*m_kprintf)(const char*, ...); -}; - -//---------------------------------------------------------------------- - -template -void coutclass::iformat (T v) -{ - char fmt [16]; - fmtstring (fmt, printf_typestring(v), numeric_limits::is_integer); - kprintf(fmt, v); -} - -/// Sets the flag \p f in the stream. -inline void coutclass::iwrite (fmtflags f) -{ - switch (f.f) { - case oct: set_base (8); break; - case dec: set_base (10); break; - case hex: set_base (16); break; - case left: m_Flags |= left; m_Flags &= ~right; break; - case right: m_Flags |= right; m_Flags &= ~left; break; - default: m_Flags |= f.f; break; - } -} - -//---------------------------------------------------------------------- - -template struct object_text_writer_cout { - inline void operator()(coutclass& os, const T& v) const { v.text_write (os); } -}; -template struct integral_text_object_writer_cout { - inline void operator()(coutclass& os, const T& v) const { os.iwrite (v); } -}; -template -inline coutclass& operator<< (coutclass& os, const T& v) { - typedef typename tm::Select ::is_integral, - integral_text_object_writer_cout, object_text_writer_cout >::Result object_writer_t; - object_writer_t()(os, v); - return (os); -} -// Needed because if called with a char[], numeric_limits will not work. Should be removed if I find out how to partial specialize for arrays... -inline coutclass& operator<< (coutclass& os, const char* v) - { os.iwrite (v); return (os); } -inline coutclass& operator<< (coutclass& os, char* v) - { os.iwrite (v); return (os); } - -//---------------------------------------------------------------------- - -template <> struct object_text_writer_cout { - inline void operator()(coutclass& os, const string& v) const { os.iwrite (v); } -}; -template struct integral_text_object_writer_cout { - inline void operator() (coutclass& os, const T* const& v) const - { os.iwrite ((uintptr_t)(v)); } -}; -#define COUTCLASS_CAST_OPERATOR(RealT, CastT) \ -template <> inline coutclass& operator<< (coutclass& os, const RealT& v) \ - { os.iwrite ((CastT)(v)); return (os); } -COUTCLASS_CAST_OPERATOR (uint8_t* const, const char*) -COUTCLASS_CAST_OPERATOR (int8_t, uint8_t) -COUTCLASS_CAST_OPERATOR (short int, int) -COUTCLASS_CAST_OPERATOR (unsigned short, unsigned int) -#if HAVE_THREE_CHAR_TYPES -COUTCLASS_CAST_OPERATOR (char, uint8_t) -#endif -#undef COUTCLASS_CAST_OPERATOR - -}; - diff --git a/common/include/ustl/sistream.h b/common/include/ustl/sistream.h deleted file mode 100644 index 3c2c4acb6..000000000 --- a/common/include/ustl/sistream.h +++ /dev/null @@ -1,132 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "mistream.h" -#include "ustring.h" -#ifndef EOF - #define EOF (-1) -#endif - -namespace ustl { - -/// \class istringstream sistream.h ustl.h -/// \ingroup TextStreams -/// -/// \brief A stream that reads textual data from a memory block. -/// -class istringstream : public istream { -public: - static const size_type c_MaxDelimiters = 16; ///< Maximum number of word delimiters. -public: - istringstream (void) noexcept; - istringstream (const void* p, size_type n) noexcept; - explicit istringstream (const cmemlink& source) noexcept; - inline fmtflags flags (void) const { return _flags; } - inline fmtflags flags (fmtflags f) { fmtflags of (_flags); _flags.f = f.f; return of; } - inline fmtflags setf (fmtflags f) { fmtflags of (_flags); _flags.f |= f.f; return of; } - inline fmtflags unsetf (fmtflags f) { fmtflags of (_flags); _flags.f &= ~f.f; return of; } - inline fmtflags setf (fmtflags f, fmtflags m) { unsetf(m); return setf(f); } - inline void iread (char& v) { v = skip_delimiters(); } - inline void iread (unsigned char& v) { char c; iread(c); v = c; } - void iread (int& v); - inline void iread (unsigned int& v) { int c; iread(c); v = c; } - inline void iread (short& v) { int c; iread(c); v = c; } - inline void iread (unsigned short& v) { int c; iread(c); v = c; } - void iread (long& v); - inline void iread (unsigned long& v) { long c; iread(c); v = c; } -#if HAVE_THREE_CHAR_TYPES - void iread (signed char& v) { char c; iread(c); v = c; } -#endif -#if HAVE_LONG_LONG - void iread (long long& v); - inline void iread (unsigned long long& v) { long long c; iread(c); v = c; } -#endif - /*inline void iread (float& v) { double c; iread(c); v = c; } - inline void iread (long double& v) { double c; iread(c); v = c; }*/ - inline void iread (fmtflags_bits f); - /*void iread (double& v); - inline void iread (float& v) { double c; iread(c); v = c; } - inline void iread (long double& v) { double c; iread(c); v = c; }*/ - void iread (bool& v); - void iread (wchar_t& v); - void iread (string& v); - inline string str (void) const { string s; s.link (*this); return s; } - inline istringstream& str (const string& s) { link (s); return *this; } - inline istringstream& get (char& c) { return read (&c, sizeof(c)); } - inline int get (void) { char c = EOF; get(c); return c; } - istringstream& get (char* p, size_type n, char delim = '\n'); - istringstream& get (string& s, char delim = '\n'); - istringstream& getline (char* p, size_type n, char delim = '\n'); - istringstream& getline (string& s, char delim = '\n'); - istringstream& ignore (size_type n, char delim = '\0'); - inline char peek (void) { char v = get(); ungetc(); return v; } - inline istringstream& unget (void) { ungetc(); return *this; } - inline void set_delimiters (const char* delimiters); - istringstream& read (void* buffer, size_type size); - inline istringstream& read (memlink& buf) { return read (buf.begin(), buf.size()); } - inline size_type gcount (void) const { return _gcount; } - inline istringstream& seekg (off_t p, seekdir d =beg) { istream::seekg(p,d); return *this; } - inline int sync (void) { skip (remaining()); return 0; } -protected: - char skip_delimiters (void); -private: - inline void read_strz (string&) { assert (!"Reading nul characters is not allowed from text streams"); } - inline bool is_delimiter (char c) const noexcept; - template void read_number (T& v); -private: - fmtflags _flags; - uint32_t _gcount; - char _delimiters [c_MaxDelimiters]; -}; - -//---------------------------------------------------------------------- - -void istringstream::iread (fmtflags_bits f) -{ - if (f & basefield) setf (f, basefield); - else if (f & floatfield) setf (f, floatfield); - else if (f & adjustfield) setf (f, adjustfield); - setf (f); -} - -/// Sets delimiters to the contents of \p delimiters. -void istringstream::set_delimiters (const char* delimiters) -{ -#if __x86__ && __SSE__ && HAVE_VECTOR_EXTENSIONS - typedef uint32_t v16ud_t __attribute__((vector_size(16))); - asm("xorps\t%%xmm0, %%xmm0\n\tmovups\t%%xmm0, %0":"=m"(*noalias_cast(_delimiters))::"xmm0"); -#else - memset (_delimiters, 0, sizeof(_delimiters)); -#endif - memcpy (_delimiters, delimiters, min (strlen(delimiters),sizeof(_delimiters)-1)); -} - -/// Reads a line of text from \p is into \p s -inline istringstream& getline (istringstream& is, string& s, char delim = '\n') - { return is.getline (s, delim); } - -//---------------------------------------------------------------------- - -template struct object_text_reader { - inline void operator()(istringstream& is, T& v) const { v.text_read (is); } -}; -template struct integral_text_object_reader { - inline void operator()(istringstream& is, T& v) const { is.iread (v); } -}; -template -inline istringstream& operator>> (istringstream& is, T& v) { - typedef typename tm::Select ::is_integral, - integral_text_object_reader, object_text_reader >::Result object_reader_t; - object_reader_t()(is, v); - return is; -} - -//---------------------------------------------------------------------- - -template <> struct object_text_reader { - inline void operator()(istringstream& is, string& v) const { is.iread (v); } -}; -} // namespace ustl diff --git a/common/include/ustl/sostream.h b/common/include/ustl/sostream.h deleted file mode 100644 index 01ff6688b..000000000 --- a/common/include/ustl/sostream.h +++ /dev/null @@ -1,213 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "ustring.h" -#include "mostream.h" - -namespace ustl { - -class string; - -/// \class ostringstream sostream.h ustl.h -/// \ingroup TextStreams -/// -/// \brief This stream writes textual data into a memory block. -/// -class ostringstream : public ostream { -public: - ostringstream (const string& v = ""); - ostringstream (void* p, size_t n) noexcept; - inline fmtflags flags (void) const { return _flags; } - inline fmtflags flags (fmtflags f) { fmtflags of (_flags); _flags.f = f.f; return of; } - inline fmtflags setf (fmtflags f) { fmtflags of (_flags); _flags.f |= f.f; return of; } - inline fmtflags unsetf (fmtflags f) { fmtflags of (_flags); _flags.f &= ~f.f; return of; } - inline fmtflags setf (fmtflags f, fmtflags m) { unsetf(m); return setf(f); } - void iwrite (unsigned char v); - void iwrite (wchar_t v); - inline void iwrite (char v) { iwrite (static_cast(v)); } - inline void iwrite (short v) { iformat (v); } - inline void iwrite (unsigned short v) { iformat (v); } - inline void iwrite (int v) { iformat (v); } - inline void iwrite (unsigned int v) { iformat (v); } - inline void iwrite (long int v) { iformat (v); } - inline void iwrite (unsigned long int v) { iformat (v); } - /*inline void iwrite (float v) { iformat (v); } - inline void iwrite (double v) { iformat (v); } - inline void iwrite (long double v) { iformat (v); }*/ - void iwrite (bool v); - inline void iwrite (const char* s) { write (s, strlen(s)); } - inline void iwrite (const unsigned char* s) { iwrite (reinterpret_cast(s)); } - inline void iwrite (const string& v) { write (v.begin(), v.size()); } - inline void iwrite (fmtflags_bits f); -#if HAVE_THREE_CHAR_TYPES - inline void iwrite (signed char v) { iwrite (static_cast(v)); } -#endif -#if HAVE_LONG_LONG - inline void iwrite (long long v) { iformat (v); } - inline void iwrite (unsigned long long v) { iformat (v); } -#endif - inline size_type max_size (void) const { return _buffer.max_size(); } - inline ostringstream& put (char c) { iwrite (uint8_t(c)); return *this; } - int vformat (const char* fmt, va_list args); - int format (const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))); - inline uint16_t width (void) const { return _width; } - inline void width (uint16_t w) { _width = w; } - inline void set_width (uint16_t w) { _width = w; } - inline char fill (void) const { return _fill; } - inline void fill (char c) { _fill = c; } - inline uint8_t precision (void) const { return _precision; } - inline void precision (uint8_t v) { _precision = v; } - inline void set_precision (uint8_t v) { _precision = v; } - void link (void* p, size_type n) noexcept; - inline void link (memlink& l) { link (l.data(), l.writable_size()); } - inline const string& str (void) { flush(); return _buffer; } - void str (const string& s); - ostringstream& write (const void* buffer, size_type size); - inline ostringstream& write (const cmemlink& buf) { return write (buf.begin(), buf.size()); } - inline ostringstream& seekp (off_t p, seekdir d =beg) { ostream::seekp(p,d); return *this; } - virtual ostream& flush (void) override { ostream::flush(); _buffer.resize (pos()); return *this; } - virtual size_type overflow (size_type n = 1) override; -protected: - inline void reserve (size_type n) { _buffer.reserve (n, false); } - inline size_type capacity (void) const { return _buffer.capacity(); } -private: - inline void write_strz (const char*) { assert (!"Writing nul characters into a text stream is not allowed"); } - inline char* encode_dec (char* fmt, uint32_t n) const noexcept; - void fmtstring (char* fmt, const char* typestr, bool bInteger) const; - template - void iformat (T v); -private: - string _buffer; ///< The output buffer. - fmtflags _flags; ///< See ios_base::fmtflags. - uint16_t _width; ///< Field width. - uint8_t _precision; ///< Number of digits after the decimal separator. - char _fill; ///< Character for padding variable width fields (space or 0 only) -}; - -//---------------------------------------------------------------------- - -template -inline const char* printf_typestring (const T&) { return ""; } -#define PRINTF_TYPESTRING_SPEC(type,str) \ -template <> inline const char* printf_typestring (const type&) { return str; } -PRINTF_TYPESTRING_SPEC (short, "hd") -PRINTF_TYPESTRING_SPEC (unsigned short, "hu") -PRINTF_TYPESTRING_SPEC (int, "d") -PRINTF_TYPESTRING_SPEC (unsigned int, "u") -PRINTF_TYPESTRING_SPEC (long, "ld") -PRINTF_TYPESTRING_SPEC (unsigned long, "lu") -/*PRINTF_TYPESTRING_SPEC (float, "f") -PRINTF_TYPESTRING_SPEC (double, "lf") -PRINTF_TYPESTRING_SPEC (long double, "Lf")*/ -#if HAVE_LONG_LONG -PRINTF_TYPESTRING_SPEC (long long, "lld") -PRINTF_TYPESTRING_SPEC (unsigned long long, "llu") -#endif -#undef PRINTF_TYPESTRING_SPEC - -template -void ostringstream::iformat (T v) -{ - char fmt [16]; - fmtstring (fmt, printf_typestring(v), numeric_limits::is_integer); - format (fmt, v); -} - -void ostringstream::iwrite (fmtflags_bits f) -{ - if (f & basefield) setf (f, basefield); - else if (f & floatfield) setf (f, floatfield); - else if (f & adjustfield) setf (f, adjustfield); - setf (f); -} - -//---------------------------------------------------------------------- - -template struct object_text_writer { - inline void operator()(ostringstream& os, const T& v) const { v.text_write (os); } -}; -template struct integral_text_object_writer { - inline void operator()(ostringstream& os, const T& v) const { os.iwrite (v); } -}; -template -inline ostringstream& operator<< (ostringstream& os, const T& v) { - typedef typename tm::Select ::isFundamental - || tm::TypeTraits::isPointer - || tm::Conversion::exists, - integral_text_object_writer, object_text_writer >::Result object_writer_t; - object_writer_t()(os, v); - return os; -} -// Needed because if called with a char[], numeric_limits will not work. Should be removed if I find out how to partial specialize for arrays... -inline ostringstream& operator<< (ostringstream& os, const char* v) - { os.iwrite (v); return os; } -inline ostringstream& operator<< (ostringstream& os, char* v) - { os.iwrite (v); return os; } - -//---------------------------------------------------------------------- -// Object writer operators - -template <> struct object_text_writer { - inline void operator()(ostringstream& os, const string& v) const { os.iwrite (v); } -}; -template struct integral_text_object_writer { - inline void operator() (ostringstream& os, const T* const& v) const - { os.iwrite (uintptr_t(v)); } -}; - -//---------------------------------------------------------------------- -// Manipulators - -namespace { -static constexpr const struct Sendl { - inline constexpr Sendl (void) {} - inline void text_write (ostringstream& os) const { os << '\n'; os.flush(); } - inline void write (ostream& os) const { os.iwrite ('\n'); } -} endl; -static constexpr const struct Sflush { - inline constexpr Sflush (void) {} - inline void text_write (ostringstream& os) const { os.flush(); } - inline void write (ostringstream& os) const { os.flush(); } - inline void write (ostream&) const { } -} flush; -constexpr const char ends = '\0'; ///< End of string character. -} // namespace - -struct setiosflags { - inline constexpr setiosflags (ios_base::fmtflags f) : _f(f) {} - inline void text_write (ostringstream& os) const { os.setf(_f); } -private: - const ios_base::fmtflags _f; -}; -struct resetiosflags { - inline constexpr resetiosflags (ios_base::fmtflags f) : _f(f) {} - inline void text_write (ostringstream& os) const { os.unsetf(_f); } -private: - const ios_base::fmtflags _f; -}; -class setw { - uint16_t _w; -public: - inline constexpr setw (uint16_t w) : _w(w) {} - inline void text_write (ostringstream& os) const { os.width(_w); } - inline void write (ostringstream& os) const { os.width(_w); } -}; -class setfill { - char _c; -public: - inline constexpr setfill (char c) : _c(c) {} - inline void text_write (ostringstream& os) const { os.fill(_c); } - inline void write (ostringstream& os) const { os.fill(_c); } -}; -class setprecision { - uint8_t _p; -public: - inline constexpr setprecision (uint8_t p) : _p(p) {} - inline void text_write (ostringstream& os) const { os.precision(_p); } - inline void write (ostringstream& os) const { os.precision(_p); } -}; - -} // namespace ustl diff --git a/common/include/ustl/strmsize.h b/common/include/ustl/strmsize.h deleted file mode 100644 index d50265603..000000000 --- a/common/include/ustl/strmsize.h +++ /dev/null @@ -1,92 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. -// -/// \file strmsize.h -/// \brief This file contains stream_size_of functions for basic types and *STREAMABLE macros. -/// stream_size_of functions return the size of the object's data that is written or -/// read from a stream. - -#pragma once - -namespace ustl { - -/// For partial specialization of stream_size_of for objects -template struct object_stream_size { - inline streamsize operator()(const T& v) const { return v.stream_size(); } -}; -template struct integral_object_stream_size { - inline streamsize operator()(const T& v) const { return sizeof(v); } -}; -/// Returns the size of the given object. Overloads for standard types are available. -template -inline streamsize stream_size_of (const T& v) { - typedef typename tm::Select ::is_integral, - integral_object_stream_size, object_stream_size >::Result stream_sizer_t; - return stream_sizer_t()(v); -} - -/// \brief Returns the recommended stream alignment for type \p T. Override with ALIGNOF. -/// Because this is occasionally called with a null value, do not access the argument! -template -inline size_t stream_align_of (const T&) -{ - if (numeric_limits::is_integral) - return __alignof__(T); - return 4; -} - -#define ALIGNOF(type,grain) \ -namespace ustl { \ - template <> inline size_t stream_align_of (const type&) { return grain; } } - -} // namespace ustl - -// -// Extra overloads in this macro are needed because it is the one used for -// marshalling pointers. Passing a pointer to stream_size_of creates a -// conversion ambiguity between converting to const pointer& and converting -// to bool; the compiler always chooses the bool conversion (because it -// requires 1 conversion instead of 2 for the other choice). There is little -// point in adding the overloads to other macros, since they are never used -// for pointers. -// -/// Declares that T is to be written as is into binary streams. -#define INTEGRAL_STREAMABLE(T) \ - namespace ustl { \ - inline istream& operator>> (istream& is, T& v) { is.iread(v); return is; } \ - inline ostream& operator<< (ostream& os, const T& v) { os.iwrite(v); return os; } \ - inline ostream& operator<< (ostream& os, T& v) { os.iwrite(v); return os; } \ - template<> inline streamsize stream_size_of(const T& v) { return sizeof(v); } \ - } - -/// Declares that T contains read, write, and stream_size methods. This is no longer needed and is deprecated. -#define STD_STREAMABLE(T) - -/// Declares \p T to be writable to text streams. This is no longer needed and is deprecated. -#define TEXT_STREAMABLE(T) - -/// Declares that T is to be cast into TSUB for streaming. -#define CAST_STREAMABLE(T,TSUB) \ - namespace ustl { \ - inline istream& operator>> (istream& is, T& v) { TSUB sv; is >> sv; v = T(sv); return is; } \ - inline ostream& operator<< (ostream& os, const T& v) { os << TSUB(v); return os; } \ - template<> inline streamsize stream_size_of(const T& v) { return stream_size_of (TSUB(v)); } \ - } - -/// Placed into a class it declares the methods required by STD_STREAMABLE. Syntactic sugar. -#define DECLARE_STD_STREAMABLE \ - public: \ - void read (istream& is); \ - void write (ostream& os) const; \ - streamsize stream_size (void) const - -/// Specifies that \p T is printed by using it as an index into \p Names string array. -#define LOOKUP_TEXT_STREAMABLE(T,Names,nNames) \ - namespace ustl { \ - inline ostringstream& operator<< (ostringstream& os, const T& v) { \ - os << Names[min(uoff_t(v),uoff_t(nNames-1))]; \ - return os; \ - } \ - } diff --git a/common/include/ustl/traits.h b/common/include/ustl/traits.h deleted file mode 100644 index fbbf875bf..000000000 --- a/common/include/ustl/traits.h +++ /dev/null @@ -1,266 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2007 by Mike Sharov -// -// This implementation is adapted from the Loki library, distributed under -// the MIT license with Copyright (c) 2001 by Andrei Alexandrescu. - -#pragma once -#include "typelist.h" - -namespace ustl { -namespace tm { - -//---------------------------------------------------------------------- -// Type classes and type modifiers -//---------------------------------------------------------------------- - -typedef tl::Seq::Type - StdUnsignedInts; -typedef tl::Seq::Type StdSignedInts; -typedef tl::Seq::Type StdOtherInts; -typedef tl::Seq::Type StdFloats; - -template struct Identity { typedef U Result; }; -template struct AddPointer { typedef U* Result; }; -template struct AddPointer { typedef U* Result; }; -template struct AddReference { typedef U& Result; }; -template struct AddReference { typedef U& Result; }; -template <> struct AddReference { typedef NullType Result; }; -template struct AddParameterType { typedef const U& Result; }; -template struct AddParameterType { typedef U& Result; }; -template <> struct AddParameterType { typedef NullType Result; }; -template struct RemoveReference { typedef U Result; }; -template struct RemoveReference { typedef U Result; }; -#if HAVE_CPP11 -template struct RemoveReference { typedef U Result; }; -#endif -template struct EnableIf { typedef void Result; }; -template struct EnableIf { typedef T Result; }; - - -//---------------------------------------------------------------------- -// Function pointer testers -//---------------------------------------------------------------------- -// Macros expand to numerous parameters - -template -struct IsFunctionPointerRaw { enum { result = false}; }; -template -struct IsMemberFunctionPointerRaw { enum { result = false}; }; - -#define TM_FPR_MAXN 9 -#define TM_FPR_TYPE(n) PASTE(T,n) -#define TM_FPR_TYPENAME(n) typename TM_FPR_TYPE(n) - -// First specialize for regular functions -template -struct IsFunctionPointerRaw -{enum {result = true};}; - -#define TM_FPR_SPEC(n) \ -template \ -struct IsFunctionPointerRaw \ -{ enum { result = true }; } - -LIST (TM_FPR_MAXN, TM_FPR_SPEC, ;); - -// Then for those with an ellipsis argument -template -struct IsFunctionPointerRaw -{enum {result = true};}; - -#define TM_FPR_SPEC_ELLIPSIS(n) \ -template \ -struct IsFunctionPointerRaw \ -{ enum { result = true }; } - -LIST (TM_FPR_MAXN, TM_FPR_SPEC_ELLIPSIS, ;); - -// Then for member function pointers -template -struct IsMemberFunctionPointerRaw -{ enum { result = true }; }; - -#define TM_MFPR_SPEC(n) \ -template \ -struct IsMemberFunctionPointerRaw \ -{ enum { result = true };}; - -LIST (TM_FPR_MAXN, TM_MFPR_SPEC, ;); - -// Then for member function pointers with an ellipsis argument -template -struct IsMemberFunctionPointerRaw -{ enum { result = true }; }; - -#define TM_MFPR_SPEC_ELLIPSIS(n) \ -template \ -struct IsMemberFunctionPointerRaw \ -{ enum { result = true }; }; - -LIST (TM_FPR_MAXN, TM_MFPR_SPEC_ELLIPSIS, ;); - -// Then for const member function pointers (getting tired yet?) -template -struct IsMemberFunctionPointerRaw -{ enum { result = true }; }; - -#define TM_CMFPR_SPEC(n) \ -template \ -struct IsMemberFunctionPointerRaw \ -{ enum { result = true };}; - -LIST (TM_FPR_MAXN, TM_CMFPR_SPEC, ;); - -// Finally for const member function pointers with an ellipsis argument (whew!) -template -struct IsMemberFunctionPointerRaw -{ enum { result = true }; }; - -#define TM_CMFPR_SPEC_ELLIPSIS(n) \ -template \ -struct IsMemberFunctionPointerRaw \ -{ enum { result = true }; }; - -LIST (TM_FPR_MAXN, TM_CMFPR_SPEC_ELLIPSIS, ;); - -#undef TM_FPR_SPEC -#undef TM_FPR_SPEC_ELLIPSIS -#undef TM_MFPR_SPEC -#undef TM_MFPR_SPEC_ELLIPSIS -#undef TM_CMFPR_SPEC -#undef TM_CMFPR_SPEC_ELLIPSIS -#undef TM_FPR_TYPENAME -#undef TM_FPR_TYPE -#undef TM_FPR_MAXN - -//---------------------------------------------------------------------- -// Type traits template -//---------------------------------------------------------------------- - -/// Figures out at compile time various properties of any given type -/// Invocations (T is a type, TypeTraits::Propertie): -/// -/// - isPointer : returns true if T is a pointer type -/// - PointeeType : returns the type to which T points if T is a pointer -/// type, NullType otherwise -/// - isReference : returns true if T is a reference type -/// - isLValue : returns true if T is an lvalue -/// - isRValue : returns true if T is an rvalue -/// - ReferredType : returns the type to which T refers if T is a reference -/// type, NullType otherwise -/// - isMemberPointer : returns true if T is a pointer to member type -/// - isStdUnsignedInt: returns true if T is a standard unsigned integral type -/// - isStdSignedInt : returns true if T is a standard signed integral type -/// - isStdIntegral : returns true if T is a standard integral type -/// - isStdFloat : returns true if T is a standard floating-point type -/// - isStdArith : returns true if T is a standard arithmetic type -/// - isStdFundamental: returns true if T is a standard fundamental type -/// - isUnsignedInt : returns true if T is a unsigned integral type -/// - isSignedInt : returns true if T is a signed integral type -/// - isIntegral : returns true if T is a integral type -/// - isFloat : returns true if T is a floating-point type -/// - isArith : returns true if T is a arithmetic type -/// - isFundamental : returns true if T is a fundamental type -/// - ParameterType : returns the optimal type to be used as a parameter for -/// functions that take Ts -/// - isConst : returns true if T is a const-qualified type -/// - NonConstType : Type with removed 'const' qualifier from T, if any -/// - isVolatile : returns true if T is a volatile-qualified type -/// - NonVolatileType : Type with removed 'volatile' qualifier from T, if any -/// - UnqualifiedType : Type with removed 'const' and 'volatile' qualifiers from -/// T, if any -/// - ConstParameterType: returns the optimal type to be used as a parameter -/// for functions that take 'const T's -/// -template -class TypeTraits { -private: - #define TMTT1 template struct - #define TMTT2 template struct - TMTT1 ReferenceTraits { enum { result = false, lvalue = true, rvalue = false }; typedef U ReferredType; }; - TMTT1 ReferenceTraits { enum { result = true, lvalue = true, rvalue = false }; typedef U ReferredType; }; - TMTT1 PointerTraits { enum { result = false }; typedef NullType PointeeType; }; - TMTT1 PointerTraits { enum { result = true }; typedef U PointeeType; }; - TMTT1 PointerTraits { enum { result = true }; typedef U PointeeType; }; - TMTT1 PToMTraits { enum { result = false }; }; - TMTT2 PToMTraits { enum { result = true }; }; - TMTT2 PToMTraits { enum { result = true }; }; - TMTT1 FunctionPointerTraits { enum { result = IsFunctionPointerRaw::result }; }; - TMTT1 PToMFunctionTraits { enum { result = IsMemberFunctionPointerRaw::result }; }; - TMTT1 UnConst { typedef U Result; enum { isConst = false }; }; - TMTT1 UnConst { typedef U Result; enum { isConst = true }; }; - TMTT1 UnConst { typedef U& Result; enum { isConst = true }; }; - TMTT1 UnVolatile { typedef U Result; enum { isVolatile = false }; }; - TMTT1 UnVolatile{ typedef U Result; enum { isVolatile = true }; }; - TMTT1 UnVolatile {typedef U& Result;enum { isVolatile = true }; }; -#if HAVE_CPP11 - TMTT1 ReferenceTraits { enum { result = true, lvalue = false, rvalue = true }; typedef U ReferredType; }; - TMTT1 PointerTraits { enum { result = true }; typedef U PointeeType; }; - TMTT2 PToMTraits { enum { result = true }; }; - TMTT1 UnConst { typedef U&& Result; enum { isConst = true }; }; - TMTT1 UnVolatile {typedef U&& Result;enum { isVolatile = true }; }; -#endif - #undef TMTT2 - #undef TMTT1 -public: - typedef typename UnConst::Result - NonConstType; - typedef typename UnVolatile::Result - NonVolatileType; - typedef typename UnVolatile::Result>::Result - UnqualifiedType; - typedef typename PointerTraits::PointeeType - PointeeType; - typedef typename ReferenceTraits::ReferredType - ReferredType; - - enum { isConst = UnConst::isConst }; - enum { isVolatile = UnVolatile::isVolatile }; - enum { isReference = ReferenceTraits::result }; - enum { isLValue = ReferenceTraits::lvalue }; - enum { isRValue = ReferenceTraits::rvalue }; - enum { isFunction = FunctionPointerTraits::Result >::result }; - enum { isFunctionPointer = FunctionPointerTraits< - typename ReferenceTraits::ReferredType >::result }; - enum { isMemberFunctionPointer= PToMFunctionTraits< - typename ReferenceTraits::ReferredType >::result }; - enum { isMemberPointer = PToMTraits< - typename ReferenceTraits::ReferredType >::result || - isMemberFunctionPointer }; - enum { isPointer = PointerTraits< - typename ReferenceTraits::ReferredType >::result || - isFunctionPointer }; - enum { isStdUnsignedInt = tl::IndexOf::value >= 0 || - tl::IndexOf::ReferredType>::value >= 0}; - enum { isStdSignedInt = tl::IndexOf::value >= 0 || - tl::IndexOf::ReferredType>::value >= 0}; - enum { isStdIntegral = isStdUnsignedInt || isStdSignedInt || - tl::IndexOf::value >= 0 || - tl::IndexOf::ReferredType>::value >= 0}; - enum { isStdFloat = tl::IndexOf::value >= 0 || - tl::IndexOf::ReferredType>::value >= 0}; - enum { isStdArith = isStdIntegral || isStdFloat }; - enum { isStdFundamental = isStdArith || isStdFloat || Conversion::sameType }; - - enum { isUnsignedInt = isStdUnsignedInt }; - enum { isUnsigned = isUnsignedInt || isPointer }; - enum { isSignedInt = isStdSignedInt }; - enum { isIntegral = isStdIntegral || isUnsignedInt || isSignedInt }; - enum { isFloat = isStdFloat }; - enum { isSigned = isSignedInt || isFloat }; - enum { isArith = isIntegral || isFloat }; - enum { isFundamental = isStdFundamental || isArith }; - - typedef typename Select::Result>::Result - ParameterType; -}; - -} // namespace tm -} // namespace ustl diff --git a/common/include/ustl/typelist.h b/common/include/ustl/typelist.h deleted file mode 100644 index b881f52f2..000000000 --- a/common/include/ustl/typelist.h +++ /dev/null @@ -1,219 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2007 by Mike Sharov -// -// This implementation is adapted from the Loki library, distributed under -// the MIT license with Copyright (c) 2001 by Andrei Alexandrescu. - -#pragma once -#include "metamac.h" -#include "typet.h" - -namespace ustl { -namespace tm { - -/// The building block of typelists. Use it throught the Seq templates. -template -struct Typelist { - typedef T Head; - typedef U Tail; -}; - -/// Namespace containing typelist-related functionality. -namespace tl { - -//---------------------------------------------------------------------- -// Seq template definitions. The macros expand to a spec per arg count -// -#define TL_MAX_SEQ_TYPES 9 -#define TL_MAX_SEQ_SPECS 8 -#define TL_SEQ_TYPE(n) T##n -#define TL_SEQ_TYPENAME(n) typename TL_SEQ_TYPE(n) -#define TL_SEQ_NULLTYPE_DEFAULT(n) TL_SEQ_TYPENAME(n)=NullType -#define TL_SEQ_TL_END(n) > -#define TL_SEQ_ONE_TYPELIST(n) Typelist -struct Seq { - typedef COMMA_LIST(TL_MAX_SEQ_TYPES,TL_SEQ_ONE_TYPELIST), - NullType REPEAT(TL_MAX_SEQ_TYPES,TL_SEQ_TL_END) Type; -}; - -#define TL_SEQ_SPEC(n) \ -template \ -struct Seq { \ - typedef COMMA_LIST(n,TL_SEQ_ONE_TYPELIST), \ - NullType REPEAT(n,TL_SEQ_TL_END) Type; \ -} -LIST(TL_MAX_SEQ_SPECS,TL_SEQ_SPEC, ;); - -#undef TL_SEQ_SPEC -#undef TL_SEQ_TL_END -#undef TL_SEQ_ONE_TYPELIST -#undef TL_SEQ_NULLTYPE_DEFAULT -#undef TL_SEQ_TYPE -#undef TL_MAX_SEQ_SPECS - -//---------------------------------------------------------------------- -// Various utility functions follow. - -/// Length::value is the number of types in the typelist. -template struct Length { }; -template <> struct Length { enum { value = 0 }; }; -template -struct Length > { enum { value = 1 + Length::value }; }; - -/// TypeAt::Result is the ith type in List -template struct TypeAt { }; -template -struct TypeAt, 0> { - typedef Head Result; -}; -template -struct TypeAt, index> { - typedef typename TypeAt::Result Result; -}; - -/// TypeAtNonStrict::Result is List[i] or DefaultType if out of range. -template -struct TypeAtNonStrict { - typedef DefaultType Result; -}; -template -struct TypeAtNonStrict, 0, DefaultType> { - typedef Head Result; -}; -template -struct TypeAtNonStrict, index, DefaultType> { - typedef typename TypeAtNonStrict::Result Result; -}; - -/// IndexOf::value is the position of T in List, or -1 if not found. -template struct IndexOf; -template -struct IndexOf { enum { value = -1 }; }; -template -struct IndexOf, T> { enum { value = 0 }; }; -template -struct IndexOf, T> { -private: - enum { iintail = IndexOf::value }; -public: - enum { value = (iintail == -1 ? -1 : 1+iintail) }; -}; - -/// Appends a type or a typelist to another in Append::Result -template struct Append; -template <> struct Append { typedef NullType Result; }; -template struct Append { - typedef Typelist Result; -}; -template -struct Append > { - typedef Typelist Result; -}; -template -struct Append, T> { - typedef Typelist::Result> Result; -}; - -// Erase::Result contains List without the first T. -template struct Erase; -template -struct Erase { typedef NullType Result; }; -template -struct Erase, T> { typedef Tail Result; }; -template -struct Erase, T> { - typedef Typelist::Result> Result; -}; - -// EraseAll::Result contains List without any T. -template struct EraseAll; -template -struct EraseAll { typedef NullType Result; }; -template -struct EraseAll, T> { - typedef typename EraseAll::Result Result; -}; -template -struct EraseAll, T> { - typedef Typelist::Result> Result; -}; - -/// Removes all duplicate types in a typelist -template struct NoDuplicates; -template <> struct NoDuplicates { typedef NullType Result; }; -template -struct NoDuplicates< Typelist > { -private: - typedef typename NoDuplicates::Result L1; - typedef typename Erase::Result L2; -public: - typedef Typelist Result; -}; - -// Replaces the first occurence of a type in a typelist, with another type -template struct Replace; -template -struct Replace { typedef NullType Result; }; -template -struct Replace, T, U> { - typedef Typelist Result; -}; -template -struct Replace, T, U> { - typedef Typelist::Result> Result; -}; - -// Replaces all occurences of a type in a typelist, with another type -template struct ReplaceAll; -template -struct ReplaceAll { typedef NullType Result; }; -template -struct ReplaceAll, T, U> { - typedef Typelist::Result> Result; -}; -template -struct ReplaceAll, T, U> { - typedef Typelist::Result> Result; -}; - -// Reverses a typelist -template struct Reverse; -template <> struct Reverse { typedef NullType Result; }; -template -struct Reverse< Typelist > { - typedef typename Append::Result, Head>::Result Result; -}; - -// Finds the type in a typelist that is the most derived from a given type -template struct MostDerived; -template struct MostDerived { typedef T Result; }; -template -struct MostDerived, T> { -private: - typedef typename MostDerived::Result Candidate; -public: - typedef typename Select::value, Head, Candidate>::Result Result; -}; - -// Arranges the types in a typelist so that the most derived types appear first -template struct DerivedToFront; -template <> struct DerivedToFront { typedef NullType Result; }; -template -struct DerivedToFront< Typelist > { -private: - typedef typename MostDerived::Result TheMostDerived; - typedef typename Replace::Result Temp; - typedef typename DerivedToFront::Result L; -public: - typedef Typelist Result; -}; - -//---------------------------------------------------------------------- - -} // namespace tl -} // namespace tm -} // namespace ustl diff --git a/common/include/ustl/typet.h b/common/include/ustl/typet.h deleted file mode 100644 index bb25e0065..000000000 --- a/common/include/ustl/typet.h +++ /dev/null @@ -1,96 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2007 by Mike Sharov -// -// This implementation is adapted from the Loki library, distributed under -// the MIT license with Copyright (c) 2001 by Andrei Alexandrescu. - -#pragma once - -namespace ustl { -/// Template metaprogramming tools -namespace tm { - -/// An empty type useful as a placeholder. -class NullType { }; - -/// Converts an integer to a type. -template struct Int2Type { enum { value = v }; }; - -/// Converts an type to a unique empty type. -template struct Type2Type { typedef T OriginalType; }; - -/// Selects type Result = flag ? T : U -template -struct Select { typedef T Result; }; -template -struct Select { typedef U Result; }; - -/// IsSameType::value is true when T=U -template -struct IsSameType { enum { value = false }; }; -template -struct IsSameType { enum { value = true }; }; - -/// \brief Checks for conversion possibilities between T and U -/// Conversion::exists is true if T is convertible to U -/// Conversion::sameType is true if U is T -template -struct Conversion { -private: - typedef char UT; - typedef short TT; - static UT Test (U); - static TT Test (...); - static T MakeT (void); -public: - enum { - exists = sizeof(UT) == sizeof(Test(MakeT())), - sameType = false - }; -}; -template -struct Conversion { enum { exists = true, sameType = true }; }; -template -struct Conversion { enum { exists = false, sameType = false }; }; -template -struct Conversion { enum { exists = false, sameType = false }; }; -template <> -struct Conversion { enum { exists = true, sameType = true }; }; - -/// SuperSubclass::value is true when U is derived from T, or when U is T -template -struct SuperSubclass { - enum { value = (::ustl::tm::Conversion::exists && - !::ustl::tm::Conversion::sameType) }; - enum { dontUseWithIncompleteTypes = sizeof(T)==sizeof(U) }; // Dummy enum to make sure that both classes are fully defined. -}; -template <> -struct SuperSubclass { enum { value = false }; }; -template -struct SuperSubclass { - enum { value = false }; - enum { dontUseWithIncompleteTypes = 0==sizeof(U) }; -}; -template -struct SuperSubclass { - enum { value = false }; - enum { dontUseWithIncompleteTypes = 0==sizeof(T) }; -}; - -/// SuperSubclassStrict::value is true when U is derived from T -template -struct SuperSubclassStrict { - enum { value = SuperSubclass::value && - !::ustl::tm::Conversion::sameType }; -}; - -#if !HAVE_CPP11 -// static assert support -template struct CompileTimeError; -template <> struct CompileTimeError {}; -#define static_assert(cond,msg) { ::ustl::tm::CompileTimeError ERROR_##msg; (void) ERROR_##msg; } -#endif - -} // namespace tm -} // namespace ustl diff --git a/common/include/ustl/ualgo.h b/common/include/ustl/ualgo.h deleted file mode 100644 index 5d9742266..000000000 --- a/common/include/ustl/ualgo.h +++ /dev/null @@ -1,733 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "upair.h" -#include "ufunction.h" -#include "umemory.h" -#include "util/qsort.h" - -namespace ustl { - -/// Swaps corresponding elements of [first, last) and [result,) -/// \ingroup SwapAlgorithms -/// -template -inline ForwardIterator2 swap_ranges (ForwardIterator1 first, ForwardIterator2 last, ForwardIterator2 result) -{ - for (; first != last; ++first, ++result) - iter_swap (first, result); - return result; -} - -/// Returns the first iterator i in the range [first, last) such that -/// *i == value. Returns last if no such iterator exists. -/// \ingroup SearchingAlgorithms -/// -template -inline InputIterator find (InputIterator first, InputIterator last, const EqualityComparable& value) -{ - while (first != last && !(*first == value)) - ++ first; - return first; -} - -/// Returns the first iterator such that *i == *(i + 1) -/// \ingroup SearchingAlgorithms -/// -template -ForwardIterator adjacent_find (ForwardIterator first, ForwardIterator last) -{ - if (first != last) - for (ForwardIterator prev = first; ++first != last; ++ prev) - if (*prev == *first) - return prev; - return last; -} - -/// Returns the pointer to the first pair of unequal elements. -/// \ingroup SearchingAlgorithms -/// -template -pair -mismatch (InputIterator first1, InputIterator last1, InputIterator first2) -{ - while (first1 != last1 && *first1 == *first2) - ++ first1, ++ first2; - return make_pair (first1, first2); -} - -/// \brief Returns true if two ranges are equal. -/// This is an extension, present in uSTL and SGI STL. -/// \ingroup SearchingAlgorithms -/// -template -inline bool equal (InputIterator first1, InputIterator last1, InputIterator first2) -{ - return mismatch (first1, last1, first2).first == last1; -} - -/// Count finds the number of elements in [first, last) that are equal -/// to value. More precisely, the first version of count returns the -/// number of iterators i in [first, last) such that *i == value. -/// \ingroup SearchingAlgorithms -/// -template -inline size_t count (InputIterator first, InputIterator last, const EqualityComparable& value) -{ - size_t total = 0; - for (; first != last; ++first) - if (*first == value) - ++ total; - return total; -} - -/// -/// The first version of transform performs the operation op(*i) for each -/// iterator i in the range [first, last), and assigns the result of that -/// operation to *o, where o is the corresponding output iterator. That is, -/// for each n such that 0 <= n < last - first, it performs the assignment -/// *(result + n) = op(*(first + n)). -/// The return value is result + (last - first). -/// \ingroup MutatingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline OutputIterator transform (InputIterator first, InputIterator last, OutputIterator result, UnaryFunction op) -{ - for (; first != last; ++result, ++first) - *result = op (*first); - return result; -} - -/// -/// The second version of transform is very similar, except that it uses a -/// Binary Function instead of a Unary Function: it performs the operation -/// op(*i1, *i2) for each iterator i1 in the range [first1, last1) and assigns -/// the result to *o, where i2 is the corresponding iterator in the second -/// input range and where o is the corresponding output iterator. That is, -/// for each n such that 0 <= n < last1 - first1, it performs the assignment -/// *(result + n) = op(*(first1 + n), *(first2 + n). -/// The return value is result + (last1 - first1). -/// \ingroup MutatingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline OutputIterator transform (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, BinaryFunction op) -{ - for (; first1 != last1; ++result, ++first1, ++first2) - *result = op (*first1, *first2); - return result; -} - -/// Replace replaces every element in the range [first, last) equal to -/// old_value with new_value. That is: for every iterator i, -/// if *i == old_value then it performs the assignment *i = new_value. -/// \ingroup MutatingAlgorithms -/// -template -inline void replace (ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value) -{ - for (; first != last; ++first) - if (*first == old_value) - *first = new_value; -} - -/// Replace_copy copies elements from the range [first, last) to the range -/// [result, result + (last-first)), except that any element equal to old_value -/// is not copied; new_value is copied instead. More precisely, for every -/// integer n such that 0 <= n < last-first, replace_copy performs the -/// assignment *(result+n) = new_value if *(first+n) == old_value, and -/// *(result+n) = *(first+n) otherwise. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator replace_copy (InputIterator first, InputIterator last, OutputIterator result, const T& old_value, const T& new_value) -{ - for (; first != last; ++result, ++first) - *result = (*first == old_value) ? new_value : *first; -} - -/// Generate assigns the result of invoking gen, a function object that -/// takes no arguments, to each element in the range [first, last). -/// \ingroup GeneratorAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline void generate (ForwardIterator first, ForwardIterator last, Generator gen) -{ - for (; first != last; ++first) - *first = gen(); -} - -/// Generate_n assigns the result of invoking gen, a function object that -/// takes no arguments, to each element in the range [first, first+n). -/// The return value is first + n. -/// \ingroup GeneratorAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline OutputIterator generate_n (OutputIterator first, size_t n, Generator gen) -{ - for (uoff_t i = 0; i != n; ++i, ++first) - *first = gen(); - return first; -} - -/// \brief Reverse reverses a range. -/// That is: for every i such that 0 <= i <= (last - first) / 2), -/// it exchanges *(first + i) and *(last - (i + 1)). -/// \ingroup MutatingAlgorithms -/// -template -inline void reverse (BidirectionalIterator first, BidirectionalIterator last) -{ - for (; distance (first, --last) > 0; ++first) - iter_swap (first, last); -} - -/// \brief Reverses [first,last) and writes it to \p output. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator reverse_copy (BidirectionalIterator first, BidirectionalIterator last, OutputIterator result) -{ - for (; first != last; ++result) - *result = *--last; - return result; -} - -/// \brief Exchanges ranges [first, middle) and [middle, last) -/// \ingroup MutatingAlgorithms -/// -template -ForwardIterator rotate (ForwardIterator first, ForwardIterator middle, ForwardIterator last) -{ - if (first == middle || middle == last) - return first; - reverse (first, middle); - reverse (middle, last); - for (;first != middle && middle != last; ++first) - iter_swap (first, --last); - reverse (first, (first == middle ? last : middle)); - return first; -} - -/// Specialization for pointers, which can be treated identically. -template -inline T* rotate (T* first, T* middle, T* last) -{ - rotate_fast (first, middle, last); - return first; -} - - -/// \brief Exchanges ranges [first, middle) and [middle, last) into \p result. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator rotate_copy (ForwardIterator first, ForwardIterator middle, ForwardIterator last, OutputIterator result) -{ - return copy (first, middle, copy (middle, last, result)); -} - -/// \brief Combines two sorted ranges. -/// \ingroup SortingAlgorithms -/// -template -OutputIterator merge (InputIterator1 first1, InputIterator1 last1, - InputIterator2 first2, InputIterator2 last2, OutputIterator result) -{ - for (; first1 != last1 && first2 != last2; ++result) { - if (*first1 < *first2) - *result = *first1++; - else - *result = *first2++; - } - if (first1 < last1) - return copy (first1, last1, result); - else - return copy (first2, last2, result); -} - -/// Combines two sorted ranges from the same container. -/// \ingroup SortingAlgorithms -/// -template -void inplace_merge (InputIterator first, InputIterator middle, InputIterator last) -{ - for (; middle != last; ++first) { - while (*first < *middle) - ++ first; - reverse (first, middle); - reverse (first, ++middle); - } -} - -/// Remove_copy copies elements that are not equal to value from the range -/// [first, last) to a range beginning at result. The return value is the -/// end of the resulting range. This operation is stable, meaning that the -/// relative order of the elements that are copied is the same as in the -/// range [first, last). -/// \ingroup MutatingAlgorithms -/// -template -OutputIterator remove_copy (InputIterator first, InputIterator last, OutputIterator result, const T& value) -{ - for (; first != last; ++first) { - if (!(*first == value)) { - *result = *first; - ++ result; - } - } - return result; -} - -/// Remove_copy copies elements pointed to by iterators in [rfirst, rlast) -/// from the range [first, last) to a range beginning at result. The return -/// value is the end of the resulting range. This operation is stable, meaning -/// that the relative order of the elements that are copied is the same as in the -/// range [first, last). Range [rfirst, rlast) is assumed to be sorted. -/// This algorithm is a uSTL extension. -/// \ingroup MutatingAlgorithms -/// -template -OutputIterator remove_copy (InputIterator first, InputIterator last, OutputIterator result, RInputIterator rfirst, RInputIterator rlast) -{ - for (; first != last; ++first) { - while (rfirst != rlast && *rfirst < first) - ++ rfirst; - if (rfirst == rlast || first != *rfirst) { - *result = *first; - ++ result; - } - } - return result; -} - -/// Remove removes from the range [first, last) all elements that are equal to -/// value. That is, remove returns an iterator new_last such that the range -/// [first, new_last) contains no elements equal to value. [1] The iterators -/// in the range [new_last, last) are all still dereferenceable, but the -/// elements that they point to are unspecified. Remove is stable, meaning -/// that the relative order of elements that are not equal to value is -/// unchanged. -/// \ingroup MutatingAlgorithms -/// -template -inline ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& value) -{ - return remove_copy (first, last, first, value); -} - -/// Unique_copy copies elements from the range [first, last) to a range -/// beginning with result, except that in a consecutive group of duplicate -/// elements only the first one is copied. The return value is the end of -/// the range to which the elements are copied. This behavior is similar -/// to the Unix filter uniq. -/// \ingroup MutatingAlgorithms -/// -template -OutputIterator unique_copy (InputIterator first, InputIterator last, OutputIterator result) -{ - if (first != last) { - *result = *first; - while (++first != last) - if (!(*first == *result)) - *++result = *first; - ++ result; - } - return result; -} - -/// Every time a consecutive group of duplicate elements appears in the range -/// [first, last), the algorithm unique removes all but the first element. -/// That is, unique returns an iterator new_last such that the range [first, -/// new_last) contains no two consecutive elements that are duplicates. -/// The iterators in the range [new_last, last) are all still dereferenceable, -/// but the elements that they point to are unspecified. Unique is stable, -/// meaning that the relative order of elements that are not removed is -/// unchanged. -/// \ingroup MutatingAlgorithms -/// -template -inline ForwardIterator unique (ForwardIterator first, ForwardIterator last) -{ - return unique_copy (first, last, first); -} - -/// Returns the furthermost iterator i in [first, last) such that, -/// for every iterator j in [first, i), *j < value -/// Assumes the range is sorted. -/// \ingroup SearchingAlgorithms -/// -template -ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const LessThanComparable& value) -{ - ForwardIterator mid; - while (first != last) { - mid = advance (first, size_t(distance (first,last)) / 2); - if (*mid < value) - first = mid + 1; - else - last = mid; - } - return first; -} - -/// Performs a binary search inside the sorted range. -/// \ingroup SearchingAlgorithms -/// -template -inline bool binary_search (ForwardIterator first, ForwardIterator last, const LessThanComparable& value) -{ - ForwardIterator found = lower_bound (first, last, value); - return found != last && !(value < *found); -} - -/// Returns the furthermost iterator i in [first,last) such that for -/// every iterator j in [first,i), value < *j is false. -/// \ingroup SearchingAlgorithms -/// -template -ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const LessThanComparable& value) -{ - ForwardIterator mid; - while (first != last) { - mid = advance (first, size_t(distance (first,last)) / 2); - if (value < *mid) - last = mid; - else - first = mid + 1; - } - return last; -} - -/// Returns pair -/// \ingroup SearchingAlgorithms -/// -template -inline pair equal_range (ForwardIterator first, ForwardIterator last, const LessThanComparable& value) -{ - pair rv; - rv.second = rv.first = lower_bound (first, last, value); - while (rv.second != last && !(value < *(rv.second))) - ++ rv.second; - return rv; -} - -/// Randomly permute the elements of the container. -/// \ingroup GeneratorAlgorithms -/// -/*template -void random_shuffle (RandomAccessIterator first, RandomAccessIterator last) -{ - for (; first != last; ++ first) - iter_swap (first, first + (rand() % distance (first, last))); - }*/ - -/// \brief Generic compare function adaptor to pass to qsort -/// \ingroup FunctorObjects -template -int qsort_adapter (const void* p1, const void* p2) -{ - ConstPointer i1 = reinterpret_cast(p1); - ConstPointer i2 = reinterpret_cast(p2); - Compare comp; - return comp (*i1, *i2) ? -1 : (comp (*i2, *i1) ? 1 : 0); -} - -/// Sorts the container -/// \ingroup SortingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -void sort (RandomAccessIterator first, RandomAccessIterator last, Compare) -{ - typedef typename iterator_traits::value_type value_type; - typedef typename iterator_traits::const_pointer const_pointer; - qsort (first, distance (first, last), sizeof(value_type), - &qsort_adapter); -} - -/// Sorts the container -/// \ingroup SortingAlgorithms -/// -template -inline void sort (RandomAccessIterator first, RandomAccessIterator last) -{ - typedef typename iterator_traits::value_type value_type; - sort (first, last, less()); -} - -/// Sorts the container preserving order of equal elements. -/// \ingroup SortingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -void stable_sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp) -{ - for (RandomAccessIterator j, i = first; ++i < last;) { // Insertion sort - for (j = i; j-- > first && comp(*i, *j);) ; - if (++j != i) rotate (j, i, i + 1); - } -} - -/// Sorts the container -/// \ingroup SortingAlgorithms -/// -template -inline void stable_sort (RandomAccessIterator first, RandomAccessIterator last) -{ - typedef typename iterator_traits::value_type value_type; - stable_sort (first, last, less()); -} - -/// \brief Searches for the first subsequence [first2,last2) in [first1,last1) -/// \ingroup SearchingAlgorithms -template -inline ForwardIterator1 search (ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2) -{ - typedef typename iterator_traits::value_type value_type; - return search (first1, last1, first2, last2, equal_to()); -} - -/// \brief Searches for the last subsequence [first2,last2) in [first1,last1) -/// \ingroup SearchingAlgorithms -template -inline ForwardIterator1 find_end (ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2) -{ - typedef typename iterator_traits::value_type value_type; - return find_end (first1, last1, first2, last2, equal_to()); -} - -/// \brief Searches for the first occurence of \p count \p values in [first, last) -/// \ingroup SearchingAlgorithms -template -inline Iterator search_n (Iterator first, Iterator last, size_t count, const T& value) -{ - typedef typename iterator_traits::value_type value_type; - return search_n (first, last, count, value, equal_to()); -} - -/// \brief Searches [first1,last1) for the first occurrence of an element from [first2,last2) -/// \ingroup SearchingAlgorithms -template -inline InputIterator find_first_of (InputIterator first1, InputIterator last1, ForwardIterator first2, ForwardIterator last2) -{ - typedef typename iterator_traits::value_type value_type; - return find_first_of (first1, last1, first2, last2, equal_to()); -} - -/// \brief Returns true if [first2,last2) is a subset of [first1,last1) -/// \ingroup ConditionAlgorithms -/// \ingroup SetAlgorithms -template -inline bool includes (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) -{ - typedef typename iterator_traits::value_type value_type; - return includes (first1, last1, first2, last2, less()); -} - -/// \brief Merges [first1,last1) with [first2,last2) -/// -/// Result will contain every element that is in either set. If duplicate -/// elements are present, max(n,m) is placed in the result. -/// -/// \ingroup SetAlgorithms -template -inline OutputIterator set_union (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) -{ - typedef typename iterator_traits::value_type value_type; - return set_union (first1, last1, first2, last2, result, less()); -} - -/// \brief Creates a set containing elements shared by the given ranges. -/// \ingroup SetAlgorithms -template -inline OutputIterator set_intersection (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) -{ - typedef typename iterator_traits::value_type value_type; - return set_intersection (first1, last1, first2, last2, result, less()); -} - -/// \brief Removes from [first1,last1) elements present in [first2,last2) -/// \ingroup SetAlgorithms -template -inline OutputIterator set_difference (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) -{ - typedef typename iterator_traits::value_type value_type; - return set_difference (first1, last1, first2, last2, result, less()); -} - -/// \brief Performs union of sets A-B and B-A. -/// \ingroup SetAlgorithms -template -inline OutputIterator set_symmetric_difference (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result) -{ - typedef typename iterator_traits::value_type value_type; - return set_symmetric_difference (first1, last1, first2, last2, result, less()); -} - -/// \brief Returns true if the given range is sorted. -/// \ingroup ConditionAlgorithms -template -inline bool is_sorted (ForwardIterator first, ForwardIterator last) -{ - typedef typename iterator_traits::value_type value_type; - return is_sorted (first, last, less()); -} - -/// \brief Compares two given containers like strcmp compares strings. -/// \ingroup ConditionAlgorithms -template -inline bool lexicographical_compare (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) -{ - typedef typename iterator_traits::value_type value_type; - return lexicographical_compare (first1, last1, first2, last2, less()); -} - -/// \brief Creates the next lexicographical permutation of [first,last). -/// Returns false if no further permutations can be created. -/// \ingroup GeneratorAlgorithms -template -inline bool next_permutation (BidirectionalIterator first, BidirectionalIterator last) -{ - typedef typename iterator_traits::value_type value_type; - return next_permutation (first, last, less()); -} - -/// \brief Creates the previous lexicographical permutation of [first,last). -/// Returns false if no further permutations can be created. -/// \ingroup GeneratorAlgorithms -template -inline bool prev_permutation (BidirectionalIterator first, BidirectionalIterator last) -{ - typedef typename iterator_traits::value_type value_type; - return prev_permutation (first, last, less()); -} - -/// Returns \p v clamped to the given range -template -inline T clamp (const T& v, const T& l, const T& h, Compare comp) - { return comp(v, l) ? l : comp(h, v) ? h : v; } -template -inline T clamp (const T& v, const T& l, const T& h) - { return v < l ? l : (h < v ? h : v); } - -/// Returns iterator to the max element in [first,last) -/// \ingroup SearchingAlgorithms -template -inline ForwardIterator max_element (ForwardIterator first, ForwardIterator last) -{ - typedef typename iterator_traits::value_type value_type; - return max_element (first, last, less()); -} - -/// Returns iterator to the min element in [first,last) -/// \ingroup SearchingAlgorithms -template -inline ForwardIterator min_element (ForwardIterator first, ForwardIterator last) -{ - typedef typename iterator_traits::value_type value_type; - return min_element (first, last, less()); -} - -#if HAVE_CPP14 - -/// Returns min,max pair of the argument -template -inline constexpr auto minmax (const T& a, const T& b) - { return a < b ? make_pair(a,b) : make_pair(b,a); } -template -inline constexpr auto minmax (T& a, T& b) - { return a < b ? make_pair(a,b) : make_pair(b,a); } -template -inline constexpr auto minmax (const T& a, const T& b, Compare comp) - { return comp(a,b) ? make_pair(a,b) : make_pair(b,a); } -template constexpr void minmax (T&& a, T&& b) = delete; -template constexpr void minmax (const T& a, const T& b, Compare comp) = delete; - -template -auto minmax (std::initializer_list l) -{ - auto r = make_pair (*l.begin(),*l.begin()); - for (auto& i : l) { - r.first = min (r.first, i); - r.second = max (r.second, i); - } - return r; -} -template -auto minmax (std::initializer_list l, Compare comp) -{ - auto r = make_pair (*l.begin(),*l.begin()); - for (auto& i : l) { - if (comp(i, r.first)) - r.first = i; - if (comp(r.second, i)) - r.second = i; - } - return r; -} -#endif - -template -pair minmax_element (ForwardIterator first, ForwardIterator last) -{ - pair r = make_pair (first, first); - for (; first != last; ++first) { - if (*first < *r.first) - r.first = first; - if (*r.second < *first) - r.second = first; - } - return r; -} -template -pair minmax_element (ForwardIterator first, ForwardIterator last, Compare comp) -{ - pair r = make_pair (first, first); - for (; first != last; ++first) { - if (comp (*first, *r.first)) - r.first = first; - if (comp (*r.second, *first)) - r.second = first; - } - return r; -} - -/// \brief Makes [first,middle) a part of the sorted array. -/// Contents of [middle,last) is undefined. This implementation just calls stable_sort. -/// \ingroup SortingAlgorithms -template -inline void partial_sort (RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last) -{ - typedef typename iterator_traits::value_type value_type; - partial_sort (first, middle, last, less()); -} - -/// \brief Puts \p nth element into its sorted position. -/// In this implementation, the entire array is sorted. I can't think of any -/// use for it where the time gained would be useful. -/// \ingroup SortingAlgorithms -/// \ingroup SearchingAlgorithms -/// -template -inline void nth_element (RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last) -{ - partial_sort (first, nth, last); -} - -/// \brief Like partial_sort, but outputs to [result_first,result_last) -/// \ingroup SortingAlgorithms -template -inline RandomAccessIterator partial_sort_copy (InputIterator first, InputIterator last, RandomAccessIterator result_first, RandomAccessIterator result_last) -{ - typedef typename iterator_traits::value_type value_type; - return partial_sort_copy (first, last, result_first, result_last, less()); -} - -} // namespace ustl diff --git a/common/include/ustl/ualgobase.h b/common/include/ustl/ualgobase.h deleted file mode 100644 index fa54dc5b5..000000000 --- a/common/include/ustl/ualgobase.h +++ /dev/null @@ -1,353 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "uutility.h" -#include - -namespace ustl { - -#if HAVE_CPP11 - -template -inline constexpr typename tm::RemoveReference::Result&& move (T&& v) noexcept - { return static_cast::Result&&>(v); } - -template -inline constexpr T&& forward (typename tm::RemoveReference::Result& v) noexcept - { return static_cast(v); } - -template -inline constexpr T&& forward (typename tm::RemoveReference::Result&& v) noexcept - { return static_cast(v); } - -#if HAVE_CPP14 -template -T exchange (T& a, U&& b) -{ - T t = move(a); - a = forward(b); - return t; -} -#endif - -#else - -template -inline constexpr typename tm::RemoveReference::Result& move (T& v) noexcept - { return v; } - -template -inline constexpr T& forward (typename tm::RemoveReference::Result& v) noexcept - { return v; } - -#endif - -/// Assigns the contents of a to b and the contents of b to a. -/// This is used as a primitive operation by many other algorithms. -/// \ingroup SwapAlgorithms -/// -template -inline void swap (T& a, T& b) -{ - typename tm::RemoveReference::Result t = move(a); - a = move(b); - b = move(t); -} - -/// Equivalent to swap (*a, *b) -/// \ingroup SwapAlgorithms -/// -template -inline void iter_swap (Iterator a, Iterator b) -{ - swap (*a, *b); -} - -/// Copy copies elements from the range [first, last) to the range -/// [result, result + (last - first)). That is, it performs the assignments -/// *result = *first, *(result + 1) = *(first + 1), and so on. [1] Generally, -/// for every integer n from 0 to last - first, copy performs the assignment -/// *(result + n) = *(first + n). Assignments are performed in forward order, -/// i.e. in order of increasing n. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result) -{ - for (; first != last; ++result, ++first) - *result = *first; - return result; -} - -/// Copy_n copies elements from the range [first, first + n) to the range -/// [result, result + n). That is, it performs the assignments -/// *result = *first, *(result + 1) = *(first + 1), and so on. Generally, -/// for every integer i from 0 up to (but not including) n, copy_n performs -/// the assignment *(result + i) = *(first + i). Assignments are performed -/// in forward order, i.e. in order of increasing n. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator copy_n (InputIterator first, size_t count, OutputIterator result) -{ - for (; count; --count, ++result, ++first) - *result = *first; - return result; -} - -/// \brief Copy copies elements from the range (last, first] to result. -/// \ingroup MutatingAlgorithms -/// Copies elements starting at last, decrementing both last and result. -/// -template -inline OutputIterator copy_backward (InputIterator first, InputIterator last, OutputIterator result) -{ - while (first != last) - *--result = *--last; - return result; -} - -/// For_each applies the function object f to each element in the range -/// [first, last); f's return value, if any, is ignored. Applications are -/// performed in forward order, i.e. from first to last. For_each returns -/// the function object after it has been applied to each element. -/// \ingroup MutatingAlgorithms -/// -template -inline UnaryFunction for_each (InputIterator first, InputIterator last, UnaryFunction f) -{ - for (; first != last; ++first) - f (*first); - return f; -} - -/// Fill assigns the value value to every element in the range [first, last). -/// That is, for every iterator i in [first, last), -/// it performs the assignment *i = value. -/// \ingroup GeneratorAlgorithms -/// -template -inline void fill (ForwardIterator first, ForwardIterator last, const T& value) -{ - for (; first != last; ++first) - *first = value; -} - -/// Fill_n assigns the value value to every element in the range -/// [first, first+count). That is, for every iterator i in [first, first+count), -/// it performs the assignment *i = value. The return value is first + count. -/// \ingroup GeneratorAlgorithms -/// -template -inline OutputIterator fill_n (OutputIterator first, size_t count, const T& value) -{ - for (; count; --count, ++first) - *first = value; - return first; -} - -#if __MMX__ -extern "C" void copy_n_fast (const void* src, size_t count, void* dest) noexcept; -#else -inline void copy_n_fast (const void* src, size_t count, void* dest) noexcept - { memmove (dest, src, count); } -#endif -#if __x86__ -extern "C" void copy_backward_fast (const void* first, const void* last, void* result) noexcept; -#else -inline void copy_backward_fast (const void* first, const void* last, void* result) noexcept -{ - const size_t nBytes (distance (first, last)); - memmove (advance (result, -nBytes), first, nBytes); -} -#endif -extern "C" void fill_n8_fast (uint8_t* dest, size_t count, uint8_t v) noexcept; -extern "C" void fill_n16_fast (uint16_t* dest, size_t count, uint16_t v) noexcept; -extern "C" void fill_n32_fast (uint32_t* dest, size_t count, uint32_t v) noexcept; -extern "C" void rotate_fast (void* first, void* middle, void* last) noexcept; - -#if __GNUC__ >= 4 -/// \brief Computes the number of 1 bits in a number. -/// \ingroup ConditionAlgorithms -inline size_t popcount (uint32_t v) { return __builtin_popcount (v); } -#if HAVE_INT64_T -inline size_t popcount (uint64_t v) { return __builtin_popcountll (v); } -#endif -#else -size_t popcount (uint32_t v) noexcept; -#if HAVE_INT64_T -size_t popcount (uint64_t v) noexcept; -#endif // HAVE_INT64_T -#endif // __GNUC__ - -//---------------------------------------------------------------------- -// Optimized versions for standard types -//---------------------------------------------------------------------- - -#if WANT_UNROLLED_COPY - -template -inline T* unrolled_copy (const T* first, size_t count, T* result) -{ - copy_n_fast (first, count * sizeof(T), result); - return advance (result, count); -} - -template <> -inline uint8_t* copy_backward (const uint8_t* first, const uint8_t* last, uint8_t* result) -{ - copy_backward_fast (first, last, result); - return result; -} - -template -inline T* unrolled_fill (T* result, size_t count, T value) -{ - for (; count; --count, ++result) - *result = value; - return result; -} -template <> inline uint8_t* unrolled_fill (uint8_t* result, size_t count, uint8_t value) - { fill_n8_fast (result, count, value); return advance (result, count); } -template <> inline uint16_t* unrolled_fill (uint16_t* result, size_t count, uint16_t value) - { fill_n16_fast (result, count, value); return advance (result, count); } -template <> inline uint32_t* unrolled_fill (uint32_t* result, size_t count, uint32_t value) - { fill_n32_fast (result, count, value); return advance (result, count); } -/*template <> inline float* unrolled_fill (float* result, size_t count, float value) - { fill_n32_fast (reinterpret_cast(result), count, *noalias_cast(&value)); return advance (result, count); }*/ - -#if __MMX__ -#define UNROLLED_COPY_SPECIALIZATION(type) \ -template <> inline type* copy (const type* first, const type* last, type* result) \ -{ return unrolled_copy (first, distance (first, last), result); } \ -template <> inline type* copy_n (const type* first, size_t count, type* result) \ -{ return unrolled_copy (first, count, result); } -#define UNROLLED_FILL_SPECIALIZATION(type) \ -template <> inline void fill (type* first, type* last, const type& value) \ -{ unrolled_fill (first, distance (first, last), value); } \ -template <> inline type* fill_n (type* first, size_t count, const type& value) \ -{ return unrolled_fill (first, count, value); } -UNROLLED_COPY_SPECIALIZATION(uint8_t) -UNROLLED_FILL_SPECIALIZATION(uint8_t) -UNROLLED_COPY_SPECIALIZATION(uint16_t) -UNROLLED_FILL_SPECIALIZATION(uint16_t) -UNROLLED_COPY_SPECIALIZATION(uint32_t) -UNROLLED_FILL_SPECIALIZATION(uint32_t) -UNROLLED_COPY_SPECIALIZATION(float) -UNROLLED_FILL_SPECIALIZATION(float) -#undef UNROLLED_FILL_SPECIALIZATION -#undef UNROLLED_COPY_SPECIALIZATION -#endif // WANT_UNROLLED_COPY -#endif // __MMX__ - -// Specializations for void* and char*, aliasing the above optimized versions. -// -// All these need duplication with const and non-const arguments, since -// otherwise the compiler will default to the unoptimized version for -// pointers not const in the caller's context, such as local variables. -// These are all inline, but they sure slow down compilation... :( -// -#define COPY_ALIAS_FUNC(ctype, type, alias_type) \ -template <> inline type* copy (ctype* first, ctype* last, type* result) \ -{ return reinterpret_cast (copy (reinterpret_cast(first), reinterpret_cast(last), reinterpret_cast(result))); } -#if WANT_UNROLLED_COPY -#if HAVE_THREE_CHAR_TYPES -COPY_ALIAS_FUNC(const char, char, uint8_t) -COPY_ALIAS_FUNC(char, char, uint8_t) -#endif -COPY_ALIAS_FUNC(const int8_t, int8_t, uint8_t) -COPY_ALIAS_FUNC(int8_t, int8_t, uint8_t) -COPY_ALIAS_FUNC(uint8_t, uint8_t, uint8_t) -COPY_ALIAS_FUNC(const int16_t, int16_t, uint16_t) -COPY_ALIAS_FUNC(int16_t, int16_t, uint16_t) -COPY_ALIAS_FUNC(uint16_t, uint16_t, uint16_t) -#if __MMX__ || (SIZE_OF_LONG > 4) -COPY_ALIAS_FUNC(const int32_t, int32_t, uint32_t) -COPY_ALIAS_FUNC(int32_t, int32_t, uint32_t) -COPY_ALIAS_FUNC(uint32_t, uint32_t, uint32_t) -#endif -#endif -COPY_ALIAS_FUNC(const void, void, uint8_t) -COPY_ALIAS_FUNC(void, void, uint8_t) -#undef COPY_ALIAS_FUNC -#define COPY_BACKWARD_ALIAS_FUNC(ctype, type, alias_type) \ -template <> inline type* copy_backward (ctype* first, ctype* last, type* result) \ -{ return reinterpret_cast (copy_backward (reinterpret_cast(first), reinterpret_cast(last), reinterpret_cast(result))); } -#if WANT_UNROLLED_COPY -#if HAVE_THREE_CHAR_TYPES -COPY_BACKWARD_ALIAS_FUNC(char, char, uint8_t) -#endif -COPY_BACKWARD_ALIAS_FUNC(uint8_t, uint8_t, uint8_t) -COPY_BACKWARD_ALIAS_FUNC(int8_t, int8_t, uint8_t) -COPY_BACKWARD_ALIAS_FUNC(uint16_t, uint16_t, uint8_t) -COPY_BACKWARD_ALIAS_FUNC(const uint16_t, uint16_t, uint8_t) -COPY_BACKWARD_ALIAS_FUNC(int16_t, int16_t, uint8_t) -COPY_BACKWARD_ALIAS_FUNC(const int16_t, int16_t, uint8_t) -#endif -COPY_BACKWARD_ALIAS_FUNC(void, void, uint8_t) -COPY_BACKWARD_ALIAS_FUNC(const void, void, uint8_t) -#undef COPY_BACKWARD_ALIAS_FUNC -#define FILL_ALIAS_FUNC(type, alias_type, v_type) \ -template <> inline void fill (type* first, type* last, const v_type& value) \ -{ fill (reinterpret_cast(first), reinterpret_cast(last), alias_type(value)); } -FILL_ALIAS_FUNC(void, uint8_t, char) -FILL_ALIAS_FUNC(void, uint8_t, uint8_t) -#if WANT_UNROLLED_COPY -#if HAVE_THREE_CHAR_TYPES -FILL_ALIAS_FUNC(char, uint8_t, char) -FILL_ALIAS_FUNC(char, uint8_t, uint8_t) -#endif -FILL_ALIAS_FUNC(int8_t, uint8_t, int8_t) -FILL_ALIAS_FUNC(int16_t, uint16_t, int16_t) -#if __MMX__ || (SIZE_OF_LONG > 4) -FILL_ALIAS_FUNC(int32_t, uint32_t, int32_t) -#endif -#endif -#undef FILL_ALIAS_FUNC -#define COPY_N_ALIAS_FUNC(ctype, type, alias_type) \ -template <> inline type* copy_n (ctype* first, size_t count, type* result) \ -{ return reinterpret_cast (copy_n (reinterpret_cast(first), count, reinterpret_cast(result))); } -COPY_N_ALIAS_FUNC(const void, void, uint8_t) -COPY_N_ALIAS_FUNC(void, void, uint8_t) -#if WANT_UNROLLED_COPY -#if HAVE_THREE_CHAR_TYPES -COPY_N_ALIAS_FUNC(const char, char, uint8_t) -COPY_N_ALIAS_FUNC(char, char, uint8_t) -#endif -COPY_N_ALIAS_FUNC(int8_t, int8_t, uint8_t) -COPY_N_ALIAS_FUNC(uint8_t, uint8_t, uint8_t) -COPY_N_ALIAS_FUNC(const int8_t, int8_t, uint8_t) -COPY_N_ALIAS_FUNC(int16_t, int16_t, uint16_t) -COPY_N_ALIAS_FUNC(uint16_t, uint16_t, uint16_t) -COPY_N_ALIAS_FUNC(const int16_t, int16_t, uint16_t) -#if __MMX__ || (SIZE_OF_LONG > 4) -COPY_N_ALIAS_FUNC(int32_t, int32_t, uint32_t) -COPY_N_ALIAS_FUNC(uint32_t, uint32_t, uint32_t) -COPY_N_ALIAS_FUNC(const int32_t, int32_t, uint32_t) -#endif -#endif -#undef COPY_N_ALIAS_FUNC -#define FILL_N_ALIAS_FUNC(type, alias_type, v_type) \ -template <> inline type* fill_n (type* first, size_t n, const v_type& value) \ -{ return reinterpret_cast (fill_n (reinterpret_cast(first), n, alias_type(value))); } -FILL_N_ALIAS_FUNC(void, uint8_t, char) -FILL_N_ALIAS_FUNC(void, uint8_t, uint8_t) -#if WANT_UNROLLED_COPY -#if HAVE_THREE_CHAR_TYPES -FILL_N_ALIAS_FUNC(char, uint8_t, char) -FILL_N_ALIAS_FUNC(char, uint8_t, uint8_t) -#endif -FILL_N_ALIAS_FUNC(int8_t, uint8_t, int8_t) -FILL_N_ALIAS_FUNC(int16_t, uint16_t, int16_t) -#if __MMX__ || (SIZE_OF_LONG > 4) -FILL_N_ALIAS_FUNC(int32_t, uint32_t, int32_t) -#endif -#endif -#undef FILL_N_ALIAS_FUNC - -extern const char _FmtPrtChr[2][8]; - -} // namespace ustl diff --git a/common/include/ustl/uatomic.h b/common/include/ustl/uatomic.h deleted file mode 100644 index fd5e090b7..000000000 --- a/common/include/ustl/uatomic.h +++ /dev/null @@ -1,115 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2016 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "utypes.h" -#if HAVE_CPP11 - -//{{{ memory_order ----------------------------------------------------- - -namespace ustl { - -enum memory_order { - memory_order_relaxed = __ATOMIC_RELAXED, - memory_order_consume = __ATOMIC_CONSUME, - memory_order_acquire = __ATOMIC_ACQUIRE, - memory_order_release = __ATOMIC_RELEASE, - memory_order_acq_rel = __ATOMIC_ACQ_REL, - memory_order_seq_cst = __ATOMIC_SEQ_CST -}; - -//}}}------------------------------------------------------------------- -//{{{ atomic - -template -class atomic { - T _v; -public: - atomic (void) = default; - inline constexpr atomic (T v) : _v(v) {} - atomic (const atomic&) = delete; - atomic& operator= (const atomic&) = delete; - inline bool is_lock_free (void) const - { return __atomic_is_lock_free (sizeof(T), &_v); } - inline void store (T v, memory_order order = memory_order_seq_cst) - { __atomic_store_n (&_v, v, order); } - inline T load (memory_order order = memory_order_seq_cst) const - { return __atomic_load_n (&_v, order); } - inline T exchange (T v, memory_order order = memory_order_seq_cst) - { return __atomic_exchange_n (&_v, v, order); } - inline bool compare_exchange_weak (T& expected, T desired, memory_order order = memory_order_seq_cst) - { return __atomic_compare_exchange_n (&_v, &expected, desired, true, order, order); } - inline bool compare_exchange_weak (T& expected, T desired, memory_order success, memory_order failure) - { return __atomic_compare_exchange_n (&_v, &expected, desired, true, success, failure); } - inline bool compare_exchange_strong (T& expected, T desired, memory_order success, memory_order failure) - { return __atomic_compare_exchange_n (&_v, &expected, desired, false, success, failure); } - inline T fetch_add (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_fetch_add (&_v, v, order); } - inline T fetch_sub (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_fetch_sub (&_v, v, order); } - inline T fetch_and (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_fetch_and (&_v, v, order); } - inline T fetch_or (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_fetch_or (&_v, v, order); } - inline T fetch_xor (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_fetch_xor (&_v, v, order); } - inline T add_fetch (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_add_fetch (&_v, v, order); } - inline T sub_fetch (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_sub_fetch (&_v, v, order); } - inline T and_fetch (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_and_fetch (&_v, v, order); } - inline T or_fetch (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_or_fetch (&_v, v, order); } - inline T xor_fetch (T v, memory_order order = memory_order_seq_cst ) - { return __atomic_xor_fetch (&_v, v, order); } - inline operator T (void) const { return load(); } - inline T operator= (T v) { store(v); return v; } - inline T operator++ (int) { return fetch_add (1); } - inline T operator-- (int) { return fetch_sub (1); } - inline T operator++ (void) { return add_fetch (1); } - inline T operator-- (void) { return sub_fetch (1); } - inline T operator+= (T v) { return add_fetch (v); } - inline T operator-= (T v) { return sub_fetch (v); } - inline T operator&= (T v) { return and_fetch (v); } - inline T operator|= (T v) { return or_fetch (v); } - inline T operator^= (T v) { return xor_fetch (v); } -}; -#define ATOMIC_VAR_INIT {0} - -//}}}------------------------------------------------------------------- -//{{{ atomic_flag - -class atomic_flag { - bool _v; -public: - atomic_flag (void) = default; - inline constexpr atomic_flag (bool v) : _v(v) {} - atomic_flag (const atomic_flag&) = delete; - atomic_flag& operator= (const atomic_flag&) = delete; - void clear (memory_order order = memory_order_seq_cst) - { __atomic_clear (&_v, order); } - bool test_and_set (memory_order order = memory_order_seq_cst) - { return __atomic_test_and_set (&_v, order); } -}; -#define ATOMIC_FLAG_INIT {false} - -//}}}------------------------------------------------------------------- -//{{{ fence functions - -namespace { - -template -static inline T kill_dependency (T v) noexcept - { T r (v); return r; } -static inline void atomic_thread_fence (memory_order order) noexcept - { __atomic_thread_fence (order); } -static inline void atomic_signal_fence (memory_order order) noexcept - { __atomic_signal_fence (order); } - -} // namespace -} // namespace ustl -#endif // HAVE_CPP11 -//}}}------------------------------------------------------------------- diff --git a/common/include/ustl/ubitset.h b/common/include/ustl/ubitset.h deleted file mode 100644 index 671be85f1..000000000 --- a/common/include/ustl/ubitset.h +++ /dev/null @@ -1,129 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "ustring.h" -#include "ufunction.h" - -namespace ustl { - -class istringstream; - -typedef uint32_t bitset_value_type; - -void convert_to_bitstring (const bitset_value_type* v, size_t n, string& buf) noexcept; -void convert_from_bitstring (const string& buf, bitset_value_type* v, size_t n) noexcept; - -/// \class bitset ubitset.h ustl.h -/// \ingroup Sequences -/// -/// \brief bitset is a fixed-size block of memory with addressable bits. -/// -/// Normally used for state flags; allows setting and unsetting of individual -/// bits as well as bitwise operations on the entire set. The interface is -/// most like that of unsigned integers, and is intended to be used as such. -/// If you were using begin() and end() functions in STL's bitset, you would -/// not be able to do the same thing here, because those functions return -/// host type iterators, not bits. -/// -template -class bitset { -public: - typedef bitset_value_type value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef pointer iterator; - typedef const_pointer const_iterator; - typedef size_t difference_type; - typedef size_t size_type; - typedef const bitset& rcself_t; -private: - static const size_t s_WordBits = BitsInType (value_type); - static const size_t s_nWords = Size / s_WordBits + ((Size % s_WordBits) != 0); - static const size_t s_nBits = s_nWords * s_WordBits; -private: - inline value_type& BitRef (uoff_t n) { assert (n < Size); return _bits [n / s_WordBits]; } - inline value_type BitRef (uoff_t n) const { assert (n < Size); return _bits [n / s_WordBits]; } - inline value_type Mask (uoff_t n) const { assert (n < Size); return 1 << (n % s_WordBits); } -public: - inline bitset (value_type v = 0) { fill_n (_bits, s_nWords, 0); _bits[0] = v; } - inline bitset (const string& buf) { convert_from_bitstring (buf, _bits, s_nWords); } - inline void flip (uoff_t n) { BitRef(n) ^= Mask(n); } - inline void reset (void) { fill_n (_bits, s_nWords, 0); } - inline void clear (void) { fill_n (_bits, s_nWords, 0); } - inline void set (void) { fill_n (_bits, s_nWords, -1); } - inline bitset operator~ (void) const { bitset rv (*this); rv.flip(); return rv; } - inline size_type size (void) const { return Size; } - inline size_type capacity (void) const { return s_nBits; } - inline bool test (uoff_t n) const { return BitRef(n) & Mask(n); } - inline bool operator[] (uoff_t n) const { return test(n); } - inline const_iterator begin (void) const { return _bits; } - inline iterator begin (void) { return _bits; } - inline const_iterator end (void) const { return _bits + s_nWords; } - inline iterator end (void) { return _bits + s_nWords; } - /// Returns the value_type with the equivalent bits. If size() > 1, you'll get only the first BitsInType(value_type) bits. - inline value_type to_value (void) const { return _bits[0]; } - /// Flips all the bits in the set. - inline void flip (void) { transform (begin(), end(), begin(), bitwise_not()); } - /// Sets or clears bit \p n. - inline void set (uoff_t n, bool val = true) - { - value_type& br (BitRef (n)); - const value_type mask (Mask (n)); - const value_type bOn (br | mask), bOff (br & ~mask); - br = val ? bOn : bOff; - } - // Sets the value of the bitrange \p first through \p last to the equivalent number of bits from \p v. - inline void set (uoff_t first, uoff_t DebugArg(last), value_type v) - { - assert (size_t (distance (first, last)) <= s_WordBits && "Bit ranges must be 32 bits or smaller"); - assert (first / s_WordBits == last / s_WordBits && "Bit ranges can not cross dword (4 byte) boundary"); - assert ((v & BitMask(value_type,distance(first,last))) == v && "The value is too large to fit in the given bit range"); - BitRef(first) |= v << (first % s_WordBits); - } - /// Clears the bit \p n. - inline void reset (uoff_t n) { set (n, false); } - /// Returns a string with bits MSB "001101001..." LSB. - inline string to_string (void) const - { - string rv (Size, '0'); - convert_to_bitstring (_bits, s_nWords, rv); - return rv; - } - inline value_type at (uoff_t n) const { return test(n); } - /// Returns the value in bits \p first through \p last. - inline value_type at (uoff_t first, uoff_t last) const - { - assert (size_t (distance (first, last)) <= s_WordBits && "Bit ranges must be 32 bits or smaller"); - assert (first / s_WordBits == last / s_WordBits && "Bit ranges can not cross dword (4 byte) boundary"); - return (BitRef(first) >> (first % s_WordBits)) & BitMask(value_type,distance(first, last)); - } - inline bool any (void) const { value_type sum = 0; foreach (const_iterator, i, *this) sum |= *i; return sum; } - inline bool none (void) const { return !any(); } - inline size_t count (void) const { size_t sum = 0; foreach (const_iterator, i, *this) sum += popcount(*i); return sum; } - inline bool operator== (rcself_t v) const - { return s_nWords == 1 ? (_bits[0] == v._bits[0]) : equal (begin(), end(), v.begin()); } - inline bitset operator& (rcself_t v) const - { bitset result; transform (begin(), end(), v.begin(), result.begin(), bitwise_and()); return result; } - inline bitset operator| (rcself_t v) const - { bitset result; transform (begin(), end(), v.begin(), result.begin(), bitwise_or()); return result; } - inline bitset operator^ (rcself_t v) const - { bitset result; transform (begin(), end(), v.begin(), result.begin(), bitwise_xor()); return result; } - inline rcself_t operator&= (rcself_t v) - { transform (begin(), end(), v.begin(), begin(), bitwise_and()); return *this; } - inline rcself_t operator|= (rcself_t v) - { transform (begin(), end(), v.begin(), begin(), bitwise_or()); return *this; } - inline rcself_t operator^= (rcself_t v) - { transform (begin(), end(), v.begin(), begin(), bitwise_xor()); return *this; } - inline void read (istream& is) { nr_container_read (is, *this); } - inline void write (ostream& os) const { nr_container_write (os, *this); } - inline void text_write (ostringstream& os) const { os << to_string(); } - void text_read (istringstream& is); - inline size_t stream_size (void) const { return sizeof(_bits); } -private: - value_type _bits [s_nWords]; -}; - -} // namespace ustl diff --git a/common/include/ustl/uctralgo.h b/common/include/ustl/uctralgo.h deleted file mode 100644 index aab0bb0e9..000000000 --- a/common/include/ustl/uctralgo.h +++ /dev/null @@ -1,467 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once - -namespace ustl { - -/// Copy copies elements from the range [first, last) to the range -/// [result, result + (last - first)). That is, it performs the assignments -/// *result = *first, *(result + 1) = *(first + 1), and so on. [1] Generally, -/// for every integer n from 0 to last - first, copy performs the assignment -/// *(result + n) = *(first + n). Assignments are performed in forward order, -/// i.e. in order of increasing n. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator copy (const Container& ctr, OutputIterator result) -{ - return copy (ctr.begin(), ctr.end(), result); -} - -/// Copy_if copies elements from the range [first, last) to the range -/// [result, result + (last - first)) if pred(*i) returns true. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator copy_if (Container& ctr, OutputIterator result, Predicate pred) -{ - return copy_if (ctr.begin(), ctr.end(), result, pred); -} - -/// For_each applies the function object f to each element in the range -/// [first, last); f's return value, if any, is ignored. Applications are -/// performed in forward order, i.e. from first to last. For_each returns -/// the function object after it has been applied to each element. -/// \ingroup MutatingAlgorithms -/// -template -inline UnaryFunction for_each (Container& ctr, UnaryFunction f) -{ - return for_each (ctr.begin(), ctr.end(), f); -} - -/// For_each applies the function object f to each element in the range -/// [first, last); f's return value, if any, is ignored. Applications are -/// performed in forward order, i.e. from first to last. For_each returns -/// the function object after it has been applied to each element. -/// \ingroup MutatingAlgorithms -/// -template -inline UnaryFunction for_each (const Container& ctr, UnaryFunction f) -{ - return for_each (ctr.begin(), ctr.end(), f); -} - -/// Returns the first iterator i in the range [first, last) such that -/// *i == value. Returns last if no such iterator exists. -/// \ingroup SearchingAlgorithms -/// -template -inline typename Container::const_iterator find (const Container& ctr, const EqualityComparable& value) -{ - return find (ctr.begin(), ctr.end(), value); -} -template -inline typename Container::iterator find (Container& ctr, const EqualityComparable& value) -{ - return find (ctr.begin(), ctr.end(), value); -} - -/// Returns the first iterator i in the range [first, last) such that -/// pred(*i) is true. Returns last if no such iterator exists. -/// \ingroup SearchingAlgorithms -/// -template -inline typename Container::const_iterator find_if (const Container& ctr, Predicate pred) -{ - return find_if (ctr.begin(), ctr.end(), pred); -} -template -inline typename Container::iterator find_if (Container& ctr, Predicate pred) -{ - return find_if (ctr.begin(), ctr.end(), pred); -} - -/// Count finds the number of elements in [first, last) that are equal -/// to value. More precisely, the first version of count returns the -/// number of iterators i in [first, last) such that *i == value. -/// \ingroup ConditionAlgorithms -/// -template -inline size_t count (const Container& ctr, const EqualityComparable& value) -{ - return count (ctr.begin(), ctr.end(), value); -} - -/// Count_if finds the number of elements in [first, last) that satisfy the -/// predicate pred. More precisely, the first version of count_if returns the -/// number of iterators i in [first, last) such that pred(*i) is true. -/// \ingroup ConditionAlgorithms -/// -template -inline size_t count_if (const Container& ctr, Predicate pred) -{ - return count_if (ctr.begin(), ctr.end(), pred); -} - -/// The first version of transform performs the operation op(*i) for each -/// iterator i in the range [first, last), and assigns the result of that -/// operation to *o, where o is the corresponding output iterator. That is, -/// for each n such that 0 <= n < last - first, it performs the assignment -/// *(result + n) = op(*(first + n)). -/// The return value is result + (last - first). -/// \ingroup MutatingAlgorithms -/// -template -inline void transform (Container& ctr, UnaryFunction op) -{ - transform (ctr.begin(), ctr.end(), ctr.begin(), op); -} - -/// The first version of transform performs the operation op(*i) for each -/// iterator i in the range [first, last), and assigns the result of that -/// operation to *o, where o is the corresponding output iterator. That is, -/// for each n such that 0 <= n < last - first, it performs the assignment -/// *(result + n) = op(*(first + n)). -/// The return value is result + (last - first). -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator transform (Container& ctr, OutputIterator result, UnaryFunction op) -{ - return transform (ctr.begin(), ctr.end(), result, op); -} - -/// The second version of transform is very similar, except that it uses a -/// Binary Function instead of a Unary Function: it performs the operation -/// op(*i1, *i2) for each iterator i1 in the range [first1, last1) and assigns -/// the result to *o, where i2 is the corresponding iterator in the second -/// input range and where o is the corresponding output iterator. That is, -/// for each n such that 0 <= n < last1 - first1, it performs the assignment -/// *(result + n) = op(*(first1 + n), *(first2 + n). -/// The return value is result + (last1 - first1). -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator transform (Container& ctr, InputIterator first, OutputIterator result, BinaryFunction op) -{ - return transform (ctr.begin(), ctr.end(), first, result, op); -} - -/// Replace replaces every element in the range [first, last) equal to -/// old_value with new_value. That is: for every iterator i, -/// if *i == old_value then it performs the assignment *i = new_value. -/// \ingroup MutatingAlgorithms -/// -template -inline void replace (Container& ctr, const T& old_value, const T& new_value) -{ - replace (ctr.begin(), ctr.end(), old_value, new_value); -} - -/// Replace_if replaces every element in the range [first, last) for which -/// pred returns true with new_value. That is: for every iterator i, if -/// pred(*i) is true then it performs the assignment *i = new_value. -/// \ingroup MutatingAlgorithms -/// -template -inline void replace_if (Container& ctr, Predicate pred, const T& new_value) -{ - replace_if (ctr.begin(), ctr.end(), pred, new_value); -} - -/// Replace_copy copies elements from the range [first, last) to the range -/// [result, result + (last-first)), except that any element equal to old_value -/// is not copied; new_value is copied instead. More precisely, for every -/// integer n such that 0 <= n < last-first, replace_copy performs the -/// assignment *(result+n) = new_value if *(first+n) == old_value, and -/// *(result+n) = *(first+n) otherwise. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator replace_copy (const Container& ctr, OutputIterator result, const T& old_value, const T& new_value) -{ - return replace_copy (ctr.begin(), ctr.end(), result, old_value, new_value); -} - -/// Replace_copy_if copies elements from the range [first, last) to the range -/// [result, result + (last-first)), except that any element for which pred is -/// true is not copied; new_value is copied instead. More precisely, for every -/// integer n such that 0 <= n < last-first, replace_copy_if performs the -/// assignment *(result+n) = new_value if pred(*(first+n)), -/// and *(result+n) = *(first+n) otherwise. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator replace_copy_if (const Container& ctr, OutputIterator result, Predicate pred, const T& new_value) -{ - return replace_copy_if (ctr.begin(), ctr.end(), result, pred, new_value); -} - -/// Fill assigns the value value to every element in the range [first, last). -/// That is, for every iterator i in [first, last), -/// it performs the assignment *i = value. -/// \ingroup GeneratorAlgorithms -/// -template -inline void fill (Container& ctr, const T& value) -{ - fill (ctr.begin(), ctr.end(), value); -} - -/// Generate assigns the result of invoking gen, a function object that -/// takes no arguments, to each element in the range [first, last). -/// \ingroup GeneratorAlgorithms -/// -template -inline void generate (Container& ctr, Generator gen) -{ - generate (ctr.begin(), ctr.end(), gen); -} - -/// Randomly permute the elements of the container. -/// \ingroup GeneratorAlgorithms -/// -template -inline void random_shuffle (Container& ctr) -{ - random_shuffle (ctr.begin(), ctr.end()); -} - -/// Remove_copy copies elements that are not equal to value from the range -/// [first, last) to a range beginning at result. The return value is the -/// end of the resulting range. This operation is stable, meaning that the -/// relative order of the elements that are copied is the same as in the -/// range [first, last). -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator remove_copy (const Container& ctr, OutputIterator result, const T& value) -{ - return remove_copy (ctr.begin(), ctr.end(), result, value); -} - -/// Remove_copy_if copies elements from the range [first, last) to a range -/// beginning at result, except that elements for which pred is true are not -/// copied. The return value is the end of the resulting range. This operation -/// is stable, meaning that the relative order of the elements that are copied -/// is the same as in the range [first, last). -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator remove_copy_if (const Container& ctr, OutputIterator result, Predicate pred) -{ - return remove_copy_if (ctr.begin(), ctr.end(), result, pred); -} - -/// Remove removes from the range [first, last) all elements that are equal to -/// value. That is, remove returns an iterator new_last such that the range -/// [first, new_last) contains no elements equal to value. Remove is stable, -/// meaning that the relative order of elements that are not equal to value is -/// unchanged. -/// \ingroup MutatingAlgorithms -/// -template -inline void remove (Container& ctr, const T& value) -{ - ctr.erase (remove_copy (ctr.begin(), ctr.end(), ctr.begin(), value), ctr.end()); -} - -/// Remove removes from the range [first, last) all elements that have an iterator -/// in range [rfirst, rlast). The range is assumed to be sorted. That is, remove -/// returns an iterator new_last such that the range [first, new_last) contains -/// no elements whose iterators are in [rfirst, rlast). Remove is stable, -/// meaning that the relative order of elements that are not equal to value is -/// unchanged. This version of the algorithm is a uSTL extension. -/// \ingroup MutatingAlgorithms -/// -template -inline void remove (Container& ctr, ForwardIterator rfirst, ForwardIterator rlast) -{ - ctr.erase (remove_copy (ctr.begin(), ctr.end(), ctr.begin(), rfirst, rlast), ctr.end()); -} - -/// Remove_if removes from the range [first, last) every element x such that -/// pred(x) is true. That is, remove_if returns an iterator new_last such that -/// the range [first, new_last) contains no elements for which pred is true. -/// The iterators in the range [new_last, last) are all still dereferenceable, -/// but the elements that they point to are unspecified. Remove_if is stable, -/// meaning that the relative order of elements that are not removed is -/// unchanged. -/// \ingroup MutatingAlgorithms -/// -template -inline void remove_if (Container& ctr, Predicate pred) -{ - ctr.erase (remove_copy_if (ctr.begin(), ctr.end(), ctr.begin(), pred), ctr.end()); -} - -/// Unique_copy copies elements from the range [first, last) to a range -/// beginning with result, except that in a consecutive group of duplicate -/// elements only the first one is copied. The return value is the end of -/// the range to which the elements are copied. This behavior is similar -/// to the Unix filter uniq. -/// \ingroup MutatingAlgorithms -/// -template -inline OutputIterator unique_copy (const Container& ctr, OutputIterator result) -{ - return unique_copy (ctr.begin(), ctr.end(), result); -} - -/// Every time a consecutive group of duplicate elements appears in the range -/// [first, last), the algorithm unique removes all but the first element. -/// That is, unique returns an iterator new_last such that the range [first, -/// new_last) contains no two consecutive elements that are duplicates. -/// The iterators in the range [new_last, last) are all still dereferenceable, -/// but the elements that they point to are unspecified. Unique is stable, -/// meaning that the relative order of elements that are not removed is -/// unchanged. -/// \ingroup MutatingAlgorithms -/// -template -inline void unique (Container& ctr) -{ - ctr.erase (unique_copy (ctr.begin(), ctr.end(), ctr.begin()), ctr.end()); -} - -/// Every time a consecutive group of duplicate elements appears in the range -/// [first, last), the algorithm unique removes all but the first element. -/// That is, unique returns an iterator new_last such that the range [first, -/// new_last) contains no two consecutive elements that are duplicates. -/// The iterators in the range [new_last, last) are all still dereferenceable, -/// but the elements that they point to are unspecified. Unique is stable, -/// meaning that the relative order of elements that are not removed is -/// unchanged. -/// \ingroup MutatingAlgorithms -/// -template -inline void unique (Container& ctr, BinaryPredicate binary_pred) -{ - ctr.erase (unique_copy (ctr.begin(), ctr.end(), ctr.begin(), binary_pred), ctr.end()); -} - -/// Reverse reverses a range. -/// That is: for every i such that 0 <= i <= (last - first) / 2), -/// it exchanges *(first + i) and *(last - (i + 1)). -/// \ingroup MutatingAlgorithms -/// -template -inline void reverse (Container& ctr) -{ - reverse (ctr.begin(), ctr.end()); -} - -/// Exchanges ranges [first, middle) and [middle, last) -/// \ingroup MutatingAlgorithms -/// -template -inline void rotate (Container& ctr, off_t offset) -{ - assert (size_t(offset > 0 ? offset : -offset) < ctr.size()); - if (offset > 0) - rotate (ctr.begin(), ctr.end() - offset, ctr.end()); - else - rotate (ctr.begin(), ctr.begin() - offset, ctr.end()); -} - -/// Returns the furthermost iterator i in [first, last) such that, -/// for every iterator j in [first, i), *j < value -/// Assumes the range is sorted. -/// \ingroup SearchingAlgorithms -/// -template -inline typename Container::const_iterator lower_bound (const Container& ctr, const LessThanComparable& value) -{ - return lower_bound (ctr.begin(), ctr.end(), value); -} -template -inline typename Container::iterator lower_bound (Container& ctr, const LessThanComparable& value) -{ - return lower_bound (ctr.begin(), ctr.end(), value); -} - -/// Returns the furthermost iterator i in [first,last) such that for -/// every iterator j in [first,i), value < *j is false. -/// \ingroup SearchingAlgorithms -/// -template -inline typename Container::const_iterator upper_bound (const Container& ctr, const LessThanComparable& value) -{ - return upper_bound (ctr.begin(), ctr.end(), value); -} -template -inline typename Container::iterator upper_bound (Container& ctr, const LessThanComparable& value) -{ - return upper_bound (ctr.begin(), ctr.end(), value); -} - -/// Performs a binary search for \p value. -/// Assumes the range is sorted. -/// \ingroup SearchingAlgorithms -/// -template -inline bool binary_search (const Container& ctr, const typename Container::value_type& value) -{ - return binary_search (ctr.begin(), ctr.end(), value); -} -template -inline bool binary_search (Container& ctr, const typename Container::value_type& value) -{ - return binary_search (ctr.begin(), ctr.end(), value); -} - -/// Returns pair -/// \ingroup SearchingAlgorithms -/// -template -inline pair equal_range (const Container& ctr, const LessThanComparable& value) -{ - return equal_range (ctr.begin(), ctr.end(), value); -} -template -inline pair equal_range (Container& ctr, const LessThanComparable& value) -{ - return equal_range (ctr.begin(), ctr.end(), value); -} - -/// Sorts the container -/// \ingroup SortingAlgorithms -/// -template -inline void sort (Container& ctr) -{ - sort (ctr.begin(), ctr.end()); -} - -/// Sorts the container -/// \ingroup SortingAlgorithms -/// -template -inline void sort (Container& ctr, Compare comp) -{ - sort (ctr.begin(), ctr.end(), comp); -} - -/// Sorts the container -/// \ingroup SortingAlgorithms -/// -template -inline void stable_sort (Container& ctr) -{ - stable_sort (ctr.begin(), ctr.end()); -} - -/// Sorts the container -/// \ingroup SortingAlgorithms -/// -template -inline void stable_sort (Container& ctr, Compare comp) -{ - stable_sort (ctr.begin(), ctr.end(), comp); -} - -} // namespace ustl diff --git a/common/include/ustl/ufunction.h b/common/include/ustl/ufunction.h deleted file mode 100644 index 2c5b5e5a4..000000000 --- a/common/include/ustl/ufunction.h +++ /dev/null @@ -1,462 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once - -namespace ustl { - -//---------------------------------------------------------------------- -// Standard functors -//---------------------------------------------------------------------- - -/// \brief void-returning function abstract interface. -/// \ingroup FunctorObjects -template -struct void_function { - typedef Result result_type; -}; - -/// \brief \p Result f (\p Arg) function abstract interface. -/// \ingroup FunctorObjects -template -struct unary_function { - typedef Arg argument_type; - typedef Result result_type; -}; - -/// \brief \p Result f (\p Arg1, \p Arg2) function abstract interface. -/// \ingroup FunctorObjects -template -struct binary_function { - typedef Arg1 first_argument_type; - typedef Arg2 second_argument_type; - typedef Result result_type; -}; - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - -#define STD_BINARY_FUNCTOR(name, rv, func) \ -template struct name : public binary_function \ -{ inline rv operator()(const T& a, const T& b) const { return func; } }; -#define STD_UNARY_FUNCTOR(name, rv, func) \ -template struct name : public unary_function \ -{ inline rv operator()(const T& a) const { return func; } }; -#define STD_CONVERSION_FUNCTOR(name, func) \ -template struct name : public unary_function \ -{ inline D operator()(const S& a) const { return func; } }; - -STD_BINARY_FUNCTOR (plus, T, (a + b)) -STD_BINARY_FUNCTOR (minus, T, (a - b)) -STD_BINARY_FUNCTOR (divides, T, (a / b)) -STD_BINARY_FUNCTOR (modulus, T, (a % b)) -STD_BINARY_FUNCTOR (multiplies, T, (a * b)) -STD_BINARY_FUNCTOR (logical_and, T, (a && b)) -STD_BINARY_FUNCTOR (logical_or, T, (a || b)) -STD_UNARY_FUNCTOR (logical_not, T, (!a)) -STD_BINARY_FUNCTOR (bitwise_or, T, (a | b)) -STD_BINARY_FUNCTOR (bitwise_and, T, (a & b)) -STD_BINARY_FUNCTOR (bitwise_xor, T, (a ^ b)) -STD_UNARY_FUNCTOR (bitwise_not, T, (~a)) -STD_UNARY_FUNCTOR (negate, T, (-a)) -STD_BINARY_FUNCTOR (equal_to, bool, (a == b)) -STD_BINARY_FUNCTOR (not_equal_to, bool, (!(a == b))) -STD_BINARY_FUNCTOR (greater, bool, (b < a)) -STD_BINARY_FUNCTOR (less, bool, (a < b)) -STD_BINARY_FUNCTOR (greater_equal, bool, (!(a < b))) -STD_BINARY_FUNCTOR (less_equal, bool, (!(b < a))) -STD_BINARY_FUNCTOR (compare, int, (a < b ? -1 : (b < a))) -STD_UNARY_FUNCTOR (identity, T, (a)) - -#endif // DOXYGEN_SHOULD_SKIP_THIS - -/// \brief Selects and returns the first argument. -/// \ingroup FunctorObjects -template struct project1st : public binary_function { inline const T1& operator()(const T1& a, const T2&) const { return a; } }; -/// \brief Selects and returns the second argument. -/// \ingroup FunctorObjects -template struct project2nd : public binary_function { inline const T2& operator()(const T1&, const T2& a) const { return a; } }; - -//---------------------------------------------------------------------- -// Generic function to functor converters. -//---------------------------------------------------------------------- - -/// \brief Wrapper object for unary function pointers. -/// Use the ptr_fun accessor to create this object. -/// \ingroup FunctorObjects -template -class pointer_to_unary_function : public unary_function { -public: - typedef Arg argument_type; - typedef Result result_type; - typedef Result (*pfunc_t)(Arg); -public: - explicit inline pointer_to_unary_function (pfunc_t pfn) : _pfn (pfn) {} - inline result_type operator() (argument_type v) const { return _pfn(v); } -private: - pfunc_t _pfn; ///< Pointer to the wrapped function. -}; - -/// \brief Wrapper object for binary function pointers. -/// Use the ptr_fun accessor to create this object. -/// \ingroup FunctorObjects -template -class pointer_to_binary_function : public binary_function { -public: - typedef Arg1 first_argument_type; - typedef Arg2 second_argument_type; - typedef Result result_type; - typedef Result (*pfunc_t)(Arg1, Arg2); -public: - explicit inline pointer_to_binary_function (pfunc_t pfn) : _pfn (pfn) {} - inline result_type operator() (first_argument_type v1, second_argument_type v2) const { return _pfn(v1, v2); } -private: - pfunc_t _pfn; ///< Pointer to the wrapped function. -}; - -/// ptr_fun(pfn) wraps function pointer pfn into a functor class that calls it. -/// \ingroup FunctorAccessors -template -inline pointer_to_unary_function ptr_fun (Result (*pfn)(Arg)) -{ - return pointer_to_unary_function (pfn); -} - -/// ptr_fun(pfn) wraps function pointer pfn into a functor class that calls it. -/// \ingroup FunctorAccessors -template -inline pointer_to_binary_function ptr_fun (Result (*pfn)(Arg1,Arg2)) -{ - return pointer_to_binary_function (pfn); -} - -//---------------------------------------------------------------------- -// Negators. -//---------------------------------------------------------------------- - -/// \brief Wraps a unary function to return its logical negative. -/// Use the unary_negator accessor to create this object. -/// \ingroup FunctorObjects -template -class unary_negate : public unary_function { -public: - typedef typename UnaryFunction::argument_type argument_type; - typedef typename UnaryFunction::result_type result_type; -public: - explicit inline unary_negate (UnaryFunction pfn) : _pfn (pfn) {} - inline result_type operator() (argument_type v) const { return !_pfn(v); } -private: - UnaryFunction _pfn; -}; - -/// Returns the functor that negates the result of *pfn(). -/// \ingroup FunctorAccessors -template -inline unary_negate unary_negator (UnaryFunction pfn) -{ - return unary_negate(pfn); -} - -//---------------------------------------------------------------------- -// Argument binders -//---------------------------------------------------------------------- - -/// \brief Converts a binary function to a unary function -/// by binding a constant value to the first argument. -/// Use the bind1st accessor to create this object. -/// \ingroup FunctorObjects -template -class binder1st : public unary_function { -public: - typedef typename BinaryFunction::first_argument_type arg1_t; - typedef typename BinaryFunction::second_argument_type arg2_t; - typedef typename BinaryFunction::result_type result_t; -public: - inline binder1st (const BinaryFunction& pfn, const arg1_t& v) : _pfn (pfn), _v(v) {} - inline result_t operator()(arg2_t v2) const { return _pfn (_v, v2); } -protected: - BinaryFunction _pfn; - arg1_t _v; -}; - -/// \brief Converts a binary function to a unary function -/// by binding a constant value to the second argument. -/// Use the bind2nd accessor to create this object. -/// \ingroup FunctorObjects -template -class binder2nd : public unary_function { -public: - typedef typename BinaryFunction::first_argument_type arg1_t; - typedef typename BinaryFunction::second_argument_type arg2_t; - typedef typename BinaryFunction::result_type result_t; -public: - inline binder2nd (const BinaryFunction& pfn, const arg2_t& v) : _pfn (pfn), _v(v) {} - inline result_t operator()(arg1_t v1) const { return _pfn (v1, _v); } -protected: - BinaryFunction _pfn; - arg2_t _v; -}; - -/// Converts \p pfn into a unary function by binding the first argument to \p v. -/// \ingroup FunctorAccessors -template -inline binder1st -bind1st (BinaryFunction pfn, typename BinaryFunction::first_argument_type v) -{ - return binder1st (pfn, v); -} - -/// Converts \p pfn into a unary function by binding the second argument to \p v. -/// \ingroup FunctorAccessors -template -inline binder2nd -bind2nd (BinaryFunction pfn, typename BinaryFunction::second_argument_type v) -{ - return binder2nd (pfn, v); -} - -//---------------------------------------------------------------------- -// Composition adapters -//---------------------------------------------------------------------- - -/// \brief Chains two unary functions together. -/// -/// When f(x) and g(x) are composed, the result is function c(x)=f(g(x)). -/// Use the \ref compose1 accessor to create this object. -/// This template is an extension, implemented by SGI STL and uSTL. -/// \ingroup FunctorObjects -/// -template -class unary_compose : public unary_function { -public: - typedef typename Operation2::argument_type arg_t; - typedef const arg_t& rcarg_t; - typedef typename Operation1::result_type result_t; -public: - inline unary_compose (const Operation1& f, const Operation2& g) : _f(f), _g(g) {} - inline result_t operator() (rcarg_t x) const { return _f(_g(x)); } -protected: - Operation1 _f; ///< f(x), if c(x) = f(g(x)) - Operation2 _g; ///< g(x), if c(x) = f(g(x)) -}; - -/// Creates a \ref unary_compose object whose function c(x)=f(g(x)) -/// \ingroup FunctorAccessors -template -inline unary_compose -compose1 (const Operation1& f, const Operation2& g) -{ return unary_compose(f, g); } - -/// \brief Chains two unary functions through a binary function. -/// -/// When f(x,y), g(x), and h(x) are composed, the result is function -/// c(x)=f(g(x),h(x)). Use the \ref compose2 accessor to create this -/// object. This template is an extension, implemented by SGI STL and uSTL. -/// \ingroup FunctorObjects -/// -template -class binary_compose : public unary_function { -public: - typedef typename Operation2::argument_type arg_t; - typedef const arg_t& rcarg_t; - typedef typename Operation1::result_type result_t; -public: - inline binary_compose (const Operation1& f, const Operation2& g, const Operation3& h) : _f(f), _g(g), _h(h) {} - inline result_t operator() (rcarg_t x) const { return _f(_g(x), _h(x)); } -protected: - Operation1 _f; ///< f(x,y), if c(x) = f(g(x),h(x)) - Operation2 _g; ///< g(x), if c(x) = f(g(x),h(x)) - Operation3 _h; ///< h(x), if c(x) = f(g(x),h(x)) -}; - -/// Creates a \ref binary_compose object whose function c(x)=f(g(x),h(x)) -/// \ingroup FunctorAccessors -template -inline binary_compose -compose2 (const Operation1& f, const Operation2& g, const Operation3& h) -{ return binary_compose (f, g, h); } - -//---------------------------------------------------------------------- -// Member function adaptors -//---------------------------------------------------------------------- - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - -#define MEM_FUN_T(WrapperName, ClassName, ArgType, FuncType, CallType) \ - template \ - class ClassName : public unary_function { \ - public: \ - typedef Ret (T::*func_t) FuncType; \ - public: \ - explicit inline ClassName (func_t pf) : _pf (pf) {} \ - inline Ret operator() (ArgType p) const { return (p CallType _pf)(); } \ - private: \ - func_t _pf; \ - }; \ - \ - template \ - inline ClassName WrapperName (Ret (T::*pf) FuncType) \ - { \ - return ClassName (pf); \ - } - -MEM_FUN_T(mem_fun, mem_fun_t, T*, (void), ->*) -MEM_FUN_T(mem_fun, const_mem_fun_t, const T*, (void) const, ->*) -MEM_FUN_T(mem_fun_ref, mem_fun_ref_t, T&, (void), .*) -MEM_FUN_T(mem_fun_ref, const_mem_fun_ref_t, const T&, (void) const, .*) - -#define EXT_MEM_FUN_T(ClassName, HostType, FuncType) \ - template \ - class ClassName : public unary_function { \ - public: \ - typedef Ret (T::*func_t)(V) FuncType; \ - public: \ - inline ClassName (HostType t, func_t pf) : _t (t), _pf (pf) {} \ - inline Ret operator() (V v) const { return (_t->*_pf)(v); } \ - private: \ - HostType _t; \ - func_t _pf; \ - }; \ - \ - template \ - inline ClassName mem_fun (HostType p, Ret (T::*pf)(V) FuncType) \ - { \ - return ClassName (p, pf); \ - } - -EXT_MEM_FUN_T(ext_mem_fun_t, T*, ) -EXT_MEM_FUN_T(const_ext_mem_fun_t, const T*, const) - -#endif // DOXYGEN_SHOULD_SKIP_THIS - -//---------------------------------------------------------------------- -// Member variable adaptors (uSTL extension) -//---------------------------------------------------------------------- - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - -#define MEM_VAR_T(FunctorName, ArgType, VarType, BaseClass, CallImpl) \ - template \ - class FunctorName##_t : public BaseClass { \ - public: \ - typedef ArgType argument_type; \ - typedef typename Function::result_type result_type; \ - typedef VarType mem_var_ptr_t; \ - public: \ - inline FunctorName##_t (mem_var_ptr_t pv, Function pfn) : _pv(pv), _pfn(pfn) {} \ - inline result_type operator() CallImpl \ - private: \ - mem_var_ptr_t _pv; \ - Function _pfn; \ - }; \ - \ - template \ - inline FunctorName##_t \ - FunctorName (VT T::*mvp, Function pfn) \ - { \ - return FunctorName##_t (mvp, pfn); \ - } - -#define FUNCTOR_UNARY_BASE(ArgType) unary_function -#define FUNCTOR_BINARY_BASE(ArgType) binary_function - -#define MEM_VAR_UNARY_ARGS (argument_type p) const \ - { return _pfn(p.*_pv); } -#define MEM_VAR_BINARY_ARGS (argument_type p1, argument_type p2) const \ - { return _pfn(p1.*_pv, p2.*_pv); } - -MEM_VAR_T(mem_var1, T&, VT T::*, FUNCTOR_UNARY_BASE(T&), MEM_VAR_UNARY_ARGS) -MEM_VAR_T(const_mem_var1, const T&, const VT T::*, FUNCTOR_UNARY_BASE(T&), MEM_VAR_UNARY_ARGS) -MEM_VAR_T(mem_var2, T&, VT T::*, FUNCTOR_BINARY_BASE(T&), MEM_VAR_BINARY_ARGS) -MEM_VAR_T(const_mem_var2, const T&, const VT T::*, FUNCTOR_BINARY_BASE(T&), MEM_VAR_BINARY_ARGS) - -#undef MEM_VAR_UNARY_ARGS -#undef MEM_VAR_BINARY_ARGS - -#endif // DOXYGEN_SHOULD_SKIP_THIS - -/// Returned functor passes member variable \p mvp reference of given object to equal\. -/// \ingroup FunctorAccessors -template -inline const_mem_var1_t >, T, VT> -mem_var_equal_to (const VT T::*mvp, const VT& v) -{ - return const_mem_var1_t >,T,VT> (mvp, bind2nd(equal_to(), v)); -} - -/// Returned functor passes member variable \p mvp reference of given object to less\. -/// \ingroup FunctorAccessors -template -inline const_mem_var1_t >, T, VT> -mem_var_less (const VT T::*mvp, const VT& v) -{ - return const_mem_var1_t >,T,VT> (mvp, bind2nd(less(), v)); -} - -/// Returned functor passes member variable \p mvp reference of given object to equal\. -/// \ingroup FunctorAccessors -template -inline const_mem_var2_t, T, VT> -mem_var_equal_to (const VT T::*mvp) -{ - return const_mem_var2_t,T,VT> (mvp, equal_to()); -} - -/// Returned functor passes member variable \p mvp reference of given object to less\. -/// \ingroup FunctorAccessors -template -inline const_mem_var2_t, T, VT> -mem_var_less (const VT T::*mvp) -{ - return const_mem_var2_t,T,VT> (mvp, less()); -} - -//---------------------------------------------------------------------- -// Dereference adaptors (uSTL extension) -//---------------------------------------------------------------------- - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - -#define DEREFERENCER_T(ClassName, ArgType, BaseClass, CallImpl, FunctorKey) \ - template \ - class ClassName : public BaseClass { \ - public: \ - typedef ArgType* argument_type; \ - typedef typename Function::result_type result_type; \ - public: \ - inline ClassName (Function pfn) : _pfn (pfn) {} \ - inline result_type operator() CallImpl \ - private: \ - Function _pfn; \ - }; \ - \ - template \ - inline ClassName _dereference (Function pfn, FunctorKey) \ - { \ - return ClassName (pfn); \ - } - -#define DEREF_UNARY_ARGS (argument_type p) const \ - { return _pfn(*p); } -#define DEREF_BINARY_ARGS (argument_type p1, argument_type p2) const \ - { return _pfn(*p1, *p2); } - -DEREFERENCER_T(deref1_t, T, FUNCTOR_UNARY_BASE(T*), DEREF_UNARY_ARGS, FUNCTOR_UNARY_BASE(T)) -DEREFERENCER_T(const_deref1_t, const T, FUNCTOR_UNARY_BASE(const T*), DEREF_UNARY_ARGS, FUNCTOR_UNARY_BASE(const T)) -DEREFERENCER_T(deref2_t, T, FUNCTOR_BINARY_BASE(T*), DEREF_BINARY_ARGS, FUNCTOR_BINARY_BASE(T)) -DEREFERENCER_T(const_deref2_t, const T, FUNCTOR_BINARY_BASE(const T*), DEREF_BINARY_ARGS, FUNCTOR_BINARY_BASE(const T)) - -#define dereference(f) _dereference(f,f) - -#undef DEREF_UNARY_ARGS -#undef DEREF_BINARY_ARGS - -#endif - -} // namespace ustl diff --git a/common/include/ustl/uheap.h b/common/include/ustl/uheap.h deleted file mode 100644 index 2ed0563e7..000000000 --- a/common/include/ustl/uheap.h +++ /dev/null @@ -1,161 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "ualgobase.h" - -namespace ustl { - -/// \brief Returns true if the given range is a heap under \p comp. -/// A heap is a sequentially encoded binary tree where for every node -/// comp(node,child1) is false and comp(node,child2) is false. -/// \ingroup HeapAlgorithms -/// \ingroup ConditionAlgorithms -/// -template -bool is_heap (RandomAccessIterator first, RandomAccessIterator last, Compare comp) -{ - RandomAccessIterator iChild (first); - for (; ++iChild < last; ++first) - if (comp (*first, *iChild) || (++iChild < last && comp (*first, *iChild))) - return false; - return true; -} - -/// Utility function to "trickle down" the root item - swaps the root item with its -/// largest child and recursively fixes the proper subtree. -template -void trickle_down_heap (RandomAccessIterator first, size_t iHole, size_t heapSize, Compare comp) -{ - typedef typename iterator_traits::value_type value_type; - const value_type v (first[iHole]); - for (size_t iChild; (iChild = 2 * iHole + 1) < heapSize;) { - if (iChild + 1 < heapSize) - iChild += comp (first[iChild], first[iChild + 1]); - if (comp (v, first[iChild])) { - first[iHole] = first[iChild]; - iHole = iChild; - } else - break; - } - first[iHole] = v; -} - -/// \brief make_heap turns the range [first, last) into a heap -/// At completion, is_heap (first, last, comp) is true. -/// The algorithm is adapted from "Classic Data Structures in C++" by Timothy Budd. -/// \ingroup HeapAlgorithms -/// \ingroup SortingAlgorithms -/// -template -void make_heap (RandomAccessIterator first, RandomAccessIterator last, Compare comp) -{ - if (last <= first) - return; - const size_t heapSize = distance (first, last); - for (RandomAccessIterator i = first + (heapSize - 1)/2; i >= first; --i) - trickle_down_heap (first, distance(first,i), heapSize, comp); -} - -/// \brief Inserts the *--last into the preceeding range assumed to be a heap. -/// \ingroup HeapAlgorithms -/// \ingroup MutatingAlgorithms -template -void push_heap (RandomAccessIterator first, RandomAccessIterator last, Compare comp) -{ - if (last <= first) - return; - typedef typename iterator_traits::value_type value_type; - const value_type v (*--last); - while (first < last) { - RandomAccessIterator iParent = first + (distance(first, last) - 1) / 2; - if (comp (v, *iParent)) - break; - *last = *iParent; - last = iParent; - } - *last = v; -} - -/// Removes the largest element from the heap (*first) and places it at *(last-1) -/// [first, last-1) is a heap after this operation. -/// \ingroup HeapAlgorithms -/// \ingroup MutatingAlgorithms -template -void pop_heap (RandomAccessIterator first, RandomAccessIterator last, Compare comp) -{ - if (--last <= first) - return; - iter_swap (first, last); - trickle_down_heap (first, 0, distance(first,last), comp); -} - -/// Sorts heap [first, last) in descending order according to comp. -/// \ingroup HeapAlgorithms -/// \ingroup SortingAlgorithms -template -void sort_heap (RandomAccessIterator first, RandomAccessIterator last, Compare comp) -{ - for (; first < last; --last) - pop_heap (first, last, comp); -} - -#define HEAP_FN_WITH_LESS(rtype, name) \ -template \ -inline rtype name (RandomAccessIterator first, RandomAccessIterator last) \ -{ \ - typedef typename iterator_traits::value_type value_type; \ - return name (first, last, less()); \ -} -HEAP_FN_WITH_LESS (bool, is_heap) -HEAP_FN_WITH_LESS (void, make_heap) -HEAP_FN_WITH_LESS (void, push_heap) -HEAP_FN_WITH_LESS (void, pop_heap) -HEAP_FN_WITH_LESS (void, sort_heap) -#undef HEAP_FN_WITH_LESS - -/// \class priority_queue uheap.h ustl.h -/// \ingroup Sequences -/// -/// \brief Sorted queue adapter to uSTL containers. -/// -/// Acts just like the queue adapter, but keeps the elements sorted by priority -/// specified by the given comparison operator. -/// -template , typename Comp = less > -class priority_queue { -public: - typedef Container container_type; - typedef typename container_type::size_type size_type; - typedef typename container_type::value_type value_type; - typedef typename container_type::reference reference; - typedef typename container_type::const_reference const_reference; - typedef typename container_type::const_iterator const_iterator; -public: - inline explicit priority_queue (const Comp& c = Comp()) : _v(), _c(c) {} - inline priority_queue (const Comp& c, const container_type& v) : _v(v), _c(c) {} - priority_queue (const_iterator f, const_iterator l, const Comp& c = Comp()) - : _v(f, l), _c(c) { make_heap (_v.begin(), _v.end(), _c); } - inline size_type size (void) const { return _v.size(); } - inline bool empty (void) const { return _v.empty(); } - inline const_reference top (void) const { return _v.front(); } - inline void push (const_reference v){ _v.push_back (v); push_heap (_v.begin(), _v.end(), _c); } - inline void pop (void) { pop_heap (_v.begin(), _v.end()); _v.pop_back(); } - inline void swap (priority_queue& v){ _v.swap (v._v); swap (_c, v._c); } -#if HAVE_CPP11 - inline explicit priority_queue (priority_queue&& v) : _v(move(v._v)),_c(v._c) {} - inline priority_queue (const Comp& c, container_type&& v) : _v(move(v)),_c(c) {} - priority_queue (const_iterator f, const_iterator l, const Comp& c, container_type&& v) - : _v(move(v)), _c(c) { _v.insert (_v.end(), f, l); make_heap (_v.begin(), _v.end(), _c); } - inline priority_queue& operator= (priority_queue&& v) { swap (v); return *this; } - template - inline void emplace (Args&&... args) { _v.emplace_back (forward(args)...); push_heap (_v.begin(), _v.end(), _c); } -#endif -private: - container_type _v; ///< Element container. - Comp _c; ///< Comparison functor by value. -}; - -} // namespace ustl diff --git a/common/include/ustl/uios.h b/common/include/ustl/uios.h deleted file mode 100644 index 941fc9e92..000000000 --- a/common/include/ustl/uios.h +++ /dev/null @@ -1,99 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "utypes.h" - -namespace ustl { - -class file_exception; - -/// Defines types and constants used by all stream classes. -class ios_base { -public: - /// Used to set parameters for stringstreams - enum fmtflags_bits { - boolalpha = (1 << 0), ///< Boolean values printed as text. - showbase = (1 << 1), ///< Add 0x or 0 prefixes on hex and octal numbers. - showpoint = (1 << 2), ///< Print decimal point. - showpos = (1 << 3), - skipws = (1 << 4), ///< Skip whitespace when reading. - unitbuf = (1 << 5), - uppercase = (1 << 6), - dec = (1 << 7), ///< Decimal number output. - oct = (1 << 8), ///< Octal number output. - hex = (1 << 9), ///< Hexadecimal number output. - fixed = (1 << 10), ///< Fixed-point float output. - scientific = (1 << 11), ///< Scientific float format. - left = (1 << 12), ///< Left alignment. - right = (1 << 13), ///< Right alignment. - internal = (1 << 14), - basefield = dec| oct| hex, - floatfield = fixed| scientific, - adjustfield = left| right| internal - }; - /// For file-based streams, specifies fd mode. - enum openmode_bits { - in = (1 << 0), - out = (1 << 1), - app = (1 << 2), - ate = (1 << 3), - binary = (1 << 4), - trunc = (1 << 5), - #ifndef DOXYGEN_SHOULD_SKIP_THIS - nonblock= (1 << 6), - nocreate= (1 << 7), - noctty = (1 << 8), - nombits = 9 - #endif - }; - /// Seek directions, equivalent to SEEK_SET, SEEK_CUR, and SEEK_END. - enum seekdir { - beg, - cur, - end - }; - /// I/O state bitmasks. - enum iostate_bits { - goodbit = 0, - badbit = (1 << 0), - eofbit = (1 << 1), - failbit = (1 << 2), - #ifndef DOXYGEN_SHOULD_SKIP_THIS - nbadbits = 3, - allbadbits = 0x7 - #endif - }; - - enum { default_stream_buffer_size = 4095 }; - - typedef uint32_t openmode; ///< Holds openmode_bits. - struct fmtflags{ fmtflags(const uint32_t& flags) : f(flags){}; fmtflags() = default; uint32_t f; }; ///< Holds fmtflags_bits for a string stream. - typedef uint32_t iostate; ///< Holds iostate_bits for a file stream. - typedef file_exception failure; ///< Thrown by fstream on errors. - - static const char c_DefaultDelimiters [16]; ///< Default word delimiters for stringstreams. -public: - inline ios_base (void) : _state (goodbit), _exceptions (allbadbits) {} - inline iostate rdstate (void) const { return _state; } - inline bool bad (void) const { return rdstate() & badbit; } - inline bool good (void) const { return rdstate() == goodbit; } - inline bool fail (void) const { return rdstate() & (badbit | failbit); } - inline bool eof (void) const { return rdstate() & eofbit; } - inline bool operator! (void) const { return fail(); } - inline operator bool (void) const { return !fail(); } - inline void clear (iostate v = goodbit) { _state = v; } - inline void setstate (iostate v) { _state |= v; } - inline iostate exceptions (void) const { return _exceptions; } - inline iostate exceptions (iostate v) { return _exceptions = v; } -protected: - inline bool set_and_throw (iostate v) { setstate(v); return exceptions() & v; } - void overrun (const char* op, const char* type, uint32_t n, uint32_t p, uint32_t rem); -private: - uint16_t _state; ///< Open state, using ios::iostate_bits. - uint16_t _exceptions; ///< Exception flags, using ios::iostate_bits. -}; - -} // namespace ustl diff --git a/common/include/ustl/uiosfunc.h b/common/include/ustl/uiosfunc.h deleted file mode 100644 index eccc6fb25..000000000 --- a/common/include/ustl/uiosfunc.h +++ /dev/null @@ -1,82 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "sostream.h" - -namespace ustl { - -class ios : public ios_base { -public: - /// \class align uiosfunc.h ustl.h - /// \ingroup StreamFunctors - /// \brief Stream functor to allow inline align() calls. - /// - /// Example: os << ios::align(sizeof(uint16_t)); - /// - class align { - public: - inline explicit align (size_t grain = c_DefaultAlignment) : _grain(grain) {} - inline istream& apply (istream& is) const { is.align (_grain); return is; } - inline ostream& apply (ostream& os) const { os.align (_grain); return os; } - inline void read (istream& is) const { apply (is); } - inline void write (ostream& os) const { apply (os); } - inline size_t stream_size (void) const { return _grain - 1; } - private: - const size_t _grain; - }; - - /// \class talign uiosfunc.h ustl.h - /// \ingroup StreamFunctors - /// \brief Stream functor to allow type-based alignment. - template - class talign : public align { - public: - inline explicit talign (void) : align (stream_align_of (NullValue())) {} - }; - - /// \class skip uiosfunc.h ustl.h - /// \ingroup StreamFunctors - /// \brief Stream functor to allow inline skip() calls. - /// - /// Example: os << ios::skip(sizeof(uint16_t)); - /// - class skip { - public: - inline explicit skip (size_t nBytes) : _nBytes(nBytes) {} - inline istream& apply (istream& is) const { is.skip (_nBytes); return is; } - inline ostream& apply (ostream& os) const { os.skip (_nBytes); return os; } - inline void read (istream& is) const { apply (is); } - inline void write (ostream& os) const { apply (os); } - inline size_t stream_size (void) const { return _nBytes; } - private: - const size_t _nBytes; - }; - - /// \class width uiosfunc.h ustl.h - /// \ingroup StreamFunctors - /// \brief Stream functor to allow inline set_width() calls. - /// - /// Example: os << ios::width(15); - /// - class width { - public: - inline explicit width (size_t nBytes) : _nBytes(nBytes) {} - inline ostringstream& apply (ostringstream& os) const { os.width (_nBytes); return os; } - inline void text_write (ostringstream& os) const { apply (os); } - private: - const size_t _nBytes; - }; - - // Deprecated way to set output format base. Use setiosflags manipulator instead. - struct base { - inline explicit base (size_t n) : _f (n == 16 ? hex : (n == 8 ? oct : dec)) {} - inline void text_write (ostringstream& os) const { os.setf (_f, basefield); } - private: - fmtflags _f; - }; -}; - -} // namespace ustl diff --git a/common/include/ustl/uiostream.h b/common/include/ustl/uiostream.h deleted file mode 100644 index c28b579e2..000000000 --- a/common/include/ustl/uiostream.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "sostream.h" -#include "mistream.h" -#include "outerrstream.h" - -namespace ustl -{ - - extern coutclass& cout; - extern coutclass& cerr; - -} diff --git a/common/include/ustl/uiterator.h b/common/include/ustl/uiterator.h deleted file mode 100644 index c45d8d876..000000000 --- a/common/include/ustl/uiterator.h +++ /dev/null @@ -1,360 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. -// -/// \file uiterator.h -/// \brief Contains various iterator adapters. - -#pragma once -#include "utypes.h" - -namespace ustl { - -//---------------------------------------------------------------------- - -template -struct iterator { - typedef T value_type; - typedef Distance difference_type; - typedef Pointer pointer; - typedef Reference reference; - typedef Category iterator_category; -}; - -//---------------------------------------------------------------------- - -struct input_iterator_tag {}; -struct output_iterator_tag {}; -struct forward_iterator_tag {}; -struct bidirectional_iterator_tag {}; -struct random_access_iterator_tag {}; - -/// \struct iterator_traits uiterator.h ustl.h -/// \brief Contains the type traits of \p Iterator -/// -template -struct iterator_traits { - typedef typename Iterator::value_type value_type; - typedef typename Iterator::difference_type difference_type; - typedef typename Iterator::pointer pointer; - typedef typename Iterator::reference reference; - typedef typename Iterator::iterator_category iterator_category; -}; - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - -template -struct iterator_traits { - typedef T value_type; - typedef ptrdiff_t difference_type; - typedef const T* const_pointer; - typedef T* pointer; - typedef const T& const_reference; - typedef T& reference; - typedef random_access_iterator_tag iterator_category; -}; - -template -struct iterator_traits { - typedef T value_type; - typedef ptrdiff_t difference_type; - typedef const T* const_pointer; - typedef const T* pointer; - typedef const T& const_reference; - typedef const T& reference; - typedef random_access_iterator_tag iterator_category; -}; - -template <> -struct iterator_traits { - typedef uint8_t value_type; - typedef ptrdiff_t difference_type; - typedef const void* const_pointer; - typedef void* pointer; - typedef const value_type& const_reference; - typedef value_type& reference; - typedef random_access_iterator_tag iterator_category; -}; - -template <> -struct iterator_traits { - typedef uint8_t value_type; - typedef ptrdiff_t difference_type; - typedef const void* const_pointer; - typedef const void* pointer; - typedef const value_type& const_reference; - typedef const value_type& reference; - typedef random_access_iterator_tag iterator_category; -}; - -#endif - -//---------------------------------------------------------------------- - -/// \class reverse_iterator uiterator.h ustl.h -/// \ingroup IteratorAdaptors -/// \brief Wraps \p Iterator to behave in an exactly opposite manner. -/// -template -class reverse_iterator { -public: - typedef typename iterator_traits::value_type value_type; - typedef typename iterator_traits::difference_type difference_type; - typedef typename iterator_traits::pointer pointer; - typedef typename iterator_traits::reference reference; - typedef typename iterator_traits::iterator_category iterator_category; -public: - reverse_iterator (void) : _i() {} - explicit reverse_iterator (Iterator iter) : _i (iter) {} - inline bool operator== (const reverse_iterator& iter) const { return _i == iter._i; } - inline bool operator< (const reverse_iterator& iter) const { return iter._i < _i; } - inline Iterator base (void) const { return _i; } - inline reference operator* (void) const { Iterator prev (_i); --prev; return *prev; } - inline pointer operator-> (void) const { return &(operator*()); } - inline reverse_iterator& operator++ (void) { -- _i; return *this; } - inline reverse_iterator& operator-- (void) { ++ _i; return *this; } - inline reverse_iterator operator++ (int) { reverse_iterator prev (*this); -- _i; return prev; } - inline reverse_iterator operator-- (int) { reverse_iterator prev (*this); ++ _i; return prev; } - inline reverse_iterator& operator+= (size_t n) { _i -= n; return *this; } - inline reverse_iterator& operator-= (size_t n) { _i += n; return *this; } - inline reverse_iterator operator+ (size_t n) const { return reverse_iterator (_i - n); } - inline reverse_iterator operator- (size_t n) const { return reverse_iterator (_i + n); } - inline reference operator[] (uoff_t n) const { return *(*this + n); } - inline difference_type operator- (const reverse_iterator& i) const { return distance (i._i, _i); } -protected: - Iterator _i; -}; - -template -inline reverse_iterator make_reverse_iterator (Iterator i) - { return reverse_iterator(i); } - -//---------------------------------------------------------------------- -#if HAVE_CPP11 - -/// \class move_iterator uiterator.h ustl.h -/// \ingroup IteratorAdaptors -/// \brief Wraps \p Iterator to behave in an exactly opposite manner. -/// -template -class move_iterator { -public: - using value_type = typename iterator_traits::value_type; - using difference_type = typename iterator_traits::difference_type; - using pointer = typename iterator_traits::pointer; - using reference = value_type&&; - using iterator_category = typename iterator_traits::iterator_category; -public: - move_iterator (void) : _i() {} - explicit move_iterator (Iterator iter) : _i (iter) {} - inline bool operator== (const move_iterator& iter) const { return _i == iter._i; } - inline bool operator< (const move_iterator& iter) const { return _i < iter._i; } - inline Iterator base (void) const { return _i; } - inline reference operator* (void) const { return move(*_i); } - inline pointer operator-> (void) const { return &*_i; } - inline move_iterator& operator++ (void) { ++_i; return *this; } - inline move_iterator& operator-- (void) { --_i; return *this; } - inline move_iterator operator++ (int) { move_iterator r (*this); ++ _i; return r; } - inline move_iterator operator-- (int) { move_iterator r (*this); -- _i; return r; } - inline move_iterator& operator+= (size_t n) { _i += n; return *this; } - inline move_iterator& operator-= (size_t n) { _i -= n; return *this; } - inline move_iterator operator+ (size_t n) const { return move_iterator (_i - n); } - inline move_iterator operator- (size_t n) const { return move_iterator (_i + n); } - inline reference operator[] (uoff_t n) const { return move(*(*this + n)); } - inline difference_type operator- (const move_iterator& i) const { return distance (_i, i._i); } -protected: - Iterator _i; -}; - -template -inline move_iterator make_move_iterator (Iterator i) - { return move_iterator(i); } - -#endif -//---------------------------------------------------------------------- - -/// \class insert_iterator uiterator.h ustl.h -/// \ingroup IteratorAdaptors -/// \brief Calls insert on bound container for each assignment. -/// -template -class insert_iterator { -public: - typedef typename Container::value_type value_type; - typedef typename Container::difference_type difference_type; - typedef typename Container::pointer pointer; - typedef typename Container::reference reference; - typedef typename Container::iterator iterator; - typedef output_iterator_tag iterator_category; -public: - explicit insert_iterator (Container& ctr, iterator ip) : _rctr (ctr), _ip (ip) {} - inline insert_iterator& operator= (typename Container::const_reference v) - { _ip = _rctr.insert (_ip, v); return *this; } - inline insert_iterator& operator* (void) { return *this; } - inline insert_iterator& operator++ (void) { ++ _ip; return *this; } - inline insert_iterator operator++ (int) { insert_iterator prev (*this); ++_ip; return prev; } -protected: - Container& _rctr; - iterator _ip; -}; - -/// Returns the insert_iterator for \p ctr. -template -inline insert_iterator inserter (Container& ctr, typename Container::iterator ip) - { return insert_iterator (ctr, ip); } - -//---------------------------------------------------------------------- - -/// \class back_insert_iterator uiterator.h ustl.h -/// \ingroup IteratorAdaptors -/// \brief Calls push_back on bound container for each assignment. -/// -template -class back_insert_iterator { -public: - typedef typename Container::value_type value_type; - typedef typename Container::difference_type difference_type; - typedef typename Container::pointer pointer; - typedef typename Container::reference reference; - typedef output_iterator_tag iterator_category; -public: - explicit back_insert_iterator (Container& ctr) : _rctr (ctr) {} - inline back_insert_iterator& operator= (typename Container::const_reference v) - { _rctr.push_back (v); return *this; } - inline back_insert_iterator& operator* (void) { return *this; } - inline back_insert_iterator& operator++ (void) { return *this; } - inline back_insert_iterator operator++ (int) { return *this; } -protected: - Container& _rctr; -}; - -/// Returns the back_insert_iterator for \p ctr. -template -inline back_insert_iterator back_inserter (Container& ctr) - { return back_insert_iterator (ctr); } - -//---------------------------------------------------------------------- - -/// \class front_insert_iterator uiterator.h ustl.h -/// \ingroup IteratorAdaptors -/// \brief Calls push_front on bound container for each assignment. -/// -template -class front_insert_iterator { -public: - typedef typename Container::value_type value_type; - typedef typename Container::difference_type difference_type; - typedef typename Container::pointer pointer; - typedef typename Container::reference reference; - typedef output_iterator_tag iterator_category; -public: - explicit front_insert_iterator (Container& ctr) : _rctr (ctr) {} - inline front_insert_iterator& operator= (typename Container::const_reference v) - { _rctr.push_front (v); return *this; } - inline front_insert_iterator& operator* (void) { return *this; } - inline front_insert_iterator& operator++ (void) { return *this; } - inline front_insert_iterator operator++ (int) { return *this; } -protected: - Container& _rctr; -}; - -/// Returns the front_insert_iterator for \p ctr. -template -inline front_insert_iterator front_inserter (Container& ctr) - { return front_insert_iterator (ctr); } - -//---------------------------------------------------------------------- - -/// \class index_iterate uiterator.h ustl.h -/// \ingroup IteratorAdaptors -/// -/// \brief Allows iteration through an index container. -/// -/// Converts an iterator into a container of uoff_t indexes to an -/// iterator of iterators into another container. -/// -template -class index_iterate { -public: - typedef RandomAccessIterator value_type; - typedef ptrdiff_t difference_type; - typedef RandomAccessIterator* pointer; - typedef RandomAccessIterator reference; - typedef random_access_iterator_tag iterator_category; -public: - index_iterate (void) : _base(), _i() {} - index_iterate (RandomAccessIterator ibase, IndexIterator iindex) : _base (ibase), _i (iindex) {} - inline bool operator== (const index_iterate& i) const { return _i == i._i; } - inline bool operator< (const index_iterate& i) const { return _i < i._i; } - inline bool operator== (const RandomAccessIterator& i) const { return _base == i; } - inline bool operator< (const RandomAccessIterator& i) const { return _base < i; } - inline IndexIterator base (void) const { return _i; } - inline reference operator* (void) const { return advance(_base, *_i); } - inline pointer operator-> (void) const { return &(operator*()); } - inline index_iterate& operator++ (void) { ++_i; return *this; } - inline index_iterate& operator-- (void) { --_i; return *this; } - inline index_iterate operator++ (int) { index_iterate prev (*this); ++_i; return prev; } - inline index_iterate operator-- (int) { index_iterate prev (*this); --_i; return prev; } - inline index_iterate& operator+= (size_t n) { _i += n; return *this; } - inline index_iterate& operator-= (size_t n) { _i -= n; return *this; } - inline index_iterate operator+ (size_t n) const { return index_iterate (_base, _i + n); } - inline index_iterate operator- (size_t n) const { return index_iterate (_base, _i - n); } - inline reference operator[] (uoff_t n) const { return *(*this + n); } - inline difference_type operator- (const index_iterate& i) const { return distance (_i, i._i); } -private: - RandomAccessIterator _base; - IndexIterator _i; -}; - -/// Returns an index_iterate for \p ibase over \p iindex. -template -inline index_iterate index_iterator (RandomAccessIterator ibase, IndexIterator iindex) -{ - return index_iterate (ibase, iindex); -} - -/// Converts the indexes in \p xc to iterators in \p ic of base \p ibase. -template -inline void indexv_to_iteratorv (typename IteratorContainer::value_type ibase, const IndexContainer& xc, IteratorContainer& ic) -{ - ic.resize (xc.size()); - copy_n (index_iterator (ibase, xc.begin()), xc.size(), ic.begin()); -} - -//---------------------------------------------------------------------- - -/// Converts the given const_iterator into an iterator. -/// -template -inline typename Container::iterator unconst (typename Container::const_iterator i, Container&) - { return const_cast(i); } - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - -#define IBYI(Iter1, Iter2, Ctr1, Ctr2) \ -template \ -inline typename Container2::Iter2 ibyi (typename Container1::Iter1 idx, Ctr1& ctr1, Ctr2& ctr2) \ -{ \ - assert (ctr1.size() == ctr2.size()); \ - return ctr2.begin() + (idx - ctr1.begin()); \ -} - -IBYI(const_iterator, const_iterator, const Container1, const Container2) -IBYI(iterator, iterator, Container1, Container2) -IBYI(const_iterator, iterator, const Container1, Container2) -IBYI(iterator, const_iterator, Container1, const Container2) - -#else // DOXYGEN - -#error "This declaration is for doxygen only; it is not compiled." - -/// Converts a const_iterator in one container into a const_iterator in another container. -template -inline typename Container2::iterator ibyi (typename Container1::iterator idx, Container1& ctr1, Container2& ctr2) {} - -#endif // DOXYGEN - -//---------------------------------------------------------------------- - -} // namespace ustl diff --git a/common/include/ustl/ulimits.h b/common/include/ustl/ulimits.h deleted file mode 100644 index bedceb9ca..000000000 --- a/common/include/ustl/ulimits.h +++ /dev/null @@ -1,324 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "utypes.h" -#include "traits.h" -#if HAVE_CPP11 - #include "uttraits.h" -#endif - -namespace ustl { - -enum float_denorm_style { - denorm_indeterminate = -1, - denorm_absent, - denorm_present -}; - -enum float_round_style { - round_indeterminate = -1, - round_toward_zero, - round_to_nearest, - round_toward_infinity, - round_toward_neg_infinity -}; - -namespace { - template - struct __limits_digits { enum { value = sizeof(T)*8 };}; - template - struct __limits_digits10 { enum { value = sizeof(T)*8*643/2136+1 };}; -} - -/// \class numeric_limits ulimits.h ustl.h -/// \brief Defines numeric limits for a type. -/// -template -struct numeric_limits { - static inline constexpr T min (void) { return T(); } // Returns the minimum value for type T. - static inline constexpr T max (void) { return T(); } // Returns the minimum value for type T. - static inline constexpr T lowest (void) { return T(); } - static inline constexpr T epsilon (void) { return T(); } - static inline constexpr T round_error (void) { return T(); } - static inline constexpr T infinity (void) { return T(); } - static inline constexpr T quiet_NaN (void) { return T(); } - static inline constexpr T signaling_NaN (void) { return T(); } - static inline constexpr T denorm_min (void) { return T(); } - static constexpr const bool is_specialized = false; - static constexpr const bool is_signed = tm::TypeTraits::isSigned; ///< True if the type is signed. - static constexpr const bool is_integer = tm::TypeTraits::isIntegral; ///< True if stores an exact value. - static constexpr const bool is_exact = tm::TypeTraits::isIntegral; ///< True if stores an exact value. - static constexpr const bool is_integral = tm::TypeTraits::isFundamental; ///< True if fixed size and cast-copyable. - static constexpr const bool is_iec559 = false; - static constexpr const bool is_bounded = false; - static constexpr const bool is_modulo = false; - static constexpr const bool has_infinity = false; - static constexpr const bool has_quiet_NaN = false; - static constexpr const bool has_signaling_NaN = false; - static constexpr const bool has_denorm_loss = false; - static constexpr const bool traps = false; - static constexpr const bool tinyness_before = false; - static constexpr const int radix = 0; - static constexpr const int min_exponent = 0; - static constexpr const int min_exponent10 = 0; - static constexpr const int max_exponent = 0; - static constexpr const int max_exponent10 = 0; - static constexpr const float_denorm_style has_denorm = denorm_absent; - static constexpr const float_round_style round_style = round_toward_zero; - static constexpr const unsigned digits = __limits_digits::value; ///< Number of bits in T - static constexpr const unsigned digits10 = 0; - static constexpr const unsigned max_digits10 = 0; -}; - -#ifndef DOXYGEN_SHOULD_SKIP_THIS - -template -struct numeric_limits { - static inline constexpr T* min (void) { return nullptr; } - static inline constexpr T* max (void) { return reinterpret_cast(UINTPTR_MAX); } - static inline constexpr T* lowest (void) { return nullptr; } - static inline constexpr T* epsilon (void) { return nullptr; } - static inline constexpr T* round_error (void) { return nullptr; } - static inline constexpr T* infinity (void) { return nullptr; } - static inline constexpr T* quiet_NaN (void) { return nullptr; } - static inline constexpr T* signaling_NaN (void) { return nullptr; } - static inline constexpr T* denorm_min (void) { return nullptr; } - static constexpr const bool is_specialized = false; - static constexpr const bool is_signed = false; - static constexpr const bool is_integer = true; - static constexpr const bool is_exact = true; - static constexpr const bool is_integral = true; - static constexpr const bool is_iec559 = false; - static constexpr const bool is_bounded = true; - static constexpr const bool is_modulo = true; - static constexpr const bool has_infinity = false; - static constexpr const bool has_quiet_NaN = false; - static constexpr const bool has_signaling_NaN = false; - static constexpr const bool has_denorm_loss = false; - static constexpr const bool traps = false; - static constexpr const bool tinyness_before = false; - static constexpr const int radix = 2; - static constexpr const int min_exponent = 0; - static constexpr const int min_exponent10 = 0; - static constexpr const int max_exponent = 0; - static constexpr const int max_exponent10 = 0; - static constexpr const float_denorm_style has_denorm = denorm_absent; - static constexpr const float_round_style round_style = round_toward_zero; - static constexpr const unsigned digits = __limits_digits::value; - static constexpr const unsigned digits10 = __limits_digits10::value; - static constexpr const unsigned max_digits10 = 0; -}; -/* -template <> struct numeric_limits { - static inline constexpr float min (void) { return FLT_MIN; } - static inline constexpr float max (void) { return FLT_MAX; } - static inline constexpr float lowest (void) { return -FLT_MAX; } - static inline constexpr float epsilon (void) { return FLT_EPSILON; } - static inline constexpr float round_error (void) { return 0.5f; } - static inline constexpr float infinity (void) { return __builtin_huge_valf(); } - static inline constexpr float quiet_NaN (void) { return __builtin_nanf(""); } - static inline constexpr float signaling_NaN (void) { return __builtin_nansf(""); } - static inline constexpr float denorm_min (void) { return __FLT_DENORM_MIN__; } - static constexpr const bool is_specialized = true; - static constexpr const bool is_signed = true; - static constexpr const bool is_integer = false; - static constexpr const bool is_exact = false; - static constexpr const bool is_integral = true; - static constexpr const bool is_iec559 = true; - static constexpr const bool is_bounded = true; - static constexpr const bool is_modulo = false; - static constexpr const bool has_infinity = __FLT_HAS_INFINITY__; - static constexpr const bool has_quiet_NaN = __FLT_HAS_QUIET_NAN__; - static constexpr const bool has_signaling_NaN = __FLT_HAS_QUIET_NAN__; - static constexpr const bool has_denorm_loss = true; - static constexpr const bool traps = false; - static constexpr const bool tinyness_before = true; - static constexpr const int radix = FLT_RADIX; - static constexpr const int min_exponent = FLT_MIN_EXP; - static constexpr const int min_exponent10 = FLT_MIN_10_EXP; - static constexpr const int max_exponent = FLT_MAX_EXP; - static constexpr const int max_exponent10 = FLT_MAX_10_EXP; - static constexpr const float_denorm_style has_denorm = denorm_present; - static constexpr const float_round_style round_style = round_to_nearest; - static constexpr const unsigned digits = FLT_MANT_DIG; - static constexpr const unsigned digits10 = FLT_DIG; - static constexpr const unsigned max_digits10 = FLT_MANT_DIG; -}; - -template <> struct numeric_limits { - static inline constexpr double min (void) { return DBL_MIN; } - static inline constexpr double max (void) { return DBL_MAX; } - static inline constexpr double lowest (void) { return -DBL_MAX; } - static inline constexpr double epsilon (void) { return DBL_EPSILON; } - static inline constexpr double round_error (void) { return 0.5; } - static inline constexpr double infinity (void) { return __builtin_huge_val(); } - static inline constexpr double quiet_NaN (void) { return __builtin_nan(""); } - static inline constexpr double signaling_NaN (void) { return __builtin_nans(""); } - static inline constexpr double denorm_min (void) { return __DBL_DENORM_MIN__; } - static constexpr const bool is_specialized = true; - static constexpr const bool is_signed = true; - static constexpr const bool is_integer = false; - static constexpr const bool is_exact = false; - static constexpr const bool is_integral = true; - static constexpr const bool is_iec559 = true; - static constexpr const bool is_bounded = true; - static constexpr const bool is_modulo = false; - static constexpr const bool has_infinity = __DBL_HAS_INFINITY__; - static constexpr const bool has_quiet_NaN = __DBL_HAS_QUIET_NAN__; - static constexpr const bool has_signaling_NaN = __DBL_HAS_QUIET_NAN__; - static constexpr const bool has_denorm_loss = true; - static constexpr const bool traps = false; - static constexpr const bool tinyness_before = true; - static constexpr const int radix = FLT_RADIX; - static constexpr const int min_exponent = DBL_MIN_EXP; - static constexpr const int min_exponent10 = DBL_MIN_10_EXP; - static constexpr const int max_exponent = DBL_MAX_EXP; - static constexpr const int max_exponent10 = DBL_MAX_10_EXP; - static constexpr const float_denorm_style has_denorm = denorm_present; - static constexpr const float_round_style round_style = round_to_nearest; - static constexpr const unsigned digits = DBL_MANT_DIG; - static constexpr const unsigned digits10 = DBL_DIG; - static constexpr const unsigned max_digits10 = DBL_MANT_DIG; -}; - -template <> struct numeric_limits { - static inline constexpr long double min (void) { return LDBL_MIN; } - static inline constexpr long double max (void) { return LDBL_MAX; } - static inline constexpr long double lowest (void) { return -LDBL_MAX; } - static inline constexpr long double epsilon (void) { return LDBL_EPSILON; } - static inline constexpr long double round_error (void) { return 0.5l; } - static inline constexpr long double infinity (void) { return __builtin_huge_vall(); } - static inline constexpr long double quiet_NaN (void) { return __builtin_nanl(""); } - static inline constexpr long double signaling_NaN (void) { return __builtin_nansl(""); } - static inline constexpr long double denorm_min (void) { return __LDBL_DENORM_MIN__; } - static constexpr const bool is_specialized = true; - static constexpr const bool is_signed = true; - static constexpr const bool is_integer = false; - static constexpr const bool is_exact = false; - static constexpr const bool is_integral = true; - static constexpr const bool is_iec559 = true; - static constexpr const bool is_bounded = true; - static constexpr const bool is_modulo = false; - static constexpr const bool has_infinity = __LDBL_HAS_INFINITY__; - static constexpr const bool has_quiet_NaN = __LDBL_HAS_QUIET_NAN__; - static constexpr const bool has_signaling_NaN = __LDBL_HAS_QUIET_NAN__; - static constexpr const bool has_denorm_loss = true; - static constexpr const bool traps = false; - static constexpr const bool tinyness_before = true; - static constexpr const int radix = FLT_RADIX; - static constexpr const int min_exponent = LDBL_MIN_EXP; - static constexpr const int min_exponent10 = LDBL_MIN_10_EXP; - static constexpr const int max_exponent = LDBL_MAX_EXP; - static constexpr const int max_exponent10 = LDBL_MAX_10_EXP; - static constexpr const float_denorm_style has_denorm = denorm_present; - static constexpr const float_round_style round_style = round_to_nearest; - static constexpr const unsigned digits = LDBL_MANT_DIG; - static constexpr const unsigned digits10 = LDBL_DIG; - static constexpr const unsigned max_digits10 = LDBL_MANT_DIG; -}; - -template <> struct numeric_limits { - static inline constexpr long double min (void) { return LDBL_MIN; } - static inline constexpr long double max (void) { return LDBL_MAX; } - static inline constexpr long double lowest (void) { return -LDBL_MAX; } - static inline constexpr long double epsilon (void) { return LDBL_EPSILON; } - static inline constexpr long double round_error (void) { return 0.5l; } - static inline constexpr long double infinity (void) { return __builtin_huge_vall(); } - static inline constexpr long double quiet_NaN (void) { return __builtin_nanl(""); } - static inline constexpr long double signaling_NaN (void) { return __builtin_nansl(""); } - static inline constexpr long double denorm_min (void) { return __LDBL_DENORM_MIN__; } - static constexpr const bool is_specialized = true; - static constexpr const bool is_signed = true; - static constexpr const bool is_integer = false; - static constexpr const bool is_exact = false; - static constexpr const bool is_integral = true; - static constexpr const bool is_iec559 = true; - static constexpr const bool is_bounded = true; - static constexpr const bool is_modulo = false; - static constexpr const bool has_infinity = __LDBL_HAS_INFINITY__; - static constexpr const bool has_quiet_NaN = __LDBL_HAS_QUIET_NAN__; - static constexpr const bool has_signaling_NaN = __LDBL_HAS_QUIET_NAN__; - static constexpr const bool has_denorm_loss = true; - static constexpr const bool traps = false; - static constexpr const bool tinyness_before = true; - static constexpr const int radix = FLT_RADIX; - static constexpr const int min_exponent = LDBL_MIN_EXP; - static constexpr const int min_exponent10 = LDBL_MIN_10_EXP; - static constexpr const int max_exponent = LDBL_MAX_EXP; - static constexpr const int max_exponent10 = LDBL_MAX_10_EXP; - static constexpr const float_denorm_style has_denorm = denorm_present; - static constexpr const float_round_style round_style = round_to_nearest; - static constexpr const unsigned digits = LDBL_MANT_DIG; - static constexpr const unsigned digits10 = LDBL_DIG; - static constexpr const unsigned max_digits10 = LDBL_MANT_DIG; - };*/ - -#define _NUMERIC_LIMITS(type, minVal, maxVal, bSigned, bInteger, bIntegral) \ -template <> struct numeric_limits { \ - static inline constexpr type min (void) { return minVal; } \ - static inline constexpr type max (void) { return maxVal; } \ - static inline constexpr type lowest (void) { return minVal; } \ - static inline constexpr type epsilon (void) { return 0; } \ - static inline constexpr type round_error (void) { return 0; } \ - static inline constexpr type infinity (void) { return 0; } \ - static inline constexpr type quiet_NaN (void) { return 0; } \ - static inline constexpr type signaling_NaN (void) { return 0; } \ - static inline constexpr type denorm_min (void) { return 0; } \ - static constexpr const bool is_specialized = true; \ - static constexpr const bool is_signed = bSigned; \ - static constexpr const bool is_integer = bInteger; \ - static constexpr const bool is_exact = bInteger; \ - static constexpr const bool is_integral = bIntegral; \ - static constexpr const bool is_iec559 = false; \ - static constexpr const bool is_bounded = true; \ - static constexpr const bool is_modulo = bIntegral; \ - static constexpr const bool has_infinity = false; \ - static constexpr const bool has_quiet_NaN = false; \ - static constexpr const bool has_signaling_NaN = false; \ - static constexpr const bool has_denorm_loss = false; \ - static constexpr const bool traps = false; \ - static constexpr const bool tinyness_before = false; \ - static constexpr const int radix = 2; \ - static constexpr const int min_exponent = 0; \ - static constexpr const int min_exponent10 = 0; \ - static constexpr const int max_exponent = 0; \ - static constexpr const int max_exponent10 = 0; \ - static constexpr const float_denorm_style has_denorm = denorm_absent; \ - static constexpr const float_round_style round_style = round_toward_zero; \ - static constexpr const unsigned digits = __limits_digits::value; \ - static constexpr const unsigned digits10 = __limits_digits10::value; \ - static constexpr const unsigned max_digits10 = 0; \ -} - -//-------------------------------------------------------------------------------------- -// type min max signed integer integral -//-------------------------------------------------------------------------------------- -_NUMERIC_LIMITS (bool, false, true, false, false, true); -_NUMERIC_LIMITS (char, CHAR_MIN, CHAR_MAX, true, true, true); -_NUMERIC_LIMITS (int, INT_MIN, INT_MAX, true, true, true); -_NUMERIC_LIMITS (short, SHRT_MIN, SHRT_MAX, true, true, true); -_NUMERIC_LIMITS (long, LONG_MIN, LONG_MAX, true, true, true); -#if HAVE_THREE_CHAR_TYPES -_NUMERIC_LIMITS (signed char, SCHAR_MIN, SCHAR_MAX, true, true, true); -#endif -_NUMERIC_LIMITS (unsigned char, 0, UCHAR_MAX, false, true, true); -_NUMERIC_LIMITS (unsigned int, 0, UINT_MAX, false, true, true); -_NUMERIC_LIMITS (unsigned short,0, USHRT_MAX, false, true, true); -_NUMERIC_LIMITS (unsigned long, 0, ULONG_MAX, false, true, true); -_NUMERIC_LIMITS (wchar_t, 0, WCHAR_MAX, false, true, true); -#if HAVE_LONG_LONG -_NUMERIC_LIMITS (long long, LLONG_MIN, LLONG_MAX, true, true, true); -_NUMERIC_LIMITS (unsigned long long, 0, ULLONG_MAX, false, true, true); -#endif -//-------------------------------------------------------------------------------------- - -#endif // DOXYGEN_SHOULD_SKIP_THIS - -/// Macro for defining numeric_limits specializations -#define NUMERIC_LIMITS(type, minVal, maxVal, bSigned, bInteger, bIntegral) \ -namespace ustl { _NUMERIC_LIMITS (type, minVal, maxVal, bSigned, bInteger, bIntegral); } - -} // namespace ustl diff --git a/common/include/ustl/ulist.h b/common/include/ustl/ulist.h deleted file mode 100644 index 988459172..000000000 --- a/common/include/ustl/ulist.h +++ /dev/null @@ -1,85 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "uvector.h" -#include "uctralgo.h" - -namespace ustl { - -/// \class list ulist.h ustl.h -/// \ingroup Sequences -/// -/// \brief Linked list, defined as an alias to vector. -/// -template -class list : public vector { -public: - typedef typename vector::size_type size_type; - typedef typename vector::iterator iterator; - typedef typename vector::const_iterator const_iterator; - typedef typename vector::reference reference; - typedef typename vector::const_reference const_reference; -public: - inline list (void) : vector () {} - inline explicit list (size_type n) : vector (n) {} - inline list (size_type n, const T& v) : vector (n, v) {} - inline list (const list& v) : vector (v) {} - inline list (const_iterator i1, const_iterator i2) : vector (i1, i2) {} - inline size_type size (void) const { return vector::size(); } - inline iterator begin (void) { return vector::begin(); } - inline const_iterator begin (void) const { return vector::begin(); } - inline iterator end (void) { return vector::end(); } - inline const_iterator end (void) const { return vector::end(); } - inline void push_front (const T& v) { this->insert (begin(), v); } - inline void pop_front (void) { this->erase (begin()); } - inline const_reference front (void) const { return *begin(); } - inline reference front (void) { return *begin(); } - inline void remove (const T& v) { ::ustl::remove (*this, v); } - template - inline void remove_if (Predicate p) { ::ustl::remove_if (*this, p); } - inline void reverse (void) { ::ustl::reverse (*this); } - inline void unique (void) { ::ustl::unique (*this); } - inline void sort (void) { ::ustl::sort (*this); } - void merge (list& l); - void splice (iterator ip, list& l, iterator first = nullptr, iterator last = nullptr); -#if HAVE_CPP11 - inline list (list&& v) : vector (move(v)) {} - inline list (std::initializer_list v) : vector(v) {} - inline list& operator= (list&& v) { vector::operator= (move(v)); return *this; } - template - inline void emplace_front (Args&&... args) { vector::emplace (begin(), forward(args)...); } - inline void push_front (T&& v) { emplace_front (move(v)); } -#endif -}; - -/// Merges the contents with \p l. Assumes both lists are sorted. -template -void list::merge (list& l) -{ - this->insert_space (begin(), l.size()); - ::ustl::merge (this->iat(l.size()), end(), l.begin(), l.end(), begin()); -} - -/// Moves the range [first, last) from \p l to this list at \p ip. -template -void list::splice (iterator ip, list& l, iterator first, iterator last) -{ - if (!first) - first = l.begin(); - if (!last) - last = l.end(); - this->insert (ip, first, last); - l.erase (first, last); -} - -#if HAVE_CPP11 - template using deque = list; -#else - #define deque list ///< list has all the functionality provided by deque -#endif - - -} // namespace ustl diff --git a/common/include/ustl/umap.h b/common/include/ustl/umap.h deleted file mode 100644 index 9aa2ff2b8..000000000 --- a/common/include/ustl/umap.h +++ /dev/null @@ -1,168 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "uvector.h" -#include "ufunction.h" - -namespace ustl { - -template -struct pair_compare_first : public binary_function { - inline bool operator()(const Pair& a, const Pair& b) { return Comp()(a.first,b.first); } -}; -template -struct pair_compare_first_key : public binary_function,K,bool> { - inline bool operator()(const pair& a, const K& b) { return Comp()(a.first,b); } - inline bool operator()(const K& a, const pair& b) { return Comp()(a,b.first); } -}; - -/// \class map umap.h ustl.h -/// \ingroup AssociativeContainers -/// -/// \brief A sorted associative container of pair -/// -template > -class map : public vector > { -public: - typedef K key_type; - typedef V data_type; - typedef const K& const_key_ref; - typedef const V& const_data_ref; - typedef const map& rcself_t; - typedef vector > base_class; - typedef typename base_class::value_type value_type; - typedef typename base_class::size_type size_type; - typedef typename base_class::pointer pointer; - typedef typename base_class::const_pointer const_pointer; - typedef typename base_class::reference reference; - typedef typename base_class::const_reference const_reference; - typedef typename base_class::const_iterator const_iterator; - typedef typename base_class::iterator iterator; - typedef typename base_class::reverse_iterator reverse_iterator; - typedef typename base_class::const_reverse_iterator const_reverse_iterator; - typedef pair const_range_t; - typedef pair range_t; - typedef pair insertrv_t; - typedef Comp key_compare; - typedef pair_compare_first value_compare; - typedef pair_compare_first_key value_key_compare; -public: - inline map (void) : base_class() {} - explicit inline map (size_type n) : base_class (n) {} - inline map (rcself_t v) : base_class (v) {} - inline map (const_iterator i1, const_iterator i2) : base_class() { insert (i1, i2); } - inline rcself_t operator= (rcself_t v) { base_class::operator= (v); return *this; } - inline const_data_ref at (const_key_ref k) const { assert (find(k) != end()); return find(k)->second; } - inline data_type& at (const_key_ref k) { assert (find(k) != end()); return find(k)->second; } - inline const_data_ref operator[] (const_key_ref i) const { return at(i); } - data_type& operator[] (const_key_ref i); - inline key_compare key_comp (void) const { return key_compare(); } - inline value_compare value_comp (void) const { return value_compare(); } - inline size_type size (void) const { return base_class::size(); } - inline iterator begin (void) { return base_class::begin(); } - inline const_iterator begin (void) const { return base_class::begin(); } - inline iterator end (void) { return base_class::end(); } - inline const_iterator end (void) const { return base_class::end(); } - inline void assign (const_iterator i1, const_iterator i2) { clear(); insert (i1, i2); } - inline void push_back (const_reference v) { insert (v); } - inline const_iterator find (const_key_ref k) const; - inline iterator find (const_key_ref k) { return const_cast (const_cast(*this).find (k)); } - inline const_iterator find_data (const_data_ref v, const_iterator first = nullptr, const_iterator last = nullptr) const; - inline iterator find_data (const_data_ref v, iterator first = nullptr, iterator last = nullptr) { return const_cast (find_data (v, const_cast(first), const_cast(last))); } - const_iterator lower_bound (const_key_ref k) const { return ::ustl::lower_bound (begin(), end(), k, value_key_compare()); } - inline iterator lower_bound (const_key_ref k) { return const_cast(const_cast(*this).lower_bound (k)); } - const_iterator upper_bound (const_key_ref k) const { return ::ustl::upper_bound (begin(), end(), k, value_key_compare()); } - inline iterator upper_bound (const_key_ref k) { return const_cast(const_cast(*this).upper_bound (k)); } - const_range_t equal_range (const_key_ref k) const { return ::ustl::equal_range (begin(), end(), k, value_key_compare()); } - inline range_t equal_range (const_key_ref k) { return ::ustl::equal_range (begin(), end(), k, value_key_compare()); } - inline size_type count (const_key_ref v) const { const_range_t r = equal_range(v); return distance(r.first,r.second); } - insertrv_t insert (const_reference v); - inline iterator insert (const_iterator, const_reference v) { return insert(v).first; } - void insert (const_iterator i1, const_iterator i2) { for (; i1 != i2; ++i1) insert (*i1); } - inline void erase (const_key_ref k); - inline iterator erase (iterator ep) { return base_class::erase (ep); } - inline iterator erase (iterator ep1, iterator ep2) { return base_class::erase (ep1, ep2); } - inline void clear (void) { base_class::clear(); } - inline void swap (map& v) { base_class::swap (v); } -#if HAVE_CPP11 - using initlist_t = std::initializer_list; - inline map (map&& v) : base_class (move(v)) {} - inline map (initlist_t v) : base_class() { insert (v.begin(), v.end()); } - inline map& operator= (map&& v) { base_class::operator= (move(v)); return *this; } - insertrv_t insert (value_type&& v); - inline iterator insert (const_iterator, value_type&& v) { return insert(move(v)).first; } - inline void insert (initlist_t v) { insert (v.begin(), v.end()); } - template - inline insertrv_t emplace (Args&&... args) { return insert (value_type(forward(args)...)); } - template - inline iterator emplace_hint (const_iterator h, Args&&... args) { return insert (h, value_type(forward(args)...)); } - template - inline insertrv_t emplace_back (Args&&... args) { return insert (value_type(forward(args)...)); } -#endif -}; - -/// Returns the pair where K = \p k. -template -inline typename map::const_iterator map::find (const_key_ref k) const -{ - const_iterator i = lower_bound (k); - return (i < end() && Comp()(k,i->first)) ? end() : i; -} - -/// Returns the pair where V = \p v, occuring in range [first,last). -template -inline typename map::const_iterator map::find_data (const_data_ref v, const_iterator first, const_iterator last) const -{ - if (!first) first = begin(); - if (!last) last = end(); - for (; first != last && first->second != v; ++first) ; - return first; -} - -/// Returns data associated with key \p k. -template -typename map::data_type& map::operator[] (const_key_ref k) -{ - iterator ip = lower_bound (k); - if (ip == end() || Comp()(k,ip->first)) - ip = base_class::insert (ip, make_pair (k, V())); - return ip->second; -} - -/// Inserts the pair into the container. -template -typename map::insertrv_t map::insert (const_reference v) -{ - iterator ip = lower_bound (v.first); - bool bInserted = ip == end() || Comp()(v.first, ip->first); - if (bInserted) - ip = base_class::insert (ip, v); - return make_pair (ip, bInserted); -} - -#if HAVE_CPP11 -/// Inserts the pair into the container. -template -typename map::insertrv_t map::insert (value_type&& v) -{ - iterator ip = lower_bound (v.first); - bool bInserted = ip == end() || Comp()(v.first, ip->first); - if (bInserted) - ip = base_class::insert (ip, move(v)); - return make_pair (ip, bInserted); -} -#endif - -/// Erases the element with key value \p k. -template -inline void map::erase (const_key_ref k) -{ - iterator ip = find (k); - if (ip != end()) - erase (ip); -} - -} // namespace ustl diff --git a/common/include/ustl/umemory.h b/common/include/ustl/umemory.h deleted file mode 100644 index 3687c8afc..000000000 --- a/common/include/ustl/umemory.h +++ /dev/null @@ -1,546 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "unew.h" -#include "uatomic.h" -#include "uiterator.h" -#include "ulimits.h" -#include "upair.h" - -namespace ustl { - -//{{{ auto_ptr ------------------------------------------------------- - -/// \class auto_ptr umemory.h ustl.h -/// \ingroup MemoryManagement -/// -/// \brief A smart pointer. -/// -/// Calls delete in the destructor; assignment transfers ownership. -/// This class does not work with void pointers due to the absence -/// of the required dereference operator. auto_ptr is deprecated in -/// c++11; use unique_ptr instead. -/// -template -class auto_ptr { -public: - typedef T value_type; - typedef T* pointer; - typedef T& reference; -public: - /// Takes ownership of \p p. - inline explicit auto_ptr (pointer p = nullptr) : _p (p) {} - /// Takes ownership of pointer in \p p. \p p relinquishes ownership. - inline auto_ptr (auto_ptr& p) : _p (p.release()) {} - /// Deletes the owned pointer. - inline ~auto_ptr (void) { delete _p; } - /// Returns the pointer without relinquishing ownership. - inline pointer get (void) const { return _p; } - /// Returns the pointer and gives up ownership. - inline pointer release (void) { pointer rv (_p); _p = nullptr; return rv; } - /// Deletes the pointer and sets it equal to \p p. - inline void reset (pointer p) { if (p != _p) { delete _p; _p = p; } } - /// Takes ownership of \p p. - inline auto_ptr& operator= (pointer p) { reset (p); return *this; } - /// Takes ownership of pointer in \p p. \p p relinquishes ownership. - inline auto_ptr& operator= (auto_ptr& p) { reset (p.release()); return *this; } - inline reference operator* (void) const { return *_p; } - inline pointer operator-> (void) const { return _p; } - inline bool operator== (const pointer p) const { return _p == p; } - inline bool operator== (const auto_ptr& p) const { return _p == p._p; } - inline bool operator< (const auto_ptr& p) const { return p._p < _p; } -private: - pointer _p; -}; - -//}}}------------------------------------------------------------------- -//{{{ unique_ptr -#if HAVE_CPP11 - -/// \class unique_ptr memory.h stl.h -/// \ingroup MemoryManagement -/// \brief A smart pointer. -/// Calls delete in the destructor; assignment transfers ownership. -/// This class does not work with void pointers due to the absence -/// of the required dereference operator. -template -class unique_ptr { -public: - using element_type = T; - using pointer = element_type*; - using reference = element_type&; -public: - inline constexpr unique_ptr (void) : _p (nullptr) {} - inline constexpr explicit unique_ptr (pointer p) : _p (p) {} - inline unique_ptr (unique_ptr&& p) : _p (p.release()) {} - unique_ptr (const unique_ptr&) = delete; - inline ~unique_ptr (void) { delete _p; } - inline constexpr pointer get (void) const { return _p; } - inline pointer release (void) { auto rv (_p); _p = nullptr; return rv; } - inline void reset (pointer p = nullptr) { assert (p != _p || !p); auto ov (_p); _p = p; delete ov; } - inline void swap (unique_ptr& v) { ::ustl::swap (_p, v._p); } - inline constexpr explicit operator bool (void) const { return _p != nullptr; } - inline unique_ptr& operator= (pointer p) { reset (p); return *this; } - inline unique_ptr& operator= (unique_ptr&& p) { reset (p.release()); return *this; } - unique_ptr& operator=(const unique_ptr&) = delete; - inline constexpr reference operator* (void) const { return *get(); } - inline constexpr pointer operator-> (void) const { return get(); } - inline constexpr reference operator[] (size_t i) const { return get()[i]; } - inline constexpr bool operator== (const pointer p) const { return _p == p; } - inline constexpr bool operator== (const unique_ptr& p) const { return _p == p._p; } - inline constexpr bool operator< (const unique_ptr& p) const { return _p < p._p; } -private: - pointer _p; -}; - -// array version -template -class unique_ptr { -public: - using element_type = T; - using pointer = element_type*; - using reference = element_type&; -public: - inline constexpr unique_ptr (void) : _p (nullptr) {} - inline constexpr explicit unique_ptr (pointer p) : _p (p) {} - inline unique_ptr (unique_ptr&& p) : _p (p.release()) {} - unique_ptr(const unique_ptr&) = delete; - inline ~unique_ptr (void) { delete [] _p; } - inline constexpr pointer get (void) const { return _p; } - inline pointer release (void) { auto rv (_p); _p = nullptr; return rv; } - inline void reset (pointer p) { assert (p != _p); auto ov (_p); _p = p; delete [] ov; } - inline void swap (unique_ptr& v) { ::ustl::swap (_p, v._p); } - inline constexpr explicit operator bool (void) const { return _p != nullptr; } - inline unique_ptr& operator= (pointer p) { reset (p); return *this; } - inline unique_ptr& operator= (unique_ptr&& p) { reset (p.release()); return *this; } - unique_ptr& operator=(const unique_ptr&) = delete; - inline constexpr reference operator* (void) const { return *_p; } - inline constexpr pointer operator-> (void) const { return _p; } - inline constexpr reference operator[] (size_t i) const { return _p[i]; } - inline constexpr bool operator== (const pointer p) const { return _p == p; } - inline constexpr bool operator== (const unique_ptr& p) const { return _p == p._p; } - inline constexpr bool operator< (const unique_ptr& p) const { return _p < p._p; } -private: - pointer _p; -}; - -#if HAVE_CPP14 - -template struct __make_unique { using __single_object = unique_ptr; }; -template struct __make_unique { using __array = unique_ptr; }; -template struct __make_unique { struct __invalid_type {}; }; - -template -inline typename __make_unique::__single_object - make_unique (Args&&... args) { return unique_ptr (new T (forward(args)...)); } - -template -inline typename __make_unique::__array - make_unique (size_t n) { return unique_ptr (new remove_extent_t[n]()); } - -template -inline typename __make_unique::__invalid_type - make_unique (Args&&...) = delete; - -#endif // HAVE_CPP14 -#endif // HAVE_CPP11 - -//}}}------------------------------------------------------------------- -//{{{ shared_ptr - -#if HAVE_CPP11 - -/// \class shared_ptr memory.h stl.h -/// \ingroup MemoryManagement -/// \brief A smart pointer. -/// Calls delete in the destructor; assignment shares ownership. -template -class shared_ptr { -public: - using element_type = T; - using pointer = element_type*; - using reference = element_type&; -private: - struct container { - pointer p; - atomic refs; - inline constexpr explicit container (pointer np) : p(np),refs(1) {} - inline ~container (void) noexcept { assert (!refs); delete p; } - }; -public: - inline constexpr shared_ptr (void) : _p (nullptr) {} - inline explicit shared_ptr (pointer p) : _p (new container (p)) {} - inline shared_ptr (shared_ptr&& p) : _p (p._p) { p._p = nullptr; } - inline shared_ptr (const shared_ptr& p): _p (p._p) { if (_p) ++_p->refs; } - inline ~shared_ptr (void) { reset(); } - inline constexpr size_t use_count (void) const { return _p ? _p->refs.load() : 0; } - inline constexpr bool unique (void) const { return use_count() == 1; } - inline constexpr pointer get (void) const { return _p ? _p->p : nullptr; } - void reset (pointer p = nullptr) { - assert (p != get() || !p); - auto ov = _p; - _p = p ? new container(p) : nullptr; - if (ov && !--ov->refs) - delete ov; - } - inline void swap (shared_ptr& v) { ::ustl::swap (_p, v._p); } - inline constexpr explicit operator bool (void) const { return get(); } - inline shared_ptr& operator= (pointer p) { reset (p); return *this; } - inline shared_ptr& operator= (shared_ptr&& p) { swap (p); return *this; } - inline shared_ptr& operator= (const shared_ptr& p) { reset(); _p = p._p; if (_p) ++_p->refs; return *this; } - inline constexpr reference operator* (void) const { return *get(); } - inline constexpr pointer operator-> (void) const { return get(); } - inline constexpr reference operator[] (size_t i) const { return get()[i]; } - inline constexpr bool operator== (const pointer p) const { return get() == p; } - inline constexpr bool operator== (const shared_ptr& p) const { return get() == p.get(); } - inline constexpr bool operator< (const shared_ptr& p) const { return get() < p.get(); } -private: - container* _p; -}; - -#if HAVE_CPP14 - -template -inline auto make_shared (Args&&... args) - { return shared_ptr (new T (forward(args)...)); } - -#endif // HAVE_CPP14 - -//}}}------------------------------------------------------------------- -//{{{ scope_exit - -template -class scope_exit { -public: - inline explicit scope_exit (F&& f) noexcept : _f(move(f)),_enabled(true) {} - inline scope_exit (scope_exit&& f) noexcept : _f(move(f._f)),_enabled(f._enabled) { f.release(); } - inline void release (void) noexcept { _enabled = false; } - inline ~scope_exit (void) noexcept (noexcept (declval())) { if (_enabled) _f(); } - scope_exit (const scope_exit&) = delete; - scope_exit& operator= (const scope_exit&) = delete; - scope_exit& operator= (scope_exit&&) = delete; -private: - F _f; - bool _enabled; -}; - -#if HAVE_CPP14 -template -auto make_scope_exit (F&& f) noexcept - { return scope_exit>(forward(f)); } -#endif // HAVE_CPP14 - -//}}}------------------------------------------------------------------- -//{{{ unique_resource - -template -class unique_resource { -public: - inline explicit unique_resource (R&& resource, D&& deleter, bool enabled = true) noexcept - : _resource(move(resource)), _deleter(move(deleter)),_enabled(enabled) {} - inline unique_resource (unique_resource&& r) noexcept - : _resource(move(r._resource)),_deleter(move(r._deleter)),_enabled(r._enabled) { r.release(); } - unique_resource (const unique_resource&) = delete; - inline ~unique_resource() noexcept(noexcept(declval>().reset())) - { reset(); } - inline const D& get_deleter (void) const noexcept { return _deleter; } - inline R const& get (void) const noexcept { return _resource; } - inline R const& release (void) noexcept { _enabled = false; return get(); } - inline void reset (void) noexcept (noexcept(declval())) { - if (_enabled) { - _enabled = false; - get_deleter()(_resource); - } - } - inline void reset (R&& r) noexcept (noexcept(reset())) { - reset(); - _resource = move(r); - _enabled = true; - } - unique_resource& operator= (const unique_resource&) = delete; - unique_resource& operator= (unique_resource &&r) noexcept(noexcept(reset())) { - reset(); - _deleter = move(r._deleter); - _resource = move(r._resource); - _enabled = r._enabled; - r.release(); - return *this; - } - inline operator R const& (void) const noexcept { return get(); } - inline R operator-> (void) const noexcept { return _resource; } - inline add_lvalue_reference_t> - operator* (void) const { return *_resource; } -private: - R _resource; - D _deleter; - bool _enabled; -}; - -#if HAVE_CPP14 - -template -auto make_unique_resource (R&& r, D&& d) noexcept - { return unique_resource>(move(r), forward>(d), true); } - -template -auto make_unique_resource_checked (R r, R invalid, D d) noexcept -{ - bool shouldrun = !(r == invalid); - return unique_resource(move(r), move(d), shouldrun); -} - -#endif // HAVE_CPP14 -#endif // HAVE_CPP11 - -//}}}------------------------------------------------------------------- -//{{{ construct and destroy - -/// Calls the placement new on \p p. -/// \ingroup RawStorageAlgorithms -/// -template -inline void construct_at (T* p) - { new (p) T; } - -/// Calls the placement new on \p p. -/// \ingroup RawStorageAlgorithms -/// -template -inline void construct_at (T* p, const T& value) - { new (p) T (value); } - -#if HAVE_CPP11 -/// Calls the move placement new on \p p. -/// \ingroup RawStorageAlgorithms -/// -template -inline void construct_at (T* p, T&& value) - { new (p) T (move(value)); } -#endif - -template -inline void construct (T* p) - { construct_at(p); } - -/// Calls the placement new on \p p. -/// \ingroup RawStorageAlgorithms -/// -template -inline void uninitialized_default_construct (ForwardIterator first, ForwardIterator last) -{ - typedef typename iterator_traits::value_type value_type; -#if HAVE_CPP11 - if (is_pod::value) -#else - if (numeric_limits::is_integral) -#endif - memset (reinterpret_cast(first), 0, max(distance(first,last),0)*sizeof(value_type)); - else - for (--last; intptr_t(first) <= intptr_t(last); ++first) - construct_at (&*first); -} -template -inline void uninitialized_default_construct_n (ForwardIterator first, size_t n) - { uninitialized_default_construct (first, first+n); } -template -inline void construct (ForwardIterator first, ForwardIterator last) - { uninitialized_default_construct (first, last); } - -/// Calls the placement new on \p [first,last) with iterator_traits::value_type() -template -inline void uninitialized_value_construct (ForwardIterator first, ForwardIterator last) -{ - typedef typename iterator_traits::value_type value_type; - for (--last; intptr_t(first) <= intptr_t(last); ++first) - construct_at (&*first, value_type()); -} -template -inline void uninitialized_value_construct_n (ForwardIterator first, size_t n) - { uninitialized_value_construct (first, first+n); } - -/// Calls the destructor of \p p without calling delete. -/// \ingroup RawStorageAlgorithms -/// -template -inline void destroy_at (T* p) noexcept - { p->~T(); } - -template -inline void destroy (T* p) noexcept - { destroy_at(p); } - -// Helper templates to not instantiate anything for integral types. -namespace { - -template -void dtors (T first, T last) noexcept - { for (--last; intptr_t(first) <= intptr_t(last); ++first) destroy_at (&*first); } -template -struct Sdtorsr { - inline void operator()(T first, T last) noexcept { dtors (first, last); } -}; -template -struct Sdtorsr { - inline void operator()(T, T) noexcept {} -}; -} // namespace - -/// Calls the destructor on elements in range [first, last) without calling delete. -/// \ingroup RawStorageAlgorithms -/// -template -inline void destroy (ForwardIterator first, ForwardIterator last) noexcept -{ - typedef typename iterator_traits::value_type value_type; -#if HAVE_CPP11 - Sdtorsr::value>()(first, last); -#else - Sdtorsr::is_integral>()(first, last); -#endif -} -template -inline void destroy_n (ForwardIterator first, size_t n) noexcept - { destroy (first, first+n); } - -//}}}------------------------------------------------------------------- -//{{{ Raw storage algorithms - -//}}}------------------------------------------------------------------- -//{{{ Raw storage algorithms - -template inline T* cast_to_type (void* p, const T*) { return reinterpret_cast(p); } - -/// \brief Creates a temporary buffer pair from \p p and \p n -/// This is intended to be used with alloca to create temporary buffers. -/// The size in the returned pair is set to 0 if the allocation is unsuccessful. -/// \ingroup RawStorageAlgorithms -/// -template -inline pair make_temporary_buffer (void* p, size_t n, const T* ptype) -{ - return make_pair (cast_to_type(p,ptype), ptrdiff_t(p ? n : 0)); -} - -#if HAVE_ALLOCA_H - /// \brief Allocates a temporary buffer, if possible. - /// \ingroup RawStorageAlgorithms - #define get_temporary_buffer(size, ptype) make_temporary_buffer (alloca(size_of_elements(size, ptype)), size, ptype) - #define return_temporary_buffer(p) -#else - #define get_temporary_buffer(size, ptype) make_temporary_buffer (malloc(size_of_elements(size, ptype)), size, ptype) - #define return_temporary_buffer(p) if (p) free (p), p = nullptr -#endif - -/// Copies [first, last) into result by calling copy constructors in result. -/// \ingroup RawStorageAlgorithms -/// -template -ForwardIterator uninitialized_copy (InputIterator first, InputIterator last, ForwardIterator result) -{ - for (; first < last; ++result, ++first) - construct_at (&*result, *first); - return result; -} - -/// Copies [first, first + n) into result by calling copy constructors in result. -/// \ingroup RawStorageAlgorithms -/// -template -ForwardIterator uninitialized_copy_n (InputIterator first, size_t n, ForwardIterator result) -{ - for (++n; --n; ++result, ++first) - construct_at (&*result, *first); - return result; -} - -/// Calls construct on all elements in [first, last) with value \p v. -/// \ingroup RawStorageAlgorithms -/// -template -void uninitialized_fill (ForwardIterator first, ForwardIterator last, const T& v) -{ - for (; first < last; ++first) - construct_at (&*first, v); -} - -/// Calls construct on all elements in [first, first + n) with value \p v. -/// \ingroup RawStorageAlgorithms -/// -template -ForwardIterator uninitialized_fill_n (ForwardIterator first, size_t n, const T& v) -{ - for (++n; --n; ++first) - construct_at (&*first, v); - return first; -} - -#if HAVE_CPP11 - -/// Moves [first, last) into result by calling move constructors in result. -/// \ingroup RawStorageAlgorithms -/// -template -ForwardIterator uninitialized_move (InputIterator first, InputIterator last, ForwardIterator result) -{ - for (; first < last; ++result, ++first) - construct_at (&*result, move(*first)); - return result; -} - -/// Moves [first, first + n) into result by calling move constructors in result. -/// \ingroup RawStorageAlgorithms -/// -template -ForwardIterator uninitialized_move_n (InputIterator first, size_t n, ForwardIterator result) -{ - for (++n; --n; ++result, ++first) - construct_at (&*result, move(*first)); - return result; -} - -#endif // HAVE_CPP11 - -} // namespace ustl - -//}}}------------------------------------------------------------------- -//{{{ initializer_list -#if HAVE_CPP11 && WITHOUT_LIBSTDCPP - -namespace std { // Internal stuff must be in std:: - -/// Internal class for compiler support of C++11 initializer lists -template -class initializer_list { -public: - typedef T value_type; - typedef size_t size_type; - typedef const T& const_reference; - typedef const_reference reference; - typedef const T* const_iterator; - typedef const_iterator iterator; -private: - /// This object is only constructed by the compiler when the {1,2,3} - /// syntax is used, so the constructor must be private - inline constexpr initializer_list (const_iterator p, size_type sz) noexcept : _data(p), _size(sz) {} -public: - inline constexpr initializer_list (void)noexcept : _data(nullptr), _size(0) {} - inline constexpr size_type size (void) const noexcept { return _size; } - inline constexpr const_iterator begin() const noexcept { return _data; } - inline constexpr const_iterator end() const noexcept { return begin()+size(); } -private: - iterator _data; - size_type _size; -}; - -template -inline constexpr const T* begin (initializer_list il) noexcept { return il.begin(); } -template -inline constexpr const T* end (initializer_list il) noexcept { return il.end(); } - -} // namespace std - -#endif // HAVE_CPP11 -//}}}------------------------------------------------------------------- diff --git a/common/include/ustl/umultimap.h b/common/include/ustl/umultimap.h deleted file mode 100644 index 1d93c06bc..000000000 --- a/common/include/ustl/umultimap.h +++ /dev/null @@ -1,96 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "umap.h" - -namespace ustl { - -/// \class multimap umultimap.h ustl.h -/// \ingroup AssociativeContainers -/// -/// \brief A sorted associative container that may container multiple entries for each key. -/// -template > -class multimap : public vector > { -public: - typedef K key_type; - typedef V data_type; - typedef const K& const_key_ref; - typedef const V& const_data_ref; - typedef const multimap& rcself_t; - typedef vector > base_class; - typedef typename base_class::value_type value_type; - typedef typename base_class::size_type size_type; - typedef typename base_class::pointer pointer; - typedef typename base_class::const_pointer const_pointer; - typedef typename base_class::reference reference; - typedef typename base_class::const_reference const_reference; - typedef typename base_class::const_iterator const_iterator; - typedef typename base_class::iterator iterator; - typedef typename base_class::reverse_iterator reverse_iterator; - typedef typename base_class::const_reverse_iterator const_reverse_iterator; - typedef pair const_range_t; - typedef pair range_t; - typedef Comp key_compare; - typedef pair_compare_first value_compare; - typedef pair_compare_first_key value_key_compare; -public: - inline multimap (void) : base_class() {} - explicit inline multimap (size_type n) : base_class (n) {} - inline multimap (rcself_t v) : base_class (v) {} - inline multimap (const_iterator i1, const_iterator i2) : base_class() { insert (i1, i2); } - inline rcself_t operator= (rcself_t v) { base_class::operator= (v); return *this; } - inline key_compare key_comp (void) const { return key_compare(); } - inline value_compare value_comp (void) const { return value_compare(); } - inline size_type size (void) const { return base_class::size(); } - inline iterator begin (void) { return base_class::begin(); } - inline const_iterator begin (void) const { return base_class::begin(); } - inline iterator end (void) { return base_class::end(); } - inline const_iterator end (void) const { return base_class::end(); } - inline const_iterator find (const_key_ref k) const; - inline iterator find (const_key_ref k) { return const_cast (const_cast(*this).find (k)); } - const_iterator lower_bound (const_key_ref k) const { return ::ustl::lower_bound (begin(), end(), k, value_key_compare()); } - inline iterator lower_bound (const_key_ref k) { return const_cast(const_cast(*this).lower_bound (k)); } - const_iterator upper_bound (const_key_ref k) const { return ::ustl::upper_bound (begin(), end(), k, value_key_compare()); } - inline iterator upper_bound (const_key_ref k) { return const_cast(const_cast(*this).upper_bound (k)); } - const_range_t equal_range (const_key_ref k) const { return ::ustl::equal_range (begin(), end(), k, value_key_compare()); } - inline range_t equal_range (const_key_ref k) { return ::ustl::equal_range (begin(), end(), k, value_key_compare()); } - inline size_type count (const_key_ref v) const { const_range_t r = equal_range(v); return distance(r.first,r.second); } - inline void assign (const_iterator i1, const_iterator i2) { clear(); insert (i1, i2); } - inline void push_back (const_reference v) { insert (v); } - inline iterator insert (const_reference v) { return base_class::insert (upper_bound (v.first), v); } - void insert (const_iterator i1, const_iterator i2) { for (; i1 != i2; ++i1) insert (*i1); } - inline void clear (void) { base_class::clear(); } - inline void erase (const_key_ref k) { erase (const_cast(lower_bound(k)), const_cast(upper_bound(k))); } - inline iterator erase (const_iterator ep) { return base_class::erase (ep); } - inline iterator erase (const_iterator ep1, const_iterator ep2) { return base_class::erase (ep1, ep2); } - inline void swap (multimap& v) { base_class::swap (v); } -#if HAVE_CPP11 - using initlist_t = std::initializer_list; - inline multimap (multimap&& v) : base_class (move(v)) {} - inline multimap (initlist_t v) : base_class() { insert (v.begin(), v.end()); } - inline multimap& operator= (multimap&& v) { base_class::operator= (move(v)); return *this; } - iterator insert (value_type&& v) { return base_class::insert (upper_bound (v.first), move(v)); } - inline iterator insert (const_iterator, value_type&& v) { return insert(move(v)); } - inline void insert (initlist_t v) { insert (v.begin(), v.end()); } - template - inline iterator emplace (Args&&... args) { return insert (value_type(forward(args)...)); } - template - inline iterator emplace_hint (const_iterator h, Args&&... args) { return insert (h, value_type(forward(args)...)); } - template - inline iterator emplace_back (Args&&... args) { return insert (value_type(forward(args)...)); } -#endif -}; - -/// Returns the pair where K = \p k. -template -inline typename multimap::const_iterator multimap::find (const_key_ref k) const -{ - const_iterator i = lower_bound (k); - return (i < end() && Comp()(k, i->first)) ? end() : i; -} - -} // namespace ustl diff --git a/common/include/ustl/umultiset.h b/common/include/ustl/umultiset.h deleted file mode 100644 index b8e0272d6..000000000 --- a/common/include/ustl/umultiset.h +++ /dev/null @@ -1,86 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "uvector.h" -#include "ualgo.h" - -namespace ustl { - -/// \class multiset umultiset.h ustl.h -/// \ingroup AssociativeContainers -/// -/// \brief Multiple sorted container. -/// Unlike set, it may contain multiple copies of each element. -/// -template > -class multiset : public vector { -public: - typedef const multiset& rcself_t; - typedef vector base_class; - typedef typename base_class::value_type value_type; - typedef typename base_class::size_type size_type; - typedef typename base_class::pointer pointer; - typedef typename base_class::const_pointer const_pointer; - typedef typename base_class::reference reference; - typedef typename base_class::const_reference const_reference; - typedef typename base_class::const_iterator const_iterator; - typedef typename base_class::iterator iterator; - typedef typename base_class::reverse_iterator reverse_iterator; - typedef typename base_class::const_reverse_iterator const_reverse_iterator; - typedef pair range_t; - typedef pair const_range_t; -public: - inline multiset (void) : base_class() {} - inline explicit multiset (size_type n) : base_class (n) {} - inline explicit multiset (rcself_t v) : base_class (v) {} - inline multiset (const_iterator i1, const_iterator i2) : base_class() { insert (i1, i2); } - inline rcself_t operator= (rcself_t v) { base_class::operator= (v); return *this; } - inline size_type size (void) const { return base_class::size(); } - inline iterator begin (void) { return base_class::begin(); } - inline const_iterator begin (void) const { return base_class::begin(); } - inline const_iterator cbegin (void) const { return base_class::cbegin(); } - inline iterator end (void) { return base_class::end(); } - inline const_iterator end (void) const { return base_class::end(); } - inline const_iterator cend (void) const { return base_class::cend(); } - inline Comp value_comp (void) const { return Comp(); } - inline Comp key_comp (void) const { return value_comp(); } - inline void assign (const_iterator i1, const_iterator i2) { clear(); insert (i1, i2); } - inline const_iterator find (const_reference v) const { const_iterator i = ::ustl::lower_bound (begin(), end(), v, Comp()); return (i != end() && *i == v) ? i : end(); } - inline iterator find (const_reference v) { return const_cast(const_cast(*this).find (v)); } - inline const_iterator lower_bound (const_reference v) const { return ::ustl::lower_bound (begin(), end(), v, Comp()); } - inline iterator lower_bound (const_reference v) { return const_cast(const_cast(*this).lower_bound (v)); } - inline const_iterator upper_bound (const_reference v) const { return ::ustl::upper_bound (begin(), end(), v, Comp()); } - inline iterator upper_bound (const_reference v) { return const_cast(const_cast(*this).upper_bound (v)); } - inline const_range_t equal_range (const_reference v) const { return ::ustl::equal_range (begin(), end(), v, Comp()); } - inline range_t equal_range (const_reference v) { return ::ustl::equal_range (begin(), end(), v, Comp()); } - inline size_type count (const_reference v) const { const_range_t r = equal_range(v); return distance(r.first,r.second); } - inline void push_back (const_reference v) { insert (v); } - inline iterator insert (const_reference v) { return base_class::insert (upper_bound(v), v); } - inline iterator insert (const_iterator, const_reference v) { return insert(v); } - void insert (const_iterator i1, const_iterator i2) { for (; i1 < i2; ++i1) insert (*i1); } - inline iterator erase (const_iterator ep) { return base_class::erase (ep); } - inline iterator erase (const_iterator ep1, const_iterator ep2) { return base_class::erase (ep1, ep2); } - inline size_type erase (const_reference v) { range_t epr = equal_range (v); erase (epr.first, epr.second); return distance(epr.first, epr.second); } - inline void clear (void) { base_class::clear(); } - inline void swap (multiset& v) { base_class::swap (v); } -#if HAVE_CPP11 - using initlist_t = std::initializer_list; - inline explicit multiset (multiset&& v) : base_class (move(v)) {} - inline multiset (initlist_t v) : base_class() { insert (v.begin(), v.end()); } - inline multiset& operator= (multiset&& v) { base_class::operator= (move(v)); return *this; } - inline iterator insert (T&& v) { return base_class::insert (upper_bound(v), move(v)); } - inline iterator insert (const_iterator, T&& v) { return insert (move(v)); } - inline void insert (initlist_t v) { insert (v.begin(), v.end()); } - template - inline iterator emplace (Args&&... args) { return insert (T(forward(args)...)); } - template - inline iterator emplace_hint (const_iterator h, Args&&... args) { return insert (h, T(forward(args)...)); } - template - inline iterator emplace_back (Args&&... args) { return insert (T(forward(args)...)); } -#endif -}; - -} // namespace ustl diff --git a/common/include/ustl/unew.h b/common/include/ustl/unew.h deleted file mode 100644 index 7ccd4b331..000000000 --- a/common/include/ustl/unew.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/common/include/ustl/unumeric.h b/common/include/ustl/unumeric.h deleted file mode 100644 index dec0ae8ac..000000000 --- a/common/include/ustl/unumeric.h +++ /dev/null @@ -1,151 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once - -namespace ustl { - -/// Returns the sum of all elements in [first, last) added to \p init. -/// \ingroup NumericAlgorithms -/// -template -inline T accumulate (InputIterator first, InputIterator last, T init) -{ - while (first < last) - init += *first++; - return init; -} - -/// Returns the sum of all elements in [first, last) via \p op, added to \p init. -/// \ingroup NumericAlgorithms -/// -template -inline T accumulate (InputIterator first, InputIterator last, T init, BinaryFunction binary_op) -{ - while (first < last) - init = binary_op (init, *first++); - return init; -} - -/// Assigns range [value, value + (last - first)) to [first, last) -/// \ingroup NumericAlgorithms -/// -template -inline void iota (ForwardIterator first, ForwardIterator last, T value) -{ - while (first < last) - *first++ = value++; -} - -/// Returns the sum of products of respective elements in the given ranges. -/// \ingroup NumericAlgorithms -/// -template -inline T inner_product (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, T init) -{ - while (first1 < last1) - init += *first1++ * *first2++; - return init; -} - -/// Returns the sum of products of respective elements in the given ranges. -/// \ingroup NumericAlgorithms -/// -template -inline T inner_product -(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, T init, - BinaryOperation1 sumOp, BinaryOperation2 productOp) -{ - while (first1 < last1) - init = sumOp (init, productOp (*first1++, *first2++)); - return init; -} - -/// Writes result such that result[i] = sum (first...first+i) -/// \ingroup NumericAlgorithms -/// -template -inline OutputIterator partial_sum (InputIterator first, InputIterator last, OutputIterator result) -{ - if (first < last) - *result = *first++; - while (first < last) - *++result = *first++ + *result; - return result; -} - -/// Writes result such that result[i] = sumOp (first...first+i) -/// \ingroup NumericAlgorithms -/// -template -inline OutputIterator partial_sum (InputIterator first, InputIterator last, OutputIterator result, BinaryOperation sumOp) -{ - if (first < last) - *result = *first++; - while (first < last) - *++result = sumOp (*first++, *result); - return result; -} - -/// Writes result such that result[i] = first[i] - first[i - 1] -/// \ingroup NumericAlgorithms -/// -template -inline OutputIterator adjacent_difference (InputIterator first, InputIterator last, OutputIterator result) -{ - if (first < last) - *result++ = *first++; - while (first < last) - *result++ = *first - *(first - 1); - return result; -} - -/// Writes result such that result[i] = differenceOp (first[i], first[i - 1]) -/// \ingroup NumericAlgorithms -/// -template -inline OutputIterator adjacent_difference (InputIterator first, InputIterator last, OutputIterator result, BinaryOperation differenceOp) -{ - if (first < last) - *result++ = *first++; - while (first < last) - *result++ = differenceOp (*first, *(first - 1)); - return result; -} - -/// \brief Returns x^n. -/// Donald Knuth's Russian Peasant algorithm. -/// \ingroup NumericAlgorithms -/// -template -inline T power (T x, unsigned n) -{ - T result (n % 2 ? x : 1); - while (n /= 2) { - x *= x; - if (n % 2) - result *= x; - } - return result; -} - -/// \brief Returns x^n, using \p op instead of multiplication. -/// Donald Knuth's Russian Peasant algorithm. -/// \ingroup NumericAlgorithms -/// -template -inline T power (T x, unsigned n, BinaryOperation op) -{ - T result (n % 2 ? x : 1); - while (n /= 2) { - x = op (x, x); - if (n % 2) - result = op (result, x); - } - return result; -} - -} // namespace ustl diff --git a/common/include/ustl/upair.h b/common/include/ustl/upair.h deleted file mode 100644 index a61e240a9..000000000 --- a/common/include/ustl/upair.h +++ /dev/null @@ -1,73 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "ualgobase.h" - -namespace ustl { - -class istream; -class ostream; -class ostringstream; - -/// \class pair upair.h ustl.h -/// \ingroup AssociativeContainers -/// -/// \brief Container for two values. -/// -template -class pair { -public: - typedef T1 first_type; - typedef T2 second_type; -public: - /// Default constructor. - inline constexpr pair (void) : first (T1()), second (T2()) {} - /// Initializes members with \p a, and \p b. - inline pair (const T1& a, const T2& b) : first (a), second (b) {} - template - inline pair (const pair& p2) : first (p2.first), second (p2.second) {} - inline pair& operator= (const pair& p2) { first = p2.first; second = p2.second; return *this; } - template - inline pair& operator= (const pair& p2) { first = p2.first; second = p2.second; return *this; } - inline bool operator== (const pair& v)const { return first == v.first && second == v.second; } - inline bool operator< (const pair& v) const { return first < v.first || (first == v.first && second < v.second); } - inline void swap (pair& v) { ::ustl::swap(first,v.first); ::ustl::swap(second,v.second); } - inline void read (istream& is); - inline void write (ostream& os) const; - void text_write (ostringstream& os) const; - inline size_t stream_size (void) const; -#if HAVE_CPP11 - pair (const pair&) = default; - pair (pair&&) = default; - template - inline pair (T3&& a, T4&& b) : first (forward(a)), second (forward(b)) {} - template - inline pair (pair&& p2) : first (forward(p2.first)), second (forward(p2.second)) {} - inline pair& operator= (pair&& p2) { first = move(p2.first); second = move(p2.second); return *this; } - template - inline pair& operator= (pair&& p2) { first = forward(p2.first); second = forward(p2.second); return *this; } - inline void swap (pair&& v) { ::ustl::swap(first,v.first); ::ustl::swap(second,v.second); } -#endif -public: - first_type first; - second_type second; -}; - -#if HAVE_CPP11 - -/// Returns a pair object with (a,b) -template -inline constexpr pair make_pair (T1&& a, T2&& b) - { return pair (forward(a), forward(b)); } - -#endif - -/// Returns a pair object with (a,b) -template -inline constexpr pair make_pair (const T1& a, const T2& b) - { return pair (a, b); } - -} // namespace ustl diff --git a/common/include/ustl/upredalgo.h b/common/include/ustl/upredalgo.h deleted file mode 100644 index 13b4ed442..000000000 --- a/common/include/ustl/upredalgo.h +++ /dev/null @@ -1,585 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "ualgo.h" - -namespace ustl { - -/// Copy_if copies elements from the range [first, last) to the range -/// [result, result + (last - first)) if pred(*i) returns true. -/// \ingroup MutatingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline OutputIterator copy_if (InputIterator first, InputIterator last, OutputIterator result, Predicate pred) -{ - for (; first != last; ++first) { - if (pred(*first)) { - *result = *first; - ++ result; - } - } - return result; -} - -/// Returns the first iterator i in the range [first, last) such that -/// pred(*i) is true. Returns last if no such iterator exists. -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline InputIterator find_if (InputIterator first, InputIterator last, Predicate pred) -{ - while (first != last && !pred (*first)) - ++ first; - return first; -} - -/// Returns the first iterator such that p(*i, *(i + 1)) == true. -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline ForwardIterator adjacent_find (ForwardIterator first, ForwardIterator last, BinaryPredicate p) -{ - if (first != last) - for (ForwardIterator prev = first; ++first != last; ++ prev) - if (p (*prev, *first)) - return prev; - return last; -} - -/// Returns the pointer to the first pair of unequal elements. -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline pair -mismatch (InputIterator first1, InputIterator last1, InputIterator first2, BinaryPredicate comp) -{ - while (first1 != last1 && comp(*first1, *first2)) - ++ first1, ++ first2; - return make_pair (first1, first2); -} - -/// Returns true if two ranges are equal. -/// This is an extension, present in uSTL and SGI STL. -/// \ingroup ConditionAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline bool equal (InputIterator first1, InputIterator last1, InputIterator first2, BinaryPredicate comp) -{ - return mismatch (first1, last1, first2, comp).first == last1; -} - -/// Count_if finds the number of elements in [first, last) that satisfy the -/// predicate pred. More precisely, the first version of count_if returns the -/// number of iterators i in [first, last) such that pred(*i) is true. -/// \ingroup ConditionAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline size_t count_if (InputIterator first, InputIterator last, Predicate pred) -{ - size_t total = 0; - for (; first != last; ++first) - if (pred (*first)) - ++ total; - return total; -} - -/// Replace_if replaces every element in the range [first, last) for which -/// pred returns true with new_value. That is: for every iterator i, if -/// pred(*i) is true then it performs the assignment *i = new_value. -/// \ingroup MutatingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline void replace_if (ForwardIterator first, ForwardIterator last, Predicate pred, const T& new_value) -{ - for (; first != last; ++first) - if (pred (*first)) - *first = new_value; -} - -/// Replace_copy_if copies elements from the range [first, last) to the range -/// [result, result + (last-first)), except that any element for which pred is -/// true is not copied; new_value is copied instead. More precisely, for every -/// integer n such that 0 <= n < last-first, replace_copy_if performs the -/// assignment *(result+n) = new_value if pred(*(first+n)), -/// and *(result+n) = *(first+n) otherwise. -/// \ingroup MutatingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline OutputIterator replace_copy_if (InputIterator first, InputIterator last, OutputIterator result, Predicate pred, const T& new_value) -{ - for (; first != last; ++result, ++first) - *result = pred(*first) ? new_value : *first; -} - -/// Remove_copy_if copies elements from the range [first, last) to a range -/// beginning at result, except that elements for which pred is true are not -/// copied. The return value is the end of the resulting range. This operation -/// is stable, meaning that the relative order of the elements that are copied -/// is the same as in the range [first, last). -/// \ingroup MutatingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline OutputIterator remove_copy_if (InputIterator first, InputIterator last, OutputIterator result, Predicate pred) -{ - for (; first != last; ++first) - if (!pred (*first)) - *result++ = *first; - return result; -} - -/// Remove_if removes from the range [first, last) every element x such that -/// pred(x) is true. That is, remove_if returns an iterator new_last such that -/// the range [first, new_last) contains no elements for which pred is true. -/// The iterators in the range [new_last, last) are all still dereferenceable, -/// but the elements that they point to are unspecified. Remove_if is stable, -/// meaning that the relative order of elements that are not removed is -/// unchanged. -/// \ingroup MutatingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline ForwardIterator remove_if (ForwardIterator first, ForwardIterator last, Predicate pred) -{ - return remove_copy_if (first, last, first, pred); -} - -/// The reason there are two different versions of unique_copy is that there -/// are two different definitions of what it means for a consecutive group of -/// elements to be duplicates. In the first version, the test is simple -/// equality: the elements in a range [f, l) are duplicates if, for every -/// iterator i in the range, either i == f or else *i == *(i-1). In the second, -/// the test is an arbitrary Binary Predicate binary_pred: the elements in -/// [f, l) are duplicates if, for every iterator i in the range, either -/// i == f or else binary_pred(*i, *(i-1)) is true. -/// \ingroup MutatingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -OutputIterator unique_copy (InputIterator first, InputIterator last, OutputIterator result, BinaryPredicate binary_pred) -{ - if (first != last) { - *result = *first; - while (++first != last) - if (!binary_pred (*first, *result)) - *++result = *first; - ++ result; - } - return result; -} - -/// Every time a consecutive group of duplicate elements appears in the range -/// [first, last), the algorithm unique removes all but the first element. -/// That is, unique returns an iterator new_last such that the range [first, -/// new_last) contains no two consecutive elements that are duplicates. -/// The iterators in the range [new_last, last) are all still dereferenceable, -/// but the elements that they point to are unspecified. Unique is stable, -/// meaning that the relative order of elements that are not removed is -/// unchanged. -/// \ingroup MutatingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline ForwardIterator unique (ForwardIterator first, ForwardIterator last, BinaryPredicate binary_pred) -{ - return unique_copy (first, last, first, binary_pred); -} - -/// Returns the furthermost iterator i in [first, last) such that, -/// for every iterator j in [first, i), comp(*j, value) is true. -/// Assumes the range is sorted. -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& value, StrictWeakOrdering comp) -{ - ForwardIterator mid; - while (first != last) { - mid = advance (first, size_t(distance (first,last)) / 2); - if (comp (*mid, value)) - first = mid + 1; - else - last = mid; - } - return first; -} - -/// Performs a binary search inside the sorted range. -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline bool binary_search (ForwardIterator first, ForwardIterator last, const T& value, StrictWeakOrdering comp) -{ - ForwardIterator found = lower_bound (first, last, value, comp); - return found != last && !comp(*found, value); -} - -/// Returns the furthermost iterator i in [first,last) such that for -/// every iterator j in [first,i), comp(value,*j) is false. -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& value, StrictWeakOrdering comp) -{ - ForwardIterator mid; - while (first != last) { - mid = advance (first, size_t(distance (first,last)) / 2); - if (comp (value, *mid)) - last = mid; - else - first = mid + 1; - } - return last; -} - -/// Returns pair -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline pair equal_range (ForwardIterator first, ForwardIterator last, const T& value, StrictWeakOrdering comp) -{ - pair rv; - rv.second = rv.first = lower_bound (first, last, value, comp); - while (rv.second != last && !comp(value, *(rv.second))) - ++ rv.second; - return rv; -} - -/// \brief Puts \p nth element into its sorted position. -/// In this implementation, the entire array is sorted. The performance difference is -/// so small and the function use is so rare, there is no need to have code for it. -/// \ingroup SortingAlgorithms -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -/// -template -inline void nth_element (RandomAccessIterator first, RandomAccessIterator, RandomAccessIterator last, Compare comp) -{ - sort (first, last, comp); -} - -/// \brief Searches for the first subsequence [first2,last2) in [first1,last1) -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -template -ForwardIterator1 search (ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2, BinaryPredicate comp) -{ - const ForwardIterator1 slast = last1 - distance(first2, last2) + 1; - for (; first1 < slast; ++first1) { - ForwardIterator2 i = first2; - ForwardIterator1 j = first1; - for (; i != last2 && comp(*j, *i); ++i, ++j) ; - if (i == last2) - return first1; - } - return last1; -} - -/// \brief Searches for the last subsequence [first2,last2) in [first1,last1) -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -template -ForwardIterator1 find_end (ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2, BinaryPredicate comp) -{ - ForwardIterator1 s = last1 - distance(first2, last2); - for (; first1 < s; --s) { - ForwardIterator2 i = first2, j = s; - for (; i != last2 && comp(*j, *i); ++i, ++j) ; - if (i == last2) - return s; - } - return last1; -} - -/// \brief Searches for the first occurence of \p count \p values in [first, last) -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -template -Iterator search_n (Iterator first, Iterator last, size_t count, const T& value, BinaryPredicate comp) -{ - size_t n = 0; - for (; first != last; ++first) { - if (!comp (*first, value)) - n = 0; - else if (++n == count) - return first - --n; - } - return last; -} - -/// \brief Searches [first1,last1) for the first occurrence of an element from [first2,last2) -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -template -InputIterator find_first_of (InputIterator first1, InputIterator last1, ForwardIterator first2, ForwardIterator last2, BinaryPredicate comp) -{ - for (; first1 != last1; ++first1) - for (ForwardIterator i = first2; i != last2; ++i) - if (comp (*first1, *i)) - return first1; - return first1; -} - -/// \brief Returns true if [first2,last2) is a subset of [first1,last1) -/// \ingroup ConditionAlgorithms -/// \ingroup SetAlgorithms -/// \ingroup PredicateAlgorithms -template -bool includes (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, StrictWeakOrdering comp) -{ - for (; (first1 != last1) & (first2 != last2); ++first1) { - if (comp (*first2, *first1)) - return false; - first2 += !comp (*first1, *first2); - } - return first2 == last2; -} - -/// \brief Merges [first1,last1) with [first2,last2) -/// -/// Result will contain every element that is in either set. If duplicate -/// elements are present, max(n,m) is placed in the result. -/// -/// \ingroup SetAlgorithms -/// \ingroup PredicateAlgorithms -template -OutputIterator set_union (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, StrictWeakOrdering comp) -{ - for (; (first1 != last1) & (first2 != last2); ++result) { - if (comp (*first2, *first1)) - *result = *first2++; - else { - first2 += !comp (*first1, *first2); - *result = *first1++; - } - } - return copy (first2, last2, copy (first1, last1, result)); -} - -/// \brief Creates a set containing elements shared by the given ranges. -/// \ingroup SetAlgorithms -/// \ingroup PredicateAlgorithms -template -OutputIterator set_intersection (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, StrictWeakOrdering comp) -{ - while ((first1 != last1) & (first2 != last2)) { - bool b1ge2 = !comp (*first1, *first2), b2ge1 = !comp (*first2, *first1); - if (b1ge2 & b2ge1) - *result++ = *first1; - first1 += b2ge1; - first2 += b1ge2; - } - return result; -} - -/// \brief Removes from [first1,last1) elements present in [first2,last2) -/// \ingroup SetAlgorithms -/// \ingroup PredicateAlgorithms -template -OutputIterator set_difference (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, StrictWeakOrdering comp) -{ - while ((first1 != last1) & (first2 != last2)) { - bool b1ge2 = !comp (*first1, *first2), b2ge1 = !comp (*first2, *first1); - if (!b1ge2) - *result++ = *first1; - first1 += b2ge1; - first2 += b1ge2; - } - return copy (first1, last1, result); -} - -/// \brief Performs union of sets A-B and B-A. -/// \ingroup SetAlgorithms -/// \ingroup PredicateAlgorithms -template -OutputIterator set_symmetric_difference (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, StrictWeakOrdering comp) -{ - while ((first1 != last1) & (first2 != last2)) { - bool b1l2 = comp (*first1, *first2), b2l1 = comp (*first2, *first1); - if (b1l2) - *result++ = *first1; - else if (b2l1) - *result++ = *first2; - first1 += !b2l1; - first2 += !b1l2; - } - return copy (first2, last2, copy (first1, last1, result)); -} - -/// \brief Returns true if the given range is sorted. -/// \ingroup ConditionAlgorithms -/// \ingroup PredicateAlgorithms -template -bool is_sorted (ForwardIterator first, ForwardIterator last, StrictWeakOrdering comp) -{ - for (ForwardIterator i = first; ++i < last; ++first) - if (comp (*i, *first)) - return false; - return true; -} - -/// \brief Compares two given containers like strcmp compares strings. -/// \ingroup ConditionAlgorithms -/// \ingroup PredicateAlgorithms -template -bool lexicographical_compare (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, BinaryPredicate comp) -{ - for (; (first1 != last1) & (first2 != last2); ++first1, ++first2) { - if (comp (*first1, *first2)) - return true; - if (comp (*first2, *first1)) - return false; - } - return (first1 == last1) & (first2 != last2); -} - -/// \brief Creates the next lexicographical permutation of [first,last). -/// Returns false if no further permutations can be created. -/// \ingroup GeneratorAlgorithms -/// \ingroup PredicateAlgorithms -template -bool next_permutation (BidirectionalIterator first, BidirectionalIterator last, StrictWeakOrdering comp) -{ - if (distance (first, last) < 2) - return false; - BidirectionalIterator i = last; - for (--i; i != first; ) { - --i; - if (comp (i[0], i[1])) { - BidirectionalIterator j = last; - while (!comp (*i, *--j)) ; - iter_swap (i, j); - reverse (i + 1, last); - return true; - } - } - reverse (first, last); - return false; -} - -/// \brief Creates the previous lexicographical permutation of [first,last). -/// Returns false if no further permutations can be created. -/// \ingroup GeneratorAlgorithms -/// \ingroup PredicateAlgorithms -template -bool prev_permutation (BidirectionalIterator first, BidirectionalIterator last, StrictWeakOrdering comp) -{ - if (distance (first, last) < 2) - return false; - BidirectionalIterator i = last; - for (--i; i != first; ) { - --i; - if (comp(i[1], i[0])) { - BidirectionalIterator j = last; - while (!comp (*--j, *i)) ; - iter_swap (i, j); - reverse (i + 1, last); - return true; - } - } - reverse (first, last); - return false; -} - -/// \brief Returns iterator to the max element in [first,last) -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -template -inline ForwardIterator max_element (ForwardIterator first, ForwardIterator last, BinaryPredicate comp) -{ - ForwardIterator result = first; - for (; first != last; ++first) - if (comp (*result, *first)) - result = first; - return result; -} - -/// \brief Returns iterator to the min element in [first,last) -/// \ingroup SearchingAlgorithms -/// \ingroup PredicateAlgorithms -template -inline ForwardIterator min_element (ForwardIterator first, ForwardIterator last, BinaryPredicate comp) -{ - ForwardIterator result = first; - for (; first != last; ++first) - if (comp (*first, *result)) - result = first; - return result; -} - -/// \brief Makes [first,middle) a part of the sorted array. -/// Contents of [middle,last) is undefined. This implementation just calls stable_sort. -/// \ingroup SortingAlgorithms -/// \ingroup PredicateAlgorithms -template -inline void partial_sort (RandomAccessIterator first, RandomAccessIterator, RandomAccessIterator last, StrictWeakOrdering comp) -{ - stable_sort (first, last, comp); -} - -/// \brief Like partial_sort, but outputs to [result_first,result_last) -/// \ingroup SortingAlgorithms -/// \ingroup PredicateAlgorithms -template -RandomAccessIterator partial_sort_copy (InputIterator first, InputIterator last, RandomAccessIterator result_first, RandomAccessIterator result_last, StrictWeakOrdering comp) -{ - RandomAccessIterator rend = result_first; - for (; first != last; ++first) { - RandomAccessIterator i = result_first; - for (; i != rend && comp (*i, *first); ++i) ; - if (i == result_last) - continue; - rend += (rend < result_last); - copy_backward (i, rend - 1, rend); - *i = *first; - } - return rend; -} - -/// \brief Like partition, but preserves equal element order. -/// \ingroup SortingAlgorithms -/// \ingroup PredicateAlgorithms -template -ForwardIterator stable_partition (ForwardIterator first, ForwardIterator last, Predicate pred) -{ - if (first == last) - return first; - ForwardIterator l, r, m = advance (first, distance (first, last) / 2); - if (first == m) - return pred(*first) ? last : first; - l = stable_partition (first, m, pred); - r = stable_partition (m, last, pred); - rotate (l, m, r); - return advance (l, distance (m, r)); -} - -/// \brief Splits [first,last) in two by \p pred. -/// -/// Creates two ranges [first,middle) and [middle,last), where every element -/// in the former is less than every element in the latter. -/// The return value is middle. -/// -/// \ingroup SortingAlgorithms -/// \ingroup PredicateAlgorithms -template -inline ForwardIterator partition (ForwardIterator first, ForwardIterator last, Predicate pred) -{ - return stable_partition (first, last, pred); -} - -} // namespace ustl diff --git a/common/include/ustl/uqueue.h b/common/include/ustl/uqueue.h deleted file mode 100644 index 655ced269..000000000 --- a/common/include/ustl/uqueue.h +++ /dev/null @@ -1,57 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "uvector.h" - -namespace ustl { - -/// \class queue uqueue.h ustl.h -/// \ingroup Sequences -/// -/// Queue adapter to uSTL containers. -/// -template > -class queue { -public: - typedef T value_type; - typedef Container container_type; - typedef typename container_type::size_type size_type; - typedef typename container_type::difference_type difference_type; - typedef value_type& reference; - typedef const value_type& const_reference; -public: - inline queue (void) : _storage(), _front (0) { } - explicit inline queue (const container_type& s) : _storage (s), _front (0) { } - explicit inline queue (const queue& s) : _storage (s._storage), _front (0) { } - inline size_type size (void) const { return _storage.size() - _front; } - inline bool empty (void) const { return !size(); } - inline reference front (void) { return _storage [_front]; } - inline const_reference front (void) const { return _storage [_front]; } - inline reference back (void) { return _storage.back(); } - inline const_reference back (void) const { return _storage.back(); } - inline void push (const_reference v) { _storage.push_back (v); } - void pop (void) { - if (++_front > _storage.size()/2) { - _storage.erase (_storage.begin(), _front); - _front = 0; - } - } - inline void swap (queue& v) { _storage.swap (v); swap (_front, v._front); } - inline bool operator== (const queue& s) const { return _storage == s._storage && _front == s._front; } - inline bool operator< (const queue& s) const { return size() < s.size(); } -#if HAVE_CPP11 - inline queue (queue&& v) : _storage(move(v._storage)),_front(v._front) { v._front = 0; } - inline queue (container_type&& s) : _storage(move(s)),_front(0) {} - inline queue& operator= (queue&& v) { swap (v); return *this; } - template - inline void emplace (Args&&... args) { _storage.emplace_back (forward(args)...); } -#endif -private: - container_type _storage; ///< Where the data actually is. - size_type _front; ///< Index of the element returned by next pop. -}; - -} // namespace ustl diff --git a/common/include/ustl/uset.h b/common/include/ustl/uset.h deleted file mode 100644 index 51220d4a9..000000000 --- a/common/include/ustl/uset.h +++ /dev/null @@ -1,112 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "uvector.h" - -namespace ustl { - -/// \class set uset.h ustl.h -/// \ingroup Sequences -/// -/// \brief Unique sorted container. Sorted vector with all values unique. -/// -template > -class set : public vector { -public: - typedef const set& rcself_t; - typedef vector base_class; - typedef typename base_class::value_type key_type; - typedef typename base_class::value_type data_type; - typedef typename base_class::value_type value_type; - typedef typename base_class::size_type size_type; - typedef typename base_class::pointer pointer; - typedef typename base_class::const_pointer const_pointer; - typedef typename base_class::reference reference; - typedef typename base_class::const_reference const_reference; - typedef typename base_class::const_iterator const_iterator; - typedef typename base_class::iterator iterator; - typedef typename base_class::reverse_iterator reverse_iterator; - typedef typename base_class::const_reverse_iterator const_reverse_iterator; - typedef pair insertrv_t; - typedef pair range_t; - typedef pair const_range_t; -public: - inline set (void) : base_class() { } - explicit inline set (size_type n) : base_class (n) { } - inline set (rcself_t v) : base_class (v) { } - inline set (const_iterator i1, const_iterator i2) : base_class() { insert (i1, i2); } - inline rcself_t operator= (rcself_t v) { base_class::operator= (v); return *this; } - inline size_type size (void) const { return base_class::size(); } - inline iterator begin (void) { return base_class::begin(); } - inline const_iterator begin (void) const { return base_class::begin(); } - inline const_iterator cbegin (void) const { return base_class::cbegin(); } - inline iterator end (void) { return base_class::end(); } - inline const_iterator end (void) const { return base_class::end(); } - inline const_iterator cend (void) const { return base_class::cend(); } - inline const_iterator find (const_reference v) const { const_iterator i = ::ustl::lower_bound (begin(), end(), v, Comp()); return (i != end() && *i == v) ? i : end(); } - inline iterator find (const_reference v) { return const_cast(const_cast(*this).find (v)); } - inline const_iterator lower_bound (const_reference v) const { return ::ustl::lower_bound (begin(), end(), v, Comp()); } - inline iterator lower_bound (const_reference v) { return const_cast(const_cast(*this).lower_bound (v)); } - inline const_iterator upper_bound (const_reference v) const { return ::ustl::upper_bound (begin(), end(), v, Comp()); } - inline iterator upper_bound (const_reference v) { return const_cast(const_cast(*this).upper_bound (v)); } - inline const_range_t equal_range (const_reference v) const { return ::ustl::equal_range (begin(), end(), v, Comp()); } - inline range_t equal_range (const_reference v) { return ::ustl::equal_range (begin(), end(), v, Comp()); } - inline size_type count (const_reference v) const { const_range_t r = equal_range(v); return distance(r.first,r.second); } - inline Comp value_comp (void) const { return Comp(); } - inline Comp key_comp (void) const { return value_comp(); } - inline void assign (const_iterator i1, const_iterator i2) { clear(); insert (i1, i2); } - inline void push_back (const_reference v) { insert (v); } - insertrv_t insert (const_reference v); - inline iterator insert (const_iterator, const_reference v) { return insert(v).first; } - inline void insert (const_iterator i1, const_iterator i2) { for (; i1 < i2; ++i1) insert (*i1); } - inline void erase (const_reference v) { iterator ip = find (v); if (ip != end()) erase (ip); } - inline iterator erase (iterator ep) { return base_class::erase (ep); } - inline iterator erase (iterator ep1, iterator ep2) { return base_class::erase (ep1, ep2); } - inline void clear (void) { base_class::clear(); } - inline void swap (set& v) { base_class::swap (v); } -#if HAVE_CPP11 - using initlist_t = std::initializer_list; - inline set (set&& v) : base_class (move(v)) {} - inline set (initlist_t v) : base_class() { insert (v.begin(), v.end()); } - inline set& operator= (set&& v) { base_class::operator= (move(v)); return *this; } - insertrv_t insert (T&& v); - inline iterator insert (const_iterator, T&& v) { return insert (move(v)); } - inline void insert (initlist_t v) { insert (v.begin(), v.end()); } - template - inline insertrv_t emplace (Args&&... args) { return insert (T(forward(args)...)); } - template - inline iterator emplace_hint (const_iterator h, Args&&... args) { return insert (h, T(forward(args)...)); } - template - inline insertrv_t emplace_back (Args&&... args) { return insert (T(forward(args)...)); } -#endif -}; - -#if HAVE_CPP11 - -template -typename set::insertrv_t set::insert (T&& v) -{ - iterator ip = lower_bound (v); - bool bInserted = (ip == end() || Comp()(v,*ip)); - if (bInserted) - ip = base_class::insert (ip, move(v)); - return make_pair (ip, bInserted); -} - -#endif - -/// Inserts \p v into the container, maintaining the sort order. -template -typename set::insertrv_t set::insert (const_reference v) -{ - iterator ip = lower_bound (v); - bool bInserted = (ip == end() || Comp()(v,*ip)); - if (bInserted) - ip = base_class::insert (ip, v); - return make_pair (ip, bInserted); -} - -} // namespace ustl diff --git a/common/include/ustl/uspecial.h b/common/include/ustl/uspecial.h deleted file mode 100644 index b24c5fe41..000000000 --- a/common/include/ustl/uspecial.h +++ /dev/null @@ -1,237 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "uvector.h" -#include "ustring.h" -#include "uset.h" -#include "umultiset.h" -#include "ubitset.h" -#include "ulaalgo.h" -#include "uarray.h" -#include "uctralgo.h" -#include "ufunction.h" -#include "uctrstrm.h" -#include "sistream.h" -#include "uchrono.h" - -namespace ustl { - -//---------------------------------------------------------------------- -// Alogrithm specializations not in use by the library code. -//---------------------------------------------------------------------- - -template <> inline void swap (cmemlink& a, cmemlink& b) { a.swap (b); } -template <> inline void swap (memlink& a, memlink& b) { a.swap (b); } -template <> inline void swap (memblock& a, memblock& b) { a.swap (b); } -template <> inline void swap (string& a, string& b) { a.swap (b); } -#define TEMPLATE_SWAP_PSPEC(type, template_decl) \ -template_decl inline void swap (type& a, type& b) { a.swap (b); } -TEMPLATE_SWAP_PSPEC (TEMPLATE_TYPE1 (vector,T), TEMPLATE_DECL1 (T)) -TEMPLATE_SWAP_PSPEC (TEMPLATE_TYPE1 (set,T), TEMPLATE_DECL1 (T)) -TEMPLATE_SWAP_PSPEC (TEMPLATE_TYPE1 (multiset,T), TEMPLATE_DECL1 (T)) -TEMPLATE_SWAP_PSPEC (TEMPLATE_TYPE2 (tuple,N,T), TEMPLATE_FULL_DECL2 (size_t,N,typename,T)) - -//---------------------------------------------------------------------- -// Streamable definitions. Not used in the library and require streams. -//---------------------------------------------------------------------- - -//----{ pair }---------------------------------------------------------- - -/// \brief Reads pair \p p from stream \p is. -template -void pair::read (istream& is) -{ - is >> first; - is.align (stream_align_of(second)); - is >> second; - is.align (stream_align_of(first)); -} - -/// Writes pair \p p to stream \p os. -template -void pair::write (ostream& os) const -{ - os << first; - os.align (stream_align_of(second)); - os << second; - os.align (stream_align_of(first)); -} - -/// Returns the written size of the object. -template -size_t pair::stream_size (void) const -{ - return Align (stream_size_of(first), stream_align_of(second)) + - Align (stream_size_of(second), stream_align_of(first)); -} - -/// Writes pair \p p to stream \p os. -template -void pair::text_write (ostringstream& os) const -{ - os << '(' << first << ',' << second << ')'; -} - -/// \brief Takes a pair and returns pair.first -/// This is an extension, available in uSTL and the SGI STL. -template struct select1st : public unary_function { - typedef typename Pair::first_type result_type; - inline const result_type& operator()(const Pair& a) const { return a.first; } - inline result_type& operator()(Pair& a) const { return a.first; } -}; - -/// \brief Takes a pair and returns pair.second -/// This is an extension, available in uSTL and the SGI STL. -template struct select2nd : public unary_function { - typedef typename Pair::second_type result_type; - inline const result_type& operator()(const Pair& a) const { return a.second; } - inline result_type& operator()(Pair& a) const { return a.second; } -}; - -/// \brief Converts a const_iterator pair into an iterator pair -/// Useful for converting pair ranges returned by equal_range, for instance. -/// This is an extension, available in uSTL. -template -inline pair -unconst (const pair& i, Container&) -{ - typedef pair unconst_pair_t; - return *noalias_cast(&i); -} - -//----{ vector }-------------------------------------------------------- - -template -inline size_t stream_align_of (const vector&) -{ - typedef typename vector::written_size_type written_size_type; - return stream_align_of (written_size_type()); -} - -//----{ bitset }-------------------------------------------------------- - -/// Writes bitset \p v into stream \p os. -template -void bitset::text_read (istringstream& is) -{ - char c; - for (int i = Size; --i >= 0 && (is >> c).good();) - set (i, c == '1'); -} - -//----{ tuple }--------------------------------------------------------- - -template -struct numeric_limits > { - typedef numeric_limits value_limits; - static inline tuple min (void) { tuple v; fill (v, value_limits::min()); return v; } - static inline tuple max (void) { tuple v; fill (v, value_limits::max()); return v; } - static const bool is_signed = value_limits::is_signed; - static const bool is_integer = value_limits::is_integer; - static const bool is_integral = value_limits::is_integral; -}; - -template -inline size_t stream_align_of (const tuple&) { return stream_align_of (NullValue()); } - -template -inline ostringstream& chartype_text_write (ostringstream& os, const T& v) -{ - os.format (_FmtPrtChr[!isprint(v)], v); - return os; -} - -template <> -inline ostringstream& container_element_text_write (ostringstream& os, const uint8_t& v) -{ return chartype_text_write (os, v); } -template <> -inline ostringstream& container_element_text_write (ostringstream& os, const int8_t& v) -{ return chartype_text_write (os, v); } - -//----{ matrix }-------------------------------------------------------- - -/// Writes matrix \p v into stream \p os. -template -void matrix::text_write (ostringstream& os) const -{ - os << '('; - for (uoff_t iRow = 0; iRow < NY; ++ iRow) { - os << '('; - for (uoff_t iColumn = 0; iColumn < NX; ++iColumn) - os << at(iRow)[iColumn] << ",)"[iColumn == NX-1]; - } - os << ')'; -} - -//----{ long4grain }---------------------------------------------------- - -#if SIZE_OF_LONG == 8 && HAVE_INT64_T -// Helper class for long4grain and ptr4grain wrappers. -class _long4grain { -public: - inline _long4grain (uint64_t v) : _v (v) {} -#if __x86_64__ - inline void read (istream& is) - { - assert (is.aligned(4)); - #if WANT_STREAM_BOUNDS_CHECKING - if (!is.verify_remaining ("read", "long4grain", sizeof(_v))) return; - #else - assert (is.remaining() >= sizeof(_v)); - #endif - _v = *reinterpret_cast(is.ipos()); - is.skip (sizeof(_v)); - } - inline void write (ostream& os) const - { - assert (os.aligned(4)); - #if WANT_STREAM_BOUNDS_CHECKING - if (!os.verify_remaining ("write", "long4grain", sizeof(_v))) return; - #else - assert (os.remaining() >= sizeof(_v)); - #endif - *reinterpret_cast(os.ipos()) = _v; - os.skip (sizeof(_v)); - } -#elif USTL_BYTE_ORDER == USTL_BIG_ENDIAN - inline void read (istream& is) { uint32_t vl, vh; is >> vh >> vl; _v = (uint64_t(vh) << 32) | uint64_t(vl); } - inline void write (ostream& os) const { os << uint32_t(_v >> 32) << uint32_t(_v); } -#else - inline void read (istream& is) { uint32_t vl, vh; is >> vl >> vh; _v = (uint64_t(vh) << 32) | uint64_t(vl); } - inline void write (ostream& os) const { os << uint32_t(_v) << uint32_t(_v >> 32); } -#endif - inline size_t stream_size (void) const { return stream_size_of(_v); } -private: - uint64_t _v; -}; - -/// Wrap long values to allow writing them on 4-grain even on 64bit platforms. -inline _long4grain& long4grain (unsigned long& v) { asm("":"+m"(v)); return *noalias_cast<_long4grain*>(&v); } -/// Wrap long values to allow writing them on 4-grain even on 64bit platforms. -inline const _long4grain long4grain (const unsigned long& v) { return _long4grain(v); } -/// Wrap pointer values to allow writing them on 4-grain even on 64bit platforms. -template -inline _long4grain& ptr4grain (T*& p) { asm("":"+m"(p)); return *noalias_cast<_long4grain*>(&p); } -/// Wrap pointer values to allow writing them on 4-grain even on 64bit platforms. -template -inline const _long4grain ptr4grain (const T* const& p) { return _long4grain(uintptr_t(p)); } -#else // if not SIZE_OF_LONG == 8 && HAVE_INT64_T -inline unsigned long& long4grain (unsigned long& v) { return v; } -inline const unsigned long& long4grain (const unsigned long& v) { return v; } -template inline T*& ptr4grain (T*& p) { return p; } -template inline const T* const& ptr4grain (const T* const& p) { return p; } -#endif // SIZE_OF_LONG == 8 - -//---------------------------------------------------------------------- - -} // namespace ustl - -ALIGNOF (_long4grain, 4) -ALIGNOF(ustl::CBacktrace, sizeof(void*)) -ALIGNOF (ustl::string, stream_align_of (string::value_type())) -// bool is a big type on some machines (like DEC Alpha), so it's written as a byte. -ALIGNOF(bool, sizeof(uint8_t)) -CAST_STREAMABLE(bool, uint8_t) diff --git a/common/include/ustl/ustack.h b/common/include/ustl/ustack.h deleted file mode 100644 index 059984231..000000000 --- a/common/include/ustl/ustack.h +++ /dev/null @@ -1,48 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once - -namespace ustl { - -/// \class stack ustack.h ustl.h -/// \ingroup Sequences -/// -/// Stack adapter to uSTL containers. -/// -template > -class stack { -public: - typedef T value_type; - typedef Container container_type; - typedef typename container_type::size_type size_type; - typedef typename container_type::difference_type difference_type; - typedef value_type& reference; - typedef const value_type& const_reference; -public: - inline stack (void) : _storage () { } - explicit inline stack (const container_type& s) : _storage (s) { } - explicit inline stack (const stack& s) : _storage (s._storage) { } - inline bool empty (void) const { return _storage.empty(); } - inline size_type size (void) const { return _storage.size(); } - inline reference top (void) { return _storage.back(); } - inline const_reference top (void) const { return _storage.back(); } - inline void push (const_reference v) { _storage.push_back (v); } - inline void pop (void) { _storage.pop_back(); } - inline void swap (stack& v) { _storage.swap (v); } - inline bool operator== (const stack& s) const { return _storage == s._storage; } - inline bool operator< (const stack& s) const { return _storage.size() < s._storage.size(); } -#if HAVE_CPP11 - inline stack (stack&& v) : _storage(move(v._storage)) {} - inline stack (container_type&& s) : _storage(move(s)) {} - inline stack& operator= (stack&& v) { swap (v); return *this; } - template - inline void emplace (Args&&... args) { _storage.emplace_back (forward(args)...); } -#endif -private: - container_type _storage; ///< Where the data actually is. -}; - -} // namespace ustl diff --git a/common/include/ustl/ustl.h b/common/include/ustl/ustl.h deleted file mode 100644 index bec591bb6..000000000 --- a/common/include/ustl/ustl.h +++ /dev/null @@ -1,182 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "ustl/uspecial.h" -#include "ustl/umap.h" -#include "ustl/umultimap.h" -#include "ustl/ustack.h" -#include "ustl/uqueue.h" -#include "ustl/unumeric.h" -#include "ustl/ulist.h" -#include "ustl/uheap.h" - -/// \mainpage -/// -/// \section intro Introduction -/// -/// uSTL is a partial implementation of the STL specification intended to -/// reduce code size of the derivative programs. Usually, the STL containers -/// manage their own storage with new[] and delete[] operators, which create -/// strongly typed storage. That is the standard way of allocating C++ object -/// vectors, allowing appropriate constructors and destructors to be called on -/// the allocated storage and ensuring that objects are copied via their copy -/// operators. Although type safety is a good thing, placing memory management -/// code into a template necessitates its reinstantiation for every template -/// instance used by the derivative program. This produces substantial code -/// bloat, that is frequently derided by C developers and used by them as -/// an argument that C is better than C++. The uSTL implementation solves -/// this problem by factoring memory management code into a non-template base -/// class, ustl::memblock, which performs unstructured memory allocation. STL -/// containers are then implemented as template wrappers for memblock to -/// provide a measure of type safety. The result is that each template -/// instantiation contains less code, and although it does not completely -/// "disappear", due to the requirement for calling placement constructors -/// on the allocated memory, most of it does, being replaced by calls to -/// memblock methods. The base classes for unstructured storage management -/// (cmemlink - link to constant memory, memlink - link to mutable memory, -/// and memblock - owner of mutable memory) are, of course, also available -/// for use as data buffers wherever those are needed, and streams that -/// efficiently read and write binary data into them are also available. -// -/// \defgroup Containers Containers -/// Here you'll find all the containers for your objects and data. -// -/// \defgroup MemoryManagement Memory Management -/// \ingroup Containers -/// Classes that implement low-level memory management and form the base for -/// all containers in the library. Almost all functionality in the containers -/// is reduced to calls to these base classes through a great deal of inline -/// crunching by the compiler, and thus you end up storing all your data in -/// ustl::memblock objects with the container templates as mere syntactic sugar. -// -/// \defgroup Sequences Sequence Containers -/// \ingroup Containers -/// Containers containing sequences of objects. -// -/// \defgroup AssociativeContainers Associative Containers -/// \ingroup Containers -/// Containers containing associations of objects. -// -/// \defgroup Streams Streams -/// Streams convert objects into flat data. -// -/// \defgroup BinaryStreams Binary Streams -/// \ingroup Streams -/// Unlike the C++ standard library, -/// the default behaviour is very strongly biased toward binary streams. I -/// believe that text formats should be used very sparingly due to numerous -/// problems they cause, such as total lack of structure, buffer overflows, -/// the great multitude of formats and encodings for even the most -/// trivial of things like integers, and the utter lack of readability -/// despite ardent claims to the contrary. Binary formats are well-structured, -/// are simpler to define exhaustively, are aggregates of basic types which -/// are universal to all architectures (with the exception of two types of -/// byte ordering, which I hope to be an issue that will go away soon), and -/// are much more readable (through an appropriate formatting tool equipped -/// to read binary format specifications). -// -/// \defgroup BinaryStreamIterators Binary Stream Iterators -/// \ingroup BinaryStreams -/// \ingroup Iterators -/// Iterators for using STL algorithms with binary streams. -// -/// \defgroup TextStreams TextStreams -/// \ingroup Streams -/// Streams converting objects into streams of text. -// -/// \defgroup DeviceStreams Device Streams -/// \ingroup Streams -/// Standard cout, cerr, and cin implementations for reading -/// and writing text through standard file descriptors. -// -/// \defgroup Iterators Iterators -/// Generalizations of the pointer concept, allowing algorithms to treat -/// all containers in a unified fashion. -// -/// \defgroup IteratorAdaptors Iterator Adaptors -/// \ingroup Iterators -/// Iterators made out of other iterators. -// -/// \defgroup Algorithms Algorithms -/// STL algorithms are the heart of generic programming. The idea is to -/// separate algorithms from containers to take advantage of the fact that -/// there are fewer distinct algorithms than typed containers. This is -/// diametrically opposed to object oriented programming, where each object -/// must contain all functionality related to its internal data. You will -/// find, I think, that in practice, generic programming is not terribly -/// convenient because it prevents you from encapsulating all your data. -/// The best approach is to compromise and have raw data classes that will -/// be manipulated by algorithms and to treat the rest of the objects as -/// stateful data transformers. -// -/// \defgroup MutatingAlgorithms Mutating Algorithms -/// \ingroup Algorithms -/// Algorithms for modifying your data in some way. -// -/// \defgroup SortingAlgorithms Sorting Algorithms -/// \ingroup MutatingAlgorithms -/// Algorithms for sorting containers. -// -/// \defgroup GeneratorAlgorithms Generator Algorithms -/// \ingroup MutatingAlgorithms -/// Algorithms for generating data. -// -/// \defgroup NumericAlgorithms Numeric Algorithms -/// \ingroup MutatingAlgorithms -/// Algorithms generalizing mathematical operations. -// -/// \defgroup SetAlgorithms Set Algorithms -/// \ingroup MutatingAlgorithms -/// Algorithms for working with sorted sets. -// -/// \defgroup HeapAlgorithms Heap Algorithms -/// \ingroup MutatingAlgorithms -/// Algorithms for generating and manipulating heaps. -// -/// \defgroup SwapAlgorithms Swap Algorithms -/// \ingroup MutatingAlgorithms -/// Algorithms for swapping elements. -// -/// \defgroup RawStorageAlgorithms Raw Storage Algorithms -/// \ingroup MutatingAlgorithms -/// Algorithms for manipulating unstructured memory. -// -/// \defgroup ConditionAlgorithms Condition Algorithms -/// \ingroup Algorithms -/// Algorithms for obtaining information about data. -// -/// \defgroup SearchingAlgorithms Searching Algorithms -/// \ingroup ConditionAlgorithms -/// Algorithms for searching through containers. -// -/// \defgroup PredicateAlgorithms Predicate Algorithms -/// \ingroup Algorithms -/// Algorithms that take a functor object. Avoid these if you can, -/// and carefully check the generated assembly if you can't. These -/// algorithms can and will generate prodigious amounts of bloat -/// if you are not very very careful about writing your functors. -// -/// \defgroup Functors Functors -/// Functors are inteded to be passed as arguments to \link PredicateAlgorithms -/// predicate algorithms\endlink. Ivory tower academics make much of this capability, -/// no doubt happy that C++ can now be made to look just like their precious lisp. -/// In practice, however, functors and predicate algorithms are mostly useless. -/// An iterative solution using \ref foreach is usually far simpler to write -/// and to maintain. Furthermore, functional programming in C++ often -/// generates much bloat and slowness, which is difficult to avoid with any -/// but the most primitive functors. Try them if you wish, now and then, but -/// compare with an iterative solution to see if the compiler really can see -/// through all your functional trickery. -// -/// \defgroup FunctorObjects Functor Objects -/// \ingroup Functors -/// Objects that wrap other functors to provide new functionality. -// -/// \defgroup FunctorAccessors Functor Object Accessors -/// \ingroup Functors -/// Because C++ is so very unsuited to functional programming, trying -/// to do so may require a lot of typing. These accessor functions -/// are somewhat helpful in making functional constructs more readable. diff --git a/common/include/ustl/ustring.h b/common/include/ustl/ustring.h deleted file mode 100644 index 5dbff94ec..000000000 --- a/common/include/ustl/ustring.h +++ /dev/null @@ -1,371 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "memblock.h" -#include "utf8.h" -#include - -namespace ustl { - -/// \class string ustring.h ustl.h -/// \ingroup Sequences -/// -/// \brief STL basic_string<char> equivalent. -/// -/// An STL container for text string manipulation. -/// Differences from C++ standard: -/// - string is a class, not a template. Wide characters are assumed to be -/// encoded with utf8 at all times except when rendering or editing, -/// where you would use a utf8 iterator. -/// - format member function - you can, of course use an \ref ostringstream, -/// which also have format functions, but most of the time this way -/// is more convenient. Because uSTL does not implement locales, -/// format is the only way to create localized strings. -/// - const char* cast operator. It is much clearer to use this than having -/// to type .c_str() every time. -/// - length returns the number of _characters_, not bytes. -/// This function is O(N), so use wisely. -/// -/// An additional note is in order regarding the use of indexes. All indexes -/// passed in as arguments or returned by find are byte offsets, not character -/// offsets. Likewise, sizes are specified in bytes, not characters. The -/// rationale is that there is no way for you to know what is in the string. -/// There is no way for you to know how many characters are needed to express -/// one thing or another. The only thing you can do to a localized string is -/// search for delimiters and modify text between them as opaque blocks. If you -/// do anything else, you are hardcoding yourself into a locale! So stop it! -/// -class string : public memblock { -public: - typedef char value_type; - typedef unsigned char uvalue_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef wchar_t wvalue_type; - typedef wvalue_type* wpointer; - typedef const wvalue_type* const_wpointer; - typedef pointer iterator; - typedef const_pointer const_iterator; - typedef value_type& reference; - typedef value_type const_reference; - typedef ::ustl::reverse_iterator reverse_iterator; - typedef ::ustl::reverse_iterator const_reverse_iterator; - typedef utf8in_iterator utf8_iterator; - typedef size_type pos_type; - static constexpr const pos_type npos = INT_MAX; ///< Value that means the end of string. -public: - inline string (void) noexcept : memblock () { relink ("",0); } - string (const string& s); - inline string (const string& s, pos_type o, size_type n = npos); - inline explicit string (const cmemlink& l); - string (const_pointer s) noexcept; - inline string (const_pointer s, size_type len); - inline string (const_pointer s1, const_pointer s2); - string (size_type n, value_type c); - inline ~string (void) noexcept { } - inline pointer data (void) { return string::pointer (memblock::data()); } - inline const_pointer data (void) const { return string::const_pointer (memblock::data()); } - inline const_pointer c_str (void) const { return string::const_pointer (memblock::cdata()); } - inline size_type max_size (void) const { size_type s (memblock::max_size()); return s - !!s; } - inline size_type capacity (void) const { size_type c (memblock::capacity()); return c - !!c; } - void resize (size_type n); - inline void resize (size_type n, value_type c); - inline void clear (void) { resize (0); } - inline iterator begin (void) { return iterator (memblock::begin()); } - inline const_iterator begin (void) const { return const_iterator (memblock::begin()); } - inline const_iterator cbegin (void) const { return begin(); } - inline iterator end (void) { return iterator (memblock::end()); } - inline const_iterator end (void) const { return const_iterator (memblock::end()); } - inline const_iterator cend (void) const { return end(); } - inline reverse_iterator rbegin (void) { return reverse_iterator (end()); } - inline const_reverse_iterator rbegin (void) const { return const_reverse_iterator (end()); } - inline const_reverse_iterator crbegin (void) const { return rbegin(); } - inline reverse_iterator rend (void) { return reverse_iterator (begin()); } - inline const_reverse_iterator rend (void) const { return const_reverse_iterator (begin()); } - inline const_reverse_iterator crend (void) const { return rend(); } - inline utf8_iterator utf8_begin (void) const { return utf8_iterator (begin()); } - inline utf8_iterator utf8_end (void) const { return utf8_iterator (end()); } - inline const_reference at (pos_type pos) const { assert (pos <= size() && begin()); return begin()[pos]; } - inline reference at (pos_type pos) { assert (pos <= size() && begin()); return begin()[pos]; } - inline const_iterator iat (pos_type pos) const { return begin() + (__builtin_constant_p(pos) && pos >= npos ? size() : min(size_type(pos),size())); } - inline iterator iat (pos_type pos) { return const_cast(const_cast(this)->iat(pos)); } - const_iterator wiat (pos_type i) const noexcept; - inline iterator wiat (pos_type i) { return const_cast(const_cast(this)->wiat(i)); } - inline const_reference front (void) const { return at(0); } - inline reference front (void) { return at(0); } - inline const_reference back (void) const { return at(size()-1); } - inline reference back (void) { return at(size()-1); } - inline size_type length (void) const { return distance (utf8_begin(), utf8_end()); } - inline string& append (const_iterator i1, const_iterator i2) { return append (i1, distance (i1, i2)); } - string& append (const_pointer s, size_type len); - string& append (const_pointer s); - string& append (size_type n, value_type c); - inline string& append (size_type n, wvalue_type c) { insert (size(), n, c); return *this; } - inline string& append (const_wpointer s1, const_wpointer s2) { insert (size(), s1, s2); return *this; } - inline string& append (const_wpointer s) { const_wpointer se (s); for (;se&&*se;++se) {} return append (s, se); } - inline string& append (const string& s) { return append (s.begin(), s.end()); } - inline string& append (const string& s,pos_type o,size_type n) { return append (s.iat(o), s.iat(o+n)); } - inline void push_back (value_type c) { resize(size()+1); end()[-1] = c; } - inline void push_back (wvalue_type c) { append (1, c); } - inline void pop_back (void) { resize (size()-1); } - inline string& assign (const_iterator i1, const_iterator i2) { return assign (i1, distance (i1, i2)); } - string& assign (const_pointer s, size_type len); - string& assign (const_pointer s); - inline string& assign (const_wpointer s1, const_wpointer s2) { clear(); return append (s1, s2); } - inline string& assign (const_wpointer s1) { clear(); return append (s1); } - inline string& assign (const string& s) { return assign (s.begin(), s.end()); } - inline string& assign (const string& s, pos_type o, size_type n) { return assign (s.iat(o), s.iat(o+n)); } - inline string& assign (size_type n, value_type c) { clear(); return append (n, c); } - inline string& assign (size_type n, wvalue_type c) { clear(); return append (n, c); } - size_type copy (pointer p, size_type n, pos_type pos = 0) const noexcept; - inline size_type copyto (pointer p, size_type n, pos_type pos = 0) const noexcept { size_type bc = copy(p,n-1,pos); p[bc]=0; return bc; } - inline int compare (const string& s) const { return compare (begin(), end(), s.begin(), s.end()); } - inline int compare (pos_type start, size_type len, const string& s) const { return compare (iat(start), iat(start+len), s.begin(), s.end()); } - inline int compare (pos_type s1, size_type l1, const string& s, pos_type s2, size_type l2) const { return compare (iat(s1), iat(s1+l1), s.iat(s2), s.iat(s2+l2)); } - inline int compare (const_pointer s) const { return compare (begin(), end(), s, s + strlen(s)); } - inline int compare (pos_type s1, size_type l1, const_pointer s, size_type l2) const { return compare (iat(s1), iat(s1+l1), s, s+l2); } - inline int compare (pos_type s1, size_type l1, const_pointer s) const { return compare (s1, l1, s, strlen(s)); } - static int compare (const_iterator first1, const_iterator last1, const_iterator first2, const_iterator last2) noexcept; - inline operator const value_type* (void) const; - inline operator value_type* (void); - inline const string& operator= (const string& s) { return assign (s.begin(), s.end()); } - inline const string& operator= (const_reference c) { return assign (&c, 1); } - inline const string& operator= (const_pointer s) { return assign (s); } - inline const string& operator= (const_wpointer s) { return assign (s); } - inline const string& operator+= (const string& s) { return append (s.begin(), s.size()); } - inline const string& operator+= (value_type c) { push_back(c); return *this; } - inline const string& operator+= (const_pointer s) { return append (s); } - inline const string& operator+= (wvalue_type c) { return append (1, c); } - inline const string& operator+= (uvalue_type c) { return operator+= (value_type(c)); } - inline const string& operator+= (const_wpointer s) { return append (s); } - inline string operator+ (const string& s) const; - inline bool operator== (const string& s) const { return memblock::operator== (s); } - bool operator== (const_pointer s) const noexcept; - inline bool operator== (value_type c) const { return size() == 1 && c == at(0); } - inline bool operator== (uvalue_type c) const { return operator== (value_type(c)); } - inline bool operator!= (const string& s) const { return !operator== (s); } - inline bool operator!= (const_pointer s) const { return !operator== (s); } - inline bool operator!= (value_type c) const { return !operator== (c); } - inline bool operator!= (uvalue_type c) const { return !operator== (c); } - inline bool operator< (const string& s) const { return 0 > compare (s); } - inline bool operator< (const_pointer s) const { return 0 > compare (s); } - inline bool operator< (value_type c) const { return 0 > compare (begin(), end(), &c, &c + 1); } - inline bool operator< (uvalue_type c) const { return operator< (value_type(c)); } - inline bool operator> (const_pointer s) const { return 0 < compare (s); } - inline string& insert (pos_type ip, size_type n, value_type c) { insert (iat(ip), n, c); return *this; } - inline string& insert (pos_type ip, const_pointer s) { insert (iat(ip), s, s + strlen(s)); return *this; } - inline string& insert (pos_type ip, const_pointer s, size_type nlen) { insert (iat(ip), s, s + nlen); return *this; } - inline string& insert (pos_type ip, const string& s) { insert (iat(ip), s.c_str(), s.size()); return *this; } - inline string& insert (pos_type ip, const string& s, size_type sp, size_type slen) { insert (iat(ip), s.iat(sp), s.iat(sp + slen)); return *this; } - string& insert (pos_type ip, size_type n, wvalue_type c); - string& insert (pos_type ip, const_wpointer first, const_wpointer last, size_type n = 1); - inline string& insert (int ip, size_type n, value_type c) { insert (pos_type(ip), n, c); return *this; } - inline string& insert (int ip, const_pointer s, size_type nlen) { insert (pos_type(ip), s, nlen); return *this; } - iterator insert (const_iterator start, size_type n, value_type c); - inline iterator insert (const_iterator start, value_type c) { return insert (start, 1u, c); } - iterator insert (const_iterator start, const_pointer s, size_type n); - iterator insert (const_iterator start, const_pointer first, const_iterator last, size_type n = 1); - iterator erase (const_iterator epo, size_type n = 1); - string& erase (pos_type epo = 0, size_type n = npos); - inline string& erase (int epo, size_type n = npos) { return erase (pos_type(epo), n); } - inline iterator erase (const_iterator first, const_iterator last) { return erase (first, size_type(distance(first,last))); } - inline iterator eraser (pos_type first, pos_type last) { return erase (iat(first), iat(last)); } - string& replace (const_iterator first, const_iterator last, const_pointer i1, const_pointer i2, size_type n); - template - string& replace (const_iterator first, const_iterator last, InputIt first2, InputIt last2) { return replace (first, last, first2, last2, 1); } - inline string& replace (const_iterator first, const_iterator last, const string& s) { return replace (first, last, s.begin(), s.end()); } - string& replace (const_iterator first, const_iterator last, const_pointer s); - inline string& replace (const_iterator first, const_iterator last, const_pointer s, size_type slen) { return replace (first, last, s, s + slen); } - inline string& replace (const_iterator first, const_iterator last, size_type n, value_type c) { return replace (first, last, &c, &c + 1, n); } - inline string& replace (pos_type rp, size_type n, const string& s) { return replace (iat(rp), iat(rp + n), s); } - inline string& replace (pos_type rp, size_type n, const string& s, uoff_t sp, size_type slen) { return replace (iat(rp), iat(rp + n), s.iat(sp), s.iat(sp + slen)); } - inline string& replace (pos_type rp, size_type n, const_pointer s, size_type slen) { return replace (iat(rp), iat(rp + n), s, s + slen); } - inline string& replace (pos_type rp, size_type n, const_pointer s) { return replace (iat(rp), iat(rp + n), string(s)); } - inline string& replace (pos_type rp, size_type n, size_type count, value_type c) { return replace (iat(rp), iat(rp + n), count, c); } - inline string substr (pos_type o = 0, size_type n = npos) const { return string (*this, o, n); } - inline void swap (string& v) { memblock::swap (v); } - pos_type find (value_type c, pos_type pos = 0) const noexcept; - pos_type find (const string& s, pos_type pos = 0) const noexcept; - inline pos_type find (uvalue_type c, pos_type pos = 0) const noexcept { return find (value_type(c), pos); } - inline pos_type find (const_pointer p, pos_type pos, size_type count) const { string sp; sp.link (p,count); return find (sp, pos); } - pos_type rfind (value_type c, pos_type pos = npos) const noexcept; - pos_type rfind (const string& s, pos_type pos = npos) const noexcept; - inline pos_type rfind (uvalue_type c, pos_type pos = npos) const noexcept { return rfind (value_type(c), pos); } - inline pos_type rfind (const_pointer p, pos_type pos, size_type count) const { string sp; sp.link (p,count); return rfind (sp, pos); } - pos_type find_first_of (const string& s, pos_type pos = 0) const noexcept; - inline pos_type find_first_of (value_type c, pos_type pos = 0) const { string sp (1, c); return find_first_of(sp,pos); } - inline pos_type find_first_of (uvalue_type c, pos_type pos = 0) const { return find_first_of (value_type(c), pos); } - inline pos_type find_first_of (const_pointer p, pos_type pos, size_type count) const { string sp; sp.link (p,count); return find_first_of (sp, pos); } - pos_type find_first_not_of (const string& s, pos_type pos = 0) const noexcept; - inline pos_type find_first_not_of (value_type c, pos_type pos = 0) const { string sp (1, c); return find_first_not_of(sp,pos); } - inline pos_type find_first_not_of (uvalue_type c, pos_type pos = 0) const { return find_first_not_of (value_type(c), pos); } - inline pos_type find_first_not_of (const_pointer p, pos_type pos, size_type count) const { string sp; sp.link (p,count); return find_first_not_of (sp, pos); } - pos_type find_last_of (const string& s, pos_type pos = npos) const noexcept; - inline pos_type find_last_of (value_type c, pos_type pos = npos) const { string sp (1, c); return find_last_of(sp,pos); } - inline pos_type find_last_of (uvalue_type c, pos_type pos = npos) const { return find_last_of (value_type(c), pos); } - inline pos_type find_last_of (const_pointer p, pos_type pos, size_type count) const { string sp; sp.link (p,count); return find_last_of (sp, pos); } - pos_type find_last_not_of (const string& s, pos_type pos = npos) const noexcept; - inline pos_type find_last_not_of (value_type c, pos_type pos = npos) const { string sp (1, c); return find_last_not_of(sp,pos); } - inline pos_type find_last_not_of (uvalue_type c, pos_type pos = npos) const { return find_last_not_of (value_type(c), pos); } - inline pos_type find_last_not_of (const_pointer p, pos_type pos, size_type count) const { string sp; sp.link (p,count); return find_last_not_of (sp, pos); } - int vformat (const char* fmt, va_list args); - int format (const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))); - void read (istream&); - void write (ostream& os) const; - size_t stream_size (void) const noexcept; - static hashvalue_t hash (const char* f1, const char* l1) noexcept; -#if HAVE_CPP11 - using initlist_t = std::initializer_list; - inline string (string&& v) : memblock (move(v)) {} - inline string (initlist_t v) : memblock() { assign (v.begin(), v.size()); } - inline string& assign (string&& v) { swap (v); return *this; } - inline string& assign (initlist_t v) { return assign (v.begin(), v.size()); } - inline string& append (initlist_t v) { return append (v.begin(), v.size()); } - inline string& operator+= (initlist_t v) { return append (v.begin(), v.size()); } - inline string& operator= (string&& v) { return assign (move(v)); } - inline string& operator= (initlist_t v) { return assign (v.begin(), v.size()); } - inline iterator insert (const_iterator ip, initlist_t v) { return insert (ip, v.begin(), v.end()); } - inline string& replace (const_iterator first, const_iterator last, initlist_t v) { return replace (first, last, v.begin(), v.end()); } -#endif -private: - virtual size_type minimumFreeCapacity (void) const noexcept final override __attribute__((const)); -}; - -//---------------------------------------------------------------------- - -/// Assigns itself the value of string \p s -inline string::string (const cmemlink& s) -: memblock () -{ - assign (const_iterator (s.begin()), s.size()); -} - -/// Assigns itself a [o,o+n) substring of \p s. -inline string::string (const string& s, pos_type o, size_type n) -: memblock() -{ - assign (s, o, n); -} - -/// Copies the value of \p s of length \p len into itself. -inline string::string (const_pointer s, size_type len) -: memblock () -{ - assign (s, len); -} - -/// Copies into itself the string data between \p s1 and \p s2 -inline string::string (const_pointer s1, const_pointer s2) -: memblock () -{ - assert (s1 <= s2 && "Negative ranges result in memory allocation errors."); - assign (s1, s2); -} - -/// Returns the pointer to the first character. -inline string::operator const string::value_type* (void) const -{ - assert ((!end() || !*end()) && "This string is linked to data that is not 0-terminated. This may cause serious security problems. Please assign the data instead of linking."); - return begin(); -} - -/// Returns the pointer to the first character. -inline string::operator string::value_type* (void) -{ - assert ((end() && !*end()) && "This string is linked to data that is not 0-terminated. This may cause serious security problems. Please assign the data instead of linking."); - return begin(); -} - -/// Concatenates itself with \p s -inline string string::operator+ (const string& s) const -{ - string result (*this); - result += s; - return result; -} - -/// Resize to \p n and fill new entries with \p c -inline void string::resize (size_type n, value_type c) -{ - const size_type oldn = size(); - resize (n); - fill_n (iat(oldn), max(ssize_t(n-oldn),0), c); -} - -//---------------------------------------------------------------------- -// Operators needed to avoid comparing pointer to pointer - -#define PTR_STRING_CMP(op, impl) \ -inline bool op (const char* s1, const string& s2) { return impl; } -PTR_STRING_CMP (operator==, (s2 == s1)) -PTR_STRING_CMP (operator!=, (s2 != s1)) -PTR_STRING_CMP (operator<, (s2 > s1)) -PTR_STRING_CMP (operator<=, (s2 >= s1)) -PTR_STRING_CMP (operator>, (s2 < s1)) -PTR_STRING_CMP (operator>=, (s2 <= s1)) -#undef PTR_STRING_CMP - -inline string operator+ (const char* cs, const string& ss) { string r; r.reserve (strlen(cs)+ss.size()); r += cs; r += ss; return r; } - -//---------------------------------------------------------------------- - -inline hashvalue_t hash_value (const char* first, const char* last) -{ return string::hash (first, last); } -inline hashvalue_t hash_value (const char* v) -{ return hash_value (v, v + strlen(v)); } - -//---------------------------------------------------------------------- -// String-number conversions - -#define STRING_TO_INT_CONVERTER(name,type,func) \ -inline type name (const string& str, size_t* idx = nullptr, int base = 10) \ -{ \ - const char* sp = str.c_str(); \ - char* endp = nullptr; \ - type r = func (sp, idx ? &endp : nullptr, base);\ - if (idx) \ - *idx = endp - sp; \ - return r; \ -} -/*STRING_TO_INT_CONVERTER(stoi,int,strtol) -STRING_TO_INT_CONVERTER(stol,long,strtol) -STRING_TO_INT_CONVERTER(stoul,unsigned long,strtoul) -#if HAVE_LONG_LONG -STRING_TO_INT_CONVERTER(stoll,long long,strtoll) -STRING_TO_INT_CONVERTER(stoull,unsigned long long,strtoull) -#endif*/ -#undef STRING_TO_INT_CONVERTER - -#define STRING_TO_FLOAT_CONVERTER(name,type,func) \ -inline type name (const string& str, size_t* idx = nullptr) \ -{ \ - const char* sp = str.c_str(); \ - char* endp = nullptr; \ - type r = func (sp, idx ? &endp : nullptr);\ - if (idx) \ - *idx = endp - sp; \ - return r; \ -} -/*STRING_TO_FLOAT_CONVERTER(stof,float,strtof) -STRING_TO_FLOAT_CONVERTER(stod,double,strtod) -STRING_TO_FLOAT_CONVERTER(stold,long double,strtold)*/ -#undef STRING_TO_FLOAT_CONVERTER - -#define NUMBER_TO_STRING_CONVERTER(type,fmts)\ - inline string to_string (type v) { string r; r.format(fmts,v); return r; } -NUMBER_TO_STRING_CONVERTER(int,"%d") -NUMBER_TO_STRING_CONVERTER(long,"%ld") -NUMBER_TO_STRING_CONVERTER(unsigned long,"%lu") -#if HAVE_LONG_LONG -NUMBER_TO_STRING_CONVERTER(long long,"%lld") -NUMBER_TO_STRING_CONVERTER(unsigned long long,"%llu") -#endif -/*NUMBER_TO_STRING_CONVERTER(float,"%f") -NUMBER_TO_STRING_CONVERTER(double,"%lf") -NUMBER_TO_STRING_CONVERTER(long double,"%Lf")*/ -#undef NUMBER_TO_STRING_CONVERTER - -} // namespace ustl diff --git a/common/include/ustl/utf8.h b/common/include/ustl/utf8.h deleted file mode 100644 index 6f9316e29..000000000 --- a/common/include/ustl/utf8.h +++ /dev/null @@ -1,213 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. -// -// This file contains stream iterators that read and write UTF-8 encoded -// characters. The encoding is defined as follows: -// -// U-00000000 - U-0000007F: 0xxxxxxx -// U-00000080 - U-000007FF: 110xxxxx 10xxxxxx -// U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx -// U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx -// U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -// U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -// U-80000000 - U-FFFFFFFF: 11111110 100000xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - -#pragma once -#include "uiterator.h" - -namespace ustl { - -//---------------------------------------------------------------------- - -typedef uint8_t utf8subchar_t; ///< Type for the encoding subcharacters. - -//---------------------------------------------------------------------- - -inline size_t Utf8Bytes (wchar_t v) __attribute__((const)); -inline size_t Utf8Bytes (const wchar_t* first, const wchar_t* last) __attribute__((pure)); -inline size_t Utf8SequenceBytes (wchar_t c) __attribute__((const)); - -//---------------------------------------------------------------------- - -/// Returns the number of bytes required to UTF-8 encode \p v. -inline size_t Utf8Bytes (wchar_t v) -{ - if (uint32_t(v) < 128) - return 1; - size_t n; - #if __x86__ - uint32_t r = 0; - asm ("bsr\t%2, %%eax\n\t" - "add\t$4, %0\n\t" - "div\t%3":"=a"(n),"+d"(r):"r"(v),"c"(5)); - #else - static const uint32_t c_Bounds[7] = { 0x0000007F, 0x000007FF, 0x0000FFFF, 0x001FFFFF, 0x03FFFFFF, 0x7FFFFFFF, 0xFFFFFFFF }; - for (n = 0; c_Bounds[n++] < uint32_t(v);); - #endif - return n; -} - -/// Measures the size of a wchar_t array in UTF-8 encoding. -inline size_t Utf8Bytes (const wchar_t* first, const wchar_t* last) -{ - size_t bc = 0; - for (; first < last; ++first) - bc += Utf8Bytes(*first); - return bc; -} - -/// Returns the number of bytes in a UTF-8 sequence that starts with \p c. -inline size_t Utf8SequenceBytes (wchar_t c) // a wchar_t to keep c in a full register -{ - // Count the leading bits. Header bits are 1 * nBytes followed by a 0. - // 0 - single byte character. Take 7 bits (0xFF >> 1) - // 1 - error, in the middle of the character. Take 6 bits (0xFF >> 2) - // so you will keep reading invalid entries until you hit the next character. - // >2 - multibyte character. Take remaining bits, and get the next bytes. - // All errors are ignored, since the user can not correct them. - // - wchar_t mask = 0x80; - size_t nBytes = 0; - for (; c & mask; ++nBytes) - mask >>= 1; - return nBytes ? nBytes : 1; // A sequence is always at least 1 byte. -} - -//---------------------------------------------------------------------- - -/// \class utf8in_iterator utf8.h ustl.h -/// \ingroup IteratorAdaptors -/// -/// \brief An iterator adaptor to character containers for reading UTF-8 encoded text. -/// -/// For example, you can copy from ustl::string to ustl::vector with -/// copy (utf8in (str.begin()), utf8in (str.end()), back_inserter(wvect)); -/// There is no error handling; if the reading frame slips you'll get extra -/// characters, one for every misaligned byte. Although it is possible to skip -/// to the start of the next character, that would result in omitting the -/// misformatted character and the one after it, making it very difficult to -/// detect by the user. It is better to write some strange characters and let -/// the user know his file is corrupted. Another problem is overflow on bad -/// encodings (like a 0xFF on the end of a string). This is checked through -/// the end-of-string nul character, which will always be there as long as -/// you are using the string class. -/// -template -class utf8in_iterator { -public: - typedef typename iterator_traits::value_type value_type; - typedef typename iterator_traits::difference_type difference_type; - typedef typename iterator_traits::pointer pointer; - typedef typename iterator_traits::reference reference; - typedef input_iterator_tag iterator_category; -public: - explicit utf8in_iterator (const Iterator& is) : _i (is), _v (0) { Read(); } - utf8in_iterator (const utf8in_iterator& i) : _i (i._i), _v (i._v) {} - inline const utf8in_iterator& operator= (const utf8in_iterator& i) { _i = i._i; _v = i._v; return *this; } - inline Iterator base (void) const { return _i - (Utf8Bytes(_v) - 1); } - /// Reads and returns the next value. - inline WChar operator* (void) const { return _v; } - inline utf8in_iterator& operator++ (void) { ++_i; Read(); return *this; } - inline utf8in_iterator operator++ (int) { utf8in_iterator old (*this); operator++(); return old; } - inline utf8in_iterator& operator+= (uoff_t n) { while (n--) operator++(); return *this; } - inline utf8in_iterator operator+ (uoff_t n) { utf8in_iterator v (*this); return v += n; } - inline bool operator== (const utf8in_iterator& i) const { return _i == i._i; } - inline bool operator< (const utf8in_iterator& i) const { return _i < i._i; } - difference_type operator- (const utf8in_iterator& i) const; -private: - void Read (void); -private: - Iterator _i; - WChar _v; -}; - -/// Steps to the next character and updates current returnable value. -template -void utf8in_iterator::Read (void) -{ - const utf8subchar_t c = *_i; - size_t nBytes = Utf8SequenceBytes (c); - _v = c & (0xFF >> nBytes); // First byte contains bits after the header. - while (--nBytes && *++_i) // Each subsequent byte has 6 bits. - _v = (_v << 6) | (*_i & 0x3F); -} - -/// Returns the distance in characters (as opposed to the distance in bytes). -template -typename utf8in_iterator::difference_type -utf8in_iterator::operator- (const utf8in_iterator& last) const -{ - difference_type dist = 0; - for (Iterator first (last._i); first < _i; ++dist) - first = advance (first, Utf8SequenceBytes (*first)); - return dist; -} - -//---------------------------------------------------------------------- - -/// \class utf8out_iterator utf8.h ustl.h -/// \ingroup IteratorAdaptors -/// -/// \brief An iterator adaptor to character containers for writing UTF-8 encoded text. -/// -template -class utf8out_iterator { -public: - typedef typename iterator_traits::value_type value_type; - typedef typename iterator_traits::difference_type difference_type; - typedef typename iterator_traits::pointer pointer; - typedef typename iterator_traits::reference reference; - typedef output_iterator_tag iterator_category; -public: - explicit utf8out_iterator (const Iterator& os) : _i (os) {} - utf8out_iterator (const utf8out_iterator& i) : _i (i._i) {} - inline const Iterator& base (void) const { return _i; } - /// Writes \p v into the stream. - utf8out_iterator& operator= (WChar v); - inline utf8out_iterator& operator* (void) { return *this; } - inline utf8out_iterator& operator++ (void) { return *this; } - inline utf8out_iterator& operator++ (int) { return *this; } - inline bool operator== (const utf8out_iterator& i) const { return _i == i._i; } - inline bool operator< (const utf8out_iterator& i) const { return _i < i._i; } -private: - Iterator _i; -}; - -/// Writes \p v into the stream. -template -utf8out_iterator& utf8out_iterator::operator= (WChar v) -{ - const size_t nBytes = Utf8Bytes (v); - if (nBytes > 1) { - // Write the bits 6 bits at a time, except for the first one, - // which may be less than 6 bits. - wchar_t shift = nBytes * 6; - *_i++ = ((v >> (shift -= 6)) & 0x3F) | (0xFF << (8 - nBytes)); - while (shift) - *_i++ = ((v >> (shift -= 6)) & 0x3F) | 0x80; - } else // If only one byte, there is no header. - *_i++ = v; - return *this; -} - -//---------------------------------------------------------------------- - -/// Returns a UTF-8 adaptor writing to \p i. Useful in conjuction with back_insert_iterator. -template -inline utf8out_iterator utf8out (Iterator i) -{ - return utf8out_iterator (i); -} - -/// Returns a UTF-8 adaptor reading from \p i. -template -inline utf8in_iterator utf8in (Iterator i) -{ - return utf8in_iterator (i); -} - -//---------------------------------------------------------------------- - -} // namespace ustl diff --git a/common/include/ustl/uttraits.h b/common/include/ustl/uttraits.h deleted file mode 100644 index e0e251a9b..000000000 --- a/common/include/ustl/uttraits.h +++ /dev/null @@ -1,506 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "utypes.h" - -#if HAVE_CPP11 -namespace ustl { - -//{{{ Helper templates and specs --------------------------------------- - -/// true or false templatized constant for metaprogramming -template -struct integral_constant { - using value_type = T; - using type = integral_constant; - static constexpr const T value = v; - constexpr operator value_type() const { return value; } - constexpr T operator()() const { return value; } -}; -template constexpr const T integral_constant::value; - -using true_type = integral_constant; -using false_type = integral_constant; - -/// Selects type = flag ? T : U -template struct conditional { using type = T; }; -template struct conditional { using type = U; }; -template using conditional_t = typename conditional::type; - -/// Selects bool = flag ? T : U -template struct t_if : public integral_constant {}; -template struct t_if : public integral_constant {}; - -template struct enable_if { }; -template struct enable_if { using type = T; }; -template using enable_if_t = typename enable_if::type; - -#define UNARY_TRAIT_DEFB(name,condition) \ -template struct name : public integral_constant {} -#define UNARY_TRAIT_DEFN(name) \ -template struct name : public false_type {} -#define UNARY_TRAIT_TRUE(name,type) \ -template <> struct name : public true_type {} - -//}}}------------------------------------------------------------------- -//{{{ Type modifications - -template struct remove_const { using type = T; }; -template struct remove_const { using type = T; }; -template using remove_const_t = typename remove_const::type; - -template struct remove_volatile { using type = T; }; -template struct remove_volatile{ using type = T; }; -template using remove_volatile_t = typename remove_volatile::type; - -template struct remove_cv { using type = remove_volatile_t>; }; -template using remove_cv_t = typename remove_cv::type; - -template struct add_const { using type = T const; }; -template struct add_const { using type = T const; }; -template using add_const_t = typename add_const::type; - -template constexpr add_const_t& as_const (T& v) { return v; } -template void as_const(const T&&) = delete; - -template struct add_volatile { using type = T volatile; }; -template struct add_volatile { using type = T volatile; }; -template using add_volatile_t = typename add_volatile::type; - -template struct add_cv { using type = add_volatile_t>; }; -template using add_cv_t = typename add_cv::type; - -template struct remove_reference { using type = T; }; -template struct remove_reference { using type = T; }; -template struct remove_reference { using type = T; }; -template using remove_reference_t = typename remove_reference::type; - -template struct remove_pointer { using type = T; }; -template struct remove_pointer { using type = T; }; -template using remove_pointer_t = typename remove_pointer::type; - -template struct add_pointer { using type = T*; }; -template struct add_pointer { using type = T*; }; -template using add_pointer_t = typename add_pointer::type; - -template struct remove_extent { using type = T; }; -template struct remove_extent { using type = T; }; -template struct remove_extent { using type = T; }; -template using remove_extent_t = typename remove_extent::type; - -template struct remove_all_extents { using type = T; }; -template struct remove_all_extents { using type = typename remove_all_extents::type; }; -template struct remove_all_extents { using type = typename remove_all_extents::type; }; -template using remove_all_extents_t = typename remove_all_extents::type; - -template struct underlying_type { using type = __underlying_type(T); }; -template using underlying_type_t = typename underlying_type::type; - -#if HAVE_CPP14 -template using void_t = void; -#endif - -template struct make_signed { using type = T; }; -template <> struct make_signed { using type = signed char; }; -template <> struct make_signed { using type = signed char; }; -template <> struct make_signed { using type = signed short; }; -template <> struct make_signed { using type = signed int; }; -template <> struct make_signed { using type = signed long; }; -#if HAVE_LONG_LONG -template <> struct make_signed { using type = signed long long; }; -#endif -template using make_signed_t = typename make_signed::type; - -template struct make_unsigned { using type = T; }; -template <> struct make_unsigned { using type = unsigned char; }; -template <> struct make_unsigned { using type = unsigned char; }; -template <> struct make_unsigned { using type = unsigned short; }; -template <> struct make_unsigned { using type = unsigned int; }; -template <> struct make_unsigned { using type = unsigned long; }; -#if HAVE_LONG_LONG -template <> struct make_unsigned { using type = unsigned long long; }; -#endif -template using make_unsigned_t = typename make_unsigned::type; - -//}}}------------------------------------------------------------------- -//{{{ Primary type categories - -#if __clang__ // clang already has these __is_ helpers as builtins - -UNARY_TRAIT_DEFB (is_void, __is_void(remove_cv_t)); -UNARY_TRAIT_DEFB (is_integral, __is_integral(remove_cv_t)); -UNARY_TRAIT_DEFB (is_signed, __is_signed(remove_cv_t)); -UNARY_TRAIT_DEFB (is_floating_point, __is_floating_point(remove_cv_t)); -UNARY_TRAIT_DEFB (is_pointer, __is_pointer(remove_cv_t)); -UNARY_TRAIT_DEFB (is_member_pointer, __is_member_pointer(remove_cv_t)); -UNARY_TRAIT_DEFB (is_member_function_pointer, __is_member_function_pointer(remove_cv_t)); - -#else - -UNARY_TRAIT_DEFN (__is_void); -UNARY_TRAIT_TRUE (__is_void, void); -UNARY_TRAIT_DEFB (is_void, __is_void>::value); - -UNARY_TRAIT_DEFN (__is_integral); -UNARY_TRAIT_TRUE (__is_integral, char); -#if HAVE_THREE_CHAR_TYPES -UNARY_TRAIT_TRUE (__is_integral, signed char); -#endif -UNARY_TRAIT_TRUE (__is_integral, short); -UNARY_TRAIT_TRUE (__is_integral, int); -UNARY_TRAIT_TRUE (__is_integral, long); -UNARY_TRAIT_TRUE (__is_integral, unsigned char); -UNARY_TRAIT_TRUE (__is_integral, unsigned short); -UNARY_TRAIT_TRUE (__is_integral, unsigned int); -UNARY_TRAIT_TRUE (__is_integral, unsigned long); -#if HAVE_LONG_LONG -UNARY_TRAIT_TRUE (__is_integral, long long); -UNARY_TRAIT_TRUE (__is_integral, unsigned long long); -#endif -UNARY_TRAIT_TRUE (__is_integral, wchar_t); -UNARY_TRAIT_TRUE (__is_integral, bool); -UNARY_TRAIT_DEFB (is_integral, __is_integral>::value); - -UNARY_TRAIT_DEFN (__is_signed); -UNARY_TRAIT_TRUE (__is_signed, char); -UNARY_TRAIT_TRUE (__is_signed, wchar_t); -UNARY_TRAIT_TRUE (__is_signed, short); -UNARY_TRAIT_TRUE (__is_signed, int); -UNARY_TRAIT_TRUE (__is_signed, long); -UNARY_TRAIT_TRUE (__is_signed, long long); -UNARY_TRAIT_DEFB (is_signed, __is_signed>::value); - -UNARY_TRAIT_DEFN (__is_floating_point); -//UNARY_TRAIT_TRUE (__is_floating_point, float); -//UNARY_TRAIT_TRUE (__is_floating_point, double); -//UNARY_TRAIT_TRUE (__is_floating_point, long double); -UNARY_TRAIT_DEFB (is_floating_point, __is_floating_point>::value); - -template struct __is_pointer : public false_type {}; -template struct __is_pointer : public true_type {}; -template struct is_pointer : public __is_pointer> {}; - -UNARY_TRAIT_DEFN (__is_member_pointer); -template struct __is_member_pointer : public true_type {}; -UNARY_TRAIT_DEFB (is_member_pointer, __is_member_pointer>::value); - -UNARY_TRAIT_DEFN (__is_member_function_pointer); -template struct __is_member_function_pointer : public true_type {}; -template struct __is_member_function_pointer : public true_type {}; -template -struct __is_member_function_pointer : public true_type {}; -template -struct __is_member_function_pointer : public true_type {}; -UNARY_TRAIT_DEFB (is_member_function_pointer, __is_member_function_pointer>::value); - -#endif // __clang__ - -UNARY_TRAIT_DEFB (is_unsigned, !is_signed::value); -UNARY_TRAIT_DEFB (is_member_object_pointer, is_member_pointer::value && !is_member_function_pointer::value); - -UNARY_TRAIT_DEFN (is_array); -UNARY_TRAIT_DEFN (is_lvalue_reference); -template struct is_lvalue_reference : public true_type {}; -UNARY_TRAIT_DEFN (is_rvalue_reference); -template struct is_rvalue_reference : public true_type {}; - -UNARY_TRAIT_DEFB (is_reference, is_lvalue_reference::value || is_rvalue_reference::value); - -template struct is_array : public true_type {}; -template struct is_array : public true_type {}; - -UNARY_TRAIT_DEFB (is_union, __is_union(T)); -UNARY_TRAIT_DEFB (is_class, __is_class(T)); -UNARY_TRAIT_DEFB (is_enum, __is_enum(T)); - -UNARY_TRAIT_DEFN (is_function); -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; -template struct is_function : public true_type { }; - -UNARY_TRAIT_DEFB (is_object, !is_reference::value && !is_void::value && !is_function::value); -UNARY_TRAIT_DEFB (__is_referenceable, is_reference::value || is_object::value); -template -struct __is_referenceable : public true_type {}; -template -struct __is_referenceable : public true_type {}; - -//}}}------------------------------------------------------------------- -//{{{ Composite type categories - -UNARY_TRAIT_DEFB (is_arithmetic, is_integral::value || is_floating_point::value); -UNARY_TRAIT_DEFB (is_fundamental, is_arithmetic::value || is_void::value); -UNARY_TRAIT_DEFB (is_scalar, is_arithmetic::value || is_enum::value || is_pointer::value || is_member_pointer::value); -UNARY_TRAIT_DEFB (is_compound, !is_fundamental::value); - -template ::value> struct __add_lvalue_reference { using type = T; }; -template struct __add_lvalue_reference { using type = T&; }; -template struct add_lvalue_reference : public __add_lvalue_reference {}; -template using add_lvalue_reference_t = typename add_lvalue_reference::type; - -template ::value> struct __add_rvalue_reference { using type = T; }; -template struct __add_rvalue_reference { using type = T&&; }; -template struct add_rvalue_reference : public __add_rvalue_reference {}; -template using add_rvalue_reference_t = typename add_rvalue_reference::type; - -/// Unusable default value for T. Use with decltype. -template add_rvalue_reference_t declval (void) noexcept; - -//}}}------------------------------------------------------------------- -//{{{ Type properties - -UNARY_TRAIT_DEFN (is_const); -template struct is_const : public true_type {}; -UNARY_TRAIT_DEFN (is_volatile); -template struct is_volatile : public true_type {}; - -UNARY_TRAIT_DEFB (is_empty, __is_empty(T)); -UNARY_TRAIT_DEFB (is_abstract, __is_abstract(T)); -UNARY_TRAIT_DEFB (is_literal_type, __is_literal_type(T)); -UNARY_TRAIT_DEFB (is_polymorphic, __is_polymorphic(T)); -#if HAVE_CPP14 -UNARY_TRAIT_DEFB (is_final, __is_final(T)); -#endif -UNARY_TRAIT_DEFB (is_standard_layout, __is_standard_layout(T)); -UNARY_TRAIT_DEFB (is_pod, __is_pod(T) || is_scalar::value || (is_array::value && is_scalar>::value)); -UNARY_TRAIT_DEFB (has_unique_object_representations, is_pod::value); -UNARY_TRAIT_DEFB (is_trivial, is_pod::value || __is_trivial(T)); -UNARY_TRAIT_DEFB (is_swappable, is_trivial::value); -UNARY_TRAIT_DEFB (is_nothrow_swappable, is_trivial::value); -UNARY_TRAIT_DEFB (has_trivial_copy, is_pod::value || __has_trivial_copy(T)); -UNARY_TRAIT_DEFB (has_trivial_assign, is_pod::value || __has_trivial_assign(T)); -UNARY_TRAIT_DEFB (has_trivial_constructor, is_pod::value || __has_trivial_constructor(T)); -UNARY_TRAIT_DEFB (has_trivial_destructor, is_pod::value || __has_trivial_destructor(T)); -UNARY_TRAIT_DEFB (has_virtual_destructor, __has_virtual_destructor(T)); -UNARY_TRAIT_DEFB (has_nothrow_assign, __has_nothrow_assign(T)); -UNARY_TRAIT_DEFB (has_nothrow_copy, __has_nothrow_copy(T)); -UNARY_TRAIT_DEFB (has_nothrow_constructor, __has_nothrow_constructor(T)); - -template struct alignment_of : public integral_constant {}; - -template struct rank : public integral_constant {}; -template struct rank : public integral_constant::value> {}; -template struct rank : public integral_constant::value> {}; - -template struct extent { static constexpr const size_t value = 0; }; -template struct extent { static constexpr const size_t value = I ? extent::value : 0; }; -template struct extent { static constexpr const size_t value = I ? extent::value : N; }; - -template ::value, bool IsFunction = is_function::value> struct __decay; -template struct __decay { using type = remove_cv_t; }; -template struct __decay { using type = remove_extent_t*; }; -template struct __decay { using type = add_pointer_t; }; -template struct decay : public __decay> {}; -template using decay_t = typename decay::type; - -template struct common_type; -template struct common_type { using type = decay_t; }; -template using common_type_t = typename common_type::type; -template struct common_type - { using type = decay_t() : declval())>; }; -template -struct common_type - { using type = common_type_t, V...>; }; - -//}}}------------------------------------------------------------------- -//{{{ Constructability and destructability - -// All these use the standard SFINAE technique -struct __is_destructible { - template ().~T())> static true_type test (int); - template static false_type test (...); -}; -template struct is_destructible : public decltype(__is_destructible::test(0)) {}; - -struct __is_nothrow_destructible { - template static integral_constant().~T())> test (int); - template static false_type test (...); -}; -template -struct is_nothrow_destructible : public decltype(__is_nothrow_destructible::test(0)) {}; - -struct __is_default_constructible { - template static true_type test (int); - template static false_type test (...); -}; -template struct is_default_constructible : public decltype(__is_default_constructible::test(0)) {}; - -struct __is_nothrow_default_constructible { - template static integral_constant test (int); - template static false_type test (...); -}; -template struct is_nothrow_default_constructible : public decltype(__is_nothrow_default_constructible::test(0)) {}; - -template struct is_constructible : public is_default_constructible {}; -template struct is_nothrow_constructible : public is_nothrow_default_constructible {}; - -struct __is_copy_constructible { - template ()))> static true_type test (int); - template static false_type test (...); -}; -template struct is_copy_constructible : public decltype(__is_copy_constructible::test(0)) {}; - -struct __is_move_constructible { - template ()))> static true_type test (int); - template static false_type test (...); -}; -template struct is_move_constructible : public decltype(__is_move_constructible::test(0)) {}; - -#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 9) || __GNUC__ >= 8 -template struct is_assignable : public integral_constant {}; -#else -struct __is_assignable { - template () = declval())> static true_type test (int); - template static false_type test (...); -}; -template struct is_assignable : public decltype(__is_assignable::test(0)) {}; -#endif - -template struct is_copy_assignable : public is_assignable {}; -template struct is_move_assignable : public is_assignable {}; - -// TODO: later -template struct is_nothrow_copy_constructible : public false_type {}; -template struct is_nothrow_move_constructible : public false_type {}; -template struct is_nothrow_assignable : public false_type {}; -template struct is_nothrow_copy_assignable : public false_type {}; -template struct is_nothrow_move_assignable : public false_type {}; - -#if __GNUC__ >= 5 -UNARY_TRAIT_DEFB (is_trivially_copyable, __is_trivially_copyable(T)); -template -struct is_trivially_constructible : public integral_constant {}; - -template -struct is_trivially_copy_constructible : public - integral_constant::value - && __is_trivially_constructible(T, const T&)> {}; - -template -struct is_trivially_move_constructible : public - integral_constant::value - && __is_trivially_constructible(T, T&&)> {}; - -template -struct is_trivially_assignable : public integral_constant {}; - -UNARY_TRAIT_DEFB (is_trivially_default_constructible, __is_trivially_constructible(T)); -UNARY_TRAIT_DEFB (is_trivially_copy_assignable, __is_trivially_assignable(T,const T&)); -UNARY_TRAIT_DEFB (is_trivially_move_assignable, __is_trivially_assignable(T,T&&)); -#else -UNARY_TRAIT_DEFB (is_trivially_copyable, __has_trivial_copy(T)); -UNARY_TRAIT_DEFB (is_trivially_default_constructible, __has_trivial_constructor(T)); -UNARY_TRAIT_DEFB (is_trivially_copy_assignable, __has_trivial_assign(T)); -UNARY_TRAIT_DEFB (is_trivially_move_assignable, false); -#endif - -UNARY_TRAIT_DEFB (is_trivially_destructible, __has_trivial_destructor(T)); -UNARY_TRAIT_DEFB (has_trivial_copy_constructor, __has_trivial_copy(T)); -UNARY_TRAIT_DEFB (has_trivial_copy_assign, __has_trivial_assign(T)); - -//}}}------------------------------------------------------------------- -//{{{ Type relations - -template struct is_same : public false_type {}; -template struct is_same : public true_type {}; - -#if __clang__ // clang has __is_convertible builtin - -template -struct is_convertible : public integral_constant {}; - -#else - -template ::value || is_function::value || is_array::value> -class __is_convertible : public integral_constant::value> {}; -template -class __is_convertible { - template static void __test_aux(TT); - template (declval()))> - static true_type __test(int); - template static false_type __test(...); -public: - using type = decltype(__test(0)); -}; -template -struct is_convertible : public __is_convertible::type {}; - -#endif - -template struct is_swappable_with - : public integral_constant::value && is_convertible::value> {}; -template struct is_nothrow_swappable_with - : public integral_constant::value && is_convertible::value> {}; - -/// Defines a has_member_function_name template where has_member_function_name::value is true when O::name exists -/// Example: HAS_MEMBER_FUNCTION(read, void (O::*)(istream&)); has_member_function_read>::value == true -#define HAS_MEMBER_FUNCTION(name, signature) \ -template \ -class __has_member_function_##name { \ - template struct test_for_##name {};\ - template static true_type found (test_for_##name*);\ - template static false_type found (...);\ -public: \ - using type = decltype(found(nullptr)); \ -}; \ -template \ -struct has_member_function_##name : public __has_member_function_##name::type {} - -/// Defines a has_static_member_variable template where has_static_member_variable_name::value is true when O::name exists -/// Example: HAS_STATIC_MEMBER_VARIABLE(int, _val); has_static_member_variable__val::value == true -#define HAS_STATIC_MEMBER_VARIABLE(varT,name) \ -template \ -class __has_static_member_variable_##name { \ - template > V> struct test_for_##name {};\ - template static true_type found (test_for_##name*);\ - template static false_type found (...);\ -public: \ - using type = decltype(found(nullptr)); \ -}; \ -template \ -struct has_static_member_variable_##name : public __has_static_member_variable_##name::type {} - -template struct is_base_of { - static constexpr const bool value = __is_base_of(T,U); -}; -template struct is_base_of : public false_type {}; -template struct is_base_of : public false_type {}; - -template struct aligned_storage - { struct type { alignas(Grain) unsigned char _data[Size]; }; }; - -//}}}------------------------------------------------------------------- -//{{{ Helper templates and specs -#undef UNARY_TRAIT_DEFN -#undef UNARY_TRAIT_DEFB -#define POD_CLASS(T) namespace ustl { UNARY_TRAIT_TRUE(is_pod,T); } -//}}} - -} // namespace ustl -#endif // HAVE_CPP11 diff --git a/common/include/ustl/utypes.h b/common/include/ustl/utypes.h deleted file mode 100644 index 018329f93..000000000 --- a/common/include/ustl/utypes.h +++ /dev/null @@ -1,134 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "config.h" - -/* -#define __STDC_LIMIT_MACROS // For WCHAR_MIN and WCHAR_MAX in stdint. -#define __STDC_CONSTANT_MACROS // For UINT??_C macros to avoid using L and UL suffixes on constants. -#include "config.h" -#if HAVE_STDINT_H - #include -#elif HAVE_INTTYPES_H - #include -#else - #error "Need standard integer types definitions, usually in stdint.h" -#endif -#if HAVE_SYS_TYPES_H - #include -#endif -#include // For ptrdiff_t, size_t -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if HAVE_ALLOCA_H - #include -#endif -#ifndef WITHOUT_LIBSTDCPP - #include - #include - #include - #if HAVE_CPP11 - #include - #endif -#endif -*/ -#ifndef SIZE_MAX - #define SIZE_MAX UINT_MAX -#endif -#if sun || __sun // Solaris defines UINTPTR_MAX as empty. - #undef UINTPTR_MAX - #define UINTPTR_MAX ULONG_MAX -#endif -#ifndef WCHAR_MAX - #ifdef __WCHAR_MAX__ - #define WCHAR_MAX __WCHAR_MAX__ - #else - #define WCHAR_MAX CHAR_MAX - #endif -#endif -#if HAVE_LONG_LONG - #ifndef LLONG_MAX - #define ULLONG_MAX UINT64_C(0xFFFFFFFFFFFFFFFF) - #define LLONG_MAX INT64_C(0x7FFFFFFFFFFFFFFF) - #define LLONG_MIN ULLONG_MAX - #endif -#endif -#ifndef BYTE_ORDER - #define LITTLE_ENDIAN USTL_LITTLE_ENDIAN - #define BIG_ENDIAN USTL_BIG_ENDIAN - #define BYTE_ORDER USTL_BYTE_ORDER -#endif - -//sweb added -#ifndef NULL -#define NULL 0 -#endif - -#ifndef CHAR_MIN -#define CHAR_MIN 0 -#endif - -#ifndef CHAR_MAX -#define CHAR_MAX 0xFF -#endif - -#ifndef UCHAR_MAX -#define UCHAR_MAX 0xFF -#endif - -#define CHAR_BIT 8 -#define DBL_MAX 0 -#define DBL_MIN 0 -#define FLT_MAX 0 -#define FLT_MIN 0 -#define INT_MAX 0xFFFFFFFF -#define INT_MIN 0 -#define LONG_MAX 0 -#define LONG_MIN 0 -#define UINTPTR_MAX 0xFFFFFFFF -#define SHRT_MAX 0 -#define SHRT_MIN 0 -#define LDBL_MAX 0 -#define LDBL_MIN 0 -#define UINT_MAX 0xFFFFFFFF -#define INT_MIN 0 -#define ULONG_MAX 0xFFFFFFFF -#define USHRT_MAX 0xFFFF - -#include "types.h" - -typedef uint64 uint64_t; -typedef int64 int64_t; -typedef uint32 uint32_t; -typedef int32 int32_t; -typedef uint16 uint16_t; -typedef int16 int16_t; -typedef uint8 uint8_t; -typedef int8 int8_t; -typedef ssize_t ptrdiff_t; -typedef ssize_t off_t; - -typedef size_t uoff_t; ///< A type for storing offsets into blocks measured by size_t. -typedef uint32_t hashvalue_t; ///< Value type returned by the hash functions. -typedef size_t streamsize; ///< Size of stream data -typedef uoff_t streamoff; ///< Offset into a stream - -typedef size_t uintptr_t; -typedef ssize_t intptr_t; -/* -#if !defined(UINTPTR_MAX) || !defined(UINT32_C) - #error "If you include stdint.h before ustl.h, define __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS first" -#endif*/ -#if WANT_ALWAYS_INLINE - #define inline INLINE inline -#endif diff --git a/common/include/ustl/uutility.h b/common/include/ustl/uutility.h deleted file mode 100644 index 3adf7dbd8..000000000 --- a/common/include/ustl/uutility.h +++ /dev/null @@ -1,449 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. -// -/// \file uutility.h -/// \brief Utility templates. - -#pragma once -#include "utypes.h" -#include "ulimits.h" -#include "assert.h" - -namespace ustl { - -#if HAVE_CPP11 - using nullptr_t = decltype(nullptr); - using max_align_t = uintptr_t; -#endif - -#if __GNUC__ - /// Returns the number of elements in a static vector - #define VectorSize(v) (sizeof(v) / sizeof(*v)) -#else - // Old compilers will not be able to evaluate *v on an empty vector. - // The tradeoff here is that VectorSize will not be able to measure arrays of local structs. - #define VectorSize(v) (sizeof(v) / ustl::size_of_elements(1, v)) -#endif - -/// Returns the end() for a static vector -template inline constexpr T* VectorEnd (T(&a)[N]) { return &a[N]; } - -/// Expands into a ptr,size expression for the given static vector; useful as link arguments. -#define VectorBlock(v) &(v)[0], VectorSize(v) -/// Expands into a begin,end expression for the given static vector; useful for algorithm arguments. -#define VectorRange(v) &(v)[0], VectorEnd(v) - -/// Returns the number of bits in the given type -#define BitsInType(t) (sizeof(t) * CHAR_BIT) - -/// Returns the mask of type \p t with the lowest \p n bits set. -#define BitMask(t,n) (t(~t(0)) >> (BitsInType(t) - (n))) - -/// Argument that is used only in debug builds (as in an assert) -#ifndef NDEBUG - #define DebugArg(x) x -#else - #define DebugArg(x) -#endif - -/// Shorthand for container iteration. -#define foreach(type,i,ctr) for (type i = (ctr).begin(); i != (ctr).end(); ++ i) -/// Shorthand for container reverse iteration. -#define eachfor(type,i,ctr) for (type i = (ctr).rbegin(); i != (ctr).rend(); ++ i) - -#ifndef DOXYGEN_SHOULD_SKIP_THIS -// Macro for passing template types as macro arguments. -// These are deprecated. Use metamac macros instead. Will remove by next release. -#define TEMPLATE_FULL_DECL1(d1,t1) template -#define TEMPLATE_FULL_DECL2(d1,t1,d2,t2) template -#define TEMPLATE_FULL_DECL3(d1,t1,d2,t2,d3,t3) template -#define TEMPLATE_DECL1(t1) TEMPLATE_FULL_DECL1(typename,t1) -#define TEMPLATE_DECL2(t1,t2) TEMPLATE_FULL_DECL2(typename,t1,typename,t2) -#define TEMPLATE_DECL3(t1,t2,t3) TEMPLATE_FULL_DECL3(typename,t1,typename,t2,typename,t3) -#define TEMPLATE_TYPE1(type,a1) type -#define TEMPLATE_TYPE2(type,a1,a2) type -#define TEMPLATE_TYPE3(type,a1,a2,a3) type -#endif - -/// Returns the minimum of \p a and \p b -template -inline constexpr T1 min (const T1& a, const T2& b) -{ - return a < b ? a : b; -} - -/// Returns the maximum of \p a and \p b -template -inline constexpr T1 max (const T1& a, const T2& b) -{ - return b < a ? a : b; -} - -/// Indexes into a static array with bounds limit -template -inline constexpr T& VectorElement (T(&v)[N], size_t i) { return v[min(i,N-1)]; } - -/// The alignment performed by default. -const size_t c_DefaultAlignment = __alignof__(void*); - -/// \brief Rounds \p n up to be divisible by \p grain -template -inline constexpr T AlignDown (T n, size_t grain = c_DefaultAlignment) - { return n - n % grain; } - -/// \brief Rounds \p n up to be divisible by \p grain -template -inline constexpr T Align (T n, size_t grain = c_DefaultAlignment) - { return AlignDown (n + grain - 1, grain); } - -/// Returns a nullptr pointer cast to T. -template -inline constexpr T* NullPointer (void) - { return nullptr; } - -/// \brief Returns a non-dereferentiable value reference. -/// This is useful for passing to stream_align_of or the like which need a value but -/// don't need to actually use it. -template -inline constexpr T& NullValue (void) - { return *NullPointer(); } - -#ifndef DOXYGEN_SHOULD_SKIP_THIS -// Offsets a pointer -template -inline T advance_ptr (T i, ptrdiff_t offset) - { return i + offset; } - -// Offsets a void pointer -template <> -inline const void* advance_ptr (const void* p, ptrdiff_t offset) -{ - assert (p || !offset); - return reinterpret_cast(p) + offset; -} - -// Offsets a void pointer -template <> -inline void* advance_ptr (void* p, ptrdiff_t offset) -{ - assert (p || !offset); - return reinterpret_cast(p) + offset; -} -#endif - -/// Offsets an iterator -template -inline T advance (T i, Distance offset) - { return advance_ptr (i, offset); } - -/// Returns the difference \p p1 - \p p2 -template -inline constexpr ptrdiff_t distance (T1 i1, T2 i2) -{ - return i2 - i1; -} - -#ifndef DOXYGEN_SHOULD_SKIP_THIS -#define UNVOID_DISTANCE(T1const,T2const) \ -template <> inline constexpr ptrdiff_t distance (T1const void* p1, T2const void* p2) \ -{ return static_cast(p2) - static_cast(p1); } -UNVOID_DISTANCE(,) -UNVOID_DISTANCE(const,const) -UNVOID_DISTANCE(,const) -UNVOID_DISTANCE(const,) -#undef UNVOID_DISTANCE -#endif - -template -T* addressof (T& v) - { return reinterpret_cast(&const_cast(reinterpret_cast(v))); } - -#ifndef DOXYGEN_SHOULD_SKIP_THIS -// The compiler issues a warning if an unsigned type is compared to 0. -template struct __is_negative { inline constexpr bool operator()(const T& v) const { return v < 0; } }; -template struct __is_negative { inline constexpr bool operator()(const T&) const { return false; } }; -/// Warning-free way to check if \p v is negative, even if for unsigned types. -template inline constexpr bool is_negative (const T& v) { return __is_negative::is_signed>()(v); } -#endif - -/// \brief Returns the absolute value of \p v -/// Unlike the stdlib functions, this is inline and works with all types. -template -inline constexpr T absv (T v) -{ - return is_negative(v) ? -v : v; -} - -/// \brief Returns -1 for negative values, 1 for positive, and 0 for 0 -template -inline constexpr T sign (T v) -{ - return (0 < v) - is_negative(v); -} - -/// Returns the absolute value of the distance i1 and i2 -template -inline constexpr size_t abs_distance (T1 i1, T2 i2) -{ - return absv (distance(i1, i2)); -} - -/// Returns the size of \p n elements of size \p T -template -inline constexpr size_t size_of_elements (size_t n, const T*) -{ - return n * sizeof(T); -} - -/// Returns the greatest common divisor -template -constexpr T gcd (T a, T b) -{ - return b ? gcd(b,a%b) : absv(a); -} - -/// Returns the least common multiple -template -constexpr T lcm (T a, T b) -{ - return a/gcd(a,b)*b; -} - -// Defined in byteswap.h, which is usually unusable. -#undef bswap_16 -#undef bswap_32 -#undef bswap_64 - -inline uint16_t bswap_16 (uint16_t v) -{ -#if __x86__ - if (!__builtin_constant_p(v)) asm ("rorw $8, %0":"+r"(v)); else -#endif - v = v << 8 | v >> 8; - return v; -} -inline uint32_t bswap_32 (uint32_t v) -{ -#if __x86__ - if (!__builtin_constant_p(v)) asm ("bswap %0":"+r"(v)); else -#endif - v = v << 24 | (v & 0xFF00) << 8 | ((v >> 8) & 0xFF00) | v >> 24; - return v; -} -#if HAVE_INT64_T -inline uint64_t bswap_64 (uint64_t v) -{ -#if __x86_64__ - if (!__builtin_constant_p(v)) asm ("bswap %0":"+r"(v)); else -#endif - v = (uint64_t(bswap_32(v)) << 32) | bswap_32(v >> 32); - return v; -} -#endif - -/// \brief Swaps the byteorder of \p v. -template -inline T bswap (const T& v) -{ - switch (BitsInType(T)) { - default: return v; - case 16: return T (bswap_16 (uint16_t (v))); - case 32: return T (bswap_32 (uint32_t (v))); -#if HAVE_INT64_T - case 64: return T (bswap_64 (uint64_t (v))); -#endif - } -} - -#if BYTE_ORDER == BIG_ENDIAN -template inline T le_to_native (const T& v) { return bswap (v); } -template inline T be_to_native (const T& v) { return v; } -template inline T native_to_le (const T& v) { return bswap (v); } -template inline T native_to_be (const T& v) { return v; } -#elif BYTE_ORDER == LITTLE_ENDIAN -template inline T le_to_native (const T& v) { return v; } -template inline T be_to_native (const T& v) { return bswap (v); } -template inline T native_to_le (const T& v) { return v; } -template inline T native_to_be (const T& v) { return bswap (v); } -#endif // BYTE_ORDER - -/// Deletes \p p and sets it to nullptr -template -inline void Delete (T*& p) -{ - delete p; - p = nullptr; -} - -/// Deletes \p p as an array and sets it to nullptr -template -inline void DeleteVector (T*& p) -{ - delete [] p; - p = nullptr; -} - -/// Template of making != from ! and == -template -inline constexpr bool operator!= (const T& x, const T& y) - { return !(x == y); } - -/// Template of making > from < -template -inline constexpr bool operator> (const T& x, const T& y) - { return y < x; } - -/// Template of making <= from < and == -template -inline constexpr bool operator<= (const T& x, const T& y) - { return !(y < x); } - -/// Template of making >= from < and == -template -inline constexpr bool operator>= (const T& x, const T& y) - { return !(x < y); } - -/// Packs \p s multiple times into \p b. Useful for loop unrolling. -template -inline void pack_type (TSmall s, TBig& b) -{ - b = s; - for (unsigned h = BitsInType(TSmall); h < BitsInType(TBig); h *= 2) - b = (b << h)|b; -} - -/// \brief Divides \p n1 by \p n2 and rounds the result up. -/// This is in contrast to regular division, which rounds down. -template -inline T1 DivRU (T1 n1, T2 n2) -{ - T2 adj = n2 - 1; - if (is_negative (n1)) - adj = -adj; - return (n1 + adj) / n2; -} - -/// Sets the contents of \p pm to 1 and returns true if the previous value was 0. -inline bool TestAndSet (int* pm) -{ -#if __x86__ - int oldVal (1); - asm volatile ("xchgl %0, %1" : "=r"(oldVal), "=m"(*pm) : "0"(oldVal), "m"(*pm) : "memory"); - return !oldVal; -#elif __sparc32__ // This has not been tested - int rv; - asm volatile ("ldstub %1, %0" : "=r"(rv), "=m"(*pm) : "m"(pm)); - return !rv; -#else - const int oldVal (*pm); - *pm = 1; - return !oldVal; -#endif -} - -/// Returns the index of the first set bit in \p v or \p nbv if none. -inline uoff_t FirstBit (uint32_t v, uoff_t nbv) -{ - uoff_t n = nbv; -#if __x86__ - if (!__builtin_constant_p(v)) asm ("bsr\t%1, %k0":"+r,r"(n):"r,m"(v)); else -#endif -#if __GNUC__ - if (v) n = 31 - __builtin_clz(v); -#else - if (v) for (uint32_t m = uint32_t(1)<<(n=31); !(v & m); m >>= 1) --n; -#endif - return n; -} -/// Returns the index of the first set bit in \p v or \p nbv if none. -inline uoff_t FirstBit (uint64_t v, uoff_t nbv) -{ - uoff_t n = nbv; -#if __x86_64__ - if (!__builtin_constant_p(v)) asm ("bsr\t%1, %0":"+r,r"(n):"r,m"(v)); else -#endif -#if __GNUC__ && SIZE_OF_LONG >= 8 - if (v) n = 63 - __builtin_clzl(v); -#elif __GNUC__ && HAVE_LONG_LONG && SIZE_OF_LONG_LONG >= 8 - if (v) n = 63 - __builtin_clzll(v); -#else - if (v) for (uint64_t m = uint64_t(1)<<(n=63); !(v & m); m >>= 1) --n; -#endif - return n; -} - -/// Returns the next power of 2 >= \p v. -/// Values larger than UINT32_MAX/2 will return 2^0 -inline uint32_t NextPow2 (uint32_t v) -{ - uint32_t r = v-1; -#if __x86__ - if (!__builtin_constant_p(r)) asm("bsr\t%0, %0":"+r"(r)); else -#endif - { r = FirstBit(r,r); if (r >= BitsInType(r)-1) r = uint32_t(-1); } - return 1<<(1+r); -} - -/// Bitwise rotate value left -template -inline T Rol (T v, size_t n) -{ -#if __x86__ - if (!(__builtin_constant_p(v) && __builtin_constant_p(n))) asm("rol\t%b1, %0":"+r,r"(v):"i,c"(n)); else -#endif - v = (v << n) | (v >> (BitsInType(T)-n)); - return v; -} - -/// Bitwise rotate value right -template -inline T Ror (T v, size_t n) -{ -#if __x86__ - if (!(__builtin_constant_p(v) && __builtin_constant_p(n))) asm("ror\t%b1, %0":"+r,r"(v):"i,c"(n)); else -#endif - v = (v >> n) | (v << (BitsInType(T)-n)); - return v; -} - -/// \brief This template is to be used for dereferencing a type-punned pointer without a warning. -/// -/// When casting a local variable to an unrelated type through a pointer (for -/// example, casting a float to a uint32_t without conversion), the resulting -/// memory location can be accessed through either pointer, which violates the -/// strict aliasing rule. While -fno-strict-aliasing option can be given to -/// the compiler, eliminating this warning, inefficient code may result in -/// some instances, because aliasing inhibits some optimizations. By using -/// this template, and by ensuring the memory is accessed in one way only, -/// efficient code can be produced without the warning. For gcc 4.1.0+. -/// -template -inline DEST noalias (const DEST&, SRC* s) -{ - asm("":"+g"(s)::"memory"); - union UPun { SRC s; DEST d; }; - return reinterpret_cast(s)->d; -} - -template -inline DEST noalias_cast (SRC s) -{ - asm("":"+g"(s)::"memory"); - union { SRC s; DEST d; } u = {s}; - return u.d; -} - -namespace simd { - /// Call after you are done using SIMD algorithms for 64 bit tuples. - #define ALL_MMX_REGS_CHANGELIST "mm0","mm1","mm2","mm3","mm4","mm5","mm6","mm7","st","st(1)","st(2)","st(3)","st(4)","st(5)","st(6)","st(7)" -#if __3DNOW__ - inline void reset_mmx (void) { asm ("femms":::ALL_MMX_REGS_CHANGELIST); } -#elif __MMX__ - inline void reset_mmx (void) { asm ("emms":::ALL_MMX_REGS_CHANGELIST); } -#else - inline void reset_mmx (void) {} -#endif -} // namespace simd -} // namespace ustl diff --git a/common/include/ustl/uvector.h b/common/include/ustl/uvector.h deleted file mode 100644 index cb88bbcf0..000000000 --- a/common/include/ustl/uvector.h +++ /dev/null @@ -1,349 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#pragma once -#include "memblock.h" -#include "umemory.h" -#include "upredalgo.h" - -namespace ustl { - -/// \class vector uvector.h ustl.h -/// \ingroup Sequences -/// -/// \brief STL vector equivalent. -/// -/// Provides a typed array-like interface to a managed memory block, including -/// element access, iteration, modification, resizing, and serialization. In -/// this design elements frequently undergo bitwise move, so don't put it in -/// here if it doesn't support it. This mostly means having no self-pointers. -/// -template -class vector { -public: - typedef T value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef pointer iterator; - typedef const_pointer const_iterator; - typedef memblock::size_type size_type; - typedef memblock::written_size_type written_size_type; - typedef memblock::difference_type difference_type; - typedef ::ustl::reverse_iterator reverse_iterator; - typedef ::ustl::reverse_iterator const_reverse_iterator; -public: - inline vector (void); - inline explicit vector (size_type n); - vector (size_type n, const T& v); - vector (const vector& v); - vector (const_iterator i1, const_iterator i2); - inline ~vector (void) noexcept; - inline const vector& operator= (const vector& v); - inline bool operator== (const vector& v) const { return _data == v._data; } - inline operator cmemlink (void) const { return cmemlink (_data); } - inline operator cmemlink (void) { return cmemlink (_data); } - inline operator memlink (void) { return memlink (_data); } - inline void reserve (size_type n, bool bExact = false); - inline void resize (size_type n); - void resize (size_type n, const_reference v); - inline size_type capacity (void) const { return _data.capacity() / sizeof(T); } - inline size_type size (void) const { return _data.size() / sizeof(T); } - inline size_type max_size (void) const { return _data.max_size() / sizeof(T); } - inline bool empty (void) const { return _data.empty(); } - inline iterator begin (void) { return iterator (_data.begin()); } - inline const_iterator begin (void) const { return const_iterator (_data.begin()); } - inline iterator end (void) { return iterator (_data.end()); } - inline const_iterator end (void) const { return const_iterator (_data.end()); } - inline const_iterator cbegin (void) const { return begin(); } - inline const_iterator cend (void) const { return end(); } - inline reverse_iterator rbegin (void) { return reverse_iterator (end()); } - inline const_reverse_iterator rbegin (void) const { return const_reverse_iterator (end()); } - inline reverse_iterator rend (void) { return reverse_iterator (begin()); } - inline const_reverse_iterator rend (void) const { return const_reverse_iterator (begin()); } - inline const_reverse_iterator crbegin (void) const { return rbegin(); } - inline const_reverse_iterator crend (void) const { return rend(); } - inline pointer data (void) { return pointer (_data.data()); } - inline const_pointer data (void) const { return const_pointer (_data.data()); } - inline const_pointer cdata (void) const { return const_pointer (_data.cdata()); } - inline iterator iat (size_type i) { assert (i <= size()); return begin() + i; } - inline const_iterator iat (size_type i) const { assert (i <= size()); return begin() + i; } - inline reference at (size_type i) { assert (i < size()); return begin()[i]; } - inline const_reference at (size_type i) const { assert (i < size()); return begin()[i]; } - inline reference operator[] (size_type i) { return at (i); } - inline const_reference operator[] (size_type i) const { return at (i); } - inline reference front (void) { return at(0); } - inline const_reference front (void) const { return at(0); } - inline reference back (void) { assert (!empty()); return end()[-1]; } - inline const_reference back (void) const { assert (!empty()); return end()[-1]; } - inline void push_back (const T& v = T()); - inline void pop_back (void) { size_type nsz = _data.size()-sizeof(T); destroy (iterator(_data.begin()+nsz)); _data.memlink::resize (nsz); } - inline void clear (void) { destroy_all(); _data.clear(); } - inline void shrink_to_fit (void) { _data.shrink_to_fit(); } - inline void deallocate (void) noexcept; - inline void assign (const_iterator i1, const_iterator i2); - inline void assign (size_type n, const T& v); - inline void swap (vector& v) { _data.swap (v._data); } - inline iterator insert (const_iterator ip, const T& v); - inline iterator insert (const_iterator ip, size_type n, const T& v); - inline iterator insert (const_iterator ip, const_iterator i1, const_iterator i2); - inline iterator erase (const_iterator ep, size_type n = 1); - inline iterator erase (const_iterator ep1, const_iterator ep2); - inline void manage (pointer p, size_type n) { _data.manage (p, n * sizeof(T)); } - inline bool is_linked (void) const { return _data.is_linked(); } - inline void unlink (void) { _data.unlink(); } - inline void copy_link (void) { _data.copy_link(); } - inline void link (const_pointer p, size_type n) { _data.link (p, n * sizeof(T)); } - inline void link (pointer p, size_type n) { _data.link (p, n * sizeof(T)); } - inline void link (const vector& v) { _data.link (v); } - inline void link (vector& v) { _data.link (v); } - inline void link (const_pointer first, const_pointer last) { _data.link (first, last); } - inline void link (pointer first, pointer last) { _data.link (first, last); } - //inline void read (istream& is) { container_read (is, *this); } - //inline void write (ostream& os) const { container_write (os, *this); } - //inline void text_write (ostringstream& os) const { container_text_write (os, *this); } - inline size_t stream_size (void) const { return container_stream_size (*this); } -#if HAVE_CPP11 - inline vector (vector&& v) : _data(move(v._data)) {} - inline vector (std::initializer_list v) : _data() { uninitialized_copy_n (v.begin(), v.size(), append_hole(v.size())); } - inline vector& operator= (vector&& v) { swap (v); return *this; } - template - inline iterator emplace (const_iterator ip, Args&&... args); - template - inline void emplace_back (Args&&... args); - inline void push_back (T&& v) { emplace_back (move(v)); } - inline iterator insert (const_iterator ip, T&& v) { return emplace (ip, move(v)); } - inline iterator insert (const_iterator ip, std::initializer_list v) { return insert (ip, v.begin(), v.end()); } -#endif -protected: - inline iterator insert_space (const_iterator ip, size_type n); -private: - inline iterator insert_hole (const_iterator ip, size_type n); - inline iterator append_hole (size_type n); - void destroy_all (void) - { if (!is_linked()) destroy (begin(), end()); } -private: - memblock _data; ///< Raw element data, consecutively stored. -}; - -/// Allocates space for at least \p n elements. -template -inline void vector::reserve (size_type n, bool bExact) -{ - _data.reserve (n*sizeof(T), bExact); -} - -template -inline typename vector::iterator vector::append_hole (size_type n) -{ - size_type nsz = _data.size() + n*sizeof(T); - _data.reserve (nsz); - iterator hp = end(); - _data.memlink::resize (nsz); - return hp; -} - -/// Resizes the vector to contain \p n elements. -template -void vector::resize (size_type n) -{ - size_type nb = n*sizeof(T); - _data.reserve (nb); - iterator inewend = iterator(_data.begin()+nb); - if (nb < _data.size()) - destroy (inewend, end()); - else - uninitialized_default_construct (end(), inewend); - _data.memlink::resize (nb); -} - -/// Resizes the vector to contain \p n elements. -template -void vector::resize (size_type n, const_reference v) -{ - size_type nb = n*sizeof(T); - _data.reserve (nb); - iterator inewend = iterator(_data.begin()+nb); - if (nb < _data.size()) - destroy (inewend, end()); - else - uninitialized_fill (end(), inewend, v); - _data.memlink::resize (nb); -} - -/// Calls element destructors and frees storage. -template -inline void vector::deallocate (void) noexcept -{ - destroy_all(); - _data.deallocate(); -} - -/// Initializes empty vector. -template -inline vector::vector (void) -:_data() -{ -} - -/// Initializes a vector of size \p n. -template -inline vector::vector (size_type n) -:_data() -{ - resize (n); -} - -/// Copies \p n elements from \p v. -template -vector::vector (size_type n, const T& v) -:_data() -{ - uninitialized_fill_n (append_hole (n), n, v); -} - -/// Copies \p v. -template -vector::vector (const vector& v) -:_data() -{ - uninitialized_copy_n (v.begin(), v.size(), append_hole(v.size())); -} - -/// Copies range [\p i1, \p i2] -template -vector::vector (const_iterator i1, const_iterator i2) -:_data() -{ - uninitialized_copy (i1, i2, append_hole(distance(i1,i2))); -} - -/// Destructor -template -inline vector::~vector (void) noexcept -{ - destroy_all(); -} - -/// Copies the range [\p i1, \p i2] -template -inline void vector::assign (const_iterator i1, const_iterator i2) -{ - assert (i1 <= i2); - resize (distance (i1, i2)); - ::ustl::copy (i1, i2, begin()); -} - -/// Copies \p n elements with value \p v. -template -inline void vector::assign (size_type n, const T& v) -{ - resize (n); - ::ustl::fill (begin(), end(), v); -} - -/// Copies contents of \p v. -template -inline const vector& vector::operator= (const vector& v) -{ - assign (v.begin(), v.end()); - return *this; -} - -/// Inserts \p n uninitialized elements at \p ip. -template -inline typename vector::iterator vector::insert_hole (const_iterator ip, size_type n) -{ - const uoff_t ipmi = distance (_data.begin(), memblock::const_iterator(ip)); - reserve (size() + n); - return iterator (_data.insert (_data.iat(ipmi), n * sizeof(T))); -} - -/// Inserts \p n uninitialized elements at \p ip. -template -inline typename vector::iterator vector::insert_space (const_iterator ip, size_type n) -{ - iterator ih = insert_hole (ip, n); - uninitialized_default_construct_n (ih, n); - return ih; -} - -/// Inserts \p n elements with value \p v at offsets \p ip. -template -inline typename vector::iterator vector::insert (const_iterator ip, size_type n, const T& v) -{ - iterator d = insert_hole (ip, n); - uninitialized_fill_n (d, n, v); - return d; -} - -/// Inserts value \p v at offset \p ip. -template -inline typename vector::iterator vector::insert (const_iterator ip, const T& v) -{ - iterator d = insert_hole (ip, 1); - construct_at (d, v); - return d; -} - -/// Inserts range [\p i1, \p i2] at offset \p ip. -template -inline typename vector::iterator vector::insert (const_iterator ip, const_iterator i1, const_iterator i2) -{ - assert (i1 <= i2); - iterator d = insert_hole (ip, distance (i1, i2)); - uninitialized_copy (i1, i2, d); - return d; -} - -/// Removes \p count elements at offset \p ep. -template -inline typename vector::iterator vector::erase (const_iterator ep, size_type n) -{ - iterator d = const_cast(ep); - destroy_n (d, n); - return iterator (_data.erase (memblock::iterator(d), n * sizeof(T))); -} - -/// Removes elements from \p ep1 to \p ep2. -template -inline typename vector::iterator vector::erase (const_iterator ep1, const_iterator ep2) -{ - assert (ep1 <= ep2); - return erase (ep1, distance(ep1, ep2)); -} - -/// Inserts value \p v at the end of the vector. -template -inline void vector::push_back (const T& v) -{ - construct_at (append_hole(1), v); -} - -#if HAVE_CPP11 - -/// Constructs value at \p ip -template -template -inline typename vector::iterator vector::emplace (const_iterator ip, Args&&... args) -{ - return new (insert_hole(ip,1)) T (forward(args)...); -} - -/// Constructs value at the end of the vector. -template -template -inline void vector::emplace_back (Args&&... args) -{ - new (append_hole(1)) T (forward(args)...); -} - -#endif - -/// Use with vector classes to allocate and link to stack space. \p n is in elements. -#define typed_alloca_link(m,T,n) (m).link ((T*) alloca ((n) * sizeof(T)), (n)) - -} // namespace ustl diff --git a/common/include/util/AtomicMpScQueue.h b/common/include/util/AtomicMpScQueue.h new file mode 100644 index 000000000..aa8cb6b52 --- /dev/null +++ b/common/include/util/AtomicMpScQueue.h @@ -0,0 +1,27 @@ +#pragma once + +#include "EASTL/atomic.h" + +// Atomic multiple producer single consumer queue +template +class AtomicMpScQueue +{ +public: + void pushFront(T* item) + { + T* expected_next = nullptr; + do + { + item->next = expected_next; + } while(!queue_head.compare_exchange_weak(expected_next, item)); + } + + T* takeAll() + { + T* front = queue_head.exchange(nullptr); + return front; + } + +private: + eastl::atomic queue_head = nullptr; +}; diff --git a/common/include/util/Bitmap.h b/common/include/util/Bitmap.h index 3ed5b3efb..9d2eb9c4d 100644 --- a/common/include/util/Bitmap.h +++ b/common/include/util/Bitmap.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" +#include #define BITMAP_BYTE_COUNT(number_of_bits) (number_of_bits / Bitmap::bits_per_bitmap_atom_ + ((number_of_bits % Bitmap::bits_per_bitmap_atom_ > 0) ? 1 : 0)) @@ -11,36 +12,38 @@ class Bitmap static uint8 const bits_per_bitmap_atom_; Bitmap(size_t number_of_bits); + Bitmap(const uint8_t* data, size_t number_of_bits); Bitmap(const Bitmap &bm); ~Bitmap(); bool setBit(size_t bit_number); static bool setBit(uint8* b, size_t& num_bits_set, size_t bit_number); - bool getBit(size_t bit_number); - static bool getBit(uint8* b, size_t bit_number); + bool getBit(size_t bit_number) const; + static bool getBit(const uint8* b, size_t bit_number); bool unsetBit(size_t bit_number); static bool unsetBit(uint8* b, size_t& num_bits_set, size_t bit_number); - size_t getSize(); + size_t getSize() const; + size_t getBytesSize() const; /** * returns the number of bits set * @return the number of bits set */ - size_t getNumBitsSet(); + size_t getNumBitsSet() const; /** * returns the number of unset bits * @return the number of unset bits */ - size_t getNumFreeBits(); + size_t getNumFreeBits() const; /** * prints the bitmap using kprintfd */ - void bmprint(); + void bmprint() const; static void bmprint(uint8* b, size_t n, size_t num_bits_set); /** @@ -57,11 +60,12 @@ class Bitmap * @param byte_number the number of the byte to return * @return the byte */ - uint8 getByte(size_t byte_number); + uint8 getByte(size_t byte_number) const; + + const uint8* data() const; private: size_t size_; size_t num_bits_set_; uint8 *bitmap_; }; - diff --git a/common/include/util/FiFo.h b/common/include/util/FiFo.h index 8df5cebe4..70bfff6e0 100644 --- a/common/include/util/FiFo.h +++ b/common/include/util/FiFo.h @@ -1,9 +1,11 @@ #pragma once +#include "Condition.h" +#include "Mutex.h" #include "kprintf.h" #include "new.h" -#include "Mutex.h" -#include "Condition.h" + +#include "EASTL/vector.h" #ifdef __cplusplus extern "C" @@ -30,7 +32,7 @@ class FiFo */ FiFo(uint32 inputb_size = 512, uint8 flags = 0); - ~FiFo(); + ~FiFo() = default; /** * Puts the given parameter in the fifo buffer. @@ -65,7 +67,7 @@ class FiFo Condition space_to_write_; uint32 input_buffer_size_; - T *input_buffer_; + eastl::vector input_buffer_; uint32 ib_write_pos_; uint32 ib_read_pos_; @@ -82,18 +84,12 @@ FiFo::FiFo(uint32 inputb_size, uint8 flags) : else input_buffer_size_ = inputb_size; - input_buffer_ = new T[input_buffer_size_]; + input_buffer_.resize(input_buffer_size_); ib_write_pos_ = 1; ib_read_pos_ = 0; flags_ = flags; } -template -FiFo::~FiFo() -{ - delete[] input_buffer_; -} - //only put uses the fallback buffer -> so it doesn't need a lock //input_buffer could be in use -> so if locked use fallback template @@ -123,7 +119,7 @@ void FiFo::put(T c) } template -void FiFo::clear(void) +void FiFo::clear() { input_buffer_lock_.acquire(); ib_write_pos_ = 1; @@ -180,4 +176,3 @@ uint32 FiFo::countElementsAhead() // count < 0 return (input_buffer_size_ + count); } - diff --git a/common/include/util/IntervalSet.h b/common/include/util/IntervalSet.h new file mode 100644 index 000000000..50df11b46 --- /dev/null +++ b/common/include/util/IntervalSet.h @@ -0,0 +1,119 @@ +#pragma once + +#include "EASTL/map.h" +#include "EASTL/utility.h" + +template +class IntervalSet : public eastl::map, TAllocator> +{ +public: + typedef eastl::map, TAllocator> base_type; + typedef IntervalSet this_type; + typedef TAllocator allocator_type; + typedef typename base_type::iterator iterator; + typedef typename base_type::key_type key_type; + typedef typename base_type::mapped_type mapped_type; + + using base_type::begin; + using base_type::end; + using base_type::get_allocator; + using base_type::upper_bound; + + using Interval = eastl::pair; + + explicit IntervalSet(const allocator_type& allocator = allocator_type("IntervalSet")) + : base_type(allocator) + { + } + + eastl::pair insert(const Interval& interval) + { + assert(eastl::less_equal{}(interval.first, interval.second)); + + iterator interval_it; + iterator next_it = upper_bound(interval.first); + + if (next_it == begin() || // is new lowest element + eastl::less{}(eastl::prev(next_it)->second, interval.first)) // new interval starts after previous end + { + // insert new element before next_it + interval_it = base_type::insert(next_it, interval); + } + else + { + // Starts inside previous interval + iterator prev_it = eastl::prev(next_it); + interval_it = prev_it; + if (eastl::equal_to{}(prev_it->second, interval.second)) + { + // fully contained + return {prev_it, false}; + } + else + { + // extend end + interval_it->second = interval.second; + } + } + + // merge forward + while (next_it != end() && eastl::greater_equal{}(interval.second, next_it->first)) + { + interval_it->second = eastl::max(next_it->second, interval_it->second); + next_it = base_type::erase(next_it); + } + + return {interval_it, true}; + } + + iterator erase(const Interval& interval) + { + assert(eastl::less_equal{}(interval.first, interval.second)); + + auto next_it = base_type::lower_bound(interval.first); + + if (next_it != begin()) + { + auto prev_it = eastl::prev(next_it); + + if (eastl::less{}(prev_it->first, interval.first) && + eastl::less{}(interval.first, prev_it->second)) + { + // interval starts in prev + + if (eastl::less{}(interval.second, prev_it->second)) + { + // parts of prev remaining after interval end + // split into two intervals + Interval remainder = *prev_it; + remainder.first = interval.second; + next_it = base_type::emplace_hint(next_it, eastl::move(remainder)); + } + + prev_it->second = interval.first; + } + } + + while (next_it != end() && eastl::less{}(next_it->first, interval.second)) + { + if (eastl::less_equal{}(next_it->second, interval.second)) + { + // completely overlapping + next_it = base_type::erase(next_it); + } + else + { + // partially overlapping, increase start + Interval tmp_val = eastl::move(*next_it); + next_it = base_type::erase(next_it); + + tmp_val.first = interval.second; + base_type::emplace_hint(next_it, eastl::move(tmp_val)); + } + } + + return next_it; + } + +private: +}; diff --git a/common/include/util/NonBlockingQueue.h b/common/include/util/NonBlockingQueue.h new file mode 100644 index 000000000..a0349b763 --- /dev/null +++ b/common/include/util/NonBlockingQueue.h @@ -0,0 +1,79 @@ +#pragma once + +#include "EASTL/atomic.h" + +#include "assert.h" +#include "debug.h" + +// Multiple producer, single consumer lock free FIFO queue +// Node class T needs to have a eastl::atomic next_node_ member +template +class NonBlockingQueue +{ +public: + void pushFront(T* node) + { + T* expected_next = list_head_.load(); + do + { + node->next_node_ = expected_next; + } + while (!list_head_.compare_exchange_weak(expected_next, node)); // compare exchange loads expected_next with the current value on failure + } + + T* peekBack() + { + eastl::atomic* curr_ptr = &list_head_; + + T* curr = curr_ptr->load(); + if (!curr) + { + return curr; + } + + while (curr->next_node_) + { + curr_ptr = &curr->next_node_; + curr = curr->next_node_; + } + + return curr; + } + + T* popBack() + { + eastl::atomic* curr_ptr = &list_head_; + + T* curr = curr_ptr->load(); + if (!curr) + { + return curr; + } + + while (curr->next_node_.load()) + { + curr_ptr = &curr->next_node_; + curr = curr->next_node_; + + assert(curr); + } + + T* last_node = curr; + + // list head might change if new nodes are added (but last node won't), so we need to be prepared to walk the list forward until we find the last node again + while (!curr_ptr->compare_exchange_strong(curr, nullptr)) + { + if (curr != nullptr) + { + curr_ptr = &curr->next_node_; + } + + curr = last_node; + } + + return last_node; + } + +private: + eastl::atomic list_head_ = nullptr; +}; diff --git a/common/include/util/RingBuffer.h b/common/include/util/RingBuffer.h index cb965573b..2308a9822 100644 --- a/common/include/util/RingBuffer.h +++ b/common/include/util/RingBuffer.h @@ -50,30 +50,29 @@ RingBuffer::~RingBuffer() template void RingBuffer::put ( T c ) { - size_t old_write_pos=write_pos_; + size_t old_write_pos = write_pos_; if ( old_write_pos == read_pos_ ) return; buffer_[old_write_pos]=c; - ArchThreads::testSetLock ( write_pos_, ( old_write_pos + 1 ) % buffer_size_ ); + ArchThreads::testSetLock(write_pos_, (old_write_pos + 1) % buffer_size_); } template void RingBuffer::clear() { - ArchThreads::testSetLock ( write_pos_,1 ); + ArchThreads::testSetLock(write_pos_, (size_t)1); // assumed that there is only one reader who can't have called clear and get at the same time. // here get would return garbage. - ArchThreads::testSetLock ( read_pos_,0 ); + ArchThreads::testSetLock(read_pos_, (size_t)0); } template bool RingBuffer::get ( T &c ) { - uint32 new_read_pos = ( read_pos_ + 1 ) % buffer_size_; + size_t new_read_pos = ( read_pos_ + 1 ) % buffer_size_; if ( write_pos_ == new_read_pos ) //nothing new to read return false; c = buffer_[new_read_pos]; - ArchThreads::testSetLock ( read_pos_,new_read_pos ); + ArchThreads::testSetLock(read_pos_, (size_t)new_read_pos); return true; } - diff --git a/common/include/util/SWEBDebugInfo.h b/common/include/util/SWEBDebugInfo.h index efaf15667..9d85a5a9b 100644 --- a/common/include/util/SWEBDebugInfo.h +++ b/common/include/util/SWEBDebugInfo.h @@ -1,9 +1,10 @@ #pragma once -#include "ustring.h" -#include "umap.h" #include "Stabs2DebugInfo.h" +#include "EASTL/map.h" +#include "EASTL/string.h" +#include "EASTL/vector_map.h" // The limit for function names, after that, they will get capped #define CALL_FUNC_NAME_LIMIT 256 @@ -11,19 +12,18 @@ class SWEBDebugInfo : public Stabs2DebugInfo { public: + SWEBDebugInfo(const char* sweb_begin, const char* sweb_end); - SWEBDebugInfo(char const *sweb_begin, char const *sweb_end); + ~SWEBDebugInfo() override = default; - virtual ~SWEBDebugInfo(); + void getCallNameAndLine(pointer address, const char *&mangled_name, ssize_t &line) const override; - virtual void getCallNameAndLine(pointer address, const char *&mangled_name, ssize_t &line) const; - - virtual void printCallInformation(pointer address) const; + void printCallInformation(pointer address) const override; private: - ustl::map file_addrs_; - ustl::map function_defs_; + eastl::vector_map file_addrs_; + eastl::vector_map function_defs_; - virtual void initialiseSymbolTable(); + void initialiseSymbolTable() override; }; diff --git a/common/include/util/SerialManager.h b/common/include/util/SerialManager.h index 136cc25f8..7ae2fc647 100644 --- a/common/include/util/SerialManager.h +++ b/common/include/util/SerialManager.h @@ -1,117 +1,133 @@ #pragma once -#include "types.h" +#include "DeviceDriver.h" +#include "IrqDomain.h" #include "chardev.h" + #include "ArchSerialInfo.h" -#define MAX_PORTS 16 +#include "types.h" class ArchSerialInfo; -class SerialPort : public CharacterDevice +class SerialPort : public CharacterDevice, public IrqDomain { public: - typedef enum _br - { - BR_9600, BR_14400, BR_19200, BR_38400, BR_55600, BR_115200 - } BAUD_RATE_E; - - typedef enum _par - { - ODD_PARITY, EVEN_PARITY, NO_PARITY - } PARITY_E; - - typedef enum _db - { - DATA_7, DATA_8 - } DATA_BITS_E; - - typedef enum _sb - { - STOP_ONE, STOP_TWO, STOP_ONEANDHALF - } STOP_BITS_E; - - typedef enum _sres - { - SR_OK, SR_ERROR // you might want to add elements for common errors that can appear - } SRESULT; - - SerialPort(char*, ArchSerialInfo port_info); - - ~SerialPort(); - - /** - * Opens a serial port for reading or writing - * @param baud_rate Speed @see BAUD_RATE_E - * @param data_bits Data bits @see DATA_BITS_E - * @param stop_bits Stop bits @see STOP_BITS_E - * @param parity Parity @see PARITY_E - * @return Result @see SERIAL_ERROR_E - */ - SRESULT setup_port(BAUD_RATE_E baud_rate, DATA_BITS_E data_bits, STOP_BITS_E stop_bits, PARITY_E parity); - - /** - * Writes size bytes to serial port - * @param offset Not used with serial ports - * @param size Number of bytes to be written - * @param buffer The data to be written - * @return Number of bytes actualy written or -1 in case of an error - */ - virtual int32 writeData(uint32 offset, uint32 size, const char*buffer); - - void irq_handler(); - - /** - * Returns the Architecture specific data for this serial port. - * The basic task of any operating system is to hide this ugly data. - * This function is mainly called from SerialManager and I do not think - * that it is needed anywhere else. Perhaps it could be protected and - * SerialManager declared as a friend class. - * @return @see ArchSerialInfo - */ - ArchSerialInfo get_info() - { - return port_info_; - } - ; - - private: - void write_UART(uint32 reg, uint8 what); - uint8 read_UART(uint32 reg); + enum BAUD_RATE_E + { + BR_9600, + BR_14400, + BR_19200, + BR_38400, + BR_55600, + BR_115200 + }; + + enum PARITY_E + { + ODD_PARITY, + EVEN_PARITY, + NO_PARITY + }; + + enum DATA_BITS_E + { + DATA_7, + DATA_8 + }; + + enum STOP_BITS_E + { + STOP_ONE, + STOP_TWO, + STOP_ONEANDHALF + }; + + enum SRESULT + { + SR_OK, + SR_ERROR // you might want to add elements for common errors that can appear + }; + + SerialPort(const char* name, const ArchSerialInfo& port_info); + + ~SerialPort() override = default; + + /** + * Opens a serial port for reading or writing + * @param baud_rate Speed @see BAUD_RATE_E + * @param data_bits Data bits @see DATA_BITS_E + * @param stop_bits Stop bits @see STOP_BITS_E + * @param parity Parity @see PARITY_E + * @return Result @see SERIAL_ERROR_E + */ + SRESULT setup_port(BAUD_RATE_E baud_rate, + DATA_BITS_E data_bits, + STOP_BITS_E stop_bits, + PARITY_E parity); + + /** + * Writes size bytes to serial port + * @param offset Not used with serial ports + * @param size Number of bytes to be written + * @param buffer The data to be written + * @return Number of bytes actualy written or -1 in case of an error + */ + int32 writeData(uint32 offset, uint32 size, const char* buffer) override; + + void irq_handler(); + + /** + * Returns the Architecture specific data for this serial port. + * The basic task of any operating system is to hide this ugly data. + * This function is mainly called from SerialManager and I do not think + * that it is needed anywhere else. Perhaps it could be protected and + * SerialManager declared as a friend class. + * @return @see ArchSerialInfo + */ + [[nodiscard]] ArchSerialInfo get_info() const { return port_info_; } + + static void write_UART(uint16_t base_port, SerialPortRegister reg, uint8 what); + static uint8 read_UART(uint16_t base_port, SerialPortRegister reg); + +private: + void write_UART(SerialPortRegister reg, uint8 what); + uint8 read_UART(SerialPortRegister reg); + + void readReceiveBuffers(); + size_t writeTransmitBuffer(const char* buffer, size_t size); size_t WriteLock; size_t SerialLock; private: ArchSerialInfo port_info_; - }; -class SerialManager +class SerialManager : public BasicDeviceDriver, + public Driver { - public: - static SerialManager *instance_; +public: + SerialManager(); + ~SerialManager() override = default; - static SerialManager * getInstance() + static SerialManager& instance() { - if (!instance_) - instance_ = new SerialManager(); - - return instance_; + static SerialManager i; + return i; } - ; - SerialManager(); - ~SerialManager(); + static constexpr size_t MAX_PORTS = 16; - SerialPort * serial_ports[ MAX_PORTS]; + SerialPort* serial_ports[MAX_PORTS]; uint32 do_detection(uint32 is_paging_set_up); - uint32 get_num_ports(); - uint32 get_port_number(const uint8* friendly_name); + [[nodiscard]] uint32 get_num_ports() const; + [[nodiscard]] uint32 get_port_number(const uint8* friendly_name); void service_irq(uint32 irq_num); - private: + void doDeviceDetection() override; + +private: uint32 num_ports; }; - diff --git a/common/include/util/Stabs2DebugInfo.h b/common/include/util/Stabs2DebugInfo.h index 191f6dd15..7bf5d14cc 100644 --- a/common/include/util/Stabs2DebugInfo.h +++ b/common/include/util/Stabs2DebugInfo.h @@ -1,29 +1,30 @@ #pragma once -#include "umap.h" #include "arch_backtrace.h" +#include "EASTL/vector_map.h" + // The limit for function names, after that, they will get capped #define CALL_FUNC_NAME_LIMIT 256 #define CALL_FUNC_NAME_LIMIT_STR macroToString(CALL_FUNC_NAME_LIMIT) -class StabEntry; +struct StabEntry; class Stabs2DebugInfo { public: + Stabs2DebugInfo(const char* stab_begin, const char* stab_end, const char* stab_str); + virtual ~Stabs2DebugInfo(); - Stabs2DebugInfo(char const *stab_begin, char const *stab_end, char const *stab_str); - virtual ~Stabs2DebugInfo(); - - virtual void getCallNameAndLine(pointer address, const char*& mangled_name, ssize_t &line) const; - virtual void printCallInformation(pointer address) const; + virtual void + getCallNameAndLine(pointer address, const char*& mangled_name, ssize_t& line) const; + virtual void printCallInformation(pointer address) const; protected: - StabEntry const *stab_start_; - StabEntry const *stab_end_; - char const *stabstr_buffer_; + const StabEntry* stab_start_; + const StabEntry* stab_end_; + const char* stabstr_buffer_; - ustl::map function_symbols_; + eastl::vector_map function_symbols_; private: virtual void initialiseSymbolTable(); @@ -31,9 +32,9 @@ class Stabs2DebugInfo virtual pointer getFunctionName(pointer address, char function_name[], size_t size) const; ssize_t getFunctionLine(pointer start, pointer offset) const; bool tryPasteOoperator(const char *& input, char *& buffer, size_t& size) const; - int readNumber(const char *& input) const; + static int readNumber(const char *& input); void pasteTypename(const char *& input, char *& buffer, size_t& size) const; void pasteArguments(const char *& input, char *& buffer, char delimiter, size_t& size) const; void demangleName(const char* name, char *buffer, size_t size) const; - size_t putChar2Buffer(char*& buffer, char c, size_t& size) const; + static size_t putChar2Buffer(char*& buffer, char c, size_t& size); }; diff --git a/common/include/util/chardev.h b/common/include/util/chardev.h index 266d45b5e..4457a585a 100644 --- a/common/include/util/chardev.h +++ b/common/include/util/chardev.h @@ -1,9 +1,14 @@ #pragma once -#include "ustring.h" +#include "Device.h" #include "FiFo.h" +#include "File.h" +#include "Inode.h" +#include "Superblock.h" -class CharacterDevice +#include "EASTL/string.h" + +class CharacterDevice : public Device, public Inode { public: @@ -11,17 +16,31 @@ class CharacterDevice * Constructor * @param name the device name * @param super_block the superblock (0) - * @param inode_type the inode type (cahracter device) + * @param inode_type the inode type (character device) */ CharacterDevice(const char* name) : + Device(name), + Inode(nullptr, I_CHARDEVICE), in_buffer_(CD_BUFFER_SIZE, FIFO_NOBLOCK_PUT | FIFO_NOBLOCK_PUT_OVERWRITE_OLD), out_buffer_(CD_BUFFER_SIZE, FIFO_NOBLOCK_PUT | FIFO_NOBLOCK_PUT_OVERWRITE_OLD), name_(name) { } - ~CharacterDevice() + ~CharacterDevice() override = default; + + Inode* deviceInode() override { return this; } + + File* open(Dentry* dentry, uint32 flag) override { + debug(INODE, "CharacterDevice: Open file\n"); + assert(eastl::find(i_dentrys_.begin(), i_dentrys_.end(), dentry) != + i_dentrys_.end()); + + File* file = (File*)(new NoOffsetFile(this, dentry, flag)); + i_files_.push_back(file); + getSuperblock()->fileOpened(file); + return file; } /** @@ -31,7 +50,7 @@ class CharacterDevice * @param offset is never to be used, because there is no offset * in character devices, but it is defined in the Inode interface */ - virtual int32 readData(uint32 offset, uint32 size, char *buffer) + int32 readData(uint32 offset, uint32 size, char *buffer) override { if (offset) return -1; // offset reading not supprted with char devices @@ -52,7 +71,7 @@ class CharacterDevice * @param offset is never to be used, because there is no offset * in character devices, but it is defined in the Inode interface */ - virtual int32 writeData(uint32 offset, uint32 size, const char*buffer) + int32 writeData(uint32 offset, uint32 size, const char*buffer) override { if (offset) return -1; // offset writing also not supp0rted @@ -66,7 +85,7 @@ class CharacterDevice return (bptr - buffer); } - const char *getDeviceName() const + [[nodiscard]] const char *getDeviceName() const { return name_.c_str(); } @@ -77,7 +96,7 @@ class CharacterDevice FiFo in_buffer_; FiFo out_buffer_; - ustl::string name_; + eastl::string name_; void processInBuffer() { @@ -90,5 +109,3 @@ class CharacterDevice } }; - - diff --git a/common/include/util/coroutine b/common/include/util/coroutine new file mode 100644 index 000000000..40cd99a57 --- /dev/null +++ b/common/include/util/coroutine @@ -0,0 +1,3 @@ +#pragma once + +#include "coroutine.h" diff --git a/common/include/util/coroutine.h b/common/include/util/coroutine.h new file mode 100644 index 000000000..90ab76607 --- /dev/null +++ b/common/include/util/coroutine.h @@ -0,0 +1,304 @@ +#pragma once + +#include + +#include + +#include "assert.h" +#include "debug.h" + +namespace std +{ + template + struct coroutine_traits { }; + + template + requires requires { typename R::promise_type; } + struct coroutine_traits + { + using promise_type = typename R::promise_type; + }; + + + template< class Promise = void> + struct coroutine_handle; + + template<> + struct coroutine_handle + { + constexpr coroutine_handle() noexcept : coro_frame_ptr_(nullptr) { } + constexpr coroutine_handle(std::nullptr_t) noexcept : coro_frame_ptr_(nullptr) { } + + coroutine_handle(const coroutine_handle&) = default; + coroutine_handle(coroutine_handle&&) = default; + coroutine_handle& operator=(const coroutine_handle&) = default; + coroutine_handle& operator=(coroutine_handle&&) = default; + + coroutine_handle& operator=(std::nullptr_t np) noexcept + { + coro_frame_ptr_ = np; + return *this; + } + + bool done() const noexcept + { + return __builtin_coro_done(coro_frame_ptr_); + } + + void resume() + { + __builtin_coro_resume(coro_frame_ptr_); + } + + void operator()() + { + resume(); + } + + void destroy() + { + __builtin_coro_destroy(coro_frame_ptr_); + coro_frame_ptr_ = nullptr; + } + + explicit operator bool() const noexcept + { + return bool(address()); + } + + constexpr void* address() const noexcept + { + return coro_frame_ptr_; + } + + constexpr static coroutine_handle from_address(void* address) noexcept + { + return {address}; + } + + friend auto operator<=>(const coroutine_handle&, const coroutine_handle&) = default; + protected: + constexpr coroutine_handle(void* addr) : coro_frame_ptr_(addr) { } + + void* coro_frame_ptr_ = nullptr; + }; + + template + struct coroutine_handle : coroutine_handle + { + constexpr coroutine_handle() noexcept { } + constexpr coroutine_handle(std::nullptr_t) noexcept { } + + coroutine_handle(const coroutine_handle&) = default; + coroutine_handle(coroutine_handle&&) = default; + coroutine_handle& operator=(const coroutine_handle&) = default; + coroutine_handle& operator=(coroutine_handle&&) = default; + + coroutine_handle& operator=(std::nullptr_t np) noexcept + { + coro_frame_ptr_ = np; + return *this; + } + + Promise& promise() const + { + return *static_cast(__builtin_coro_promise (coro_frame_ptr_, __alignof(Promise), false)); + } + + static coroutine_handle from_promise(Promise& promise) + { + return {__builtin_coro_promise((char*) &promise, __alignof(Promise), true)}; + } + + constexpr static coroutine_handle from_address(void* address) noexcept + { + return {address}; + } + + protected: + constexpr coroutine_handle(void* addr) : coroutine_handle(addr) { } + }; + + struct noop_coroutine_promise { }; + + template <> + struct coroutine_handle + { + constexpr operator coroutine_handle<>() const noexcept + { + return coroutine_handle<>::from_address(address()); + } + + constexpr explicit operator bool() const noexcept + { + return true; + } + + constexpr bool done() const noexcept + { + return false; + } + + void operator()() const noexcept { } + + void resume() const noexcept { } + + void destroy() const noexcept { } + + noop_coroutine_promise& promise() const noexcept + { + return dummy_noop_coro_frame_.__p; + } + + constexpr void* address() const noexcept + { + return coro_frame_ptr_; + } + + private: + friend coroutine_handle noop_coroutine() noexcept; + + struct __frame + { + static void __dummy_resume_destroy() { } + + void (*__r)() = __dummy_resume_destroy; + void (*__d)() = __dummy_resume_destroy; + struct noop_coroutine_promise __p; + }; + + static __frame dummy_noop_coro_frame_; + + explicit coroutine_handle() noexcept = default; + + void* coro_frame_ptr_ = &dummy_noop_coro_frame_; + }; + + using noop_coroutine_handle = coroutine_handle; + + inline noop_coroutine_handle noop_coroutine() noexcept + { + return noop_coroutine_handle(); + } + + + struct suspend_always + { + constexpr bool await_ready() const noexcept { return false; } + constexpr void await_suspend(std::coroutine_handle<>) noexcept { } + constexpr void await_resume() noexcept { } + }; + + struct suspend_never + { + constexpr bool await_ready() const noexcept { return true; } + constexpr void await_suspend(std::coroutine_handle<>) noexcept { } + constexpr void await_resume() noexcept { } + }; + + +}; + +template +struct eastl::hash> +{ + size_t operator()(const std::coroutine_handle& h) noexcept + { + return reinterpret_cast(h.address()); + } +}; + + +template +class generator +{ +private: + using value = T; +public: + using yielded = T; + + struct iterator; + + struct promise_type + { + generator get_return_object() { return generator(std::coroutine_handle::from_promise(*this)); } + constexpr std::suspend_always initial_suspend() noexcept { return {}; } + constexpr std::suspend_always final_suspend() noexcept { return {}; } + + constexpr void return_void() const noexcept {} + std::suspend_always yield_value(const yielded& val) + { + value_ = &val; + return {}; + } + + void await_transform() = delete; + void unhanded_exception() {} + + private: + const T* value_ = nullptr; + friend iterator; + }; + + struct iterator : eastl::iterator + { + struct sentinel_t {}; + + iterator(const std::coroutine_handle& h) : coroutine_(h) { } + + iterator& operator++() + { + assert(coroutine_); + coroutine_.resume(); + if (coroutine_.done()) + { + coroutine_ = nullptr; + } + return *this; + } + + const T& operator*() const + { + return *coroutine_.promise().value_; + } + + bool operator==(sentinel_t) + { + return !coroutine_ || coroutine_.done(); + } + + private: + std::coroutine_handle coroutine_; + }; + + iterator begin() + { + if (coroutine_) + { + coroutine_.resume(); + } + + return iterator(coroutine_); + } + + constexpr iterator::sentinel_t end() + { + return {}; + } + + generator(const generator&) = delete; + generator(generator&& other) noexcept; + + explicit generator(std::coroutine_handle h) : coroutine_(h) { } + + ~generator() noexcept + { + if (coroutine_) + { + coroutine_.destroy(); + } + } + +private: + std::coroutine_handle coroutine_ = nullptr; +}; diff --git a/common/include/util/kstring.h b/common/include/util/kstring.h index 8f39f3467..7857e5a2c 100644 --- a/common/include/util/kstring.h +++ b/common/include/util/kstring.h @@ -2,8 +2,10 @@ #define STRING_SAVE -#include "types.h" #include "paging-definitions.h" +#include + +#include "types.h" /** * Convert a define to a string. @@ -21,26 +23,11 @@ extern "C" { #endif - size_t strlen(const char* str); - void *memcpy(void *dest, const void *src, size_t length); - void *memmove(void *dest, const void *src, size_t length); void *memccpy(void *dest, const void *src, uint8 c, size_t length); - void *memset(void *block, uint8 c, size_t size); - char *strcpy(char *dest, const char* src); - char *strncpy(char *dest, const char* src, size_t size); char *strdup(const char *src); - char *strcat(char *dest, const char*append); - char *strncat(char *dest, const char*append, size_t size); size_t strlcat(char *dest, const char*append, size_t size); - int32 memcmp(const void *region1, const void *region2, size_t size); - int32 strcmp(const char *str1, const char *str2); - int32 strncmp(const char *str1, const char *str2, size_t n); void *memnotchr(const void *block, uint8 c, size_t size); - void *memchr(const void *block, uint8 c, size_t size); void *memrchr(const void *block, uint8 c, size_t size); - char *strchr(const char* str, char c); - char *strrchr(const char* str, char c); - char* strtok(char* str, const char* delimiters); char* itoa(int value, char * str, int base); /** @@ -57,9 +44,8 @@ extern "C" /** * Computes a CRC- like checksum */ - uint32 checksum(uint32* src, uint32 nbytes); + uint32 checksum(const uint32* src, uint32 nbytes); #ifdef __cplusplus } #endif - diff --git a/common/include/util/libc++.h b/common/include/util/libc++.h new file mode 100644 index 000000000..02cbc964f --- /dev/null +++ b/common/include/util/libc++.h @@ -0,0 +1,5 @@ +#pragma once + +void _preinit(); +void globalConstructors(); +void globalDestructors(); diff --git a/common/include/util/libgcc.h b/common/include/util/libgcc.h index 3a249b197..b52b94377 100644 --- a/common/include/util/libgcc.h +++ b/common/include/util/libgcc.h @@ -1,9 +1,9 @@ #pragma once -#include "types.h" +#include -extern "C" int64 __divdi3(int64 num, int64 den); -extern "C" uint64 __udivdi3(uint64 num, uint64 den); -extern "C" uint64 __umoddi3(uint64 a, uint64 b); -extern "C" uint64 __udivmoddi4(uint64 num, uint64 den, uint64 *rem_p); +extern "C" int64_t __divdi3(int64_t num, int64_t den); +extern "C" uint64_t __udivdi3(uint64_t num, uint64_t den); +extern "C" uint64_t __umoddi3(uint64_t a, uint64_t b); +extern "C" uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem_p); diff --git a/common/include/util/qsort.h b/common/include/util/qsort.h index 704fd8c2f..3c0f98d09 100644 --- a/common/include/util/qsort.h +++ b/common/include/util/qsort.h @@ -1,5 +1,5 @@ #pragma once -#include "types.h" +#include void qsort(void* base, size_t nitems, size_t size, int (*compar)(const void *, const void*)); diff --git a/common/include/util/ranges/ranges.h b/common/include/util/ranges/ranges.h new file mode 100644 index 000000000..fed6bc96a --- /dev/null +++ b/common/include/util/ranges/ranges.h @@ -0,0 +1,291 @@ +#pragma once + +#include "EASTL/iterator.h" +#include "EASTL/memory.h" +#include "EASTL/type_traits.h" + +namespace ranges +{ + + +struct dangling +{ + constexpr dangling() noexcept = default; + template constexpr dangling(Args&&...) noexcept {} +}; + +template +concept __member_begin = requires(_Tp& __t) +{ + __t.begin(); +}; + +template +concept __adl_begin = requires(_Tp& __t) +{ + eastl::begin(__t); +}; + +template +requires eastl::is_array_v<_Tp> || __member_begin<_Tp&> || __adl_begin<_Tp&> +auto begin(_Tp& __t) +{ + // if constexpr (eastl::is_array_v<_Tp>) + // return __t + 0; + // else + if constexpr (__member_begin<_Tp&>) + return __t.begin(); + else + return eastl::begin(__t); +} + +template +requires (eastl::is_array_v<_Tp> || __member_begin<_Tp&> || __adl_begin<_Tp&>) +auto end(_Tp& __t) +{ + // if constexpr (eastl::is_array_v<_Tp>) + // return __t + 0; + // else + if constexpr (__member_begin<_Tp&>) + return __t.end(); + else + return eastl::end(__t); +} + +template +concept range = requires(T& t) +{ + ranges::begin(t); + ranges::end(t); +}; + +template using iterator_t = decltype(ranges::begin(eastl::declval())); +template using sentinel_t = decltype(ranges::end(eastl::declval())); + +template using iter_reference_t = decltype(*eastl::declval()); +template +using iter_difference_t = typename eastl::iterator_traits>::difference_type; + +template +using range_reference_t = iter_reference_t>; + +template +using range_difference_t = iter_difference_t>; + +template class subrange +{ +private: + I iterator_; + S sentinel_; + +public: + constexpr subrange(I iterator, S sentinel) : + iterator_(eastl::move(iterator)), + sentinel_(sentinel) + { + } + + template + constexpr subrange(R&& range) : + subrange(eastl::begin(range), eastl::end(range)) + { + } + + constexpr auto begin() const { return iterator_; } + + constexpr auto end() const { return sentinel_; } + + constexpr bool empty() { return iterator_ == sentinel_; } + + // Enable structured decomposition + template + + requires(N < 2) constexpr auto get() const + { + if constexpr (N == 0) + return begin(); + else + return end(); + } +}; + + +template inline constexpr bool enable_borrowed_range = false; + +template +concept borrowed_range = range && + (eastl::is_lvalue_reference_v || enable_borrowed_range>); + + +template +using borrowed_iterator_t = eastl::conditional_t, + iterator_t, + dangling>; + +template +using borrowed_subrange_t = + eastl::conditional_t, + subrange>, + dangling>; + +template subrange(R&& r) -> subrange, sentinel_t>; + +template +inline constexpr bool enable_borrowed_range> = true; + +namespace detail +{ + template + concept SameHelper = eastl::is_same_v; +} + +template +concept same_as = detail::SameHelper && detail::SameHelper; + +template +concept common_range = + ranges::range && same_as, ranges::sentinel_t>; + +template class owning_view +{ +private: + R r_; + +public: + owning_view() = default; + owning_view(owning_view&& other) = default; + constexpr owning_view(R&& t) : + r_(eastl::move(t)) + { + } + owning_view(const owning_view&) = delete; + owning_view& operator=(owning_view&& other) = default; + owning_view& operator=(const owning_view&) = delete; + + constexpr R& base() & noexcept { return r_; } + constexpr const R& base() const & noexcept { return r_; } + constexpr R&& base() && noexcept { return eastl::move(r_); } + constexpr const R&& base() const && noexcept { return eastl::move(r_); } + + constexpr iterator_t begin() { return ranges::begin(r_); } + constexpr auto begin() const requires range { return ranges::begin(r_); } + + constexpr sentinel_t end() { return ranges::end(r_); } + constexpr auto end() const requires range { return ranges::end(r_); } + }; + + template owning_view(R&&) -> owning_view; + + template + class ref_view + { + private: + R* r_; + public: + ref_view() = default; + ref_view(ref_view&& other) = default; + constexpr ref_view(R&& t) : + r_(eastl::addressof(static_cast(eastl::forward(t)))) + { + } + ref_view(const ref_view&) = delete; + ref_view& operator=(ref_view&& other) = default; + ref_view& operator=(const ref_view&) = delete; + + constexpr R& base() const { return *r_; } + + constexpr iterator_t begin() { return ranges::begin(*r_); } + + constexpr sentinel_t end() { return ranges::end(*r_); } + }; + + template ref_view(R&) -> ref_view; + + // template + // using all = eastl::conditional_t, + // ref_view, + // owning_view>; + // using all = eastl::conditional_t, + // decltype(ref_view{R}) + // ref_view())>, + // owning_view())>>; + + template + concept __can_ref_view = requires + { + ref_view{eastl::declval<_Range>()}; + }; + + template + concept __can_owning_view = requires + { + owning_view{eastl::declval<_Range>()}; + }; + + struct all + { + template constexpr auto operator()(R&& r) const + { + if constexpr (__can_ref_view) + { + return ref_view{eastl::forward(r)}; + } + else + { + return owning_view{eastl::forward(r)}; + } + } + }; + + template + using all_t = decltype(ranges::all(eastl::declval())); +} // namespace ranges + +namespace std +{ + template + struct tuple_size> : eastl::integral_constant + { + }; + + template struct tuple_element<0, ranges::subrange> + { + using type = I; + }; + + template struct tuple_element<0, const ranges::subrange> + { + using type = I; + }; + + template struct tuple_element<1, ranges::subrange> + { + using type = S; + }; + + template struct tuple_element<1, const ranges::subrange> + { + using type = S; + }; +}; // namespace std + +template +concept convertible_to = eastl::is_convertible_v && requires +{ + static_cast(eastl::declval()); +}; + +template +concept destructible = eastl::is_nothrow_destructible_v; + +template +concept constructible_from = destructible && eastl::is_constructible_v; + +template +concept move_constructible = constructible_from && convertible_to; + +template +concept copy_constructible = move_constructible && + constructible_from && convertible_to && + constructible_from && convertible_to && + constructible_from && convertible_to; diff --git a/common/include/util/ranges/reverse_view.h b/common/include/util/ranges/reverse_view.h new file mode 100644 index 000000000..c2ada9511 --- /dev/null +++ b/common/include/util/ranges/reverse_view.h @@ -0,0 +1,50 @@ +#pragma once + +#include "EASTL/iterator.h" + +template class reverse_view +{ +public: + reverse_view(const T& source_view) : + source_view_(source_view) + { + } + + auto begin() + { + if constexpr (requires { source_view_.rbegin(); }) + { + return base().rbegin(); + } + else + { + auto it = base().begin(); + auto end_it = base().end(); + while (it != end_it) + { + ++it; + } + return eastl::reverse_iterator(it); + } + } + + auto end() + { + if constexpr (requires { base().rend(); }) + { + return source_view_.rend(); + } + else + { + return eastl::reverse_iterator(base().begin()); + } + } + + auto base() + { + return source_view_; + } + +private: + T source_view_; +}; diff --git a/common/include/util/ranges/transform.h b/common/include/util/ranges/transform.h new file mode 100644 index 000000000..b8cf79092 --- /dev/null +++ b/common/include/util/ranges/transform.h @@ -0,0 +1,177 @@ +#pragma once + +#include "ranges.h" + +#include "EASTL/functional.h" +#include "EASTL/iterator.h" +#include "EASTL/type_traits.h" +#include "EASTL/utility.h" + +#include "debug.h" + +namespace ranges +{ + + template class transform_view + { + private: + using base_iterator_t = iterator_t; + using base_sentinel_t = sentinel_t; + + template struct iterator + { + using parent_type = eastl:: + conditional_t, transform_view>; + using base_type = eastl::conditional_t; + + using value_type = eastl::remove_cvref_t< + eastl::invoke_result_t>>; + + using difference_type = ranges::range_difference_t; + + using iterator_category = eastl::conditional_t< + eastl::is_lvalue_reference_v< + eastl::invoke_result_t>>, + typename eastl::iterator_traits>::iterator_category, + eastl::input_iterator_tag>; + + using pointer = void; + // using reference = iter_reference_t; + using reference = value_type&; + + constexpr iterator() = default; + + constexpr iterator(parent_type& parent, + ranges::iterator_t current) : + parent_(eastl::addressof(parent)), + current_(eastl::move(current)) + { + } + + constexpr iterator(iterator i) requires Const + && convertible_to, ranges::iterator_t> + : parent_(eastl::move(i.parent_)), current_(eastl::move(i.current_)) + { + } + + constexpr const ranges::iterator_t& base() const & noexcept + { + return current_; + } + + constexpr auto operator*() const + { + return eastl::invoke(parent_->func_, *current_); + } + + constexpr iterator& operator++() + { + ++current_; + return *this; + } + + constexpr iterator operator++(int) + { + auto tmp = *this; + ++(*this); + return tmp; + } + + constexpr iterator& operator--() + { + --current_; + return *this; + } + + constexpr iterator operator--(int) + { + auto tmp = *this; + --(*this); + return tmp; + } + + friend constexpr bool operator==(iterator lhs, iterator rhs) + { + return lhs.current_ == rhs.current_; + } + + private: + parent_type* parent_; + ranges::iterator_t current_; + }; + + template struct sentinel + { + using parent_type = eastl:: + conditional, transform_view>; + using base_type = eastl::conditional; + + sentinel() = default; + + constexpr explicit sentinel(ranges::sentinel_t end) : + end_(eastl::move(end)) + { + } + + constexpr sentinel(sentinel i) requires Const + : end_(eastl::move(i.end_)) + { + } + + constexpr ranges::sentinel_t base() const { return end_; } + + friend constexpr bool operator==(const iterator& x, const sentinel& y) + { + return x.current_ == y.end_; + } + + sentinel_t end_; + }; + + public: + transform_view(V base, F func) : + base_(eastl::forward(base)), + func_(eastl::forward(func)) + { + } + + V base() const & requires copy_constructible { return base_; } + + V base() && { return eastl::move(base_); } + + constexpr iterator begin() + { + return iterator{*this, ranges::begin(base_)}; + } + + constexpr iterator begin() const requires ranges::range + { + return iterator{*this, ranges::begin(base_)}; + } + + constexpr sentinel end() { return sentinel{ranges::end(base_)}; } + + constexpr iterator end() requires ranges::common_range + { + return iterator{*this, ranges::end(base_)}; + } + + constexpr sentinel end() const requires ranges::range + { + return sentinel{ranges::end(base_)}; + } + + constexpr iterator end() const requires ranges::common_range + { + return iterator{*this, ranges::end(base_)}; + } + + private: + V base_; + F func_; + }; + + + template + transform_view(V&& v, F f)->transform_view, F>; +} // namespace ranges diff --git a/common/include/util/source_location.h b/common/include/util/source_location.h new file mode 100644 index 000000000..7760202f6 --- /dev/null +++ b/common/include/util/source_location.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +class source_location +{ +public: + source_location(const source_location&) = default; + source_location(source_location&&) = default; + + static constexpr source_location current( + const char* file_name = __builtin_FILE(), + const char* function_name = __builtin_FUNCTION(), + const uint_least32_t line_number = __builtin_LINE(), + const uint_least32_t column_offset = 0) noexcept + { + return source_location(file_name, function_name, line_number, column_offset); + } + + [[nodiscard]] constexpr const char* file_name() const noexcept + { + return file_name_; + } + + [[nodiscard]] constexpr const char* function_name() const noexcept + { + return function_name_; + } + + [[nodiscard]] constexpr uint_least32_t line() const noexcept + { + return line_number_; + } + + [[nodiscard]] constexpr uint_least32_t column() const noexcept + { + return column_offset_; + } + +private: + constexpr source_location(const char* file_name, const char* function_name, const uint_least32_t line_number, const uint_least32_t column_offset) noexcept : + file_name_(file_name), + function_name_(function_name), + line_number_(line_number), + column_offset_(column_offset) + { + } + + const char* file_name_; + const char* function_name_; + const uint_least32_t line_number_; + const uint_least32_t column_offset_; +}; diff --git a/common/source/CMakeLists.txt b/common/source/CMakeLists.txt index 0ca12ad49..317362c02 100644 --- a/common/source/CMakeLists.txt +++ b/common/source/CMakeLists.txt @@ -3,4 +3,5 @@ add_subdirectory(fs) add_subdirectory(kernel) add_subdirectory(mm) add_subdirectory(util) -add_subdirectory(ustl) +add_subdirectory(klibc) +add_subdirectory(drivers) diff --git a/common/source/console/CMakeLists.txt b/common/source/console/CMakeLists.txt index 5d173b76e..dc46f243c 100644 --- a/common/source/console/CMakeLists.txt +++ b/common/source/console/CMakeLists.txt @@ -1,3 +1,4 @@ -include_directories(../../include/console) +add_project_library(common_console) -add_project_library(common_console) \ No newline at end of file +target_include_directories(kernel PUBLIC + ../../include/console) diff --git a/common/source/console/Console.cpp b/common/source/console/Console.cpp index d124ed47b..5d30347c6 100644 --- a/common/source/console/Console.cpp +++ b/common/source/console/Console.cpp @@ -1,16 +1,23 @@ -#include #include "Console.h" -#include "Terminal.h" + #include "KeyboardManager.h" -#include "Scheduler.h" #include "PageManager.h" +#include "Scheduler.h" +#include "ScopeLock.h" +#include "Terminal.h" #include "backtrace.h" +#include + +#include "ArchMulticore.h" + +#include "debug.h" Console* main_console; -Console::Console(uint32, const char* name) : Thread(0, name, Thread::KERNEL_THREAD), console_lock_("Console::console_lock_"), +Console::Console([[maybe_unused]] uint32 num_terminals, const char* name) : Thread(nullptr, name, Thread::KERNEL_THREAD), console_lock_("Console::console_lock_"), set_active_lock_("Console::set_active_state_lock_"), locked_for_drawing_(0), active_terminal_(0) { + debug(CONSOLE, "Created console at [%p, %p)\n", this, (char*)this + sizeof(*this)); } void Console::lockConsoleForDrawing() @@ -27,28 +34,43 @@ void Console::unLockConsoleForDrawing() void Console::handleKey(uint32 key) { - KeyboardManager * km = KeyboardManager::instance(); - - // the keycode for F1 is 0x80, while F2..F12 have keycodes 0x82..0x8e - uint32 terminal_selected = 0; - if (key == KEY_F1) - terminal_selected = 0; - else - terminal_selected = key - (KEY_F2 - 1); + KeyboardManager * km = &KeyboardManager::instance(); if (km->isShift()) { - if (terminal_selected < getNumTerminals()) - setActiveTerminal(terminal_selected); + // the keycode for F1 is 0x80, while F2..F12 have keycodes 0x82..0x8e + uint32 terminal_selected = 0; + if (key == KEY_F1) + terminal_selected = 0; + else + terminal_selected = key - (KEY_F2 - 1); - return; + if (terminal_selected < getNumTerminals()) + setActiveTerminal(terminal_selected); + + return; } -// else... switch (key) { + case KEY_F7: + + ArchMulticore::stopOtherCpus(); + Scheduler::instance()->printThreadList(); + Scheduler::instance()->printStackTraces(); + Scheduler::instance()->printLockingInformation(); + for(auto* t : Scheduler::instance()->threads_) + { + kprintfd("Thread %p = %s\n", t, t->getName()); + ArchThreads::printThreadRegisters(t, true); + } + assert(false && "F7 pressed"); + break; + case KEY_F8: + debug_print_to_fb = !debug_print_to_fb; + break; case KEY_F9: - PageManager::instance()->printBitmap(); + PageManager::instance().printUsageInfo(); kprintfd("Used kernel memory: %zu\n", KernelMemoryManager::instance()->getUsedKernelMemory(true)); break; @@ -75,33 +97,34 @@ uint32 Console::getNumTerminals() const return terminals_.size(); } -Terminal *Console::getActiveTerminal() +Terminal *Console::getActiveTerminal() const { + assert(active_terminal_ < terminals_.size()); return terminals_[active_terminal_]; // why not locked? integer read is consistent anyway } -Terminal *Console::getTerminal(uint32 term) +Terminal *Console::getTerminal(uint32 term) const { + assert(term < terminals_.size()); return terminals_[term]; } void Console::setActiveTerminal(uint32 term) { - set_active_lock_.acquire(); + ScopeLock l(set_active_lock_); - Terminal *t = terminals_[active_terminal_]; + assert(term < terminals_.size()); + Terminal* t = terminals_[active_terminal_]; t->unSetAsActiveTerminal(); t = terminals_[term]; t->setAsActiveTerminal(); active_terminal_ = term; - - set_active_lock_.release(); } -void Console::Run(void) +void Console::Run() { - KeyboardManager * km = KeyboardManager::instance(); + KeyboardManager * km = &KeyboardManager::instance(); uint32 key; do { @@ -125,7 +148,7 @@ void Console::Run(void) Scheduler::instance()->yield(); } while (1); } -bool Console::isDisplayable(uint32 key) +bool Console::isDisplayable(uint32 key) const { return (((key & 127) >= ' ') || (key == '\n') || (key == '\b')); } diff --git a/common/source/console/FrameBufferConsole.cpp b/common/source/console/FrameBufferConsole.cpp index e548821b3..86bceeeee 100644 --- a/common/source/console/FrameBufferConsole.cpp +++ b/common/source/console/FrameBufferConsole.cpp @@ -1,11 +1,13 @@ #include "FrameBufferConsole.h" -#include "ArchCommon.h" -#include "Terminal.h" + #include "KeyboardManager.h" -#include "kprintf.h" #include "Scheduler.h" +#include "Terminal.h" +#include "kprintf.h" #include "kstring.h" +#include "ArchCommon.h" + FrameBufferConsole::FrameBufferConsole(uint32 num_terminals) : Console(num_terminals, "VESAConsoleThread") { @@ -74,8 +76,10 @@ void FrameBufferConsole::setPixel(uint32 x, uint32 y, uint8 r, uint8 g, uint8 b) lfb[offset] = color; } -uint32 FrameBufferConsole::consoleSetCharacter(uint32 const &row, uint32 const&column, uint8 const &character, - uint8 const &state) +uint32 FrameBufferConsole::consoleSetCharacter(const uint32& row, + const uint32& column, + const uint8& character, + const uint8& state) { CONSOLECOLOR fg, bg; colorsFromState(state, fg, bg); @@ -111,7 +115,7 @@ uint32 FrameBufferConsole::consoleSetCharacter(uint32 const &row, uint32 const&c return 0; } -void FrameBufferConsole::consoleScrollUp(uint8 const &state) +void FrameBufferConsole::consoleScrollUp(const uint8& state) { CONSOLECOLOR fg, bg; colorsFromState(state, fg, bg); @@ -133,52 +137,52 @@ uint16 FrameBufferConsole::convertConsoleColor(CONSOLECOLOR color) uint8 r = 0, g = 0, b = 0; switch(color) { - case BLACK: + case CONSOLECOLOR::BLACK: r = 0; g = 0; b = 0; break; - case BLUE: + case CONSOLECOLOR::BLUE: r = 0; g = 0; b = 255; break; - case GREEN: + case CONSOLECOLOR::GREEN: r = 0; g = 200; b = 0; break; - case CYAN: + case CONSOLECOLOR::CYAN: r = 0; g = 255; b = 255; break; - case RED: + case CONSOLECOLOR::RED: r = 255; g = 0; b = 0; break; - case MAGENTA: + case CONSOLECOLOR::MAGENTA: r = 255; g = 0; b = 255; break; - case BROWN: + case CONSOLECOLOR::BROWN: r = 165; g = 42; b = 42; break; - case WHITE: + case CONSOLECOLOR::WHITE: r = 245; g = 245; b = 245; break; - case DARK_GREY: + case CONSOLECOLOR::DARK_GREY: r = 169; g = 169; b = 169; break; - case BRIGHT_BLUE: + case CONSOLECOLOR::BRIGHT_BLUE: r = 144, g = 144; b = 238; break; - case BRIGHT_GREEN: + case CONSOLECOLOR::BRIGHT_GREEN: r = 144; g = 238; b = 144; break; - case BRIGHT_CYAN: + case CONSOLECOLOR::BRIGHT_CYAN: r = 224; g = 255; b = 255; break; - case PINK: + case CONSOLECOLOR::PINK: r = 255; g = 192; b = 203; break; - case BRIGHT_MAGENTA: + case CONSOLECOLOR::BRIGHT_MAGENTA: r = 255; g = 100; b = 255; break; - case YELLOW: + case CONSOLECOLOR::YELLOW: r = 255; g = 255; b = 0; break; - case BRIGHT_WHITE: + case CONSOLECOLOR::BRIGHT_WHITE: r = 255; g = 255; b = 255; break; } @@ -188,7 +192,7 @@ uint16 FrameBufferConsole::convertConsoleColor(CONSOLECOLOR color) return scaled_b | (scaled_g << 5) | (scaled_r << 11); } -void FrameBufferConsole::colorsFromState(uint8 const &state, CONSOLECOLOR &fg, CONSOLECOLOR &bg) +void FrameBufferConsole::colorsFromState(const uint8& state, CONSOLECOLOR& fg, CONSOLECOLOR& bg) { fg = (CONSOLECOLOR) (state & 15); bg = (CONSOLECOLOR) ((state & 240) >> 4); diff --git a/common/source/console/KprintfFlushingThread.cpp b/common/source/console/KprintfFlushingThread.cpp new file mode 100644 index 000000000..5a088aabd --- /dev/null +++ b/common/source/console/KprintfFlushingThread.cpp @@ -0,0 +1,37 @@ +#include "KprintfFlushingThread.h" + +#include "Console.h" +#include "RingBuffer.h" +#include "Scheduler.h" +#include "Terminal.h" +#include "Thread.h" +#include "kprintf.h" + +#include "ArchInterrupts.h" + +void flushActiveConsole(RingBuffer* rb) +{ + assert(main_console); + assert(rb); + assert(ArchInterrupts::testIFSet()); + char c = 0; + while (rb->get(c)) + { + main_console->getActiveTerminal()->write(c); + } + Scheduler::instance()->yield(); +} + +KprintfFlushingThread::KprintfFlushingThread(RingBuffer* rb) : + Thread(nullptr, "KprintfFlushingThread", Thread::KERNEL_THREAD), + rb(rb) +{ +} + +void KprintfFlushingThread::Run() +{ + while (true) + { + flushActiveConsole(rb); + } +} diff --git a/common/source/console/Terminal.cpp b/common/source/console/Terminal.cpp index fe8139b3e..b10ecd864 100644 --- a/common/source/console/Terminal.cpp +++ b/common/source/console/Terminal.cpp @@ -1,17 +1,20 @@ #include "Terminal.h" -#include "Console.h" +#include "Console.h" #include "KeyboardManager.h" - #include "kprintf.h" +#include "debug.h" + Terminal::Terminal(char *name, Console *console, uint32 num_columns, uint32 num_rows) : CharacterDevice(name), console_(console), num_columns_(num_columns), num_rows_(num_rows), len_( num_rows * num_columns), current_column_(0), current_state_(0x93), active_(0), mutex_("Terminal::mutex_"), layout_( EN) { characters_ = new uint8[len_]; + debug(TERMINAL, "Created characters_ at [%p, %p)\n", characters_, (uint8*)characters_ + len_); character_states_ = new uint8[len_]; + debug(TERMINAL, "Created characters_states_ at [%p, %p)\n", character_states_, (uint8*)character_states_ + len_); uint32 i; for (i = 0; i < len_; ++i) @@ -27,9 +30,9 @@ void Terminal::clearBuffer() in_buffer_.clear(); } -void Terminal::putInBuffer(uint32 what) +void Terminal::putInBuffer(uint32 key) { - in_buffer_.put(what); + in_buffer_.put(key); } char Terminal::read() @@ -37,7 +40,7 @@ char Terminal::read() return (char) in_buffer_.get(); } -void Terminal::backspace(void) +void Terminal::backspace() { if (in_buffer_.countElementsAhead()) in_buffer_.get(); @@ -99,9 +102,9 @@ void Terminal::write(char character) console_->lockConsoleForDrawing(); writeInternal(character); console_->unLockConsoleForDrawing(); - } -void Terminal::writeString(char const *string) + +void Terminal::writeString(const char* string) { ScopeLock lock(mutex_); console_->lockConsoleForDrawing(); @@ -122,7 +125,7 @@ int32 Terminal::writeData(uint32 offset, uint32 size, const char*buffer) return size; } -void Terminal::writeBuffer(char const *buffer, size_t len) +void Terminal::writeBuffer(const char* buffer, size_t len) { ScopeLock lock(mutex_); console_->lockConsoleForDrawing(); @@ -166,7 +169,7 @@ uint32 Terminal::setCharacter(uint32 row, uint32 column, uint8 character) return 0; } -void Terminal::setForegroundColor(Console::CONSOLECOLOR const &color) +void Terminal::setForegroundColor(CONSOLECOLOR const &color) { ScopeLock lock(mutex_); // 4 bit set == 1+2+4+8, shifted by 0 bits @@ -175,7 +178,7 @@ void Terminal::setForegroundColor(Console::CONSOLECOLOR const &color) current_state_ |= color; } -void Terminal::setBackgroundColor(Console::CONSOLECOLOR const &color) +void Terminal::setBackgroundColor(CONSOLECOLOR const &color) { ScopeLock lock(mutex_); // 4 bit set == 1+2+4+8, shifted by 4 bits @@ -185,7 +188,7 @@ void Terminal::setBackgroundColor(Console::CONSOLECOLOR const &color) current_state_ |= col << 4; } -void Terminal::initTerminalColors(Console::CONSOLECOLOR fg, Console::CONSOLECOLOR bg) +void Terminal::initTerminalColors(CONSOLECOLOR fg, CONSOLECOLOR bg) { setForegroundColor(fg); setBackgroundColor(bg); @@ -252,7 +255,7 @@ uint32 Terminal::remap(uint32 key) ')', '!', '@', '#', '$', '%', '^', '&', '*', '(' }; - KeyboardManager * km = KeyboardManager::instance(); + KeyboardManager * km = &KeyboardManager::instance(); if (isLetter(key)) { diff --git a/common/source/console/TextConsole.cpp b/common/source/console/TextConsole.cpp index ff9bc8578..bebe93047 100644 --- a/common/source/console/TextConsole.cpp +++ b/common/source/console/TextConsole.cpp @@ -1,14 +1,16 @@ #include "TextConsole.h" -#include "Terminal.h" -#include "ArchCommon.h" -#include "panic.h" - -#include "Scheduler.h" #include "KeyboardManager.h" +#include "Scheduler.h" +#include "Terminal.h" #include "kprintf.h" #include "kstring.h" +#include "ArchCommon.h" + +#include "assert.h" +#include "debug.h" + TextConsole::TextConsole(uint32 num_terminals) : Console(num_terminals, "TxTConsoleThread") { @@ -36,7 +38,9 @@ TextConsole::TextConsole(uint32 num_terminals) : cterm -= ((cterm / k) * k); } + debug(CONSOLE, "Creating Terminal\n"); Terminal *term = new Terminal(term_name, this, consoleGetNumColumns(), consoleGetNumRows()); + debug(CONSOLE, "Created Terminal at [%p, %p)\n", term, (char*)term + sizeof(*term)); terminals_.push_back(term); } @@ -63,25 +67,33 @@ void TextConsole::consoleClearScreen() } } -uint32 TextConsole::consoleSetCharacter(uint32 const &row, uint32 const&column, uint8 const &character, - uint8 const &state) +uint32 TextConsole::consoleSetCharacter(const uint32& row, + const uint32& column, + const uint8& character, + const uint8& state) { char *fb = (char*) ArchCommon::getFBPtr(); - fb[(column + row * consoleGetNumColumns()) * 2] = character; - fb[(column + row * consoleGetNumColumns()) * 2 + 1] = state; + uint32_t console_columns = consoleGetNumColumns(); + uint32_t console_rows = consoleGetNumRows(); + assert(column < console_columns); + assert(row < console_rows); + fb[(column + row * console_columns) * 2] = character; + fb[(column + row * console_columns) * 2 + 1] = state; return 0; } -#define STAT_ROWS (1) +#define STAT_ROWS (2) -void TextConsole::consoleScrollUp(uint8 const &state) +void TextConsole::consoleScrollUp(const uint8& state) { char* fb = (char*) ArchCommon::getFBPtr(); - memmove((void*) (fb + (consoleGetNumColumns() * 2 * STAT_ROWS)), - (void*) (fb + (consoleGetNumColumns() * 2 * (STAT_ROWS + 1))), - (consoleGetNumRows() - 1 + STAT_ROWS) * consoleGetNumColumns() * 2); + char* dst = (char*) (fb + (consoleGetNumColumns() * 2 * STAT_ROWS)); + char* src = (char*) (fb + (consoleGetNumColumns() * 2 * (STAT_ROWS + 1))); + size_t size = (consoleGetNumRows() - STAT_ROWS - 1) * consoleGetNumColumns() * 2; + memmove(dst, src, size); + for(size_t i = 0; i < consoleGetNumColumns(); i++) { fb[(i + (consoleGetNumRows() - 1) * consoleGetNumColumns()) * 2] = ' '; diff --git a/common/source/console/debug.cpp b/common/source/console/debug.cpp new file mode 100644 index 000000000..fcc58a5a3 --- /dev/null +++ b/common/source/console/debug.cpp @@ -0,0 +1,3 @@ +#include "debug.h" + +bool debug_print_to_fb = DEBUG_TO_FB; diff --git a/common/source/console/kprintf.cpp b/common/source/console/kprintf.cpp index 40b7823de..79093938e 100644 --- a/common/source/console/kprintf.cpp +++ b/common/source/console/kprintf.cpp @@ -1,92 +1,223 @@ -#include "stdarg.h" #include "kprintf.h" + +#include "BasicSpinLock.h" #include "Console.h" +#include "KprintfFlushingThread.h" +#include "RingBuffer.h" +#include "Scheduler.h" +#include "SystemState.h" #include "Terminal.h" #include "debug_bochs.h" +#include "vsnprintf.h" + +#include "ArchCommon.h" +#include "ArchCpuLocalStorage.h" #include "ArchInterrupts.h" -#include "RingBuffer.h" -#include "Scheduler.h" +#include "ArchMulticore.h" + +#include "stdarg.h" + #include "assert.h" #include "debug.h" -#include "ustringformat.h" -//it's more important to keep the messages that led to an error, instead of -//the ones following it, when the nosleep buffer gets full +// it's more important to keep the messages that led to an error, instead of +// the ones following it, when the nosleep buffer gets full + +RingBuffer* nosleep_rb_; +Thread* flush_thread_; + +static uint8 fb_row = 0; +static uint8 fb_col = 0; +static bool kprintf_initialized = false; + +static void setFBrow(uint8 row) +{ + fb_row = row; +} + +static void setFBcol(uint8 col) +{ + fb_col = col; +} + +static uint8 getFBrow() +{ + return fb_row; +} + +static uint8 getFBcol() +{ + return fb_col; +} -RingBuffer *nosleep_rb_; -Thread *flush_thread_; +static uint8 getNextFBrow() +{ + return (getFBrow() == 24 ? 0 : getFBrow() + 1); +} + +static char* getFBAddr(uint8 row, uint8 col, bool is_paging_set_up) +{ + return (char*)ArchCommon::getFBPtr(is_paging_set_up) + ((row * 80 + col) * 2); +} -void flushActiveConsole() +static void clearFBrow(uint8 row, bool is_paging_set_up) { - assert(main_console); - assert(nosleep_rb_); - assert(ArchInterrupts::testIFSet()); - char c = 0; - while (nosleep_rb_->get(c)) - { - main_console->getActiveTerminal()->write(c); - } - Scheduler::instance()->yield(); + memset(getFBAddr(row, 0, is_paging_set_up), 0, 80 * 2); } -class KprintfFlushingThread : public Thread +static void FBnewline(bool is_paging_set_up) { - public: + uint8 next_row = getNextFBrow(); + clearFBrow(next_row, is_paging_set_up); + setFBrow(next_row); + setFBcol(0); +} - KprintfFlushingThread() : Thread(0, "KprintfFlushingThread", Thread::KERNEL_THREAD) +static void kputc(const char c, bool is_paging_set_up) +{ + if (c == '\n') { + FBnewline(is_paging_set_up); } - - virtual void Run() + else { - while (true) - { - flushActiveConsole(); - } + if (getFBcol() == 80) + { + FBnewline(is_paging_set_up); + } + + uint32 row = getFBrow(); + uint32 col = getFBcol(); + + char* fb_pos = getFBAddr(row, col, is_paging_set_up); + fb_pos[0] = c; + fb_pos[1] = 0x02; + + setFBcol(getFBcol() + 1); } -}; +} void kprintf_init() { - nosleep_rb_ = new RingBuffer(1024); - debug(KPRINTF, "Adding Important kprintf Flush Thread\n"); - flush_thread_ = new KprintfFlushingThread(); - Scheduler::instance()->addNewThread(flush_thread_); + nosleep_rb_ = new RingBuffer(1024); + debug(KPRINTF, "Adding Important kprintf Flush Thread\n"); + flush_thread_ = new KprintfFlushingThread(nosleep_rb_); + Scheduler::instance()->addNewThread(flush_thread_); + kprintf_initialized = true; } -void kprintf_func(int ch, void *arg __attribute__((unused))) +void kprintf_func(int ch, void* arg __attribute__((unused))) { - //check if atomar or not in current context - if ((ArchInterrupts::testIFSet() && Scheduler::instance()->isSchedulingEnabled()) - || (main_console->areLocksFree() && main_console->getActiveTerminal()->isLockFree())) - { - main_console->getActiveTerminal()->write(ch); - } - else - { - nosleep_rb_->put(ch); - } + if (kprintf_initialized) + { + // check if atomar or not in current context + if ((system_state == RUNNING) && ArchInterrupts::testIFSet() && + Scheduler::instance()->isSchedulingEnabled()) + { + main_console->getActiveTerminal()->write(ch); + } + else + { + nosleep_rb_->put(ch); + } + } + else + { + kputc(ch, false); // Used while booting + } } -void kprintf(const char *fmt, ...) +void kprintf(const char* fmt, ...) { - va_list args; + va_list args; - va_start(args, fmt); - kvprintf(fmt, kprintf_func, 0, 10, args); - va_end(args); + va_start(args, fmt); + kvprintf(fmt, kprintf_func, nullptr, 10, args); + va_end(args); } -void kprintfd_func(int ch, void *arg __attribute__((unused))) +void kprintfd_func(int ch, void* arg __attribute__((unused))) { - writeChar2Bochs((uint8) ch); + writeChar2Bochs((uint8)ch); } -void kprintfd(const char *fmt, ...) +/* + * Locked so that messages are not interleaved and are actually readable. + * Since this is called in interrupt contexts with interrupts disabled + * as well as outside with interrupts enabled, we need to prevent preemption + * while holding the lock to prevent deadlocks. + * Only preventing preemption without a lock would work for a single cpu + * but not for multicore systems. + * + * disable interrupts -> prevent preemption while holding lock as well as maskable interrupts + * lock -> prevent concurrent access from multiple cpu cores + * + * kprintfd being interrupted while holding the lock (e.g. due to a pagefault) would block all other + * threads attempting to print anything (and cause deadlocks if the holding thread then waits for + * one of the blocked threads) This is a tradeoff between readability of debug output and + * performance/safety in rare edge cases + * + * Example scenario that would produce a deadlock: + * kprintfd -> disable interrupts -> acquire lock (thread A) -> pagefault -> enable interrupts -> + * debug() -> kprintfd -> deadlock + * + * Even a reentrant lock does not solve this problem: + * kprintfd -> disable interrupts -> acquire lock (thread A) -> pagefault -> enable interrupts -> + * read from disk -> thread A sleep until read finished (wait for ATA irq handler) -> ata irq + * handler (in arbitrary thread context) blocks on kprintfd lock -> deadlock + * + * PREVENT PAGEFAULTS FROM OCCURRING IN KPRINTFD/DEBUG WHENEVER POSSIBLE! + */ +void kprintfd(const char* fmt, ...) { - va_list args; + va_list args; + + static BasicSpinLock kprintfd_lock; + + bool kprintfd_recursion_detected = false; + + WithInterrupts intr(false); + + Thread* calling_thread = CpuLocalStorage::ClsInitialized() ? currentThread : nullptr; + + if (calling_thread && kprintfd_lock.isHeldBy(calling_thread)) + { + // WARNING: recursive call to kprintf while holding kprintfd lock! (e.g. due to pagefault in + // kprintfd) + + writeLine2Bochs( + "\n\n\033[1;31mWARNING: recursive call to kprintfd while holding kprintfd lock. No " + "longer properly serializing debug output to prevent deadlock!\033[0;39m\n\n"); + + // Prevent extra unlock in previous kprintfd call since this is already done here + if (calling_thread->kprintfd_recursion_detected) + *calling_thread->kprintfd_recursion_detected = true; + + // We can only do this since this won't break anything (aside from producing garbled output + // on the debug console) Don't release any other locks this way! + kprintfd_lock.release(); + } + + while (!kprintfd_lock.acquireNonBlocking()) + { + // Still allow handling interrupts if we don't get the lock + if (intr.previousInterruptState()) + { + ArchInterrupts::enableInterrupts(); + ArchInterrupts::disableInterrupts(); + } + } + + if (calling_thread) + calling_thread->kprintfd_recursion_detected = &kprintfd_recursion_detected; + + va_start(args, fmt); + kvprintf(fmt, kprintfd_func, nullptr, 10, args); + va_end(args); + + if (calling_thread) + calling_thread->kprintfd_recursion_detected = nullptr; - va_start(args, fmt); - kvprintf(fmt, kprintfd_func, 0, 10, args); - va_end(args); + if (!kprintfd_recursion_detected) + kprintfd_lock.release(); } diff --git a/common/source/console/panic.cpp b/common/source/console/panic.cpp deleted file mode 100644 index 46fac4feb..000000000 --- a/common/source/console/panic.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include -#include -#include "kprintf.h" -#include "panic.h" -#include "debug_bochs.h" - -void kpanict ( const char * message ) -{ - system_state = KPANIC; - ArchInterrupts::disableInterrupts(); - - size_t* stack = (size_t*) currentThread->getKernelStackStartPointer(); - - kprintfd("%s \n", message ); - kprintf("%s \n", message ); - - kprintfd( "KPANICT: stack is > %p ", stack ); - //kprintf( "KPANICT: stack is > %x ", stack ); - - if (currentThread) - currentThread->printBacktrace(false); - - Scheduler::instance()->printThreadList(); - - ArchInterrupts::disableInterrupts(); - ArchInterrupts::disableTimer(); - //disable other IRQ's ??? - - for(;;); - - kprintf("MAJOR KERNEL PANIC!: Should never reach here\n"); - - currentThread->printBacktrace(false); - - Scheduler::instance()->printThreadList(); - -} diff --git a/common/source/drivers/CMakeLists.txt b/common/source/drivers/CMakeLists.txt new file mode 100644 index 000000000..92dafa862 --- /dev/null +++ b/common/source/drivers/CMakeLists.txt @@ -0,0 +1,6 @@ +add_project_library(common_drivers) + +target_include_directories(kernel PUBLIC + ../../include/drivers) + +add_subdirectory(platform) diff --git a/common/source/drivers/DeviceBus.cpp b/common/source/drivers/DeviceBus.cpp new file mode 100644 index 000000000..4401ebced --- /dev/null +++ b/common/source/drivers/DeviceBus.cpp @@ -0,0 +1,7 @@ +#include "DeviceBus.h" + +DeviceBus<>& deviceTreeRoot() +{ + static DeviceBus device_bus_root("Device Root"); + return device_bus_root; +} diff --git a/common/source/drivers/platform/CMakeLists.txt b/common/source/drivers/platform/CMakeLists.txt new file mode 100644 index 000000000..c12cf98ff --- /dev/null +++ b/common/source/drivers/platform/CMakeLists.txt @@ -0,0 +1,4 @@ +add_project_library(common_drivers_platform) + +target_include_directories(kernel PUBLIC + ../../../include/drivers/platform) diff --git a/common/source/drivers/platform/PlatformBus.cpp b/common/source/drivers/platform/PlatformBus.cpp new file mode 100644 index 000000000..7de1acacc --- /dev/null +++ b/common/source/drivers/platform/PlatformBus.cpp @@ -0,0 +1,12 @@ +#include "PlatformBus.h" + +PlatformBus& PlatformBus::instance() +{ + static PlatformBus instance_; + return instance_; +} + +void PlatformBus::initPlatformBus() +{ + deviceTreeRoot().addSubDevice(instance()); +} diff --git a/common/source/fs/BDManager.cpp b/common/source/fs/BDManager.cpp index 2dd634e6d..adc2e6fee 100644 --- a/common/source/fs/BDManager.cpp +++ b/common/source/fs/BDManager.cpp @@ -1,17 +1,17 @@ -#include "BDDriver.h" #include "BDManager.h" + +#include "BDDriver.h" #include "BDRequest.h" #include "BDVirtualDevice.h" -#include "IDEDriver.h" -#include "kprintf.h" -#include "debug.h" +#include "PlatformBus.h" #include "kstring.h" -BDManager *BDManager::getInstance() +#include "debug.h" + +BDManager& BDManager::instance() { - if (!instance_) - instance_ = new BDManager(); - return instance_; + static BDManager instance_; + return instance_; } @@ -20,34 +20,26 @@ BDManager::BDManager() : { } -void BDManager::doDeviceDetection(void) -{ - debug(BD_MANAGER, "doDeviceDetection: Detecting BD devices\n"); - IDEDriver id; - // insert other device detectors here - debug(BD_MANAGER, "doDeviceDetection:Detection done\n"); -} - void BDManager::addRequest(BDRequest* bdr) { if (bdr->getDevID() < getNumberOfDevices()) getDeviceByNumber(bdr->getDevID())->addRequest(bdr); else - bdr->setStatus(BDRequest::BD_ERROR); + bdr->setStatus(BDRequest::BD_RESULT::BD_ERROR); } void BDManager::addVirtualDevice(BDVirtualDevice* dev) { - debug(BD_MANAGER, "addVirtualDevice:Adding device\n"); + debug(BD_MANAGER, "addVirtualDevice: Adding device\n"); dev->setDeviceNumber(device_list_.size()); device_list_.push_back(dev); - debug(BD_MANAGER, "addVirtualDevice:Device added\n"); + debug(BD_MANAGER, "addVirtualDevice: Device added\n"); } void BDManager::serviceIRQ(uint32 irq_num) { - debug(BD_MANAGER, "serviceIRQ:Servicing IRQ\n"); - probeIRQ = false; + debug(BD_MANAGER, "serviceIRQ: Servicing IRQ\n"); + probeIRQ.clear(); for (BDVirtualDevice* dev : device_list_) if (dev->getDriver()->irq == irq_num) @@ -56,12 +48,19 @@ void BDManager::serviceIRQ(uint32 irq_num) return; } - debug(BD_MANAGER, "serviceIRQ:End servicing IRQ\n"); + debug(BD_MANAGER, "serviceIRQ: End servicing IRQ\n"); } BDVirtualDevice* BDManager::getDeviceByNumber(uint32 dev_num) { - return device_list_[dev_num]; + auto found = eastl::find_if(device_list_.begin(), device_list_.end(), [dev_num](auto dev){ return dev->getDeviceNumber() == dev_num; }); + + if (found != device_list_.end()) + { + return *found; + } + + return nullptr; } BDVirtualDevice* BDManager::getDeviceByName(const char * dev_name) @@ -80,12 +79,12 @@ BDVirtualDevice* BDManager::getDeviceByName(const char * dev_name) return dev; } } - return 0; + return nullptr; } -uint32 BDManager::getNumberOfDevices(void) +uint32 BDManager::getNumberOfDevices() const { return device_list_.size(); } -BDManager* BDManager::instance_ = 0; +BDManager* BDManager::instance_ = nullptr; diff --git a/common/source/fs/BDVirtualDevice.cpp b/common/source/fs/BDVirtualDevice.cpp index 9d30cc36c..1fd190ab7 100644 --- a/common/source/fs/BDVirtualDevice.cpp +++ b/common/source/fs/BDVirtualDevice.cpp @@ -1,11 +1,13 @@ -#include "ArchInterrupts.h" +#include "BDVirtualDevice.h" + #include "BDDriver.h" #include "BDRequest.h" -#include "BDVirtualDevice.h" +#include "Thread.h" #include "kstring.h" + +#include "ArchInterrupts.h" + #include "debug.h" -#include "kprintf.h" -#include "Thread.h" BDVirtualDevice::BDVirtualDevice(BDDriver * driver, uint32 offset, uint32 num_sectors, uint32 sector_size, const char *name, bool writable) : @@ -13,8 +15,7 @@ BDVirtualDevice::BDVirtualDevice(BDDriver * driver, uint32 offset, uint32 num_se writable_(writable), driver_(driver), partition_type_(0), name_(name) { - debug(BD_VIRT_DEVICE, "ctor: offset = %d, num_sectors = %d,\n sector_size = %d, " - "name = %s \n", + debug(BD_VIRT_DEVICE, "ctor: offset = %d, num_sectors = %d, sector_size = %d, name = %s\n", offset, num_sectors, sector_size, name); dev_number_ = 0; } @@ -24,16 +25,16 @@ void BDVirtualDevice::addRequest(BDRequest * command) command->setResult(5); switch (command->getCmd()) { - case BDRequest::BD_GET_BLK_SIZE: + case BDRequest::BD_CMD::BD_GET_BLK_SIZE: command->setResult(block_size_); - command->setStatus(BDRequest::BD_DONE); + command->setStatus(BDRequest::BD_RESULT::BD_DONE); break; - case BDRequest::BD_GET_NUM_BLOCKS: + case BDRequest::BD_CMD::BD_GET_NUM_BLOCKS: command->setResult(getNumBlocks()); - command->setStatus(BDRequest::BD_DONE); + command->setStatus(BDRequest::BD_RESULT::BD_DONE); break; - case BDRequest::BD_READ: - case BDRequest::BD_WRITE: + case BDRequest::BD_CMD::BD_READ: + case BDRequest::BD_CMD::BD_WRITE: //start block and num blocks will be interpreted as start sector and num sectors command->setStartBlock(command->getStartBlock() * (block_size_ / sector_size_) + offset_); command->setNumBlocks(command->getNumBlocks() * (block_size_ / sector_size_)); @@ -42,60 +43,68 @@ void BDVirtualDevice::addRequest(BDRequest * command) command->setResult(driver_->addRequest(command)); break; } - return; } int32 BDVirtualDevice::readData(uint32 offset, uint32 size, char *buffer) { + uint32 blocks2read = size / block_size_, jiffies = 0; + uint32 blockoffset = offset / block_size_; + + debug(BD_VIRT_DEVICE, "readData, [%x, %x), size: %u, buffer: %p, #blocks: %u, blockoffset: %u\n", offset, offset + size, size, buffer, blocks2read, blockoffset); assert(buffer); - assert(offset % block_size_ == 0 && "we can only read multiples of block_size_ from the device"); + assert(offset % block_size_ == 0 && "we can only read from the device in offsets of multiples of block_size_"); assert(size % block_size_ == 0 && "we can only read multiples of block_size_ from the device"); assert((offset + size <= getNumBlocks() * block_size_) && "tried reading out of range"); - debug(BD_VIRT_DEVICE, "readData\n"); - uint32 blocks2read = size / block_size_, jiffies = 0; - uint32 blockoffset = offset / block_size_; - - debug(BD_VIRT_DEVICE, "blocks2read %d\n", blocks2read); - BDRequest bd(dev_number_, BDRequest::BD_READ, blockoffset, blocks2read, buffer); + BDRequest bd(dev_number_, BDRequest::BD_CMD::BD_READ, blockoffset, blocks2read, buffer); addRequest(&bd); if (driver_->irq != 0) { - bool interrupt_context = ArchInterrupts::disableInterrupts(); - ArchInterrupts::enableInterrupts(); + WithInterrupts intr(true); - while (bd.getStatus() == BDRequest::BD_QUEUED && jiffies++ < IO_TIMEOUT) + while (bd.getStatus() == BDRequest::BD_RESULT::BD_QUEUED && jiffies++ < IO_TIMEOUT) ArchInterrupts::yieldIfIFSet(); - - if (!interrupt_context) - ArchInterrupts::disableInterrupts(); } assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - if (bd.getStatus() != BDRequest::BD_DONE) + if (bd.getStatus() != BDRequest::BD_RESULT::BD_DONE) { + debug(BD_VIRT_DEVICE, "readData failed for request %p id: %zu, status: %u, requesting thread: %p (%s)\n", + &bd, bd.request_id, (int)bd.getStatus(), bd.requesting_thread_, bd.requesting_thread_ ? bd.requesting_thread_->getName() : "(nil)"); return -1; } + + uint32_t chks = 0; + if (BD_VIRT_DEVICE & OUTPUT_ENABLED) + chks = checksum((uint32*)buffer, size); + + debug(BD_VIRT_DEVICE, "readData finished for request %p id: %zu, requesting thread: %p (%s), checksum: %x\n", + &bd, bd.request_id, bd.requesting_thread_, bd.requesting_thread_ ? bd.requesting_thread_->getName() : "(nil)", chks); return size; } -int32 BDVirtualDevice::writeData(uint32 offset, uint32 size, char *buffer) +int32 BDVirtualDevice::writeData(uint32 offset, uint32 size, const char *buffer) { + uint32 blocks2write = size / block_size_, jiffies = 0; + uint32 blockoffset = offset / block_size_; + uint32_t chks = 0; + if (BD_VIRT_DEVICE & OUTPUT_ENABLED) + chks = checksum((const uint32*)buffer, size); + + debug(BD_VIRT_DEVICE, "writeData, [%x, %x), size: %u, buffer: %p, #blocks: %u, blockoffset: %u, checksum: %x\n", + offset, offset + size, size, buffer, blocks2write, blockoffset, chks); + assert(offset % block_size_ == 0 && "we can only write multiples of block_size_ to the device"); assert(size % block_size_ == 0 && "we can only write multiples of block_size_ to the device"); assert((offset + size <= getNumBlocks() * block_size_) && "tried writing out of range"); - debug(BD_VIRT_DEVICE, "writeData\n"); - uint32 blocks2write = size / block_size_, jiffies = 0; - uint32 blockoffset = offset / block_size_; - - BDRequest bd(dev_number_, BDRequest::BD_WRITE, blockoffset, blocks2write, buffer); + BDRequest bd(dev_number_, BDRequest::BD_CMD::BD_WRITE, blockoffset, blocks2write, const_cast(buffer)); addRequest(&bd); if (driver_->irq != 0) @@ -103,7 +112,7 @@ int32 BDVirtualDevice::writeData(uint32 offset, uint32 size, char *buffer) bool interrupt_context = ArchInterrupts::disableInterrupts(); ArchInterrupts::enableInterrupts(); - while (bd.getStatus() == BDRequest::BD_QUEUED && jiffies++ < IO_TIMEOUT) + while (bd.getStatus() == BDRequest::BD_RESULT::BD_QUEUED && jiffies++ < IO_TIMEOUT) ArchInterrupts::yieldIfIFSet(); if (!interrupt_context) @@ -112,7 +121,7 @@ int32 BDVirtualDevice::writeData(uint32 offset, uint32 size, char *buffer) assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - if (bd.getStatus() != BDRequest::BD_DONE) + if (bd.getStatus() != BDRequest::BD_RESULT::BD_DONE) return -1; else return size; @@ -124,7 +133,7 @@ void BDVirtualDevice::setPartitionType(uint8 part_type) partition_type_ = part_type; } -uint8 BDVirtualDevice::getPartitionType(void) const +uint8 BDVirtualDevice::getPartitionType() const { return partition_type_; } diff --git a/common/source/fs/CMakeLists.txt b/common/source/fs/CMakeLists.txt index d04432f55..f9c2052e0 100644 --- a/common/source/fs/CMakeLists.txt +++ b/common/source/fs/CMakeLists.txt @@ -1,7 +1,8 @@ -include_directories(../../include/fs) - add_project_library(common_fs) +target_include_directories(kernel PUBLIC + ../../include/fs) + add_subdirectory(devicefs) add_subdirectory(minixfs) -add_subdirectory(ramfs) \ No newline at end of file +add_subdirectory(ramfs) diff --git a/common/source/fs/Dentry.cpp b/common/source/fs/Dentry.cpp index 6d849f819..680ac4b66 100644 --- a/common/source/fs/Dentry.cpp +++ b/common/source/fs/Dentry.cpp @@ -1,34 +1,37 @@ #include "Dentry.h" -#include "assert.h" -#include "Inode.h" +#include "Inode.h" #include "kprintf.h" +#include "assert.h" + Dentry::Dentry(Inode* inode) : d_inode_(inode), d_parent_(this), d_mounts_(0), d_name_("/") { - debug(DENTRY, "Created root Dentry\n"); + debug(DENTRY, "Created root Dentry %p %s\n", this, getName()); + assert(inode); inode->addDentry(this); } -Dentry::Dentry(Inode* inode, Dentry* parent, const ustl::string& name) : +Dentry::Dentry(Inode* inode, Dentry* parent, const eastl::string& name) : d_inode_(inode), d_parent_(parent), d_mounts_(0), d_name_(name) { - debug(DENTRY, "created Dentry with Name %s\n", name.c_str()); + debug(DENTRY, "Created Dentry %p, name: %s\n", this, name.c_str()); assert(name != ""); parent->setChild(this); + assert(inode); inode->addDentry(this); } Dentry::~Dentry() { - debug(DENTRY, "Deleting Dentry %s, d_parent_: %p, this: %p\n", d_name_.c_str(), d_parent_, this); + debug(DENTRY, "Deleting Dentry %p, name: %s, d_parent_: %p, this: %p\n", this, d_name_.c_str(), d_parent_, this); if (d_parent_ && (d_parent_ != this)) { d_parent_->childRemove(this); } for (Dentry* dentry : d_child_) - dentry->d_parent_ = 0; + dentry->d_parent_ = nullptr; d_inode_->removeDentry(this); } @@ -40,29 +43,29 @@ void Dentry::setInode(Inode *inode) void Dentry::childInsert(Dentry *child_dentry) { - assert(child_dentry != 0); + assert(child_dentry != nullptr); d_child_.push_back(child_dentry); } int32 Dentry::childRemove(Dentry *child_dentry) { debug(DENTRY, "Dentry childRemove d_child_ included: %d\n", - ustl::find(d_child_.begin(), d_child_.end(), child_dentry) != d_child_.end()); - assert(child_dentry != 0); + eastl::find(d_child_.begin(), d_child_.end(), child_dentry) != d_child_.end()); + assert(child_dentry != nullptr); assert(child_dentry->d_parent_ == this); d_child_.remove(child_dentry); - child_dentry->d_parent_ = 0; + child_dentry->d_parent_ = nullptr; return 0; } -const char* Dentry::getName() +const char* Dentry::getName() const { return d_name_.c_str(); } int32 Dentry::setChild(Dentry *dentry) { - if (dentry == 0 || ustl::find(d_child_.begin(), d_child_.end(), dentry) != d_child_.end()) + if (dentry == nullptr || eastl::find(d_child_.begin(), d_child_.end(), dentry) != d_child_.end()) return -1; d_child_.push_back(dentry); @@ -80,7 +83,7 @@ Dentry* Dentry::checkName(const char* name) return dentry; } - return 0; + return nullptr; } uint32 Dentry::getNumChild() @@ -90,5 +93,23 @@ uint32 Dentry::getNumChild() bool Dentry::emptyChild() { - return d_child_.empty(); + return d_child_.empty(); +} + +bool Dentry::emptyChild(std::initializer_list exceptDentries) +{ + for (auto child : d_child_) + { + for (auto excludedName : exceptDentries) + { + if (strcmp(child->getName(), excludedName) == 0) + { + continue; + } + } + + return false; + } + + return true; } diff --git a/common/source/fs/File.cpp b/common/source/fs/File.cpp index 2f0499c4a..ba4d0e005 100644 --- a/common/source/fs/File.cpp +++ b/common/source/fs/File.cpp @@ -1,13 +1,21 @@ #include "File.h" -#include "Inode.h" + #include "FileDescriptor.h" -#include "assert.h" -#include "Superblock.h" #include "FileSystemType.h" +#include "Inode.h" +#include "Superblock.h" +#include "assert.h" File::File(Inode* inode, Dentry* dentry, uint32 flag) : - uid(0), gid(0), version(0), f_superblock_(0), f_inode_(inode), f_dentry_(dentry), flag_(flag) + uid(0), + gid(0), + version(0), + f_superblock_(inode->getSuperblock()), + f_inode_(inode), + f_dentry_(dentry), + flag_(flag), + offset_(0) { f_inode_->incRefCount(); } @@ -34,6 +42,7 @@ uint32 File::getSize() l_off_t File::lseek(l_off_t offset, uint8 origin) { + debug(VFS_FILE, "(lseek) offset: %llu, origin: %u\n", (long long unsigned int)offset, origin); if (origin == SEEK_SET) offset_ = offset; else if (origin == SEEK_CUR) @@ -77,3 +86,87 @@ int File::closeFd(FileDescriptor* fd) return 0; } + +int32 File::flush() +{ + if (f_inode_) + return f_inode_->flush(); + + return 0; +} + + +SimpleFile::SimpleFile(Inode* inode, Dentry* dentry, uint32 flag) : + File(inode, dentry, flag) +{ +} + +int32 SimpleFile::read(char *buffer, size_t count, l_off_t offset) +{ + debug(VFS_FILE, "(read) buffer: %p, count: %zu, offset: %llu(%zu)\n", buffer, count, (long long unsigned int)offset, (size_t)(offset_ + offset)); + if (((flag_ & O_RDONLY) || (flag_ & O_RDWR)) && (f_inode_->getMode() & A_READABLE)) + { + int32 read_bytes = f_inode_->readData(offset_ + offset, count, buffer); + if (read_bytes >= 0) + offset_ += read_bytes; + return read_bytes; + } + else + { + // ERROR_FF + return -1; + } +} + +int32 SimpleFile::write(const char *buffer, size_t count, l_off_t offset) +{ + debug(VFS_FILE, "(write) buffer: %p, count: %zu, offset: %llu(%zu)\n", buffer, count, (long long unsigned int)offset, (size_t)(offset_ + offset)); + if (((flag_ & O_WRONLY) || (flag_ & O_RDWR)) && (f_inode_->getMode() & A_WRITABLE)) + { + int32 written_bytes = f_inode_->writeData(offset_ + offset, count, buffer); + if (written_bytes >= 0) + offset_ += written_bytes; + return written_bytes; + } + else + { + // ERROR_FF + return -1; + } +} + +NoOffsetFile::NoOffsetFile(Inode* inode, Dentry* dentry, uint32 flag) : + File(inode, dentry, flag) +{ +} + +int32 NoOffsetFile::read(char* buffer, size_t count, l_off_t offset) +{ + debug(VFS_FILE, "(read) buffer: %p, count: %zu, offset: %llu(%zu)\n", buffer, count, + (long long unsigned int)offset, (size_t)(offset_ + offset)); + if (((flag_ & O_RDONLY) || (flag_ & O_RDWR)) && (f_inode_->getMode() & A_READABLE)) + { + return f_inode_->readData(offset_ + offset, count, buffer); + } + else + { + // ERROR_FF + return -1; + } +} + + +int32 NoOffsetFile::write(const char* buffer, size_t count, l_off_t offset) +{ + debug(VFS_FILE, "(write) buffer: %p, count: %zu, offset: %llu(%zu)\n", buffer, count, + (long long unsigned int)offset, (size_t)(offset_ + offset)); + if (((flag_ & O_WRONLY) || (flag_ & O_RDWR)) && (f_inode_->getMode() & A_WRITABLE)) + { + return f_inode_->writeData(offset_ + offset, count, buffer); + } + else + { + // ERROR_FF + return -1; + } +} diff --git a/common/source/fs/FileDescriptor.cpp b/common/source/fs/FileDescriptor.cpp index 7dcade3be..9eac0dda1 100644 --- a/common/source/fs/FileDescriptor.cpp +++ b/common/source/fs/FileDescriptor.cpp @@ -1,21 +1,24 @@ #include "FileDescriptor.h" -#include -#ifndef EXE2MINIXFS -#include "ArchThreads.h" -#endif -#include "kprintf.h" -#include "debug.h" -#include "assert.h" + +#include "File.h" #include "Mutex.h" #include "ScopeLock.h" -#include "File.h" +#include "kprintf.h" -FileDescriptorList global_fd_list; +#include "EASTL/atomic.h" +#include "EASTL/list.h" -static size_t fd_num_ = 3; +#include "assert.h" +#include "debug.h" + +#ifndef EXE2MINIXFS +#include "ArchThreads.h" +#endif + +static eastl::atomic fd_counter_ = {3}; FileDescriptor::FileDescriptor(File* file) : - fd_(ArchThreads::atomic_add(fd_num_, 1)), + fd_(++fd_counter_), file_(file) { debug(VFS_FILE, "Create file descriptor %u\n", getFd()); @@ -88,3 +91,9 @@ FileDescriptor* FileDescriptorList::getFileDescriptor(uint32 fd_num) return nullptr; } + +FileDescriptorList& FileDescriptorList::globalFdList() +{ + static FileDescriptorList global_fd_list; + return global_fd_list; +} diff --git a/common/source/fs/FileSystemInfo.cpp b/common/source/fs/FileSystemInfo.cpp index 2a4459bbd..45183464c 100644 --- a/common/source/fs/FileSystemInfo.cpp +++ b/common/source/fs/FileSystemInfo.cpp @@ -1,7 +1,4 @@ #include "FileSystemInfo.h" -#include "Dentry.h" -#include "kstring.h" -#include "assert.h" FileSystemInfo::FileSystemInfo() : root_(), pwd_() @@ -9,10 +6,6 @@ FileSystemInfo::FileSystemInfo() : } FileSystemInfo::FileSystemInfo(const FileSystemInfo& fsi) : - root_(fsi.root_),pwd_(fsi.pwd_) -{ -} - -FileSystemInfo::~FileSystemInfo() + root_(fsi.root_), pwd_(fsi.pwd_) { } diff --git a/common/source/fs/FileSystemType.cpp b/common/source/fs/FileSystemType.cpp index acf43b87a..b43eb47a7 100644 --- a/common/source/fs/FileSystemType.cpp +++ b/common/source/fs/FileSystemType.cpp @@ -1,5 +1,4 @@ #include "FileSystemType.h" -#include "assert.h" FileSystemType::FileSystemType(const char *fs_name) : fs_name_ ( fs_name ), @@ -7,10 +6,6 @@ FileSystemType::FileSystemType(const char *fs_name) : {} -FileSystemType::~FileSystemType() -{} - - const char* FileSystemType::getFSName() const { return fs_name_; diff --git a/common/source/fs/Inode.cpp b/common/source/fs/Inode.cpp index a2ba0b756..955d165af 100644 --- a/common/source/fs/Inode.cpp +++ b/common/source/fs/Inode.cpp @@ -1,7 +1,8 @@ #include "Inode.h" -#include "Superblock.h" -#include "FileSystemType.h" + #include "File.h" +#include "FileSystemType.h" +#include "Superblock.h" Inode::Inode(Superblock *superblock, uint32 inode_type) : i_dentrys_(), @@ -14,6 +15,10 @@ Inode::Inode(Superblock *superblock, uint32 inode_type) : i_state_(I_UNUSED), i_mode_((A_READABLE ^ A_WRITABLE) ^ A_EXECABLE) { + auto sb = getSuperblock(); + auto type = sb ? sb->getFSType() : nullptr; + auto fs_name = type ? type->getFSName() : nullptr; + debug(INODE, "Inode() %s %p, type: %x\n", fs_name, this, i_type_); } Inode::~Inode() @@ -66,7 +71,7 @@ uint32 Inode::numLinks() bool Inode::hasDentry(Dentry* dentry) { - return ustl::find(i_dentrys_.begin(), i_dentrys_.end(), dentry) != i_dentrys_.end(); + return eastl::find(i_dentrys_.begin(), i_dentrys_.end(), dentry) != i_dentrys_.end(); } void Inode::addDentry(Dentry* dentry) @@ -212,14 +217,14 @@ Dentry* Inode::lookup(const char* name) if (name == 0) { // ERROR_DNE - return 0; + return nullptr; } debug(INODE, "%s inode %p lookup %s\n", getSuperblock()->getFSType()->getFSName(), this, name); if (i_type_ != I_DIR) { - return 0; + return nullptr; } assert(i_dentrys_.size() >= 1); diff --git a/common/source/fs/MasterBootRecord.cpp b/common/source/fs/MasterBootRecord.cpp new file mode 100644 index 000000000..77c579d50 --- /dev/null +++ b/common/source/fs/MasterBootRecord.cpp @@ -0,0 +1,95 @@ +#include "MasterBootRecord.h" + +#include "BDDriver.h" +#include "BDManager.h" +#include "BDVirtualDevice.h" + +#include "ArchInterrupts.h" + +#include "EASTL/array.h" +#include "EASTL/string.h" + +#include "debug.h" + +int detectMBRPartitions(BDVirtualDevice* bdv, BDDriver* drv, uint32_t sector, uint32_t SPT, const char* name) +{ + uint32 offset = 0, numsec = 0; + eastl::array buff; // read buffer + debug(BD_VIRT_DEVICE, "processMBR:reading MBR\n"); + + static uint32 part_num = 0; + + int32 read_res = bdv->readData(sector*drv->getSectorSize(), 512, buff.data()); + + if (read_res != 512) + { + debug(BD_VIRT_DEVICE, "processMBR: drv returned BD_ERROR\n"); + return -1; + } + + MasterBootRecord* mbr = (MasterBootRecord*)buff.data(); + + if (mbr->signature == MasterBootRecord::PC_MBR_SIGNATURE) + { + debug(BD_VIRT_DEVICE, "processMBR: | Valid PC MBR | \n"); + for (int i = 0; MasterBootRecord::PartitionEntry& fp : mbr->parts) + { + debug(BD_VIRT_DEVICE, + "partition %u: type %x [%s] at sectors [%d -> %d), num sectors: %d, " + "bytesize: %u, bootable: %d\n", + i, fp.type, partitionTypeName(fp.type), fp.first_sector_lba, + fp.first_sector_lba + fp.num_sectors, fp.num_sectors, + fp.num_sectors * drv->getSectorSize(), + fp.bootable == + MasterBootRecord::PartitionEntry::BootableStatus::BOOTABLE); + + switch (fp.type) + { + case EMPTY: + // do nothing + break; + case DOS_EXTENDED_CHS: + case WINDOWS_EXTENDED_LBA: + case LINUX_EXTENDED: + { + if (detectMBRPartitions(bdv, drv, sector + fp.first_sector_lba, SPT, name) == -1) + detectMBRPartitions(bdv, drv, sector + fp.num_sectors - SPT, SPT, name); + break; + } + case MINIXFS_ALT: + case MINIXFS_OLD: + case MINIXFS: + case SWAP: + case LINUX_ANY_NATIVE: + default: + { + // offset = fp->relsect - SPT; + offset = fp.first_sector_lba; + numsec = fp.num_sectors; + + eastl::string part_name{name}; + part_name += eastl::to_string(part_num); + part_num++; + BDVirtualDevice* bdv = new BDVirtualDevice( + drv, offset, numsec, drv->getSectorSize(), part_name.c_str(), true); + + // set Partition Type (FileSystem identifier) + bdv->setPartitionType(fp.type); + + BDManager::instance().addVirtualDevice(bdv); + break; + } + } + + ++i; + } + } + else + { + debug(BD_VIRT_DEVICE, "processMBR: | Invalid PC MBR %d | \n", mbr->signature); + return -1; + } + + debug(BD_VIRT_DEVICE, "processMBR:, done with partitions \n"); + return 0; +} diff --git a/common/source/fs/Path.cpp b/common/source/fs/Path.cpp index afb8bd9b3..caa841d7b 100644 --- a/common/source/fs/Path.cpp +++ b/common/source/fs/Path.cpp @@ -1,12 +1,14 @@ #include "Path.h" -#include "kprintf.h" -#include "debug.h" + #include "Dentry.h" -#include "VfsMount.h" #include "Inode.h" -#include "assert.h" -#include "VirtualFileSystem.h" #include "PathWalker.h" +#include "VfsMount.h" +#include "VirtualFileSystem.h" +#include "kprintf.h" + +#include "assert.h" +#include "debug.h" Path::Path(Dentry* dentry, VfsMount* mnt) : dentry_(dentry), mnt_(mnt) @@ -42,7 +44,7 @@ Path Path::parent(const Path* global_root) const return Path(dentry_->getParent(), mnt_); } -int Path::child(const ustl::string& name, Path& out) const +int Path::child(const eastl::string& name, Path& out) const { // Warning: out parameter may refer to this object! debug(PATHWALKER, "Walk down to child %s\n", name.c_str()); assert(dentry_ && mnt_); @@ -73,7 +75,7 @@ int Path::child(const ustl::string& name, Path& out) const } -ustl::string Path::getAbsolutePath(const Path* global_root) const +eastl::string Path::getAbsolutePath(const Path* global_root) const { if(isGlobalRoot(global_root)) { diff --git a/common/source/fs/PathWalker.cpp b/common/source/fs/PathWalker.cpp index 9c69a9309..7c149bcf6 100644 --- a/common/source/fs/PathWalker.cpp +++ b/common/source/fs/PathWalker.cpp @@ -1,13 +1,16 @@ #include "PathWalker.h" -#include "Inode.h" + #include "Dentry.h" -#include "VfsMount.h" -#include "Superblock.h" -#include "assert.h" -#include "kstring.h" -#include "kprintf.h" #include "FileSystemInfo.h" +#include "Inode.h" #include "Path.h" +#include "Superblock.h" +#include "VfsMount.h" +#include "kprintf.h" +#include "kstring.h" + +#include "assert.h" + #ifndef EXE2MINIXFS #include "Mutex.h" #include "Thread.h" @@ -138,7 +141,7 @@ size_t PathWalker::getNextPartLen(const char* path) } -ustl::string PathWalker::pathPrefix(const ustl::string& path) +eastl::string PathWalker::pathPrefix(const eastl::string& path) { ssize_t prefix_len = path.find_last_of("/"); if(prefix_len == -1) @@ -147,12 +150,12 @@ ustl::string PathWalker::pathPrefix(const ustl::string& path) return ""; } - ustl::string retval = path.substr(0, prefix_len); + eastl::string retval = path.substr(0, prefix_len); debug(PATHWALKER, "pathPrefix: %s -> %s\n", path.c_str(), retval.c_str()); return retval; } -ustl::string PathWalker::lastPathSegment(const ustl::string& path, bool ignore_separator_at_end) +eastl::string PathWalker::lastPathSegment(const eastl::string& path, bool ignore_separator_at_end) { if(path.length() == 0) return path; @@ -160,15 +163,15 @@ ustl::string PathWalker::lastPathSegment(const ustl::string& path, bool ignore_s if(!ignore_separator_at_end || path.back() != '/') { ssize_t prefix_len = path.find_last_of("/"); - ustl::string retval = path.substr(prefix_len+1, path.length() - prefix_len); + eastl::string retval = path.substr(prefix_len+1, path.length() - prefix_len); debug(PATHWALKER, "lastPathSegment: %s -> %s\n", path.c_str(), retval.c_str()); return retval; } else { - ustl::string tmp_path = path.substr(0, path.find_last_not_of("/") + 1); + eastl::string tmp_path = path.substr(0, path.find_last_not_of("/") + 1); ssize_t prefix_len = tmp_path.find_last_of("/"); - ustl::string retval = tmp_path.substr(prefix_len+1, tmp_path.length() - prefix_len); + eastl::string retval = tmp_path.substr(prefix_len+1, tmp_path.length() - prefix_len); debug(PATHWALKER, "lastPathSegment: %s -> %s\n", path.c_str(), retval.c_str()); return retval; } diff --git a/common/source/fs/RamDiskDriver.cpp b/common/source/fs/RamDiskDriver.cpp new file mode 100644 index 000000000..9e1615957 --- /dev/null +++ b/common/source/fs/RamDiskDriver.cpp @@ -0,0 +1,85 @@ +#include "RamDiskDriver.h" + +#include "BDRequest.h" +#include "BDVirtualDevice.h" +#include "kstring.h" + +#include "assert.h" +#include "debug.h" + +RamDiskDriver::RamDiskDriver(void* start_vaddr, size_t size) : + start_vaddr_(start_vaddr), + size_(size) +{ + debug(RAMDISK, "Create ramdisk, start: %p, size: %zx\n", start_vaddr_, size_); + assert((((size_t)-1) - size_) >= (size_t)start_vaddr_); +} + +RamDiskDriver::~RamDiskDriver() +{ + debug(RAMDISK, "Destruct ramdisk, start: %p, size: %zx\n", start_vaddr_, size_); +} + +uint32_t RamDiskDriver::addRequest(BDRequest * request) +{ + switch (request->getCmd()) + { + case BDRequest::BD_CMD::BD_GET_BLK_SIZE: + request->setResult(getSectorSize()); + request->setStatus(BDRequest::BD_RESULT::BD_DONE); + break; + case BDRequest::BD_CMD::BD_GET_NUM_BLOCKS: + request->setResult(getNumSectors()); + request->setStatus(BDRequest::BD_RESULT::BD_DONE); + break; + case BDRequest::BD_CMD::BD_READ: + readSector(request->getStartBlock(), request->getNumBlocks(), request->getBuffer()); + break; + case BDRequest::BD_CMD::BD_WRITE: + writeSector(request->getStartBlock(), request->getNumBlocks(), request->getBuffer()); + // fall-through + default: + request->setStatus(BDRequest::BD_RESULT::BD_DONE); + request->setResult(0); + break; + } + return 0; +} + +uint32_t RamDiskDriver::getNumSectors() +{ + debug(RAMDISK, "ramdisk: getNumSectors\n"); + return size_; +} + +uint32_t RamDiskDriver::getSectorSize() +{ + debug(RAMDISK, "ramdisk: getSectorSize\n"); + return 1; +} + +int32_t RamDiskDriver::readSector( uint32_t start_sector, uint32_t num_sectors, void *buffer ) +{ + debug(RAMDISK, "ramdisk: readSector, start: %x, num: %x, buf: %p => reading[%p, %p)\n", start_sector, num_sectors, buffer, (char*)start_vaddr_ + start_sector, (char*)start_vaddr_ + start_sector + num_sectors); + assert(num_sectors <= getNumSectors()); + memcpy(buffer, (char*)start_vaddr_ + start_sector, num_sectors); + return 0; +} + +int32_t RamDiskDriver::writeSector( uint32_t start_sector, uint32_t num_sectors, void *buffer ) +{ + debug(RAMDISK, "ramdisk: writeSector, start: %x, num: %x, buf: %p\n", start_sector, num_sectors, buffer); + assert(num_sectors <= getNumSectors()); + memcpy((char*)start_vaddr_ + start_sector, buffer, num_sectors); + return 0; +} + +void RamDiskDriver::serviceIRQ() +{ + assert(false && "RamDiskDriver doesn't need a service irq"); +} + +BDVirtualDevice* RamDiskDriver::createRamDisk(void* start_vaddr, size_t size, const char* name) +{ + return new BDVirtualDevice(new RamDiskDriver(start_vaddr, size), 0, size, 1, name, true); +} diff --git a/common/source/fs/Superblock.cpp b/common/source/fs/Superblock.cpp index 8690f33ed..07a760d30 100644 --- a/common/source/fs/Superblock.cpp +++ b/common/source/fs/Superblock.cpp @@ -1,9 +1,11 @@ #include "Superblock.h" -#include "assert.h" + #include "Dentry.h" -#include "Inode.h" #include "File.h" #include "FileSystemType.h" +#include "Inode.h" + +#include "assert.h" Superblock::Superblock(FileSystemType* fs_type, size_t s_dev) : s_magic_(0), s_type_(fs_type), s_dev_(s_dev), s_flags_(0), s_root_(0), s_mountpoint_(0) @@ -18,7 +20,7 @@ Superblock::~Superblock() void Superblock::deleteInode(Inode* inode) { - assert(inode != 0); + assert(inode); dirty_inodes_.remove(inode); used_inodes_.remove(inode); all_inodes_.remove(inode); @@ -51,7 +53,7 @@ int Superblock::fileOpened(File* file) Inode* inode = file->getInode(); assert(inode->getSuperblock() == this); - if(ustl::find(s_files_.begin(), s_files_.end(), file) != s_files_.end()) + if(eastl::find(s_files_.begin(), s_files_.end(), file) != s_files_.end()) { return -1; } @@ -93,12 +95,13 @@ void Superblock::deleteAllInodes() { for (Inode* inode : all_inodes_) { + debug(SUPERBLOCK, "~Superblock remove dentries for inode %p\n", inode); while(!inode->getDentrys().empty()) { delete inode->getDentrys().front(); } - debug(SUPERBLOCK, "~Superblock write inode to disc\n"); + debug(SUPERBLOCK, "~Superblock write inode %p to disc\n", inode); writeInode(inode); debug(SUPERBLOCK, "~Superblock delete inode %p\n", inode); diff --git a/common/source/fs/VfsMount.cpp b/common/source/fs/VfsMount.cpp index f5179f658..62d672837 100644 --- a/common/source/fs/VfsMount.cpp +++ b/common/source/fs/VfsMount.cpp @@ -2,10 +2,10 @@ VfsMount::VfsMount() : - mnt_parent_ ( 0 ), - mnt_mountpoint_ ( 0 ), - mnt_root_ ( 0 ), - mnt_sb_ ( 0 ), + mnt_parent_ ( nullptr ), + mnt_mountpoint_ ( nullptr ), + mnt_root_ ( nullptr ), + mnt_sb_ ( nullptr ), mnt_flags_ ( 0 ) {} @@ -22,10 +22,10 @@ VfsMount::VfsMount ( VfsMount* parent, Dentry * mountpoint, Dentry* root, VfsMount::~VfsMount() { - mnt_parent_ = 0; - mnt_mountpoint_ = 0; - mnt_root_ = 0; - mnt_sb_ = 0; + mnt_parent_ = nullptr; + mnt_mountpoint_ = nullptr; + mnt_root_ = nullptr; + mnt_sb_ = nullptr; mnt_flags_ = 0; } @@ -63,10 +63,10 @@ int32 VfsMount::getFlags() const //NOTE: only used as workaround void VfsMount::clear() { - mnt_parent_ = 0; - mnt_mountpoint_ = 0; - mnt_root_ = 0; - mnt_sb_ = 0; + mnt_parent_ = nullptr; + mnt_mountpoint_ = nullptr; + mnt_root_ = nullptr; + mnt_sb_ = nullptr; mnt_flags_ = 0; } diff --git a/common/source/fs/VfsSyscall.cpp b/common/source/fs/VfsSyscall.cpp index b05d23c42..adadaccb9 100644 --- a/common/source/fs/VfsSyscall.cpp +++ b/common/source/fs/VfsSyscall.cpp @@ -1,19 +1,22 @@ #include "VfsSyscall.h" -#include "kstring.h" -#include "assert.h" -#include "Dirent.h" -#include "Inode.h" + #include "Dentry.h" -#include "Superblock.h" +#include "Dirent.h" #include "File.h" #include "FileDescriptor.h" -#include "FileSystemType.h" #include "FileSystemInfo.h" -#include "VirtualFileSystem.h" +#include "FileSystemType.h" +#include "Inode.h" #include "MinixFSType.h" #include "PathWalker.h" +#include "Superblock.h" #include "VfsMount.h" +#include "VirtualFileSystem.h" #include "kprintf.h" +#include "kstring.h" + +#include "assert.h" + #ifndef EXE2MINIXFS #include "Thread.h" #endif @@ -23,13 +26,14 @@ FileDescriptor* VfsSyscall::getFileDescriptor(uint32 fd) { - return global_fd_list.getFileDescriptor(fd); + return FileDescriptorList::globalFdList().getFileDescriptor(fd); } int32 VfsSyscall::mkdir(const char* pathname, int32) { debug(VFSSYSCALL, "(mkdir) Path: %s\n", pathname); + FileSystemInfo *fs_info = getcwd(); Path target_path; @@ -38,7 +42,7 @@ int32 VfsSyscall::mkdir(const char* pathname, int32) if((path_walk_status == PW_ENOTFOUND) && parent_dir_path.dentry_) { - ustl::string new_dir_name = PathWalker::lastPathSegment(pathname, true); + eastl::string new_dir_name = PathWalker::lastPathSegment(pathname, true); Inode* parent_dir_inode = parent_dir_path.dentry_->getInode(); Superblock* parent_dir_sb = parent_dir_inode->getSuperblock(); @@ -52,7 +56,7 @@ int32 VfsSyscall::mkdir(const char* pathname, int32) debug(VFSSYSCALL, "(mkdir) Creating new directory Inode in superblock %p of type %s\n", parent_dir_sb, parent_dir_sb->getFSType()->getFSName()); Inode* new_dir_inode = parent_dir_sb->createInode(I_DIR); - debug(VFSSYSCALL, "(mkdir) Creating new dentry: %s in parent dir %s\n", new_dir_name.c_str(), parent_dir_path.dentry_->getName()); + debug(VFSSYSCALL, "(mkdir) Creating new dentry: %s in parent dir %s, inode: %p\n", new_dir_name.c_str(), parent_dir_path.dentry_->getName(), new_dir_inode); Dentry* new_dir_dentry = new Dentry(new_dir_inode, parent_dir_path.dentry_, new_dir_name); new_dir_inode->mkdir(new_dir_dentry); @@ -74,74 +78,69 @@ int32 VfsSyscall::mkdir(const char* pathname, int32) return -1; } -static const char* readdirPrefix(uint32 inode_type) -{ - switch (inode_type) - { - case I_DIR: - return "[D] "; - case I_FILE: - return "[F] "; - case I_LNK: - return "[L] "; - default: - return ""; - } -} -Dirent* VfsSyscall::readdir(const char* pathname, char* buffer, size_t size) +ssize_t VfsSyscall::getdents(int fd, char* buffer, size_t buffer_size) { - FileSystemInfo *fs_info = getcwd(); - - Path target_path; - if (PathWalker::pathWalk(pathname, fs_info, target_path) != PW_SUCCESS) - { - debug(VFSSYSCALL, "(readdir) ERROR: Path doesn't exist\n"); - kprintf("Error: %s not found\n", pathname); - return 0; - } - - if (target_path.dentry_->getInode()->getType() != I_DIR) - { - debug(VFSSYSCALL, "(readdir) ERROR: This path is not a directory\n"); - kprintf("Error: %s is not a directory\n", pathname); - return nullptr; - } + debug(VFSSYSCALL, "(getdents) Getting dentries for fd: %d\n", fd); - debug(VFSSYSCALL, "(readdir) listing dir %s:\n", target_path.dentry_->getName()); - size_t it = 0; - for (Dentry* sub_dentry : target_path.dentry_->d_child_) - { - uint32 inode_type = sub_dentry->getInode()->getType(); - const char* prefix = readdirPrefix(inode_type); - const char* name = sub_dentry->getName(); + if (!buffer) + { + debug(VFSSYSCALL, "(getdents) Error: buffer = NULL\n"); + return -1; + } - if(buffer) + FileDescriptor* file_descriptor = getFileDescriptor(fd); + if (file_descriptor == nullptr) { - auto len_name = strlen(name); - if(it + len_name + 1 >= size) - { - debug(VFSSYSCALL, "Buffer is too small for all elements -> return it as it is\n"); - buffer[it] = 0; - return 0; - } - - memcpy(buffer + it, name, len_name); - it += len_name; - buffer[it++] = '\n'; + debug(VFSSYSCALL, "(getdents) Error: The fd does not exist.\n"); + return -1; } - else + + if (file_descriptor->getFile()->getInode()->getType() != I_DIR) { - kprintf("%s%s\n", prefix, name); + debug(VFSSYSCALL, "(getdents) Error: fd is not a directory.\n"); + return -1; } - } - if(buffer) - { - buffer[it] = 0; - } + Dentry* dir_dentry = file_descriptor->getFile()->getDentry(); + debug(VFSSYSCALL, "(getdents) Reading dentries for dir %p (inode %p) %s\n", + dir_dentry, dir_dentry->getInode(), dir_dentry->getName()); - return 0; + ssize_t buf_offs = 0; + user_dirent* u_dirent = nullptr; + user_dirent* u_dirent_prev = nullptr; + for (Dentry* sub_dentry : dir_dentry->d_child_) + { + uint32 d_type = sub_dentry->getInode()->getType(); + const char* d_name = sub_dentry->getName(); + auto d_name_len = strlen(d_name) + 1; + + debug(VFSSYSCALL, "(getdents) child %s, type: %x\n", d_name, d_type); + + u_dirent = (user_dirent*)(buffer + buf_offs); + + size_t u_dirent_size = sizeof(*u_dirent) + d_name_len; + + if (buf_offs + u_dirent_size > buffer_size) + { + debug(VFSSYSCALL, "(getdents) Error: Buffer is too small for all dirents. Size: %zu\n", buffer_size); + return -1; + } + + u_dirent->d_offs_next = 0; + u_dirent->d_type = d_type; + memcpy(u_dirent->d_name, d_name, d_name_len); + + buf_offs += u_dirent_size; + + if (u_dirent_prev) + { + u_dirent_prev->d_offs_next = (char*)u_dirent - (char*)u_dirent_prev; + } + u_dirent_prev = u_dirent; + } + + return buf_offs; } int32 VfsSyscall::chdir(const char* pathname) @@ -254,15 +253,16 @@ int32 VfsSyscall::rmdir(const char* pathname) int32 VfsSyscall::close(uint32 fd) { debug(VFSSYSCALL, "(close) Close fd num %u\n", fd); + FileDescriptor* file_descriptor = getFileDescriptor(fd); - if (file_descriptor == 0) + if (file_descriptor == nullptr) { debug(VFSSYSCALL, "(close) Error: the fd does not exist.\n"); return -1; } - assert(!global_fd_list.remove(file_descriptor)); + assert(!FileDescriptorList::globalFdList().remove(file_descriptor)); file_descriptor->getFile()->closeFd(file_descriptor); debug(VFSSYSCALL, "(close) File closed\n"); @@ -277,6 +277,11 @@ int32 VfsSyscall::open(const char* pathname, uint32 flag) return -1; } +#ifndef EXE2MINIXFS + // MutexLock l(vfs_lock); // TODO: When userspace pagefault occurs while holding vfs lock -> pagefault handler attempts to load page data -> calls vfs read -> lock vfs lock -> deadlock +#endif + + debug(VFSSYSCALL, "(open) Opening file %s\n", pathname); if (flag & ~(O_RDONLY | O_WRONLY | O_CREAT | O_RDWR | O_TRUNC | O_APPEND)) { @@ -299,16 +304,16 @@ int32 VfsSyscall::open(const char* pathname, uint32 flag) { debug(VFSSYSCALL, "(open) Found target file: %s\n", target_path.dentry_->getName()); Inode* target_inode = target_path.dentry_->getInode(); + assert(target_inode); - if (target_inode->getType() != I_FILE) + File* file = target_inode->open(target_path.dentry_, flag); + if (!file) { - debug(VFSSYSCALL, "(open) Error: This path is not a file\n"); - return -1; + debug(VFSSYSCALL, "(open) Unable to open file\n"); + return -1; } - - File* file = target_inode->open(target_path.dentry_, flag); FileDescriptor* fd = file->openFd(); - assert(!global_fd_list.add(fd)); + assert(!FileDescriptorList::globalFdList().add(fd)); debug(VFSSYSCALL, "(open) Fd for new open file: %d, flags: %x\n", fd->getFd(), flag); return fd->getFd(); @@ -323,7 +328,7 @@ int32 VfsSyscall::open(const char* pathname, uint32 flag) debug(VFSSYSCALL, "(open) target does not exist, creating new file\n"); - ustl::string new_dentry_name = PathWalker::lastPathSegment(pathname); + eastl::string new_dentry_name = PathWalker::lastPathSegment(pathname); if(new_dentry_name == "") // Path has trailing slashes { debug(VFSSYSCALL, "(open) Error: last path segment is empty (trailing '/')\n"); @@ -355,7 +360,7 @@ int32 VfsSyscall::open(const char* pathname, uint32 flag) File* file = new_file_inode->open(new_file_dentry, flag); FileDescriptor* fd = file->openFd(); - assert(!global_fd_list.add(fd)); + assert(!FileDescriptorList::globalFdList().add(fd)); debug(VFSSYSCALL, "(open) Fd for new open file: %d, flags: %x\n", fd->getFd(), flag); return fd->getFd(); @@ -375,7 +380,7 @@ int32 VfsSyscall::read(uint32 fd, char* buffer, uint32 count) { FileDescriptor* file_descriptor = getFileDescriptor(fd); - if (file_descriptor == 0) + if (file_descriptor == nullptr) { debug(VFSSYSCALL, "(read) Error: the fd does not exist.\n"); return -1; @@ -389,9 +394,11 @@ int32 VfsSyscall::read(uint32 fd, char* buffer, uint32 count) int32 VfsSyscall::write(uint32 fd, const char *buffer, uint32 count) { + debug(VFSSYSCALL, "(write) Write %u bytes from %p to fd %u\n", count, buffer, fd); + FileDescriptor* file_descriptor = getFileDescriptor(fd); - if (file_descriptor == 0) + if (file_descriptor == nullptr) { debug(VFSSYSCALL, "(write) Error: the fd does not exist.\n"); return -1; @@ -407,7 +414,7 @@ l_off_t VfsSyscall::lseek(uint32 fd, l_off_t offset, uint8 origin) { FileDescriptor* file_descriptor = getFileDescriptor(fd); - if (file_descriptor == 0) + if (file_descriptor == nullptr) { debug(VFSSYSCALL, "(lseek) Error: the fd does not exist.\n"); return -1; @@ -420,7 +427,7 @@ int32 VfsSyscall::flush(uint32 fd) { FileDescriptor* file_descriptor = getFileDescriptor(fd); - if (file_descriptor == 0) + if (file_descriptor == nullptr) { debug(VFSSYSCALL, "(read) Error: the fd does not exist.\n"); return -1; @@ -452,7 +459,7 @@ uint32 VfsSyscall::getFileSize(uint32 fd) { FileDescriptor* file_descriptor = getFileDescriptor(fd); - if (file_descriptor == 0) + if (file_descriptor == nullptr) { debug(VFSSYSCALL, "(read) Error: the fd does not exist.\n"); return -1; diff --git a/common/source/fs/VirtualFileSystem.cpp b/common/source/fs/VirtualFileSystem.cpp index b56b004f6..44cde9d13 100644 --- a/common/source/fs/VirtualFileSystem.cpp +++ b/common/source/fs/VirtualFileSystem.cpp @@ -1,30 +1,23 @@ -#include "FileSystemType.h" -#include "FileSystemInfo.h" -#include "PathWalker.h" #include "VirtualFileSystem.h" + +#include "BDManager.h" +#include "BDVirtualDevice.h" #include "Dentry.h" +#include "FileSystemInfo.h" +#include "FileSystemType.h" +#include "PathWalker.h" +#include "Scheduler.h" #include "Superblock.h" +#include "Thread.h" #include "VfsMount.h" +#include "kprintf.h" #include "kstring.h" -#include "assert.h" -#include "BDManager.h" -#include "BDVirtualDevice.h" -#include "Thread.h" -#include "console/kprintf.h" +#include "assert.h" VirtualFileSystem vfs; void VirtualFileSystem::initialize() -{ - new (this) VirtualFileSystem(); -} - -VirtualFileSystem::VirtualFileSystem() -{ -} - -VirtualFileSystem::~VirtualFileSystem() { } @@ -43,7 +36,7 @@ int32 VirtualFileSystem::registerFileSystem(FileSystemType *file_system_type) int32 VirtualFileSystem::unregisterFileSystem(FileSystemType *file_system_type) { - assert(file_system_type != 0); + assert(file_system_type != nullptr); const char *fs_name = file_system_type->getFSName(); for (FileSystemType* fst : file_system_types_) @@ -63,14 +56,14 @@ FileSystemType *VirtualFileSystem::getFsType(const char* fs_name) if (strcmp(fst->getFSName(), fs_name) == 0) return fst; } - return 0; + return nullptr; } VfsMount *VirtualFileSystem::getVfsMount(const Dentry* dentry, bool is_root) { assert(dentry); - if (is_root == false) + if (!is_root) { for (VfsMount* mnt : mounts_) { @@ -81,7 +74,7 @@ VfsMount *VirtualFileSystem::getVfsMount(const Dentry* dentry, bool is_root) return mnt; } } - return 0; + return nullptr; } FileSystemInfo *VirtualFileSystem::rootMount(const char *fs_name, uint32 /*flags*/) @@ -101,14 +94,14 @@ FileSystemInfo *VirtualFileSystem::rootMount(const char *fs_name, uint32 /*flags debug(VFS, "Create root %s superblock\n", fst->getFSName()); Superblock *super = fst->createSuper(-1); - super = fst->readSuper(super, 0); + super = fst->readSuper(super, nullptr); Dentry *root = super->getRoot(); super->setMountPoint(root); root->setMountedRoot(root); - VfsMount *root_mount = new VfsMount(0, root, root, super, 0); + VfsMount *root_mount = new VfsMount(nullptr, root, root, super, 0); mounts_.push_back(root_mount); superblocks_.push_back(super); @@ -148,7 +141,7 @@ int32 VirtualFileSystem::mount(const char* dev_name, const char* dir_name, const uint32_t dev = -1; if(fst->getFSFlags() & FS_REQUIRES_DEV) { - BDVirtualDevice* bddev = BDManager::getInstance()->getDeviceByName(dev_name); + BDVirtualDevice* bddev = BDManager::instance().getDeviceByName(dev_name); if (!bddev) { debug(VFS, "mount: device with name %s doesn't exist\n", dev_name); @@ -179,9 +172,10 @@ int32 VirtualFileSystem::mount(const char* dev_name, const char* dir_name, const } debug(VFS, "mount: Fill superblock\n"); - super = fst->readSuper(super, 0); //? + super = fst->readSuper(super, nullptr); //? Dentry *root = super->getRoot(); + assert(root); assert(root->getParent() == root); super->setMountPoint(mountpoint_path.dentry_); // Set mountpoint for new superblock @@ -202,14 +196,17 @@ int32 VirtualFileSystem::mount(const char* dev_name, const char* dir_name, const int32 VirtualFileSystem::rootUmount() { - if (superblocks_.size() == 0) + if (superblocks_.empty()) { return -1; } - VfsMount *root_vfs_mount = mounts_.at(0); + + assert(mounts_.size() > 0); + VfsMount *root_vfs_mount = mounts_.front(); delete root_vfs_mount; - Superblock *root_sb = superblocks_.at(0); + assert(superblocks_.size() > 0); + Superblock *root_sb = superblocks_.front(); delete root_sb; return 0; } @@ -217,7 +214,7 @@ int32 VirtualFileSystem::rootUmount() int32 VirtualFileSystem::umount(const char* dir_name, uint32 /*flags*/) { FileSystemInfo *fs_info = currentThread->getWorkingDirInfo(); - if (dir_name == 0) + if (dir_name == nullptr) return -1; Path mountpount_path; @@ -256,4 +253,3 @@ int32 VirtualFileSystem::umount(const char* dir_name, uint32 /*flags*/) return 0; } - diff --git a/common/source/fs/devicefs/CMakeLists.txt b/common/source/fs/devicefs/CMakeLists.txt index 0b1abab20..49d13e0e9 100644 --- a/common/source/fs/devicefs/CMakeLists.txt +++ b/common/source/fs/devicefs/CMakeLists.txt @@ -1,3 +1,6 @@ -include_directories(../../../include/fs/devicefs) +add_project_library(common_fs_devicefs) -add_project_library(common_fs_devicefs) \ No newline at end of file +target_include_directories(kernel PUBLIC + ../../../include/fs/devicefs) + +add_subdirectory(devices) diff --git a/common/source/fs/devicefs/DeviceFSSuperblock.cpp b/common/source/fs/devicefs/DeviceFSSuperblock.cpp index 8843f2b0c..a4d77c71a 100644 --- a/common/source/fs/devicefs/DeviceFSSuperblock.cpp +++ b/common/source/fs/devicefs/DeviceFSSuperblock.cpp @@ -1,14 +1,19 @@ #include "fs/devicefs/DeviceFSSuperblock.h" -#include "fs/ramfs/RamFSInode.h" + +#include "BDManager.h" +#include "BDVirtualDevice.h" +#include "BlockDeviceInode.h" +#include "Console.h" +#include "Device.h" #include "DeviceFSType.h" +#include "console/kprintf.h" #include "fs/Dentry.h" -#include "fs/Inode.h" #include "fs/File.h" #include "fs/FileDescriptor.h" +#include "fs/Inode.h" +#include "fs/ramfs/RamFSInode.h" -#include "console/kprintf.h" - -#include "Console.h" +#include "debug.h" class DeviceFSType; @@ -17,7 +22,7 @@ extern Console* main_console; const char DeviceFSSuperBlock::ROOT_NAME[] = "/"; const char DeviceFSSuperBlock::DEVICE_ROOT_NAME[] = "dev"; -DeviceFSSuperBlock* DeviceFSSuperBlock::instance_ = 0; +DeviceFSSuperBlock* DeviceFSSuperBlock::instance_ = nullptr; DeviceFSSuperBlock::DeviceFSSuperBlock(DeviceFSType* fs_type, uint32 s_dev) : RamFSSuperblock(fs_type, s_dev) @@ -28,12 +33,12 @@ DeviceFSSuperBlock::~DeviceFSSuperBlock() { } -void DeviceFSSuperBlock::addDevice(Inode* device_inode, const char* node_name) +void DeviceFSSuperBlock::addDevice(Inode* device_inode, const char* device_name) { // Devices are mounted at the devicefs root (s_root_) device_inode->setSuperBlock(this); - Dentry* fdntr = new Dentry(device_inode, s_root_, node_name); + Dentry* fdntr = new Dentry(device_inode, s_root_, device_name); assert(device_inode->mknod(fdntr) == 0); all_inodes_.push_back(device_inode); @@ -45,3 +50,32 @@ DeviceFSSuperBlock* DeviceFSSuperBlock::getInstance() instance_ = new DeviceFSSuperBlock(DeviceFSType::getInstance(), 0); return instance_; } + +void DeviceFSSuperBlock::addBlockDeviceInodes() +{ + for (BDVirtualDevice* bdvd : BDManager::instance().device_list_) + { + debug(BD_VIRT_DEVICE, "Detected Device: %s :: %d\n", bdvd->getName(), bdvd->getDeviceNumber()); + kprintf("Detected Device: %s :: %d\n", bdvd->getName(), bdvd->getDeviceNumber()); + auto bdInode = new BlockDeviceInode(bdvd); + addDevice(bdInode, bdvd->getName()); + } +} + +void DeviceFSSuperBlock::addDeviceInodes(Device& device_root) +{ + const auto rec_lambda = [this](Device& device, auto& rec_func) -> void + { + if (auto device_inode = device.deviceInode()) + { + debug(DRIVER, "Device %s has inode, adding to devicefs\n", + device.deviceName().c_str()); + addDevice(device_inode, device.deviceName().c_str()); + } + for (Device* sd : device.subdevices()) + { + rec_func(*sd, rec_func); + } + }; + rec_lambda(device_root, rec_lambda); +} diff --git a/common/source/fs/devicefs/DeviceFSType.cpp b/common/source/fs/devicefs/DeviceFSType.cpp index 3e07213ca..56a6da5e7 100644 --- a/common/source/fs/devicefs/DeviceFSType.cpp +++ b/common/source/fs/devicefs/DeviceFSType.cpp @@ -1,4 +1,5 @@ #include "fs/devicefs/DeviceFSType.h" + #include "fs/devicefs/DeviceFSSuperblock.h" DeviceFSType* DeviceFSType::instance_ = nullptr; @@ -9,16 +10,12 @@ DeviceFSType::DeviceFSType() : fs_name_ = "devicefs"; } -DeviceFSType::~DeviceFSType() -{ -} - Superblock* DeviceFSType::readSuper(Superblock *superblock, void*) const { return superblock; } -Superblock* DeviceFSType::createSuper(uint32 __attribute__((unused)) s_dev) +Superblock* DeviceFSType::createSuper([[maybe_unused]] uint32 s_dev) { Superblock *super = DeviceFSSuperBlock::getInstance(); return super; diff --git a/common/source/fs/devicefs/devices/BlockDeviceInode.cpp b/common/source/fs/devicefs/devices/BlockDeviceInode.cpp new file mode 100644 index 000000000..bb424a5ef --- /dev/null +++ b/common/source/fs/devicefs/devices/BlockDeviceInode.cpp @@ -0,0 +1,111 @@ +#include "BlockDeviceInode.h" + +#include "BDVirtualDevice.h" +#include "File.h" +#include "Superblock.h" + +#include "EASTL/memory.h" +#include "EASTL/unique_ptr.h" + +BlockDeviceInode::BlockDeviceInode(BDVirtualDevice* device) : + Inode(nullptr, I_BLOCKDEVICE), + device_(device) +{ + debug(INODE, "New block device inode for device: %s\n", device->getName()); +} + +File* BlockDeviceInode::open(Dentry* dentry, uint32 flag) +{ + debug(INODE, "BlockDeviceInode: Open file\n"); + assert(eastl::find(i_dentrys_.begin(), i_dentrys_.end(), dentry) != i_dentrys_.end()); + + File* file = (File*) (new SimpleFile(this, dentry, flag)); + i_files_.push_back(file); + getSuperblock()->fileOpened(file); + return file; +} + +int32 BlockDeviceInode::readData(uint32 offset, uint32 size, char* buffer) +{ + size_t block_size = device_->getBlockSize(); + size_t bd_size = device_->getNumBlocks() * block_size; + size_t read_size = eastl::min(size, bd_size - offset); + + if ((offset % block_size == 0) && (read_size % block_size == 0)) + { + return device_->readData(offset, read_size, buffer); + } + + int32 bd_status = 0; + + auto tmp_buf = eastl::make_unique(block_size); + + size_t num_read = 0; + size_t read_loc = offset; + while (num_read < read_size) + { + size_t num_remaining = read_size - num_read; + size_t block_offset = read_loc % block_size; + size_t chunk_size = eastl::min(num_remaining, block_size - block_offset); + + bd_status = device_->readData(read_loc - block_offset, block_size, tmp_buf.get()); + if (bd_status == -1) + { + break; + } + + memcpy(buffer + num_read, tmp_buf.get() + block_offset, chunk_size); + + num_read += chunk_size; + read_loc += chunk_size; + } + + return num_read; +} + +int32 BlockDeviceInode::writeData(uint32 offset, uint32 size, const char* buffer) +{ + size_t block_size = device_->getBlockSize(); + size_t bd_size = device_->getNumBlocks() * block_size; + size_t write_size = eastl::min(size, bd_size - offset); + + if ((offset % block_size == 0) && (write_size % block_size == 0)) + { + return device_->writeData(offset, write_size, buffer); + } + + int32 bd_status = 0; + + auto tmp_buf = eastl::make_unique(block_size); + + size_t num_written = 0; + size_t write_loc = offset; + while (num_written < write_size) + { + size_t num_remaining = write_size - num_written; + size_t block_offset = write_loc % block_size; + size_t chunk_size = eastl::min(num_remaining, block_size - block_offset); + + if (block_offset != 0 || chunk_size != block_size) + { + bd_status = device_->readData(write_loc - block_offset, block_size, tmp_buf.get()); + if (bd_status == -1) + { + break; + } + } + + memcpy(tmp_buf.get() + block_offset, buffer + num_written, chunk_size); + + bd_status = device_->writeData(write_loc - block_offset, block_size, tmp_buf.get()); + if (bd_status == -1) + { + break; + } + + num_written += chunk_size; + write_loc += chunk_size; + } + + return num_written; +} diff --git a/common/source/fs/devicefs/devices/CMakeLists.txt b/common/source/fs/devicefs/devices/CMakeLists.txt new file mode 100644 index 000000000..70d76a14a --- /dev/null +++ b/common/source/fs/devicefs/devices/CMakeLists.txt @@ -0,0 +1,4 @@ +add_project_library(common_fs_devicefs_devices) + +target_include_directories(kernel PUBLIC + ../../../../include/fs/devicefs/devices) diff --git a/common/source/fs/minixfs/CMakeLists.txt b/common/source/fs/minixfs/CMakeLists.txt index 3bc99ee70..4f6a2ed59 100644 --- a/common/source/fs/minixfs/CMakeLists.txt +++ b/common/source/fs/minixfs/CMakeLists.txt @@ -1,3 +1,4 @@ -include_directories(../../../include/fs/minixfs) +add_project_library(common_fs_minixfs) -add_project_library(common_fs_minixfs) \ No newline at end of file +target_include_directories(kernel PUBLIC + ../../../include/fs/minixfs) diff --git a/common/source/fs/minixfs/MinixFSFile.cpp b/common/source/fs/minixfs/MinixFSFile.cpp index 76940cc83..72b4d192b 100644 --- a/common/source/fs/minixfs/MinixFSFile.cpp +++ b/common/source/fs/minixfs/MinixFSFile.cpp @@ -1,46 +1,12 @@ #include "MinixFSFile.h" -#include "MinixFSInode.h" + +#include "File.h" #include "Inode.h" +#include "MinixFSInode.h" MinixFSFile::MinixFSFile(Inode* inode, Dentry* dentry, uint32 flag) : - File(inode, dentry, flag) + SimpleFile(inode, dentry, flag) { - f_superblock_ = inode->getSuperblock(); - offset_ = 0; -} - -MinixFSFile::~MinixFSFile() -{ -} - -int32 MinixFSFile::read(char *buffer, size_t count, l_off_t offset) -{ - if (((flag_ & O_RDONLY) || (flag_ & O_RDWR)) && (f_inode_->getMode() & A_READABLE)) - { - int32 read_bytes = f_inode_->readData(offset_ + offset, count, buffer); - offset_ += read_bytes; - return read_bytes; - } - else - { - // ERROR_FF - return -1; - } -} - -int32 MinixFSFile::write(const char *buffer, size_t count, l_off_t offset) -{ - if (((flag_ & O_WRONLY) || (flag_ & O_RDWR)) && (f_inode_->getMode() & A_WRITABLE)) - { - int32 written = f_inode_->writeData(offset_ + offset, count, buffer); - offset_ += written; - return written; - } - else - { - // ERROR_FF - return -1; - } } int32 MinixFSFile::flush() @@ -48,4 +14,3 @@ int32 MinixFSFile::flush() ((MinixFSInode *) f_inode_)->flush(); return 0; } - diff --git a/common/source/fs/minixfs/MinixFSInode.cpp b/common/source/fs/minixfs/MinixFSInode.cpp index 171a76a97..71559bb25 100644 --- a/common/source/fs/minixfs/MinixFSInode.cpp +++ b/common/source/fs/minixfs/MinixFSInode.cpp @@ -1,23 +1,25 @@ #include "MinixFSInode.h" + +#include "Dentry.h" +#include "MinixFSFile.h" +#include "MinixFSSuperblock.h" + +#include "assert.h" + #ifndef EXE2MINIXFS #include "kstring.h" #endif -#include -#include "MinixFSSuperblock.h" -#include "MinixFSFile.h" -#include "Dentry.h" MinixFSInode::MinixFSInode(Superblock *super_block, uint32 inode_type) : Inode(super_block, inode_type), - i_zones_(0), + i_zones_(nullptr), i_num_(0), children_loaded_(false) { debug(M_INODE, "Simple Constructor\n"); } -MinixFSInode::MinixFSInode(Superblock *super_block, uint16 i_mode, uint32 i_size, uint16 i_nlinks, uint32* i_zones, - uint32 i_num) : +MinixFSInode::MinixFSInode(Superblock *super_block, uint16 i_mode, uint32 i_size, uint16 i_nlinks, uint32* i_zones, uint32 i_num) : Inode(super_block, 0), i_zones_(new MinixFSZone((MinixFSSuperblock*) super_block, i_zones)), i_num_(i_num), children_loaded_(false) { @@ -34,25 +36,31 @@ MinixFSInode::MinixFSInode(Superblock *super_block, uint16 i_mode, uint32 i_size } else { - debug(M_INODE, "i_mode = %x\n", i_mode); - assert(false); + debugAlways(M_INODE, "i_mode = %x\n", i_mode); + assert(false && "Unsupported inode mode"); } // (hard/sym link/...) not handled! - debug(M_INODE, "Constructor: size: %d\tnlink: %d\tnum zones: %d\tmode: %x\n", i_size_, numLinks(), - i_zones_->getNumZones(), i_mode); + debug(M_INODE, "Constructor: %p, i_num: %u, size: %d, nlink: %d, num zones: %d, mode: %x\n", + this, i_num_, i_size_, numLinks(), i_zones_->getNumZones(), i_mode); + + if (M_ZONE & OUTPUT_ENABLED) + i_zones_->printZones(); } MinixFSInode::~MinixFSInode() { - debug(M_INODE, "Destructor\n"); + debug(M_INODE, "Destructor %p, i_num: %u, size: %u\n", this, i_num_, i_size_); + if (M_ZONE & OUTPUT_ENABLED) + i_zones_->printZones(); + delete i_zones_; } int32 MinixFSInode::readData(uint32 offset, uint32 size, char *buffer) { assert(buffer); - debug(M_INODE, "readData: offset: %d, size; %d,i_size_: %d\n", offset, size, i_size_); + debug(M_INODE, "readData: i_num: %u, offset: %d, size; %d, i_size_: %d\n", i_num_, offset, size, i_size_); if ((size + offset) > i_size_) { if (i_size_ <= offset) @@ -70,10 +78,15 @@ int32 MinixFSInode::readData(uint32 offset, uint32 size, char *buffer) for (uint32 zone = start_zone; zone < start_zone + num_zones; zone++) { memset(rbuffer, 0, sizeof(rbuffer)); - ((MinixFSSuperblock *) superblock_)->readZone(i_zones_->getZone(zone), rbuffer); + auto zone_num = i_zones_->getZone(zone); + + ((MinixFSSuperblock *) superblock_)->readZone(zone_num, rbuffer); uint32 count = size - index; uint32 zone_diff = ZONE_SIZE - zone_offset; count = count < zone_diff ? count : zone_diff; + + debug(M_INODE, "readData: zone[%u] = %x, count: %u\n", zone, zone_num, count); + memcpy(buffer + index, rbuffer + zone_offset, count); index += count; zone_offset = 0; @@ -83,10 +96,10 @@ int32 MinixFSInode::readData(uint32 offset, uint32 size, char *buffer) int32 MinixFSInode::writeData(uint32 offset, uint32 size, const char *buffer) { - debug(M_INODE, "MinixFSInode writeData> offset: %d, size: %d, i_size_: %d\n", offset, size, i_size_); + debug(M_INODE, "writeData: i_num: %u, offset: %d, size: %d, i_size_: %d\n", i_num_, offset, size, i_size_); uint32 zone = offset / ZONE_SIZE; uint32 num_zones = (offset % ZONE_SIZE + size) / ZONE_SIZE + 1; - + if (num_zones*ZONE_SIZE < size) return -1; uint32 last_used_zone = i_size_ / ZONE_SIZE; @@ -136,7 +149,7 @@ int32 MinixFSInode::writeData(uint32 offset, uint32 size, const char *buffer) for (uint32 zone_index = 0; zone_index < num_zones; zone_index++) { debug(M_INODE, "writeData: writing zone_index: %d, i_zones_->getZone(zone) : %d\n", zone_index, - i_zones_->getZone(zone)); + i_zones_->getZone(zone_index)); ((MinixFSSuperblock *) superblock_)->writeZone(i_zones_->getZone(zone_index + zone), wbuffer); wbuffer += ZONE_SIZE; } @@ -151,7 +164,7 @@ int32 MinixFSInode::writeData(uint32 offset, uint32 size, const char *buffer) int32 MinixFSInode::mknod(Dentry *dentry) { Inode::mknod(dentry); - debug(M_INODE, "mknod: dentry: %p, i_type_: %x\n", dentry, i_type_); + debug(M_INODE, "mknod: i_num: %u, dentry: %p, i_type_: %x\n", i_num_, dentry, i_type_); ((MinixFSInode *) dentry->getParent()->getInode())->writeDentry(0, i_num_, dentry->getName()); return 0; @@ -160,7 +173,7 @@ int32 MinixFSInode::mknod(Dentry *dentry) int32 MinixFSInode::mkfile(Dentry *dentry) { Inode::mkfile(dentry); - debug(M_INODE, "mkfile: dentry: %p (%s)\n", dentry, dentry->getName()); + debug(M_INODE, "mkfile: i_num: %u, dentry: %p (%s)\n", i_num_, dentry, dentry->getName()); ((MinixFSInode *) dentry->getParent()->getInode())->writeDentry(0, i_num_, dentry->getName()); return 0; @@ -169,7 +182,7 @@ int32 MinixFSInode::mkfile(Dentry *dentry) int32 MinixFSInode::mkdir(Dentry *dentry) { Inode::mkdir(dentry); - debug(M_INODE, "mkdir: dentry: %p (%s)\n", dentry, dentry->getName()); + debug(M_INODE, "mkdir: i_num: %u, dentry: %p (%s)\n", i_num_, dentry, dentry->getName()); MinixFSInode* parent_inode = ((MinixFSInode *) dentry->getParent()->getInode()); assert(parent_inode->getType() == I_DIR); @@ -188,7 +201,7 @@ int32 MinixFSInode::mkdir(Dentry *dentry) int32 MinixFSInode::findDentry(uint32 i_num) { - debug(M_INODE, "findDentry: i_num: %d\n", i_num); + debug(M_INODE, "findDentry: i_num: %u\n", i_num); char dbuffer[ZONE_SIZE]; for (uint32 zone = 0; zone < i_zones_->getNumZones(); zone++) { @@ -219,20 +232,26 @@ void MinixFSInode::writeDentry(uint32 dest_i_num, uint32 src_i_num, const char* } char dbuffer[ZONE_SIZE]; uint32 zone = i_zones_->getZone(dentry_pos / ZONE_SIZE); + debug(M_INODE, "writeDentry: dentry pos: %d, zone: %u\n", dentry_pos, zone); + ((MinixFSSuperblock *) superblock_)->readZone(zone, dbuffer); - *(uint16*) (dbuffer + (dentry_pos % ZONE_SIZE)) = src_i_num; - strncpy(dbuffer + dentry_pos % ZONE_SIZE + INODE_BYTES, name, MAX_NAME_LENGTH); + + assert(dentry_pos >= 0); + char* dentry_write_pos = (dbuffer + (dentry_pos % ZONE_SIZE)); + *(uint16*)dentry_write_pos = src_i_num; // TODO: INODE_BYTES is either 2 or 4 depending on version, this always writes 2 bytes + + strncpy(dentry_write_pos + INODE_BYTES, name, MAX_NAME_LENGTH); + ((MinixFSSuperblock *) superblock_)->writeZone(zone, dbuffer); if (dest_i_num == 0 && i_size_ < (uint32) dentry_pos + INODE_SIZE) i_size_ += INODE_SIZE; - } File* MinixFSInode::open(Dentry* dentry, uint32 flag) { - debug(M_INODE, "Open file, flag: %x\n", flag); - assert(ustl::find(i_dentrys_.begin(), i_dentrys_.end(), dentry) != i_dentrys_.end()); + debug(M_INODE, "open: %p i_num_: %u, dentry name: %s, flag: %x\n", this, i_num_, dentry->getName(), flag); + assert(eastl::find(i_dentrys_.begin(), i_dentrys_.end(), dentry) != i_dentrys_.end()); File* file = (File*) (new MinixFSFile(this, dentry, flag)); i_files_.push_back(file); getSuperblock()->fileOpened(file); @@ -242,6 +261,7 @@ File* MinixFSInode::open(Dentry* dentry, uint32 flag) int32 MinixFSInode::link(Dentry* dentry) { + debug(M_INODE, "link: i_num_: %u, dentry name: %s\n", i_num_, dentry->getName()); int32 link_status = Inode::link(dentry); if(link_status) { @@ -255,6 +275,7 @@ int32 MinixFSInode::link(Dentry* dentry) int32 MinixFSInode::unlink(Dentry* dentry) { + debug(M_INODE, "unlink: i_num_: %u, dentry name: %s\n", i_num_, dentry->getName()); int32 unlink_status = Inode::unlink(dentry); if(unlink_status) { @@ -272,7 +293,7 @@ int32 MinixFSInode::rmdir(Dentry* dentry) assert(dentry->getParent() && (dentry->getParent()->getInode())); assert(getType() == I_DIR); - debug(M_INODE, "rmdir %s for inode %p\n", dentry->getName(), this); + debug(M_INODE, "rmdir %s for inode %p, i_num: %u\n", dentry->getName(), this, i_num_); MinixFSInode* parent_inode = static_cast(dentry->getParent()->getInode()); @@ -284,7 +305,7 @@ int32 MinixFSInode::rmdir(Dentry* dentry) { //if directory contains other entries than "." or ".." //-> directory not empty - debug(M_INODE, "Error: Cannot remove non-empty directory\n"); + debugAlways(M_INODE, "Error: Cannot remove non-empty directory\n"); return -1; } } @@ -312,20 +333,20 @@ Dentry* MinixFSInode::lookup(const char* name) assert(i_dentrys_.size() >= 1); - debug(M_INODE, "lookup: name: %s this->i_dentry_->getName(): %s \n", name, i_dentrys_.front()->getName()); - if (name == 0) + debug(M_INODE, "lookup: name: %s in inode %p, i_num: %u, this->i_dentry_->getName(): %s \n", name, this, i_num_, i_dentrys_.front()->getName()); + if (name == nullptr) { // ERROR_DNE - return 0; + return nullptr; } - Dentry* dentry_update = 0; + Dentry* dentry_update = nullptr; dentry_update = i_dentrys_.front()->checkName(name); if (dentry_update == 0) { // ERROR_NNE - return (Dentry*) 0; + return (Dentry*) nullptr; } else { @@ -340,9 +361,10 @@ Dentry* MinixFSInode::lookup(const char* name) void MinixFSInode::loadChildren() { + debug(M_INODE, "loadChildren of inode with i_num: %x\n", i_num_); if (children_loaded_) { - debug(M_INODE, "loadChildren: Children allready loaded\n"); + debug(M_INODE, "loadChildren: Children already loaded\n"); return; } char dbuffer[ZONE_SIZE]; @@ -354,14 +376,18 @@ void MinixFSInode::loadChildren() uint16 inode_index = *(uint16*) (dbuffer + curr_dentry); if (inode_index) { - debug(M_INODE, "loadChildren: loading child %d\n", inode_index); + debug(M_INODE, "loadChildren: loading child i_num: %d\n", inode_index); bool is_already_loaded = false; + char name[MAX_NAME_LENGTH + 1]; + strncpy(name, dbuffer + curr_dentry + INODE_BYTES, MAX_NAME_LENGTH); + name[MAX_NAME_LENGTH] = 0; + MinixFSInode* inode = ((MinixFSSuperblock *) superblock_)->getInode(inode_index, is_already_loaded); if (!inode) { - kprintfd("MinixFSInode::loadChildren: inode nr. %d not set in bitmap, but occurs in directory-entry; " + debug(M_INODE, "MinixFSInode::loadChildren: inode nr. %d not set in bitmap, but occurs in directory-entry; " "maybe filesystem was not properly unmounted last time\n", inode_index); char ch = 0; @@ -369,12 +395,7 @@ void MinixFSInode::loadChildren() continue; } - char name[MAX_NAME_LENGTH + 1]; - strncpy(name, dbuffer + curr_dentry + INODE_BYTES, MAX_NAME_LENGTH); - - name[MAX_NAME_LENGTH] = 0; - - debug(M_INODE, "loadChildren: dentry name: %s\n", name); + debug(M_INODE, "loadChildren: dentry name: %s, inode: %p, i_num: %u\n", name, inode, inode->i_num_); assert(i_dentrys_.size() >= 1); Dentry *new_dentry = new Dentry(inode, i_dentrys_.front(), name); inode->i_dentrys_.push_back(new_dentry); @@ -387,11 +408,12 @@ void MinixFSInode::loadChildren() } } children_loaded_ = true; + debug(M_INODE, "Finished loading children for inode with i_num: %x\n", i_num_); } int32 MinixFSInode::flush() { + debug(M_INODE, "flush: inode %p, i_num: %u\n", this, i_num_); superblock_->writeInode(this); - debug(M_INODE, "flush: flushed\n"); return 0; } diff --git a/common/source/fs/minixfs/MinixFSSuperblock.cpp b/common/source/fs/minixfs/MinixFSSuperblock.cpp index 9a01461b5..7e78253c3 100644 --- a/common/source/fs/minixfs/MinixFSSuperblock.cpp +++ b/common/source/fs/minixfs/MinixFSSuperblock.cpp @@ -1,9 +1,16 @@ -#include "FileDescriptor.h" -#include "MinixFSType.h" #include "MinixFSSuperblock.h" -#include "MinixFSInode.h" + +#include "Dentry.h" +#include "FileDescriptor.h" #include "MinixFSFile.h" +#include "MinixFSInode.h" +#include "MinixFSType.h" +#include "kstring.h" + +#include "EASTL/unique_ptr.h" + #include "assert.h" +#include "debug.h" #ifdef EXE2MINIXFS #include @@ -12,58 +19,128 @@ #include "BDVirtualDevice.h" #endif + #define ROOT_NAME "/" -MinixFSSuperblock::MinixFSSuperblock(MinixFSType* fs_type, size_t s_dev, uint64 offset) : - Superblock(fs_type, s_dev), superblock_(this), offset_(offset) +MinixFSSuperblock::MinixFSSuperblock(MinixFSType* fs_type, size_t s_dev, uint64 offset, uint64 partition_size) : + Superblock(fs_type, s_dev), superblock_(this), offset_(offset), size_(partition_size) { //read Superblock data from disc readHeader(); - debug(M_SB, "s_num_inodes_ : %d\ns_zones_ : %d\ns_num_inode_bm_blocks_ : %d\ns_num_zone_bm_blocks_ : %d\n" - "s_1st_datazone_ : %d\ns_log_zone_size_ : %d\ns_max_file_size_ : %d\ns_block_size_ : %d\ns_magic_ : %llu\n", - s_num_inodes_, s_zones_, s_num_inode_bm_blocks_, s_num_zone_bm_blocks_, s_1st_datazone_, s_log_zone_size_, - s_max_file_size_, s_block_size_, (long long unsigned)s_magic_); + debug(M_SB, + "s_num_inodes_ : %d\n" + "s_zones_ : %d\n" + "s_num_inode_bm_blocks_ : %d\n" + "s_num_zone_bm_blocks_ : %d\n" + "s_1st_datazone_ : %d\n" + "s_log_zone_size_ : %d\n" + "s_max_file_size_ : %d\n" + "s_block_size_ : %d\n" + "s_magic_ : %llu\n", + s_num_inodes_, s_zones_, s_num_inode_bm_blocks_, s_num_zone_bm_blocks_, s_1st_datazone_, s_log_zone_size_, s_max_file_size_, s_block_size_, (long long unsigned)s_magic_); assert(s_log_zone_size_ == 0); - assert(s_block_size_ == 1024); + assert(s_block_size_ == BLOCK_SIZE); //create Storage Manager uint32 bm_size = s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_; - char* bm_buffer = new char[BLOCK_SIZE * bm_size]; - readBlocks(2, bm_size, bm_buffer); + auto bm_buffer = eastl::make_unique(BLOCK_SIZE * bm_size); + readBlocks(2, bm_size, bm_buffer.get()); debug(M_SB, "---creating Storage Manager\n"); - storage_manager_ = new MinixStorageManager(bm_buffer, s_num_inode_bm_blocks_, s_num_zone_bm_blocks_, s_num_inodes_, + storage_manager_ = new MinixStorageManager(bm_buffer.get(), s_num_inode_bm_blocks_, s_num_zone_bm_blocks_, s_num_inodes_, s_zones_); if (M_STORAGE_MANAGER & OUTPUT_ENABLED) { storage_manager_->printBitmap(); } - delete[] bm_buffer; initInodes(); + debug(M_SB, "MinixFSSuperblock ctor finished\n"); +} + +MinixFSSuperblock::~MinixFSSuperblock() +{ + debug(M_SB, "~MinixSuperblock\n"); + assert(dirty_inodes_.empty() == true); + + storage_manager_->flush(this); + + debug(M_SB, "Release open files\n"); + releaseAllOpenFiles(); + + if (M_SB & OUTPUT_ENABLED) + { + for (auto* it : all_inodes_) + debug(M_SB, "Inode: %p\n", it); + } + + // Also writes back inodes to disk + debug(M_SB, "Delete inodes and write back to disk\n"); + deleteAllInodes(); + all_inodes_set_.clear(); + + delete storage_manager_; + + debug(M_SB, "~MinixSuperblock finished\n"); } void MinixFSSuperblock::readHeader() { char buffer[BLOCK_SIZE]; readBlocks(1, 1, buffer); - s_magic_ = ((uint16*) buffer)[12]; - s_num_inodes_ = V3_ARRAY(buffer,0); - if (s_magic_ == MINIX_V3) + + auto* v3_sb = (MinixFSSuperblockOnDiskDataV3*)&buffer; + auto* v1_sb = (MinixFSSuperblockOnDiskDataV1*)&buffer; + + if(v1_sb->s_magic == MINIX_V1) + { + s_magic_ = v1_sb->s_magic; + debug(M_SB, "Found minixfs v1, magic value: %x\n", v1_sb->s_magic); + } + else if(v3_sb->s_magic == MINIX_V3) { - s_block_size_ = ((uint16*) buffer)[14]; - s_disk_version_ = buffer[30]; - s_zones_ = ((uint32*) buffer)[5]; + s_magic_ = v3_sb->s_magic; + debug(M_SB, "Found minixfs v3, magic value: %x\n", v3_sb->s_magic); } else { - s_magic_ = ((uint16*) buffer)[8]; - s_zones_ = ((uint16*) buffer)[1]; - s_block_size_ = 1024; + debugAlways(M_SB, "ERROR: Unknown magic value for minixfs superblock, V1: %x, V3: %x\n", v1_sb->s_magic, v3_sb->s_magic); + assert(false && "Unknown minixfs version (or not minixfs at all?)"); } - s_num_inode_bm_blocks_ = ((uint16*) buffer)[2 + V3_OFFSET]; - s_num_zone_bm_blocks_ = ((uint16*) buffer)[3 + V3_OFFSET]; - s_1st_datazone_ = ((uint16*) buffer)[4 + V3_OFFSET]; - s_log_zone_size_ = ((uint16*) buffer)[5 + V3_OFFSET]; - s_max_file_size_ = ((uint32*) buffer)[3 + V3_OFFSET]; + + s_num_inodes_ = (s_magic_ == MINIX_V3 ? v3_sb->s_num_inodes : + (s_magic_ == MINIX_V1 ? v1_sb->s_num_inodes : + 0)); + + s_block_size_ = (s_magic_ == MINIX_V3 ? v3_sb->s_blocksize : + (s_magic_ == MINIX_V1 ? 1024 : + 0)); + + s_disk_version_ = (s_magic_ == MINIX_V3 ? v3_sb->s_disk_version : + (s_magic_ == MINIX_V1 ? 0 : + 0)); + + s_zones_ = (s_magic_ == MINIX_V3 ? v3_sb->s_num_zones : + (s_magic_ == MINIX_V1 ? v1_sb->s_num_zones : + 0)); + + s_num_inode_bm_blocks_ = (s_magic_ == MINIX_V3 ? v3_sb->s_imap_blocks : + (s_magic_ == MINIX_V1 ? v1_sb->s_imap_blocks : + 0)); + + s_num_zone_bm_blocks_ = (s_magic_ == MINIX_V3 ? v3_sb->s_zmap_blocks : + (s_magic_ == MINIX_V1 ? v1_sb->s_zmap_blocks : + 0)); + + s_1st_datazone_ = (s_magic_ == MINIX_V3 ? v3_sb->s_firstdatazone : + (s_magic_ == MINIX_V1 ? v1_sb->s_firstdatazone : + 0)); + + s_log_zone_size_ = (s_magic_ == MINIX_V3 ? v3_sb->s_log_zone_size : + (s_magic_ == MINIX_V1 ? v1_sb->s_log_zone_size : + 0)); + + s_max_file_size_ = (s_magic_ == MINIX_V3 ? v3_sb->s_max_file_size : + (s_magic_ == MINIX_V1 ? v1_sb->s_max_file_size : + 0)); } void MinixFSSuperblock::initInodes() @@ -97,7 +174,7 @@ MinixFSInode* MinixFSSuperblock::getInode(uint16 i_num) if (i_num >= storage_manager_->getNumUsedInodes()) { debug(M_SB, "getInode::bad inode number %d\n", i_num); - return 0; + return nullptr; } if (!storage_manager_->isInodeSet(i_num)) @@ -105,55 +182,44 @@ MinixFSInode* MinixFSSuperblock::getInode(uint16 i_num) if (i_num == 1) assert(storage_manager_->isInodeSet(1)); - return 0; + return nullptr; } - uint32 inodes_start = s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_ + 2; - uint32 inode_block_num = inodes_start + (i_num - 1) / INODES_PER_BLOCK; - MinixFSInode *inode = 0; + uint32 first_inode_block = 2 + s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_; + uint32 inode_block_num = first_inode_block + (i_num - 1) / INODES_PER_BLOCK; + uint32 byte_offset = ((i_num - 1) % INODES_PER_BLOCK) * INODE_SIZE; + MinixFSInode *inode = nullptr; + char ibuffer_array[BLOCK_SIZE]; char* ibuffer = ibuffer_array; + debug(M_SB, "getInode::reading block num: %d\n", inode_block_num); readBlocks(inode_block_num, 1, ibuffer); debug(M_SB, "getInode:: returned reading block num: %d\n", inode_block_num); - uint32 offset = ((i_num - 1) % INODES_PER_BLOCK) * INODE_SIZE; - debug(M_SB, "getInode:: setting offset: %d\n", offset); - ibuffer += offset; + + debug(M_SB, "getInode:: setting offset: %d\n", byte_offset); + ibuffer += byte_offset; + auto* idata_v1 = (MinixFSInode::MinixFSInodeOnDiskDataV1*)ibuffer; + auto* idata_v3 = (MinixFSInode::MinixFSInodeOnDiskDataV3*)ibuffer; + uint32 i_zones[NUM_ZONES]; - for (uint32 num_zone = 0; num_zone < NUM_ZONES; num_zone++) + for (uint32 num_zone = 0; num_zone < NUM_ZONES; ++num_zone) { - i_zones[num_zone] = V3_ARRAY(ibuffer, 7 - V3_OFFSET + num_zone); + i_zones[num_zone] = (s_magic_ == MINIX_V3 ? idata_v3->i_zone[num_zone] : + idata_v1->i_zone[num_zone]); } + debug(M_SB, "getInode:: calling creating Inode\n"); - inode = new MinixFSInode(this, ((uint16*) ibuffer)[0], ((uint32*) ibuffer)[1 + V3_OFFSET], - ((uint16*) ibuffer)[V3_OFFSET], i_zones, i_num); + uint16 i_mode = (s_magic_ == MINIX_V3 ? idata_v3->i_mode : + idata_v1->i_mode); + uint32 i_size = (s_magic_ == MINIX_V3 ? idata_v3->i_size : + idata_v1->i_size); + uint32 i_nlinks = (s_magic_ == MINIX_V3 ? idata_v3->i_nlinks : + idata_v1->i_nlinks); + inode = new MinixFSInode(this, i_mode, i_size, i_nlinks, i_zones, i_num); debug(M_SB, "getInode:: returned creating Inode\n"); return inode; } -MinixFSSuperblock::~MinixFSSuperblock() -{ - debug(M_SB, "~MinixSuperblock\n"); - assert(dirty_inodes_.empty() == true); - storage_manager_->flush(this); - - releaseAllOpenFiles(); - - debug(M_SB, "Open files released\n"); - if (M_SB & OUTPUT_ENABLED) - { - for (auto it : all_inodes_) - debug(M_SB, "Inode: %p\n", it); - } - - // Also writes back inodes to disk - deleteAllInodes(); - all_inodes_set_.clear(); - - delete storage_manager_; - - debug(M_SB, "~MinixSuperblock finished\n"); -} - Inode* MinixFSSuperblock::createInode(uint32 type) { uint16 mode = 0x01ff; @@ -166,7 +232,7 @@ Inode* MinixFSSuperblock::createInode(uint32 type) for (uint32 i = 0; i < NUM_ZONES; i++) zones[i] = 0; uint32 i_num = storage_manager_->allocInode(); - debug(M_SB, "createInode> acquired inode %d mode: %d\n", i_num, mode); + debug(M_SB, "createInode> allocated inode %d mode: %x\n", i_num, mode); Inode *inode = new MinixFSInode(this, mode, 0, 0, zones, i_num); debug(M_SB, "createInode> created Inode\n"); all_inodes_add_inode(inode); @@ -180,12 +246,12 @@ int32 MinixFSSuperblock::readInode(Inode* inode) { assert(inode); MinixFSInode *minix_inode = (MinixFSInode *) inode; - assert(ustl::find(all_inodes_.begin(), all_inodes_.end(), inode) != all_inodes_.end()); + assert(eastl::find(all_inodes_.begin(), all_inodes_.end(), inode) != all_inodes_.end()); uint32 block = 2 + s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_ + ((minix_inode->i_num_ - 1) * INODE_SIZE / BLOCK_SIZE); - uint32 offset = ((minix_inode->i_num_ - 1) * INODE_SIZE) % BLOCK_SIZE; + uint32 byte_offset = ((minix_inode->i_num_ - 1) * INODE_SIZE) % BLOCK_SIZE; char buffer[INODE_SIZE]; - readBytes(block, offset, INODE_SIZE, buffer); + readBytes(block, byte_offset, INODE_SIZE, buffer); uint32 *i_zones = new uint32[NUM_ZONES]; for (uint32 num_zone = 0; num_zone < NUM_ZONES; num_zone++) { @@ -206,17 +272,20 @@ int32 MinixFSSuperblock::readInode(Inode* inode) void MinixFSSuperblock::writeInode(Inode* inode) { assert(inode); - assert(ustl::find(all_inodes_.begin(), all_inodes_.end(), inode) != all_inodes_.end()); + assert(eastl::find(all_inodes_.begin(), all_inodes_.end(), inode) != all_inodes_.end()); //flush zones MinixFSInode *minix_inode = (MinixFSInode *) inode; uint32 block = 2 + s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_ + ((minix_inode->i_num_ - 1) * INODE_SIZE / BLOCK_SIZE); - uint32 offset = ((minix_inode->i_num_ - 1) * INODE_SIZE) % BLOCK_SIZE; + uint32 byte_offset = ((minix_inode->i_num_ - 1) * INODE_SIZE) % BLOCK_SIZE; + char buffer[INODE_SIZE]; memset((void*) buffer, 0, sizeof(buffer)); - debug(M_SB, "writeInode> reading block %d with offset %d from disc\n", block, offset); - readBytes(block, offset, INODE_SIZE, buffer); + + debug(M_SB, "writeInode> reading block %d with offset %d from disc\n", block, byte_offset); + readBytes(block, byte_offset, INODE_SIZE, buffer); debug(M_SB, "writeInode> read data from disc\n"); + debug(M_SB, "writeInode> the inode: i_type_: %d, i_nlink_: %d, i_size_: %d\n", minix_inode->i_type_, minix_inode->numLinks(), minix_inode->i_size_); if (minix_inode->i_type_ == I_FILE) @@ -232,15 +301,18 @@ void MinixFSSuperblock::writeInode(Inode* inode) else { // link etc. unhandled + debugAlways(M_SB, "WARNING: Unhandled file type, cannot write to disk!\n"); } + ((uint32*)buffer)[1+V3_OFFSET] = minix_inode->i_size_; debug(M_SB, "writeInode> write inode %p link count %u\n", inode, minix_inode->numLinks()); if (s_magic_ == MINIX_V3) ((uint16*)buffer)[1] = minix_inode->numLinks(); else buffer[13] = minix_inode->numLinks(); - debug(M_SB, "writeInode> writing bytes to disc on block %d with offset %d\n", block, offset); - writeBytes(block, offset, INODE_SIZE, buffer); + debug(M_SB, "writeInode> writing bytes to disc on block %d with offset %d\n", block, byte_offset); + writeBytes(block, byte_offset, INODE_SIZE, buffer); + debug(M_SB, "writeInode> flushing zones of inode %p\n", inode); minix_inode->i_zones_->flush(minix_inode->i_num_); } @@ -260,7 +332,7 @@ void MinixFSSuperblock::all_inodes_remove_inode(Inode* inode) void MinixFSSuperblock::deleteInode(Inode* inode) { assert(inode->getDentrys().size() == 0); - assert(ustl::find(used_inodes_.begin(), used_inodes_.end(), inode) == used_inodes_.end()); + assert(eastl::find(used_inodes_.begin(), used_inodes_.end(), inode) == used_inodes_.end()); dirty_inodes_.remove(inode); MinixFSInode *minix_inode = (MinixFSInode *) inode; all_inodes_remove_inode(minix_inode); @@ -269,10 +341,10 @@ void MinixFSSuperblock::deleteInode(Inode* inode) storage_manager_->freeInode(minix_inode->i_num_); uint32 block = 2 + s_num_inode_bm_blocks_ + s_num_zone_bm_blocks_ + ((minix_inode->i_num_ - 1) * INODE_SIZE / BLOCK_SIZE); - uint32 offset = ((minix_inode->i_num_ - 1) * INODE_SIZE) % BLOCK_SIZE; + uint32 byte_offset = ((minix_inode->i_num_ - 1) * INODE_SIZE) % BLOCK_SIZE; char buffer[INODE_SIZE]; memset((void*) buffer, 0, sizeof(buffer)); - writeBytes(block, offset, INODE_SIZE, buffer); + writeBytes(block, byte_offset, INODE_SIZE, buffer); delete inode; } @@ -281,6 +353,7 @@ uint16 MinixFSSuperblock::allocateZone() debug(M_ZONE, "MinixFSSuperblock allocateZone>\n"); uint16 ret = (storage_manager_->allocZone() + s_1st_datazone_ - 1); // -1 because the zone nr 0 is set in the bitmap and should never be used! debug(M_ZONE, "MinixFSSuperblock allocateZone> returning %d\n", ret); + assert(ret < s_zones_); return ret; } @@ -294,53 +367,60 @@ void MinixFSSuperblock::readBlocks(uint16 block, uint32 num_blocks, char* buffer { assert(buffer); #ifdef EXE2MINIXFS + size_t offset = offset_ + block * BLOCK_SIZE; size_t read_size = BLOCK_SIZE * num_blocks; - fseek((FILE*)s_dev_, offset_ + block * BLOCK_SIZE, SEEK_SET); + size_t read_end = -1; + assert(!__builtin_add_overflow(offset, read_size, &read_end) && read_end <= size_); + fseek((FILE*)s_dev_, offset, SEEK_SET); assert(fread(buffer, 1, read_size, (FILE*)s_dev_) == read_size); #else - BDVirtualDevice* bdvd = BDManager::getInstance()->getDeviceByNumber(s_dev_); + BDVirtualDevice* bdvd = BDManager::instance().getDeviceByNumber(s_dev_); size_t block_size = bdvd->getBlockSize(); size_t read_size = num_blocks * block_size; - assert(bdvd->readData(block * block_size, read_size, buffer) == (ssize_t)read_size && - "Disk read failed in MinixFSSuperblock::readBlocks()"); + assert((bdvd->readData(block * block_size, read_size, buffer) == (ssize_t)read_size) && + "Failed to read all requested blocks from device. (Potentially corrupted disk image)"); #endif } void MinixFSSuperblock::writeZone(uint16 zone, char* buffer) { + assert(zone < s_zones_); writeBlocks(zone, ZONE_SIZE / BLOCK_SIZE, buffer); } void MinixFSSuperblock::writeBlocks(uint16 block, uint32 num_blocks, char* buffer) { #ifdef EXE2MINIXFS + size_t offset = offset_ + block * BLOCK_SIZE; size_t write_size = BLOCK_SIZE * num_blocks; - fseek((FILE*)s_dev_, offset_ + block * BLOCK_SIZE, SEEK_SET); + size_t write_end = -1; + assert(!__builtin_add_overflow(offset, write_size, &write_end) && write_end <= size_); + fseek((FILE*)s_dev_, offset, SEEK_SET); assert(fwrite(buffer, 1, write_size, (FILE*)s_dev_) == write_size); #else - BDVirtualDevice* bdvd = BDManager::getInstance()->getDeviceByNumber(s_dev_); + BDVirtualDevice* bdvd = BDManager::instance().getDeviceByNumber(s_dev_); size_t block_size = bdvd->getBlockSize(); size_t write_size = num_blocks * block_size; - assert(bdvd->writeData(block * block_size, write_size, buffer) == (ssize_t)write_size && - "Disk write failed in MinixFSSuperblock::writeBlocks()"); + assert((bdvd->writeData(block * block_size, write_size, buffer) == (ssize_t)write_size) && + "Failed to write all requested blocks to device"); #endif } -int32 MinixFSSuperblock::readBytes(uint32 block, uint32 offset, uint32 size, char* buffer) +int32 MinixFSSuperblock::readBytes(uint32 block, uint32 byte_offset, uint32 size, char* buffer) { - assert(offset+size <= BLOCK_SIZE); + assert(byte_offset+size <= BLOCK_SIZE); char rbuffer[BLOCK_SIZE]; readBlocks(block, 1, rbuffer); - memcpy(rbuffer + offset, buffer, size); + memcpy(rbuffer + byte_offset, buffer, size); return size; } -int32 MinixFSSuperblock::writeBytes(uint32 block, uint32 offset, uint32 size, char* buffer) +int32 MinixFSSuperblock::writeBytes(uint32 block, uint32 byte_offset, uint32 size, char* buffer) { - assert(offset+size <= BLOCK_SIZE); + assert(byte_offset+size <= BLOCK_SIZE); char wbuffer[BLOCK_SIZE]; readBlocks(block, 1, wbuffer); - memcpy(wbuffer + offset, buffer, size); + memcpy(wbuffer + byte_offset, buffer, size); writeBlocks(block, 1, wbuffer); return size; } diff --git a/common/source/fs/minixfs/MinixFSType.cpp b/common/source/fs/minixfs/MinixFSType.cpp index 4eb48c500..c6011ffa2 100644 --- a/common/source/fs/minixfs/MinixFSType.cpp +++ b/common/source/fs/minixfs/MinixFSType.cpp @@ -1,7 +1,8 @@ #include "MinixFSType.h" -#include "MinixFSSuperblock.h" + #include "BDManager.h" #include "BDVirtualDevice.h" +#include "MinixFSSuperblock.h" MinixFSType::MinixFSType() : FileSystemType("minixfs") { @@ -9,12 +10,7 @@ MinixFSType::MinixFSType() : FileSystemType("minixfs") } -MinixFSType::~MinixFSType() -{ -} - - -Superblock *MinixFSType::readSuper(Superblock *superblock, void*) const +Superblock *MinixFSType::readSuper(Superblock *superblock, [[maybe_unused]]void* data) const { return superblock; } @@ -23,9 +19,12 @@ Superblock *MinixFSType::readSuper(Superblock *superblock, void*) const Superblock *MinixFSType::createSuper(uint32 s_dev) { if (s_dev == (uint32) -1) - return 0; + return nullptr; + + auto bdev = BDManager::instance().getDeviceByNumber(s_dev); + bdev->setBlockSize(BLOCK_SIZE); + uint64 size = bdev->getNumBlocks()*bdev->getBlockSize(); - BDManager::getInstance()->getDeviceByNumber(s_dev)->setBlockSize(BLOCK_SIZE); - Superblock *super = new MinixFSSuperblock(this, s_dev, 0); + Superblock *super = new MinixFSSuperblock(this, s_dev, 0, size); return super; } diff --git a/common/source/fs/minixfs/MinixFSZone.cpp b/common/source/fs/minixfs/MinixFSZone.cpp index e8a0b5d7f..072e6f22a 100644 --- a/common/source/fs/minixfs/MinixFSZone.cpp +++ b/common/source/fs/minixfs/MinixFSZone.cpp @@ -1,11 +1,14 @@ #include "MinixFSZone.h" + #include "MinixFSSuperblock.h" +#include "kprintf.h" +#include "minix_fs_consts.h" + +#include "assert.h" + #ifndef EXE2MINIXFS #include "kstring.h" #endif -#include "kprintf.h" -#include -#include "minix_fs_consts.h" MinixFSZone::MinixFSZone(MinixFSSuperblock *superblock, uint32 *zones) { @@ -32,7 +35,7 @@ MinixFSZone::MinixFSZone(MinixFSSuperblock *superblock, uint32 *zones) } else { - indirect_zones_ = 0; + indirect_zones_ = nullptr; } if (zones[8]) { @@ -57,14 +60,14 @@ MinixFSZone::MinixFSZone(MinixFSSuperblock *superblock, uint32 *zones) } else { - double_indirect_zones_[ind_zone] = 0; + double_indirect_zones_[ind_zone] = nullptr; } } } else { - double_indirect_zones_ = 0; - double_indirect_linking_zone_ = 0; + double_indirect_zones_ = nullptr; + double_indirect_linking_zone_ = nullptr; } if (M_ZONE & OUTPUT_ENABLED) @@ -109,6 +112,16 @@ MinixFSZone::~MinixFSZone() delete[] indirect_zones_; } +void MinixFSZone::printZones() +{ + debug(M_ZONE, "Used minixfs zones:\n"); + for (uint32_t i = 0, num_zones = getNumZones(); i < num_zones; ++i) + { + auto z = getZone(i); + debug(M_ZONE, "Zone[%u] = %x\n", i, z); + } +} + uint32 MinixFSZone::getZone(uint32 index) { assert(index < num_zones_); @@ -123,7 +136,7 @@ uint32 MinixFSZone::getZone(uint32 index) void MinixFSZone::setZone(uint32 index, uint32 zone) { - debug(M_ZONE, "MinixFSZone::setZone> index: %d, zone: %d\n", index, zone); + debug(M_ZONE, "MinixFSZone::setZone> index: %d, zone: %x\n", index, zone); if (index < 7) { direct_zones_[index] = zone; @@ -154,7 +167,7 @@ void MinixFSZone::setZone(uint32 index, uint32 zone) double_indirect_zones_ = new uint32*[NUM_ZONE_ADDRESSES]; for (uint32 i = 0; i < NUM_ZONE_ADDRESSES; i++) - double_indirect_zones_[i] = 0; + double_indirect_zones_[i] = nullptr; } if (!double_indirect_zones_[index / NUM_ZONE_ADDRESSES]) { @@ -173,15 +186,15 @@ void MinixFSZone::addZone(uint32 zone) setZone(num_zones_, zone); } -void MinixFSZone::flush(uint32 i_num) +void MinixFSZone::flush(uint32 inode_num) { - debug(M_ZONE, "MinixFSZone::flush i_num : %d; %p\n", i_num, this); + debug(M_ZONE, "MinixFSZone::flush i_num : %d; %p\n", inode_num, this); char buffer[NUM_ZONES * INODE_BYTES]; for (uint32 index = 0; index < NUM_ZONES; index++) SET_V3_ARRAY(buffer,index,direct_zones_[index]); uint32 block = 2 + superblock_->s_num_inode_bm_blocks_ + superblock_->s_num_zone_bm_blocks_ - + ((i_num - 1) * INODE_SIZE) / BLOCK_SIZE; - superblock_->writeBytes(block, ((i_num - 1) * INODE_SIZE) % BLOCK_SIZE + INODE_BYTES * (7 - V3_OFFSET), + + ((inode_num - 1) * INODE_SIZE) / BLOCK_SIZE; + superblock_->writeBytes(block, ((inode_num - 1) * INODE_SIZE) % BLOCK_SIZE + INODE_BYTES * (7 - V3_OFFSET), NUM_ZONES * INODE_BYTES, buffer); debug(M_ZONE, "MinixFSZone::flush direct written\n"); if (direct_zones_[7]) diff --git a/common/source/fs/minixfs/MinixStorageManager.cpp b/common/source/fs/minixfs/MinixStorageManager.cpp index 0ee31693a..48e5e2a34 100644 --- a/common/source/fs/minixfs/MinixStorageManager.cpp +++ b/common/source/fs/minixfs/MinixStorageManager.cpp @@ -1,15 +1,17 @@ #include "MinixStorageManager.h" + #include "MinixFSSuperblock.h" -#include -#include "kprintf.h" -MinixStorageManager::MinixStorageManager(char *bm_buffer, uint16 num_inode_bm_blocks, uint16 num_zone_bm_blocks, - uint16 num_inodes, uint16 num_zones) : +#include "EASTL/unique_ptr.h" + +#include "assert.h" +#include "debug.h" + +MinixStorageManager::MinixStorageManager(char *bm_buffer, uint16 num_inode_bm_blocks, uint16 num_zone_bm_blocks, uint16 num_inodes, uint16 num_zones) : StorageManager(num_inodes, num_zones) { debug(M_STORAGE_MANAGER, - "Constructor: num_inodes:%d\tnum_inode_bm_blocks:%d\tnum_zones:%d\tnum_zone_bm_blocks:%d\t\n", num_inodes, - num_inode_bm_blocks, num_zones, num_zone_bm_blocks); + "Constructor: num_inodes:%d\tnum_inode_bm_blocks:%d\tnum_zones:%d\tnum_zone_bm_blocks:%d\t\n", num_inodes, num_inode_bm_blocks, num_zones, num_zone_bm_blocks); num_inode_bm_blocks_ = num_inode_bm_blocks; num_zone_bm_blocks_ = num_zone_bm_blocks; @@ -80,7 +82,7 @@ size_t MinixStorageManager::allocZone() } } debug(M_STORAGE_MANAGER, "acquireZone: NO FREE ZONE FOUND!\n"); - assert(false); // full memory should have been checked. + assert(false && "No free minixfs zone found"); // full memory should have been checked. return 0; } @@ -100,7 +102,7 @@ size_t MinixStorageManager::allocInode() } } kprintfd("acquireInode: NO FREE INODE FOUND!\n"); - assert(false); // full memory should have been checked. + assert(false && "Unable to allocate new inode on minixfs, no free inode found"); // full memory should have been checked. return 0; } @@ -116,64 +118,24 @@ void MinixStorageManager::freeInode(size_t index) debug(M_STORAGE_MANAGER, "freeInode: Inode %zu freed\n", index); } -void MinixStorageManager::flush(MinixFSSuperblock *superblock) +void MinixStorageManager::flush([[maybe_unused]]MinixFSSuperblock *superblock) { debug(M_STORAGE_MANAGER, "flush: starting flushing\n"); - char* bm_buffer = new char[(num_inode_bm_blocks_ + num_zone_bm_blocks_) * BLOCK_SIZE]; - uint32 num_inodes = inode_bitmap_.getSize(); - uint32 i_byte = 0; - for (; i_byte < num_inodes / 8; i_byte++) - { - bm_buffer[i_byte] = inode_bitmap_.getByte(i_byte); - } - uint8 byte = 0; - for (uint32 i_bit = 0; i_bit < 8; i_bit++) - { - if (i_bit < num_inodes % 8) - { - if (inode_bitmap_.getBit(i_byte * 8 + i_bit)) - { - byte &= 0x01 << i_bit; - } - } - else - byte &= 0x01 << i_bit; - } - bm_buffer[i_byte] = byte; - ++i_byte; - for (; i_byte < num_inode_bm_blocks_ * BLOCK_SIZE; i_byte++) - { - bm_buffer[i_byte] = 0xff; - } + if (M_STORAGE_MANAGER & OUTPUT_ENABLED) + printBitmap(); + + auto buffer_block_size = num_inode_bm_blocks_ + num_zone_bm_blocks_; + auto buffer_size = buffer_block_size * BLOCK_SIZE; + + auto bm_buffer = eastl::make_unique(buffer_size); + memset(bm_buffer.get(), 0, buffer_size); + + memcpy(bm_buffer.get(), inode_bitmap_.data(), inode_bitmap_.getBytesSize()); + memcpy(bm_buffer.get() + num_inode_bm_blocks_*BLOCK_SIZE, zone_bitmap_.data(), zone_bitmap_.getBytesSize()); + + + superblock->writeBlocks(2, buffer_block_size, bm_buffer.get()); - uint32 num_zones = zone_bitmap_.getSize(); - uint32 z_byte = 0; - for (; z_byte < num_zones / 8; z_byte++, i_byte++) - { - bm_buffer[i_byte] = zone_bitmap_.getByte(z_byte); - } - byte = 0; - for (uint32 z_bit = 0; z_bit < 8; z_bit++) - { - if (z_bit < num_zones % 8) - { - if (zone_bitmap_.getBit(z_byte * 8 + z_bit)) - { - byte &= 0x01 << z_bit; - } - } - else - byte &= 0x01 << z_bit; - } - bm_buffer[i_byte] = byte; - ++z_byte; - ++i_byte; - for (; z_byte < num_zone_bm_blocks_ * BLOCK_SIZE; z_byte++, i_byte++) - { - bm_buffer[i_byte] = 0xff; - } - superblock->writeBlocks(2, num_inode_bm_blocks_ + num_zone_bm_blocks_, bm_buffer); - delete[] bm_buffer; debug(M_STORAGE_MANAGER, "flush: flushing finished\n"); } diff --git a/common/source/fs/minixfs/StorageManager.cpp b/common/source/fs/minixfs/StorageManager.cpp index 8f53b307d..690a57e22 100644 --- a/common/source/fs/minixfs/StorageManager.cpp +++ b/common/source/fs/minixfs/StorageManager.cpp @@ -4,8 +4,3 @@ StorageManager::StorageManager(uint16 num_inodes, uint16 num_zones) : inode_bitmap_(num_inodes), zone_bitmap_(num_zones) { } - -StorageManager::~StorageManager() -{ -} - diff --git a/common/source/fs/ramfs/CMakeLists.txt b/common/source/fs/ramfs/CMakeLists.txt index dc3b9b431..3b1e5f944 100644 --- a/common/source/fs/ramfs/CMakeLists.txt +++ b/common/source/fs/ramfs/CMakeLists.txt @@ -1,3 +1,4 @@ -include_directories(../../../include/fs/ramfs) +add_project_library(common_fs_ramfs) -add_project_library(common_fs_ramfs) \ No newline at end of file +target_include_directories(kernel PUBLIC + ../../../include/fs/ramfs) diff --git a/common/source/fs/ramfs/RamFSFile.cpp b/common/source/fs/ramfs/RamFSFile.cpp index 6d4cd08ba..0646dc466 100644 --- a/common/source/fs/ramfs/RamFSFile.cpp +++ b/common/source/fs/ramfs/RamFSFile.cpp @@ -1,64 +1,14 @@ -#include "fs/ramfs/RamFSInode.h" #include "fs/ramfs/RamFSFile.h" -#include "fs/ramfs/RamFSSuperblock.h" + #include "fs/Dentry.h" +#include "fs/ramfs/RamFSInode.h" +#include "fs/ramfs/RamFSSuperblock.h" #define ERROR_FRO "ERROR: The flag muss be READONLY for several opened files" #define ERROR_FF "ERROR: The flag does not allow this operation" #define ERROR_FNO "ERROR: The file is not open." RamFSFile::RamFSFile(Inode* inode, Dentry* dentry, uint32 flag) : - File(inode, dentry, flag) -{ - f_superblock_ = inode->getSuperblock(); - offset_ = 0; -} - -RamFSFile::~RamFSFile() -{ -} - -int32 RamFSFile::read(char *buffer, size_t count, l_off_t offset) -{ - if (((flag_ & O_RDONLY) || (flag_ & O_RDWR)) && (f_inode_->getMode() & A_READABLE)) - { - int32 read_bytes = f_inode_->readData(offset_ + offset, count, buffer); - offset_ += read_bytes; - return read_bytes; - } - else - { - // ERROR_FF - return -1; - } -} - -int32 RamFSFile::write(const char *buffer, size_t count, l_off_t offset) -{ - if (((flag_ & O_WRONLY) || (flag_ & O_RDWR)) && (f_inode_->getMode() & A_WRITABLE)) - { - int32 written_bytes = f_inode_->writeData(offset_ + offset, count, buffer); - offset_ += written_bytes; - return written_bytes; - } - else - { - // ERROR_FF - return -1; - } -} - -int32 RamFSFile::open(uint32 __attribute__((unused)) flag) -{ - return 0; -} - -int32 RamFSFile::close() -{ - return 0; -} - -int32 RamFSFile::flush() + SimpleFile(inode, dentry, flag) { - return 0; } diff --git a/common/source/fs/ramfs/RamFSInode.cpp b/common/source/fs/ramfs/RamFSInode.cpp index 0926bec2c..cb0cf1523 100644 --- a/common/source/fs/ramfs/RamFSInode.cpp +++ b/common/source/fs/ramfs/RamFSInode.cpp @@ -1,18 +1,19 @@ #include "fs/ramfs/RamFSInode.h" -#include "kstring.h" -#include "assert.h" -#include "fs/ramfs/RamFSSuperblock.h" -#include "fs/ramfs/RamFSFile.h" -#include "fs/Dentry.h" + #include "FileSystemType.h" +#include "fs/Dentry.h" +#include "fs/ramfs/RamFSFile.h" +#include "fs/ramfs/RamFSSuperblock.h" +#include "kstring.h" -#include "console/kprintf.h" +#include "assert.h" +#include "debug.h" #define BASIC_ALLOC 256 RamFSInode::RamFSInode(Superblock *super_block, uint32 inode_type) : Inode(super_block, inode_type), - data_(0) + data_(nullptr) { debug(RAMFS, "New RamFSInode %p\n", this); if (inode_type == I_FILE) @@ -28,6 +29,38 @@ RamFSInode::~RamFSInode() delete[] data_; } +int32 RamFSInode::mkdir(Dentry *dentry) +{ + Inode::mkdir(dentry); + + if (!new Dentry(this, dentry, ".")) + return -1; + if (!new Dentry(dentry->getParent()->getInode(), dentry, "..")) + return -1; + + return 0; +} + +int32 RamFSInode::rmdir(Dentry* dentry) +{ + assert(dentry); + assert(dentry->getInode() == this); + assert(hasDentry(dentry)); + assert(getType() == I_DIR); + + if (!dentry->emptyChild({".", ".."})) + { + debug(RAMFS, "Error: %s inode %p has children, cannot unlink %s\n", + getSuperblock()->getFSType()->getFSName(), this, dentry->getName()); + return -1; + } + + delete lookup("."); + delete lookup(".."); + + return Inode::rmdir(dentry); +} + int32 RamFSInode::readData(uint32 offset, uint32 size, char *buffer) { if(offset >= getSize()) @@ -65,7 +98,7 @@ int32 RamFSInode::writeData(uint32 offset, uint32 size, const char *buffer) File* RamFSInode::open(Dentry* dentry, uint32 flag) { debug(INODE, "%s Inode: Open file\n", getSuperblock()->getFSType()->getFSName()); - assert(ustl::find(i_dentrys_.begin(), i_dentrys_.end(), dentry) != i_dentrys_.end()); + assert(eastl::find(i_dentrys_.begin(), i_dentrys_.end(), dentry) != i_dentrys_.end()); File* file = (File*) (new RamFSFile(this, dentry, flag)); i_files_.push_back(file); diff --git a/common/source/fs/ramfs/RamFSSuperblock.cpp b/common/source/fs/ramfs/RamFSSuperblock.cpp index 7fc7ab250..9647160bb 100644 --- a/common/source/fs/ramfs/RamFSSuperblock.cpp +++ b/common/source/fs/ramfs/RamFSSuperblock.cpp @@ -1,13 +1,14 @@ -#include "fs/FileDescriptor.h" #include "fs/ramfs/RamFSSuperblock.h" -#include "fs/ramfs/RamFSInode.h" + +#include "fs/Dentry.h" +#include "fs/FileDescriptor.h" #include "fs/ramfs/RamFSFile.h" +#include "fs/ramfs/RamFSInode.h" #include "fs/ramfs/RamFSType.h" -#include "fs/Dentry.h" + #include "assert.h" +#include "debug.h" -#include "console/kprintf.h" -#include "console/debug.h" #define ROOT_NAME "/" RamFSSuperblock::RamFSSuperblock(RamFSType* fs_type, uint32 s_dev) : @@ -40,7 +41,7 @@ int32 RamFSSuperblock::readInode(Inode* inode) { assert(inode); - if (ustl::find(all_inodes_, inode) == all_inodes_.end()) + if (eastl::find(all_inodes_.begin(), all_inodes_.end(), inode) == all_inodes_.end()) { all_inodes_.push_back(inode); } @@ -51,7 +52,7 @@ void RamFSSuperblock::writeInode(Inode* inode) { assert(inode); - if (ustl::find(all_inodes_, inode) == all_inodes_.end()) + if (eastl::find(all_inodes_.begin(), all_inodes_.end(), inode) == all_inodes_.end()) { all_inodes_.push_back(inode); } diff --git a/common/source/fs/ramfs/RamFSType.cpp b/common/source/fs/ramfs/RamFSType.cpp index 4546976a9..5b466ca47 100644 --- a/common/source/fs/ramfs/RamFSType.cpp +++ b/common/source/fs/ramfs/RamFSType.cpp @@ -1,22 +1,16 @@ #include "fs/ramfs/RamFSType.h" -#include "fs/ramfs/RamFSSuperblock.h" +#include "fs/ramfs/RamFSSuperblock.h" RamFSType::RamFSType() : FileSystemType("ramfs") { } - -RamFSType::~RamFSType() -{} - - Superblock *RamFSType::readSuper(Superblock *superblock, void*) const { return superblock; } - Superblock *RamFSType::createSuper (uint32 s_dev) { Superblock *super = new RamFSSuperblock(this, s_dev); diff --git a/common/source/kernel/BasicSpinLock.cpp b/common/source/kernel/BasicSpinLock.cpp new file mode 100644 index 000000000..4df4762ce --- /dev/null +++ b/common/source/kernel/BasicSpinLock.cpp @@ -0,0 +1,59 @@ +#include "BasicSpinLock.h" + +#include "Scheduler.h" + +#include "ArchCpuLocalStorage.h" +#include "ArchInterrupts.h" +#include "ArchThreads.h" + +BasicSpinLock::BasicSpinLock() : + lock_(0) +{ +} + +void BasicSpinLock::acquire(bool yield) +{ + Thread* calling_thread = CpuLocalStorage::ClsInitialized() ? currentThread : nullptr; + + while(lock_.test_and_set()) + { + ArchCommon::spinlockPause(); + //SpinLock: Simplest of Locks, do the next best thing to busy waiting + if(calling_thread && yield) + { + ArchInterrupts::yieldIfIFSet(); + } + } + + held_by_ = calling_thread; +} + +bool BasicSpinLock::acquireNonBlocking() +{ + Thread* calling_thread = CpuLocalStorage::ClsInitialized() ? currentThread : nullptr; + + bool got_lock = !lock_.test_and_set(); + + if (got_lock) + { + held_by_ = calling_thread; + } + + return got_lock; +} + +void BasicSpinLock::release() +{ + held_by_ = nullptr; + lock_.clear(); +} + +Thread* BasicSpinLock::heldBy() +{ + return held_by_; +} + +bool BasicSpinLock::isHeldBy(Thread* t) +{ + return heldBy() == t; +} diff --git a/common/source/kernel/BootloaderModules.cpp b/common/source/kernel/BootloaderModules.cpp new file mode 100644 index 000000000..407f67585 --- /dev/null +++ b/common/source/kernel/BootloaderModules.cpp @@ -0,0 +1,84 @@ +#include "BootloaderModules.h" + +#include "BDManager.h" +#include "BDVirtualDevice.h" +#include "PageManager.h" +#include "RamDiskDriver.h" +#include "offsets.h" + +#include "ArchCommon.h" +#include "ArchMemory.h" + +#include "debug.h" + +void BootloaderModules::reserveModulePages(Allocator& allocator) +{ + debug(MAIN, "Marking bootloader loaded modules as reserved\n"); + for (size_t i = 0; i < ArchCommon::getNumModules(); ++i) + { + size_t module_phys_start = (ArchCommon::getModuleStartAddress(i) - (size_t)PHYSICAL_TO_VIRTUAL_OFFSET); + size_t module_phys_end = (ArchCommon::getModuleEndAddress(i) - (size_t)PHYSICAL_TO_VIRTUAL_OFFSET); + debug(MAIN, "module [%s]: phys [%p, %p)\n", ArchCommon::getModuleName(i), (void*)module_phys_start, (void*)module_phys_end); + if(module_phys_end < module_phys_start) + continue; + + allocator.setUnuseable(module_phys_start, module_phys_end); + } +} + +void BootloaderModules::mapModules() +{ + debug(MAIN, "Mapping bootloader loaded modules\n"); + for (size_t i = 0; i < ArchCommon::getNumModules(); ++i) + { + size_t module_phys_start = (ArchCommon::getModuleStartAddress(i) - (size_t)PHYSICAL_TO_VIRTUAL_OFFSET); + size_t module_phys_end = (ArchCommon::getModuleEndAddress(i) - (size_t)PHYSICAL_TO_VIRTUAL_OFFSET); + debug(MAIN, "module [%s]: virt [%p, %p), phys [%p, %p)\n", ArchCommon::getModuleName(i), (void*)ArchCommon::getModuleStartAddress(i), (void*)ArchCommon::getModuleEndAddress(i), (void*)module_phys_start, (void*)module_phys_end); + if(module_phys_end < module_phys_start) + continue; + + size_t start_page = module_phys_start / PAGE_SIZE; + size_t end_page = (module_phys_end + PAGE_SIZE-1) / PAGE_SIZE; + for (size_t k = start_page; k < Min(end_page, PageManager::instance().getTotalNumPages()); ++k) + { + size_t vpage = (size_t)PHYSICAL_TO_VIRTUAL_OFFSET / PAGE_SIZE + k; + debugAdvanced(MAIN, "Mapping kernel module at %#zx -> %#zx\n", vpage, k); + + mmio_addr_allocator.setUnuseable(vpage*PAGE_SIZE, (vpage+1)*PAGE_SIZE); + + bool mapped = ArchMemory::mapKernelPage(vpage, k, true); + if (!mapped) + { + debug(MAIN, "Cannot map kernel module at %#zx, already mapped\n", k*PAGE_SIZE); + } + } + } + debug(MAIN, "Finished mapping modules\n"); +} + + +BDVirtualDevice* BootloaderModules::createRamDiskFromModule(int module_num, const char* name) +{ + size_t ramdisk_size = ArchCommon::getModuleEndAddress(module_num) - ArchCommon::getModuleStartAddress(module_num); + debug(MAIN, "Creating ram disk from module %s at [%zx, %zx), size: %zx\n", ArchCommon::getModuleName(module_num), ArchCommon::getModuleStartAddress(module_num), ArchCommon::getModuleEndAddress(module_num), ramdisk_size); + return RamDiskDriver::createRamDisk((void*)ArchCommon::getModuleStartAddress(module_num), ramdisk_size, name); +} + +void BootloaderModules::loadInitrdIfExists() +{ + // TODO: ArchCommon::getModuleEndAddress(i) -> getKernelEndAddress() crashes on arm rpi2 + + for(size_t i = 0; i < ArchCommon::getNumModules(); ++i) + { + debug(MAIN, "Checking module %zu: %s\n", i, ArchCommon::getModuleName(i)); + + if(strcmp(ArchCommon::getModuleName(i), "/boot/initrd") == 0) + { + debug(MAIN, "Initialize initrd\n"); + BDVirtualDevice* initrd_dev = createRamDiskFromModule(i, "initrd"); + initrd_dev->setPartitionType(0x81); + BDManager::instance().addVirtualDevice(initrd_dev); + break; + } + } +} diff --git a/common/source/kernel/CMakeLists.txt b/common/source/kernel/CMakeLists.txt index 922f5b98d..95d9ed791 100644 --- a/common/source/kernel/CMakeLists.txt +++ b/common/source/kernel/CMakeLists.txt @@ -1,3 +1,6 @@ -include_directories(../../include/kernel) add_project_library(common_kernel) +target_include_directories(kernel PUBLIC + ../../include/kernel) + +add_subdirectory(smp) diff --git a/common/source/kernel/CleanupThread.cpp b/common/source/kernel/CleanupThread.cpp index b076aaade..864cba7a2 100644 --- a/common/source/kernel/CleanupThread.cpp +++ b/common/source/kernel/CleanupThread.cpp @@ -1,8 +1,16 @@ #include "CleanupThread.h" + #include "Scheduler.h" +#include "VgaColors.h" + +#include "ArchMulticore.h" -CleanupThread::CleanupThread() : Thread(0, "CleanupThread", Thread::KERNEL_THREAD) +#include "assert.h" + +CleanupThread::CleanupThread() : + Thread(0, "CleanupThread", Thread::KERNEL_THREAD) { + console_color = CONSOLECOLOR::BROWN; } CleanupThread::~CleanupThread() @@ -17,10 +25,9 @@ void CleanupThread::kill() void CleanupThread::Run() { - while (1) + while (true) { Scheduler::instance()->cleanupDeadThreads(); Scheduler::instance()->yield(); } } - diff --git a/common/source/kernel/Condition.cpp b/common/source/kernel/Condition.cpp index e7b51b73d..04f22ea16 100644 --- a/common/source/kernel/Condition.cpp +++ b/common/source/kernel/Condition.cpp @@ -1,12 +1,15 @@ #include "Condition.h" -#include "Thread.h" + #include "Mutex.h" #include "Scheduler.h" -#include "assert.h" -#include "kprintf.h" -#include "debug.h" #include "Stabs2DebugInfo.h" +#include "SystemState.h" +#include "Thread.h" #include "backtrace.h" +#include "kprintf.h" + +#include "assert.h" +#include "debug.h" Condition::Condition(Mutex* mutex, const char* name) : Lock(name), mutex_(mutex) @@ -32,7 +35,7 @@ void Condition::wait(bool re_acquire_mutex, pointer called_by) if(!called_by) called_by = getCalledBefore(1); // debug(LOCK, "Condition::wait: Thread %s (%p) is waiting on condition %s (%p).\n", -// currentThread->getName(), currentThread, getName(), this); +// Scheduler::instance()->currentThread()->getName(), Scheduler::instance()->currentThread(), getName(), this); // if(kernel_debug_info) // { // debug(LOCK, "The wait is called by: "); @@ -64,7 +67,7 @@ void Condition::wait(bool re_acquire_mutex, pointer called_by) mutex_->release(called_by); sleepAndRelease(); // Thread has been woken up again - currentThread->lock_waiting_on_ = 0; + currentThread->lock_waiting_on_ = nullptr; if(re_acquire_mutex) { @@ -84,7 +87,7 @@ void Condition::signal(pointer called_by, bool broadcast) called_by = getCalledBefore(1); } // debug(LOCK, "Condition::signal: Thread %s (%p) is signaling condition %s (%p).\n", -// currentThread->getName(), currentThread, getName(), this); +// currentThread()->getName(), currentThread(), getName(), this); // debug(LOCK, "The signal is called by: "); // kernel_debug_info->printCallInformation(called_by); diff --git a/common/source/kernel/CpuExclusiveLock.cpp b/common/source/kernel/CpuExclusiveLock.cpp new file mode 100644 index 000000000..5004bd5f6 --- /dev/null +++ b/common/source/kernel/CpuExclusiveLock.cpp @@ -0,0 +1,69 @@ +#include "CpuExclusiveLock.h" + +#include "SMP.h" +#include "kprintf.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMulticore.h" + +#include "assert.h" + +CpuExclusiveLock::CpuExclusiveLock(const char* name) : + held_by_cpu_(-1), + name_(name) +{ +} + +// Thread must not be re-scheduled to other cpus while holding CPU exclusive locks! +// i.e. disable scheduling or pin thread to cpu +void CpuExclusiveLock::acquire([[maybe_unused]]pointer called_by) +{ + { + // CPU must not change between reading CPU ID and setting the lock value + WithInterrupts intr(false); + + size_t expected = -1; + size_t cpu_id = SMP::currentCpuId(); + + if(held_by_cpu_.load() == cpu_id) + { + kprintfd("ERROR: cpu exclusive lock %s already locked by cpu %zd\n", getName(), cpu_id); + assert(false && "CPU exclusive lock already held by current cpu"); + } + + while(!held_by_cpu_.compare_exchange_weak(expected, cpu_id)) + { + expected = -1; + ArchCommon::spinlockPause(); + } + } +} + +void CpuExclusiveLock::release([[maybe_unused]]pointer called_by) +{ + size_t cpu_id = SMP::currentCpuId(); + + size_t was_locked_by = held_by_cpu_.exchange(-1); + if(was_locked_by != cpu_id) + { + kprintfd("ERROR: CPU exclusive lock %s unlocked by CPU %zd, but was locked by CPU %zd\n", getName(), cpu_id, was_locked_by); + } + assert(was_locked_by == cpu_id); +} + + +const char* CpuExclusiveLock::getName() const +{ + return name_; +} + +bool CpuExclusiveLock::isHeldBy(size_t cpu_id) const +{ + return held_by_cpu_ == cpu_id; +} + +size_t CpuExclusiveLock::heldBy() const +{ + return held_by_cpu_; +} diff --git a/common/source/kernel/IdleThread.cpp b/common/source/kernel/IdleThread.cpp index 750b39110..7de35d414 100644 --- a/common/source/kernel/IdleThread.cpp +++ b/common/source/kernel/IdleThread.cpp @@ -1,18 +1,32 @@ #include "IdleThread.h" + #include "Scheduler.h" +#include "VgaColors.h" + #include "ArchCommon.h" +#include "ArchMulticore.h" + +#include "assert.h" +#include "debug.h" + +IdleThread::IdleThread() : + Thread(nullptr, "IdleThread", Thread::KERNEL_THREAD) +{ + console_color = CONSOLECOLOR::WHITE; +} -IdleThread::IdleThread() : Thread(0, "IdleThread", Thread::KERNEL_THREAD) +IdleThread::~IdleThread() { + assert(false && "Idle thread should not be destroyed"); } void IdleThread::Run() { uint32 last_ticks = 0; uint32 new_ticks = 0; - while (1) + while (true) { - new_ticks = Scheduler::instance()->getTicks(); + new_ticks = Scheduler::instance()->getCpuTimerTicks(); if (new_ticks == last_ticks) { last_ticks = new_ticks + 1; diff --git a/common/source/kernel/InitThread.cpp b/common/source/kernel/InitThread.cpp new file mode 100644 index 000000000..06f680995 --- /dev/null +++ b/common/source/kernel/InitThread.cpp @@ -0,0 +1,146 @@ +#include "InitThread.h" + +#include "BootloaderModules.h" +#include "DeviceBus.h" +#include "DeviceFSSuperblock.h" +#include "KernelMemoryManager.h" +#include "PageManager.h" +#include "ProcessRegistry.h" +#include "Scheduler.h" +#include "VfsSyscall.h" +#include "VirtualFileSystem.h" +#include "kprintf.h" + +#include "debug.h" + +InitThread* InitThread::instance_ = nullptr; + +void printInterruptMappings(); + +InitThread::InitThread(FileSystemInfo* root_fs_info, const char* progs[]) : + Thread(root_fs_info, "InitThread", Thread::KERNEL_THREAD), + progs_(progs) +{ +} + +InitThread::~InitThread() +{ +} + +void InitThread::init(FileSystemInfo* root_fs_info, const char* progs[]) +{ + assert(!instance_ && "InitThread already initialized"); + // Need to allocate this on the heap -> gets deleted by cleanup thread + instance_ = new InitThread(root_fs_info, progs); +} + +InitThread* InitThread::instance() +{ + assert(instance_ && "InitThread not yet initialized"); + return instance_; +} + + +void InitThread::Run() +{ + debug(INITTHREAD, "InitThread starting\n"); + + assert(ArchInterrupts::testIFSet()); + + debug(INITTHREAD, "Checking for initrd\n"); + BootloaderModules::loadInitrdIfExists(); + + debug(INITTHREAD, "Block Device creation\n"); + ArchCommon::initBlockDeviceDrivers(); + + debug(INITTHREAD, "Interrupt mappings:\n"); + printInterruptMappings(); + + debug(INITTHREAD, "Registered devices:\n"); + deviceTreeRoot().printSubDevices(); + + debug(INITTHREAD, "Add devices to devicefs\n"); + DeviceFSSuperBlock::getInstance()->addBlockDeviceInodes(); + DeviceFSSuperBlock::getInstance()->addDeviceInodes(deviceTreeRoot()); + + if (!progs_ || !progs_[0]) + return; + + size_t free_pages_pre = PageManager::instance().getNumFreePages(); + + debug(INITTHREAD, "mounting userprog-partition\n"); + + debug(INITTHREAD, "mkdir /usr\n"); + assert( !VfsSyscall::mkdir("/usr", 0) ); + + // Mount user partition (initrd if it exists, else partition 1 of IDE drive A) + bool usr_mounted = false; + if (VfsSyscall::mount("initrd", "/usr", "minixfs", 0) == 0) + { + debug(INITTHREAD, "initrd mounted at /usr\n"); + usr_mounted = true; + } + else if (VfsSyscall::mount("idea1", "/usr", "minixfs", 0) == 0) + { + debug(INITTHREAD, "idea1 mounted at /usr\n"); + usr_mounted = true; + } + + if (!usr_mounted) + kprintf("ERROR: Unable to mount userspace partition\n"); + assert(usr_mounted && "Unable to mount userspace partition"); + + debug(INITTHREAD, "mkdir /dev\n"); + assert( !VfsSyscall::mkdir("/dev", 0) ); + debug(INITTHREAD, "mount devicefs\n"); + assert( !VfsSyscall::mount(nullptr, "/dev", "devicefs", 0) ); + + KernelMemoryManager::instance()->startTracing(); + + debug(INITTHREAD, "Starting user processes\n"); + + for (uint32 i = 0; progs_[i]; i++) + { + debug(INITTHREAD, "Starting %s\n", progs_[i]); + kprintf("Starting %s\n", progs_[i]); + ProcessRegistry::instance()->createProcess(progs_[i]); + } + + ProcessRegistry::instance()->waitAllKilled(); + + kprintf("All processes terminated\n"); + + + + debug(INITTHREAD, "unmounting userprog-partition because all processes terminated\n"); + + VfsSyscall::umount("/usr", 0); + VfsSyscall::umount("/dev", 0); + + size_t free_pages_post = PageManager::instance().getNumFreePages(); + if ((free_pages_pre != free_pages_post) && !DYNAMIC_KMM) + { + PageManager::instance().printUsageInfo(); + debugAlways(PM, "WARNING: You might be leaking physical memory pages somewhere\n"); + debugAlways(PM, "%zu/%zu free physical pages after unmounting detected, difference: %zd\n", + free_pages_post, + free_pages_pre, + free_pages_pre - free_pages_post); + } + + vfs.rootUmount(); + + Scheduler::instance()->printStackTraces(); + Scheduler::instance()->printThreadList(); + + kill(); +} + +void printInterruptMappings() +{ + for (ArchCpu* cpu : SMP::cpuList()) + { + debug(MAIN, "CPU %zu IRQ mappings:\n", cpu->id()); + cpu->rootIrqDomain().printAllReverseMappings(); + } +} diff --git a/common/source/kernel/IrqDomain.cpp b/common/source/kernel/IrqDomain.cpp new file mode 100644 index 000000000..0918e5c18 --- /dev/null +++ b/common/source/kernel/IrqDomain.cpp @@ -0,0 +1,145 @@ +#include "IrqDomain.h" + +#include "debug.h" + +IrqDomain::IrqDomain(const eastl::string& name, + size_t num_irqs, + InterruptController* controller) : + controller_(controller), + name_(name) +{ + setNumIrqs(num_irqs); +} + +void IrqDomain::setNumIrqs(size_t num_irqs) +{ + irqs_.resize(num_irqs); +} + +// Unsafe if irq could be used concurrently (by any cpu) +void IrqDomain::mapIrqTo(irqnum_t irq, IrqDomain& target_domain, irqnum_t target_irq) +{ + assert(&target_domain); + debug(A_INTERRUPTS, "Mapping %s IRQ: %zx -> %s IRQ: %zx\n", name_.c_str(), irq, + target_domain.name().c_str(), target_irq); + + auto& info = irqs_.at(irq); + + if (irqs_[irq].map_to.domain) + debug(A_INTERRUPTS, "WARNING: %s IRQ: %zx already mapped to %s IRQ: %zx\n", + name_.c_str(), irq, info.map_to.domain->name().c_str(), + info.map_to.irqnum); + + info.map_to = {&target_domain, target_irq, nullptr}; + target_domain.irqMappedBy(target_irq, *this, irq, info.map_to); +} + +void IrqDomain::irqMappedBy(irqnum_t irq, + IrqDomain& source_domain, + irqnum_t source_irq, + IrqMappingTarget& mapped_by_entry) +{ + auto& info = irqs_.at(irq); + auto it = eastl::find_if( + info.mapped_by.begin(), info.mapped_by.end(), + [&](auto&& x) { return x.domain == &source_domain && x.irqnum == source_irq; }); + assert(it == info.mapped_by.end()); + + auto entry = new IrqMappingTarget{&source_domain, source_irq, &mapped_by_entry}; + mapped_by_entry.reverse_ref = entry; + info.mapped_by.push_back(*entry); +} + +// Unsafe if irq could be used concurrently (by any cpu) +void IrqDomain::removeIrq(irqnum_t irq) +{ + auto& info = irqs_.at(irq); + + auto& map_to = info.map_to; + debug(A_INTERRUPTS, "Remove IRQ mapping %s IRQ: %zx -> %s IRQ: %zx\n", name_.c_str(), + irq, map_to.domain->name().c_str(), map_to.irqnum); + + activateIrq(irq, false); + + assert(map_to.domain); + map_to.domain->removeIrqMappedBy(map_to.irqnum, *this, irq); + info = {}; +} + +void IrqDomain::removeIrqMappedBy(irqnum_t irq, + IrqDomain& source_domain, + irqnum_t source_irq) +{ + auto& info = irqs_.at(irq); + + auto& mapped_by = info.mapped_by; + auto m_it = eastl::find_if( + mapped_by.begin(), mapped_by.end(), + [&](auto&& x) { return x.domain == &source_domain && x.irqnum == source_irq; }); + assert(m_it != mapped_by.end()); + IrqMappingTarget* entry = &*m_it; + mapped_by.erase(m_it); + delete entry; +} + +void IrqDomain::setIrqHandler(irqnum_t irq, const IrqInfo::handler_func_t& handler) +{ + irqs_.at(irq).handler = handler; +} + +IrqDomain::IrqInfo::handler_func_t IrqDomain::getIrqHandler(irqnum_t irq) +{ + return irqs_.at(irq).handler; +} + +void IrqDomain::handleIrq(irqnum_t irqnum) +{ + debugAdvanced(A_INTERRUPTS, "Irq domain %s handle irq %zu\n", name().c_str(), irqnum); + auto info = irqInfo(irqnum); + assert(info); + if (info->handler) + { + info->handler(); + } +} + +void IrqDomain::activateIrq(irqnum_t irq, bool activate) +{ + debug(A_INTERRUPTS, "Irq domain %s %s irq %zu\n", name().c_str(), + activate ? "activate" : "deactivate", irq); + if (controller()) + { + controller()->mask(irq, !activate); + } +} + +IrqDomain::IrqInfo* IrqDomain::irqInfo(irqnum_t irqnum) +{ + auto& info = irqs_.at(irqnum); + return &info; +} + +void IrqDomain::printReverseMappingTree(irqnum_t irqnum) +{ + auto mtree = irq(irqnum).reverseMappingTree(); + for (auto it = mtree.begin(); it != mtree.end(); ++it) + { + auto&& [domain, local_irq] = *it; + assert(domain); + auto info = domain->irqInfo(local_irq); + if (it.level > 0 || (info && (!info->mapped_by.empty() || info->handler))) + { + kprintfd("%*s- %s:%zu\n", (int)it.level * 2, "", domain->name().c_str(), local_irq); + } + + } +} + +void IrqDomain::printAllReverseMappings() +{ + debugAlways(A_INTERRUPTS, "Interrupt mappings for '%s' domain:\n", name().c_str()); + for (size_t i = 0; i < irqs_.size(); ++i) + { + printReverseMappingTree(i); + } +} diff --git a/common/source/kernel/Loader.cpp b/common/source/kernel/Loader.cpp index 67e165d40..ccf0ae69d 100644 --- a/common/source/kernel/Loader.cpp +++ b/common/source/kernel/Loader.cpp @@ -1,16 +1,23 @@ #include "Loader.h" -#include "ArchThreads.h" -#include "PageManager.h" -#include "ArchMemory.h" -#include "Syscall.h" -#include "VfsSyscall.h" -#include "Stabs2DebugInfo.h" -#include "SWEBDebugInfo.h" + #include "File.h" #include "FileDescriptor.h" +#include "PageManager.h" +#include "SWEBDebugInfo.h" #include "Scheduler.h" +#include "Stabs2DebugInfo.h" +#include "Syscall.h" +#include "VfsSyscall.h" + +#include "ArchMemory.h" +#include "ArchThreads.h" + +#include "EASTL/memory.h" +#include "EASTL/vector.h" + +#include "debug.h" -Loader::Loader(ssize_t fd) : fd_(fd), hdr_(0), phdrs_(), program_binary_lock_("Loader::program_binary_lock_"), userspace_debug_info_(0) +Loader::Loader(ssize_t fd) : fd_(fd), hdr_(nullptr), program_binary_lock_("Loader::program_binary_lock_"), userspace_debug_info_(nullptr) { } @@ -29,33 +36,33 @@ void Loader::loadPage(pointer virtual_address) const pointer virt_page_end_addr = virt_page_start_addr + PAGE_SIZE; bool found_page_content = false; // get a new page for the mapping - size_t ppn = PageManager::instance()->allocPPN(); + size_t ppn = PageManager::instance().allocPPN(); program_binary_lock_.acquire(); // Iterate through all sections and load the ones intersecting into the page. - for(ustl::list::iterator it = phdrs_.begin(); it != phdrs_.end(); it++) + for(auto & phdr : phdrs_) { - if((*it).p_vaddr < virt_page_end_addr) + if(phdr.p_vaddr < virt_page_end_addr) { - if((*it).p_vaddr + (*it).p_filesz > virt_page_start_addr) + if(phdr.p_vaddr + phdr.p_filesz > virt_page_start_addr) { - const pointer virt_start_addr = ustl::max(virt_page_start_addr, (*it).p_vaddr); + const pointer virt_start_addr = eastl::max(virt_page_start_addr, phdr.p_vaddr); const size_t virt_offs_on_page = virt_start_addr - virt_page_start_addr; - const l_off_t bin_start_addr = (*it).p_offset + (virt_start_addr - (*it).p_vaddr); - const size_t bytes_to_load = ustl::min(virt_page_end_addr, (*it).p_vaddr + (*it).p_filesz) - virt_start_addr; + const l_off_t bin_start_addr = phdr.p_offset + (virt_start_addr - phdr.p_vaddr); + const size_t bytes_to_load = eastl::min(virt_page_end_addr, phdr.p_vaddr + phdr.p_filesz) - virt_start_addr; //debug(LOADER, "Loader::loadPage: Loading %d bytes from binary address %p to virtual address %p\n", // bytes_to_load, bin_start_addr, virt_start_addr); if(readFromBinary((char *)ArchMemory::getIdentAddressOfPPN(ppn) + virt_offs_on_page, bin_start_addr, bytes_to_load)) { program_binary_lock_.release(); - PageManager::instance()->freePPN(ppn); + PageManager::instance().freePPN(ppn); debug(LOADER, "ERROR! Some parts of the content could not be loaded from the binary.\n"); Syscall::exit(999); } found_page_content = true; } - else if((*it).p_vaddr + (*it).p_memsz > virt_page_start_addr) + else if(phdr.p_vaddr + phdr.p_memsz > virt_page_start_addr) { found_page_content = true; } @@ -65,7 +72,7 @@ void Loader::loadPage(pointer virtual_address) if(!found_page_content) { - PageManager::instance()->freePPN(ppn); + PageManager::instance().freePPN(ppn); debug(LOADER, "Loader::loadPage: ERROR! No section refers to the given address.\n"); Syscall::exit(666); } @@ -74,7 +81,7 @@ void Loader::loadPage(pointer virtual_address) if (!page_mapped) { debug(LOADER, "Loader::loadPage: The page has been mapped by someone else.\n"); - PageManager::instance()->freePPN(ppn); + PageManager::instance().freePPN(ppn); } debug(LOADER, "Loader::loadPage: Load request for address %p has been successfully finished.\n", (void*)virtual_address); } @@ -93,7 +100,7 @@ bool Loader::readHeaders() if(readFromBinary((char*)hdr_, 0, sizeof(Elf::Ehdr))) { - debug(LOADER, "Loader::readHeaders: ERROR! The headers could not be load.\n"); + debug(LOADER, "Loader::readHeaders: ERROR! The headers could not be loaded.\n"); return false; } @@ -129,12 +136,12 @@ void* Loader::getEntryFunction() const bool Loader::loadExecutableAndInitProcess() { - debug ( LOADER,"Loader::loadExecutableAndInitProcess: going to load an executable\n" ); + debug (LOADER ,"Loader::loadExecutableAndInitProcess: going to load an executable\n" ); if(!readHeaders()) return false; - debug ( LOADER,"loadExecutableAndInitProcess: Entry: %zx, num Sections %zx\n",hdr_->e_entry, (size_t)hdr_->e_phnum ); + debug (LOADER ,"loadExecutableAndInitProcess: Entry: %zx, num Sections %zx\n", (size_t)hdr_->e_entry, (size_t)hdr_->e_phnum ); if (LOADER & OUTPUT_ADVANCED) Elf::printElfHeader ( *hdr_ ); @@ -157,9 +164,11 @@ bool Loader::loadDebugInfoIfAvailable() ScopeLock lock(program_binary_lock_); - ustl::vector section_headers; + eastl::vector section_headers; section_headers.resize(hdr_->e_shnum); - if (readFromBinary(reinterpret_cast(§ion_headers[0]), hdr_->e_shoff, hdr_->e_shnum*sizeof(Elf::Shdr))) + debug(USERTRACE, "Reading section headers, shoff: %zx, num headers: %u, read size_ %zu\n", + (size_t)hdr_->e_shoff, hdr_->e_shnum, (size_t)hdr_->e_shnum*sizeof(Elf::Shdr)); + if (readFromBinary(reinterpret_cast(section_headers.data()), hdr_->e_shoff, hdr_->e_shnum*sizeof(Elf::Shdr))) { debug(USERTRACE, "Failed to load section headers!\n"); return false; @@ -172,8 +181,9 @@ bool Loader::loadDebugInfoIfAvailable() size_t section_name_section = hdr_->e_shstrndx; size_t section_name_size = section_headers[section_name_section].sh_size; - ustl::vector section_names(section_name_size); + eastl::vector section_names(section_name_size); + debug(USERTRACE, "Reading shstrtab sh section %zu, offset: %zx, size: %zu\n", section_name_section, section_headers[section_name_section].sh_offset, section_name_size); if (readFromBinary(§ion_names[0], section_headers[section_name_section].sh_offset, section_name_size )) { debug(USERTRACE, "Failed to load section name section\n"); @@ -183,13 +193,13 @@ bool Loader::loadDebugInfoIfAvailable() // now that we have names we read through all the sections // and load the two we're interested in - char *stab_data=0; - char *stabstr_data=0; - char* sweb_data=0; + char* stab_data = nullptr; + char* stabstr_data = nullptr; + char* sweb_data = nullptr; size_t stab_data_size=0; size_t sweb_data_size=0; - for (Elf::Shdr const §ion: section_headers) + for (const Elf::Shdr& section : section_headers) { if (section.sh_name) { @@ -209,7 +219,7 @@ bool Loader::loadDebugInfoIfAvailable() { debug(USERTRACE, "Failed to load stab section!\n"); delete[] stab_data; - stab_data=0; + stab_data = nullptr; } } } @@ -228,7 +238,7 @@ bool Loader::loadDebugInfoIfAvailable() { debug(USERTRACE, "Failed to load stabstr section!\n"); delete[] stabstr_data; - stabstr_data=0; + stabstr_data = nullptr; } } } @@ -241,7 +251,7 @@ bool Loader::loadDebugInfoIfAvailable() if (readFromBinary(sweb_data, section.sh_offset, size)) { debug(USERTRACE, "Could not read swebdbg section!\n"); delete[] sweb_data; - sweb_data = 0; + sweb_data = nullptr; } } else { debug(USERTRACE, "SWEBDbg Infos are empty\n"); @@ -269,32 +279,32 @@ bool Loader::loadDebugInfoIfAvailable() return true; } -Stabs2DebugInfo const *Loader::getDebugInfos()const +const Stabs2DebugInfo* Loader::getDebugInfos() const { return userspace_debug_info_; } bool Loader::prepareHeaders() { - ustl::list::iterator it, it2; + eastl::vector::iterator it, it2; for(it = phdrs_.begin(); it != phdrs_.end(); it++) { // remove sections which shall not be load from anywhere if((*it).p_type != Elf::PT_LOAD || ((*it).p_memsz == 0 && (*it).p_filesz == 0)) { - it = phdrs_.erase(it, 1) - 1; + it = phdrs_.erase(it) - 1; continue; } // check if some sections shall load data from the binary to the same location for(it2 = phdrs_.begin(); it2 != it; it2++) { - if(ustl::max((*it).p_vaddr, (*it2).p_vaddr) < - ustl::min((*it).p_vaddr + (*it).p_filesz, (*it2).p_vaddr + (*it2).p_filesz)) + if(eastl::max((*it).p_vaddr, (*it2).p_vaddr) < + eastl::min((*it).p_vaddr + (*it).p_filesz, (*it2).p_vaddr + (*it2).p_filesz)) { debug(LOADER, "Loader::prepareHeaders: Failed to load the segments, some of them overlap!\n"); return false; } } } - return phdrs_.size() > 0; + return !phdrs_.empty(); } diff --git a/common/source/kernel/Lock.cpp b/common/source/kernel/Lock.cpp index fa38e14ef..f996b9f00 100644 --- a/common/source/kernel/Lock.cpp +++ b/common/source/kernel/Lock.cpp @@ -1,19 +1,26 @@ #include "Lock.h" -#include "kprintf.h" -#include "assert.h" -#include "Thread.h" -#include "ArchThreads.h" -#include "ArchInterrupts.h" + +#include "SMP.h" #include "Scheduler.h" #include "Stabs2DebugInfo.h" -extern Stabs2DebugInfo const* kernel_debug_info; +#include "SystemState.h" +#include "Thread.h" +#include "kprintf.h" + +#include "ArchInterrupts.h" +#include "ArchMulticore.h" +#include "ArchThreads.h" + +#include "assert.h" + +extern const Stabs2DebugInfo* kernel_debug_info; Lock::Lock(const char *name) : - held_by_(0), - next_lock_on_holding_list_(0), + held_by_(nullptr), + next_lock_on_holding_list_(nullptr), last_accessed_at_(0), name_(name ? name : ""), - waiters_list_(0), + waiters_list_(nullptr), waiters_list_lock_(0) { } @@ -28,37 +35,40 @@ Lock::~Lock() if(waiter) { debug(LOCK, "ERROR: Lock::~Lock %s (%p): At least thread %p is still waiting on this lock,\n" - "currentThread is: %p, the thread holding this lock is: %p\n", - name_, this, waiter, currentThread, held_by); + "currentThread() is: %p, the thread holding this lock is: %p\n", + name_, this, waiter, currentThread, held_by); lockWaitersList(); printWaitersList(); // The waiters list does not has to be unlocked here, because the kernel is going to die. - assert(false); + assert(false && "A thread is still waiting on a lock that is about to be destroyed"); } if(held_by) { - debug(LOCK, "Warning: Lock::~Lock %s (%p): Thread %p is still holding this lock, currentThread is: %p\n", + debug(LOCK, "Warning: Lock::~Lock %s (%p): Thread %p is still holding this lock, currentThread() is: %p\n", name_, this, held_by, currentThread); } } void Lock::printWaitersList() { - debug(LOCK, "Threads waiting for lock %s (%p), newest thread waiting first:\n", name_, this); + debugAlways(LOCK, "Threads waiting for lock %s (%p), newest thread waiting first:\n", name_, this); size_t count = 0; - for(Thread* thread = waiters_list_; thread != 0; thread = thread->next_thread_in_lock_waiters_list_) + for(Thread* thread = waiters_list_; thread != nullptr; thread = thread->next_thread_in_lock_waiters_list_) { - kprintfd("%zu: %s (%p)\n", ++count, thread->getName(), thread); + debugAlways(LOCK, "%zu: %s (%p)\n", ++count, thread->getName(), thread); } } void Lock::printHoldingList(Thread* thread) { - debug(LOCK, "Locks held by thread %s (%p):", + debugAlways(LOCK, "Locks held by thread %s (%p):\n", thread->getName(), thread); - for(Lock* lock = thread->holding_lock_list_; lock != 0; lock = lock->next_lock_on_holding_list_) + for(Lock* lock = thread->holding_lock_list_; lock != nullptr; lock = lock->next_lock_on_holding_list_) { - kprintfd(" %s (%p)%s", lock->name_, lock, lock->next_lock_on_holding_list_ ? "," : ".\n"); + auto accessed_at = lock->last_accessed_at_; + debugAlways(LOCK, " %s (%p), locked at %zx%s", lock->name_, lock, accessed_at, kernel_debug_info ? "" : "\n"); + if (kernel_debug_info) + kernel_debug_info->printCallInformation(accessed_at); } } @@ -77,7 +87,7 @@ void Lock::printStatus() pointer last_accessed_at = last_accessed_at_; if(!last_accessed_at && !thread) { - debug(LOCK, "Lock %s (%p) probably has not been used yet.\n", getName(), this); + debugAlways(LOCK, "Lock %s (%p) probably has not been used yet.\n", getName(), this); return; } @@ -85,12 +95,12 @@ void Lock::printStatus() { if(thread) { - debug(LOCK, "Lock %s (%p) has been acquired by thread %s (%p) at: ", + debugAlways(LOCK, "Lock %s (%p) has been acquired by thread %s (%p) at: ", getName(), this, thread->getName(), thread); } else { - debug(LOCK, "Lock %s (%p) has been released at: ", getName(), this); + debugAlways(LOCK, "Lock %s (%p) has been released at: ", getName(), this); } kernel_debug_info->printCallInformation(last_accessed_at); } @@ -102,10 +112,10 @@ void Lock::checkForDeadLock() return; if(held_by_ == currentThread) { - debug(LOCK, "Deadlock: Lock: %s (%p), held already by currentThread: %s (%p).\n", + debug(LOCK, "Deadlock: Lock: %s (%p), held already by currentThread(): %s (%p).\n", name_, this, currentThread->getName(), currentThread); printStatus(); - assert(false); + assert(false && "Deadlock, lock already held by currentThread"); } checkForCircularDeadLock(currentThread, this); } @@ -121,7 +131,7 @@ void Lock::removeFromCurrentThreadHoldingList() else { Lock* current; - for(current = currentThread->holding_lock_list_; current != 0; current = current->next_lock_on_holding_list_) + for(current = currentThread->holding_lock_list_; current != nullptr; current = current->next_lock_on_holding_list_) { if(current->next_lock_on_holding_list_ == this) { @@ -130,15 +140,14 @@ void Lock::removeFromCurrentThreadHoldingList() } } } - next_lock_on_holding_list_ = 0; - return; + next_lock_on_holding_list_ = nullptr; } void Lock::checkCurrentThreadStillWaitingOnAnotherLock() { if(!currentThread) return; - if(currentThread->lock_waiting_on_ != 0) + if(currentThread->lock_waiting_on_ != nullptr) { debug(LOCK, "ERROR: Lock: Thread %s (%p) is trying to lock %s (%p), eventhough is already waiting on lock %s (%p).\n" "You shouldn't use Scheduler::wake() with a thread sleeping on a lock!\n", @@ -170,44 +179,44 @@ void Lock::checkForCircularDeadLock(Thread* thread_waiting, Lock* start) // Check all locks which are held by the target thread. // This can be done, because we know that the list may not be modified // (accept of an atomic push front, which does not matter at all) - for(Lock* lock = thread_waiting->holding_lock_list_; lock != 0; lock = lock->next_lock_on_holding_list_) + for(Lock* lock = thread_waiting->holding_lock_list_; lock != nullptr; lock = lock->next_lock_on_holding_list_) { // In case a thread which is indirectly waiting for the current thread // holds the lock, a deadlock happened. if(lock == start) { printOutCircularDeadLock(thread_waiting); - assert(false); + assert(false && "Circular deadlock detected"); } - for(Thread* thread_waiting = lock->waiters_list_; thread_waiting != 0; - thread_waiting = thread_waiting->next_thread_in_lock_waiters_list_) + for(Thread* t_waiting_on_lock = lock->waiters_list_; t_waiting_on_lock != nullptr; + t_waiting_on_lock = t_waiting_on_lock->next_thread_in_lock_waiters_list_) { // The method has to be called recursively, so it is possible to check indirect // deadlocks. The recursive approach may be slower than other checking methods, // but it is safe and more precise - checkForCircularDeadLock(thread_waiting, start); + checkForCircularDeadLock(t_waiting_on_lock, start); } } } void Lock::printOutCircularDeadLock(Thread* starting) { - debug(LOCK, "CIRCULAR DEADLOCK when waiting for %s (%p) with thread %s (%p)!\n", + debugAlways(LOCK, "CIRCULAR DEADLOCK when waiting for %s (%p) with thread %s (%p)!\n", getName(), this, currentThread->getName(), currentThread); - debug(LOCK, "Printing out the circular deadlock:\n"); + debugAlways(LOCK, "Printing out the circular deadlock:\n"); currentThread->lock_waiting_on_ = this; // in this case we can access the other threads, because we KNOW that they are indirectly waiting on the current thread. - for(Thread* thread = starting; thread != 0; thread = thread->lock_waiting_on_->held_by_) + for(Thread* thread = starting; thread != nullptr; thread = thread->lock_waiting_on_->held_by_) { Thread * holding = thread->lock_waiting_on_->held_by_; Lock* lock = thread->lock_waiting_on_; - debug(LOCK, "Thread %-40.40s (%p) holding lock %-40.40s (%p), waiting for lock %-40.40s (%p)\n", + debugAlways(LOCK, "Thread %-40.40s (%p) holding lock %-40.40s (%p), waiting for lock %-40.40s (%p)\n", holding->getName(), holding, lock->getName(), lock, holding->lock_waiting_on_->getName(), holding->lock_waiting_on_); if(kernel_debug_info) { - debug(LOCK, "This lock has been locked at "); + debugAlways(LOCK, "This lock has been locked at "); kernel_debug_info->printCallInformation(lock->last_accessed_at_); } // In the thread we are looking at is the current one, we have to stop. @@ -221,30 +230,33 @@ void Lock::checkInterrupts(const char* method) // it would be nice to assert Scheduler::instance()->isSchedulingEnabled() as well. // unfortunately this is difficult because we might want to acquire/release locks // while scheduling is disabled - if(unlikely(ArchInterrupts::testIFSet() == false)) + if(unlikely((ArchInterrupts::testIFSet() == false) && (SMP::numRunningCpus() == 1))) { ArchInterrupts::disableInterrupts(); - debug(LOCK, "(ERROR) %s: Lock %s (%p) with IF=0 and SchedulingEnabled=%d ! Now we're dead !!!\n" + debugAlways(LOCK, "(ERROR) %s: Lock %s (%p) with IF=0 and SchedulingEnabled=%d ! Now we're dead !!!\n" "Maybe you used new/delete in irq/int-Handler context or while Scheduling disabled?\n\n", method, name_, this, Scheduler::instance()->isSchedulingEnabled()); - assert(false); + assert(false && "Blocking on lock with disabled interrupts/scheduling"); } } -void Lock::lockWaitersList() +void Lock::lockWaitersList(bool yield) { // The waiters list lock is a simple spinlock. // Just wait until the holding thread is releasing the lock, // and acquire it. These steps have to be atomic. - while(ArchThreads::testSetLock(waiters_list_lock_, 1)) + while(ArchThreads::testSetLock(waiters_list_lock_, (size_t)1)) { - Scheduler::instance()->yield(); + if(yield) + { + Scheduler::instance()->yield(); + } } } void Lock::unlockWaitersList() { - waiters_list_lock_ = 0; + ArchThreads::syncLockRelease(waiters_list_lock_); } void Lock::pushFrontCurrentThreadToWaitersList() @@ -253,24 +265,24 @@ void Lock::pushFrontCurrentThreadToWaitersList() assert(waitersListIsLocked()); currentThread->next_thread_in_lock_waiters_list_ = waiters_list_; // the following set has to be atomic - // waiters_list_ = currentThread; + // waiters_list_ = currentThread(); ArchThreads::atomic_set((pointer&)(waiters_list_), (pointer)(currentThread)); } Thread* Lock::popBackThreadFromWaitersList() { assert(waitersListIsLocked()); - Thread* thread = 0; - if(waiters_list_ == 0) + Thread* thread = nullptr; + if(waiters_list_ == nullptr) { // the waiters list is empty } - else if(waiters_list_->next_thread_in_lock_waiters_list_ == 0) + else if(waiters_list_->next_thread_in_lock_waiters_list_ == nullptr) { // this thread is the only one in the waiters list thread = waiters_list_; ArchThreads::atomic_set((pointer&)(waiters_list_), (pointer)(0)); - waiters_list_ = 0; + waiters_list_ = nullptr; } else { @@ -301,7 +313,7 @@ void Lock::removeCurrentThreadFromWaitersList() else { - for(Thread* thread = waiters_list_; thread->next_thread_in_lock_waiters_list_ != 0; + for(Thread* thread = waiters_list_; thread->next_thread_in_lock_waiters_list_ != nullptr; thread = thread->next_thread_in_lock_waiters_list_) { if(thread->next_thread_in_lock_waiters_list_ == currentThread) @@ -314,7 +326,6 @@ void Lock::removeCurrentThreadFromWaitersList() } } ArchThreads::atomic_set((pointer&)(currentThread->next_thread_in_lock_waiters_list_ ), (pointer)0); - return; } void Lock::checkInvalidRelease(const char* method) @@ -325,21 +336,35 @@ void Lock::checkInvalidRelease(const char* method) // push the information onto the stack, so the variable may not be modified meanwhile we are working with it Thread* holding = held_by_; - debug(LOCK, "%s: Lock %s (%p) currently not held by currentThread! " + debugAlways(LOCK, "%s: Lock %s (%p) currently not held by currentThread! " "Held by %s (%p), currentThread is %s (%p)\n", method, name_, this, (holding ? holding->getName() : "UNKNOWN THREAD"), holding, currentThread->getName(), currentThread); printStatus(); - assert(false); + if (currentThread) + { + debugAlways(LOCK, "Backtrace:\n"); + currentThread->printBacktrace(false); + } + + assert(false && "Thread tried to release lock that it is not currently holding"); } } -void Lock::sleepAndRelease () +void Lock::sleepAndRelease(bool should_yield) { currentThread->lock_waiting_on_ = this; pushFrontCurrentThreadToWaitersList(); - unlockWaitersList(); - // we can risk to go to sleep after the list has been unlocked, - // because the thread waking this thread waits until it goes to sleep - Scheduler::instance()->sleep(); + { + PreemptProtect p; + // Ensure that other threads only see this thread in the waiters list + // after it has already been set to sleep + // Must not be interrupted between sleep and unlock + Scheduler::instance()->sleep(false); + unlockWaitersList(); + } + if (should_yield) + { + Scheduler::instance()->yield(); + } } diff --git a/common/source/kernel/Mutex.cpp b/common/source/kernel/Mutex.cpp index f637d2592..f772d22a0 100644 --- a/common/source/kernel/Mutex.cpp +++ b/common/source/kernel/Mutex.cpp @@ -1,46 +1,56 @@ #include "Mutex.h" -#include "kprintf.h" -#include "ArchThreads.h" -#include "ArchInterrupts.h" + +#include "SMP.h" #include "Scheduler.h" +#include "Stabs2DebugInfo.h" +#include "SystemState.h" #include "Thread.h" -#include "panic.h" #include "backtrace.h" +#include "kprintf.h" + +#include "ArchInterrupts.h" +#include "ArchMulticore.h" +#include "ArchThreads.h" + #include "assert.h" -#include "Stabs2DebugInfo.h" -extern Stabs2DebugInfo const* kernel_debug_info; + +extern const Stabs2DebugInfo* kernel_debug_info; Mutex::Mutex(const char* name) : Lock::Lock(name), mutex_(0) { } -bool Mutex::acquireNonBlocking(pointer called_by) +bool Mutex::acquireNonBlocking(pointer called_by, bool do_checks) { if(unlikely(system_state != RUNNING)) return true; if(!called_by) called_by = getCalledBefore(1); -// debug(LOCK, "Mutex::acquireNonBlocking: Mutex: %s (%p), currentThread: %s (%p).\n", -// getName(), this, currentThread->getName(), currentThread); -// if(kernel_debug_info) -// { -// debug(LOCK, "The acquire is called by: "); -// kernel_debug_info->printCallInformation(called_by); -// } + + // debug(LOCK, "Mutex::acquireNonBlocking: Mutex: %s (%p), Scheduler::instance()->currentThread(): %s (%p).\n", + // getName(), this, currentThread->getName(), currentThread); + // if(kernel_debug_info) + // { + // debug(LOCK, "The acquire is called by: "); + // kernel_debug_info->printCallInformation(called_by); + // } // There may be some cases where the pre-checks may not be wished here. // But these cases are usually dirty implemented, and it would not be necessary to call this method there. // So in case you see this comment, re-think your implementation and don't just comment out this line! - doChecksBeforeWaiting(); + if(do_checks) + { + doChecksBeforeWaiting(); + } - if(ArchThreads::testSetLock(mutex_, 1)) + if(ArchThreads::testSetLock(mutex_, (size_t)1)) { // The mutex is already held by another thread, // so we are not allowed to lock it. return false; } - assert(held_by_ == 0); + assert(held_by_ == nullptr); last_accessed_at_ = called_by; held_by_ = currentThread; pushFrontToCurrentThreadHoldingList(); @@ -53,22 +63,27 @@ void Mutex::acquire(pointer called_by) return; if(!called_by) called_by = getCalledBefore(1); -// debug(LOCK, "Mutex::acquire: Mutex: %s (%p), currentThread: %s (%p).\n", -// getName(), this, currentThread->getName(), currentThread); -// if(kernel_debug_info) -// { -// debug(LOCK, "The acquire is called by: "); -// kernel_debug_info->printCallInformation(called_by); -// } + + if (debug_lock || LOCK & OUTPUT_ADVANCED) + { + debugAlways(LOCK, "Mutex::acquire: Mutex: %s (%p), currentThread: %s (%p).\n", + getName(), this, currentThread->getName(), currentThread); + if(kernel_debug_info) + { + debugAlways(LOCK, "The acquire is called by: "); + kernel_debug_info->printCallInformation(called_by); + } + } + // check for deadlocks, interrupts... doChecksBeforeWaiting(); - while(ArchThreads::testSetLock(mutex_, 1)) + while(ArchThreads::testSetLock(mutex_, (size_t)1)) { checkCurrentThreadStillWaitingOnAnotherLock(); lockWaitersList(); // Here we have to check for the lock again, in case some one released it in between, we might sleep forever. - if(!ArchThreads::testSetLock(mutex_, 1)) + if(!ArchThreads::testSetLock(mutex_, (size_t)1)) { unlockWaitersList(); break; @@ -77,33 +92,41 @@ void Mutex::acquire(pointer called_by) doChecksBeforeWaiting(); sleepAndRelease(); // We have been waken up again. - currentThread->lock_waiting_on_ = 0; + currentThread->lock_waiting_on_ = nullptr; } - assert(held_by_ == 0); + assert(held_by_ == nullptr); pushFrontToCurrentThreadHoldingList(); last_accessed_at_ = called_by; held_by_ = currentThread; } -void Mutex::release(pointer called_by) +void Mutex::release(pointer called_by, bool do_checks) { if(unlikely(system_state != RUNNING)) return; if(!called_by) called_by = getCalledBefore(1); -// debug(LOCK, "Mutex::release: Mutex: %s (%p), currentThread: %s (%p).\n", -// getName(), this, currentThread->getName(), currentThread); -// if(kernel_debug_info) -// { -// debug(LOCK, "The release is called by: "); -// kernel_debug_info->printCallInformation(called_by); -// } - checkInvalidRelease("Mutex::release"); + + if (debug_lock || LOCK & OUTPUT_ADVANCED) + { + debugAlways(LOCK, "Mutex::release: Mutex: %s (%p), currentThread(): %s (%p).\n", + getName(), this, currentThread->getName(), currentThread); + if(kernel_debug_info) + { + debugAlways(LOCK, "The release is called by: "); + kernel_debug_info->printCallInformation(called_by); + } + } + + if(do_checks) + { + checkInvalidRelease("Mutex::release"); + } removeFromCurrentThreadHoldingList(); last_accessed_at_ = called_by; - held_by_ = 0; - mutex_ = 0; + held_by_ = nullptr; + ArchThreads::syncLockRelease(mutex_); // Wake up a sleeping thread. It is okay that the mutex is not held by the current thread any longer. // In worst case a new thread is woken up. Otherwise (first wake up, then release), // it could happen that a thread is going to sleep after the this one is trying to wake up one. @@ -117,12 +140,13 @@ void Mutex::release(pointer called_by) } } -bool Mutex::isFree() +bool Mutex::isFree() const { - if(unlikely(ArchInterrupts::testIFSet() && Scheduler::instance()->isSchedulingEnabled())) + if(unlikely((ArchInterrupts::testIFSet() && Scheduler::instance()->isSchedulingEnabled()) || (SMP::numRunningCpus() > 1))) { - debug(LOCK, "Mutex::isFree: ERROR: Should not be used with IF=1 AND enabled Scheduler, use acquire instead\n"); - assert(false); + return false; + //debug(LOCK, "Mutex::isFree: ERROR: Should not be used with IF=1 AND enabled Scheduler, use acquire instead\n"); + //assert(false); } return (mutex_ == 0); } diff --git a/common/source/kernel/ProcessRegistry.cpp b/common/source/kernel/ProcessRegistry.cpp index f60baa0b5..dc7bd1b60 100644 --- a/common/source/kernel/ProcessRegistry.cpp +++ b/common/source/kernel/ProcessRegistry.cpp @@ -1,113 +1,75 @@ -#include #include "ProcessRegistry.h" + +#include "KernelMemoryManager.h" #include "Scheduler.h" #include "UserProcess.h" -#include "kprintf.h" #include "VfsSyscall.h" #include "VirtualFileSystem.h" -#include "PageManager.h" +#include "kprintf.h" + +#include "ArchMulticore.h" -ProcessRegistry* ProcessRegistry::instance_ = 0; +ProcessRegistry* ProcessRegistry::instance_ = nullptr; -ProcessRegistry::ProcessRegistry(FileSystemInfo *root_fs_info, char const *progs[]) : - Thread(root_fs_info, "ProcessRegistry", Thread::KERNEL_THREAD), progs_(progs), progs_running_(0), +ProcessRegistry::ProcessRegistry(FileSystemInfo* root_fs_info) : + default_working_dir_(root_fs_info), + progs_running_(0), counter_lock_("ProcessRegistry::counter_lock_"), all_processes_killed_(&counter_lock_, "ProcessRegistry::all_processes_killed_") { - instance_ = this; // instance_ is static! -> Singleton-like behaviour -} - -ProcessRegistry::~ProcessRegistry() -{ + assert(default_working_dir_); } ProcessRegistry* ProcessRegistry::instance() { + assert(instance_ && "ProcessRegistry not yet initialized"); return instance_; } -void ProcessRegistry::Run() +void ProcessRegistry::init(FileSystemInfo *root_fs_info) { - if (!progs_ || !progs_[0]) - return; - - debug(PROCESS_REG, "mounting userprog-partition \n"); - - debug(PROCESS_REG, "mkdir /usr\n"); - assert( !VfsSyscall::mkdir("/usr", 0) ); - debug(PROCESS_REG, "mount idea1\n"); - assert( !VfsSyscall::mount("idea1", "/usr", "minixfs", 0) ); - - debug(PROCESS_REG, "mkdir /dev\n"); - assert( !VfsSyscall::mkdir("/dev", 0) ); - debug(PROCESS_REG, "mount devicefs\n"); - assert( !VfsSyscall::mount(NULL, "/dev", "devicefs", 0) ); - - - KernelMemoryManager::instance()->startTracing(); - - for (uint32 i = 0; progs_[i]; i++) - { - createProcess(progs_[i]); - } - - counter_lock_.acquire(); - - while (progs_running_) - all_processes_killed_.wait(); - - counter_lock_.release(); - - debug(PROCESS_REG, "unmounting userprog-partition because all processes terminated \n"); - - VfsSyscall::umount("/usr", 0); - VfsSyscall::umount("/dev", 0); - vfs.rootUmount(); - - Scheduler::instance()->printStackTraces(); - Scheduler::instance()->printThreadList(); - - PageManager* pm = PageManager::instance(); - if(!DYNAMIC_KMM && pm->getNumFreePages() != pm->getNumPagesForUser()) - { - PageManager::instance()->printBitmap(); - debug(PM, "WARNING: You might be leaking physical memory pages somewhere\n"); - debug(PM, "%u/%u free physical pages after unmounting detected\n", - pm->getNumFreePages(), - pm->getNumPagesForUser()); - } - - kill(); + assert(!instance_ && "ProcessRegistry already initialized"); + static ProcessRegistry inst(root_fs_info); + instance_ = &inst; } void ProcessRegistry::processExit() { - counter_lock_.acquire(); + ScopeLock l(counter_lock_); if (--progs_running_ == 0) - all_processes_killed_.signal(); - - counter_lock_.release(); + all_processes_killed_.broadcast(); } void ProcessRegistry::processStart() { - counter_lock_.acquire(); + ScopeLock l(counter_lock_); ++progs_running_; - counter_lock_.release(); } size_t ProcessRegistry::processCount() { + // Note that the returned count value is out of date as soon + // as the lock is released ScopeLock lock(counter_lock_); return progs_running_; } -void ProcessRegistry::createProcess(const char* path) +void ProcessRegistry::waitAllKilled() +{ + ScopeLock l(counter_lock_); + while (progs_running_) + all_processes_killed_.wait(); +} + +int ProcessRegistry::createProcess(const char* path) { - debug(PROCESS_REG, "create process %s\n", path); - Thread* process = new UserProcess(path, new FileSystemInfo(*working_dir_)); + int creation_status = -1; + Thread* process = new UserProcess(path, new FileSystemInfo(*default_working_dir_), 0, creation_status); debug(PROCESS_REG, "created userprocess %s\n", path); + Scheduler::instance()->addNewThread(process); debug(PROCESS_REG, "added thread %s\n", path); + + return creation_status; } diff --git a/common/source/kernel/Scheduler.cpp b/common/source/kernel/Scheduler.cpp index 31ef1da95..7a7ed5dd7 100644 --- a/common/source/kernel/Scheduler.cpp +++ b/common/source/kernel/Scheduler.cpp @@ -1,21 +1,34 @@ #include "Scheduler.h" + +#include "KernelMemoryManager.h" +#include "Loader.h" +#include "Lock.h" +#include "Mutex.h" +#include "SystemState.h" #include "Thread.h" -#include "panic.h" -#include "ArchThreads.h" -#include "ArchCommon.h" +#include "backtrace.h" #include "kprintf.h" + +#include "ArchCommon.h" #include "ArchInterrupts.h" -#include "KernelMemoryManager.h" -#include -#include "backtrace.h" +#include "ArchMulticore.h" #include "ArchThreads.h" -#include "Mutex.h" -#include "umap.h" -#include "ustring.h" -#include "Lock.h" -ArchThreadRegisters *currentThreadRegisters; -Thread *currentThread; +#include "EASTL/iterator.h" +#include "EASTL/list.h" +#include "EASTL/map.h" +#include "EASTL/string.h" + +#include "assert.h" + +__cpu Thread* currentThread = nullptr; +__cpu ArchThreadRegisters* currentThreadRegisters = nullptr; + +cpu_local IdleThread* idle_thread; + +__cpu size_t cpu_timer_ticks = 0; + +__cpu eastl::atomic preempt_protect_count_ = {}; Scheduler *Scheduler::instance_ = nullptr; @@ -28,35 +41,143 @@ Scheduler *Scheduler::instance() Scheduler::Scheduler() { - block_scheduling_ = 0; + debug(SCHEDULER, "Initializing scheduler\n"); ticks_ = 0; addNewThread(&cleanup_thread_); - addNewThread(&idle_thread_); + debug(SCHEDULER, "Initializing scheduler END\n"); } void Scheduler::schedule() { + debugAdvanced(SCHEDULER, "CPU %zu, scheduling, currentThread: %p = %s\n", SMP::currentCpuId(), currentThread, currentThread ? currentThread->getName() : "(nil)"); + + assert(system_state == RUNNING); + + if (preempt_protect_count_.load() > 0) + { + debugAlways(SCHEDULER, "Re-Schedule blocked (preemption disabled)\n"); + return; + } + assert(!ArchInterrupts::testIFSet() && "Tried to schedule with Interrupts enabled"); - if (block_scheduling_) + + if(scheduler_lock_.isHeldBy(SMP::currentCpuId())) { - debug(SCHEDULER, "schedule: currently blocked\n"); + debug(SCHEDULER_LOCK, "CPU %zu schedule: currently blocked by thread on own cpu\n", SMP::currentCpuId()); return; } + uint64 now = ArchCommon::cpuTimestamp(); + + scheduler_lock_.acquire(); + + + Thread* previous_thread = currentThread; + Thread* next_thread = nullptr; + + assert(scheduler_lock_.isHeldBy(SMP::currentCpuId())); + + uint64 thread_ran_for = descheduleThread(previous_thread, now); + + assert(!threads_.empty()); + + // Pick the thread with the lowest virtual running time (that is schedulable and not already running) + // Thread list is ordered by increasing vruntime, i.e. we pick the first suitable thread auto it = threads_.begin(); for(; it != threads_.end(); ++it) { - if((*it)->schedulable()) + bool already_running = (*it)->isCurrentlyScheduled(); // Prevent scheduling threads on multiple CPUs simultaneously + bool schedulable = (*it)->schedulable(); + bool can_run_on_cpu = (*it)->canRunOnCpu(SMP::currentCpuId()); + bool just_woken = schedulable && !(*it)->prev_schedulable; + + (*it)->prev_schedulable = schedulable; + + if(SCHEDULER & OUTPUT_ADVANCED) + debug(SCHEDULER, "Check thread (%p) %s, schedulable: %u, just woken: %u, already running: %u, vruntime: %" PRIu64 "\n", + *it, (*it)->getName(), schedulable, just_woken, already_running, (*it)->vruntime); + + if(!already_running && schedulable && can_run_on_cpu) { - currentThread = *it; - break; + // Found next thread + next_thread = *it; + + // Relative virtual running time for threads that have just woken up is set to same as thread with least running time + // (i.e., schedule them asap, but don't let them run for a really long time to 'catch up' the difference) + if(just_woken) + { + auto min_non_woken = eastl::find_if(eastl::next(it), threads_.end(), [](Thread* t){ return t->schedulable() && t->prev_schedulable; }); + + if(min_non_woken != threads_.end()) + { + auto adjusted_vruntime = eastl::max((*it)->vruntime, (*min_non_woken)->vruntime); + setThreadVruntime(it, adjusted_vruntime); // Invalidates iterator + } + } + + break; } } - assert(it != threads_.end() && "No schedulable thread found"); - ustl::rotate(threads_.begin(), it + 1, threads_.end()); // no new/delete here - important because interrupts are disabled - //debug(SCHEDULER, "Scheduler::schedule: new currentThread is %p %s, switch_to_userspace: %d\n", currentThread, currentThread->getName(), currentThread->switch_to_userspace_); - currentThreadRegisters = currentThread->switch_to_userspace_ ? currentThread->user_registers_ : currentThread->kernel_registers_; + assert(it != threads_.end()); + assert(next_thread); + + currentThread = next_thread; + + debugAdvanced(SCHEDULER, "schedule CPU %zu, currentThread %-21s (%p) -> %-21s (%p) ran for {%" PRId64 "}\n", + SMP::currentCpuId(), + (previous_thread ? previous_thread->getName() : "(nil)"), previous_thread, + (currentThread ? currentThread->getName() : "(nil)"), currentThread, + thread_ran_for); + + assert(currentThread); + assert(currentThread->schedulable()); + assert(!currentThread->isCurrentlyScheduled()); + + + ArchThreads::switchToAddressSpace(currentThread); + + currentThread->currently_scheduled_on_cpu_ = SMP::currentCpuId(); + + scheduler_lock_.release(); + + currentThreadRegisters = + (currentThread->switch_to_userspace_ ? currentThread->user_registers_.get() + : currentThread->kernel_registers_.get()); + + currentThread->setSchedulingStartTimestamp(ArchCommon::cpuTimestamp()); +} + +uint64 Scheduler::descheduleThread(Thread* t, uint64 deschedule_time) +{ + if (!t) + return 0; + + assert(t->isCurrentlyScheduledOnCpu(SMP::currentCpuId())); + + // Increase virtual running time of the thread by the difference between last schedule and deschedule_time + uint64 thread_ran_for = updateVruntime(t, deschedule_time); + + // Threads that yielded their time (without waiting on a lock) are moved to the back of the list + // by increasing their virtual running time to that of the longest (virtually) running thread + // Better: increase vruntime by the time slice that would have been allocated for the thread + // (requires an actual time base and not just cpu timestamps) + if(t->yielded) + { + const Thread* max_vruntime_thread = maxVruntimeThread(); + uint64 new_vruntime = max_vruntime_thread->vruntime + 1; + + debugAdvanced(SCHEDULER, "%s yielded while running, increasing vruntime %" PRIu64 " -> %" PRIu64 " (after %s)\n", + currentThread->getName(), currentThread->vruntime, new_vruntime, max_vruntime_thread->getName()); + + setThreadVruntime(t, eastl::max(t->vruntime, new_vruntime)); + + t->yielded = false; + } + + t->currently_scheduled_on_cpu_ = (size_t)-1; + + return thread_ran_for; } void Scheduler::addNewThread(Thread *thread) @@ -65,65 +186,100 @@ void Scheduler::addNewThread(Thread *thread) debug(SCHEDULER, "addNewThread: %p %zd:%s\n", thread, thread->getTID(), thread->getName()); if (currentThread) ArchThreads::debugCheckNewThread(thread); + // Inserting a new thread into the thread list requires allocations + // -> ensure we won't block on the KMM lock with scheduling disabled KernelMemoryManager::instance()->getKMMLock().acquire(); - lockScheduling(); + scheduler_lock_.acquire(); KernelMemoryManager::instance()->getKMMLock().release(); - threads_.push_back(thread); - unlockScheduling(); + + // Set virtual runtime of new thread to that of the currently lowest runtime thread. + // Absolute values don't matter, only relative differences to other threads. + // (Don't use 0 as initial virtual runtime. That would mean the new thread would be continuously + // scheduled and block other threads until it reaches the same running time as the currently lowest runtime thread (~ system uptime)) + const Thread* min_thread = minVruntimeThread(); + if(min_thread) + { + thread->vruntime = min_thread->vruntime; + debug(SCHEDULER, "vruntime for %s = %" PRIu64 "\n", thread->getName(), thread->vruntime); + } + + threads_.insert(thread); + ++num_threads; + scheduler_lock_.release(); } -void Scheduler::sleep() +void Scheduler::sleep(bool should_yield) { - currentThread->setState(Sleeping); - assert(block_scheduling_ == 0); - yield(); + currentThread->setState(Thread::Sleeping); + if (should_yield) + { + yield(); + } } void Scheduler::wake(Thread* thread_to_wake) { - // wait until the thread is sleeping - while(thread_to_wake->getState() != Sleeping) - yield(); - thread_to_wake->setState(Running); + assert(thread_to_wake->getState() == Thread::Sleeping); + thread_to_wake->setState(Thread::Running); } void Scheduler::yield() { assert(this); + assert(currentThread); + + if (preempt_protect_count_.load() > 0) + { + debugAlways(SCHEDULER, "Yield blocked (preemption disabled)\n"); + return; + } + if (!ArchInterrupts::testIFSet()) { - assert(currentThread); - kprintfd("Scheduler::yield: WARNING Interrupts disabled, do you really want to yield ? (currentThread %p %s)\n", + debugAlways(SCHEDULER, "Scheduler::yield: WARNING Interrupts disabled, do you really want to yield ? (currentThread %p %s)\n", currentThread, currentThread->name_.c_str()); currentThread->printBacktrace(); } + + debugAdvanced(SCHEDULER, "%s yielded\n", currentThread->getName()); + + if (currentThread->getState() == Thread::Running) + { + currentThread->yielded = true; + } + ArchThreads::yield(); } void Scheduler::cleanupDeadThreads() { /* Before adding new functionality to this function, consider if that - functionality could be implemented more cleanly in another place. - (e.g. Thread/Process destructor) */ - - assert(currentThread == &cleanup_thread_); + functionality could be implemented somewhere else in a cleaner way. + (e.g. Thread/Process destructor which are called by this function) */ - lockScheduling(); - uint32 thread_count_max = sizeof(cleanup_thread_.kernel_stack_) / (2 * sizeof(Thread*)); - thread_count_max = ustl::min(thread_count_max, threads_.size()); + scheduler_lock_.acquire(); + uint32 thread_count_max = eastl::min(threads_.size(), sizeof(cleanup_thread_.kernel_stack_) / (2 * sizeof(Thread*))); Thread* destroy_list[thread_count_max]; uint32 thread_count = 0; - for (uint32 i = 0; i < threads_.size() && thread_count < thread_count_max; ++i) + + auto it = threads_.begin(); + while(it != threads_.end()) { - Thread* tmp = threads_[i]; - if (tmp->getState() == ToBeDestroyed) + Thread* t = *it; + if ((t->getState() == Thread::ToBeDestroyed) && !t->isCurrentlyScheduled()) { - destroy_list[thread_count++] = tmp; - threads_.erase(threads_.begin() + i); // Note: erase will not realloc! - --i; + destroy_list[thread_count++] = t; + it = threads_.erase(it); // Note: erase will not realloc! + --num_threads; + + if (thread_count >= thread_count_max) + break; } + else + ++it; } - unlockScheduling(); + scheduler_lock_.release(); + if (thread_count > 0) { for (uint32 i = 0; i < thread_count; ++i) @@ -136,28 +292,28 @@ void Scheduler::cleanupDeadThreads() void Scheduler::printThreadList() { - lockScheduling(); - debug(SCHEDULER, "Scheduler::printThreadList: %zd Threads in List\n", threads_.size()); - for (size_t c = 0; c < threads_.size(); ++c) - debug(SCHEDULER, "Scheduler::printThreadList: threads_[%zd]: %p %zd:%25s [%s]\n", c, threads_[c], - threads_[c]->getTID(), threads_[c]->getName(), Thread::threadStatePrintable[threads_[c]->state_]); - unlockScheduling(); -} - -void Scheduler::lockScheduling() //not as severe as stopping Interrupts -{ - if (unlikely(ArchThreads::testSetLock(block_scheduling_, 1))) - kpanict("FATAL ERROR: Scheduler::*: block_scheduling_ was set !! How the Hell did the program flow get here then ?\n"); -} - -void Scheduler::unlockScheduling() -{ - block_scheduling_ = 0; + scheduler_lock_.acquire(); + kprintfd("Scheduler::printThreadList: %zd Threads in List\n", threads_.size()); + for (auto t : threads_) + { + kprintfd("Scheduler::printThreadList: %p %zd:%25s [%s] at saved %s ip %p, " + "vruntime: %" PRIu64 "\n", + t, t->getTID(), t->getName(), Thread::threadStatePrintable[t->state_], + (t->switch_to_userspace_ ? "user" : "kernel"), + (void*)(t->switch_to_userspace_ + ? ArchThreads::getInstructionPointer(*t->user_registers_) + : ArchThreads::getInstructionPointer(*t->kernel_registers_)), + t->vruntime); + } + scheduler_lock_.release(); } bool Scheduler::isSchedulingEnabled() { - return this && block_scheduling_ == 0; + if (this) + return scheduler_lock_.heldBy() == (size_t)-1; + else + return false; } bool Scheduler::isCurrentlyCleaningUp() @@ -165,48 +321,137 @@ bool Scheduler::isCurrentlyCleaningUp() return currentThread == &cleanup_thread_; } -size_t Scheduler::getTicks() +size_t Scheduler::getCpuTimerTicks() const { - return ticks_; + return cpu_timer_ticks; } -void Scheduler::incTicks() +void Scheduler::incCpuTimerTicks() { - ++ticks_; + ++cpu_timer_ticks; } void Scheduler::printStackTraces() { - lockScheduling(); - debug(BACKTRACE, "printing the backtraces of <%zd> threads:\n", threads_.size()); + scheduler_lock_.acquire(); + debugAlways(BACKTRACE, "printing the backtraces of <%zu> threads:\n", threads_.size()); - for (const auto& thread : threads_) + for (const auto thread : threads_) { thread->printBacktrace(); - debug(BACKTRACE, "\n"); - debug(BACKTRACE, "\n"); + debugAlways(BACKTRACE, "\n"); + debugAlways(BACKTRACE, "\n"); } - unlockScheduling(); + scheduler_lock_.release(); } void Scheduler::printLockingInformation() { - lockScheduling(); - kprintfd("\n"); - debug(LOCK, "Scheduler::printLockingInformation:\n"); - - for(Thread* t : threads_) + scheduler_lock_.acquire(); + debugAlways(LOCK, "\nScheduler::printLockingInformation:\n"); + for (const auto thread : threads_) { - if(t->holding_lock_list_) - Lock::printHoldingList(t); + if(thread->holding_lock_list_) + { + Lock::printHoldingList(thread); + } } - for(Thread* t : threads_) + for (const auto thread : threads_) { - if(t->lock_waiting_on_) - debug(LOCK, "Thread %s (%p) is waiting on lock: %s (%p).\n", - t->getName(), t, t->lock_waiting_on_ ->getName(), t->lock_waiting_on_ ); + if(thread->lock_waiting_on_) + { + debugAlways(LOCK, "Thread %s (%p) is waiting on lock: %s (%p), held by: %p, last accessed at %zx\n", + thread->getName(), thread, thread->lock_waiting_on_ ->getName(), thread->lock_waiting_on_, + thread->lock_waiting_on_->heldBy(), thread->lock_waiting_on_->last_accessed_at_); + + thread->lock_waiting_on_->printStatus(); + } } - debug(LOCK, "Scheduler::printLockingInformation finished\n"); - unlockScheduling(); + debugAlways(LOCK, "Scheduler::printLockingInformation finished\n"); + scheduler_lock_.release(); +} + +bool Scheduler::isInitialized() +{ + return instance_ != nullptr; +} + +Thread* Scheduler::minVruntimeThread() +{ + assert(scheduler_lock_.isHeldBy(SMP::currentCpuId())); + for(auto & thread : threads_) + { + if(thread->schedulable()) + { + return thread; + } + } + + return nullptr; +} + +Thread* Scheduler::maxVruntimeThread() +{ + assert(scheduler_lock_.isHeldBy(SMP::currentCpuId())); + for(auto it = threads_.rbegin(); it != threads_.rend(); ++it) + { + if((*it)->schedulable()) + { + return *it; + } + } + + return nullptr; +} + +uint64 Scheduler::updateVruntime(Thread* t, uint64 now) +{ + assert(t->currently_scheduled_on_cpu_ == SMP::currentCpuId()); + + if(now <= t->schedulingStartTimestamp()) + { + return 0; + } + + uint64 time_delta = now - t->schedulingStartTimestamp(); + + setThreadVruntime(t, t->vruntime + time_delta); + + + if(SCHEDULER & OUTPUT_ADVANCED) + debug(SCHEDULER, "CPU %zu, %s vruntime: %" PRIu64 " (+ %" PRIu64 ") [%" PRIu64 " -> %" PRIu64 "]\n", + SMP::currentCpuId(), t->getName(), t->vruntime, time_delta, t->schedulingStartTimestamp(), now); + + t->setSchedulingStartTimestamp(now); + + return time_delta; +} + +Scheduler::ThreadList::iterator Scheduler::setThreadVruntime(Thread* t, uint64 new_vruntime) +{ + assert(scheduler_lock_.isHeldBy(SMP::currentCpuId())); + + auto it = threads_.find(t); + + return setThreadVruntime(it, new_vruntime); +} + +Scheduler::ThreadList::iterator Scheduler::setThreadVruntime(Scheduler::ThreadList::iterator it, uint64 new_vruntime) +{ + assert(scheduler_lock_.isHeldBy(SMP::currentCpuId())); + assert(it != threads_.end()); + Thread* t = *it; + if(SCHEDULER & OUTPUT_ADVANCED) + debug(SCHEDULER, "CPU %zu, set %s vruntime = %" PRIu64 "\n", + SMP::currentCpuId(), t->getName(), new_vruntime); + + // vruntime is used as the sorting key for the set and cannot be modified in place without temporarily removing the element from the set + // Using C++17 extract() would be much better here, but is unfortunately not (yet) available in EASTL + // https://en.cppreference.com/w/cpp/container/multiset/extract + threads_.erase(it); + + t->vruntime = new_vruntime; + + return threads_.insert(t); } diff --git a/common/source/kernel/SchedulerLock.cpp b/common/source/kernel/SchedulerLock.cpp new file mode 100644 index 000000000..82570e955 --- /dev/null +++ b/common/source/kernel/SchedulerLock.cpp @@ -0,0 +1,49 @@ +#include "SchedulerLock.h" + +#include "Scheduler.h" +#include "Thread.h" + +#include "ArchCommon.h" +#include "ArchMulticore.h" + +#include "debug.h" + +enum SCHED_LOCK_INDICATOR : char +{ + FREE = ' ', + BLOCKED = '#', + HOLDING = '-', +}; + +SchedulerLock::SchedulerLock() : + CpuExclusiveLock("Scheduler lock") +{ +} + +void SchedulerLock::acquire([[maybe_unused]]pointer called_by) +{ + size_t cpu_id = SMP::currentCpuId(); + ((char*)ArchCommon::getFBPtr())[80*2 + cpu_id*2] = SCHED_LOCK_INDICATOR::BLOCKED; + + CpuExclusiveLock::acquire(called_by); + + scheduling_locked_by_ = currentThread; + + debug(SCHEDULER_LOCK, "locked by %s (%p) on CPU %zu\n", (currentThread ? currentThread->getName() : "(nil)"), currentThread, SMP::currentCpuId()); + + ((char*)ArchCommon::getFBPtr())[80*2 + cpu_id*2] = SCHED_LOCK_INDICATOR::HOLDING; +} + +void SchedulerLock::release([[maybe_unused]]pointer called_by) +{ + size_t cpu_id = SMP::currentCpuId(); + if(currentThread) + { + debug(SCHEDULER_LOCK, "unlocked by %s (%p) on CPU %zu\n", currentThread->getName(), currentThread, cpu_id); + } + scheduling_locked_by_ = nullptr; + + ((char*)ArchCommon::getFBPtr())[80*2 + cpu_id*2] = SCHED_LOCK_INDICATOR::FREE; + + CpuExclusiveLock::release(called_by); +} diff --git a/common/source/kernel/ScopeLock.cpp b/common/source/kernel/ScopeLock.cpp index eb2289aee..73c0d0bca 100644 --- a/common/source/kernel/ScopeLock.cpp +++ b/common/source/kernel/ScopeLock.cpp @@ -1,12 +1,23 @@ -#include "backtrace.h" +#include "ScopeLock.h" + #include "Mutex.h" +#include "backtrace.h" -ScopeLock::ScopeLock(Mutex &m) : mutex_(m) +ScopeLock::ScopeLock(Mutex& m, bool b, pointer called_by) : + mutex_(m), + use_mutex_(b) { - mutex_.acquire(getCalledBefore(1)); + if (likely(use_mutex_)) + { + mutex_.acquire(called_by); + } } ScopeLock::~ScopeLock() { - mutex_.release(getCalledBefore(1)); + if (likely(use_mutex_)) + { + mutex_.release(getCalledBefore(1)); + use_mutex_ = false; + } } diff --git a/common/source/kernel/SpinLock.cpp b/common/source/kernel/SpinLock.cpp index 7f14674a0..756744961 100644 --- a/common/source/kernel/SpinLock.cpp +++ b/common/source/kernel/SpinLock.cpp @@ -1,12 +1,20 @@ #include "SpinLock.h" -#include "kprintf.h" -#include "ArchThreads.h" -#include "ArchInterrupts.h" + +#include "SMP.h" #include "Scheduler.h" -#include "assert.h" #include "Stabs2DebugInfo.h" +#include "SystemState.h" +#include "Thread.h" #include "backtrace.h" -extern Stabs2DebugInfo const *kernel_debug_info; +#include "kprintf.h" + +#include "ArchInterrupts.h" +#include "ArchMulticore.h" +#include "ArchThreads.h" + +#include "assert.h" + +extern const Stabs2DebugInfo* kernel_debug_info; SpinLock::SpinLock(const char* name) : Lock::Lock(name), lock_(0) @@ -20,7 +28,7 @@ bool SpinLock::acquireNonBlocking(pointer called_by) if(!called_by) called_by = getCalledBefore(1); // debug(LOCK, "Spinlock::acquireNonBlocking: Acquire spinlock %s (%p) with thread %s (%p)\n", -// getName(), this, currentThread->getName(), currentThread); +// getName(), this, currentThread()->getName(), currentThread()); // if(kernel_debug_info) // { // debug(LOCK, "The acquire is called by: "); @@ -32,79 +40,109 @@ bool SpinLock::acquireNonBlocking(pointer called_by) // So in case you see this comment, re-think your implementation and don't just comment out this line! doChecksBeforeWaiting(); - if(ArchThreads::testSetLock(lock_, 1)) + if(ArchThreads::testSetLock(lock_, (size_t)1)) { // The spinlock is held by another thread at the moment return false; } // The spinlock is now held by the current thread. - assert(held_by_ == 0); + assert(held_by_ == nullptr); last_accessed_at_ = called_by; held_by_ = currentThread; pushFrontToCurrentThreadHoldingList(); return true; } -void SpinLock::acquire(pointer called_by) +void SpinLock::acquire(pointer called_by, bool yield) { - if(unlikely(system_state != RUNNING)) - return; if(!called_by) called_by = getCalledBefore(1); + + if(system_state == RUNNING) + { + debugAdvanced(LOCK, "CPU %zx acquiring spinlock %s (%p), called by: %zx\n", SMP::currentCpuId(), getName(), this, called_by); + } + else + { + debugAdvanced(LOCK, "acquiring spinlock %s (%p), called by: %zx\n", getName(), this, called_by); + } + // debug(LOCK, "Spinlock::acquire: Acquire spinlock %s (%p) with thread %s (%p)\n", -// getName(), this, currentThread->getName(), currentThread); +// getName(), this, currentThread()->getName(), currentThread()); // if(kernel_debug_info) // { // debug(LOCK, "The acquire is called by: "); // kernel_debug_info->printCallInformation(called_by); // } - if(ArchThreads::testSetLock(lock_, 1)) + if(ArchThreads::testSetLock(lock_, (size_t)1)) { + debugAdvanced(LOCK, "didn't get spinlock %s (%p), called by: %zx\n", getName(), this, called_by); // We did not directly managed to acquire the spinlock, need to check for deadlocks and // to push the current thread to the waiters list. doChecksBeforeWaiting(); - currentThread->lock_waiting_on_ = this; - lockWaitersList(); - pushFrontCurrentThreadToWaitersList(); - unlockWaitersList(); + //assert(currentThread); // debug + if(currentThread) + { + currentThread->lock_waiting_on_ = this; + lockWaitersList(yield); + pushFrontCurrentThreadToWaitersList(); + unlockWaitersList(); + } // here comes the basic spinlock - while(ArchThreads::testSetLock(lock_, 1)) + while(ArchThreads::testSetLock(lock_, (size_t)1)) { + ArchCommon::spinlockPause(); //SpinLock: Simplest of Locks, do the next best thing to busy waiting - Scheduler::instance()->yield(); + if(currentThread && yield) + { + ArchInterrupts::yieldIfIFSet(); + } } // Now we managed to acquire the spinlock. Remove the current thread from the waiters list. - lockWaitersList(); - removeCurrentThreadFromWaitersList(); - unlockWaitersList(); - currentThread->lock_waiting_on_ = 0; + if(currentThread) + { + lockWaitersList(yield); + removeCurrentThreadFromWaitersList(); + unlockWaitersList(); + currentThread->lock_waiting_on_ = nullptr; + } } + debugAdvanced(LOCK, "got spinlock %s (%p), called by: %zx\n", getName(), this, called_by); // The current thread is now holding the spinlock last_accessed_at_ = called_by; held_by_ = currentThread; pushFrontToCurrentThreadHoldingList(); } -bool SpinLock::isFree() +bool SpinLock::isFree() const { - if(unlikely(ArchInterrupts::testIFSet() && Scheduler::instance()->isSchedulingEnabled())) + if(unlikely(ArchInterrupts::testIFSet() && Scheduler::instance()->isSchedulingEnabled() && !(SMP::numRunningCpus() > 1))) { - debug(LOCK, "SpinLock::isFree: ERROR: Should not be used with IF=1 AND enabled Scheduler, use acquire instead\n"); - assert(false); + return false; + //debug(LOCK, "SpinLock::isFree: ERROR: Should not be used with IF=1 AND enabled Scheduler, use acquire instead\n"); + //assert(false); } return (lock_ == 0); } void SpinLock::release(pointer called_by) { - if(unlikely(system_state != RUNNING)) - return; if(!called_by) called_by = getCalledBefore(1); + + if(system_state == RUNNING) + { + debugAdvanced(LOCK, "CPU %zx releasing spinlock %s (%p), called by: %zx\n", SMP::currentCpuId(), getName(), this, called_by); + } + else + { + debugAdvanced(LOCK, "releasing spinlock %s (%p), called by: %zx\n", getName(), this, called_by); + } + // debug(LOCK, "Spinlock::release: Release spinlock %s (%p) with thread %s (%p)\n", -// getName(), this, currentThread->getName(), currentThread); +// getName(), this, currentThread()->getName(), currentThread()); // if(kernel_debug_info) // { // debug(LOCK, "The release is called by: "); @@ -113,7 +151,6 @@ void SpinLock::release(pointer called_by) checkInvalidRelease("SpinLock::release"); removeFromCurrentThreadHoldingList(); last_accessed_at_ = called_by; - held_by_ = 0; + held_by_ = nullptr; lock_ = 0; } - diff --git a/common/source/kernel/Syscall.cpp b/common/source/kernel/Syscall.cpp index 964cd5b44..17aa8a01b 100644 --- a/common/source/kernel/Syscall.cpp +++ b/common/source/kernel/Syscall.cpp @@ -1,20 +1,36 @@ -#include "offsets.h" #include "Syscall.h" -#include "syscall-definitions.h" -#include "Terminal.h" -#include "debug_bochs.h" -#include "VfsSyscall.h" -#include "ProcessRegistry.h" + #include "File.h" +#include "ProcessRegistry.h" #include "Scheduler.h" +#include "Terminal.h" +#include "VfsSyscall.h" +#include "debug_bochs.h" +#include "offsets.h" +#include "syscall-definitions.h" + +#include "ArchMulticore.h" -size_t Syscall::syscallException(size_t syscall_number, size_t arg1, size_t arg2, size_t arg3, size_t arg4, size_t arg5) +#include "assert.h" + +size_t Syscall::syscallException(size_t syscall_number, + size_t arg1, + size_t arg2, + size_t arg3, + size_t arg4, + size_t arg5) { size_t return_value = 0; - if ((syscall_number != sc_sched_yield) && (syscall_number != sc_outline)) // no debug print because these might occur very often + + // no debug print because these might occur very often + if ((syscall_number != sc_sched_yield) && (syscall_number != sc_outline)) { - debug(SYSCALL, "Syscall %zd called with arguments %zd(=%zx) %zd(=%zx) %zd(=%zx) %zd(=%zx) %zd(=%zx)\n", - syscall_number, arg1, arg1, arg2, arg2, arg3, arg3, arg4, arg4, arg5, arg5); + debug(SYSCALL, + "CPU %zu: Syscall %zd called with arguments %zd(=%zx) %zd(=%zx) %zd(=%zx) " + "%zd(=%zx) %zd(=%zx) by %s[%zu] (%p)\n", + SMP::currentCpuId(), syscall_number, arg1, arg1, arg2, arg2, arg3, arg3, arg4, + arg4, arg5, arg5, currentThread->getName(), currentThread->getTID(), + currentThread); } switch (syscall_number) @@ -40,36 +56,41 @@ size_t Syscall::syscallException(size_t syscall_number, size_t arg1, size_t arg2 case sc_close: return_value = close(arg1); break; + case sc_lseek: + return_value = lseek(arg1, arg2, arg3); + break; case sc_outline: outline(arg1, arg2); break; case sc_trace: trace(); break; - case sc_pseudols: - pseudols((const char*) arg1, (char*) arg2, arg3); + case sc_getdents: + return_value = getdents((int) arg1, (char*) arg2, arg3); + break; + case sc_getcpu: + return_value = getcpu((size_t*)arg1, (size_t*)arg2, (void*)arg3); break; default: return_value = -1; - kprintf("Syscall::syscallException: Unimplemented Syscall Number %zd\n", syscall_number); + kprintf("Syscall::syscallException: Unimplemented Syscall Number %zu\n", syscall_number); } return return_value; } -void Syscall::pseudols(const char *pathname, char *buffer, size_t size) +ssize_t Syscall::getdents(int fd, char *buffer, size_t size) { - if(buffer && ((size_t)buffer >= USER_BREAK || (size_t)buffer + size > USER_BREAK)) - return; - if((size_t)pathname >= USER_BREAK) - return; - VfsSyscall::readdir(pathname, buffer, size); + if(buffer && ((size_t)buffer >= USER_BREAK || (size_t)buffer + size > USER_BREAK)) + return -1; + return VfsSyscall::getdents(fd, buffer, size); } -void Syscall::exit(size_t exit_code) +[[noreturn]] void Syscall::exit(size_t exit_code) { debug(SYSCALL, "Syscall::EXIT: called, exit_code: %zd\n", exit_code); currentThread->kill(); - assert(false && "This should never happen"); + + assert(false && "Returned from currentThread->kill()"); } size_t Syscall::write(size_t fd, pointer buffer, size_t size) @@ -131,6 +152,11 @@ size_t Syscall::open(size_t path, size_t flags) return VfsSyscall::open((char*) path, flags); } +size_t Syscall::lseek(int fd, off_t offset, int whence) +{ + return VfsSyscall::lseek(fd, offset, whence); +} + void Syscall::outline(size_t port, pointer text) { //WARNING: this might fail if Kernel PageFaults are not handled @@ -165,7 +191,7 @@ size_t Syscall::createprocess(size_t path, size_t sleep) // parameter check end size_t process_count = ProcessRegistry::instance()->processCount(); - ProcessRegistry::instance()->createProcess((const char*) path); + int status = ProcessRegistry::instance()->createProcess((const char*) path); if (sleep) { while (ProcessRegistry::instance()->processCount() > process_count) // please note that this will fail ;) @@ -173,7 +199,7 @@ size_t Syscall::createprocess(size_t path, size_t sleep) Scheduler::instance()->yield(); } } - return 0; + return status; } void Syscall::trace() @@ -181,3 +207,26 @@ void Syscall::trace() currentThread->printBacktrace(); } + +int Syscall::getcpu(size_t *cpu, size_t *node, [[maybe_unused]] void *tcache) +{ + if(((size_t)cpu >= USER_BREAK) || + ((size_t)cpu + sizeof(*cpu) >= USER_BREAK) || + ((size_t)node >= USER_BREAK) || + ((size_t)node + sizeof(*node) >= USER_BREAK)) + { + return -1; + } + + if(cpu != nullptr) + { + *cpu = SMP::currentCpuId(); + } + + if(node != nullptr) + { + *node = 0; + } + + return 0; +} diff --git a/common/source/kernel/SystemState.cpp b/common/source/kernel/SystemState.cpp new file mode 100644 index 000000000..47df65250 --- /dev/null +++ b/common/source/kernel/SystemState.cpp @@ -0,0 +1,3 @@ +#include "SystemState.h" + +SystemState system_state; diff --git a/common/source/kernel/Thread.cpp b/common/source/kernel/Thread.cpp index 50f59f125..7d5e4157e 100644 --- a/common/source/kernel/Thread.cpp +++ b/common/source/kernel/Thread.cpp @@ -1,58 +1,60 @@ #include "Thread.h" -#include "kprintf.h" -#include "ArchThreads.h" -#include "ArchInterrupts.h" -#include "Scheduler.h" -#include "Loader.h" #include "Console.h" -#include "Terminal.h" -#include "backtrace.h" #include "KernelMemoryManager.h" +#include "Loader.h" +#include "ProcessRegistry.h" +#include "Scheduler.h" #include "Stabs2DebugInfo.h" +#include "Terminal.h" +#include "backtrace.h" +#include "kprintf.h" -#define BACKTRACE_MAX_FRAMES 20 - - +#include "ArchInterrupts.h" +#include "ArchThreads.h" -const char* Thread::threadStatePrintable[3] = -{ -"Running", "Sleeping", "ToBeDestroyed" -}; +static constexpr size_t BACKTRACE_MAX_FRAMES = 20; -extern "C" void threadStartHack() +extern "C" [[noreturn]] void threadStartHack() { + assert(currentThread); currentThread->setTerminal(main_console->getActiveTerminal()); currentThread->Run(); currentThread->kill(); - debug(THREAD, "ThreadStartHack: Panic, thread couldn't be killed\n"); - while(1); + debugAlways(THREAD, "ThreadStartHack: Panic, thread couldn't be killed\n"); + assert(false); } -Thread::Thread(FileSystemInfo *working_dir, ustl::string name, Thread::TYPE type) : - kernel_registers_(0), user_registers_(0), switch_to_userspace_(type == Thread::USER_THREAD ? 1 : 0), loader_(0), - next_thread_in_lock_waiters_list_(0), lock_waiting_on_(0), holding_lock_list_(0), state_(Running), tid_(0), - my_terminal_(0), working_dir_(working_dir), name_(ustl::move(name)) +Thread::Thread(FileSystemInfo* working_dir, eastl::string name, Thread::TYPE type) : + kernel_registers_(ArchThreads::createKernelRegisters( + (type == Thread::USER_THREAD ? nullptr : (void*)&threadStartHack), + getKernelStackStartPointer())), + switch_to_userspace_(type == Thread::USER_THREAD ? 1 : 0), + console_color(type == Thread::USER_THREAD ? CONSOLECOLOR::BRIGHT_BLUE : CONSOLECOLOR::BRIGHT_GREEN), + state_(Running), + tid_(0), + working_dir_(working_dir), + name_(eastl::move(name)) { - debug(THREAD, "Thread ctor, this is %p, stack is %p, fs_info ptr: %p\n", this, kernel_stack_, working_dir_); - ArchThreads::createKernelRegisters(kernel_registers_, (void*) (type == Thread::USER_THREAD ? 0 : threadStartHack), getKernelStackStartPointer()); - kernel_stack_[2047] = STACK_CANARY; - kernel_stack_[0] = STACK_CANARY; + debug(THREAD, + "Thread ctor, this is %p, name: %s, kernel stack: [%p, %p), working_dir ptr: " + "%p\n", + this, getName(), kernel_stack_, (char*)kernel_stack_ + sizeof(kernel_stack_), + working_dir_); + + initKernelStackCanary(); } Thread::~Thread() { - debug(THREAD, "~Thread: freeing ThreadInfos\n"); - delete user_registers_; - user_registers_ = 0; - delete kernel_registers_; - kernel_registers_ = 0; - if(unlikely(holding_lock_list_ != 0)) + debug(THREAD, "~Thread %s: freeing ThreadInfos\n", getName()); + // registers automatically destroyed via unique_ptr + + if(unlikely(holding_lock_list_ != nullptr)) { - debug(THREAD, "~Thread: ERROR: Thread <%s (%p)> is going to be destroyed, but still holds some locks!\n", - getName(), this); + debugAlways(THREAD, "~Thread: ERROR: Thread <%s [%zu] (%p)> is going to be destroyed, but still holds some locks!\n", getName(), getTID(), this); Lock::printHoldingList(this); - assert(false); + assert(false && "~Thread: ERROR: Thread is going to be destroyed, but still holds some locks!\n"); } debug(THREAD, "~Thread: done (%s)\n", name_.c_str()); } @@ -60,8 +62,7 @@ Thread::~Thread() // DO NOT use new / delete in this Method, as it is sometimes called from an Interrupt Handler with Interrupts disabled void Thread::kill() { - debug(THREAD, "kill: Called by <%s (%p)>. Preparing Thread <%s (%p)> for destruction\n", currentThread->getName(), - currentThread, getName(), this); + debug(THREAD, "kill: Called by <%s (%p)>. Preparing Thread <%s (%p)> for destruction\n", currentThread->getName(), currentThread, getName(), this); setState(ToBeDestroyed); // vvv Code below this line may not be executed vvv @@ -69,27 +70,46 @@ void Thread::kill() { ArchInterrupts::enableInterrupts(); Scheduler::instance()->yield(); - assert(false && "This should never happen, how are we still alive?"); + assert(false && "Thread scheduled again after yield with state == ToBeDestroyed"); } } -void* Thread::getKernelStackStartPointer() +void* Thread::getKernelStackStartPointer() const { pointer stack = (pointer) kernel_stack_; - stack += sizeof(kernel_stack_) - sizeof(uint32); + stack += sizeof(kernel_stack_) - 2*sizeof(size_t); return (void*)stack; } +void Thread::initKernelStackCanary() +{ + kernel_stack_[2047] = STACK_CANARY; + kernel_stack_[0] = STACK_CANARY; +} + bool Thread::currentThreadIsStackCanaryOK() { - return !currentThread || currentThread->isStackCanaryOK(); + if (!CpuLocalStorage::ClsInitialized()) + return true; + + return !currentThread || currentThread->isStackCanaryOK(); } -bool Thread::isStackCanaryOK() + +// Hack to allow the function to be called from the klibc +// klibc is standalone and cannot include other SWEB code, +// but function can be declared with external linkage. +// We cannot use a class member function for that purpose +extern "C" bool currentThreadIsStackCanaryOK() +{ + return Thread::currentThreadIsStackCanaryOK(); +} + +bool Thread::isStackCanaryOK() const { return kernel_stack_[0] == STACK_CANARY && kernel_stack_[2047] == STACK_CANARY; } -Terminal *Thread::getTerminal() +Terminal* Thread::getTerminal() const { return my_terminal_ ? my_terminal_ : main_console->getActiveTerminal(); } @@ -104,7 +124,7 @@ void Thread::printBacktrace() printBacktrace(currentThread != this); } -FileSystemInfo* Thread::getWorkingDirInfo() +FileSystemInfo* Thread::getWorkingDirInfo() const { return working_dir_; } @@ -121,7 +141,7 @@ void Thread::setWorkingDirInfo(FileSystemInfo* working_dir) working_dir_ = working_dir; } -extern Stabs2DebugInfo const *kernel_debug_info; +extern const Stabs2DebugInfo* kernel_debug_info; void Thread::printBacktrace(bool use_stored_registers) { @@ -138,7 +158,8 @@ void Thread::printBacktrace(bool use_stored_registers) { count = backtrace(call_stack, BACKTRACE_MAX_FRAMES, this, use_stored_registers); - debug(BACKTRACE, "=== Begin of backtrace for %sthread <%s> ===\n", user_registers_ ? "user" : "kernel", getName()); + debug(BACKTRACE, "=== Begin of backtrace for %sthread <%s>[%zu] (%p) ===\n", user_registers_ ? "user" : "kernel", getName(), getTID(), this); + debug(BACKTRACE, " ----- Kernel -----------------------\n"); for(size_t i = 0; i < count; ++i) { debug(BACKTRACE, " "); @@ -147,21 +168,22 @@ void Thread::printBacktrace(bool use_stored_registers) } if(user_registers_) { - Stabs2DebugInfo const *deb = loader_->getDebugInfos(); - count = backtrace_user(call_stack, BACKTRACE_MAX_FRAMES, this, 0); - debug(BACKTRACE, " ----- Userspace --------------------\n"); - if(!deb) - debug(BACKTRACE, "Userspace debug info not set up, backtrace won't look nice!\n"); - else - { - for(size_t i = 0; i < count; ++i) + const Stabs2DebugInfo* deb = loader_->getDebugInfos(); + count = backtrace_user(call_stack, BACKTRACE_MAX_FRAMES, this, false); + debug(BACKTRACE, " ----- Userspace --------------------\n"); + if (!deb) + debug(BACKTRACE, + "Userspace debug info not set up, backtrace won't look nice!\n"); + else { - debug(BACKTRACE, " "); - deb->printCallInformation(call_stack[i]); - } + for (size_t i = 0; i < count; ++i) + { + debug(BACKTRACE, " "); + deb->printCallInformation(call_stack[i]); + } } } - debug(BACKTRACE, "=== End of backtrace for %sthread <%s> ===\n", user_registers_ ? "user" : "kernel", getName()); + debug(BACKTRACE, "=== End of backtrace for %sthread <%s>[%zu] (%p) ===\n", user_registers_ ? "user" : "kernel", getName(), getTID(), this); } bool Thread::schedulable() @@ -169,23 +191,55 @@ bool Thread::schedulable() return (getState() == Running); } -const char *Thread::getName() +bool Thread::canRunOnCpu(size_t cpu_id) const +{ + return (pinned_to_cpu == (size_t)-1) || (pinned_to_cpu == cpu_id); +} + +bool Thread::isCurrentlyScheduled() const +{ + return currently_scheduled_on_cpu_ != (size_t)-1; +} + +bool Thread::isCurrentlyScheduledOnCpu(size_t cpu_id) const { + return currently_scheduled_on_cpu_ == cpu_id; +} + +void Thread::setSchedulingStartTimestamp(uint64 timestamp) +{ + sched_start = timestamp; +} + +uint64 Thread::schedulingStartTimestamp() const +{ + return sched_start; +} + +const char *Thread::getName() const +{ + if(!this) + { + return "(nil)"; + } + return name_.c_str(); } -size_t Thread::getTID() +size_t Thread::getTID() const { return tid_; } -ThreadState Thread::getState() const +Thread::ThreadState Thread::getState() const { + assert(this); return state_; } void Thread::setState(ThreadState new_state) { + assert(this); assert(!((state_ == ToBeDestroyed) && (new_state != ToBeDestroyed)) && "Tried to change thread state when thread was already set to be destroyed"); assert(!((new_state == Sleeping) && (currentThread != this)) && "Setting other threads to sleep is not thread-safe"); diff --git a/common/source/kernel/TimerTickHandler.cpp b/common/source/kernel/TimerTickHandler.cpp new file mode 100644 index 000000000..654e3ed3f --- /dev/null +++ b/common/source/kernel/TimerTickHandler.cpp @@ -0,0 +1,12 @@ +#include "TimerTickHandler.h" + +#include "Scheduler.h" + +// Architecture independent timer tick handler +void TimerTickHandler::handleTimerTick() +{ + ArchCommon::drawHeartBeat(); + Scheduler::instance()->incCpuTimerTicks(); + debugAdvanced(A_INTERRUPTS, "Timer tick %zu\n", Scheduler::instance()->getCpuTimerTicks()); + Scheduler::instance()->schedule(); +} diff --git a/common/source/kernel/UserProcess.cpp b/common/source/kernel/UserProcess.cpp index f30965589..21c048db4 100644 --- a/common/source/kernel/UserProcess.cpp +++ b/common/source/kernel/UserProcess.cpp @@ -1,66 +1,95 @@ -#include "ProcessRegistry.h" #include "UserProcess.h" -#include "kprintf.h" + #include "Console.h" -#include "Loader.h" -#include "VfsSyscall.h" #include "File.h" +#include "Loader.h" #include "PageManager.h" -#include "ArchThreads.h" -#include "offsets.h" +#include "ProcessRegistry.h" #include "Scheduler.h" +#include "VfsSyscall.h" +#include "kprintf.h" +#include "offsets.h" + +#include "ArchMemory.h" +#include "ArchMulticore.h" +#include "ArchThreads.h" + +#include "EASTL/string.h" -UserProcess::UserProcess(ustl::string filename, FileSystemInfo *fs_info, uint32 terminal_number) : - Thread(fs_info, filename, Thread::USER_THREAD), fd_(VfsSyscall::open(filename, O_RDONLY)) +UserProcess::UserProcess(const eastl::string& executable_path, + FileSystemInfo* working_dir, + uint32 terminal_number, + int& creation_status) : + Thread(working_dir, executable_path, Thread::USER_THREAD), + fd_(VfsSyscall::open(executable_path.c_str(), O_RDONLY)) { - ProcessRegistry::instance()->processStart(); //should also be called if you fork a process + debug(USERPROCESS, "Creating new user process %s\n", executable_path.c_str()); + creation_status = -1; + // should also be called if you fork a process + ProcessRegistry::instance()->processStart(); - if (fd_ >= 0) - loader_ = new Loader(fd_); + if (fd_ >= 0) + loader_ = new Loader(fd_); - if (!loader_ || !loader_->loadExecutableAndInitProcess()) - { - debug(USERPROCESS, "Error: loading %s failed!\n", filename.c_str()); - kill(); - return; - } + if (!loader_ || !loader_->loadExecutableAndInitProcess()) + { + debug(USERPROCESS, "Error: loading %s failed!\n", executable_path.c_str()); + kill(); + return; + } - size_t page_for_stack = PageManager::instance()->allocPPN(); - bool vpn_mapped = loader_->arch_memory_.mapPage(USER_BREAK / PAGE_SIZE - 1, page_for_stack, 1); - assert(vpn_mapped && "Virtual page for stack was already mapped - this should never happen"); + // Allocate a physical page for the stack and map it into the virtual address space + size_t stack_ppn = PageManager::instance().allocPPN(); + size_t stack_vpn = (USER_BREAK / PAGE_SIZE) - 1; - ArchThreads::createUserRegisters(user_registers_, loader_->getEntryFunction(), - (void*) (USER_BREAK - sizeof(pointer)), - getKernelStackStartPointer()); + bool vpn_mapped = loader_->arch_memory_.mapPage(stack_vpn, stack_ppn, true); + assert(vpn_mapped && + "Virtual page for stack was already mapped - this should never happen"); - ArchThreads::setAddressSpace(this, loader_->arch_memory_); + debug(THREAD, "Mapped stack at virt [%zx, %zx) -> phys [%zx, %zx)\n", + stack_vpn * PAGE_SIZE, (stack_vpn + 1) * PAGE_SIZE, stack_ppn * PAGE_SIZE, + (stack_ppn + 1) * PAGE_SIZE); - debug(USERPROCESS, "ctor: Done loading %s\n", filename.c_str()); + // Stack pointer is decremented as new items are pushed onto the stack -> start at + // high end + void* init_user_stackptr = + (void*)((stack_vpn * PAGE_SIZE) + PAGE_SIZE - 2 * sizeof(pointer)); - if (main_console->getTerminal(terminal_number)) - setTerminal(main_console->getTerminal(terminal_number)); + user_registers_ = ArchThreads::createUserRegisters( + loader_->getEntryFunction(), init_user_stackptr, getKernelStackStartPointer()); - switch_to_userspace_ = 1; + // Ensure the thread is using the correct virtual memory address space + ArchThreads::setAddressSpace(this, loader_->arch_memory_); + + debug(USERPROCESS, "ctor: Done loading %s\n", executable_path.c_str()); + + if (main_console->getTerminal(terminal_number)) + setTerminal(main_console->getTerminal(terminal_number)); + + // Run this thread in userspace when it is scheduled + switch_to_userspace_ = 1; + + creation_status = 0; } UserProcess::~UserProcess() { - assert(Scheduler::instance()->isCurrentlyCleaningUp()); - delete loader_; - loader_ = 0; + assert(Scheduler::instance()->isCurrentlyCleaningUp()); + delete loader_; + loader_ = nullptr; - if (fd_ > 0) - VfsSyscall::close(fd_); + if (fd_ > 0) + VfsSyscall::close(fd_); - delete working_dir_; - working_dir_ = 0; + delete working_dir_; + working_dir_ = nullptr; - ProcessRegistry::instance()->processExit(); + ProcessRegistry::instance()->processExit(); } void UserProcess::Run() { - debug(USERPROCESS, "Run: Fail-safe kernel panic - you probably have forgotten to set switch_to_userspace_ = 1\n"); - assert(false); + debug(USERPROCESS, "Run: Fail-safe kernel panic - you probably have forgotten to set " + "switch_to_userspace_ = 1\n"); + assert(false && "UserProcess::Run called"); } - diff --git a/common/source/kernel/main.cpp b/common/source/kernel/main.cpp index f7957074e..5e2555720 100644 --- a/common/source/kernel/main.cpp +++ b/common/source/kernel/main.cpp @@ -1,61 +1,100 @@ -#include -#include #include "BDManager.h" #include "BDVirtualDevice.h" +#include "BlockDeviceInode.h" +#include "BootloaderModules.h" +#include "Dentry.h" +#include "DeviceBus.h" +#include "DeviceFSSuperblock.h" +#include "DeviceFSType.h" +#include "FileDescriptor.h" +#include "FileSystemInfo.h" +#include "InitThread.h" +#include "KeyboardManager.h" +#include "Loader.h" +#include "MinixFSType.h" +#include "Mutex.h" #include "PageManager.h" -#include "ArchInterrupts.h" -#include "ArchThreads.h" -#include "kprintf.h" +#include "PlatformBus.h" +#include "ProcessRegistry.h" +#include "RamFSType.h" #include "Scheduler.h" -#include "ArchCommon.h" -#include "debug_bochs.h" -#include "ArchMemory.h" -#include "Loader.h" -#include "assert.h" #include "SerialManager.h" -#include "KeyboardManager.h" +#include "SystemState.h" +#include "Terminal.h" +#include "TextConsole.h" #include "VfsSyscall.h" -#include "Dentry.h" -#include "DeviceFSType.h" -#include "RamFSType.h" -#include "MinixFSType.h" #include "VirtualFileSystem.h" -#include "FileDescriptor.h" -#include "TextConsole.h" -#include "Terminal.h" -#include "outerrstream.h" +#include "debug_bochs.h" +#include "kprintf.h" #include "user_progs.h" +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMemory.h" +#include "ArchMulticore.h" +#include "ArchThreads.h" + +#include "libc++.h" +#include "types.h" + +#include "assert.h" + extern void* kernel_end_address; uint8 boot_stack[0x4000] __attribute__((aligned(0x4000))); -SystemState system_state; FileSystemInfo* default_working_dir; -extern "C" void initialiseBootTimePaging(); extern "C" void removeBootTimeIdentMapping(); -extern "C" void startup() +void printRunningCpus(); + +extern "C" [[noreturn]] void startup() { - writeLine2Bochs("Removing Boot Time Ident Mapping...\n"); - removeBootTimeIdentMapping(); system_state = BOOTING; - PageManager::instance(); - writeLine2Bochs("PageManager and KernelMemoryManager created \n"); + debug(MAIN, "Initializing kernel arch mem\n"); + ArchMemory::kernelArchMemory(); + + debug(MAIN, "Initializing kernel memory management\n"); + PageManager::init(); + debug(MAIN, "PageManager and KernelMemoryManager created \n"); + + debug(MAIN, "Calling global constructors\n"); + _preinit(); + globalConstructors(); + debug(MAIN, "Global constructors finished\n"); + + ArchCommon::initKernelVirtualAddressAllocator(); + + PlatformBus::initPlatformBus(); + + BootloaderModules::mapModules(); + + ArchCommon::postBootInit(); + + debug(MAIN, "SMP init\n"); + SMP::initialize(); + debug(MAIN, "Interrupts init\n"); + ArchInterrupts::initialise(); + + ArchCommon::initPlatformDrivers(); + + debug(MAIN, "Creating console\n"); main_console = ArchCommon::createConsole(1); - writeLine2Bochs("Console created \n"); + debug(MAIN, "Console created\n"); Terminal *term_0 = main_console->getTerminal(0); // add more if you need more... - term_0->initTerminalColors(Console::GREEN, Console::BLACK); + term_0->initTerminalColors(CONSOLECOLOR::GREEN, CONSOLECOLOR::BLACK); kprintfd("Init debug printf\n"); term_0->writeString("This is on term 0, you should see me now\n"); + debug(MAIN, "SetActiveTerminal 0\n"); main_console->setActiveTerminal(0); kprintf("Kernel end address is %p\n", &kernel_end_address); + debug(MAIN, "Scheduler init\n"); Scheduler::instance(); //needs to be done after scheduler and terminal, but prior to enableInterrupts @@ -63,13 +102,20 @@ extern "C" void startup() debug(MAIN, "Threads init\n"); ArchThreads::initialise(); - debug(MAIN, "Interrupts init\n"); - ArchInterrupts::initialise(); + debug(MAIN, "Multicore start CPUs\n"); + ArchMulticore::startOtherCPUs(); + + debug(MAIN, "Removing Boot Time Ident Mapping...\n"); + removeBootTimeIdentMapping(); + + debug(MAIN, "Setting scheduler tick frequency...\n"); ArchInterrupts::setTimerFrequency(IRQ0_TIMER_FREQUENCY); + debug(MAIN, "Init debug...\n"); ArchCommon::initDebug(); + debug(MAIN, "Init VFS...\n"); vfs.initialize(); debug(MAIN, "Mounting root file system\n"); vfs.registerFileSystem(DeviceFSType::getInstance()); @@ -78,24 +124,10 @@ extern "C" void startup() default_working_dir = vfs.rootMount("ramfs", 0); assert(default_working_dir); - // Important: Initialise global and static objects - new (&global_fd_list) FileDescriptorList(); - - debug(MAIN, "Block Device creation\n"); - BDManager::getInstance()->doDeviceDetection(); - debug(MAIN, "Block Device done\n"); - - for (BDVirtualDevice* bdvd : BDManager::getInstance()->device_list_) - { - debug(MAIN, "Detected Device: %s :: %d\n", bdvd->getName(), bdvd->getDeviceNumber()); - } - - debug(MAIN, "make a deep copy of FsWorkingDir\n"); main_console->setWorkingDirInfo(new FileSystemInfo(*default_working_dir)); debug(MAIN, "main_console->setWorkingDirInfo done\n"); - ustl::coutclass::init(); debug(MAIN, "default_working_dir root name: %s\t pwd name: %s\n", default_working_dir->getRoot().dentry_->getName(), default_working_dir->getPwd().dentry_->getName()); @@ -105,6 +137,8 @@ extern "C" void startup() } main_console->setWorkingDirInfo(default_working_dir); + ProcessRegistry::init(default_working_dir); + debug(MAIN, "Timer enable\n"); ArchInterrupts::enableTimer(); @@ -113,15 +147,34 @@ extern "C" void startup() debug(MAIN, "Adding Kernel threads\n"); Scheduler::instance()->addNewThread(main_console); - Scheduler::instance()->addNewThread(new ProcessRegistry(new FileSystemInfo(*default_working_dir), user_progs /*see user_progs.h*/)); + InitThread::init(new FileSystemInfo(*default_working_dir), user_progs /*see user_progs.h*/); + Scheduler::instance()->addNewThread(InitThread::instance()); + Scheduler::instance()->printThreadList(); + printRunningCpus(); - kprintf("Now enabling Interrupts...\n"); + // Ensure we already have a currentThread when interrupts are enabled + debug(MAIN, "Starting threads and enabling interrupts...\n"); system_state = RUNNING; - ArchInterrupts::enableInterrupts(); + ArchThreads::startThreads(InitThread::instance()); + + // See InitThread::Run() for further startup code - Scheduler::instance()->yield(); //not reached - assert(false); + assert(false && "Reached end of startup()"); } + + +void printRunningCpus() +{ + debug(MAIN, "%zu CPU(s) running\n", SMP::cpuList().size()); + kprintf("%zu CPU(s) running\n", SMP::cpuList().size()); + for (auto* cpu : SMP::cpuList()) + { + debug(MAIN, "CPU %zu\n", cpu->id()); + kprintf("CPU %zu\n", cpu->id()); + } +} + + diff --git a/common/source/kernel/smp/CMakeLists.txt b/common/source/kernel/smp/CMakeLists.txt new file mode 100644 index 000000000..7c56a9e29 --- /dev/null +++ b/common/source/kernel/smp/CMakeLists.txt @@ -0,0 +1,4 @@ +add_project_library(common_kernel_smp) + +target_include_directories(kernel PUBLIC + ../../../include/kernel/smp) diff --git a/common/source/kernel/smp/Cpu.cpp b/common/source/kernel/smp/Cpu.cpp new file mode 100644 index 000000000..c4c0ede6b --- /dev/null +++ b/common/source/kernel/smp/Cpu.cpp @@ -0,0 +1,33 @@ +#include "Cpu.h" + +#include "DeviceBus.h" + +#include "ArchCpuLocalStorage.h" + +#include "assert.h" +#include "debug.h" + +cpu_local size_t Cpu::cpu_id; + +Cpu::Cpu() : + Device("CPU"), + cpu_id_(&cpu_id) +{ + deviceTreeRoot().addSubDevice(*this); +} + +size_t Cpu::id() +{ + return *cpu_id_; +} + +void Cpu::setId(size_t id) +{ + *cpu_id_ = id; + setDeviceName(eastl::string("CPU ") + eastl::to_string(id)); +} + +void Cpu::enqueueFunctionCallMessage(RemoteFunctionCallMessage* fcall_message) +{ + fcall_queue.pushFront(fcall_message); +} diff --git a/common/source/kernel/smp/SMP.cpp b/common/source/kernel/smp/SMP.cpp new file mode 100644 index 000000000..804e26460 --- /dev/null +++ b/common/source/kernel/smp/SMP.cpp @@ -0,0 +1,110 @@ +#include "SMP.h" + +#include "ArchMulticore.h" + +#include "EASTL/atomic.h" + +#include "debug.h" + +cpu_local ArchCpu current_cpu; +eastl::atomic running_cpus; + +extern cpu_local Thread* currentThread; + +ArchCpu& SMP::currentCpu() +{ + return current_cpu; +} + +size_t SMP::currentCpuId() +{ + return (!CpuLocalStorage::ClsInitialized() ? 0 : currentCpu().id()); +} + +size_t SMP::numRunningCpus() +{ + return running_cpus; +} + +void SMP::initialize() +{ + ArchMulticore::initialize(); +} + +void SMP::addCpuToList(ArchCpu* cpu) +{ + ScopeLock l(cpuListLock()); + SMP::cpuList().push_back(cpu); +} + +eastl::vector& SMP::cpuList() +{ + static eastl::vector cpu_list_; + return cpu_list_; +} + +Mutex& SMP::cpuListLock() +{ + static Mutex cpu_list_lock_("CPU list lock"); + return cpu_list_lock_; +} + +ArchCpu* SMP::cpu(size_t cpu_id) +{ + ScopeLock l(cpuListLock()); + for (auto c : SMP::cpuList()) + { + if (c->id() == cpu_id) + return c; + } + return nullptr; +} + +void SMP::callOnOtherCpus(const RemoteFunctionCallMessage::function_t& func) +{ + assert(func); + debug(A_MULTICORE, "Calling function on other cpus\n"); + + // Prepare + RemoteFunctionCallMessage funcdata[SMP::cpuList().size()]{}; + + auto orig_cpu = SMP::currentCpuId(); + + for (auto& fd : funcdata) + { + fd.func = func; + fd.orig_cpu = orig_cpu; + fd.target_cpu = -1; + fd.done = false; + fd.received = false; + } + + // Send + for (auto* cpu : SMP::cpuList()) + { + auto id = cpu->id(); + funcdata[id].target_cpu = id; + + if (id != orig_cpu) + { + ArchMulticore::sendFunctionCallMessage(*cpu, &funcdata[id]); + } + } + + // Wait for ack + for (auto& fd : funcdata) + { + assert(fd.target_cpu != (size_t)-1); + if (fd.target_cpu == orig_cpu) + continue; + + if (!fd.done.load(eastl::memory_order_acquire)) + { + if (A_MULTICORE & OUTPUT_ADVANCED) + debug(A_MULTICORE, "Waiting for ack from CPU %zu for request from CPU %zu, received: %u\n", + fd.target_cpu, orig_cpu, fd.received.load(eastl::memory_order_acquire)); + } + + while(!fd.done.load(eastl::memory_order_acquire)); + } +} diff --git a/common/source/klibc/CMakeLists.txt b/common/source/klibc/CMakeLists.txt new file mode 100644 index 000000000..105e4058e --- /dev/null +++ b/common/source/klibc/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(kernel_libc) + +target_include_directories(kernel_libc PUBLIC + ../../include/klibc +) + + +file(GLOB source_files CONFIGURE_DEPENDS ${SOURCE_WILDCARDS}) + +target_sources(kernel_libc PRIVATE + ${source_files}) + +target_compile_definitions(kernel_libc PUBLIC + __STDC_LIMIT_MACROS=1 + __STDC_CONSTANT_MACROS=1 +) diff --git a/common/source/klibc/ctype.cpp b/common/source/klibc/ctype.cpp new file mode 100644 index 000000000..6df8bdced --- /dev/null +++ b/common/source/klibc/ctype.cpp @@ -0,0 +1,33 @@ +/* + * linux/lib/ctype.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include "ctype.h" + +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ diff --git a/common/source/klibc/math.cpp b/common/source/klibc/math.cpp new file mode 100644 index 000000000..aa4c57c86 --- /dev/null +++ b/common/source/klibc/math.cpp @@ -0,0 +1,59 @@ +#include "math.h" +#include "inttypes.h" + +// float ceilf(float x) +// { +// union {float f; uint32_t i;} u = {x}; +// int e = (int)(u.i >> 23 & 0xff) - 0x7f; +// uint32_t m; + +// if (e >= 23) +// return x; +// if (e >= 0) { +// m = 0x007fffff >> e; +// if ((u.i & m) == 0) +// return x; +// FORCE_EVAL(x + 0x1p120f); +// if (u.i >> 31 == 0) +// u.i += m; +// u.i &= ~m; +// } else { +// FORCE_EVAL(x + 0x1p120f); +// if (u.i >> 31) +// u.f = -0.0; +// else if (u.i << 1) +// u.f = 1.0; +// } +// return u.f; +// } + + +// #if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 +// #define EPS DBL_EPSILON +// #elif FLT_EVAL_METHOD==2 +// #define EPS LDBL_EPSILON +// #endif +// static const double_t toint = 1/EPS; + +// double ceil(double x) +// { +// union {double f; uint64_t i;} u = {x}; +// int e = u.i >> 52 & 0x7ff; +// double_t y; + +// if (e >= 0x3ff+52 || x == 0) +// return x; +// /* y = int(x) - x, where int(x) is an integer neighbor of x */ +// if (u.i >> 63) +// y = x - toint + toint - x; +// else +// y = x + toint - toint - x; +// /* special case because of non-nearest rounding modes */ +// if (e <= 0x3ff-1) { +// FORCE_EVAL(y); +// return u.i >> 63 ? -0.0 : 1; +// } +// if (y < 0) +// return x + y + 1; +// return x + y; +// } diff --git a/common/source/klibc/string.cpp b/common/source/klibc/string.cpp new file mode 100644 index 000000000..b1d2ab8de --- /dev/null +++ b/common/source/klibc/string.cpp @@ -0,0 +1,365 @@ +#include "string.h" +#include "stdint.h" +#include "assert.h" + +extern "C" bool currentThreadIsStackCanaryOK(); + +extern "C" char *strcpy(char *dest, const char* src) +{ + assert(!"don't use strcpy"); + + char *start = dest; + + for (; (*dest = *src); ++src, ++dest) + ; + + return start; +} + +extern "C" char *strncpy(char *dest, const char* src, size_t size) +{ + char *start = dest; + int8_t fill = 0; + + while (size--) + { + if (fill) + { + *dest = 0; + } + else if ((*dest = *src) == 0) + { + fill = 1; + } + + src++; + dest++; + } + + assert(currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); + + return start; +} + +extern "C" char *strcat(char *dest, const char*append) +{ + char *start = dest + strlen(dest); + strcpy(start, append); + return dest; +} + +extern "C" char *strncat(char *dest, const char*append, size_t size) +{ + char* save = dest; + + if (size == 0) + { + return save; + } + + while (*dest) + { + ++dest; + } + + while (size--) + { + if ((*dest = *append++) == '\0') + { + break; + } + ++dest; + } + + *dest = '\0'; + return save; +} + +extern "C" size_t strlen(const char *str) +{ + const char *pos = str; + + while (*pos) + { + ++pos; + } + + return (pos - str); +} + +extern "C" int strcmp(const char *str1, const char *str2) +{ + assert(str1); + assert(str2); + + if (str1 == str2) + { + return 0; + } + + while ((*str1) && (*str2)) + { + if (*str1 != *str2) + { + break; + } + ++str1; + ++str2; + } + + return (*(uint8_t *) str1 - *(uint8_t *) str2); +} + +extern "C" int strncmp(const char *str1, const char *str2, size_t n) +{ + while (n && (*str1) && (*str2)) + { + if (*str1 != *str2) + { + break; + } + ++str1; + ++str2; + --n; + } + if (n == 0) + return 0; + else + return (*(uint8_t *) str1 - *(uint8_t *) str2); +} + +extern "C" char *strchr(const char* str, char c) +{ + do + { + if (*str == c) + { + return (char *) str; + } + } while (*++str); + + return (char *) nullptr; +} + +extern "C" char *strrchr(const char* str, char c) +{ + size_t len = strlen(str); + const char *pos = str + len; // goes to '\0' + + do + { + if (*--pos == c) + { + return (char *) pos; + } + } while (--len); + + return (char *) nullptr; +} + +extern "C" char* strtok(char* str, const char* delimiters) +{ + static char* str_to_tok = nullptr; + if (str != nullptr) + str_to_tok = str; + + // no delimiters, so just return the rest-string + if (delimiters == nullptr) + return str_to_tok; + + if (str_to_tok == nullptr) + return nullptr; + + // determine token start and end + size_t tok_start = 0; + size_t tok_end = -1; + + // find first char which is not one of the delimiters + size_t str_pos = 0; + for (str_pos = 0; str_to_tok[str_pos] != '\0'; str_pos++) + { + uint8_t char_is_delimiter = 0; + + size_t del_pos = 0; + for (del_pos = 0; delimiters[del_pos] != '\0'; del_pos++) + { + if (str_to_tok[str_pos] == delimiters[del_pos]) + { + char_is_delimiter = 1; + break; + } + } + + if (char_is_delimiter == 0) + { + // this is the start char of the token + tok_start = str_pos; + break; + } + } + + // find next delimiter in the string + for (str_pos = tok_start; str_to_tok[str_pos] != '\0'; str_pos++) + { + size_t del_pos = 0; + for (; delimiters[del_pos] != '\0'; del_pos++) + { + if (str_to_tok[str_pos] == delimiters[del_pos]) + { + // delimiter found! + tok_end = str_pos; + break; + } + } + + if (tok_end != -1U) + break; + } + + // create and return token: + char* token = str_to_tok + tok_start; + + // update string + if (tok_end == -1U) + { + // finished, no next token + str_to_tok = nullptr; + } + else + { + str_to_tok[tok_end] = '\0'; + str_to_tok += tok_end + 1; + } + + return token; +} + +extern "C" void *memchr(const void *block, char c, size_t size) +{ + const char *b = (const char*) block; + + while (size--) + { + if (*b == c) + { + return (void *) b; + } + ++b; + } + + return (void *) nullptr; +} + +extern "C" int memcmp(const void *region1, const void *region2, size_t size) +{ + const char* b1 = (const char*)region1; + const char* b2 = (const char*)region2; + + if (size == 0) + { + return 0; + } + + while (size--) + { + if (*b1++ != *b2++) + { + return (*--b1 - *--b2); + } + } + + return 0; +} + +extern "C" void *memset(void *block, char c, size_t size) +{ + if (size) + { + size_t i; + size_t* d = (size_t*) block; + size_t large_c = c; + for (i = 0; i < sizeof(size_t); i++) + { + large_c = (large_c << 8) | c; + } + size_t num_large_copies = size / sizeof(size_t); + size_t num_rest_copies = size % sizeof(size_t); + for (i = 0; i < num_large_copies; ++i) + { + *d++ = large_c; + } + uint8_t* d8 = (uint8_t*) d; + for (i = 0; i < num_rest_copies; ++i) + { + *d8++ = c; + } + } + + // memset called in early boot -> crashes + #if !CMAKE_ARM_RPI2 + assert(currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); + #endif + + return block; +} + +extern int system_state; + +extern "C" void *memcpy(void *dest, const void *src, size_t length) +{ + size_t* s = (size_t*) src; + size_t* d = (size_t*) dest; + size_t num_large_copies = length / sizeof(size_t); + size_t num_rest_copies = length % sizeof(size_t); + for (size_t i = 0; i < num_large_copies; ++i) + { + *d++ = *s++; + } + uint8_t* s8 = (uint8_t*) s; + uint8_t* d8 = (uint8_t*) d; + for (size_t i = 0; i < num_rest_copies; ++i) + { + *d8++ = *s8++; + } + + // No assert if we're already in a kernel panic -> infinite recursion + stack overflow + if (system_state != 2) + assert(currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); + + return dest; +} + +extern "C" void *memmove(void *dest, const void *src, size_t length) +{ + uint8_t* dest8 = (uint8_t*) dest; + const uint8_t* src8 = (const uint8_t*) src; + + if (length == 0 || src == dest) + { + return dest; + } + + if (src > dest) + { + // if src is _not_ before dest we can do a forward copy + while (length--) + { + *dest8++ = *src8++; + } + } + else + { + // if src is before dest we have to do a backward copy + src8 += length - 1; + dest8 += length - 1; + + while (length--) + { + *dest8-- = *src8--; + } + } + + assert(currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); + + return dest; +} diff --git a/common/source/ustl/ustringformat.cpp b/common/source/klibc/vsnprintf.cpp similarity index 89% rename from common/source/ustl/ustringformat.cpp rename to common/source/klibc/vsnprintf.cpp index 83e57ac7a..7a7617765 100644 --- a/common/source/ustl/ustringformat.cpp +++ b/common/source/klibc/vsnprintf.cpp @@ -1,11 +1,17 @@ -#include "ustringformat.h" +#include "vsnprintf.h" + +#include "stdint.h" +#include "string.h" + +#define Max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) /* Blatantly stolen from FREEBSD. */ -typedef uint64 uintmax_t; -typedef int64 intmax_t; -typedef uint64 u_quad_t; -typedef int64 quad_t; +using u_quad_t = uint64_t; +using quad_t = int64_t; char* ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) { @@ -20,7 +26,7 @@ char* ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) return (p); } -int kvprintf(char const *fmt, void (*func)(int,void*), void *arg, int radix, va_list ap) +int kvprintf(const char* fmt, void (*func)(int, void*), void* arg, int radix, va_list ap) { #define PCHAR(c) {int cc=(c); if (func) (*func)(cc,arg); else *d++ = cc; retval++; } char nbuf[MAXNBUF]; @@ -39,9 +45,9 @@ int kvprintf(char const *fmt, void (*func)(int,void*), void *arg, int radix, va_ if (!func) d = (char *) arg; else - d = NULL; + d = nullptr; - if (fmt == NULL) + if (fmt == nullptr) fmt = "(fmt null)\n"; if (radix < 2 || radix > 36) @@ -136,7 +142,7 @@ int kvprintf(char const *fmt, void (*func)(int,void*), void *arg, int radix, va_ case 'b': num = (u_int) va_arg(ap, int); p = va_arg(ap, char *); - for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) + for (q = ksprintn(nbuf, num, *p++, nullptr, 0); *q;) PCHAR(*q--) ; @@ -241,7 +247,7 @@ int kvprintf(char const *fmt, void (*func)(int,void*), void *arg, int radix, va_ goto handle_nosign; case 's': p = va_arg(ap, char *); - if (p == NULL) + if (p == nullptr) p = "(null)"; if (!dot) n = strlen(p); @@ -283,7 +289,8 @@ int kvprintf(char const *fmt, void (*func)(int,void*), void *arg, int radix, va_ case 'z': zflag = 1; goto reswitch; - handle_nosign: sign = 0; + handle_nosign: + sign = 0; if (jflag) num = va_arg(ap, uintmax_t); else if (qflag) @@ -301,7 +308,8 @@ int kvprintf(char const *fmt, void (*func)(int,void*), void *arg, int radix, va_ else num = va_arg(ap, u_int); goto number; - handle_sign: if (jflag) + handle_sign: + if (jflag) num = va_arg(ap, intmax_t); else if (qflag) num = va_arg(ap, quad_t); @@ -402,7 +410,7 @@ void snprintf_func(int ch, void *arg) /* * Scaled down version of vsnprintf(3). */ -int vsnprintf(char *str, size_t size, const char *format, va_list ap) +extern "C" int vsnprintf(char *str, size_t size, const char *format, va_list ap) { snprintf_arg info; int retval; @@ -414,3 +422,16 @@ int vsnprintf(char *str, size_t size, const char *format, va_list ap) *info.str++ = '\0'; return retval; } + +extern int +Vsnprintf8(char* pDestination, size_t n, const char* pFormat, va_list arguments) + __attribute__((alias("vsnprintf"))); + +int snprintf(char* buf, size_t bufsz, const char* format, ...) +{ + va_list args; + va_start(args, format); + int ret = vsnprintf(buf, bufsz, format, args); + va_end(args); + return ret; +} diff --git a/common/source/mm/BootstrapRangeAllocator.cpp b/common/source/mm/BootstrapRangeAllocator.cpp new file mode 100644 index 000000000..cfd568d6c --- /dev/null +++ b/common/source/mm/BootstrapRangeAllocator.cpp @@ -0,0 +1,9 @@ +#include "BootstrapRangeAllocator.h" + +BootstrapRangeAllocator::BootstrapRangeAllocator() +{ + iset_.get_allocator().init(iset_buffer_, + sizeof(iset_buffer_), + sizeof(decltype(iset_)::node_type), + alignof(decltype(iset_)::node_type)); +} diff --git a/common/source/mm/CMakeLists.txt b/common/source/mm/CMakeLists.txt index 4c625145f..06f9141dd 100644 --- a/common/source/mm/CMakeLists.txt +++ b/common/source/mm/CMakeLists.txt @@ -1,3 +1,4 @@ -include_directories(../../include/mm) +add_project_library(common_mm) -add_project_library(common_mm) \ No newline at end of file +target_include_directories(kernel PUBLIC + ../../include/mm) diff --git a/common/source/mm/KernelMemoryManager.cpp b/common/source/mm/KernelMemoryManager.cpp index 8e5975c9c..4e8ee062e 100644 --- a/common/source/mm/KernelMemoryManager.cpp +++ b/common/source/mm/KernelMemoryManager.cpp @@ -1,4 +1,5 @@ #include "KernelMemoryManager.h" +#include "types.h" #include "ArchCommon.h" #include "assert.h" #include "debug_bochs.h" @@ -11,45 +12,95 @@ #include "kstring.h" #include "Stabs2DebugInfo.h" #include "backtrace.h" +#include "ArchMulticore.h" +#include "offsets.h" +#include "SystemState.h" #include "Thread.h" extern Stabs2DebugInfo const* kernel_debug_info; -KernelMemoryManager kmm; +alignas(KernelMemoryManager) unsigned char kmm[sizeof(KernelMemoryManager)]; -KernelMemoryManager * KernelMemoryManager::instance_; -size_t KernelMemoryManager::pm_ready_; +KernelMemoryManager* KernelMemoryManager::instance_; + +bool KernelMemoryManager::kmm_ready_ = false; KernelMemoryManager* KernelMemoryManager::instance() { - if (unlikely(!instance_)) + assert(instance_ && "you can not use KernelMemoryManager::instance before the PageManager is ready!"); + return instance_; +} + +void KernelMemoryManager::init() +{ + assert(KernelMemoryManager::instance_ == nullptr); + size_t max_heap_pages = calcNumHeapPages(); + size_t num_reserved_heap_pages = mapKernelHeap(max_heap_pages); + KernelMemoryManager::instance_ = new (&kmm) KernelMemoryManager(num_reserved_heap_pages, max_heap_pages); +} + +size_t KernelMemoryManager::calcNumHeapPages() +{ + size_t heap_pages = PageManager::instance().getNumFreePages()/2; + if (heap_pages > 1024) + heap_pages = 1024 + (heap_pages - 1024)/8; + return heap_pages; +} + +size_t KernelMemoryManager::mapKernelHeap(size_t max_heap_pages) +{ + debug(MAIN, "Before kernel heap allocation:\n"); + PageManager::instance().printUsageInfo(); + debug(MAIN, "Num free pages: %zu\n", PageManager::instance().getNumFreePages()); + + debug(MAIN, "Mapping %zu kernel heap pages at [%p, %p)\n", max_heap_pages, (char*)ArchCommon::getFreeKernelMemoryStart(), (char*)ArchCommon::getFreeKernelMemoryStart() + max_heap_pages*PAGE_SIZE); + size_t num_reserved_heap_pages = 0; + for (size_t kheap_vpn = ArchCommon::getFreeKernelMemoryStart() / PAGE_SIZE; num_reserved_heap_pages < max_heap_pages; ++num_reserved_heap_pages, ++kheap_vpn) { - assert(false && "you can not use KernelMemoryManager::instance before the PageManager is ready!"); + ppn_t ppn_to_map = PageManager::instance().allocPPN(); + if(MAIN & OUTPUT_ADVANCED) + debug(MAIN, "Mapping kernel heap vpn %p -> ppn %p\n", (void*)kheap_vpn, (void*)ppn_to_map); + assert(ArchMemory::mapKernelPage(kheap_vpn, ppn_to_map, true)); } - return instance_; + + return num_reserved_heap_pages; } + KernelMemoryManager::KernelMemoryManager(size_t min_heap_pages, size_t max_heap_pages) : - tracing_(false), lock_("KMM::lock_"), segments_used_(0), segments_free_(0), approx_memory_free_(0) + tracing_(false), lock_("KMM::lock_"), segments_used_(0), segments_free_(0) { - assert(instance_ == 0); - instance_ = this; + debug(KMM, "Initializing KernelMemoryManager\n"); + pointer start_address = ArchCommon::getFreeKernelMemoryStart(); assert(((start_address) % PAGE_SIZE) == 0); base_break_ = start_address; kernel_break_ = start_address + min_heap_pages * PAGE_SIZE; reserved_min_ = min_heap_pages * PAGE_SIZE; reserved_max_ = max_heap_pages * PAGE_SIZE; - debug(KMM, "Clearing initial heap pages\n"); + debug(KMM, "Reserved min: %#zx pages (%zu bytes), reserved max: %#zx pages (%zu bytes)\n", min_heap_pages, reserved_min_, max_heap_pages, reserved_max_); + + debug(KMM, "Clearing initial heap pages [%p, %p)\n", (void*)start_address, (char*)start_address + min_heap_pages * PAGE_SIZE); memset((void*)start_address, 0, min_heap_pages * PAGE_SIZE); + first_ = (MallocSegment*)start_address; - new ((void*)start_address) MallocSegment(0, 0, min_heap_pages * PAGE_SIZE - sizeof(MallocSegment), false); + new (first_) MallocSegment(nullptr, nullptr, min_heap_pages * PAGE_SIZE - sizeof(MallocSegment), false); last_ = first_; + + kmm_ready_ = true; + debug(KMM, "KernelMemoryManager::ctor, Heap starts at %zx and initially ends at %zx\n", start_address, start_address + min_heap_pages * PAGE_SIZE); } +bool KernelMemoryManager::isReady() +{ + return kmm_ready_; +} + + pointer KernelMemoryManager::allocateMemory(size_t requested_size, pointer called_by) { + debug(KMM, "allocateMemory, size: %zu, called by: %zx\n", requested_size, called_by); assert((requested_size & 0x80000000) == 0 && "requested too much memory"); // 16 byte alignment @@ -57,24 +108,24 @@ pointer KernelMemoryManager::allocateMemory(size_t requested_size, pointer calle lockKMM(); pointer ptr = private_AllocateMemory(requested_size, called_by); - if (ptr) - unlockKMM(); + unlockKMM(); debug(KMM, "allocateMemory returns address: %zx \n", ptr); return ptr; } + + pointer KernelMemoryManager::private_AllocateMemory(size_t requested_size, pointer called_by) { assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - + debugAdvanced(KMM, "private_allocateMemory, size: %zu, called by: %zx\n", requested_size, called_by); assert((requested_size & 0xF) == 0 && "Attempt to allocate block with unaligned size"); // find next free pointer of neccessary size + sizeof(MallocSegment); MallocSegment *new_pointer = findFreeSegment(requested_size); - if (new_pointer == 0) + if (new_pointer == nullptr) { - unlockKMM(); kprintfd("KernelMemoryManager::allocateMemory: Not enough Memory left\n"); kprintfd("Are we having a memory leak in the kernel??\n"); kprintfd( @@ -94,19 +145,17 @@ pointer KernelMemoryManager::private_AllocateMemory(size_t requested_size, point bool KernelMemoryManager::freeMemory(pointer virtual_address, pointer called_by) { assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - - if (virtual_address == 0 || virtual_address < ((pointer) first_) || virtual_address >= kernel_break_) + if (virtual_address == 0) return false; + assert((virtual_address >= ((pointer) first_)) && (virtual_address < kernel_break_) && "Invalid free of address not in kernel heap"); + lockKMM(); MallocSegment *m_segment = getSegmentFromAddress(virtual_address); - if (not m_segment->markerOk()) - { - unlockKMM(); - return false; - } - freeSegment(m_segment); + m_segment->checkCanary(); + + freeSegment(m_segment, called_by); if ((pointer)m_segment < kernel_break_ && m_segment->markerOk()) m_segment->freed_at_ = called_by; @@ -114,10 +163,11 @@ bool KernelMemoryManager::freeMemory(pointer virtual_address, pointer called_by) return true; } + pointer KernelMemoryManager::reallocateMemory(pointer virtual_address, size_t new_size, pointer called_by) { assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - + debug(KMM, "realloc %p, new size: %zu, calledbefore(1): %zx\n", (void*)virtual_address, new_size, called_by); assert((new_size & 0x80000000) == 0 && "requested too much memory"); if (new_size == 0) { @@ -131,10 +181,13 @@ pointer KernelMemoryManager::reallocateMemory(pointer virtual_address, size_t ne // 16 byte alignment new_size = (new_size + 0xF) & ~0xF; + assert((new_size & 0xF) == 0 && "BUG: segment size must be aligned"); lockKMM(); MallocSegment *m_segment = getSegmentFromAddress(virtual_address); + m_segment->checkCanary(); + assert(m_segment->getUsed()); if (new_size == m_segment->getSize()) { @@ -151,14 +204,16 @@ pointer KernelMemoryManager::reallocateMemory(pointer virtual_address, size_t ne else { //maybe we can solve this the easy way... - if (m_segment->next_ != 0) - if (m_segment->next_->getUsed() == false && m_segment->next_->getSize() + m_segment->getSize() >= new_size) - { - mergeWithFollowingFreeSegment(m_segment); - fillSegment(m_segment, new_size, 0); - unlockKMM(); - return virtual_address; - } + if (m_segment->next_ && !m_segment->next_->getUsed() && (m_segment->getSize() + sizeof(*m_segment->next_) + m_segment->next_->getSize() >= new_size)) + { + auto s = mergeSegments(m_segment, m_segment->next_); + fillSegment(s, new_size, 0); + assert(s == m_segment); + assert(m_segment->getSize() >= new_size); + assert(s->getUsed()); + unlockKMM(); + return virtual_address; + } //or not.. lets search for larger space @@ -169,16 +224,16 @@ pointer KernelMemoryManager::reallocateMemory(pointer virtual_address, size_t ne //we are not freeing the old semgent in here, so that the data is not //getting lost, although we could not allocate more memory - //just if you wonder: the KMM is already unlocked kprintfd("KernelMemoryManager::reallocateMemory: Not enough Memory left\n"); kprintfd("Are we having a memory leak in the kernel??\n"); kprintfd( "This might as well be caused by running too many threads/processes, which partially reside in the kernel.\n"); assert(false && "Kernel Heap is out of memory"); + unlockKMM(); return 0; } memcpy((void*) new_address, (void*) virtual_address, m_segment->getSize()); - freeSegment(m_segment); + freeSegment(m_segment, called_by); if ((pointer)m_segment < kernel_break_ && m_segment->markerOk()) m_segment->freed_at_ = called_by; unlockKMM(); @@ -186,37 +241,44 @@ pointer KernelMemoryManager::reallocateMemory(pointer virtual_address, size_t ne } } -MallocSegment *KernelMemoryManager::getSegmentFromAddress(pointer virtual_address) + +MallocSegment* KernelMemoryManager::getSegmentFromAddress(pointer virtual_address) const { MallocSegment *m_segment; m_segment = (MallocSegment*) (virtual_address - sizeof(MallocSegment)); - assert(virtual_address != 0 && m_segment != 0 && "trying to access a nullpointer"); - assert(m_segment->markerOk() && "memory corruption - probably 'write after delete'"); + assert(virtual_address != 0 && m_segment != nullptr && "trying to access a nullpointer"); + m_segment->checkCanary(); return m_segment; } + MallocSegment *KernelMemoryManager::findFreeSegment(size_t requested_size) { - debug(KMM, "findFreeSegment: seeking memory block of bytes: %zd \n", requested_size + sizeof(MallocSegment)); + debugAdvanced(KMM, "findFreeSegment: seeking memory block of bytes: %zd \n", + requested_size + sizeof(MallocSegment)); MallocSegment *current = first_; - while (current != 0) + while (current) { - debug(KMM, "findFreeSegment: current: %p size: %zd used: %d \n", current, current->getSize() + sizeof(MallocSegment), - current->getUsed()); - assert(current->markerOk() && "memory corruption - probably 'write after delete'"); - if ((current->getSize() >= requested_size) && (current->getUsed() == false)) + debugAdvanced(KMM, "findFreeSegment: current: %p size: %zd used: %d \n", + current, current->getSize() + sizeof(MallocSegment), current->getUsed()); + + current->checkCanary(); + if ((current->getSize() >= requested_size) && !current->getUsed()) return current; current = current->next_; } + // No free segment found, could we allocate more memory? if(last_->getUsed()) { // In this case we have to create a new segment... - MallocSegment* new_segment = new ((void*)ksbrk(sizeof(MallocSegment) + requested_size)) MallocSegment(last_, 0, requested_size, 0); + MallocSegment* new_segment = new ((void*)ksbrk(sizeof(MallocSegment) + requested_size)) MallocSegment(last_, nullptr, requested_size, false); last_->next_ = new_segment; last_ = new_segment; + assert(new_segment->getSize() == requested_size); + assert(!new_segment->getUsed()); } else { @@ -229,27 +291,53 @@ MallocSegment *KernelMemoryManager::findFreeSegment(size_t requested_size) return last_; } + void KernelMemoryManager::fillSegment(MallocSegment *this_one, size_t requested_size, uint32 zero_check) { - assert(this_one != 0 && "trying to access a nullpointer"); - assert(this_one->markerOk() && "memory corruption - probably 'write after delete'"); - assert(this_one->getSize() >= requested_size && "segment is too small for requested size"); + if(KMM & OUTPUT_ADVANCED) + debug(KMM, "fillSegment, %p, size: %zu, zero_check: %u\n", this_one, requested_size, zero_check); + + assert(this_one && "trying to access a nullpointer"); + this_one->checkCanary(); + const size_t size = this_one->getSize(); + assert(size >= requested_size && "segment is too small for requested size"); assert((requested_size & 0xF) == 0 && "Attempt to fill segment with unaligned size"); - uint32* mem = (uint32*) (this_one + 1); + // TODO: Rest of free memory is in one big (> multiple megabytes) unused chunk at the end that will be completely checked + // and set to zero _every time_ a fresh chunk is allocated/split off from the big chunk. This is slow as hell. + size_t* mem = (size_t*) (this_one + 1); + uint8* memb = (uint8*)mem; + // sizeof(size_t) steps if (zero_check) { - for (uint32 i = 0; i < requested_size / 4; ++i) + for (size_t i = 0, steps = size / sizeof(*mem); i < steps; ++i) { if(unlikely(mem[i] != 0)) { - - kprintfd("KernelMemoryManager::fillSegment: WARNING: Memory not zero at %p (value=%x)\n", mem + i, mem[i]); + debugAlways(KMM, "KernelMemoryManager::fillSegment: WARNING: Memory not zero at %p (value=%zx)\n", mem + i, mem[i]); if(this_one->freed_at_) { if(kernel_debug_info) { - kprintfd("KernelMemoryManager::freeSegment: The chunk may previously be freed at: "); + debugAlways(KMM, "KernelMemoryManager::freeSegment: The chunk may previously be freed at: "); + kernel_debug_info->printCallInformation(this_one->freed_at_); + } + assert(false); + } + mem[i] = 0; + } + } + // handle remaining bytes + for(size_t i = size - (size % sizeof(*mem)); i < size; ++i) + { + if(unlikely(memb[i] != 0)) + { + debugAlways(KMM, "KernelMemoryManager::fillSegment: WARNING: Memory not zero at %p (value=%x)\n", memb + i, memb[i]); + if(this_one->freed_at_) + { + if(kernel_debug_info) + { + debugAlways(KMM, "KernelMemoryManager::freeSegment: The chunk may previously be freed at: "); kernel_debug_info->printCallInformation(this_one->freed_at_); } assert(false); @@ -259,13 +347,12 @@ void KernelMemoryManager::fillSegment(MallocSegment *this_one, size_t requested_ } } - size_t space_left = this_one->getSize() - requested_size; - //size stays as it is, if there would be no more space to add a new segment this_one->setUsed(true); assert(this_one->getUsed() == true && "trying to fill an unused segment"); //add a free segment after this one, if there's enough space + size_t space_left = this_one->getSize() - requested_size; if (space_left > sizeof(MallocSegment)) { this_one->setSize(requested_size); @@ -273,28 +360,36 @@ void KernelMemoryManager::fillSegment(MallocSegment *this_one, size_t requested_ assert(this_one->getUsed() == true && "trying to fill an unused segment"); MallocSegment *new_segment = - new ((void*) (((pointer) this_one) + sizeof(MallocSegment) + requested_size)) MallocSegment( - this_one, this_one->next_, space_left - sizeof(MallocSegment), false); - if (this_one->next_ != 0) + new ((void*) (((pointer)(this_one + 1)) + requested_size)) MallocSegment( + this_one, this_one->next_, space_left - sizeof(MallocSegment), false); + + assert(this_one->next_ || (this_one == last_)); + + if (this_one->next_) { - assert(this_one->next_->markerOk() && "memory corruption - probably 'write after delete'"); + this_one->next_->checkCanary(); this_one->next_->prev_ = new_segment; } - this_one->next_ = new_segment; - - if (new_segment->next_ == 0) + else + { last_ = new_segment; + } + + this_one->next_ = new_segment; + assert(new_segment->next_ || (new_segment == last_)); } + debug(KMM, "fillSegment: filled memory block of bytes: %zd \n", this_one->getSize() + sizeof(MallocSegment)); } -void KernelMemoryManager::freeSegment(MallocSegment *this_one) + +void KernelMemoryManager::freeSegment(MallocSegment *this_one, pointer called_by) { debug(KMM, "KernelMemoryManager::freeSegment(%p)\n", this_one); - assert(this_one != 0 && "trying to access a nullpointer"); - assert(this_one->markerOk() && "memory corruption - probably 'write after delete'"); + assert(this_one && "trying to access a nullpointer"); + this_one->checkCanary(); - if (this_one->getUsed() == false) + if (!this_one->getUsed()) { kprintfd("KernelMemoryManager::freeSegment: FATAL ERROR\n"); kprintfd("KernelMemoryManager::freeSegment: tried freeing not used memory block\n"); @@ -309,47 +404,16 @@ void KernelMemoryManager::freeSegment(MallocSegment *this_one) assert(false); } - debug(KMM, "fillSegment: freeing block: %p of bytes: %zd \n", this_one, this_one->getSize() + sizeof(MallocSegment)); + debug(KMM, "freeSegment: freeing block: %p of bytes: %zd \n", + this_one, this_one->getSize() + sizeof(MallocSegment)); this_one->setUsed(false); + this_one->freed_at_ = called_by; - if (this_one->prev_ != 0) - { - assert(this_one->prev_->markerOk() && "memory corruption - probably 'write after delete'"); - if (this_one->prev_->getUsed() == false) - { - size_t my_true_size = ( - (this_one->next_ == 0) ? kernel_break_ - ((pointer) this_one) : - ((pointer) this_one->next_) - ((pointer) this_one)); - - MallocSegment *previous_one = this_one->prev_; - - previous_one->setSize(my_true_size + previous_one->getSize()); - previous_one->next_ = this_one->next_; - if (this_one->next_ != 0) - { - assert(this_one->next_->markerOk() && "memory corruption - probably 'write after delete'"); - this_one->next_->prev_ = previous_one; - } - else - { - assert(this_one == last_ && "this should never happen, there must be a bug in KMM"); - last_ = previous_one; - } - - debug(KMM, "freeSegment: post premerge, pre postmerge\n"); - debug(KMM, "freeSegment: previous_one: %p size: %zd used: %d\n", previous_one, - previous_one->getSize() + sizeof(MallocSegment), previous_one->getUsed()); - debug(KMM, "freeSegment: this_one: %p size: %zd used: %d\n", this_one, this_one->getSize() + sizeof(MallocSegment), - this_one->getUsed()); + this_one = mergeSegments(this_one, this_one->prev_); + this_one = mergeSegments(this_one, this_one->next_); - this_one = previous_one; - } - } - - mergeWithFollowingFreeSegment(this_one); - - memset((void*) ((size_t) this_one + sizeof(MallocSegment)), 0, this_one->getSize()); // ease debugging + memset((void*)(this_one + 1), 0, this_one->getSize()); // ease debugging // Change break if this is the last segment if(this_one == last_) @@ -363,8 +427,9 @@ void KernelMemoryManager::freeSegment(MallocSegment *this_one) if((size_t)this_one > base_break_ + reserved_min_) { // Case 1 - assert(this_one && this_one->prev_ && this_one->prev_->markerOk() && "memory corruption - probably 'write after delete'"); - this_one->prev_->next_ = 0; + assert(this_one && this_one->prev_); + this_one->prev_->checkCanary(); + this_one->prev_->next_ = nullptr; last_ = this_one->prev_; ksbrk(-(this_one->getSize() + sizeof(MallocSegment))); } @@ -396,56 +461,26 @@ void KernelMemoryManager::freeSegment(MallocSegment *this_one) { MallocSegment *current = first_; - while (current != 0) + while (current) { - debug(KMM, "freeSegment: current: %p prev: %p next: %p size: %zd used: %d\n", current, current->prev_, + if(KMM & OUTPUT_ADVANCED) + debug(KMM, "freeSegment: current: %p prev: %p next: %p size: %zd used: %d\n", current, current->prev_, current->next_, current->getSize() + sizeof(MallocSegment), current->getUsed()); - assert(current->markerOk() && "memory corruption - probably 'write after delete'"); + current->checkCanary(); current = current->next_; } - } } -bool KernelMemoryManager::mergeWithFollowingFreeSegment(MallocSegment *this_one) +pointer KernelMemoryManager::ksbrk(ssize_t size) { - assert(this_one != 0 && "trying to access a nullpointer"); - assert(this_one->markerOk() && "memory corruption - probably 'write after delete'"); + assert(base_break_ <= (size_t)kernel_break_ + size && "kernel heap break value corrupted"); - if (this_one->next_ != 0) + if (!(((kernel_break_ - base_break_) + size) <= reserved_max_)) { - assert(this_one->next_->markerOk() && "memory corruption - probably 'write after delete'"); - if (this_one->next_->getUsed() == false) - { - MallocSegment *next_one = this_one->next_; - size_t true_next_size = ( - (next_one->next_ == 0) ? kernel_break_ - ((pointer) next_one) : - ((pointer) next_one->next_) - ((pointer) next_one)); - - this_one->setSize(this_one->getSize() + true_next_size); - this_one->next_ = next_one->next_; - if (next_one->next_ != 0) - { - assert(next_one->next_->markerOk() && "memory corruption - probably 'write after delete'"); - next_one->next_->prev_ = this_one; - } - - memset((void*) next_one, 0, sizeof(MallocSegment)); - - //have to check again, could have changed... - if (this_one->next_ == 0) - last_ = this_one; - - return true; - } + debugAlways(KMM, "Used kernel memory: %zu\n", getUsedKernelMemory(true)); + assert(false && "maximum kernel heap size reached"); } - return false; -} - -pointer KernelMemoryManager::ksbrk(ssize_t size) -{ - assert(base_break_ <= (size_t)kernel_break_ + size && "kernel heap break value corrupted"); - assert((reserved_max_ == 0 || ((kernel_break_ - base_break_) + size) <= reserved_max_) && "maximum kernel heap size reached"); assert(DYNAMIC_KMM && "ksbrk should only be called if DYNAMIC_KMM is 1 - not in baseline SWEB"); if(size != 0) { @@ -464,17 +499,15 @@ pointer KernelMemoryManager::ksbrk(ssize_t size) { debug(KMM, "%zx != %zx\n", cur_top_vpn, new_top_vpn); cur_top_vpn++; - assert(pm_ready_ && "Kernel Heap should not be used before PageManager is ready"); - size_t new_page = PageManager::instance()->allocPPN(); + size_t new_page = PageManager::instance().allocPPN(); if(unlikely(new_page == 0)) { - kprintfd("KernelMemoryManager::freeSegment: FATAL ERROR\n"); - kprintfd("KernelMemoryManager::freeSegment: no more physical memory\n"); + debug(KMM, "KernelMemoryManager::freeSegment: FATAL ERROR\n"); + debug(KMM, "KernelMemoryManager::freeSegment: no more physical memory\n"); assert(new_page != 0 && "Kernel Heap is out of memory"); } debug(KMM, "kbsrk: map %zx -> %zx\n", cur_top_vpn, new_page); - memset((void*)ArchMemory::getIdentAddressOfPPN(new_page), 0 , PAGE_SIZE); - ArchMemory::mapKernelPage(cur_top_vpn, new_page); + assert(ArchMemory::mapKernelPage(cur_top_vpn, new_page)); } } @@ -482,7 +515,6 @@ pointer KernelMemoryManager::ksbrk(ssize_t size) { while(cur_top_vpn != new_top_vpn) { - assert(pm_ready_ && "Kernel Heap should not be used before PageManager is ready"); ArchMemory::unmapKernelPage(cur_top_vpn); cur_top_vpn--; } @@ -495,33 +527,45 @@ pointer KernelMemoryManager::ksbrk(ssize_t size) } } -Thread* KernelMemoryManager::KMMLockHeldBy() + +Thread* KernelMemoryManager::KMMLockHeldBy() const { return lock_.heldBy(); } + void KernelMemoryManager::lockKMM() { - assert((!(system_state == RUNNING) || PageManager::instance()->heldBy() != currentThread) && "You're abusing the PageManager lock"); + if(system_state == RUNNING) + { + debug(KMM, "CPU %zx lock KMM\n", SMP::currentCpuId()); + } + assert(((system_state != RUNNING) || ArchInterrupts::testIFSet()) && "Cannot allocate heap memory/lock KMM while interrupts are disabled"); lock_.acquire(getCalledBefore(1)); } + void KernelMemoryManager::unlockKMM() { - assert((!(system_state == RUNNING) || PageManager::instance()->heldBy() != currentThread) && "You're abusing the PageManager lock"); + if(system_state == RUNNING) + { + debug(KMM, "CPU %zx unlock KMM\n", SMP::currentCpuId()); + } lock_.release(getCalledBefore(1)); } -SpinLock& KernelMemoryManager::getKMMLock() + +Mutex& KernelMemoryManager::getKMMLock() { return lock_; } + size_t KernelMemoryManager::getUsedKernelMemory(bool show_allocs = false) { MallocSegment *current = first_; size_t size = 0, blocks = 0, unused = 0; if(show_allocs) kprintfd("Kernel Memory Usage\n\n"); - while (current != 0) + while (current) { if (current->getUsed()) { size += current->getSize(); @@ -544,14 +588,94 @@ size_t KernelMemoryManager::getUsedKernelMemory(bool show_allocs = false) { return size; } + void KernelMemoryManager::startTracing() { tracing_ = true; } + void KernelMemoryManager::stopTracing() { tracing_ = false; } + +bool MallocSegment::checkCanary() +{ + if (!markerOk()) + { + kprintfd("Memory corruption in KMM segment %p, size: %zx, marker: %" PRIx64 " at %p, alloc at: %zx, freed at: %zx\n", + this, getSize(), marker_, &marker_, alloc_at_, freed_at_); + if(freed_at_) + { + if(kernel_debug_info) + { + kprintfd("The segment may have previously been freed at: "); + kernel_debug_info->printCallInformation(freed_at_); + } + } + assert(false && "memory corruption - probably 'write after delete'"); + } + return true; +} + + + +MallocSegment* KernelMemoryManager::mergeSegments(MallocSegment* s1, MallocSegment* s2) +{ + assert(s1); + s1->checkCanary(); + if(s2) s2->checkCanary(); + + // Only merge if the segment we want to merge with is not in use + if(!s2 || (s1 == s2) || s2->getUsed()) + return s1; + + if(s2 < s1) + { + MallocSegment* tmp = s1; + s1 = s2; + s2 = tmp; + } + + // Can merge a used segment with an unused one following it but not the other way around + assert(!s2->getUsed()); + + size_t s2_true_size = (s2->next_ ? (pointer)s2->next_ : kernel_break_) - (pointer)s2; + debug(KMM, "mergeSegments %p [%zu] used: %u + %p [%zu] used: %u => %p [%zu] used %u\n", + s1, s1->getSize() + sizeof(*s1), s1->getUsed(), s2, s2->getSize() + sizeof(*s2), s2->getUsed(), + s1, sizeof(*s1) + s1->getSize() + s2_true_size, s1->getUsed()); + + + assert(s1->next_ == s2); + assert(((pointer)(s1+1) + s1->getSize()) == (pointer)s2); + + + s1->setSize(s1->getSize() + s2_true_size); + s1->next_ = s2->next_; + if(s2->next_) + { + s2->next_->checkCanary(); + s2->next_->prev_ = s1; + } + else + { + assert(s2 == last_ && "this should never happen, there must be a bug in KMM"); + last_ = s1; + } + + memset(s2, 0, sizeof(*s2)); + + return s1; +} + pointer KernelMemoryManager::getKernelBreak() const { return kernel_break_; } + +pointer KernelMemoryManager::getKernelHeapStart() const { + return base_break_; +} + +pointer KernelMemoryManager::getKernelHeapMaxEnd() const { + return base_break_ + reserved_max_; +} diff --git a/common/source/mm/PageFaultHandler.cpp b/common/source/mm/PageFaultHandler.cpp index 3224b055b..89e6c9e36 100644 --- a/common/source/mm/PageFaultHandler.cpp +++ b/common/source/mm/PageFaultHandler.cpp @@ -1,22 +1,29 @@ #include "PageFaultHandler.h" -#include "kprintf.h" -#include "Thread.h" -#include "ArchInterrupts.h" -#include "offsets.h" -#include "Scheduler.h" + #include "Loader.h" +#include "Scheduler.h" #include "Syscall.h" +#include "Thread.h" +#include "kprintf.h" +#include "offsets.h" + +#include "ArchCommon.h" +#include "ArchInterrupts.h" +#include "ArchMulticore.h" #include "ArchThreads.h" -extern "C" void arch_contextSwitch(); + +#include "assert.h" const size_t PageFaultHandler::null_reference_check_border_ = PAGE_SIZE; +size_t PageFaultHandler::pf_address = 0xDEADBEEF; +size_t PageFaultHandler::pf_address_counter = 0; inline bool PageFaultHandler::checkPageFaultIsValid(size_t address, bool user, bool present, bool switch_to_us) { assert((user == switch_to_us) && "Thread is in user mode even though is should not be."); - assert(!(address < USER_BREAK && currentThread->loader_ == 0) && "Thread accesses the user space, but has no loader."); - assert(!(user && currentThread->user_registers_ == 0) && "Thread is in user mode, but has no valid registers."); + assert(!(address < USER_BREAK && currentThread->loader_ == nullptr) && "Thread accesses the user space, but has no loader."); + assert(!(user && currentThread->user_registers_ == nullptr) && "Thread is in user mode, but has no valid registers."); if(address < null_reference_check_border_) { @@ -48,8 +55,8 @@ inline void PageFaultHandler::handlePageFault(size_t address, bool user, { if (PAGEFAULT & OUTPUT_ENABLED) kprintfd("\n"); - debug(PAGEFAULT, "Address: %18zx - Thread %zu: %s (%p)\n", - address, currentThread->getTID(), currentThread->getName(), currentThread); + debug(PAGEFAULT, "CPU %zu, Address: %18zx - Thread %zu: %s (%p)\n", + SMP::currentCpuId(), address, currentThread->getTID(), currentThread->getName(), currentThread); debug(PAGEFAULT, "Flags: %spresent, %s-mode, %s, %s-fetch, switch to userspace: %1d\n", present ? " " : "not ", user ? " user" : "kernel", @@ -69,23 +76,28 @@ inline void PageFaultHandler::handlePageFault(size_t address, bool user, ArchThreads::printThreadRegisters(currentThread, true); currentThread->printBacktrace(true); if (currentThread->loader_) + { Syscall::exit(9999); + } else + { currentThread->kill(); + } } debug(PAGEFAULT, "Page fault handling finished for Address: %18zx.\n", address); } -void PageFaultHandler::enterPageFault(size_t address, bool user, +void PageFaultHandler::enterPageFault(size_t address, size_t ip, bool user, bool present, bool writing, bool fetch) { + debug(PAGEFAULT, "CPU %zu, Pagefault at %zx, ip %zx, present: %u, writing: %u, user: %u, instr fetch: %u\n", SMP::currentCpuId(), address, ip, present, writing, user, fetch); assert(currentThread && "You have a pagefault, but no current thread"); //save previous state on stack of currentThread uint32 saved_switch_to_userspace = currentThread->switch_to_userspace_; currentThread->switch_to_userspace_ = 0; - currentThreadRegisters = currentThread->kernel_registers_; + currentThreadRegisters = currentThread->kernel_registers_.get(); ArchInterrupts::enableInterrupts(); handlePageFault(address, user, present, writing, fetch, saved_switch_to_userspace); @@ -93,5 +105,26 @@ void PageFaultHandler::enterPageFault(size_t address, bool user, ArchInterrupts::disableInterrupts(); currentThread->switch_to_userspace_ = saved_switch_to_userspace; if (currentThread->switch_to_userspace_) - currentThreadRegisters = currentThread->user_registers_; + { + currentThreadRegisters = currentThread->user_registers_.get(); + } +} + + +void PageFaultHandler::countPageFault(size_t address) +{ + if ((address ^ (size_t)currentThread) == pf_address) + { + pf_address_counter++; + } + else + { + pf_address = address ^ (size_t)currentThread; + pf_address_counter = 0; + } + if (pf_address_counter >= 10) + { + kprintfd("same pagefault from the same thread for 10 times in a row. most likely you have an error in your code\n"); + ArchCommon::halt(); + } } diff --git a/common/source/mm/PageManager.cpp b/common/source/mm/PageManager.cpp index 8cdbc800d..88b55a0ef 100644 --- a/common/source/mm/PageManager.cpp +++ b/common/source/mm/PageManager.cpp @@ -1,3 +1,4 @@ +#include "types.h" #include "PageManager.h" #include "new.h" #include "offsets.h" @@ -5,255 +6,215 @@ #include "ArchCommon.h" #include "ArchMemory.h" #include "kprintf.h" +#include "kstring.h" #include "Scheduler.h" #include "KernelMemoryManager.h" #include "assert.h" #include "Bitmap.h" +#include "Allocator.h" +#include "BitmapAllocator.h" +#include "BootloaderModules.h" -PageManager pm; +extern void* kernel_start_address; +extern void* kernel_end_address; PageManager* PageManager::instance_ = nullptr; +bool PageManager::pm_ready_ = false; -PageManager* PageManager::instance() +PageManager& PageManager::instance() { - if (unlikely(!instance_)) - new (&pm) PageManager(); - return instance_; + assert(instance_ && "PageManager not yet initialized"); + return *instance_; } -PageManager::PageManager() : lock_("PageManager::lock_") +void PageManager::init() { - assert(!instance_); - instance_ = this; - assert(!KernelMemoryManager::instance_); - number_of_pages_ = 0; - lowest_unreserved_page_ = 0; + assert(!instance_ && "PageManager already initialized"); + BootstrapRangeAllocator bootstrap_pm_allocator{}; - size_t num_mmaps = ArchCommon::getNumUseableMemoryRegions(); + static PageManager instance(&bootstrap_pm_allocator); + instance_ = &instance; - size_t highest_address = 0, used_pages = 0; + ArchCommon::reservePagesPreKernelInit(bootstrap_pm_allocator); - //Determine Amount of RAM - for (size_t i = 0; i < num_mmaps; ++i) - { - pointer start_address = 0, end_address = 0, type = 0; - ArchCommon::getUsableMemoryRegion(i, start_address, end_address, type); - debug(PM, "Ctor: memory region from physical %zx to %zx (%zu bytes) of type %zd\n", - start_address, end_address, end_address - start_address, type); - - if (type == 1) - highest_address = Max(highest_address, end_address & 0x7FFFFFFF); - } - - number_of_pages_ = highest_address / PAGE_SIZE; + initFreePageCanaries(bootstrap_pm_allocator); - size_t boot_bitmap_size = Min(4096 * 8 * 2, number_of_pages_); - uint8 page_usage_table[BITMAP_BYTE_COUNT(boot_bitmap_size)]; - used_pages = boot_bitmap_size; - memset(page_usage_table,0xFF,BITMAP_BYTE_COUNT(boot_bitmap_size)); + // Need to do this while bootstrap allocator is still valid/alive + KernelMemoryManager::init(); + instance_->switchToHeapBitmapAllocator(); +} - //mark as free, everything that might be useable - for (size_t i = 0; i < num_mmaps; ++i) - { - pointer start_address = 0, end_address = 0, type = 0; - ArchCommon::getUsableMemoryRegion(i, start_address, end_address, type); - if (type != 1) - continue; - size_t start_page = start_address / PAGE_SIZE; - size_t end_page = end_address / PAGE_SIZE; - debug(PM, "Ctor: usable memory region: start_page: %zx, end_page: %zx, type: %zd\n", start_page, end_page, type); - - for (size_t k = Max(start_page, lowest_unreserved_page_); k < Min(end_page, number_of_pages_); ++k) +void PageManager::initFreePageCanaries(BootstrapRangeAllocator& allocator) +{ + debug(PM, "Filling free pages with canary value\n"); + for (auto paddr : allocator.freeBlocks(PAGE_SIZE, PAGE_SIZE)) { - Bitmap::unsetBit(page_usage_table, used_pages, k); + ppn_t ppn = paddr/PAGE_SIZE; + memset((void*)ArchMemory::getIdentAddressOfPPN(ppn), 0xFF, PAGE_SIZE); } - } +} - debug(PM, "Ctor: Marking pages used by the kernel as reserved\n"); - for (size_t i = ArchMemory::RESERVED_START; i < ArchMemory::RESERVED_END; ++i) - { - size_t physical_page = 0; - size_t pte_page = 0; - size_t this_page_size = ArchMemory::get_PPN_Of_VPN_In_KernelMapping(i, &physical_page, &pte_page); - assert(this_page_size == 0 || this_page_size == PAGE_SIZE || this_page_size == PAGE_SIZE * PAGE_TABLE_ENTRIES); - if (this_page_size > 0) - { - //our bitmap only knows 4k pages for now - uint64 num_4kpages = this_page_size / PAGE_SIZE; //should be 1 on 4k pages and 1024 on 4m pages - for (uint64 p = 0; p < num_4kpages; ++p) - { - if (physical_page * num_4kpages + p < number_of_pages_) - Bitmap::setBit(page_usage_table, used_pages, physical_page * num_4kpages + p); - } - i += (num_4kpages - 1); //+0 in most cases - - if (num_4kpages == 1 && i % 1024 == 0 && pte_page < number_of_pages_) - Bitmap::setBit(page_usage_table, used_pages, pte_page); - } - } +bool PageManager::isReady() +{ + return pm_ready_; +} - debug(PM, "Ctor: Marking GRUB loaded modules as reserved\n"); - //LastbutNotLeast: Mark Modules loaded by GRUB as reserved (i.e. pseudofs, etc) - for (size_t i = 0; i < ArchCommon::getNumModules(); ++i) - { - size_t start_page = (ArchCommon::getModuleStartAddress(i) & 0x7FFFFFFF) / PAGE_SIZE; - size_t end_page = (ArchCommon::getModuleEndAddress(i) & 0x7FFFFFFF) / PAGE_SIZE; - debug(PM, "Ctor: module: start_page: %zx, end_page: %zx\n", start_page, end_page); - for (size_t k = Min(start_page, number_of_pages_); k <= Min(end_page, number_of_pages_ - 1); ++k) - { - Bitmap::setBit(page_usage_table, used_pages, k); - if (ArchMemory::get_PPN_Of_VPN_In_KernelMapping(PHYSICAL_TO_VIRTUAL_OFFSET / PAGE_SIZE + k, 0, 0) == 0) - ArchMemory::mapKernelPage(PHYSICAL_TO_VIRTUAL_OFFSET / PAGE_SIZE + k,k); - } - } - size_t num_pages_for_bitmap = (number_of_pages_ / 8) / PAGE_SIZE + 1; - assert(used_pages < number_of_pages_/2 && "No space for kernel heap!"); +enum MemType +{ + M_USEABLE = 1, + M_ACPI_INFO = 3, + M_RESERVED_PRESERVE_ON_HIBERNATION = 4, + M_BAD = 5, +}; + +const char* multiboot_memtype[] = { + "reserved", + "available RAM", + "reserved", + "ACPI information", + "reserved (preserve on hibernation)", + "bad memory", +}; + +const char* getMultibootMemTypeString(size_t type) +{ + return (type < sizeof(multiboot_memtype)/sizeof(char*) ? multiboot_memtype[type] : "reserved"); +} - HEAP_PAGES = number_of_pages_/2 - used_pages; - if (HEAP_PAGES > 1024) - HEAP_PAGES = 1024 + (HEAP_PAGES - Min(HEAP_PAGES,1024))/8; - size_t start_vpn = ArchCommon::getFreeKernelMemoryStart() / PAGE_SIZE; - size_t free_page = 0; - size_t temp_page_size = 0; - size_t num_reserved_heap_pages = 0; - for (num_reserved_heap_pages = 0; num_reserved_heap_pages < num_pages_for_bitmap || temp_page_size != 0 || - num_reserved_heap_pages < ((DYNAMIC_KMM || (number_of_pages_ < 512)) ? 0 : HEAP_PAGES); ++num_reserved_heap_pages) - { - while (!Bitmap::setBit(page_usage_table, used_pages, free_page)) - free_page++; - if ((temp_page_size = ArchMemory::get_PPN_Of_VPN_In_KernelMapping(start_vpn,0,0)) == 0) - ArchMemory::mapKernelPage(start_vpn,free_page++); - start_vpn++; - } +PageManager::PageManager(Allocator* allocator) : + allocator_(allocator), + number_of_pages_(0), + lock_("PageManager::lock_") +{ + size_t highest_address = initUsableMemoryRegions(*allocator_); - extern KernelMemoryManager kmm; - new (&kmm) KernelMemoryManager(num_reserved_heap_pages,HEAP_PAGES); - page_usage_table_ = new Bitmap(number_of_pages_); + size_t total_num_useable_pages = allocator_->numFreeBlocks(PAGE_SIZE, PAGE_SIZE); + number_of_pages_ = highest_address / PAGE_SIZE; - for (size_t i = 0; i < boot_bitmap_size; ++i) - { - if (Bitmap::getBit(page_usage_table,i)) - page_usage_table_->setBit(i); - } + debug(PM, "Bootstrap PM useable ranges:\n"); + allocator_->printUsageInfo(); + debug(PM, "Total useable pages: %zu (%zu bytes)\n", total_num_useable_pages, total_num_useable_pages*PAGE_SIZE); - debug(PM, "Ctor: find lowest unreserved page\n"); - for (size_t p = 0; p < number_of_pages_; ++p) - { - if (!page_usage_table_->getBit(p)) - { - lowest_unreserved_page_ = p; - break; - } - } - debug(PM, "Ctor: Physical pages - free: %zu used: %zu total: %u\n", page_usage_table_->getNumFreeBits(), - page_usage_table_->getNumBitsSet(), number_of_pages_); - assert(lowest_unreserved_page_ < number_of_pages_); + reserveKernelPages(*allocator_); + BootloaderModules::reserveModulePages(*allocator_); + debug(PM, "Ctor: Physical pages - free: %zu used: %zu total: %zu\n", getNumFreePages(), total_num_useable_pages - getNumFreePages(), total_num_useable_pages); - debug(PM, "Clearing free pages\n"); - for(size_t p = lowest_unreserved_page_; p < number_of_pages_; ++p) - { - if(!page_usage_table_->getBit(p)) - { - memset((void*)ArchMemory::getIdentAddressOfPPN(p), 0xFF, PAGE_SIZE); - } - } - - num_pages_for_user_ = DYNAMIC_KMM ? -1 : getNumFreePages(); - KernelMemoryManager::pm_ready_ = 1; + pm_ready_ = true; + debug(PM, "PM ctor finished\n"); } + uint32 PageManager::getTotalNumPages() const { return number_of_pages_; } -uint32 PageManager::getNumFreePages() const -{ - return page_usage_table_->getNumFreeBits(); -} -bool PageManager::reservePages(uint32 ppn, uint32 num) +size_t PageManager::getNumFreePages() const { - assert(lock_.heldBy() == currentThread); - if (ppn < number_of_pages_ && !page_usage_table_->getBit(ppn)) - { - if (num == 1 || reservePages(ppn + 1, num - 1)) - { - page_usage_table_->setBit(ppn); - return true; - } - } - return false; + return allocator_->numFreeBlocks(PAGE_SIZE, PAGE_SIZE); } + uint32 PageManager::allocPPN(uint32 page_size) { - uint32 p; - uint32 found = 0; - assert((page_size % PAGE_SIZE) == 0); + assert(allocator_); lock_.acquire(); - - for (p = lowest_unreserved_page_; !found && (p < number_of_pages_); ++p) - { - if ((p % (page_size / PAGE_SIZE)) != 0) - continue; - if (reservePages(p, page_size / PAGE_SIZE)) - found = p; - } - while ((lowest_unreserved_page_ < number_of_pages_) && page_usage_table_->getBit(lowest_unreserved_page_)) - ++lowest_unreserved_page_; - + size_t phys_addr = allocator_->alloc(page_size, page_size); lock_.release(); - if (found == 0) + if (phys_addr == (size_t)-1) { assert(false && "PageManager::allocPPN: Out of memory / No more free physical pages"); } - const char* page_ident_addr = (const char*)ArchMemory::getIdentAddressOfPPN(found); + ppn_t ppn = phys_addr/PAGE_SIZE; + + debugAdvanced(PM, "Allocated PPN %llx\n", (long long unsigned)ppn); + + char* page_ident_addr = (char*)ArchMemory::getIdentAddressOfPPN(ppn); const char* page_modified = (const char*)memnotchr(page_ident_addr, 0xFF, page_size); if(page_modified) { - debug(PM, "Detected use-after-free for PPN %x at offset %zx\n", found, page_modified - page_ident_addr); - assert(!page_modified && "Page modified after free"); + debugAlways(PM, "Detected use-after-free for PPN %llx at offset %zx\n", (long long unsigned)ppn, page_modified - page_ident_addr); + assert(!page_modified && "Page modified after free"); } - memset((void*)ArchMemory::getIdentAddressOfPPN(found), 0, page_size); - return found; + memset(page_ident_addr, 0, page_size); + return ppn; } + void PageManager::freePPN(uint32 page_number, uint32 page_size) { + debugAdvanced(PM, "Free PPN %x\n", page_number); + assert((page_size % PAGE_SIZE) == 0); + assert(allocator_); if(page_number > getTotalNumPages()) { - debug(PM, "Tried to free PPN %u (=%x)\n", page_number, page_number); + debugAlways(PM, "Tried to free nonexistent PPN %x\n", page_number); assert(false && "PPN to be freed is out of range"); } + + // Fill page with 0xFF for use-after-free detection memset((void*)ArchMemory::getIdentAddressOfPPN(page_number), 0xFF, page_size); lock_.acquire(); - if (page_number < lowest_unreserved_page_) - lowest_unreserved_page_ = page_number; - for (uint32 p = page_number; p < (page_number + page_size / PAGE_SIZE); ++p) + bool free_status = allocator_->dealloc(page_number*PAGE_SIZE, page_size); + if (!free_status) { - assert(page_usage_table_->getBit(p) && "Double free PPN"); - page_usage_table_->unsetBit(p); + debugAlways(PM, "Double free PPN %x\n", page_number); + assert(false && "Double free PPN"); } lock_.release(); } -void PageManager::printBitmap() +size_t PageManager::initUsableMemoryRegions(Allocator& allocator) +{ + size_t num_mmaps = ArchCommon::getNumUseableMemoryRegions(); + + size_t highest_address = 0; + + //Determine Amount of RAM + for (size_t i = 0; i < num_mmaps; ++i) + { + pointer start_address = 0, end_address = 0; + size_t type = 0; + ArchCommon::getUseableMemoryRegion(i, start_address, end_address, type); + assert(type <= 5); + debug(PM, "Ctor: memory region from physical %#zx to %#zx (%zu bytes) of type %zd [%s]\n", + start_address, end_address, end_address - start_address, type, getMultibootMemTypeString(type)); + + if (type == M_USEABLE) + { + highest_address = Max(highest_address, end_address); + allocator.setUseable(start_address, end_address); + } + } + + return highest_address; +} + +void PageManager::reserveKernelPages(Allocator& allocator) { - page_usage_table_->bmprint(); + debug(PM, "Marking pages used by the kernel as reserved\n"); + size_t kernel_virt_start = (size_t)&kernel_start_address; + size_t kernel_virt_end = (size_t)&kernel_end_address; + size_t kernel_phys_start = kernel_virt_start - (size_t)PHYSICAL_TO_VIRTUAL_OFFSET; + size_t kernel_phys_end = kernel_virt_end - (size_t)PHYSICAL_TO_VIRTUAL_OFFSET; + debug(PM, "Ctor: kernel phys [%#zx, %#zx) -> virt [%#zx, %#zx)\n", kernel_phys_start, kernel_phys_end, kernel_virt_start, kernel_virt_end); + + allocator.setUnuseable(kernel_phys_start, kernel_phys_end); } -uint32 PageManager::getNumPagesForUser() const +void PageManager::switchToHeapBitmapAllocator() { - return num_pages_for_user_; -} \ No newline at end of file + debug(PM, "Allocating PM bitmap with %#zx bits\n", number_of_pages_); + allocator_ = new BitmapAllocator(number_of_pages_*PAGE_SIZE, eastl::move(*allocator_)); +} diff --git a/common/source/mm/new.cpp b/common/source/mm/new.cpp index 7fb2f108d..de8ff5c58 100644 --- a/common/source/mm/new.cpp +++ b/common/source/mm/new.cpp @@ -2,45 +2,58 @@ #include "assert.h" #include "KernelMemoryManager.h" #include "backtrace.h" +#include "debug.h" +#include "stdint.h" +#include "ArchInterrupts.h" +#include "SMP.h" +#include "ArchMulticore.h" +#include "Scheduler.h" +#include "SystemState.h" + +/** + * delete (free) memory. This function is used by the wrappers. + * @param address the address of the memory to delete + */ +static void _delete(void* address) +{ + if (address) { + address = ((uint8*)address) - 0x10; + } + + pointer called_by = getCalledBefore(2); + debug(KMM, "delete %p\n", address); + assert(address > (void*)0x80000000 || address == (void*)nullptr); + KernelMemoryManager::instance()->freeMemory ( ( pointer ) address, called_by); +} /** * Allocate new memory. This function is used by the wrappers. * @param size the size of the memory to allocate * @return the pointer to the new memory */ -static void* _new(size_t size) +__attribute__((__alloc_size__ (1), __malloc__, __malloc__(_delete, 1))) +static void* _new(size_t size, pointer called_by = getCalledBefore(1)) { + debug(KMM, "new, size: %zu\n", size); // maybe we could take some precautions not to be interrupted while doing this - pointer called_by = getCalledBefore(2); void* p = ( void* ) KernelMemoryManager::instance()->allocateMemory (size + 0x10, called_by); - assert(p > (void*)0x80000000 || p == (void*)0); + assert(p > (void*)0x80000000 || p == (void*)nullptr); if (p) { *static_cast(p) = size; p = ((uint8 *)p) + 0x10; } +#if !DYNAMIC_KMM + assert(p); +#endif return p; } -/** - * delete (free) memory. This function is used by the wrappers. - * @param address the address of the memory to delete - */ -static void _delete(void* address) -{ - if (address) { - address = ((uint8*)address) - 0x10; - } - - pointer called_by = getCalledBefore(2); - assert(address > (void*)0x80000000 || address == (void*)0); - KernelMemoryManager::instance()->freeMemory ( ( pointer ) address, called_by); - return; -} /** * overloaded normal new operator * @param size the size of the memory to allocate * @return the pointer to the new memory */ +__attribute__((__alloc_size__ (1), __malloc__, __malloc__(_delete, 1))) void* operator new ( size_t size ) { return _new(size); @@ -59,6 +72,10 @@ void operator delete(void* address, size_t size) { if (address) { auto new_size = *(size_t *)(((uint8 *)address) - 0x10); + if (new_size != size) + { + debugAlways(KMM, "Invalid delete call. delete and new size do not match. Expected size: %zu, delete size: %zu\n", new_size, size); + } assert(new_size == size && "Invalid delete call. delete and new size do not match."); } _delete(address); @@ -69,6 +86,7 @@ void operator delete(void* address, size_t size) * @param size the size of the array to allocate * @return the pointer to the new memory */ +__attribute__((__alloc_size__ (1), __malloc__, __malloc__(_delete, 1))) void* operator new[] ( size_t size ) { return _new(size); @@ -87,20 +105,25 @@ void operator delete[](void* address, size_t size) { if (address) { auto new_size = *(size_t *)(((uint8 *)address) - 0x10); + if (new_size != size) + { + debugAlways(KMM, "Invalid delete call. delete and new size do not match. Expected size: %zu, delete size: %zu\n", new_size, size); + } assert(new_size == size && "Invalid delete call. delete and new size do not match."); } _delete(address); } extern "C" void __cxa_pure_virtual(); -extern "C" void _pure_virtual ( void ); -extern "C" void __pure_virtual ( void ); -extern "C" uint32 atexit ( void ( *func ) ( void ) ); +extern "C" void _pure_virtual(); +extern "C" void __pure_virtual(); +extern "C" uint32 atexit ( void ( *func )() ); extern "C" uint32 __cxa_atexit(); -extern "C" void* __dso_handle; +extern void* __dso_handle; void __cxa_pure_virtual() { + assert(false); } void _pure_virtual() @@ -111,10 +134,41 @@ void __pure_virtual() { } -uint32 atexit ( void ( * ) ( void ) ) {return ( uint32 )-1;} +uint32 atexit ( void ( * )() ) {return ( uint32 )-1;} uint32 __cxa_atexit() {return ( uint32 )-1;} #ifndef GCC29 void* __dso_handle = ( void* ) &__dso_handle; #endif + +void checkKMMDeadlock(const char* pName = nullptr, const char* file = nullptr, int line = 0) +{ + if (SMP::numRunningCpus() == 1 && unlikely (ArchInterrupts::testIFSet() == false || Scheduler::instance()->isSchedulingEnabled() == false)) + { + if (unlikely (KernelMemoryManager::instance()->KMMLockHeldBy() != nullptr)) + { + system_state = KPANIC; + kprintfd("(ERROR) checkKMMDeadlock: Using a non resize-safe container method with IF=%d and SchedulingEnabled=%d ! This will fail!!!\n" + " container: %s, called at: %s, line: %d\n", + ArchInterrupts::testIFSet(), Scheduler::instance()->isSchedulingEnabled(), pName ? pName : "(nil)", file ? file : "(nil)", line); + currentThread->printBacktrace(true); + assert(false); + } + } +} + +// Required for EASTL +void* operator new[](size_t size, [[maybe_unused]] const char* pName, [[maybe_unused]] int flags, [[maybe_unused]] unsigned debugFlags, [[maybe_unused]] const char* file, [[maybe_unused]] int line) +{ + checkKMMDeadlock(pName, file, line); + // Number of call frames to go up the stack to get to the actually location where allocation happened in user code varies depending on container, etc... 6 is enough to get the actual call site for vector::emplace_back() and is close enough for other containers to get useful function information instead of just allocator::allocate + return _new(size, getCalledBefore(6)); +} + +void* operator new[](size_t size, [[maybe_unused]] size_t alignment, [[maybe_unused]] size_t alignmentOffset, [[maybe_unused]] const char* pName, [[maybe_unused]] int flags, [[maybe_unused]] unsigned debugFlags, [[maybe_unused]] const char* file, [[maybe_unused]] int line) +{ + // TODO: respect alignment + checkKMMDeadlock(pName, file, line); + return _new(size, getCalledBefore(6)); +} diff --git a/common/source/ustl/CMakeLists.txt b/common/source/ustl/CMakeLists.txt deleted file mode 100644 index 73ad63719..000000000 --- a/common/source/ustl/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -include_directories(../../include/ustl) - -add_project_library(common_ustl) diff --git a/common/source/ustl/LICENSE b/common/source/ustl/LICENSE deleted file mode 100644 index ad459b2bd..000000000 --- a/common/source/ustl/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -License for the USTL, forked from https://github.com/msharov/ustl - - - The MIT License - -Copyright (c) 2005 by Mike Sharov - -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. diff --git a/common/source/ustl/cmemlink.cpp b/common/source/ustl/cmemlink.cpp deleted file mode 100644 index 91dd8a3ba..000000000 --- a/common/source/ustl/cmemlink.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#include "cmemlink.h" -//#include "ofstream.h" -#include "strmsize.h" -#include "ualgo.h" -#include "panic.h" - -namespace ustl { - -/// \brief Attaches the object to pointer \p p of size \p n. -/// -/// If \p p is nullptr and \p n is non-zero, bad_alloc is thrown and current -/// state remains unchanged. -/// -void cmemlink::link (const void* p, size_type n) -{ - if (!p && n) - kpanict("bad_alloc"); - //throw bad_alloc (n); - unlink(); - relink (p, n); -} - -/// Writes the object to stream \p os -/*void cmemlink::write (ostream& os) const -{ - const written_size_type sz (size()); - assert (sz == size() && "No support for writing memblocks larger than 4G"); - os << sz; - os.write (cdata(), sz); - os.align (stream_align_of (sz)); -} - -/// Writes the object to stream \p os -void cmemlink::text_write (ostringstream& os) const -{ - os.write (begin(), readable_size()); -}*/ - -/// Returns the number of bytes required to write this object to a stream. -cmemlink::size_type cmemlink::stream_size (void) const noexcept -{ - const written_size_type sz (size()); - return Align (stream_size_of (sz) + sz, stream_align_of(sz)); -} - -/// Writes the data to file \p "filename". -/*void cmemlink::write_file (const char* filename, int mode) const -{ - fstream f; - f.exceptions (fstream::allbadbits); - f.open (filename, fstream::out | fstream::trunc, mode); - f.write (cdata(), readable_size()); - f.close(); -}*/ - -/// Compares to memory block pointed by l. Size is compared first. -bool cmemlink::operator== (const cmemlink& l) const noexcept -{ - return l._size == _size && - (l._data == _data || 0 == memcmp (l._data, _data, _size)); -} - -} // namespace ustl diff --git a/common/source/ustl/debugchecks.cpp b/common/source/ustl/debugchecks.cpp deleted file mode 100644 index 2fb4d3ec8..000000000 --- a/common/source/ustl/debugchecks.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "ArchInterrupts.h" -#include "Scheduler.h" -#include "kprintf.h" -#include "KernelMemoryManager.h" -#include "Thread.h" -#include "types.h" - -namespace ustl -{ - void checkKMMDeadlock() - { - if (unlikely (ArchInterrupts::testIFSet() == false || Scheduler::instance()->isSchedulingEnabled() == false)) - { - if (unlikely (KernelMemoryManager::instance()->KMMLockHeldBy() != 0)) - { - system_state = KPANIC; - kprintfd("(ERROR) checkKMMDeadlock: Using a not resize-safe ustl container method with IF=%d and SchedulingEnabled=%d ! This will fail!!!\n", - ArchInterrupts::testIFSet(), Scheduler::instance()->isSchedulingEnabled()); - currentThread->printBacktrace(true); - assert(false); - } - } - } -} diff --git a/common/source/ustl/memblock.cpp b/common/source/ustl/memblock.cpp deleted file mode 100644 index 0cc95894b..000000000 --- a/common/source/ustl/memblock.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#include "mistream.h" -#include "memblock.h" -#include "ualgo.h" -#include "umemory.h" -//#include "fstream.h" -#include "kmalloc.h" -#include "panic.h" - -namespace ustl { - -memblock::memblock (void) noexcept : memlink(), _capacity (0) { } -memblock::memblock (const void* p, size_type n) : memlink(), _capacity (0) { assign (p, n); } -memblock::memblock (size_type n) : memlink(), _capacity (0) { resize (n); } -memblock::memblock (const cmemlink& b) : memlink(), _capacity (0) { assign (b); } -memblock::memblock (const memlink& b) : memlink(), _capacity (0) { assign (b); } -memblock::memblock (const memblock& b) : memlink(), _capacity (0) { assign (b); } -memblock::~memblock (void) noexcept { deallocate(); } - -void memblock::unlink (void) noexcept -{ - _capacity = 0; - memlink::unlink(); -} - -/// resizes the block to \p newSize bytes, reallocating if necessary. -void memblock::resize (size_type newSize, bool bExact) -{ - if (_capacity < newSize + minimumFreeCapacity()) - reserve (newSize, bExact); - memlink::resize (newSize); -} - -/// Frees internal data. -void memblock::deallocate (void) noexcept -{ - if (_capacity) { - assert (cdata() && "Internal error: space allocated, but the pointer is nullptr"); - assert (data() && "Internal error: read-only block is marked as allocated space"); - kfree (data()); - } - unlink(); -} - -/// Assumes control of the memory block \p p of size \p n. -/// The block assigned using this function will be freed in the destructor. -void memblock::manage (void* p, size_type n) noexcept -{ - assert (p || !n); - assert (!_capacity && "Already managing something. deallocate or unlink first."); - link (p, n); - _capacity = n; -} - -/// "Instantiate" a linked block by allocating and copying the linked data. -void memblock::copy_link (void) -{ - reserve (size()); -} - -/// Copies data from \p p, \p n. -void memblock::assign (const void* p, size_type n) -{ - assert ((static_cast(p) != cdata() || size() == n) && "Self-assignment can not resize"); - resize (n); - copy_n (const_pointer(p), n, begin()); -} - -/// \brief Reallocates internal block to hold at least \p newSize bytes. -/// -/// Additional memory may be allocated, but for efficiency it is a very -/// good idea to call reserve before doing byte-by-byte edit operations. -/// The block size as returned by size() is not altered. reserve will not -/// reduce allocated memory. If you think you are wasting space, call -/// deallocate and start over. To avoid wasting space, use the block for -/// only one purpose, and try to get that purpose to use similar amounts -/// of memory on each iteration. -/// -void memblock::reserve (size_type newSize, bool bExact) -{ - if ((newSize += minimumFreeCapacity()) <= _capacity) - return; - extern void checkKMMDeadlock(); - checkKMMDeadlock(); - pointer oldBlock (is_linked() ? nullptr : data()); - const size_t alignedSize (NextPow2 (newSize)); - if (!bExact) - newSize = alignedSize; - pointer newBlock = static_cast (krealloc (oldBlock, newSize)); - if (!newBlock) - kpanict("bad_alloc"); - if (!oldBlock & (cdata() != nullptr)) - copy_n (cdata(), min (size() + 1, newSize), newBlock); - link (newBlock, size()); - _capacity = newSize; -} - -/// Reduces capacity to match size -void memblock::shrink_to_fit (void) -{ - if (is_linked()) - return; - pointer newBlock = static_cast (krealloc (begin(), size())); - if (!newBlock && size()) - kpanict("bad_alloc"); - _capacity = size(); - memlink::relink (newBlock, size()); -} - -/// Shifts the data in the linked block from \p start to \p start + \p n. -memblock::iterator memblock::insert (const_iterator start, size_type n) -{ - const uoff_t ip = start - begin(); - assert (ip <= size()); - resize (size() + n, false); - memlink::insert (iat(ip), n); - return iat (ip); -} - -/// Shifts the data in the linked block from \p start + \p n to \p start. -memblock::iterator memblock::erase (const_iterator start, size_type n) -{ - const uoff_t ep = start - begin(); - assert (ep + n <= size()); - reserve (size()); // copy-on-write - iterator iep = iat(ep); - memlink::erase (iep, n); - memlink::resize (size() - n); - return iep; -} - -/// Reads the object from stream \p s -/*void memblock::read (istream& is) -{ - written_size_type n = 0; - is >> n; - if (!is.verify_remaining ("read", "ustl::memblock", n)) - return; - resize (n); - is.read (data(), writable_size()); - is.align (stream_align_of (n)); -} - -/// Reads the entire file \p "filename". -void memblock::read_file (const char* filename) -{ - fstream f; - f.exceptions (fstream::allbadbits); - f.open (filename, fstream::in); - const off_t fsize (f.size()); - reserve (fsize); - f.read (data(), fsize); - f.close(); - resize (fsize); -}*/ - -memblock::size_type memblock::minimumFreeCapacity (void) const noexcept { return 0; } - -} // namespace ustl diff --git a/common/source/ustl/memlink.cpp b/common/source/ustl/memlink.cpp deleted file mode 100644 index e427b60c9..000000000 --- a/common/source/ustl/memlink.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#include "memlink.h" -//#include "mistream.h" -//#include "ustdxept.h" - -namespace ustl { - -/// Reads the object from stream \p s -/*void memlink::read (istream& is) -{ - written_size_type n = 0; - is >> n; - if (!is.verify_remaining ("read", "ustl::memlink", n)) - return; - if (n > size()) - throw length_error ("memlink can not increase the size of the linked storage for reading"); - resize (n); - is.read (data(), n); - is.align (stream_align_of (n)); -}*/ - -/// Fills the linked block with the given pattern. -/// \arg start Offset at which to start filling the linked block -/// \arg p Pointer to the pattern. -/// \arg elSize Size of the pattern. -/// \arg elCount Number of times to write the pattern. -/// Total number of bytes written is \p elSize * \p elCount. -/// -void memlink::fill (const_iterator cstart, const void* p, size_type elSize, size_type elCount) noexcept -{ - assert (data() || !elCount || !elSize); - assert (cstart >= begin() && cstart + elSize * elCount <= end()); - iterator start = const_cast(cstart); - if (elSize == 1) - fill_n (start, elCount, *reinterpret_cast(p)); - else while (elCount--) - start = copy_n (const_iterator(p), elSize, start); -} - -} // namespace ustl diff --git a/common/source/ustl/mistream.cpp b/common/source/ustl/mistream.cpp deleted file mode 100644 index 700a3361d..000000000 --- a/common/source/ustl/mistream.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#include "mistream.h" -#include "sostream.h" -//#include "ustdxept.h" -#include "ustring.h" -#include "ualgo.h" -#include "kprintf.h" -#include "panic.h" - -namespace ustl { - -//-------------------------------------------------------------------- - -/// Checks that \p n bytes are available in the stream, or else throws. -void ios_base::overrun (__attribute__((unused)) const char* op, __attribute__((unused)) const char* type, __attribute__((unused)) uint32_t n, __attribute__((unused)) uint32_t pos, uint32_t rem) -{ - if (set_and_throw (rem ? failbit : (failbit | eofbit))) - /* throw stream_bounds_exception (op, type, pos, n, rem)*/{ kprintfd("ERROR stream_bounds_exception: %s:%d", __FILE__, __LINE__); kpanict("ERROR stream_bounds_exception " LOCATION); } -} - -//-------------------------------------------------------------------- - -/// Attaches to the block pointed to by source of size source.pos() -istream::istream (const ostream& source) noexcept -: cmemlink (source.begin(), source.pos()) -,_pos (0) -{ -} - -void istream::unlink (void) noexcept { cmemlink::unlink(); _pos = 0; } -void ostream::unlink (void) noexcept { memlink::unlink(); _pos = 0; } - -/// Writes all unread bytes into \p os. -void istream::write (ostream& os) const -{ - os.write (ipos(), remaining()); -} - -/// Writes the object to stream \p os. -/*void istream::text_write (ostringstream& os) const -{ - os.write (ipos(), remaining()); -}*/ - -/// Reads a null-terminated string into \p str. -void istream::read_strz (string& str) -{ - const_iterator zp = find (ipos(), end(), '\0'); - if (zp == end()) - zp = ipos(); - const size_type strl = distance (ipos(), zp); - str.assign (ipos(), strl); - _pos += strl + 1; -} - -/// Reads at most \p n bytes into \p s. -istream::size_type istream::readsome (void* s, size_type n) -{ - if (remaining() < n) - underflow (n); - const size_type ntr (min (n, remaining())); - read (s, ntr); - return ntr; -} - -streamsize istream::underflow (streamsize n) -{ - verify_remaining ("read", "byte", n); - return remaining(); -} - -//-------------------------------------------------------------------- - -/// Aligns the write pointer on \p grain. The skipped bytes are zeroed. -void ostream::align (size_type grain) -{ - assert (!((grain-1)&grain) && "grain must be a power of 2"); - iterator ip = ipos(); - iterator ipa = iterator((uintptr_t(ip) + (grain-1)) & ~(grain-1)); - size_t nb = distance (ip, ipa); -#if WANT_STREAM_BOUNDS_CHECKING - if (!verify_remaining ("align", "padding", nb)) - return; -#else - assert (remaining() >= nb && "Buffer overrun. Check your stream size calculations."); -#endif - memset (ip, '\x0', nb); - _pos += nb; -} - -/// Writes \p str as a null-terminated string. -void ostream::write_strz (const char* str) -{ - write (str, strlen(str)+1); -} - -/// Writes all available data from \p is. -void ostream::read (istream& is) -{ - write (is.ipos(), is.remaining()); - is.seek (is.size()); -} - -/// Writes all written data to \p os. -/*void ostream::text_write (ostringstream& os) const -{ - os.write (begin(), pos()); -}*/ - -/// Inserts an empty area of \p size, at \p start. -void ostream::insert (iterator start, size_type s) -{ - _pos += s; - memlink::insert (start, s); -} - -/// Erases an area of \p size, at \p start. -void ostream::erase (iterator start, size_type s) -{ - _pos -= s; - memlink::erase (start, s); -} - -//-------------------------------------------------------------------- - -} // namespace ustl diff --git a/common/source/ustl/outerrstream.cpp b/common/source/ustl/outerrstream.cpp deleted file mode 100644 index 4ed6e36bf..000000000 --- a/common/source/ustl/outerrstream.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "outerrstream.h" -#include "ustringformat.h" // for vsnprintf (in string::format) - -namespace ustl -{ - -coutclass cout_obj; -coutclass cerr_obj; -coutclass& cout = cout_obj; -coutclass& cerr = cerr_obj; - -void coutclass::init() -{ - new (&cout_obj) coutclass(&kprintf); - new (&cerr_obj) coutclass(&kprintfd); -} - - -/// Creates an output string stream linked to the given memory area. -coutclass::coutclass (void* p __attribute__((unused)), size_t n __attribute__((unused))) -: ostream (), - m_Flags (0), - m_Width (0), - m_Base (10), - m_Precision (2), - m_kprintf(&kprintf) -{ - exceptions (goodbit); -} -/// Creates an output string stream linked to the given memory area. -coutclass::coutclass (void* p __attribute__((unused)), size_t n __attribute__((unused)), void (*m_kprintf)(const char*, ...)) -: ostream (), - m_Flags (0), - m_Width (0), - m_Base (10), - m_Precision (2), - m_kprintf(m_kprintf) -{ - exceptions (goodbit); -} - -/// Creates an output string stream, initializing the buffer with v. -coutclass::coutclass () -: ostream (), - m_Flags (0), - m_Width (0), - m_Base (10), - m_Precision (2), - m_kprintf(&kprintf) -{ - exceptions (goodbit); -} - -/// Creates an output string stream, initializing the buffer with v. -coutclass::coutclass (void (*m_kprintf)(const char*, ...)) -: ostream (), - m_Flags (0), - m_Width (0), - m_Base (10), - m_Precision (2), - m_kprintf(m_kprintf) -{ - exceptions (goodbit); -} - -/// Writes a single character into the stream. -void coutclass::iwrite (uint8_t v) -{ - //debug(SCHEDULER, "writing single character: %d\n", v); - m_kprintf("%c", v); -} - -/// Writes the contents of \p buffer of \p size into the stream. -coutclass& coutclass::write (const void* buffer, size_type sz) -{ - kprintf("%.*s",(int)sz,(const char*)buffer); - return (*this); -} - -/// Simple decimal encoding of \p n into \p fmt. -inline char* coutclass::encode_dec (char* fmt, uint32_t n) const -{ - do { - *fmt++ = '0' + n % 10; - } while (n /= 10); - return (fmt); -} - -/// Generates a sprintf format string for the given type. -void coutclass::fmtstring (char* fmt, const char* typestr, bool bInteger) const -{ - *fmt++ = '%'; - if (m_Width) - fmt = encode_dec (fmt, m_Width); - if (m_Flags & left) - *fmt++ = '-'; - if (!bInteger) { - *fmt++ = '.'; - fmt = encode_dec (fmt, m_Precision); - } - while (*typestr) - *fmt++ = *typestr++; - if (bInteger) { - if (m_Base == 16) - fmt[-1] = 'X'; - else if (m_Base == 8) - fmt[-1] = 'o'; - } else { - if (m_Flags & scientific) - fmt[-1] = 'E'; - } - *fmt = 0; -} - -/// Writes \p v into the stream as utf8 -void coutclass::iwrite (wchar_t v) -{ - char buffer [8]; - *utf8out(buffer) = v; - write (buffer, Utf8Bytes(v)); -} - -/// Writes value \p v into the stream as text. -void coutclass::iwrite (bool v) -{ - static const char tf[2][8] = { "false", "true" }; - write (tf[v], 5 - v); -} - -/// Equivalent to a sprintf on the string. -int coutclass::format (const char* fmt, ...) -{ - va_list args; - va_start (args, fmt); - m_kprintf(fmt, args); - va_end (args); - return (0); -} - -/// Links to string \p l as resizable. -void coutclass::link (void* p, size_type n) -{ - assert ((p || !n) && "The output string buffer must not be read-only"); -} - -/// Attempts to create more output space. Returns remaining(). -coutclass::size_type coutclass::overflow (size_type n __attribute__((unused))) -{ - return remaining(); -} - -} diff --git a/common/source/ustl/sistream.cpp b/common/source/ustl/sistream.cpp deleted file mode 100644 index 071eaf365..000000000 --- a/common/source/ustl/sistream.cpp +++ /dev/null @@ -1,232 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#include "sistream.h" -#include "sostream.h" -#include "ustring.h" - -namespace ustl { - -#define DEFAULT_DELIMITERS " \t\n\r;:,.?" -const char ios_base::c_DefaultDelimiters [istringstream::c_MaxDelimiters] = DEFAULT_DELIMITERS; - -/// Default constructor. -istringstream::istringstream (void) noexcept -: istream() -,_flags (0) -,_gcount (0) -{ - exceptions (goodbit); - set_delimiters (DEFAULT_DELIMITERS); -} - -istringstream::istringstream (const void* p, size_type n) noexcept -: istream() -,_flags (0) -,_gcount (0) -{ - exceptions (goodbit); - relink (p, n); - set_delimiters (DEFAULT_DELIMITERS); -} - -istringstream::istringstream (const cmemlink& source) noexcept -: istream() -,_flags (0) -,_gcount (0) -{ - exceptions (goodbit); - relink (source); - set_delimiters (DEFAULT_DELIMITERS); -} - -bool istringstream::is_delimiter (char c) const noexcept -{ - return memchr (_delimiters, c, VectorSize(_delimiters)-1); -} - -char istringstream::skip_delimiters (void) -{ - char c = _delimiters[0]; - while (is_delimiter(c)) { - if (!remaining() && !underflow()) { - verify_remaining ("read", "", 1); - return 0; - } - istream::iread (c); - } - return c; -} -/* -//{{{ str_to_num -namespace { - -typedef istringstream::iterator issiter_t; -template -inline void str_to_num (issiter_t i, issiter_t* iend, unsigned base, T& v) - { v = strtol (i, const_cast(iend), base); } -template <> inline void str_to_num (issiter_t i, issiter_t* iend, unsigned, double& v) - { v = strtod (i, const_cast(iend)); } -#if HAVE_LONG_LONG && SIZE_OF_LONG_LONG > SIZE_OF_LONG -template <> inline void str_to_num (issiter_t i, issiter_t* iend, unsigned base, long long& v) - { v = strtoll (i, const_cast(iend), base); } -#endif - -} //}}} namespace - -template -inline void istringstream::read_number (T& v) -{ - v = 0; - if (!skip_delimiters()) - return; - ungetc(); - iterator ilast; - do { - str_to_num (ipos(), &ilast, (_flags & hex) ? 16 : (_flags & oct) ? 8 : 0, v); - skip (distance (ipos(), ilast)); - } while (ilast == end() && underflow()); -}*/ - -void istringstream::iread (int& v) { read_number (v); } -//void istringstream::iread (double& v) { read_number (v); } -void istringstream::iread (long& v) { read_number (v); } -#if HAVE_LONG_LONG -void istringstream::iread (long long& v) { read_number (v); } -#endif - -void istringstream::iread (wchar_t& v) -{ - if (!(v = skip_delimiters())) - return; - ungetc(); - size_t cs = Utf8SequenceBytes (v); - if (remaining() < cs && underflow(cs) < cs) - verify_remaining ("read", "wchar_t", cs); - else { - v = *utf8in (ipos()); - skip (cs); - } -} - -void istringstream::iread (bool& v) -{ - static const char tf[2][8] = { "false", "true" }; - char c = skip_delimiters(); - v = (c == 't' || c == '1'); - if (c != tf[v][0]) - return; - for (const char* tv = tf[v]; c == *tv && (remaining() || underflow()); ++tv) - istream::iread (c); - ungetc(); -} - -void istringstream::iread (string& v) -{ - v.clear(); - char prevc, quoteChar = 0, c = skip_delimiters(); - if (!c) - return; - if (c == '\"' || c == '\'') - quoteChar = c; - else - v += c; - while (remaining() || underflow()) { - prevc = c; - istream::iread (c); - if (!quoteChar && is_delimiter(c)) - break; - if (prevc == '\\') { - switch (c) { - case 't': c = '\t'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 'b': c = '\b'; break; - case 'E': c = 27; break; // ESC sequence - case '\"': c = '\"'; break; - case '\'': c = '\''; break; - case '\\': c = '\\'; break; - }; - v.end()[-1] = c; - } else { - if (c == quoteChar) - break; - v += c; - } - } -} - -istringstream& istringstream::read (void* buffer, size_type sz) -{ - if (remaining() < sz && underflow(sz) < sz) - verify_remaining ("read", "", sz); - else - istream::read (buffer, _gcount = sz); - return *this; -} - -/// Reads characters into \p p,n until \p delim is found (but not stored or extracted) -istringstream& istringstream::get (char* p, size_type n, char delim) -{ - _gcount = 0; - for (char c = 0, *pend = p+n-1; p < pend && (remaining() || underflow()); ++p, ++_gcount) { - istream::iread (c); - if (c == delim) { - ungetc(); - break; - } - *p = c; - } - *p = 0; - return *this; -} - -/// Reads characters into \p s until \p delim is found (but not stored or extracted) -istringstream& istringstream::get (string& v, char delim) -{ - _gcount = 0; - v.clear(); - while ((remaining() || underflow()) && ipos()[0] != delim) { - const_iterator p = ipos(); - size_type n = find (p, end(), delim) - p; - v.append (p, n); - skip (n); - _gcount += n; - } - return *this; -} - -/// Reads characters into \p s until \p delim is extracted (but not stored) -istringstream& istringstream::getline (string& s, char delim) -{ - get (s, delim); - if (remaining() && ipos()[0] == delim) { - skip (1); - ++_gcount; - } - return *this; -} - -/// Reads characters into \p p,n until \p delim is extracted (but not stored) -istringstream& istringstream::getline (char* p, size_type n, char delim) -{ - get (p, n, delim); - if (remaining() && ipos()[0] == delim) { - skip (1); - ++_gcount; - } - return *this; -} - -/// Extract until \p delim or \p n chars have been read. -istringstream& istringstream::ignore (size_type n, char delim) -{ - _gcount = n; - while (n-- && (remaining() || underflow()) && get() != delim) {} - _gcount -= n; - return *this; -} - -} // namespace ustl diff --git a/common/source/ustl/sostream.cpp b/common/source/ustl/sostream.cpp deleted file mode 100644 index 134bda6fe..000000000 --- a/common/source/ustl/sostream.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#include "mistream.h" // for istream_iterator, referenced in utf8.h -#include "sostream.h" -#include "ustring.h" -#include "ulimits.h" -#include "ustringformat.h" -//#include - -namespace ustl { - -/// Creates an output string stream linked to the given memory area. -ostringstream::ostringstream (void* p, size_t n) noexcept -: ostream() -,_buffer() -,_flags (0) -,_width (0) -,_precision (2) -,_fill (0) -{ - exceptions (goodbit); - link (p, n); -} - -/// Creates an output string stream, initializing the buffer with v. -ostringstream::ostringstream (const string& v) -: ostream() -,_buffer (v) -,_flags (0) -,_width (0) -,_precision (2) -,_fill (0) -{ - exceptions (goodbit); - ostream::link (_buffer); -} - -/// Copies \p s to the internal buffer. -void ostringstream::str (const string& s) -{ - _buffer = s; - ostream::link (_buffer); - SetPos (_buffer.size()); -} - -/// Writes a single character into the stream. -void ostringstream::iwrite (unsigned char v) -{ - if (remaining() >= 1 || overflow() >= 1) - ostream::iwrite (v); -} - -/// Writes the contents of \p buffer of \p size into the stream. -ostringstream& ostringstream::write (const void* buffer, size_type sz) -{ - const char* buf = static_cast(buffer); - for (size_type bw = 0; (bw = min(sz, remaining() ? remaining() : overflow(sz))); buf += bw, sz -= bw) - ostream::write (buf, bw); - return *this; -} - -/// Simple decimal encoding of \p n into \p fmt. -inline char* ostringstream::encode_dec (char* fmt, uint32_t n) const noexcept -{ - do { - *fmt++ = '0' + n % 10; - } while (n /= 10); - return fmt; -} - -/// Generates a sprintf format string for the given type. -void ostringstream::fmtstring (char* fmt, const char* typestr, bool bInteger) const -{ - *fmt++ = '%'; - if (_width) { - if (_fill == '0') - *fmt++ = '0'; - fmt = encode_dec (fmt, _width); - } - if (_flags.f & left) - *fmt++ = '-'; - if (bInteger) { - if (_flags.f & showpos) - *fmt++ = '+'; - if (_flags.f & showbase) - *fmt++ = '#'; - } else { - *fmt++ = '.'; - fmt = encode_dec (fmt, _precision); - } - while (*typestr) - *fmt++ = *typestr++; - if (bInteger) { - if (_flags.f & hex) - fmt[-1] = (_flags.f & uppercase) ? 'X' : 'x'; - else if (_flags.f & oct) - fmt[-1] = 'o'; - } else if (_flags.f & scientific) - fmt[-1] = 'E'; - *fmt = 0; -} - -/// Writes \p v into the stream as utf8 -void ostringstream::iwrite (wchar_t v) -{ - char buffer [8]; - *utf8out(buffer) = v; - write (buffer, Utf8Bytes(v)); -} - -/// Writes value \p v into the stream as text. -void ostringstream::iwrite (bool v) -{ - static const char tf[2][8] = { "false", "true" }; - write (tf[v], 5 - v); -} - -/// Equivalent to a vsprintf on the string. -int ostringstream::vformat (const char* fmt, va_list args) -{ -#if HAVE_VA_COPY - va_list args2; -#else - #define args2 args - #undef __va_copy - #define __va_copy(x,y) -#endif - int rv, space; - do { - space = remaining(); - __va_copy (args2, args); - if (0 > (rv = vsnprintf (ipos(), space, fmt, args2))) - return rv; - } while (rv >= space && rv < int(overflow(rv+1))); - SetPos (pos() + min (rv, space)); - return rv; -} - -/// Equivalent to a sprintf on the string. -int ostringstream::format (const char* fmt, ...) -{ - va_list args; - va_start (args, fmt); - const int rv = vformat (fmt, args); - va_end (args); - return rv; -} - -/// Links to string \p l as resizable. -void ostringstream::link (void* p, size_type n) noexcept -{ - assert ((p || !n) && "The output string buffer must not be read-only"); - ostream::link (p, n); - _buffer.link (p, n); -} - -/// Attempts to create more output space. Returns remaining(). -ostringstream::size_type ostringstream::overflow (size_type n) -{ - if (n > remaining() && (good() || n <= capacity() - pos())) { - const uoff_t oldPos (pos()); - _buffer.reserve (oldPos + n, false); - _buffer.resize (oldPos + n); - ostream::link (_buffer); - SetPos (oldPos); - } - verify_remaining ("write", "text", n); - return remaining(); -} - -} // namespace ustl diff --git a/common/source/ustl/ualgobase.cpp b/common/source/ustl/ualgobase.cpp deleted file mode 100644 index 73b26803f..000000000 --- a/common/source/ustl/ualgobase.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#ifndef NDEBUG // Optimized code here. asserts slow it down, and are checked elsewhere. -#define NDEBUG -#endif - -#include "ualgo.h" - -namespace ustl { - -// Generic version for implementing fill_nX_fast on non-i386 architectures. -template static inline void stosv (T*& p, size_t n, T v) - { while (n--) *p++ = v; } - -#if __x86__ - -//---------------------------------------------------------------------- -// Copy functions -//---------------------------------------------------------------------- - -static inline void movsb_dir_up (void) { asm volatile ("cld"); } -static inline void movsb_dir_down (void) { asm volatile ("std"); } - -static inline void movsb (const void*& src, size_t nBytes, void*& dest) -{ - asm volatile ("rep;\n\tmovsb" - : "=&S"(src), "=&D"(dest), "=&c"(nBytes) - : "0"(src), "1"(dest), "2"(nBytes) - : "memory"); -} - -static inline void movsd (const void*& src, size_t nWords, void*& dest) -{ - asm volatile ("rep;\n\tmovsl" - : "=&S"(src), "=&D"(dest), "=&c"(nWords) - : "0"(src), "1"(dest), "2"(nWords) - : "memory"); -} - -#if __MMX__ -template <> inline void stosv (uint8_t*& p, size_t n, uint8_t v) -{ asm volatile ("rep;\n\tstosb" : "=&D"(p), "=c"(n) : "0"(p), "1"(n), "a"(v) : "memory"); } -#endif -template <> inline void stosv (uint16_t*& p, size_t n, uint16_t v) -{ asm volatile ("rep;\n\tstosw" : "=&D"(p), "=c"(n) : "0"(p), "1"(n), "a"(v) : "memory"); } -template <> inline void stosv (uint32_t*& p, size_t n, uint32_t v) -{ asm volatile ("rep;\n\tstosl" : "=&D"(p), "=c"(n) : "0"(p), "1"(n), "a"(v) : "memory"); } - -#if __MMX__ -#define MMX_ALIGN 16U // Data must be aligned on this grain -#define MMX_BS 32U // Assembly routines copy data this many bytes at a time. - -static inline void simd_block_copy (const void* src, void* dest) -{ - const char* csrc = static_cast(src); - char* cdest = static_cast(dest); - #if __SSE__ - asm ( - "movaps\t%2, %%xmm0 \n\t" - "movaps\t%3, %%xmm1 \n\t" - "movntps\t%%xmm0, %0 \n\t" - "movntps\t%%xmm1, %1" - : "=m"(cdest[0]), "=m"(cdest[16]) - : "m"(csrc[0]), "m"(csrc[16]) - : "xmm0", "xmm1", "memory"); - #else - asm ( - "movq %4, %%mm0 \n\t" - "movq %5, %%mm1 \n\t" - "movq %6, %%mm2 \n\t" - "movq %7, %%mm3 \n\t" - "movq %%mm0, %0 \n\t" - "movq %%mm1, %1 \n\t" - "movq %%mm2, %2 \n\t" - "movq %%mm3, %3" - : "=m"(cdest[0]), "=m"(cdest[8]), "=m"(cdest[16]), "=m"(cdest[24]) - : "m"(csrc[0]), "m"(csrc[8]), "m"(csrc[16]), "m"(csrc[24]) - : "mm0", "mm1", "mm2", "mm3", "st", "st(1)", "st(2)", "st(3)", "memory"); - #endif -} - -static inline void simd_block_store (uint8_t* dest) -{ - #if __SSE__ - asm volatile ( - "movntq %%mm0, %0\n\t" - "movntq %%mm0, %1\n\t" - "movntq %%mm0, %2\n\t" - "movntq %%mm0, %3" - : "=m"(dest[0]), "=m"(dest[8]), "=m"(dest[16]), "=m"(dest[24]) - :: "memory"); - #else - asm volatile ( - "movq %%mm0, %0 \n\t" - "movq %%mm0, %1 \n\t" - "movq %%mm0, %2 \n\t" - "movq %%mm0, %3" - : "=m"(dest[0]), "=m"(dest[8]), "=m"(dest[16]), "=m"(dest[24]) - :: "memory"); - #endif -} - -static inline void simd_block_cleanup (void) -{ - #if !__SSE__ - simd::reset_mmx(); - #endif - asm volatile ("sfence"); -} - -/// The fastest optimized raw memory copy. -void copy_n_fast (const void* src, size_t nBytes, void* dest) noexcept -{ - movsb_dir_up(); - size_t nHeadBytes = Align(uintptr_t(src), MMX_ALIGN) - uintptr_t(src); - nHeadBytes = min (nHeadBytes, nBytes); - movsb (src, nHeadBytes, dest); - nBytes -= nHeadBytes; - if (!(uintptr_t(dest) % MMX_ALIGN)) { - const size_t nMiddleBlocks = nBytes / MMX_BS; - for (uoff_t i = 0; i < nMiddleBlocks; ++ i) { - prefetch (advance (src, 512), 0, 0); - simd_block_copy (src, dest); - src = advance (src, MMX_BS); - dest = advance (dest, MMX_BS); - } - simd_block_cleanup(); - nBytes %= MMX_BS; - } - movsb (src, nBytes, dest); -} -#endif // __MMX__ - -/// The fastest optimized backwards raw memory copy. -void copy_backward_fast (const void* first, const void* last, void* result) noexcept -{ - prefetch (first, 0, 0); - prefetch (result, 1, 0); - size_t nBytes (distance (first, last)); - movsb_dir_down(); - size_t nHeadBytes = uintptr_t(last) % 4; - last = advance (last, -1); - result = advance (result, -1); - movsb (last, nHeadBytes, result); - nBytes -= nHeadBytes; - if (uintptr_t(result) % 4 == 3) { - const size_t nMiddleBlocks = nBytes / 4; - last = advance (last, -3); - result = advance (result, -3); - movsd (last, nMiddleBlocks, result); - nBytes %= 4; - } - movsb (last, nBytes, result); - movsb_dir_up(); -} -#endif // __x86__ - -//---------------------------------------------------------------------- -// Fill functions -//---------------------------------------------------------------------- - -#if __MMX__ -template static inline void build_block (T) {} -template <> inline void build_block (uint8_t v) -{ - asm volatile ( - "movd %0, %%mm0\n\tpunpcklbw %%mm0, %%mm0\n\tpshufw $0, %%mm0, %%mm0" - : : "g"(uint32_t(v)) : "mm0"); -} -template <> inline void build_block (uint16_t v) -{ - asm volatile ( - "movd %0, %%mm0\n\tpshufw $0, %%mm0, %%mm0" - : : "g"(uint32_t(v)) : "mm0"); -} -template <> inline void build_block (uint32_t v) -{ - asm volatile ( - "movd %0, %%mm0\n\tpunpckldq %%mm0, %%mm0" - : : "g"(uint32_t(v)) : "mm0"); -} - -static inline void simd_block_fill_loop (uint8_t*& dest, size_t count) -{ - prefetch (advance (dest, 512), 1, 0); - for (const uint8_t* destEnd = dest + count * MMX_BS; dest < destEnd; dest += MMX_BS) - simd_block_store (dest); - simd_block_cleanup(); - simd::reset_mmx(); -} - -template -static inline void fill_n_fast (T* dest, size_t count, T v) -{ - size_t nHead = Align(uintptr_t(dest), MMX_ALIGN) - uintptr_t(dest) / sizeof(T); - nHead = min (nHead, count); - stosv (dest, nHead, v); - count -= nHead; - build_block (v); - uint8_t* bdest = reinterpret_cast(dest); - simd_block_fill_loop (bdest, count * sizeof(T) / MMX_BS); - count %= MMX_BS; - dest = reinterpret_cast(bdest); - stosv (dest, count, v); -} - -void fill_n8_fast (uint8_t* dest, size_t count, uint8_t v) noexcept - { fill_n_fast (dest, count, v); } -void fill_n16_fast (uint16_t* dest, size_t count, uint16_t v) noexcept - { fill_n_fast (dest, count, v); } -void fill_n32_fast (uint32_t* dest, size_t count, uint32_t v) noexcept - { fill_n_fast (dest, count, v); } -#else -void fill_n8_fast (uint8_t* dest, size_t count, uint8_t v) noexcept { memset (dest, v, count); } -void fill_n16_fast (uint16_t* dest, size_t count, uint16_t v) noexcept { stosv (dest, count, v); } -void fill_n32_fast (uint32_t* dest, size_t count, uint32_t v) noexcept { stosv (dest, count, v); } -#endif // __MMX__ - -/// Exchanges ranges [first, middle) and [middle, last) -void rotate_fast (void* first, void* middle, void* last) noexcept -{ -#if HAVE_ALLOCA_H - const size_t half1 (distance (first, middle)), half2 (distance (middle, last)); - const size_t hmin (min (half1, half2)); - if (!hmin) - return; - void* buf = alloca (hmin); - if (buf) { - if (half2 < half1) { - copy_n_fast (middle, half2, buf); - copy_backward_fast (first, middle, last); - copy_n_fast (buf, half2, first); - } else { - copy_n_fast (first, half1, buf); - copy_n_fast (middle, half2, first); - copy_n_fast (buf, half1, advance (first, half2)); - } - } else -#else - if (first == middle || middle == last) - return; -#endif - { - char* f = static_cast(first); - char* m = static_cast(middle); - char* l = static_cast(last); - reverse (f, m); - reverse (m, l); - while (f != m && m != l) - iter_swap (f++, --l); - reverse (f, (f == m ? l : m)); - } -} - -#if __GNUC__ < 4 -size_t popcount (uint32_t v) noexcept -{ - const uint32_t w = v - ((v >> 1) & 0x55555555); // Algorithm from AMD optimization guide - const uint32_t x = (w & 0x33333333) + ((w >> 2) & 0x33333333); - return ((x + (x >> 4) & 0x0F0F0F0F) * 0x01010101) >> 24; -} - -#if HAVE_INT64_T -/// \brief Returns the number of 1s in \p v in binary. -size_t popcount (uint64_t v) noexcept -{ - v -= (v >> 1) & UINT64_C(0x5555555555555555); // Algorithm from Wikipedia - v = (v & UINT64_C(0x3333333333333333)) + ((v >> 2) & UINT64_C(0x3333333333333333)); - v = (v + (v >> 4)) & UINT64_C(0x0F0F0F0F0F0F0F0F); - return (v * UINT64_C(0x0101010101010101)) >> 56; -} -#endif // HAVE_INT64_T -#endif // !__GNUC__ - -//---------------------------------------------------------------------- -// Miscellaneous instantiated stuff from headers which don't have enough -// to warrant creation of a separate file.cc -//---------------------------------------------------------------------- - -// Used in uspecial to print printable characters -const char _FmtPrtChr[2][8]={"'%c'","%d"}; - -} // namespace ustl diff --git a/common/source/ustl/ubitset.cpp b/common/source/ustl/ubitset.cpp deleted file mode 100644 index 89ee6c172..000000000 --- a/common/source/ustl/ubitset.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#include "ubitset.h" - -namespace ustl { - -/// Copies bits from \p v of size \p n into \p buf as MSB "1011001..." LSB -/// If \p buf is too small, MSB bits will be truncated. -void convert_to_bitstring (const bitset_value_type* v, size_t n, string& buf) noexcept -{ - string::iterator stri = buf.end(); - for (size_t i = 0; i < n && stri > buf.begin(); ++ i) - for (bitset_value_type b = 1; b && stri > buf.begin(); b <<= 1) - *--stri = (v[i] & b) ? '1' : '0'; -} - -/// Copies bits from \p buf as MSB "1011001..." LSB into \p v of size \p n. -void convert_from_bitstring (const string& buf, bitset_value_type* v, size_t n) noexcept -{ - string::const_iterator stri = buf.end(); - for (size_t i = 0; i < n; ++ i) { - for (bitset_value_type b = 1; b; b <<= 1) { - if (stri == buf.begin() || *--stri == '0') - v[i] &= ~b; - else - v[i] |= b; - } - } -} - -} // namespace ustl diff --git a/common/source/ustl/ustring.cpp b/common/source/ustl/ustring.cpp deleted file mode 100644 index a0c6871df..000000000 --- a/common/source/ustl/ustring.cpp +++ /dev/null @@ -1,400 +0,0 @@ -// This file is part of the uSTL library, an STL implementation. -// -// Copyright (c) 2005 by Mike Sharov -// This file is free software, distributed under the MIT License. - -#include "ustring.h" -#include "mistream.h" -#include "mostream.h" -#include "ualgo.h" -#include "ustringformat.h" // for vsnprintf (in string::format) - -extern char data_end_address; - -namespace ustl { - -//---------------------------------------------------------------------- - -constexpr const string::pos_type string::npos; - -//---------------------------------------------------------------------- - -/// Assigns itself the value of string \p s -string::string (const string& s) -: memblock ((s.size()+1) & (s.is_linked()-1)) // +1 because base ctor can't call virtuals of this class -{ -// if (s.is_linked()) -// relink (s.c_str(), s.size()); -// else { - copy_n (s.begin(), size(), begin()); - relink (begin(), size()-1); // --m_Size -// } -} - -/// Links to \p s -string::string (const_pointer s) noexcept -: memblock (strlen(s)+1) -{ - if (!s) s = ""; - copy_n (s, strlen(s)+1, begin()); - relink (begin(), size()-1); // --m_Size -} - -/// Creates a string of length \p n filled with character \p c. -string::string (size_type n, value_type c) -: memblock (n+1) // +1 because base ctor can't call virtuals of this class -{ - relink (begin(), size()-1); // --m_Size - fill_n (begin(), n, c); - at(n) = 0; -} - -/// Resize the string to \p n characters. New space contents is undefined. -void string::resize (size_type n) -{ - if (!(n | memblock::capacity())) - return relink ("",0); - memblock::resize (n); - at(n) = 0; -} - -/// Assigns itself the value of string \p s -string& string::assign (const_pointer s) -{ - if (!s) s = ""; - assign (s, strlen (s)); - return *this; -} - -/// Assigns itself the value of string \p s of length \p len. -string& string::assign (const_pointer s, size_type len) -{ - resize (len); - copy_n (s, len, begin()); - return *this; -} - -/// Appends to itself the value of string \p s of length \p len. -string& string::append (const_pointer s) -{ - if (!s) s = ""; - append (s, strlen (s)); - return *this; -} - -/// Appends to itself the value of string \p s of length \p len. -string& string::append (const_pointer s, size_type len) -{ - resize (size() + len); - copy_n (s, len, end() - len); - return *this; -} - -/// Appends to itself \p n characters of value \p c. -string& string::append (size_type n, value_type c) -{ - resize (size() + n); - fill_n (end() - n, n, c); - return *this; -} - -/// Copies [start,start+n) into [p,n). The result is not null terminated. -string::size_type string::copy (pointer p, size_type n, pos_type start) const noexcept -{ - assert (p && n && start <= size()); - const size_type btc = min(n, size()-start); - copy_n (iat(start), btc, p); - return btc; -} - -/// Returns comparison value regarding string \p s. -/// The return value is: -/// \li 1 if this string is greater (by value, not length) than string \p s -/// \li 0 if this string is equal to string \p s -/// \li -1 if this string is less than string \p s -/// -int string::compare (const_iterator first1, const_iterator last1, const_iterator first2, const_iterator last2) noexcept // static -{ - assert (first1 <= last1 && (first2 <= last2 || !last2) && "Negative ranges result in memory allocation errors."); - const size_type len1 = distance (first1, last1), len2 = distance (first2, last2); - const int rvbylen = sign (int(len1 - len2)); - int rv = memcmp (first1, first2, min (len1, len2)); - return rv ? rv : rvbylen; -} - -/// Returns true if this string is equal to string \p s. -bool string::operator== (const_pointer s) const noexcept -{ - if (!s) s = ""; - return size() == strlen(s) && 0 == memcmp (c_str(), s, size()); -} - -/// Returns the beginning of character \p i. -string::const_iterator string::wiat (pos_type i) const noexcept -{ - utf8in_iterator cfinder (begin()); - cfinder += i; - return cfinder.base(); -} - -/// Inserts wide character \p c at \p ipo \p n times as a UTF-8 string. -/// -/// \p ipo is a byte position, not a character position, and is intended -/// to be obtained from one of the find functions. Generally you are not -/// able to know the character position in a localized string; different -/// languages will have different character counts, so use find instead. -/// -string& string::insert (pos_type ipo, size_type n, wvalue_type c) -{ - iterator ip (iat(ipo)); - ip = iterator (memblock::insert (memblock::iterator(ip), n * Utf8Bytes(c))); - fill_n (utf8out (ip), n, c); - *end() = 0; - return *this; -} - -/// Inserts sequence of wide characters at \p ipo (byte position from a find call) -string& string::insert (pos_type ipo, const wvalue_type* first, const wvalue_type* last, const size_type n) -{ - iterator ip (iat(ipo)); - size_type nti = distance (first, last), bti = 0; - for (size_type i = 0; i < nti; ++ i) - bti += Utf8Bytes(first[i]); - ip = iterator (memblock::insert (memblock::iterator(ip), n * bti)); - utf8out_iterator uout (utf8out (ip)); - for (size_type j = 0; j < n; ++ j) - for (size_type k = 0; k < nti; ++ k, ++ uout) - *uout = first[k]; - *end() = 0; - return *this; -} - -/// Inserts character \p c into this string at \p start. -string::iterator string::insert (const_iterator start, size_type n, value_type c) -{ - memblock::iterator ip = memblock::insert (memblock::const_iterator(start), n); - fill_n (ip, n, c); - *end() = 0; - return iterator(ip); -} - -/// Inserts \p count instances of string \p s at offset \p start. -string::iterator string::insert (const_iterator start, const_pointer s, size_type n) -{ - if (!s) s = ""; - return insert (start, s, s + strlen(s), n); -} - -/// Inserts [first,last] \p n times. -string::iterator string::insert (const_iterator start, const_pointer first, const_pointer last, size_type n) -{ - assert (first <= last); - assert (begin() <= start && end() >= start); - assert ((first < begin() || first >= end() || size() + abs_distance(first,last) < capacity()) && "Insertion of self with autoresize is not supported"); - memblock::iterator ip = iterator (memblock::insert (memblock::const_iterator(start), distance(first, last) * n)); - fill (ip, first, distance(first, last), n); - *end() = 0; - return iterator(ip); -} - -/// Erases \p size bytes at \p ep. -string::iterator string::erase (const_iterator ep, size_type n) -{ - string::iterator rv = memblock::erase (memblock::const_iterator(ep), n); - *end() = 0; - return rv; -} - -/// Erases \p n bytes at byte offset \p epo. -string& string::erase (pos_type epo, size_type n) -{ - erase (iat(epo), min (n, size()-epo)); - return *this; -} - -/// Replaces range [\p start, \p start + \p len] with string \p s. -string& string::replace (const_iterator first, const_iterator last, const_pointer s) -{ - if (!s) s = ""; - replace (first, last, s, s + strlen(s)); - return *this; -} - -/// Replaces range [\p start, \p start + \p len] with \p count instances of string \p s. -string& string::replace (const_iterator first, const_iterator last, const_pointer i1, const_pointer i2, size_type n) -{ - assert (first <= last); - assert (n || distance(first, last)); - assert (first >= begin() && first <= end() && last >= first && last <= end()); - assert ((i1 < begin() || i1 >= end() || abs_distance(i1,i2) * n + size() < capacity()) && "Replacement by self can not autoresize"); - const size_type bte = distance(first, last), bti = distance(i1, i2) * n; - memblock::const_iterator rp = static_cast(first); - if (bti < bte) - rp = memblock::erase (rp, bte - bti); - else if (bte < bti) - rp = memblock::insert (rp, bti - bte); - fill (rp, i1, distance(i1, i2), n); - *end() = 0; - return *this; -} - -/// Returns the offset of the first occurence of \p c after \p pos. -string::pos_type string::find (value_type c, pos_type pos) const noexcept -{ - const_iterator found = ::ustl::find (iat(pos), end(), c); - return found < end() ? pos_type (distance(begin(),found)) : npos; -} - -/// Returns the offset of the first occurence of substring \p s of length \p n after \p pos. -string::pos_type string::find (const string& s, pos_type pos) const noexcept -{ - if (s.empty() || s.size() > size() - pos) - return npos; - pos_type endi = s.size() - 1; - value_type endchar = s[endi]; - pos_type lastPos = endi; - while (lastPos-- && s[lastPos] != endchar) ; - const size_type skip = endi - lastPos; - const_iterator i = iat(pos) + endi; - for (; i < end() && (i = ::ustl::find (i, end(), endchar)) < end(); i += skip) - if (memcmp (i - endi, s.c_str(), s.size()) == 0) - return distance (begin(), i) - endi; - return npos; -} - -/// Returns the offset of the last occurence of character \p c before \p pos. -string::pos_type string::rfind (value_type c, pos_type pos) const noexcept -{ - for (int i = min(pos,pos_type(size()-1)); i >= 0; --i) - if (at(i) == c) - return i; - return npos; -} - -/// Returns the offset of the last occurence of substring \p s of size \p n before \p pos. -string::pos_type string::rfind (const string& s, pos_type pos) const noexcept -{ - const_iterator d = iat(pos) - 1; - const_iterator sp = begin() + s.size() - 1; - const_iterator m = s.end() - 1; - for (long int i = 0; d > sp && size_type(i) < s.size(); -- d) - for (i = 0; size_type(i) < s.size(); ++ i) - if (m[-i] != d[-i]) - break; - return d > sp ? pos_type (distance (begin(), d + 2 - s.size())) : npos; -} - -/// Returns the offset of the first occurence of one of characters in \p s of size \p n after \p pos. -string::pos_type string::find_first_of (const string& s, pos_type pos) const noexcept -{ - for (size_type i = min(size_type(pos),size()); i < size(); ++ i) - if (s.find (at(i)) != npos) - return i; - return npos; -} - -/// Returns the offset of the first occurence of one of characters not in \p s of size \p n after \p pos. -string::pos_type string::find_first_not_of (const string& s, pos_type pos) const noexcept -{ - for (size_type i = min(size_type(pos),size()); i < size(); ++ i) - if (s.find (at(i)) == npos) - return i; - return npos; -} - -/// Returns the offset of the last occurence of one of characters in \p s of size \p n before \p pos. -string::pos_type string::find_last_of (const string& s, pos_type pos) const noexcept -{ - for (int i = min(size_type(pos),size()-1); i >= 0; -- i) - if (s.find (at(i)) != npos) - return i; - return npos; -} - -/// Returns the offset of the last occurence of one of characters not in \p s of size \p n before \p pos. -string::pos_type string::find_last_not_of (const string& s, pos_type pos) const noexcept -{ - for (int i = min(pos,pos_type(size()-1)); i >= 0; -- i) - if (s.find (at(i)) == npos) - return i; - return npos; -} - -/// Equivalent to a vsprintf on the string. -int string::vformat (const char* fmt, va_list args) -{ -#if HAVE_VA_COPY - va_list args2; -#else - #define args2 args - #undef __va_copy - #define __va_copy(x,y) -#endif - int rv = size(), wcap; - do { - __va_copy (args2, args); - rv = vsnprintf (data(), wcap = memblock::capacity(), fmt, args2); - resize (rv); - } while (rv >= wcap); - return rv; -} - -/// Equivalent to a sprintf on the string. -int string::format (const char* fmt, ...) -{ - va_list args; - va_start (args, fmt); - const int rv = vformat (fmt, args); - va_end (args); - return rv; -} - -/// Returns the number of bytes required to write this object to a stream. -size_t string::stream_size (void) const noexcept -{ - return Utf8Bytes(size()) + size(); -} - -/// Reads the object from stream \p os -/*void string::read (istream& is) -{ - char szbuf [8]; - is >> szbuf[0]; - size_t szsz (Utf8SequenceBytes (szbuf[0]) - 1), n = 0; - if (!is.verify_remaining ("read", "ustl::string", szsz)) return; - is.read (szbuf + 1, szsz); - n = *utf8in(szbuf); - if (!is.verify_remaining ("read", "ustl::string", n)) return; - resize (n); - is.read (data(), size()); -} - -/// Writes the object to stream \p os -void string::write (ostream& os) const -{ - const written_size_type sz (size()); - assert (sz == size() && "No support for writing strings larger than 4G"); - - char szbuf [8]; - utf8out_iterator szout (szbuf); - *szout = sz; - size_t szsz = distance (szbuf, szout.base()); - - if (!os.verify_remaining ("write", "ustl::string", szsz + sz)) return; - os.write (szbuf, szsz); - os.write (cdata(), sz); -}*/ - -/// Returns a hash value for [first, last) -hashvalue_t string::hash (const char* first, const char* last) noexcept // static -{ - hashvalue_t h = 0; - // This has the bits flowing into each other from both sides of the number - for (; first < last; ++ first) - h = *first + ((h << 7) | (h >> (BitsInType(hashvalue_t) - 7))); - return h; -} - -string::size_type string::minimumFreeCapacity (void) const noexcept { return 1; } - -} // namespace ustl diff --git a/common/source/util/Bitmap.cpp b/common/source/util/Bitmap.cpp index 7e1bb01c5..c94f0f002 100644 --- a/common/source/util/Bitmap.cpp +++ b/common/source/util/Bitmap.cpp @@ -23,6 +23,19 @@ Bitmap::Bitmap(size_t number_of_bits) : bitmap_ = new uint8[BITMAP_BYTE_COUNT(number_of_bits)]{}; } +Bitmap::Bitmap(const uint8_t* data, size_t number_of_bits) : + size_(number_of_bits), + num_bits_set_(0) +{ + bitmap_ = new uint8[BITMAP_BYTE_COUNT(number_of_bits)]{}; + memcpy(bitmap_, data, BITMAP_BYTE_COUNT(number_of_bits)); + for (size_t i = 0; i < number_of_bits; ++i) + { + if (getBit(i)) + ++num_bits_set_; + } +} + Bitmap::Bitmap(const Bitmap &bm) : size_(bm.size_) , num_bits_set_(bm.num_bits_set_) @@ -48,7 +61,7 @@ bool Bitmap::setBit(size_t bit_number) bool Bitmap::setBit(uint8* b, size_t& num_bits_set, size_t bit_number) { - //kprintfd("bitmap %p, %zu, %zu\n",b,num_bits_set, bit_number); + //kprintfd("bitmap %p, %zu, set bit %zu\n",b,num_bits_set, bit_number); if (!(BYTE & MASK)) { BYTE |= MASK; @@ -58,25 +71,30 @@ bool Bitmap::setBit(uint8* b, size_t& num_bits_set, size_t bit_number) return false; } -bool Bitmap::getBit(size_t bit_number) +bool Bitmap::getBit(size_t bit_number) const { assert(bit_number < size_); return getBit(bitmap_, bit_number); } -bool Bitmap::getBit(uint8* b, size_t bit_number) +bool Bitmap::getBit(const uint8* b, size_t bit_number) { return BYTE & MASK; } bool Bitmap::unsetBit(size_t bit_number) { - assert(bit_number < size_); + if(bit_number >= size_) + { + kprintfd("bit number %zu < size %zu\n", bit_number, size_); + assert(bit_number < size_); + } return unsetBit(bitmap_, num_bits_set_, bit_number); } bool Bitmap::unsetBit(uint8* b, size_t& num_bits_set, size_t bit_number) { + //kprintfd("bitmap %p, %zu, unset bit %zu\n",b,num_bits_set, bit_number); if (BYTE & MASK) { BYTE &= ~MASK; @@ -96,13 +114,13 @@ void Bitmap::setByte(size_t byte_number, uint8 byte) b = byte; } -uint8 Bitmap::getByte(size_t byte_number) +uint8 Bitmap::getByte(size_t byte_number) const { assert(byte_number * bits_per_bitmap_atom_ < size_); return bitmap_[byte_number]; } -void Bitmap::bmprint() +void Bitmap::bmprint() const { bmprint(bitmap_, size_, num_bits_set_); } @@ -114,27 +132,43 @@ void Bitmap::bmprint(uint8* b, size_t n, size_t num_bits_set) for (size_t i = 0; i < n; i++) { if (i % 64 == 0) + { kprintfd("%05zx| ",i); + } kprintfd("%d", getBit(b, i)); if (i % 8 == 7) + { kprintfd(" "); + } if (i % 64 == 63) + { kprintfd("\n"); + } } kprintfd("\n----------------------------------Bitmap:end----------------------------------\n"); } -size_t Bitmap::getSize() +size_t Bitmap::getSize() const { return size_; } -size_t Bitmap::getNumBitsSet() +size_t Bitmap::getBytesSize() const +{ + return BITMAP_BYTE_COUNT(size_); +} + +size_t Bitmap::getNumBitsSet() const { return num_bits_set_; } -size_t Bitmap::getNumFreeBits() +size_t Bitmap::getNumFreeBits() const { return size_ - num_bits_set_; } + +const uint8* Bitmap::data() const +{ + return bitmap_; +} diff --git a/common/source/util/CMakeLists.txt b/common/source/util/CMakeLists.txt index 68d29e3ef..40020c5e8 100644 --- a/common/source/util/CMakeLists.txt +++ b/common/source/util/CMakeLists.txt @@ -1,3 +1,5 @@ -include_directories(../../include/util) +add_project_library(common_util) -add_project_library(common_util) \ No newline at end of file +target_include_directories(kernel PUBLIC + ../../include/util + ../../include/util/ranges) diff --git a/common/source/util/SWEBDebugInfo.cpp b/common/source/util/SWEBDebugInfo.cpp index 8db946c4e..3225af541 100644 --- a/common/source/util/SWEBDebugInfo.cpp +++ b/common/source/util/SWEBDebugInfo.cpp @@ -1,11 +1,11 @@ -#include +#include "EASTL/string.h" #include "kprintf.h" +#include "kstring.h" #include "SWEBDebugInfo.h" #include "Stabs2DebugInfo.h" #include "ArchCommon.h" #include "ArchMemory.h" - struct FileHeader { uint16_t functions; uint8_t filename_len; @@ -23,19 +23,12 @@ struct LineHeader { } __attribute__((packed)); -SWEBDebugInfo::SWEBDebugInfo(char const *sweb_start, char const *sweb_end) : Stabs2DebugInfo(sweb_start, sweb_end, 0) { - if (sweb_start != 0 && sweb_end != 0) +SWEBDebugInfo::SWEBDebugInfo(char const *sweb_begin, char const *sweb_end) : Stabs2DebugInfo(sweb_begin, sweb_end, nullptr) { + if (sweb_begin != nullptr && sweb_end != nullptr) initialiseSymbolTable(); } -SWEBDebugInfo::~SWEBDebugInfo() { -} - - void SWEBDebugInfo::initialiseSymbolTable() { - function_defs_.reserve(256); - file_addrs_.reserve(256); - char *data = (char *) stab_start_ + 8; char buffer[256]; @@ -45,7 +38,7 @@ void SWEBDebugInfo::initialiseSymbolTable() { strncpy(buffer, data, fh->filename_len); buffer[fh->filename_len] = 0; data += fh->filename_len; - ustl::string filename(buffer); + eastl::string filename(buffer); for(int fn = 0; fn < fh->functions; fn++) { FunctionHeader* fnh = (FunctionHeader*)data; @@ -68,15 +61,15 @@ void SWEBDebugInfo::getCallNameAndLine(pointer address, const char *&name, ssize name = "UNKNOWN FUNCTION"; line = 0; - if (!this || function_defs_.size() == 0) + if (!this || function_defs_.empty()) return; - FunctionHeader* fh = 0; + FunctionHeader* fh = nullptr; for(auto f : function_defs_) { if (address >= f.first) fh = (FunctionHeader*)f.second; } - if (fh == 0) + if (fh == nullptr) return; name = ((char*)fh) + sizeof(FunctionHeader); @@ -104,9 +97,9 @@ void SWEBDebugInfo::printCallInformation(pointer address) const { ssize_t line; getCallNameAndLine(address, name, line); if (line >= 0) { - kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s:%zu \n", address, name, line ); + kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s:%zu \n", address, name, (size_t)line ); } else { - kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s+%zx\n", address, name, -line); + kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s+%zx\n", address, name, (size_t)-line); } } diff --git a/common/source/util/Stabs2DebugInfo.cpp b/common/source/util/Stabs2DebugInfo.cpp index 93ac7b842..82bcee207 100644 --- a/common/source/util/Stabs2DebugInfo.cpp +++ b/common/source/util/Stabs2DebugInfo.cpp @@ -1,4 +1,5 @@ #include "kprintf.h" +#include "kstring.h" #include "Stabs2DebugInfo.h" #include "ArchCommon.h" #include "ArchMemory.h" @@ -17,9 +18,10 @@ struct StabEntry uint32 n_value; }__attribute__((packed)); -Stabs2DebugInfo::Stabs2DebugInfo(char const *stab_start, char const *stab_end, char const *stab_str) : - stab_start_(reinterpret_cast(stab_start)), - stab_end_(reinterpret_cast(stab_end)), stabstr_buffer_(stab_str) +Stabs2DebugInfo::Stabs2DebugInfo(char const *stab_begin, char const *stab_end, char const *stab_str) : + stab_start_(reinterpret_cast(stab_begin)), + stab_end_(reinterpret_cast(stab_end)), + stabstr_buffer_(stab_str) { if (stabstr_buffer_) initialiseSymbolTable(); @@ -35,12 +37,10 @@ Stabs2DebugInfo::~Stabs2DebugInfo() void Stabs2DebugInfo::initialiseSymbolTable() { - function_symbols_.reserve(256); - // debug output for userspace symols for (StabEntry const *current_stab = stab_start_; current_stab < stab_end_; ++current_stab) { - if (ArchMemory::get_PPN_Of_VPN_In_KernelMapping((size_t)current_stab / PAGE_SIZE,0,0) && (current_stab->n_type == N_FUN || current_stab->n_type == N_FNAME)) + if (ArchMemory::get_PPN_Of_VPN_In_KernelMapping((size_t)current_stab / PAGE_SIZE, nullptr, nullptr) && (current_stab->n_type == N_FUN || current_stab->n_type == N_FNAME)) { function_symbols_[current_stab->n_value] = current_stab; } @@ -67,7 +67,7 @@ bool Stabs2DebugInfo::tryPasteOoperator(const char *& input, char *& buffer, siz if (!input || !buffer) return false; - const char* operator_name = 0; + const char* operator_name = nullptr; for (StabsOperator op : operators) { @@ -78,7 +78,7 @@ bool Stabs2DebugInfo::tryPasteOoperator(const char *& input, char *& buffer, siz } } - if (operator_name == 0) + if (operator_name == nullptr) return false; size_t n = strlen(operator_name); @@ -95,7 +95,7 @@ bool Stabs2DebugInfo::tryPasteOoperator(const char *& input, char *& buffer, siz ssize_t Stabs2DebugInfo::getFunctionLine(pointer start, pointer offset) const { ssize_t line = -1; - ustl::map::const_iterator it = function_symbols_.find(start); + auto it = function_symbols_.find(start); if (it != function_symbols_.end()) { StabEntry const *se = it->second + 1; @@ -116,15 +116,15 @@ ssize_t Stabs2DebugInfo::getFunctionLine(pointer start, pointer offset) const void Stabs2DebugInfo::getCallNameAndLine(pointer address, const char*& mangled_name, ssize_t &line) const { - mangled_name = 0; + mangled_name = nullptr; line = 0; - if (!this || function_symbols_.size() == 0 || + if (!this || function_symbols_.empty() || !(ADDRESS_BETWEEN(address, function_symbols_.begin()->first, ArchCommon::getKernelEndAddress()))) return; - ustl::map::const_iterator it; - for(it = function_symbols_.begin(); it != function_symbols_.end() && it->first <= address; ++it); + auto it = function_symbols_.begin(); + for(; it != function_symbols_.end() && it->first <= address; ++it); if (it == function_symbols_.end()) return; @@ -159,20 +159,20 @@ void Stabs2DebugInfo::printCallInformation(pointer address) const } if(line >= 0) { - kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s:%zu \n", address, name, line ); + kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s:%zu \n", address, name, (size_t)line ); } else { - kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s+%zx\n", address, name, -line); + kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s+%zx\n", address, name, (size_t)-line); } } pointer Stabs2DebugInfo::getFunctionName(pointer address, char function_name[], size_t size) const { - if (function_symbols_.size() == 0 || !(ADDRESS_BETWEEN(address, function_symbols_.begin()->first, ArchCommon::getKernelEndAddress()))) + if (function_symbols_.empty() || !(ADDRESS_BETWEEN(address, function_symbols_.begin()->first, ArchCommon::getKernelEndAddress()))) return 0; - ustl::map::const_iterator it; + auto it = function_symbols_.begin(); for(it = function_symbols_.begin(); it != function_symbols_.end() && it->first <= address; ++it) { } @@ -187,7 +187,7 @@ pointer Stabs2DebugInfo::getFunctionName(pointer address, char function_name[], return 0; } -int Stabs2DebugInfo::readNumber(const char *& input) const +int Stabs2DebugInfo::readNumber(const char *& input) { if (!input) return -1; @@ -294,7 +294,7 @@ void Stabs2DebugInfo::pasteArguments(const char *& input, char *& buffer, char d } } -size_t Stabs2DebugInfo::putChar2Buffer(char*& buffer, char c, size_t& size) const +size_t Stabs2DebugInfo::putChar2Buffer(char*& buffer, char c, size_t& size) { if (size <= 1) return -1; @@ -377,4 +377,3 @@ void Stabs2DebugInfo::demangleName(const char* name, char *buffer, size_t size) putChar2Buffer(buffer,')',size); } } - diff --git a/common/source/util/backtrace.cpp b/common/source/util/backtrace.cpp index b1ef753f9..752dd6429 100644 --- a/common/source/util/backtrace.cpp +++ b/common/source/util/backtrace.cpp @@ -1,5 +1,6 @@ #include "backtrace.h" #include "Thread.h" +#include "Scheduler.h" pointer getCalledBefore(size_t offset) { diff --git a/common/source/util/coroutine.cpp b/common/source/util/coroutine.cpp new file mode 100644 index 000000000..db852a07a --- /dev/null +++ b/common/source/util/coroutine.cpp @@ -0,0 +1,3 @@ +#include "coroutine.h" + +std::noop_coroutine_handle::__frame std::noop_coroutine_handle::dummy_noop_coro_frame_{}; diff --git a/common/source/util/kstring.cpp b/common/source/util/kstring.cpp index 270d2ee64..217d1a038 100644 --- a/common/source/util/kstring.cpp +++ b/common/source/util/kstring.cpp @@ -4,76 +4,6 @@ #include "ArchMemory.h" #include "Thread.h" -extern "C" size_t strlen(const char *str) -{ - const char *pos = str; - - while (*pos) - { - ++pos; - } - - return (pos - str); -} - -extern "C" void *memcpy(void *dest, const void *src, size_t length) -{ - size_t i; - size_t* s = (size_t*) src; - size_t* d = (size_t*) dest; - size_t num_large_copies = length / sizeof(size_t); - size_t num_rest_copies = length % sizeof(size_t); - for (size_t i = 0; i < num_large_copies; ++i) - { - *d++ = *s++; - } - uint8* s8 = (uint8*) s; - uint8* d8 = (uint8*) d; - for (i = 0; i < num_rest_copies; ++i) - { - *d8++ = *s8++; - } - - assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - - return dest; -} - -extern "C" void *memmove(void *dest, const void *src, size_t length) -{ - uint8* dest8 = (uint8*) dest; - const uint8* src8 = (const uint8*) src; - - if (length == 0 || src == dest) - { - return dest; - } - - if (src > dest) - { - // if src is _not_ before dest we can do a forward copy - while (length--) - { - *dest8++ = *src8++; - } - } - else - { - // if src is before dest we have to do a backward copy - src8 += length - 1; - dest8 += length - 1; - - while (length--) - { - *dest8-- = *src8--; - } - } - - assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - - return dest; -} - void *memccpy(void *dest, const void *src, uint8 c, size_t length) { uint8 *dest8 = (uint8*) dest; @@ -81,7 +11,7 @@ void *memccpy(void *dest, const void *src, uint8 c, size_t length) if (length == 0) { - return (void*) 0; + return (void*) nullptr; } while (length--) @@ -94,122 +24,22 @@ void *memccpy(void *dest, const void *src, uint8 c, size_t length) assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - return (void *) 0; -} - -extern "C" void *memset(void *block, uint8 c, size_t size) -{ - if (size) - { - size_t i; - size_t* d = (size_t*) block; - size_t large_c = c; - for (i = 0; i < sizeof(size_t); i++) - { - large_c = (large_c << 8) | c; - } - size_t num_large_copies = size / sizeof(size_t); - size_t num_rest_copies = size % sizeof(size_t); - for (i = 0; i < num_large_copies; ++i) - { - *d++ = large_c; - } - uint8* d8 = (uint8*) d; - for (i = 0; i < num_rest_copies; ++i) - { - *d8++ = c; - } - } - - assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - - return block; -} - -extern "C" char *strcpy(char *dest, const char* src) -{ - assert("don't use strcpy" == 0); - - char *start = dest; - - for (; (*dest = *src); ++src, ++dest) - ; - - return start; -} - -extern "C" char *strncpy(char *dest, const char* src, size_t size) -{ - char *start = dest; - int8 fill = 0; - - while (size--) - { - if (fill) - { - *dest = 0; - } - else if ((*dest = *src) == 0) - { - fill = 1; - } - - src++; - dest++; - } - - assert(Thread::currentThreadIsStackCanaryOK() && "Kernel stack corruption detected."); - - return start; + return (void *) nullptr; } extern "C" char *strdup(const char *src) { size_t size = strlen(src) + 1; - char *dest = 0; + char *dest = nullptr; - if ((dest = (char*) kmalloc((size) * sizeof(char))) == (char*) 0) + if ((dest = (char*) kmalloc((size) * sizeof(char))) == (char*) nullptr) { - return (char*) 0; + return (char*) nullptr; } return (char*) memcpy(dest, src, size); } -extern "C" char *strcat(char *dest, const char*append) -{ - char *start = dest + strlen(dest); - strcpy(start, append); - return dest; -} - -extern "C" char *strncat(char *dest, const char*append, size_t size) -{ - char* save = dest; - - if (size == 0) - { - return save; - } - - while (*dest) - { - ++dest; - } - - while (size--) - { - if ((*dest = *append++) == '\0') - { - break; - } - ++dest; - } - - *dest = '\0'; - return save; -} - extern "C" size_t strlcat(char *dest, const char*append, size_t size) { size_t count = size; @@ -240,100 +70,6 @@ extern "C" size_t strlcat(char *dest, const char*append, size_t size) return done + (append - append_start) - 1; } -extern "C" void bcopy(void *src, void* dest, size_t length) -{ - uint8* dest8 = (uint8*) dest; - const uint8* src8 = (const uint8*) src; - - if (length == 0 || src == dest) - { - return; - } - - if (src < dest) - { - // if src is before dest we can do a forward copy - while (length--) - { - *dest8++ = *src8++; - } - } - else - { - // if src is _not_ before dest we can do a forward copy - src8 += length; - dest8 += length; - - while (length--) - { - *dest8-- = *src8--; - } - } - -} - -extern "C" int32 memcmp(const void *region1, const void *region2, size_t size) -{ - const uint8* b1 = (const uint8*)region1; - const uint8* b2 = (const uint8*)region2; - - if (size == 0) - { - return 0; - } - - while (size--) - { - if (*b1++ != *b2++) - { - return (*--b1 - *--b2); - } - } - - return 0; -} - -extern "C" int32 strcmp(const char *str1, const char *str2) -{ - assert(str1); - assert(str2); - - if (str1 == str2) - { - return 0; - } - - while ((*str1) && (*str2)) - { - if (*str1 != *str2) - { - break; - } - ++str1; - ++str2; - } - - return (*(uint8 *) str1 - *(uint8 *) str2); -} - -extern "C" int32 strncmp(const char *str1, const char *str2, size_t n) -{ - while (n && (*str1) && (*str2)) - { - if (*str1 != *str2) - { - break; - } - ++str1; - ++str2; - --n; - } - if (n == 0) - return 0; - else - return (*(uint8 *) str1 - *(uint8 *) str2); -} - extern "C" int32 bcmp(const void *region1, const void *region2, size_t size) { const uint8* b1 = (const uint8*)region1; @@ -371,22 +107,6 @@ extern "C" void *memnotchr(const void *block, uint8 c, size_t size) return (void *) 0; } -extern "C" void *memchr(const void *block, uint8 c, size_t size) -{ - const uint8 *b = (const uint8*) block; - - while (size--) - { - if (*b == c) - { - return (void *) b; - } - ++b; - } - - return (void *) 0; -} - extern "C" void *memrchr(const void *block, uint8 c, size_t size) { const uint8 *b = ((const uint8*) block + size - 1); @@ -400,113 +120,7 @@ extern "C" void *memrchr(const void *block, uint8 c, size_t size) --b; } - return (void *) 0; -} - -extern "C" char *strchr(const char* str, char c) -{ - do - { - if (*str == c) - { - return (char *) str; - } - } while (*++str); - - return (char *) 0; -} - -extern "C" char *strrchr(const char* str, char c) -{ - uint32 len = strlen(str); - const char *pos = str + len; // goes to '\0' - - do - { - if (*--pos == c) - { - return (char *) pos; - } - } while (--len); - - return (char *) 0; -} - -extern "C" char* strtok(char* str, const char* delimiters) -{ - static char* str_to_tok = 0; - if (str != 0) - str_to_tok = str; - - // no delimiters, so just return the rest-string - if (delimiters == 0) - return str_to_tok; - - if (str_to_tok == 0) - return 0; - - // determine token start and end - uint32 tok_start = 0; - uint32 tok_end = -1; - - // find first char which is not one of the delimiters - uint32 str_pos = 0; - for (str_pos = 0; str_to_tok[str_pos] != '\0'; str_pos++) - { - uint8 char_is_delimiter = 0; - - uint32 del_pos = 0; - for (del_pos = 0; delimiters[del_pos] != '\0'; del_pos++) - { - if (str_to_tok[str_pos] == delimiters[del_pos]) - { - char_is_delimiter = 1; - break; - } - } - - if (char_is_delimiter == 0) - { - // this is the start char of the token - tok_start = str_pos; - break; - } - } - - // find next delimiter in the string - for (str_pos = tok_start; str_to_tok[str_pos] != '\0'; str_pos++) - { - uint32 del_pos = 0; - for (; delimiters[del_pos] != '\0'; del_pos++) - { - if (str_to_tok[str_pos] == delimiters[del_pos]) - { - // delimiter found! - tok_end = str_pos; - break; - } - } - - if (tok_end != -1U) - break; - } - - // create and return token: - char* token = str_to_tok + tok_start; - - // update string - if (tok_end == -1U) - { - // finished, no next token - str_to_tok = 0; - } - else - { - str_to_tok[tok_end] = '\0'; - str_to_tok += tok_end + 1; - } - - return token; + return (void *) nullptr; } // converts a single digit into an @@ -525,7 +139,7 @@ extern "C" char numToASCIIChar(uint8 number) extern "C" char* itoa(int value, char* str, int base) { if (!str) - return 0; + return nullptr; int div = value; int mod; @@ -560,7 +174,7 @@ extern "C" char* itoa(int value, char* str, int base) return str; } -extern "C" uint32 checksum(uint32* src, uint32 nbytes) +extern "C" uint32 checksum(const uint32* src, uint32 nbytes) { nbytes /= sizeof(uint32); uint32 poly = 0xEDB88320; diff --git a/common/source/util/libc++.cpp b/common/source/util/libc++.cpp new file mode 100644 index 000000000..1f2645de4 --- /dev/null +++ b/common/source/util/libc++.cpp @@ -0,0 +1,120 @@ +#include "EASTL/atomic.h" +#include "assert.h" +#include "debug.h" + +extern "C" int __cxa_thread_atexit(__attribute__((unused)) void (*func)(), __attribute__((unused)) void *obj, __attribute__((unused)) void *dso_symbol) +{ + // Required for thread_local keyword (used for cpu local storage). Should technically call destructors of thread local objects (respectively cpu local in this case), but we don't care about that since we don't want to destroy cpu local storage + return 0; +} + + +// https://android.googlesource.com/platform/bionic/+/c9a659c/libc/bionic/__cxa_guard.cpp + +#if defined(__arm__) +// The ARM C++ ABI mandates that guard variables are 32-bit aligned, 32-bit +// values. The LSB is tested by the compiler-generated code before calling +// __cxa_guard_acquire. +union _guard_t { + eastl::atomic state; + int32_t aligner; +}; +static_assert(sizeof(_guard_t) == sizeof(int32_t), "Size of static initializer guard struct must be 4 bytes"); +#else +// The Itanium/x86 C++ ABI (used by all other architectures) mandates that +// guard variables are 64-bit aligned, 64-bit values. The LSB is tested by +// the compiler-generated code before calling __cxa_guard_acquire. +union _guard_t { + eastl::atomic state; + int64_t aligner; +}; +static_assert(sizeof(_guard_t) == sizeof(int64_t), "Size of static initializer guard struct must be 8 bytes"); +#endif + + +// Set construction state values according to reference documentation. +// 0 is the initialization value. +// Arm requires ((*gv & 1) == 1) after __cxa_guard_release, ((*gv & 3) == 0) after __cxa_guard_abort. +// X86 requires first byte not modified by __cxa_guard_acquire, first byte is non-zero after +// __cxa_guard_release. +#define CONSTRUCTION_NOT_YET_STARTED 0 +#define CONSTRUCTION_COMPLETE 1 +#define CONSTRUCTION_UNDERWAY 0x100 + +extern "C" int __cxa_guard_acquire(_guard_t* gv) { + int old_value = gv->state.load(eastl::memory_order_relaxed); + while (true) { + if (old_value == CONSTRUCTION_COMPLETE) + { + // A load_acquire operation is need before exiting with COMPLETE state, as we have to ensure + // that all the stores performed by the construction function are observable on this CPU + // after we exit. + eastl::atomic_thread_fence(eastl::memory_order_acquire); + return 0; + } + else if (old_value == CONSTRUCTION_NOT_YET_STARTED) + { + // Spinlock via compare exchange + if (!gv->state.compare_exchange_weak(old_value, + CONSTRUCTION_UNDERWAY)) //, + //eastl::memory_order_release, + //eastl::memory_order_acquire)) + { + continue; + } + // The acquire fence may not be needed. But as described in section 3.3.2 of + // the Itanium C++ ABI specification, it probably has to behave like the + // acquisition of a mutex, which needs an acquire fence. + eastl::atomic_thread_fence(eastl::memory_order_acquire); + return 1; + } + } +} + +extern "C" void __cxa_guard_release(_guard_t* gv) { + gv->state.store(CONSTRUCTION_COMPLETE, eastl::memory_order_release); +} + +extern "C" void __cxa_guard_abort(_guard_t* gv) { + gv->state.store(CONSTRUCTION_NOT_YET_STARTED, eastl::memory_order_release); +} + + +using func_ptr = void (*)(); +extern "C" func_ptr __preinit_array_start; +extern "C" func_ptr __preinit_array_end; +extern "C" func_ptr __init_array_start; +extern "C" func_ptr __init_array_end; +extern "C" func_ptr __fini_array_start; +extern "C" func_ptr __fini_array_end; + +void _preinit() +{ + assert(&__preinit_array_start <= &__preinit_array_end); + for (func_ptr* it = &__preinit_array_start; it < &__preinit_array_end; ++it) + { + assert(*it); + (*it)(); + } +} + +void globalConstructors() +{ + assert(&__init_array_start <= &__init_array_end); + for (func_ptr* it = &__init_array_start; it < &__init_array_end; ++it) + { + debugAdvanced(MAIN, "Calling global constructor %p\n", *it); + assert(*it); + (*it)(); + debugAdvanced(MAIN, "Global constructor %p finished\n", *it); + } +} + +void globalDestructors() +{ + for (func_ptr* it = &__fini_array_start; it < &__fini_array_end; ++it) + { + assert(*it); + (*it)(); + } +} diff --git a/common/source/util/libgcc.cpp b/common/source/util/libgcc.cpp index d8dbaae13..aa84f0f76 100644 --- a/common/source/util/libgcc.cpp +++ b/common/source/util/libgcc.cpp @@ -18,9 +18,11 @@ int64 __divdi3(int64 num, int64 den) minus ^= 1; } - v = __udivmoddi4(num, den, 0); + v = __udivmoddi4(num, den, nullptr); if ( minus ) + { v = -v; + } return v; } @@ -28,7 +30,7 @@ int64 __divdi3(int64 num, int64 den) uint64 __udivdi3(uint64 num, uint64 den) { - return __udivmoddi4(num, den, 0); + return __udivmoddi4(num, den, nullptr); } uint64 __umoddi3(uint64 a, uint64 b) @@ -61,7 +63,9 @@ uint64 __udivmoddi4(uint64 num, uint64 den, uint64 *rem_p) } if ( rem_p ) + { *rem_p = num; + } return quot; } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 000000000..4fe0c58ed --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.13) + +# https://eastl.docsforge.com/master/cmake/project-integration/ + +add_subdirectory(EASTL) + +target_link_libraries(EASTL kernel_options) +target_link_libraries(EASTL kernel_libc) +target_include_directories(EASTL + PRIVATE + "$" +) +target_sources(EASTL PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/EASTL/doc/EASTL.natvis") + +target_compile_options(EASTL PRIVATE -Wno-unused-variable) + +target_include_directories(EASTL PUBLIC EASTL_userconfig) +target_compile_definitions(EASTL PUBLIC + EASTL_USER_CONFIG_HEADER="eastl_userconfig.h" + EA_PLATFORM_SWEB=1 # EABase dependency does not check userconfig header. Need to define this globally. +) + +target_link_libraries(kernel PUBLIC EASTL) diff --git a/lib/EASTL/.clang-format b/lib/EASTL/.clang-format new file mode 100644 index 000000000..1680c8966 --- /dev/null +++ b/lib/EASTL/.clang-format @@ -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 +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- +#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- diff --git a/lib/EASTL/.gitattributes b/lib/EASTL/.gitattributes new file mode 100644 index 000000000..5e47b8d7d --- /dev/null +++ b/lib/EASTL/.gitattributes @@ -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 \ No newline at end of file diff --git a/lib/EASTL/.github/workflows/c-cpp.yml b/lib/EASTL/.github/workflows/c-cpp.yml new file mode 100644 index 000000000..1537c9cb7 --- /dev/null +++ b/lib/EASTL/.github/workflows/c-cpp.yml @@ -0,0 +1,132 @@ +name: EASTL Build & Test Pipeline + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + checkout: + name: Checkout EASTL and submodules + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + path: EASTL/ + - run: cd EASTL/ && git submodule update --init + - name: Upload checked out code + uses: actions/upload-artifact@v2.3.1 + with: + name: Code + path: EASTL/ + + build: + needs: checkout + + strategy: + fail-fast: false + matrix: + os: [ windows-latest, ubuntu-latest ] + compiler: [ clang, gcc, msvc ] + configuration: [ Debug, Release ] + exclude: + - os: windows-latest + compiler: gcc + - os: windows-latest + compiler: clang + - os: ubuntu-latest + compiler: msvc + include: + - os: windows-latest + compiler: msvc + cxxflags: '/std:c++20 /Zc:char8_t' + - os: ubuntu-latest + compiler: clang + cc: 'clang-11' + cxx: 'clang++-11' + cxxflags: '-std=c++20' + - os: ubuntu-latest + compiler: gcc + cc: 'gcc-10' + cxx: 'g++-10' + cxxflags: '-std=c++2a' + + name: Build EASTL + runs-on: ${{ matrix.os }} + + steps: + - name: Download a Build Artifact + uses: actions/download-artifact@v2.1.0 + with: + name: Code + path: Code/ + + - run: mkdir build + - run: cd build && cmake ../Code -DEASTL_BUILD_BENCHMARK:BOOL=ON -DEASTL_BUILD_TESTS:BOOL=ON + env: + CXXFLAGS: ${{ matrix.cxxflags }} + CXX: ${{ matrix.cxx }} + CC: ${{ matrix.cc }} + - run: cd build && cmake --build . --config ${{ matrix.configuration }} + - name: Upload binaries + uses: actions/upload-artifact@v2.3.1 + with: + name: Binaries-${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.configuration }} + path: build/ + + test: + needs: build + name: Run EASTL tests + strategy: + fail-fast: false + matrix: + os: [ windows-latest, ubuntu-latest ] + compiler: [ clang, msvc, gcc ] + configuration: [ Debug, Release ] + exclude: + - os: windows-latest + compiler: gcc + - os: windows-latest + compiler: clang + - os: ubuntu-latest + compiler: msvc + runs-on: ${{ matrix.os }} + + steps: + - name: Download a Build Artifact + uses: actions/download-artifact@v2.1.0 + with: + name: Binaries-${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.configuration }} + path: Binaries/ + - if: matrix.os == 'ubuntu-latest' + run: chmod 755 ./Binaries/test/EASTLTest + - run: cd Binaries/test && ctest -C ${{ matrix.configuration }} -V + + benchmark: + needs: build + name: Run EASTL benchmarks + strategy: + fail-fast: false + matrix: + os: [ windows-latest, ubuntu-latest ] + compiler: [ clang, msvc, gcc ] + configuration: [ Release ] + exclude: + - os: windows-latest + compiler: gcc + - os: windows-latest + compiler: clang + - os: ubuntu-latest + compiler: msvc + runs-on: ${{ matrix.os }} + + steps: + - name: Download a Build Artifact + uses: actions/download-artifact@v2.1.0 + with: + name: Binaries-${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.configuration }} + path: Binaries/ + - if: matrix.os == 'ubuntu-latest' + run: chmod 755 ./Binaries/benchmark/EASTLBenchmarks + - run: cd Binaries/benchmark && ctest -C ${{ matrix.configuration }} -V diff --git a/lib/EASTL/.gitignore b/lib/EASTL/.gitignore new file mode 100644 index 000000000..8d148cdce --- /dev/null +++ b/lib/EASTL/.gitignore @@ -0,0 +1,49 @@ +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 diff --git a/lib/EASTL/.gitmodules b/lib/EASTL/.gitmodules new file mode 100644 index 000000000..0b6afcc8b --- /dev/null +++ b/lib/EASTL/.gitmodules @@ -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 diff --git a/lib/EASTL/.p4ignore b/lib/EASTL/.p4ignore new file mode 100644 index 000000000..4bddd6145 --- /dev/null +++ b/lib/EASTL/.p4ignore @@ -0,0 +1,4 @@ +/.git/ +tags +.gitignore +cscope.out diff --git a/lib/EASTL/.travis.yml b/lib/EASTL/.travis.yml new file mode 100644 index 000000000..11b4a229c --- /dev/null +++ b/lib/EASTL/.travis.yml @@ -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 + diff --git a/lib/EASTL/3RDPARTYLICENSES.TXT b/lib/EASTL/3RDPARTYLICENSES.TXT new file mode 100644 index 000000000..41fe473c9 --- /dev/null +++ b/lib/EASTL/3RDPARTYLICENSES.TXT @@ -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. + +============================================================================== + diff --git a/lib/EASTL/CMakeLists.txt b/lib/EASTL/CMakeLists.txt new file mode 100644 index 000000000..e8700dc9c --- /dev/null +++ b/lib/EASTL/CMakeLists.txt @@ -0,0 +1,54 @@ +#------------------------------------------------------------------------------------------- +# 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) + +#------------------------------------------------------------------------------------------- +# 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(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) + +#------------------------------------------------------------------------------------------- +# Include dirs +#------------------------------------------------------------------------------------------- +target_include_directories(EASTL PUBLIC include) + +#------------------------------------------------------------------------------------------- +# Dependencies +#------------------------------------------------------------------------------------------- +if (NOT TARGET EABase) + add_subdirectory(test/packages/EABase) +endif() + +target_link_libraries(EASTL EABase) + diff --git a/lib/EASTL/CONTRIBUTING.md b/lib/EASTL/CONTRIBUTING.md new file mode 100644 index 000000000..036520e45 --- /dev/null +++ b/lib/EASTL/CONTRIBUTING.md @@ -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. diff --git a/lib/EASTL/LICENSE b/lib/EASTL/LICENSE new file mode 100644 index 000000000..1b112db64 --- /dev/null +++ b/lib/EASTL/LICENSE @@ -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. diff --git a/lib/EASTL/README.md b/lib/EASTL/README.md new file mode 100644 index 000000000..8548d9e95 --- /dev/null +++ b/lib/EASTL/README.md @@ -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. diff --git a/lib/EASTL/_config.yml b/lib/EASTL/_config.yml new file mode 100644 index 000000000..2f7efbeab --- /dev/null +++ b/lib/EASTL/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal \ No newline at end of file diff --git a/lib/EASTL/benchmark/CMakeLists.txt b/lib/EASTL/benchmark/CMakeLists.txt new file mode 100644 index 000000000..94bc97107 --- /dev/null +++ b/lib/EASTL/benchmark/CMakeLists.txt @@ -0,0 +1,93 @@ +#------------------------------------------------------------------------------------------- +# 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(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") + diff --git a/lib/EASTL/benchmark/source/BenchmarkAlgorithm.cpp b/lib/EASTL/benchmark/source/BenchmarkAlgorithm.cpp new file mode 100644 index 000000000..57e155ea7 --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkAlgorithm.cpp @@ -0,0 +1,1241 @@ +///////////////////////////////////////////////////////////////////////////// +// BenchmarkAlgorithm.cpp +// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +#include +#include +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + +#ifdef _MSC_VER + #pragma warning(disable: 4996) // Function call with parameters that may be unsafe +#endif + + +using namespace EA; + + +typedef std::vector StdVectorUChar; +typedef eastl::vector EaVectorUChar; + +typedef std::vector StdVectorSChar; +typedef eastl::vector EaVectorSChar; + +typedef std::vector StdVectorUint32; +typedef eastl::vector EaVectorUint32; + +typedef std::vector StdVectorUint64; +typedef eastl::vector EaVectorUint64; + +typedef std::vector StdVectorTO; +typedef eastl::vector EaVectorTO; + + +// We make a fake version of C++11 std::next, as some C++ compilers don't currently +// provide the C++11 next algorithm in their standard libraries. +namespace std__ +{ + template + inline InputIterator + next(InputIterator it, typename std::iterator_traits::difference_type n = 1) + { + std::advance(it, n); + return it; + } +} + + +namespace +{ + void TestFindEndStd(EA::StdC::Stopwatch& stopwatch, const std::string& sTest, const char* pSearchStringBegin, const char* pSearchStringEnd) + { + stopwatch.Restart(); + std::string::const_iterator it = std::find_end(sTest.begin(), sTest.end(), pSearchStringBegin, pSearchStringEnd); + stopwatch.Stop(); + if(it != sTest.end()) + sprintf(Benchmark::gScratchBuffer, "%c", *it); + } + + void TestFindEndEa(EA::StdC::Stopwatch& stopwatch, const eastl::string& sTest, const char* pSearchStringBegin, const char* pSearchStringEnd) + { + stopwatch.Restart(); + eastl::string::const_iterator it = eastl::find_end(sTest.begin(), sTest.end(), pSearchStringBegin, pSearchStringEnd); + stopwatch.Stop(); + if(it != sTest.end()) + sprintf(Benchmark::gScratchBuffer, "%c", *it); + } + + + + void TestSearchStd(EA::StdC::Stopwatch& stopwatch, const std::string& sTest, const char* pSearchStringBegin, const char* pSearchStringEnd) + { + stopwatch.Restart(); + std::string::const_iterator it = std::search(sTest.begin(), sTest.end(), pSearchStringBegin, pSearchStringEnd); + stopwatch.Stop(); + if(it != sTest.end()) + sprintf(Benchmark::gScratchBuffer, "%c", *it); + } + + void TestSearchEa(EA::StdC::Stopwatch& stopwatch, const eastl::string& sTest, const char* pSearchStringBegin, const char* pSearchStringEnd) + { + stopwatch.Restart(); + eastl::string::const_iterator it = eastl::search(sTest.begin(), sTest.end(), pSearchStringBegin, pSearchStringEnd); + stopwatch.Stop(); + if(it != sTest.end()) + sprintf(Benchmark::gScratchBuffer, "%c", *it); + } + + + + void TestSearchNStd(EA::StdC::Stopwatch& stopwatch, const std::string& sTest, int n, char c) + { + stopwatch.Restart(); + std::string::const_iterator it = std::search_n(sTest.begin(), sTest.end(), n, c); + stopwatch.Stop(); + if(it != sTest.end()) + sprintf(Benchmark::gScratchBuffer, "%c", *it); + } + + void TestSearchNEa(EA::StdC::Stopwatch& stopwatch, const eastl::string& sTest, int n, char c) + { + stopwatch.Restart(); + eastl::string::const_iterator it = eastl::search_n(sTest.begin(), sTest.end(), n, c); + stopwatch.Stop(); + if(it != sTest.end()) + sprintf(Benchmark::gScratchBuffer, "%c", *it); + } + + + + template + void TestUniqueStd(EA::StdC::Stopwatch& stopwatch, Container& c) + { + stopwatch.Restart(); + typename Container::iterator it = std::unique(c.begin(), c.end()); + stopwatch.Stop(); + c.erase(it, c.end()); + } + + template + void TestUniqueEa(EA::StdC::Stopwatch& stopwatch, Container& c) + { + stopwatch.Restart(); + typename Container::iterator it = eastl::unique(c.begin(), c.end()); + stopwatch.Stop(); + c.erase(it, c.end()); + } + + + + template + void TestMinElementStd(EA::StdC::Stopwatch& stopwatch, const Container& c) + { + stopwatch.Restart(); + const typename Container::const_iterator it = std::min_element(c.begin(), c.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &it); + } + + template + void TestMinElementEa(EA::StdC::Stopwatch& stopwatch, const Container& c) + { + stopwatch.Restart(); + const typename Container::const_iterator it = eastl::min_element(c.begin(), c.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &it); + } + + + + template + void TestCountStd(EA::StdC::Stopwatch& stopwatch, const Container& c) + { + stopwatch.Restart(); + const typename Container::difference_type n = std::count(c.begin(), c.end(), (typename Container::value_type)999999); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%d", (int)n); + } + + template + void TestCountEa(EA::StdC::Stopwatch& stopwatch, const Container& c) + { + stopwatch.Restart(); + const typename Container::difference_type n = eastl::count(c.begin(), c.end(), (typename Container::value_type)999999); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%d", (int)n); + } + + + + template + void TestAdjacentFindStd(EA::StdC::Stopwatch& stopwatch, const Container& c) + { + stopwatch.Restart(); + const typename Container::const_iterator it = std::adjacent_find(c.begin(), c.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &it); + } + + template + void TestAdjacentFindEa(EA::StdC::Stopwatch& stopwatch, const Container& c) + { + stopwatch.Restart(); + const typename Container::const_iterator it = eastl::adjacent_find(c.begin(), c.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &it); + } + + + + template + void TestLowerBoundStd(EA::StdC::Stopwatch& stopwatch, const Container& c, const typename Container::value_type* pBegin, const typename Container::value_type* pEnd) + { + + stopwatch.Restart(); + while(pBegin != pEnd) + { + typename Container::const_iterator it = std::lower_bound(c.begin(), c.end(), *pBegin++); + Benchmark::DoNothing(&it); + } + stopwatch.Stop(); + } + + template + void TestLowerBoundEa(EA::StdC::Stopwatch& stopwatch, Container& c, const typename Container::value_type* pBegin, const typename Container::value_type* pEnd) + { + stopwatch.Restart(); + while(pBegin != pEnd) + { + typename Container::const_iterator it = eastl::lower_bound(c.begin(), c.end(), *pBegin++); + Benchmark::DoNothing(&it); + } + stopwatch.Stop(); + } + + + + template + void TestUpperBoundStd(EA::StdC::Stopwatch& stopwatch, const Container& c, const typename Container::value_type* pBegin, const typename Container::value_type* pEnd) + { + stopwatch.Restart(); + while(pBegin != pEnd) + { + typename Container::const_iterator it = std::upper_bound(c.begin(), c.end(), *pBegin++); + Benchmark::DoNothing(&it); + } + stopwatch.Stop(); + } + + template + void TestUpperBoundEa(EA::StdC::Stopwatch& stopwatch, Container& c, const typename Container::value_type* pBegin, const typename Container::value_type* pEnd) + { + stopwatch.Restart(); + while(pBegin != pEnd) + { + typename Container::const_iterator it = eastl::upper_bound(c.begin(), c.end(), *pBegin++); + Benchmark::DoNothing(&it); + } + stopwatch.Stop(); + } + + + + template + void TestEqualRangeStd(EA::StdC::Stopwatch& stopwatch, const Container& c, const typename Container::value_type* pBegin, const typename Container::value_type* pEnd) + { + stopwatch.Restart(); + while(pBegin != pEnd) + { + std::pair itPair = std::equal_range(c.begin(), c.end(), *pBegin++); + + Benchmark::DoNothing(&itPair); + } + stopwatch.Stop(); + } + + template + void TestEqualRangeEa(EA::StdC::Stopwatch& stopwatch, Container& c, const typename Container::value_type* pBegin, const typename Container::value_type* pEnd) + { + stopwatch.Restart(); + while(pBegin != pEnd) + { + eastl::pair itPair = eastl::equal_range(c.begin(), c.end(), *pBegin++); + Benchmark::DoNothing(&itPair); + } + stopwatch.Stop(); + } + + + + template + void TestLexicographicalCompareStd(EA::StdC::Stopwatch& stopwatch, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) + { + stopwatch.Restart(); + const bool bResult = std::lexicographical_compare(first1, last1, first2, last2); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%d", bResult ? (int)1 : (int)0); + } + + template + void TestLexicographicalCompareEa(EA::StdC::Stopwatch& stopwatch, Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2) + { + stopwatch.Restart(); + const bool bResult = eastl::lexicographical_compare(first1, last1, first2, last2); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%d", bResult ? (int)1 : (int)0); + } + + + + template + void TestCopyStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last, OutputIterator result) + { + stopwatch.Restart(); + std::copy(first, last, result); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%d", (int)*first); + } + + template + void TestCopyEa(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last, OutputIterator result) + { + stopwatch.Restart(); + eastl::copy(first, last, result); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%d", (int)*first); + } + + + + template + void TestCopyBackwardStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last, OutputIterator result) + { + stopwatch.Restart(); + std::copy_backward(first, last, result); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%d", (int)*first); + } + + template + void TestCopyBackwardEa(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last, OutputIterator result) + { + stopwatch.Restart(); + eastl::copy_backward(first, last, result); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%d", (int)*first); + } + + + + template + void TestFillStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last, const Value& v) + { + stopwatch.Restart(); + std::fill(first, last, v); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*first); + } + + template + void TestFillEa(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last, const Value& v) + { + stopwatch.Restart(); + eastl::fill(first, last, v); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*first); + } + + + + template + void TestFillNStd(EA::StdC::Stopwatch& stopwatch, Iterator first, int n, const Value& v) + { + stopwatch.Restart(); + std::fill_n(first, n, v); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*first); + } + + template + void TestFillNEa(EA::StdC::Stopwatch& stopwatch, Iterator first, int n, const Value& v) + { + stopwatch.Restart(); + eastl::fill_n(first, n, v); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*first); + } + + + + template + void TestReverseStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last) + { + stopwatch.Restart(); + std::reverse(first, last); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*first); + } + + template + void TestReverseEa(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last) + { + stopwatch.Restart(); + eastl::reverse(first, last); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*first); + } + + + + template + void TestRotateStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator middle, Iterator last) + { + stopwatch.Restart(); + std::rotate(first, middle, last); // C++11 specifies that rotate has a return value, but not all std implementations return it. + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*first); + } + + template + void TestRotateEa(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator middle, Iterator last) + { + stopwatch.Restart(); + eastl::rotate(first, middle, last); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*first); + } + + template + void TestMergeStd(EA::StdC::Stopwatch& stopwatch, Iterator firstIn1, Iterator lastIn1, Iterator firstIn2, Iterator lastIn2, Iterator out) + { + stopwatch.Restart(); + std::merge(firstIn1, lastIn1, firstIn2, lastIn2, out); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*out); + } + + template + void TestMergeEa(EA::StdC::Stopwatch& stopwatch, Iterator firstIn1, Iterator lastIn1, Iterator firstIn2, Iterator lastIn2, Iterator out) + { + stopwatch.Restart(); + eastl::merge(firstIn1, lastIn1, firstIn2, lastIn2, out); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%p", &*out); + } +} // namespace + + + + +void BenchmarkAlgorithm1(EASTLTest_Rand& /*rng*/, EA::StdC::Stopwatch& stopwatch1, EA::StdC::Stopwatch& stopwatch2) +{ + { + std::string sTestStd; + eastl::string sTestEa; + const char* pSearchString1Begin = "AAA"; + const char* pSearchString1End = pSearchString1Begin + strlen(pSearchString1Begin); + const char* pSearchString2Begin = "BBB"; // This is something that doesn't exist searched string. + const char* pSearchString2End = pSearchString2Begin + strlen(pSearchString2Begin); + const char* pSearchString3Begin = "CCC"; + const char* pSearchString3End = pSearchString3Begin + strlen(pSearchString3Begin); + + for(int i = 0; i < 10000; i++) + sTestStd += "This is a test of the find_end algorithm. "; + sTestEa.assign(sTestStd.data(), (eastl_size_t)sTestStd.length()); + + for(int i = 0; i < 2; i++) + { + /////////////////////////////// + // Test find_end + /////////////////////////////// + + sTestStd.insert(sTestStd.size() * 15 / 16, pSearchString1Begin); + sTestEa.insert (sTestEa.size() * 15 / 16, pSearchString1Begin); + TestFindEndStd(stopwatch1, sTestStd, pSearchString1Begin, pSearchString1End); + TestFindEndEa (stopwatch2, sTestEa, pSearchString1Begin, pSearchString1End); + + if(i == 1) + Benchmark::AddResult("algorithm/find_end/string/end", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + sTestStd.insert(sTestStd.size() / 2, pSearchString2Begin); + sTestEa.insert (sTestEa.size() / 2, pSearchString2Begin); + TestFindEndStd(stopwatch1, sTestStd, pSearchString2Begin, pSearchString2End); + TestFindEndEa (stopwatch2, sTestEa, pSearchString2Begin, pSearchString2End); + + if(i == 1) + Benchmark::AddResult("algorithm/find_end/string/middle", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestFindEndStd(stopwatch1, sTestStd, pSearchString3Begin, pSearchString3End); + TestFindEndEa (stopwatch2, sTestEa, pSearchString3Begin, pSearchString3End); + + if(i == 1) + Benchmark::AddResult("algorithm/find_end/string/none", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + /////////////////////////////// + // Test search + /////////////////////////////// + TestSearchStd(stopwatch1, sTestStd, pSearchString1Begin, pSearchString1End); + TestSearchEa (stopwatch2, sTestEa, pSearchString1Begin, pSearchString1End); + + if(i == 1) + Benchmark::AddResult("algorithm/search/string", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test search_n + /////////////////////////////// + TestSearchNStd(stopwatch1, sTestStd, 3, 'A'); + TestSearchNEa (stopwatch2, sTestEa, 3, 'A'); + + if(i == 1) + Benchmark::AddResult("algorithm/search_n/string", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test adjacent_find + /////////////////////////////// + + } + } +} + + +void BenchmarkAlgorithm2(EASTLTest_Rand& rng, EA::StdC::Stopwatch& stopwatch1, EA::StdC::Stopwatch& stopwatch2) +{ + { + StdVectorUint32 stdVectorUint32; + EaVectorUint32 eaVectorUint32; + + StdVectorUint64 stdVectorUint64; + EaVectorUint64 eaVectorUint64; + + StdVectorTO stdVectorTO; + EaVectorTO eaVectorTO; + + for(int i = 0; i < 2; i++) + { + stdVectorUint32.clear(); + eaVectorUint32.clear(); + + for(int j = 0; j < 100000; j++) + { + stdVectorUint32.push_back(j); + eaVectorUint32.push_back(j); + stdVectorUint64.push_back(j); + eaVectorUint64.push_back(j); + stdVectorTO.push_back(TestObject(j)); + eaVectorTO.push_back(TestObject(j)); + + if((rng() % 16) == 0) + { + stdVectorUint32.push_back(i); + eaVectorUint32.push_back(i); + stdVectorUint64.push_back(j); + eaVectorUint64.push_back(j); + stdVectorTO.push_back(TestObject(j)); + eaVectorTO.push_back(TestObject(j)); + + if((rng() % 16) == 0) + { + stdVectorUint32.push_back(i); + eaVectorUint32.push_back(i); + stdVectorUint64.push_back(j); + eaVectorUint64.push_back(j); + stdVectorTO.push_back(TestObject(j)); + eaVectorTO.push_back(TestObject(j)); + } + } + } + + + /////////////////////////////// + // Test unique + /////////////////////////////// + + TestUniqueStd(stopwatch1, stdVectorUint32); + TestUniqueEa (stopwatch2, eaVectorUint32); + + if(i == 1) + Benchmark::AddResult("algorithm/unique/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestUniqueStd(stopwatch1, stdVectorUint64); + TestUniqueEa (stopwatch2, eaVectorUint64); + + if(i == 1) + Benchmark::AddResult("algorithm/unique/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestUniqueStd(stopwatch1, stdVectorTO); + TestUniqueEa (stopwatch2, eaVectorTO); + + if(i == 1) + Benchmark::AddResult("algorithm/unique/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + /////////////////////////////// + // Test min_element + /////////////////////////////// + + TestMinElementStd(stopwatch1, stdVectorTO); + TestMinElementEa (stopwatch2, eaVectorTO); + + if(i == 1) + Benchmark::AddResult("algorithm/min_element/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + /////////////////////////////// + // Test count + /////////////////////////////// + + TestCountStd(stopwatch1, stdVectorUint64); + TestCountEa (stopwatch2, eaVectorUint64); + + if(i == 1) + Benchmark::AddResult("algorithm/count/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + /////////////////////////////// + // Test adjacent_find + /////////////////////////////// + + // Due to the above unique testing, the container should container unique elements. Let's change that. + stdVectorTO[stdVectorTO.size() - 2] = stdVectorTO[stdVectorTO.size() - 1]; + eaVectorTO[eaVectorTO.size() - 2] = eaVectorTO[eaVectorTO.size() - 1]; + TestAdjacentFindStd(stopwatch1, stdVectorTO); + TestAdjacentFindEa (stopwatch2, eaVectorTO); + + if(i == 1) + Benchmark::AddResult("algorithm/adj_find/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + /////////////////////////////// + // Test lower_bound + /////////////////////////////// + + // Sort the containers for the following tests. + std::sort(stdVectorTO.begin(), stdVectorTO.end()); + eaVectorTO.assign(&stdVectorTO[0], &stdVectorTO[0] + stdVectorTO.size()); + + TestLowerBoundStd(stopwatch1, stdVectorTO, &stdVectorTO[0], &stdVectorTO[0] + stdVectorTO.size()); + TestLowerBoundEa (stopwatch2, eaVectorTO, &eaVectorTO[0], &eaVectorTO[0] + eaVectorTO.size()); + + if(i == 1) + Benchmark::AddResult("algorithm/lower_bound/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test upper_bound + /////////////////////////////// + + std::sort(stdVectorUint32.begin(), stdVectorUint32.end()); + eaVectorUint32.assign(&stdVectorUint32[0], &stdVectorUint32[0] + stdVectorUint32.size()); + + TestUpperBoundStd(stopwatch1, stdVectorUint32, &stdVectorUint32[0], &stdVectorUint32[0] + stdVectorUint32.size()); + TestUpperBoundEa (stopwatch2, eaVectorUint32, &eaVectorUint32[0], &eaVectorUint32[0] + eaVectorUint32.size()); + + if(i == 1) + Benchmark::AddResult("algorithm/upper_bound/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test equal_range + /////////////////////////////// + + // VS2010 (and later versions?) is extremely slow executing this in debug builds. It can take minutes for a + // single TestEqualRangeStd call to complete. It's so slow that it's nearly pointless to execute. + #if !defined(_MSC_VER) || (_MSC_VER < 1600) || !defined(_ITERATOR_DEBUG_LEVEL) || (_ITERATOR_DEBUG_LEVEL < 2) + std::sort(stdVectorUint64.begin(), stdVectorUint64.end()); + eaVectorUint64.assign(&stdVectorUint64[0], &stdVectorUint64[0] + stdVectorUint64.size()); + + TestEqualRangeStd(stopwatch1, stdVectorUint64, &stdVectorUint64[0], &stdVectorUint64[0] + stdVectorUint64.size()); + TestEqualRangeEa (stopwatch2, eaVectorUint64, &eaVectorUint64[0], &eaVectorUint64[0] + eaVectorUint64.size()); + + if(i == 1) + Benchmark::AddResult("algorithm/equal_range/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + #endif + } + } +} + + +void BenchmarkAlgorithm3(EASTLTest_Rand& /*rng*/, EA::StdC::Stopwatch& stopwatch1, EA::StdC::Stopwatch& stopwatch2) +{ + { + StdVectorUChar stdVectorUChar1(100000); + StdVectorUChar stdVectorUChar2(100000); + EaVectorUChar eaVectorUChar1(100000); + EaVectorUChar eaVectorUChar2(100000); + + StdVectorSChar stdVectorSChar1(100000); + StdVectorSChar stdVectorSChar2(100000); + EaVectorSChar eaVectorSChar1(100000); + EaVectorSChar eaVectorSChar2(100000); + + StdVectorTO stdVectorTO1(100000); + StdVectorTO stdVectorTO2(100000); + EaVectorTO eaVectorTO1(100000); + EaVectorTO eaVectorTO2(100000); + + // All these containers should have values of zero in them. + + for(int i = 0; i < 2; i++) + { + /////////////////////////////// + // Test lexicographical_compare + /////////////////////////////// + + TestLexicographicalCompareStd(stopwatch1, stdVectorUChar1.begin(), stdVectorUChar1.end(), stdVectorUChar2.begin(), stdVectorUChar2.end()); + TestLexicographicalCompareEa (stopwatch2, eaVectorUChar1.begin(), eaVectorUChar2.end(), eaVectorUChar2.begin(), eaVectorUChar2.end()); + + if(i == 1) + Benchmark::AddResult("algorithm/lex_cmp/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestLexicographicalCompareStd(stopwatch1, &stdVectorSChar1[0], &stdVectorSChar1[0] + stdVectorSChar1.size(), &stdVectorSChar2[0], &stdVectorSChar2[0] + stdVectorSChar2.size()); + TestLexicographicalCompareEa (stopwatch2, &eaVectorSChar1[0], &eaVectorSChar1[0] + eaVectorSChar1.size(), &eaVectorSChar2[0], &eaVectorSChar2[0] + eaVectorSChar2.size()); + + if(i == 1) + Benchmark::AddResult("algorithm/lex_cmp/schar[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestLexicographicalCompareStd(stopwatch1, stdVectorTO1.begin(), stdVectorTO1.end(), stdVectorTO2.begin(), stdVectorTO2.end()); + TestLexicographicalCompareEa (stopwatch2, eaVectorTO1.begin(), eaVectorTO1.end(), eaVectorTO2.begin(), eaVectorTO2.end()); + + if(i == 1) + Benchmark::AddResult("algorithm/lex_cmp/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } + +} + + +void BenchmarkAlgorithm4(EASTLTest_Rand& /*rng*/, EA::StdC::Stopwatch& stopwatch1, EA::StdC::Stopwatch& stopwatch2) +{ + { + std::vector stdVectorUint321(10000); + std::vector stdVectorUint322(10000); + eastl::vector eaVectorUint321(10000); + eastl::vector eaVectorUint322(10000); + + std::vector stdVectorUint64(100000); + eastl::vector eaVectorUint64(100000); + + + for(int i = 0; i < 2; i++) + { + /////////////////////////////// + // Test copy + /////////////////////////////// + + TestCopyStd(stopwatch1, stdVectorUint321.begin(), stdVectorUint321.end(), stdVectorUint322.begin()); + TestCopyEa (stopwatch2, eaVectorUint321.begin(), eaVectorUint321.end(), eaVectorUint322.begin()); + + if(i == 1) + Benchmark::AddResult("algorithm/copy/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test copy_backward + /////////////////////////////// + + TestCopyBackwardStd(stopwatch1, stdVectorUint321.begin(), stdVectorUint321.end(), stdVectorUint322.end()); + TestCopyBackwardEa (stopwatch2, eaVectorUint321.begin(), eaVectorUint321.end(), eaVectorUint322.end()); + + if(i == 1) + Benchmark::AddResult("algorithm/copy_backward/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test fill + /////////////////////////////// + + TestFillStd(stopwatch1, stdVectorUint64.begin(), stdVectorUint64.end(), UINT64_C(37)); + TestFillEa (stopwatch2, eaVectorUint64.begin(), eaVectorUint64.end(), UINT64_C(37)); + TestFillStd(stopwatch1, stdVectorUint64.begin(), stdVectorUint64.end(), UINT64_C(37)); // Intentionally do this a second time, as we are finding + TestFillEa (stopwatch2, eaVectorUint64.begin(), eaVectorUint64.end(), UINT64_C(37)); // the results are inconsistent otherwise. + if(EA::StdC::Memcheck64(&eaVectorUint64[0], UINT64_C(37), eaVectorUint64.size())) + EA::UnitTest::Report("eastl algorithm 64 bit fill failure."); + + if(i == 1) + Benchmark::AddResult("algorithm/fill/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test fill_n + /////////////////////////////// + + TestFillNStd(stopwatch1, stdVectorUint64.begin(), (int)stdVectorUint64.size(), UINT64_C(37)); + TestFillNEa (stopwatch2, eaVectorUint64.begin(), (int) eaVectorUint64.size(), UINT64_C(37)); + TestFillNStd(stopwatch1, stdVectorUint64.begin(), (int)stdVectorUint64.size(), UINT64_C(37)); // Intentionally do this a second time, as we are finding + TestFillNEa (stopwatch2, eaVectorUint64.begin(), (int) eaVectorUint64.size(), UINT64_C(37)); // the results are inconsistent otherwise. + + if(i == 1) + Benchmark::AddResult("algorithm/fill_n/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } +} + + +void BenchmarkAlgorithm5(EASTLTest_Rand& /*rng*/, EA::StdC::Stopwatch& stopwatch1, EA::StdC::Stopwatch& stopwatch2) +{ + { + std::vector stdVectorVoid(100000); + eastl::vector eaVectorVoid(100000); + + std::vector stdVectorChar(100000); + eastl::vector eaVectorChar(100000); + + std::vector stdVectorBool(100000); + eastl::vector eaVectorBool(100000); + + for(int i = 0; i < 2; i++) + { + TestFillStd(stopwatch1, stdVectorVoid.begin(), stdVectorVoid.end(), (void*)NULL); + TestFillEa (stopwatch2, eaVectorVoid.begin(), eaVectorVoid.end(), (void*)NULL); + + if(i == 1) + Benchmark::AddResult("algorithm/fill/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestFillStd(stopwatch1, &stdVectorChar[0], &stdVectorChar[0] + stdVectorChar.size(), 'd'); // Intentionally use ' ' and not casted to any type. + TestFillEa (stopwatch2, eaVectorChar.data(), eaVectorChar.data() + eaVectorChar.size(), 'd'); + TestFillStd(stopwatch1, &stdVectorChar[0], &stdVectorChar[0] + stdVectorChar.size(), 'd'); // Intentionally do this a second time, as we are finding + TestFillEa (stopwatch2, eaVectorChar.data(), eaVectorChar.data() + eaVectorChar.size(), 'd'); // the results are inconsistent otherwise. + + if(i == 1) + Benchmark::AddResult("algorithm/fill/char[]/'d'", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestFillStd(stopwatch1, stdVectorChar.begin(), stdVectorChar.end(), (char)'d'); + TestFillEa (stopwatch2, eaVectorChar.begin(), eaVectorChar.end(), (char)'d'); + TestFillStd(stopwatch1, stdVectorChar.begin(), stdVectorChar.end(), (char)'d'); // Intentionally do this a second time, as we are finding + TestFillEa (stopwatch2, eaVectorChar.begin(), eaVectorChar.end(), (char)'d'); // the results are inconsistent otherwise. + + if(i == 1) + Benchmark::AddResult("algorithm/fill/vector/'d'", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestFillStd(stopwatch1, stdVectorChar.begin(), stdVectorChar.end(), (char)0); + TestFillEa (stopwatch2, eaVectorChar.begin(), eaVectorChar.end(), (char)0); + TestFillStd(stopwatch1, stdVectorChar.begin(), stdVectorChar.end(), (char)0); // Intentionally do this a second time, as we are finding + TestFillEa (stopwatch2, eaVectorChar.begin(), eaVectorChar.end(), (char)0); // the results are inconsistent otherwise. + + if(i == 1) + Benchmark::AddResult("algorithm/fill/vector/0", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestFillStd(stopwatch1, eaVectorBool.data(), eaVectorBool.data() + eaVectorBool.size(), false); // Intentionally use eaVectorBool for the array. + TestFillEa (stopwatch2, eaVectorBool.data(), eaVectorBool.data() + eaVectorBool.size(), false); + TestFillStd(stopwatch1, eaVectorBool.data(), eaVectorBool.data() + eaVectorBool.size(), false); + TestFillEa (stopwatch2, eaVectorBool.data(), eaVectorBool.data() + eaVectorBool.size(), false); + + if(i == 1) + Benchmark::AddResult("algorithm/fill/bool[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test fill_n + /////////////////////////////// + + TestFillNStd(stopwatch1, eaVectorChar.data(), (int) eaVectorChar.size(), 'd'); // Intentionally use eaVectorBool for the array. + TestFillNEa (stopwatch2, eaVectorChar.data(), (int) eaVectorChar.size(), 'd'); + TestFillNStd(stopwatch1, eaVectorChar.data(), (int) eaVectorChar.size(), 'd'); // Intentionally do this a second time, as we are finding + TestFillNEa (stopwatch2, eaVectorChar.data(), (int) eaVectorChar.size(), 'd'); // the results are inconsistent otherwise. + + if(i == 1) + Benchmark::AddResult("algorithm/fill_n/char[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestFillNStd(stopwatch1, eaVectorBool.data(), (int) eaVectorBool.size(), false); // Intentionally use eaVectorBool for the array. + TestFillNEa (stopwatch2, eaVectorBool.data(), (int) eaVectorBool.size(), false); + TestFillNStd(stopwatch1, eaVectorBool.data(), (int) eaVectorBool.size(), false); // Intentionally do this a second time, as we are finding + TestFillNEa (stopwatch2, eaVectorBool.data(), (int) eaVectorBool.size(), false); // the results are inconsistent otherwise. + + if(i == 1) + Benchmark::AddResult("algorithm/fill_n/bool[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } +} + + +void BenchmarkAlgorithm6(EASTLTest_Rand& /*rng*/, EA::StdC::Stopwatch& stopwatch1, EA::StdC::Stopwatch& stopwatch2) +{ + // We allocate this on the heap because some platforms don't always have enough stack space for this. + std::vector* pstdVectorLP1 = new std::vector(100); + std::vector* pstdVectorLP2 = new std::vector(100); + eastl::vector* peaVectorLP1 = new eastl::vector(100); + eastl::vector* peaVectorLP2 = new eastl::vector(100); + + // Aliases. + std::vector& stdVectorLP1 = *pstdVectorLP1; + std::vector& stdVectorLP2 = *pstdVectorLP2; + eastl::vector& eaVectorLP1 = *peaVectorLP1; + eastl::vector& eaVectorLP2 = *peaVectorLP2; + + for(int i = 0; i < 2; i++) + { + /////////////////////////////// + // Test copy + /////////////////////////////// + + TestCopyStd(stopwatch1, stdVectorLP1.begin(), stdVectorLP1.end(), stdVectorLP2.begin()); + TestCopyEa (stopwatch2, eaVectorLP1.begin(), eaVectorLP1.end(), eaVectorLP2.begin()); + + if(i == 1) + Benchmark::AddResult("algorithm/copy/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test copy_backward + /////////////////////////////// + + TestCopyBackwardStd(stopwatch1, stdVectorLP1.begin(), stdVectorLP1.end(), stdVectorLP2.end()); + TestCopyBackwardEa (stopwatch2, eaVectorLP1.begin(), eaVectorLP1.end(), eaVectorLP2.end()); + + if(i == 1) + Benchmark::AddResult("algorithm/copy_backward/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + + delete pstdVectorLP1; + delete pstdVectorLP2; + delete peaVectorLP1; + delete peaVectorLP2; +} + + +void BenchmarkAlgorithm7(EASTLTest_Rand& /*rng*/, EA::StdC::Stopwatch& stopwatch1, EA::StdC::Stopwatch& stopwatch2) +{ + { + std::list stdListTO(10000); + eastl::list eaListTO(10000); + + std::vector stdVectorTO(10000); + eastl::vector eaVectorTO(10000); + + for(int i = 0; i < 2; i++) + { + /////////////////////////////// + // Test reverse + /////////////////////////////// + + TestReverseStd(stopwatch1, stdListTO.begin(), stdListTO.end()); + TestReverseEa (stopwatch2, eaListTO.begin(), eaListTO.end()); + + if(i == 1) + Benchmark::AddResult("algorithm/reverse/list", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestReverseStd(stopwatch1, stdVectorTO.begin(), stdVectorTO.end()); + TestReverseEa (stopwatch2, eaVectorTO.begin(), eaVectorTO.end()); + + if(i == 1) + Benchmark::AddResult("algorithm/reverse/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } + + { + // Create some containers and seed them with incremental values (i.e. 0, 1, 2, 3...). + eastl::slist eaSlistIntLarge(10000); + eastl::generate(eaSlistIntLarge.begin(), eaSlistIntLarge.end(), GenerateIncrementalIntegers()); + + + std::vector< SizedPOD<32> > stdVectorLargePod32(10000); + for(std::vector< SizedPOD<32> >::iterator it = stdVectorLargePod32.begin(); it != stdVectorLargePod32.end(); ++it) + memset(&*it, 0, sizeof(SizedPOD<32>)); + eastl::vector< SizedPOD<32> > eaVectorLargePod32(10000); + for(eastl::vector< SizedPOD<32> >::iterator it = eaVectorLargePod32.begin(); it != eaVectorLargePod32.end(); ++it) + memset(&*it, 0, sizeof(SizedPOD<32>)); + + std::list stdListIntLarge(10000); + eastl::generate(stdListIntLarge.begin(), stdListIntLarge.end(), GenerateIncrementalIntegers()); + + eastl::list eaListIntLarge(10000); + eastl::generate(eaListIntLarge.begin(), eaListIntLarge.end(), GenerateIncrementalIntegers()); + + + std::vector stdVectorIntLarge(10000); + eastl::generate(stdVectorIntLarge.begin(), stdVectorIntLarge.end(), GenerateIncrementalIntegers()); + + eastl::vector eaVectorIntLarge(10000); + eastl::generate(eaVectorIntLarge.begin(), eaVectorIntLarge.end(), GenerateIncrementalIntegers()); + + + std::list stdListIntSmall(10); + eastl::generate(stdListIntLarge.begin(), stdListIntLarge.end(), GenerateIncrementalIntegers()); + + eastl::list eaListIntSmall(10); + eastl::generate(eaListIntLarge.begin(), eaListIntLarge.end(), GenerateIncrementalIntegers()); + + + std::vector stdVectorIntSmall(10); + eastl::generate(stdVectorIntLarge.begin(), stdVectorIntLarge.end(), GenerateIncrementalIntegers()); + + eastl::vector eaVectorIntSmall(10); + eastl::generate(eaVectorIntLarge.begin(), eaVectorIntLarge.end(), GenerateIncrementalIntegers()); + + + + std::list stdListTOLarge(10000); + eastl::generate(stdListTOLarge.begin(), stdListTOLarge.end(), GenerateIncrementalIntegers()); + + eastl::list eaListTOLarge(10000); + eastl::generate(eaListTOLarge.begin(), eaListTOLarge.end(), GenerateIncrementalIntegers()); + + + std::vector stdVectorTOLarge(10000); + eastl::generate(stdVectorTOLarge.begin(), stdVectorTOLarge.end(), GenerateIncrementalIntegers()); + + eastl::vector eaVectorTOLarge(10000); + eastl::generate(eaVectorTOLarge.begin(), eaVectorTOLarge.end(), GenerateIncrementalIntegers()); + + + std::list stdListTOSmall(10); + eastl::generate(stdListTOSmall.begin(), stdListTOSmall.end(), GenerateIncrementalIntegers()); + + eastl::list eaListTOSmall(10); + eastl::generate(eaListTOSmall.begin(), eaListTOSmall.end(), GenerateIncrementalIntegers()); + + + std::vector stdVectorTOSmall(10); + eastl::generate(stdVectorTOSmall.begin(), stdVectorTOSmall.end(), GenerateIncrementalIntegers()); + + eastl::vector eaVectorTOSmall(10); + eastl::generate(eaVectorTOSmall.begin(), eaVectorTOSmall.end(), GenerateIncrementalIntegers()); + + + for(int i = 0; i < 2; i++) + { + /////////////////////////////// + // Test reverse + /////////////////////////////// + + // There is no guaranteed Standard Library forward_list or slist. + TestRotateEa (stopwatch2, eaSlistIntLarge.begin(), eastl::next( eaSlistIntLarge.begin(), (eaSlistIntLarge.size() / 2) - 1), eaSlistIntLarge.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/slist large", stopwatch1.GetUnits(), 0 /* untested */, stopwatch2.GetElapsedTime()); + + + + TestRotateStd(stopwatch1, stdVectorLargePod32.begin(), std__::next(stdVectorLargePod32.begin(), (stdVectorLargePod32.size() / 2) - 1), stdVectorLargePod32.end()); + TestRotateEa (stopwatch2, eaVectorLargePod32.begin(), eastl::next( eaVectorLargePod32.begin(), (eaVectorLargePod32.size() / 2) - 1), eaVectorLargePod32.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/vector> large", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + + TestRotateStd(stopwatch1, stdListIntLarge.begin(), std__::next(stdListIntLarge.begin(), (stdListIntLarge.size() / 2) - 1), stdListIntLarge.end()); + TestRotateEa (stopwatch2, eaListIntLarge.begin(), eastl::next( eaListIntLarge.begin(), (eaListIntLarge.size() / 2) - 1), eaListIntLarge.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/list large", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestRotateStd(stopwatch1, stdVectorIntLarge.begin(), std__::next(stdVectorIntLarge.begin(), (stdVectorIntLarge.size() / 2) - 1), stdVectorIntLarge.end()); + TestRotateEa (stopwatch2, eaVectorIntLarge.begin(), eastl::next( eaVectorIntLarge.begin(), (eaVectorIntLarge.size() / 2) - 1), eaVectorIntLarge.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + TestRotateStd(stopwatch1, stdListIntSmall.begin(), std__::next(stdListIntSmall.begin(), (stdListIntSmall.size() / 2) - 1), stdListIntSmall.end()); + TestRotateEa (stopwatch2, eaListIntSmall.begin(), eastl::next( eaListIntSmall.begin(), (eaListIntSmall.size() / 2) - 1), eaListIntSmall.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/list small", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestRotateStd(stopwatch1, stdVectorIntSmall.begin(), std__::next(stdVectorIntSmall.begin(), (stdVectorIntSmall.size() / 2) - 1), stdVectorIntSmall.end()); + TestRotateEa (stopwatch2, eaVectorIntSmall.begin(), eastl::next( eaVectorIntSmall.begin(), (eaVectorIntSmall.size() / 2) - 1), eaVectorIntSmall.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + TestRotateStd(stopwatch1, stdListTOLarge.begin(), std__::next(stdListTOLarge.begin(), (stdListTOLarge.size() / 2) - 1), stdListTOLarge.end()); + TestRotateEa (stopwatch2, eaListTOLarge.begin(), eastl::next( eaListTOLarge.begin(), (eaListTOLarge.size() / 2) - 1), eaListTOLarge.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/list", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestRotateStd(stopwatch1, stdVectorTOLarge.begin(), std__::next(stdVectorTOLarge.begin(), (stdVectorTOLarge.size() / 2) - 1), stdVectorTOLarge.end()); + TestRotateEa (stopwatch2, eaVectorTOLarge.begin(), eastl::next( eaVectorTOLarge.begin(), (eaVectorTOLarge.size() / 2) - 1), eaVectorTOLarge.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + TestRotateStd(stopwatch1, stdListTOSmall.begin(), std__::next(stdListTOSmall.begin(), (stdListTOSmall.size() / 2) - 1), stdListTOSmall.end()); + TestRotateEa (stopwatch2, eaListTOSmall.begin(), eastl::next( eaListTOSmall.begin(), (eaListTOSmall.size() / 2) - 1), eaListTOSmall.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/list", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestRotateStd(stopwatch1, stdVectorTOSmall.begin(), std__::next(stdVectorTOSmall.begin(), (stdVectorTOSmall.size() / 2) - 1), stdVectorTOSmall.end()); + TestRotateEa (stopwatch2, eaVectorTOSmall.begin(), eastl::next( eaVectorTOSmall.begin(), (eaVectorTOSmall.size() / 2) - 1), eaVectorTOSmall.end()); + if(i == 1) + Benchmark::AddResult("algorithm/rotate/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } +} + +void BenchmarkAlgorithm8(EASTLTest_Rand& rng, EA::StdC::Stopwatch& stopwatch1, EA::StdC::Stopwatch& stopwatch2) +{ + const uint32_t ElementCount = 10000; + + eastl::vector srcVecA(ElementCount); + eastl::vector srcVecB(ElementCount); + + std::vector stdVecAInt(ElementCount); + std::vector stdVecBInt(ElementCount); + std::vector stdVecOutInt(2 * ElementCount); + std::vector stdVecATestObject(ElementCount); + std::vector stdVecBTestObject(ElementCount); + std::vector stdVecOutTestObject(2 * ElementCount); + + eastl::vector eaVecAInt(ElementCount); + eastl::vector eaVecBInt(ElementCount); + eastl::vector eaVecOutInt(2 * ElementCount); + eastl::vector eaVecATestObject(ElementCount); + eastl::vector eaVecBTestObject(ElementCount); + eastl::vector eaVecOutTestObject(2 * ElementCount); + + // Note: + // In some cases the compiler may generate branch free code for the loop body of merge. + // In this situation the performance of merging data that has a random merge selection (i.e. the chance that the smallest + // element is taken from the first or second list is essentially random) is the same as merging data where the choice of + // which list has the smallest element is predictable. + // However, if the compiler doesn't generate branch free code, then the performance of merge will suffer from branch + // misprediction when merging random data and will benefit greatly when misprediction is rare. + // This benchmark is aimed at highlighting what sort of code is being generated, and also showing the impact of + // predictability of the comparisons performed during merge. The branch predictablity /can/ have a large impact + // on merge sort performance. + + // 'unpred' is the case where the comparison is unpredictable + // 'pred' is the case where the comparison is mostly predictable + const char* patternDescriptions[][2] = + { + { + "algorithm/merge/vector (unpred)", + "algorithm/merge/vector (pred)", + }, + { + "algorithm/merge/vector (unpred)", + "algorithm/merge/vector (pred)", + }, + }; + + enum Pattern + { + P_Random, + P_Predictable, + P_Count + }; + + for (int pattern = 0; pattern < P_Count; pattern++) + { + if (pattern == P_Random) + { + eastl::generate(srcVecA.begin(), srcVecA.end(), [&]{ return int(rng()); }); + eastl::sort(srcVecA.begin(), srcVecA.end()); + eastl::generate(srcVecB.begin(), srcVecB.end(), [&] { return int(rng()); }); + eastl::sort(srcVecB.begin(), srcVecB.end()); + } + else if (pattern == P_Predictable) + { + // The data pattern means that a simple/naive algorithm will select 'runLen' values + // from one list, and then 'runLen' values from the other list (alternating back and forth). + // Of course, a merge algorithm that is more complicated might have a different order of + // comparison. + const int runLen = 32; + for (int i = 0; i < ElementCount; i++) + { + int baseValue = ((i / runLen) * 2 * runLen) + (i % (runLen)); + srcVecA[i] = baseValue; + srcVecB[i] = baseValue + runLen; + } + } + + /////////////////////////////// + // Test merge + /////////////////////////////// + for (int i = 0; i < 2; i++) + { + eastl::copy(srcVecA.begin(), srcVecA.end(), stdVecAInt.begin()); + eastl::copy(srcVecB.begin(), srcVecB.end(), stdVecBInt.begin()); + eastl::copy(srcVecA.begin(), srcVecA.end(), eaVecAInt.begin()); + eastl::copy(srcVecB.begin(), srcVecB.end(), eaVecBInt.begin()); + TestMergeStd(stopwatch1, stdVecAInt.begin(), stdVecAInt.end(), stdVecBInt.begin(), stdVecBInt.end(), stdVecOutInt.begin()); + TestMergeEa(stopwatch2, eaVecAInt.begin(), eaVecAInt.end(), eaVecBInt.begin(), eaVecBInt.end(), eaVecOutInt.begin()); + + if (i == 1) + { + Benchmark::AddResult(patternDescriptions[0][pattern], stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + + for (int j = 0; j < ElementCount; j++) + { + stdVecATestObject[j] = TestObject(srcVecA[j]); + stdVecBTestObject[j] = TestObject(srcVecB[j]); + eaVecATestObject[j] = TestObject(srcVecA[j]); + eaVecBTestObject[j] = TestObject(srcVecB[j]); + } + TestMergeStd(stopwatch1, stdVecATestObject.begin(), stdVecATestObject.end(), stdVecBTestObject.begin(), stdVecBTestObject.end(), stdVecOutTestObject.begin()); + TestMergeEa(stopwatch2, eaVecATestObject.begin(), eaVecATestObject.end(), eaVecBTestObject.begin(), eaVecBTestObject.end(), eaVecOutTestObject.begin()); + + if (i == 1) + { + Benchmark::AddResult(patternDescriptions[1][pattern], stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } + } + +} + + + +void BenchmarkAlgorithm() +{ + EASTLTest_Printf("Algorithm\n"); + + EASTLTest_Rand rng(EA::UnitTest::GetRandSeed()); + EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles); + EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles); + + BenchmarkAlgorithm1(rng, stopwatch1, stopwatch2); + BenchmarkAlgorithm2(rng, stopwatch1, stopwatch2); + BenchmarkAlgorithm3(rng, stopwatch1, stopwatch2); + BenchmarkAlgorithm4(rng, stopwatch1, stopwatch2); + BenchmarkAlgorithm5(rng, stopwatch1, stopwatch2); + BenchmarkAlgorithm6(rng, stopwatch1, stopwatch2); + BenchmarkAlgorithm7(rng, stopwatch1, stopwatch2); + BenchmarkAlgorithm8(rng, stopwatch1, stopwatch2); +} + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkBitset.cpp b/lib/EASTL/benchmark/source/BenchmarkBitset.cpp new file mode 100644 index 000000000..680622bdc --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkBitset.cpp @@ -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 +#include + + +EA_DISABLE_ALL_VC_WARNINGS() +#include +EA_RESTORE_ALL_VC_WARNINGS() + + +using namespace EA; + + +namespace +{ + template + 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 + 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 + 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 + 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 + 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 + 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 + 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); + } + } +} + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkDeque.cpp b/lib/EASTL/benchmark/source/BenchmarkDeque.cpp new file mode 100644 index 000000000..d3c69dea4 --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkDeque.cpp @@ -0,0 +1,342 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + #pragma warning(push, 0) + #pragma warning(disable: 4350) // behavior change: X called instead of Y +#endif +#include +#include +#include +#include +#include +#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 StdDeque; +typedef eastl::deque 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 + void TestPushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector& 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 + void TestPushFront(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector& 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 + 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 + 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 + 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 + 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 + 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 + 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 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 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/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/push_front", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test operator[] + /////////////////////////////// + + TestBracket(stopwatch1, stdDeque); + TestBracket(stopwatch2, eaDeque); + + if(i == 1) + Benchmark::AddResult("deque/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test iteration + /////////////////////////////// + + TestIteration(stopwatch1, stdDeque); + TestIteration(stopwatch2, eaDeque); + + if(i == 1) + Benchmark::AddResult("deque/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test find() + /////////////////////////////// + + TestFind(stopwatch1, stdDeque); + TestFind(stopwatch2, eaDeque); + + if(i == 1) + Benchmark::AddResult("deque/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/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + #endif + + + /////////////////////////////// + // Test insert + /////////////////////////////// + + TestInsert(stopwatch1, stdDeque); + TestInsert(stopwatch2, eaDeque); + + if(i == 1) + Benchmark::AddResult("deque/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test erase + /////////////////////////////// + + TestErase(stopwatch1, stdDeque); + TestErase(stopwatch2, eaDeque); + + if(i == 1) + Benchmark::AddResult("deque/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } +} + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkHash.cpp b/lib/EASTL/benchmark/source/BenchmarkHash.cpp new file mode 100644 index 000000000..35470e760 --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkHash.cpp @@ -0,0 +1,469 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include +#include + + + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + + + +using namespace EA; + + +// HashString8 +// +// We define a string +// +template +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; +using StdMapStrUint32 = std::unordered_map>; + +using EaMapUint32TO = eastl::hash_map; +using EaMapStrUint32 = eastl::hash_map>; + + +namespace +{ + template + void TestInsert(EA::StdC::Stopwatch& stopwatch, Container& c, const Value* pArrayBegin, const Value* pArrayEnd) + { + stopwatch.Restart(); + c.insert(pArrayBegin, pArrayEnd); + stopwatch.Stop(); + } + + + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 > stdVectorUT(10000); + eastl::vector< eastl::pair > eaVectorUT(10000); + + eastl::vector< std::pair< std::string, uint32_t> > stdVectorSU(10000); + eastl::vector< eastl::pair > 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(n1, TestObject(n2)); + eaVectorUT[i] = eastl::pair(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(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/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/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/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/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/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/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/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/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/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/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/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/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/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/erase pos", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestErasePosition(stopwatch1, stdMapStrUint32); + TestErasePosition(stopwatch2, eaMapStrUint32); + + if(i == 1) + Benchmark::AddResult("hash_map/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/erase range", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestEraseRange(stopwatch1, stdMapStrUint32); + TestEraseRange(stopwatch2, eaMapStrUint32); + + if(i == 1) + Benchmark::AddResult("hash_map/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/clear", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestClear(stopwatch1, stdMapStrUint32); + TestClear(stopwatch2, eaMapStrUint32); + + if(i == 1) + Benchmark::AddResult("hash_map/clear", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + } + } +} + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkHeap.cpp b/lib/EASTL/benchmark/source/BenchmarkHeap.cpp new file mode 100644 index 000000000..635cf319e --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkHeap.cpp @@ -0,0 +1,238 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include + +#ifdef _MSC_VER + #pragma warning(push, 0) + #pragma warning(disable: 4350) // behavior change: X called instead of Y +#endif +#include +#include +#ifdef _MSC_VER + #pragma warning(pop) +#endif + + +using namespace EA; + + +namespace +{ + template + void TestMakeHeapStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last) + { + stopwatch.Restart(); + std::make_heap(first, last); + stopwatch.Stop(); + } + + template + void TestMakeHeapEa(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last) + { + stopwatch.Restart(); + eastl::make_heap(first, last); + stopwatch.Stop(); + } + + + + template + 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 + 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 + 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 + 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 + void TestSortHeapStd(EA::StdC::Stopwatch& stopwatch, Iterator first, Iterator last) + { + stopwatch.Restart(); + std::sort_heap(first, last); + stopwatch.Stop(); + } + + template + 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 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 + std::vector stdVectorTO(kArraySize * 2); + std::vector stdVectorTO2(kArraySize); + eastl::vector eaVectorTO(kArraySize * 2); + eastl::vector 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)/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)/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)/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)/sort_heap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + + delete[] pIntArrayS; + delete[] pIntArrayE; + delete[] pIntArray2; + } +} + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkList.cpp b/lib/EASTL/benchmark/source/BenchmarkList.cpp new file mode 100644 index 000000000..1d22ad8a7 --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkList.cpp @@ -0,0 +1,382 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include +#include + +#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 +#ifdef _MSC_VER + #pragma warning(pop) +#endif + + +using namespace EA; +using namespace eastl; + + + +typedef std::list StdListTO; +typedef eastl::list EaListTO; + + + +namespace +{ + void DoNothing(void*) + { + // Empty + } + + + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 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/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/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/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/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/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/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/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/find", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + + /////////////////////////////// + // Test reverse() + /////////////////////////////// + + TestReverse(stopwatch1, stdListTO); + TestReverse(stopwatch2, eaListTO); + + if(i == 1) + Benchmark::AddResult("list/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/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/splice", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + + /////////////////////////////// + // Test erase() + /////////////////////////////// + + TestErase(stopwatch1, stdListTO); + TestErase(stopwatch2, eaListTO); + + if(i == 1) + Benchmark::AddResult("list/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkMap.cpp b/lib/EASTL/benchmark/source/BenchmarkMap.cpp new file mode 100644 index 000000000..d2fc35e3e --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkMap.cpp @@ -0,0 +1,382 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + + +using namespace EA; + + +typedef std::map StdMapTOUint32; +typedef eastl::map EaMapTOUint32; + + +namespace +{ + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 > stdVector(10000); + eastl::vector< eastl::pair > 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(n1), n2); + eaVector[i] = eastl::pair(TestObject(n1), n2); + } + + for(int i = 0; i < 2; i++) + { + StdMapTOUint32 stdMapTOUint32; + EaMapTOUint32 eaMapTOUint32; + + + /////////////////////////////// + // Test insert(const value_type&) + /////////////////////////////// + const std::pair stdHighValue(TestObject(0x7fffffff), 0x7fffffff); + const eastl::pair 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/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/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/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/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/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/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/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/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/erase/key", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test erase(iterator position) + /////////////////////////////// + + TestErasePosition(stopwatch1, stdMapTOUint32); + TestErasePosition(stopwatch2, eaMapTOUint32); + + if(i == 1) + Benchmark::AddResult("map/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/erase/range", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test clear() + /////////////////////////////// + + TestClear(stopwatch1, stdMapTOUint32); + TestClear(stopwatch2, eaMapTOUint32); + + if(i == 1) + Benchmark::AddResult("map/clear", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + } + } +} + + + + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkSet.cpp b/lib/EASTL/benchmark/source/BenchmarkSet.cpp new file mode 100644 index 000000000..4a58b1a76 --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkSet.cpp @@ -0,0 +1,353 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + + +using namespace EA; + + +typedef std::set StdSetUint32; +typedef eastl::set EaSetUint32; + + +namespace +{ + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 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/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test iteration + /////////////////////////////// + + TestIteration(stopwatch1, stdSetUint32); + TestIteration(stopwatch2, eaSetUint32); + + if(i == 1) + Benchmark::AddResult("set/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/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/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/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/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/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/erase/val", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test erase(iterator position) + /////////////////////////////// + + TestErasePosition(stopwatch1, stdSetUint32); + TestErasePosition(stopwatch2, eaSetUint32); + + if(i == 1) + Benchmark::AddResult("set/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/erase range", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test clear() + /////////////////////////////// + + TestClear(stopwatch1, stdSetUint32); + TestClear(stopwatch2, eaSetUint32); + + if(i == 1) + Benchmark::AddResult("set/clear", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + } + } +} + + + + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkSort.cpp b/lib/EASTL/benchmark/source/BenchmarkSort.cpp new file mode 100644 index 000000000..ccd2f4365 --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkSort.cpp @@ -0,0 +1,1399 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include +#include "EASTLBenchmark.h" +#include "EASTLTest.h" + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + + +using namespace EA; + + +namespace +{ + struct ValuePair + { + uint32_t key; + uint32_t v; + }; + + struct VPCompare + { + bool operator()(const ValuePair& vp1, const ValuePair& vp2) const + { + // return *(const uint64_t*)&vp1 < *(const uint64_t*)&vp2; + return (vp1.key == vp2.key) ? (vp1.v < vp2.v) : (vp1.key < vp2.key); + } + }; + + bool operator<(const ValuePair& vp1, const ValuePair& vp2) + { + // return *(const uint64_t*)&vp1 < *(const uint64_t*)&vp2; + return (vp1.key == vp2.key) ? (vp1.v < vp2.v) : (vp1.key < vp2.key); + } + + bool operator==(const ValuePair& vp1, const ValuePair& vp2) + { + // return *(const uint64_t*)&vp1 == *(const uint64_t*)&vp2; + return (vp1.key == vp2.key) && (vp1.v == vp2.v); + } +} + +// VPCompareC +// Useful for testing the the C qsort function. +int VPCompareC(const void* elem1, const void* elem2) +{ + return (int)(*(const uint64_t*)elem1 - *(const uint64_t*)elem2); +} + + +typedef std::vector StdVectorVP; +typedef eastl::vector EaVectorVP; + +typedef std::vector StdVectorInt; +typedef eastl::vector EaVectorInt; + +typedef std::vector StdVectorTO; +typedef eastl::vector EaVectorTO; + + +namespace +{ + #ifndef EA_PREFIX_NO_INLINE + #ifdef _MSC_VER + #define EA_PREFIX_NO_INLINE EA_NO_INLINE + #define EA_POSTFIX_NO_INLINE + #else + #define EA_PREFIX_NO_INLINE + #define EA_POSTFIX_NO_INLINE EA_NO_INLINE + #endif + #endif + + EA_PREFIX_NO_INLINE void TestQuickSortStdVP (EA::StdC::Stopwatch& stopwatch, StdVectorVP& stdVectorVP) EA_POSTFIX_NO_INLINE; + EA_PREFIX_NO_INLINE void TestQuickSortEaVP (EA::StdC::Stopwatch& stopwatch, EaVectorVP& eaVectorVP) EA_POSTFIX_NO_INLINE; + EA_PREFIX_NO_INLINE void TestQuickSortStdInt(EA::StdC::Stopwatch& stopwatch, StdVectorInt& stdVectorInt) EA_POSTFIX_NO_INLINE; + EA_PREFIX_NO_INLINE void TestQuickSortEaInt (EA::StdC::Stopwatch& stopwatch, EaVectorInt& eaVectorInt) EA_POSTFIX_NO_INLINE; + EA_PREFIX_NO_INLINE void TestQuickSortStdTO (EA::StdC::Stopwatch& stopwatch, StdVectorTO& stdVectorTO) EA_POSTFIX_NO_INLINE; + EA_PREFIX_NO_INLINE void TestQuickSortEaTO (EA::StdC::Stopwatch& stopwatch, EaVectorTO& eaVectorTO) EA_POSTFIX_NO_INLINE; + + + + void TestQuickSortStdVP(EA::StdC::Stopwatch& stopwatch, StdVectorVP& stdVectorVP) + { + stopwatch.Restart(); + std::sort(stdVectorVP.begin(), stdVectorVP.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)stdVectorVP[0].key); + } + + + void TestQuickSortEaVP(EA::StdC::Stopwatch& stopwatch, EaVectorVP& eaVectorVP) + { + stopwatch.Restart(); + eastl::quick_sort(eaVectorVP.begin(), eaVectorVP.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eaVectorVP[0].key); + } + + + void TestQuickSortStdInt(EA::StdC::Stopwatch& stopwatch, StdVectorInt& stdVectorInt) + { + stopwatch.Restart(); + std::sort(stdVectorInt.begin(), stdVectorInt.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)stdVectorInt[0]); + } + + + void TestQuickSortEaInt(EA::StdC::Stopwatch& stopwatch, EaVectorInt& eaVectorInt) + { + stopwatch.Restart(); + eastl::quick_sort(eaVectorInt.begin(), eaVectorInt.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eaVectorInt[0]); + } + + + void TestQuickSortStdTO(EA::StdC::Stopwatch& stopwatch, StdVectorTO& stdVectorTO) + { + stopwatch.Restart(); + std::sort(stdVectorTO.begin(), stdVectorTO.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)stdVectorTO[0].mX); + } + + + void TestQuickSortEaTO(EA::StdC::Stopwatch& stopwatch, EaVectorTO& eaVectorTO) + { + stopwatch.Restart(); + eastl::quick_sort(eaVectorTO.begin(), eaVectorTO.end()); + stopwatch.Stop(); + sprintf(Benchmark::gScratchBuffer, "%u", (unsigned)eaVectorTO[0].mX); + } + +} // namespace + + +namespace +{ + enum SortFunctionType + { + sf_qsort, // C qsort + sf_shell_sort, // eastl::shell_sort. + sf_heap_sort, // eastl::heap_sort + sf_merge_sort, // eastl::merge_sort + sf_merge_sort_buffer, // eastl::merge_sort_buffer + sf_comb_sort, // eastl::comb_sort + sf_bubble_sort, // eastl::bubble_sort + sf_selection_sort, // eastl::selection_sort + sf_shaker_sort, // eastl::shaker_sort + sf_quick_sort, // eastl::quick_sort + sf_tim_sort, // eastl::tim_sort + sf_insertion_sort, // eastl::insertion_sort + sf_std_sort, // std::sort + sf_std_stable_sort, // std::stable_sort + sf_radix_sort, // eastl::radix_sort (unconventional sort) + sf_count // + }; + + const char* GetSortFunctionName(int sortFunctionType) + { + switch (sortFunctionType) + { + case sf_quick_sort: + return "eastl::sort"; + + case sf_tim_sort: + return "eastl::tim_sort"; + + case sf_insertion_sort: + return "eastl::insertion_sort"; + + case sf_shell_sort: + return "eastl::shell_sort"; + + case sf_heap_sort: + return "eastl::heap_sort"; + + case sf_merge_sort: + return "eastl::merge_sort"; + + case sf_merge_sort_buffer: + return "eastl::merge_sort_buffer"; + + case sf_comb_sort: + return "eastl::comb_sort"; + + case sf_bubble_sort: + return "eastl::bubble_sort"; + + case sf_selection_sort: + return "eastl::selection_sort"; + + case sf_shaker_sort: + return "eastl::shaker_sort"; + + case sf_radix_sort: + return "eastl::radix_sort"; + + case sf_qsort: + return "qsort"; + + case sf_std_sort: + return "std::sort"; + + case sf_std_stable_sort: + return "std::stable_sort"; + + default: + return "unknown"; + } + } + + + enum RandomizationType + { + kRandom, // Completely random data. + kRandomSorted, // Random values already sorted. + kOrdered, // Already sorted. + kMostlyOrdered, // Partly sorted already. + kRandomizationTypeCount + }; + + const char* GetRandomizationTypeName(int randomizationType) + { + switch (randomizationType) + { + case kRandom: + return "random"; + + case kRandomSorted: + return "random sorted"; + + case kOrdered: + return "ordered"; + + case kMostlyOrdered: + return "mostly ordered"; + + default: + return "unknown"; + } + } + + template + void Randomize(eastl::vector& v, EA::UnitTest::RandGenT& rng, RandomizationType type) + { + typedef RandomType value_type; + + switch (type) + { + default: + case kRandomizationTypeCount: // We specify this only to avoid a compiler warning about not testing for it. + case kRandom: + { + eastl::generate(v.begin(), v.end(), rng); + break; + } + + case kRandomSorted: + { + // This randomization type differs from kOrdered because the set of values is random (but sorted), in the kOrdered + // case the set of values is contiguous (i.e. 0, 1, ..., n) which can have different performance characteristics. + // For example, radix_sort performs poorly for kOrdered. + eastl::generate(v.begin(), v.end(), rng); + eastl::sort(v.begin(), v.end()); + break; + } + + case kOrdered: + { + for(eastl_size_t i = 0; i < v.size(); ++i) + v[i] = value_type((value_type)i); // Note that value_type may be a struct and not an integer. Thus the casting and construction here. + break; + } + + case kMostlyOrdered: + { + for(eastl_size_t i = 0; i < v.size(); ++i) + v[i] = value_type((value_type)i); // Note that value_type may be a struct and not an integer. Thus the casting and construction here. + + // We order random segments. + // The algorithm below in practice will make slightly more than kPercentOrdered be ordered. + const eastl_size_t kPercentOrdered = 80; // In actuality, due to statistics, the actual ordered percent will be about 82-85%. + + for(eastl_size_t n = 0, s = v.size(), nEnd = ((s < (100 - kPercentOrdered)) ? 1 : (s / (100 - kPercentOrdered))); n < nEnd; n++) + { + eastl_size_t i = rng.mRand.RandLimit((uint32_t)s); + eastl_size_t j = rng.mRand.RandLimit((uint32_t)s); + + eastl::swap(v[i], v[j]); + } + + break; + } + } + } + + + char gSlowAssignBuffer1[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* ... */}; + char gSlowAssignBuffer2[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* ... */}; + + + // SlowAssign + // Implements an object which has slow assign performance. + template + struct SlowAssign + { + typedef T key_type; + T x; + + static int nAssignCount; + + SlowAssign() + { x = 0; memcpy(gSlowAssignBuffer1, gSlowAssignBuffer2, sizeof(gSlowAssignBuffer1)); } + + SlowAssign(const SlowAssign& sa) + { ++nAssignCount; x = sa.x; memcpy(gSlowAssignBuffer1, gSlowAssignBuffer2, sizeof(gSlowAssignBuffer1)); } + + SlowAssign& operator=(const SlowAssign& sa) + { ++nAssignCount; x = sa.x; memcpy(gSlowAssignBuffer1, gSlowAssignBuffer2, sizeof(gSlowAssignBuffer1)); return *this; } + + SlowAssign& operator=(int a) + { x = (T)a; return *this; } + + static void Reset() + { nAssignCount = 0; } + }; + + template<> int SlowAssign::nAssignCount = 0; + + template + bool operator <(const SlowAssign& a, const SlowAssign& b) + { return a.x < b.x; } + + + // SlowCompare + // Implements a compare which is N time slower than a simple integer compare. + template + struct SlowCompare + { + static int nCompareCount; + + bool operator()(T a, T b) + { + ++nCompareCount; + + return (a < b) && // It happens that gSlowAssignBuffer1 is always zeroed. + (gSlowAssignBuffer1[0] == 0) && (gSlowAssignBuffer1[1] == 0) && (gSlowAssignBuffer1[1] == 0) && + (gSlowAssignBuffer1[2] == 0) && (gSlowAssignBuffer1[4] == 0) && (gSlowAssignBuffer1[5] == 0); + } + + static void Reset() { nCompareCount = 0; } + }; + + template <> + int SlowCompare::nCompareCount = 0; + + + // qsort callback functions + // qsort compare function returns negative if b > a and positive if a > b. + template + int CompareInteger(const void* a, const void* b) + { + // Even though you see the following in Internet example code, it doesn't work! + // The reason is that it works only if a and b are both >= 0, otherwise large + // values can cause integer register wraparound. A similar kind of problem happens + // if you try to do the same thing with floating point value compares. + // See http://www.akalin.cx/2006/06/23/on-the-qsort-comparison-function/ + // Internet exmaple code: + // return *(const int32_t*)a - *(const int32_t*)b; + + // This double comparison might seem like it's crippling qsort against the + // STL-based sorts which do a single compare. But consider that the returning + // of -1, 0, +1 gives qsort more information, and its logic takes advantage + // of that. + if (*(const T*)a < *(const T*)b) + return -1; + if (*(const T*)a > *(const T*)b) + return +1; + return 0; + } + + + int SlowCompareInt32(const void* a, const void* b) + { + ++SlowCompare::nCompareCount; + + // This code is similar in performance to the C++ SlowCompare template functor above. + if((gSlowAssignBuffer1[0] == 0) && (gSlowAssignBuffer1[1] == 0) && + (gSlowAssignBuffer1[1] == 0) && (gSlowAssignBuffer1[2] == 0) && + (gSlowAssignBuffer1[4] == 0) && (gSlowAssignBuffer1[5] == 0)) + { + if (*(const int32_t*)a < *(const int32_t*)b) + return -1; + if (*(const int32_t*)a > *(const int32_t*)b) + return +1; + } + + return 0; + } + + template + struct slow_assign_extract_radix_key + { + typedef typename slow_assign_type::key_type radix_type; + + const radix_type operator()(const slow_assign_type& obj) const + { + return obj.x; + } + }; + + template + struct identity_extract_radix_key + { + typedef integer_type radix_type; + + const radix_type operator()(const integer_type& x) const + { + return x; + } + }; +} // namespace + + +struct BenchmarkResult +{ + uint64_t mTime; + uint64_t mCompareCount; + uint64_t mAssignCount; + + BenchmarkResult() : mTime(0), mCompareCount(0), mAssignCount(0) {} +}; + + +int CompareSortPerformance() +{ + // Sizes of arrays to be sorted. + const eastl_size_t kSizes[] = { 10, 100, 1000, 10000 }; + const eastl_size_t kSizesCount = EAArrayCount(kSizes); + static BenchmarkResult sResults[kRandomizationTypeCount][kSizesCount][sf_count]; + int nErrorCount = 0; + + EA::UnitTest::ReportVerbosity(2, "Sort comparison\n"); + EA::UnitTest::ReportVerbosity(2, "Random seed = %u\n", (unsigned)EA::UnitTest::GetRandSeed()); + + EA::UnitTest::RandGenT rng(EA::UnitTest::GetRandSeed()); + EA::StdC::Stopwatch stopwatch(EA::StdC::Stopwatch::kUnitsCPUCycles); + EA::StdC::Stopwatch stopwatchGlobal(EA::StdC::Stopwatch::kUnitsSeconds); + const eastl_size_t kArraySizeMax = *eastl::max_element(eastl::begin(kSizes), eastl::end(kSizes)); + const int kRunCount = 4; + + #if !defined(EA_DEBUG) + EA::UnitTest::SetHighThreadPriority(); + #endif + + eastl::vector allSortFunctions; + for (int i = 0; i < sf_count; i++) + { + allSortFunctions.push_back(SortFunctionType(i)); + } + + { + auto& sortFunctions = allSortFunctions; + + // Regular speed test. + // In this case we test the sorting of integral values. + // This is probably the most common type of comparison. + EA::UnitTest::ReportVerbosity(2, "Sort comparison: Regular speed test\n"); + + typedef uint32_t ElementType; + typedef eastl::less CompareFunction; + + eastl::string sOutput; + sOutput.set_capacity(100000); + ElementType* pBuffer = new ElementType[kArraySizeMax]; + + memset(sResults, 0, sizeof(sResults)); + + stopwatchGlobal.Restart(); + + for (int c = 0; c < kRunCount; c++) + { + for (int i = 0; i < kRandomizationTypeCount; i++) + { + for (size_t sizeType = 0; sizeType < EAArrayCount(kSizes); sizeType++) + { + const eastl_size_t size = kSizes[sizeType]; + + for (SortFunctionType sortFunction : sortFunctions) + { + eastl::vector v(size); + + rng.SetSeed(EA::UnitTest::GetRandSeed()); + Randomize(v, rng, (RandomizationType)i); + + switch (sortFunction) + { + case sf_quick_sort: + stopwatch.Restart(); + eastl::quick_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_tim_sort: + stopwatch.Restart(); + eastl::tim_sort_buffer(v.begin(), v.end(), pBuffer, CompareFunction()); + stopwatch.Stop(); + break; + + case sf_insertion_sort: + stopwatch.Restart(); + eastl::insertion_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_shell_sort: + stopwatch.Restart(); + eastl::shell_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_heap_sort: + stopwatch.Restart(); + eastl::heap_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_merge_sort: + stopwatch.Restart(); + eastl::merge_sort(v.begin(), v.end(), *get_default_allocator((EASTLAllocatorType*)NULL), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_merge_sort_buffer: + stopwatch.Restart(); + eastl::merge_sort_buffer(v.begin(), v.end(), pBuffer, CompareFunction()); + stopwatch.Stop(); + break; + + case sf_comb_sort: + stopwatch.Restart(); + eastl::comb_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_bubble_sort: + stopwatch.Restart(); + eastl::bubble_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_selection_sort: + stopwatch.Restart(); + eastl::selection_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_shaker_sort: + stopwatch.Restart(); + eastl::shaker_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_radix_sort: + stopwatch.Restart(); + eastl::radix_sort>(v.begin(), v.end(), pBuffer); + stopwatch.Stop(); + break; + + case sf_qsort: + stopwatch.Restart(); + qsort(&v[0], (size_t)v.size(), sizeof(ElementType), CompareInteger); + stopwatch.Stop(); + break; + + case sf_std_sort: + stopwatch.Restart(); + std::sort(v.data(), v.data() + v.size(), std::less()); + stopwatch.Stop(); + break; + + case sf_std_stable_sort: + stopwatch.Restart(); + std::stable_sort(v.data(), v.data() + v.size(), std::less()); + stopwatch.Stop(); + break; + + case sf_count: + default: + // unsupported + break; + } + + const uint64_t elapsedTime = (uint64_t)stopwatch.GetElapsedTime(); + + // If this result was faster than a previously fastest result, record this one instead. + if ((c == 0) || (elapsedTime < sResults[i][sizeType][sortFunction].mTime)) + sResults[i][sizeType][sortFunction].mTime = elapsedTime; + + VERIFY(eastl::is_sorted(v.begin(), v.end())); + + } // for each sort function... + + } // for each size type... + + } // for each randomization type... + + } // for each run + + EA::UnitTest::ReportVerbosity(2, "Total time: %.2f s\n", stopwatchGlobal.GetElapsedTimeFloat()); + + delete[] pBuffer; + + // Now print the results. + for (int i = 0; i < kRandomizationTypeCount; i++) + { + for (size_t sizeType = 0; sizeType < EAArrayCount(kSizes); sizeType++) + { + const eastl_size_t size = kSizes[sizeType]; + + for (SortFunctionType sortFunction : sortFunctions) + { + sOutput.append_sprintf("%25s, %14s, Size: %8u, Time: %14" PRIu64 " ticks %0.2f ticks/elem\n", + GetSortFunctionName(sortFunction), GetRandomizationTypeName(i), + (unsigned)size, sResults[i][sizeType][sortFunction].mTime, + float(sResults[i][sizeType][sortFunction].mTime)/float(size)); + } + sOutput.append("\n"); + } + } + + EA::UnitTest::ReportVerbosity(2, "%s\n\n", sOutput.c_str()); + } + + { + // Do a speed test for the case of slow compares. + // By this we mean to compare sorting speeds when the comparison of elements is slow. + // Sort functions use element comparison to tell where elements go and use element + // movement to get them there. But some sorting functions accomplish sorting performance by + // minimizing the amount of movement, some minimize the amount of comparisons, and the + // best do a good job of minimizing both. + auto sortFunctions = allSortFunctions; + // We can't test this radix_sort because what we need isn't exposed. + sortFunctions.erase(eastl::remove(sortFunctions.begin(), sortFunctions.end(), sf_radix_sort), sortFunctions.end()); + EA::UnitTest::ReportVerbosity(2, "Sort comparison: Slow compare speed test\n"); + + typedef int32_t ElementType; + typedef SlowCompare CompareFunction; + + eastl::string sOutput; + sOutput.set_capacity(100000); + ElementType* pBuffer = new ElementType[kArraySizeMax]; + + memset(sResults, 0, sizeof(sResults)); + + stopwatchGlobal.Restart(); + + for (int c = 0; c < kRunCount; c++) + { + for (int i = 0; i < kRandomizationTypeCount; i++) + { + for (size_t sizeType = 0; sizeType < EAArrayCount(kSizes); sizeType++) + { + const eastl_size_t size = kSizes[sizeType]; + + for (SortFunctionType sortFunction : sortFunctions) + { + eastl::vector v(size); + + rng.SetSeed(EA::UnitTest::GetRandSeed()); + Randomize(v, rng, (RandomizationType)i); + CompareFunction::Reset(); + + switch (sortFunction) + { + case sf_quick_sort: + stopwatch.Restart(); + eastl::quick_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_tim_sort: + stopwatch.Restart(); + eastl::tim_sort_buffer(v.begin(), v.end(), pBuffer, CompareFunction()); + stopwatch.Stop(); + break; + + case sf_insertion_sort: + stopwatch.Restart(); + eastl::insertion_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_shell_sort: + stopwatch.Restart(); + eastl::shell_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_heap_sort: + stopwatch.Restart(); + eastl::heap_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_merge_sort: + stopwatch.Restart(); + eastl::merge_sort(v.begin(), v.end(), *get_default_allocator((EASTLAllocatorType*)NULL), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_merge_sort_buffer: + stopwatch.Restart(); + eastl::merge_sort_buffer(v.begin(), v.end(), pBuffer, CompareFunction()); + stopwatch.Stop(); + break; + + case sf_comb_sort: + stopwatch.Restart(); + eastl::comb_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_bubble_sort: + stopwatch.Restart(); + eastl::bubble_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_selection_sort: + stopwatch.Restart(); + eastl::selection_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_shaker_sort: + stopwatch.Restart(); + eastl::shaker_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_qsort: + stopwatch.Restart(); + qsort(&v[0], (size_t)v.size(), sizeof(ElementType), SlowCompareInt32); + stopwatch.Stop(); + break; + + case sf_std_sort: + stopwatch.Restart(); + std::sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_std_stable_sort: + stopwatch.Restart(); + std::stable_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_radix_sort: + case sf_count: + default: + // unsupported + break; + } + + const uint64_t elapsedTime = (uint64_t)stopwatch.GetElapsedTime(); + + // If this result was faster than a previously fastest result, record this one instead. + if ((c == 0) || (elapsedTime < sResults[i][sizeType][sortFunction].mTime)) + { + sResults[i][sizeType][sortFunction].mTime = elapsedTime; + sResults[i][sizeType][sortFunction].mCompareCount = (uint64_t)CompareFunction::nCompareCount; + } + + VERIFY(eastl::is_sorted(v.begin(), v.end())); + } // for each sort function... + + } // for each size type... + + } // for each randomization type... + + } // for each run + + EA::UnitTest::ReportVerbosity(2, "Total time: %.2f s\n", stopwatchGlobal.GetElapsedTimeFloat()); + + delete[] pBuffer; + + // Now print the results. + for (int i = 0; i < kRandomizationTypeCount; i++) + { + for (size_t sizeType = 0; sizeType < EAArrayCount(kSizes); sizeType++) + { + const eastl_size_t size = kSizes[sizeType]; + + for (SortFunctionType sortFunction : sortFunctions) + { + sOutput.append_sprintf("%25s, %14s, Size: %6u, Time: %11" PRIu64 " ticks, Compares: %11" PRIu64 "\n", + GetSortFunctionName(sortFunction), GetRandomizationTypeName(i), + (unsigned)size, sResults[i][sizeType][sortFunction].mTime, + sResults[i][sizeType][sortFunction].mCompareCount); + } + + sOutput.append("\n"); + } + } + + EA::UnitTest::ReportVerbosity(2, "%s\n\n", sOutput.c_str()); + } + + { + // Do a speed test for the case of slow assignment. + // By this we mean to compare sorting speeds when the movement of elements is slow. + // Sort functions use element comparison to tell where elements go and use element + // movement to get them there. But some sorting functions accomplish sorting performance by + // minimizing the amount of movement, some minimize the amount of comparisons, and the + // best do a good job of minimizing both. + auto sortFunctions = allSortFunctions; + // Can't implement this for qsort because the C standard library doesn't expose it. + // We could implement it by copying and modifying the source code. + sortFunctions.erase(eastl::remove(sortFunctions.begin(), sortFunctions.end(), sf_qsort), sortFunctions.end()); + + EA::UnitTest::ReportVerbosity(2, "Sort comparison: Slow assignment speed test\n"); + + typedef SlowAssign ElementType; + typedef eastl::less CompareFunction; + + eastl::string sOutput; + sOutput.set_capacity(100000); + ElementType* pBuffer = new ElementType[kArraySizeMax]; + + memset(sResults, 0, sizeof(sResults)); + + stopwatchGlobal.Restart(); + + for (int c = 0; c < kRunCount; c++) + { + for (int i = 0; i < kRandomizationTypeCount; i++) + { + for (size_t sizeType = 0; sizeType < EAArrayCount(kSizes); sizeType++) + { + const eastl_size_t size = kSizes[sizeType]; + + for (SortFunctionType sortFunction : sortFunctions) + { + eastl::vector v(size); + + Randomize(v, rng, (RandomizationType)i); + ElementType::Reset(); + + switch (sortFunction) + { + case sf_quick_sort: + stopwatch.Restart(); + eastl::quick_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_tim_sort: + stopwatch.Restart(); + eastl::tim_sort_buffer(v.begin(), v.end(), pBuffer, CompareFunction()); + stopwatch.Stop(); + break; + + case sf_insertion_sort: + stopwatch.Restart(); + eastl::insertion_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_shell_sort: + stopwatch.Restart(); + eastl::shell_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_heap_sort: + stopwatch.Restart(); + eastl::heap_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_merge_sort: + stopwatch.Restart(); + eastl::merge_sort(v.begin(), v.end(), *get_default_allocator((EASTLAllocatorType*)NULL), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_merge_sort_buffer: + stopwatch.Restart(); + eastl::merge_sort_buffer(v.begin(), v.end(), pBuffer, CompareFunction()); + stopwatch.Stop(); + break; + + case sf_comb_sort: + stopwatch.Restart(); + eastl::comb_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_bubble_sort: + stopwatch.Restart(); + eastl::bubble_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_selection_sort: + stopwatch.Restart(); + eastl::selection_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_shaker_sort: + stopwatch.Restart(); + eastl::shaker_sort(v.begin(), v.end(), CompareFunction()); + stopwatch.Stop(); + break; + + case sf_radix_sort: + stopwatch.Restart(); + eastl::radix_sort>(v.begin(), v.end(), pBuffer); + stopwatch.Stop(); + break; + + case sf_std_sort: + stopwatch.Restart(); + std::sort(v.begin(), v.end(), std::less()); + stopwatch.Stop(); + break; + + case sf_std_stable_sort: + stopwatch.Restart(); + std::stable_sort(v.begin(), v.end(), std::less()); + stopwatch.Stop(); + break; + + case sf_qsort: + case sf_count: + default: + // unsupported + break; + } + + const uint64_t elapsedTime = (uint64_t)stopwatch.GetElapsedTime(); + + // If this result was faster than a previously fastest result, record this one instead. + if ((c == 0) || (elapsedTime < sResults[i][sizeType][sortFunction].mTime)) + { + sResults[i][sizeType][sortFunction].mTime = elapsedTime; + sResults[i][sizeType][sortFunction].mAssignCount = (uint64_t)ElementType::nAssignCount; + } + + VERIFY(eastl::is_sorted(v.begin(), v.end())); + + } // for each sort function... + + } // for each size type... + + } // for each randomization type... + + } // for each run + + EA::UnitTest::ReportVerbosity(2, "Total time: %.2f s\n", stopwatchGlobal.GetElapsedTimeFloat()); + + delete[] pBuffer; + + // Now print the results. + for (int i = 0; i < kRandomizationTypeCount; i++) + { + for (size_t sizeType = 0; sizeType < EAArrayCount(kSizes); sizeType++) + { + const eastl_size_t size = kSizes[sizeType]; + + for (SortFunctionType sortFunction : sortFunctions) + { + sOutput.append_sprintf("%25s, %14s, Size: %6u, Time: %11" PRIu64 " ticks, Assignments: %11" PRIu64 "\n", + GetSortFunctionName(sortFunction), GetRandomizationTypeName(i), + (unsigned)size, sResults[i][sizeType][sortFunction].mTime, + sResults[i][sizeType][sortFunction].mAssignCount); + } + + sOutput.append("\n"); + } + } + EA::UnitTest::ReportVerbosity(2, "%s\n", sOutput.c_str()); + } + + #if !defined(EA_DEBUG) + EA::UnitTest::SetNormalThreadPriority(); + #endif + + return nErrorCount; +} + +typedef eastl::function OutputResultCallback; +typedef eastl::function PostExecuteCallback; +typedef eastl::function PreExecuteCallback; + + +template +static int CompareSmallInputSortPerformanceHelper(eastl::vector &arraySizes, eastl::vector &sortFunctions, const PreExecuteCallback &preExecuteCallback, const PostExecuteCallback &postExecuteCallback, const OutputResultCallback &outputResultCallback) +{ + int nErrorCount = 0; + + EA::UnitTest::RandGenT rng(EA::UnitTest::GetRandSeed()); + EA::StdC::Stopwatch stopwatch(EA::StdC::Stopwatch::kUnitsCPUCycles); + EA::StdC::Stopwatch stopwatchGlobal(EA::StdC::Stopwatch::kUnitsSeconds); + const eastl_size_t kArraySizeMax = *eastl::max_element(eastl::begin(arraySizes), eastl::end(arraySizes)); + const int kRunCount = 4; + const int numSubArrays = 128; + + eastl::string sOutput; + sOutput.set_capacity(100000); + ElementType* pBuffer = new ElementType[kArraySizeMax]; + + stopwatchGlobal.Restart(); + + for (int i = 0; i < kRandomizationTypeCount; i++) + { + for (size_t size : arraySizes) + { + for (SortFunctionType sortFunction : sortFunctions) + { + BenchmarkResult bestResult{}; + + for (int c = 0; c < kRunCount; c++) + { + eastl::vector v(size * numSubArrays); + + rng.SetSeed(EA::UnitTest::GetRandSeed()); + Randomize(v, rng, (RandomizationType)i); + preExecuteCallback(); + + switch (sortFunction) + { + case sf_quick_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::quick_sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_tim_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::tim_sort_buffer(begin, begin + size, pBuffer, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_insertion_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::insertion_sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_shell_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::shell_sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_heap_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::heap_sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_merge_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::merge_sort(begin, begin + size, *get_default_allocator((EASTLAllocatorType*)NULL), CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_merge_sort_buffer: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::merge_sort_buffer(begin, begin + size, pBuffer, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_comb_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::comb_sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_bubble_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::bubble_sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_selection_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::selection_sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_shaker_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + eastl::shaker_sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_std_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + std::sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_std_stable_sort: + stopwatch.Restart(); + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + std::stable_sort(begin, begin + size, CompareFunction()); + } + stopwatch.Stop(); + break; + + case sf_qsort: + case sf_radix_sort: + case sf_count: + default: + EATEST_VERIFY_F(false, "Missing case statement for sort function %s.", GetSortFunctionName(sortFunction)); + break; + } + + BenchmarkResult result {}; + result.mTime = (uint64_t)stopwatch.GetElapsedTime(); + postExecuteCallback(result); + + // If this result was faster than a previously fastest result, record this one instead. + if ((c == 0) || (result.mTime < bestResult.mTime)) + bestResult = result; + + for (auto begin = v.begin(); begin != v.end(); begin += size) + { + VERIFY(eastl::is_sorted(begin, begin + size)); + } + } // for each run + + outputResultCallback(sOutput, GetSortFunctionName(sortFunction), GetRandomizationTypeName(i), size, numSubArrays, bestResult); + + } // for each sort function... + sOutput.append("\n"); + + } // for each size type... + + } // for each randomization type... + + EA::UnitTest::ReportVerbosity(2, "Total time: %.2f s\n", stopwatchGlobal.GetElapsedTimeFloat()); + EA::UnitTest::ReportVerbosity(2, "%s\n", sOutput.c_str()); + + delete[] pBuffer; + return nErrorCount; +} + +static int CompareSmallInputSortPerformance() +{ + int nErrorCount = 0; + eastl::vector arraySizes{1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 64, 128, 256}; + // Test quick sort and merge sort to provide a "base line" for performance. The other sort algorithms are mostly + // O(n^2) and they are benchmarked to determine what sorts are ideal for sorting small arrays or sub-arrays. (i.e. + // this is useful to determine good algorithms to choose as a base case for some of the recursive sorts). + eastl::vector sortFunctions{sf_quick_sort, sf_merge_sort_buffer, sf_bubble_sort, sf_comb_sort, + sf_insertion_sort, sf_selection_sort, sf_shell_sort, sf_shaker_sort}; + + EA::UnitTest::ReportVerbosity(2, "Small Sub-array Sort comparison: Regular speed test\n"); + nErrorCount += CompareSmallInputSortPerformanceHelper>( + arraySizes, sortFunctions, PreExecuteCallback([]() {}), PostExecuteCallback([](BenchmarkResult&) {}), + OutputResultCallback([](eastl::string& output, const char* sortFunction, const char* randomizationType, + size_t size, size_t numSubArrays, const BenchmarkResult& result) { + output.append_sprintf("%25s, %14s, Size: %8u, Time: %0.1f ticks %0.2f ticks/elem\n", sortFunction, + randomizationType, (unsigned)size, float(result.mTime) / float(numSubArrays), + float(result.mTime) / float(size * numSubArrays)); + })); + + EA::UnitTest::ReportVerbosity(2, "Small Sub-array Sort comparison: Slow compare speed test\n"); + nErrorCount += CompareSmallInputSortPerformanceHelper>( + arraySizes, sortFunctions, PreExecuteCallback([]() { SlowCompare::Reset(); }), + PostExecuteCallback( + [](BenchmarkResult& result) { result.mCompareCount = (uint64_t)SlowCompare::nCompareCount; }), + OutputResultCallback([](eastl::string& output, const char* sortFunction, const char* randomizationType, + size_t size, size_t numSubArrays, const BenchmarkResult& result) { + output.append_sprintf("%25s, %14s, Size: %6u, Time: %0.2f ticks, Compares: %0.2f\n", sortFunction, + randomizationType, (unsigned)size, float(result.mTime) / float(numSubArrays), + float(result.mCompareCount) / float(numSubArrays)); + })); + + EA::UnitTest::ReportVerbosity(2, "Small Sub-array Sort comparison: Slow assignment speed test\n"); + nErrorCount += CompareSmallInputSortPerformanceHelper, eastl::less>>( + arraySizes, sortFunctions, PreExecuteCallback([]() { SlowAssign::Reset(); }), + PostExecuteCallback([](BenchmarkResult& result) { + result.mCompareCount = (uint64_t)SlowCompare::nCompareCount; + result.mAssignCount = (uint64_t)SlowAssign::nAssignCount; + }), + OutputResultCallback([](eastl::string& output, const char* sortFunction, const char* randomizationType, + size_t size, size_t numSubArrays, const BenchmarkResult& result) { + output.append_sprintf("%25s, %14s, Size: %6u, Time: %0.2f ticks, Assignments: %0.2f\n", sortFunction, + randomizationType, (unsigned)size, float(result.mTime) / float(numSubArrays), + float(result.mAssignCount) / float(numSubArrays)); + })); + + return nErrorCount; +} + + +void BenchmarkSort() +{ + EASTLTest_Printf("Sort\n"); + + EA::UnitTest::RandGenT rng(12345678); // For debugging sort code we should use 12345678, for normal testing use EA::UnitTest::GetRandSeed(). + EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles); + EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles); + + if (EA::UnitTest::GetVerbosity() >= 3) + { + CompareSortPerformance(); + CompareSmallInputSortPerformance(); + } + + { // 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 intVector(10000); + eastl::generate(intVector.begin(), intVector.end(), rng); + + for (int i = 0; i < 2; i++) + { + /////////////////////////////// + // Test quick_sort/vector/ValuePair + /////////////////////////////// + + StdVectorVP stdVectorVP(intVector.size()); + EaVectorVP eaVectorVP(intVector.size()); + + for (eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++) + { + const ValuePair vp = {intVector[j], intVector[j]}; + stdVectorVP[j] = vp; + eaVectorVP[j] = vp; + } + + TestQuickSortStdVP(stopwatch1, stdVectorVP); + TestQuickSortEaVP (stopwatch2, eaVectorVP); + + if(i == 1) + Benchmark::AddResult("sort/q_sort/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + // Benchmark the sorting of something that is already sorted. + TestQuickSortStdVP(stopwatch1, stdVectorVP); + TestQuickSortEaVP (stopwatch2, eaVectorVP); + + if(i == 1) + Benchmark::AddResult("sort/q_sort/vector/sorted", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + /////////////////////////////// + // Test quick_sort/vector/Int + /////////////////////////////// + + StdVectorInt stdVectorInt(intVector.size()); + EaVectorInt eaVectorInt (intVector.size()); + + for(eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++) + { + stdVectorInt[j] = intVector[j]; + eaVectorInt[j] = intVector[j]; + } + + TestQuickSortStdInt(stopwatch1, stdVectorInt); + TestQuickSortEaInt (stopwatch2, eaVectorInt); + + if(i == 1) + Benchmark::AddResult("sort/q_sort/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + // Benchmark the sorting of something that is already sorted. + TestQuickSortStdInt(stopwatch1, stdVectorInt); + TestQuickSortEaInt (stopwatch2, eaVectorInt); + + if(i == 1) + Benchmark::AddResult("sort/q_sort/vector/sorted", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + /////////////////////////////// + // Test quick_sort/vector/TestObject + /////////////////////////////// + + StdVectorTO stdVectorTO(intVector.size()); + EaVectorTO eaVectorTO(intVector.size()); + + for (eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++) + { + stdVectorTO[j] = TestObject(intVector[j]); + eaVectorTO[j] = TestObject(intVector[j]); + } + + TestQuickSortStdTO(stopwatch1, stdVectorTO); + TestQuickSortEaTO(stopwatch2, eaVectorTO); + + if (i == 1) + Benchmark::AddResult("sort/q_sort/vector", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + // Benchmark the sorting of something that is already sorted. + TestQuickSortStdTO(stopwatch1, stdVectorTO); + TestQuickSortEaTO(stopwatch2, eaVectorTO); + + if (i == 1) + Benchmark::AddResult("sort/q_sort/vector/sorted", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + /////////////////////////////// + // Test quick_sort/TestObject[] + /////////////////////////////// + + // Reset the values back to the unsorted state. + for(eastl_size_t j = 0, jEnd = intVector.size(); j < jEnd; j++) + { + stdVectorTO[j] = TestObject(intVector[j]); + eaVectorTO[j] = TestObject(intVector[j]); + } + + TestQuickSortStdTO(stopwatch1, stdVectorTO); + TestQuickSortEaTO (stopwatch2, eaVectorTO); + + if(i == 1) + Benchmark::AddResult("sort/q_sort/TestObject[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + // Benchmark the sorting of something that is already sorted. + TestQuickSortStdTO(stopwatch1, stdVectorTO); + TestQuickSortEaTO (stopwatch2, eaVectorTO); + + if(i == 1) + Benchmark::AddResult("sort/q_sort/TestObject[]/sorted", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } +} + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkString.cpp b/lib/EASTL/benchmark/source/BenchmarkString.cpp new file mode 100644 index 000000000..5dfefbcb0 --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkString.cpp @@ -0,0 +1,531 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + + +using namespace EA; + + +namespace +{ + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 ss8(16, 0); // We initialize to size of 16 because different implementations may make + eastl::basic_string 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 ss16(16, 0); // We try to nullify this tradeoff for the tests below by starting all at + eastl::basic_string es16(16, 0); // the same baseline allocation. + + + /////////////////////////////// + // Test push_back + /////////////////////////////// + + TestPushBack(stopwatch1, ss8); + TestPushBack(stopwatch2, es8); + + if(i == 1) + Benchmark::AddResult("string/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestPushBack(stopwatch1, ss16); + TestPushBack(stopwatch2, es16); + + if(i == 1) + Benchmark::AddResult("string/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/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/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/erase/pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestErase1(stopwatch1, ss16); + TestErase1(stopwatch2, es16); + + if(i == 1) + Benchmark::AddResult("string/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/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/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/reserve", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestReserve(stopwatch1, ss16); + TestReserve(stopwatch2, es16); + + if(i == 1) + Benchmark::AddResult("string/reserve", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test size() + /////////////////////////////// + + TestSize(stopwatch1, ss8); + TestSize(stopwatch2, es8); + + if(i == 1) + Benchmark::AddResult("string/size", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestSize(stopwatch1, ss16); + TestSize(stopwatch2, es16); + + if(i == 1) + Benchmark::AddResult("string/size", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test operator[]. + /////////////////////////////// + + TestBracket(stopwatch1, ss8); + TestBracket(stopwatch2, es8); + + if(i == 1) + Benchmark::AddResult("string/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestBracket(stopwatch1, ss16); + TestBracket(stopwatch2, es16); + + if(i == 1) + Benchmark::AddResult("string/operator[]", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test iteration via find(). + /////////////////////////////// + + TestFind(stopwatch1, ss8); + TestFind(stopwatch2, es8); + + if(i == 1) + Benchmark::AddResult("string/iteration", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestFind(stopwatch1, ss16); + TestFind(stopwatch2, es16); + + if(i == 1) + Benchmark::AddResult("string/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/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/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/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/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/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/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/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/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/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/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/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/find_last_of/p,pos,n", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + #endif + + /////////////////////////////// + // Test compare() + /////////////////////////////// + + std::basic_string ss8X(ss8); + eastl::basic_string es8X(es8); + std::basic_string ss16X(ss16); + eastl::basic_string es16X(es16); + + TestCompare(stopwatch1, ss8, ss8X); + TestCompare(stopwatch2, es8, es8X); + + if(i == 1) + Benchmark::AddResult("string/compare", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestCompare(stopwatch1, ss16, ss16X); + TestCompare(stopwatch2, es16, es16X); + + if(i == 1) + Benchmark::AddResult("string/compare", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + + /////////////////////////////// + // Test swap() + /////////////////////////////// + + TestSwap(stopwatch1, ss8, ss8X); + TestSwap(stopwatch2, es8, es8X); + + if(i == 1) + Benchmark::AddResult("string/swap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + TestSwap(stopwatch1, ss16, ss16X); + TestSwap(stopwatch2, es16, es16X); + + if(i == 1) + Benchmark::AddResult("string/swap", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + } + } + +} + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkTupleVector.cpp b/lib/EASTL/benchmark/source/BenchmarkTupleVector.cpp new file mode 100644 index 000000000..3a8e79dde --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkTupleVector.cpp @@ -0,0 +1,667 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include + +#ifdef _MSC_VER + #pragma warning(push, 0) + #pragma warning(disable: 4350) +#endif +#include +#include +#include +#include +#ifdef _MSC_VER + #pragma warning(pop) +#endif + + +using namespace EA; + + +typedef std::vector StdVectorUint64; +typedef eastl::tuple_vector EaTupleVectorUint64; + + struct PaddingStruct +{ + char padding[56] = { 0 }; +}; +static const PaddingStruct DefaultPadding; +typedef eastl::tuple PaddedTuple; +typedef std::vector StdVectorUint64Padded; +typedef eastl::tuple_vector 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 + 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 + void TestPushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector& 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 + 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 + 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 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 + 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 + 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 + 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 + void TestMoveReallocate(EA::StdC::Stopwatch& stopwatch, Container& c) + { + stopwatch.Restart(); + while(c.size() < 8192) + c.resize(c.capacity() + 1); + stopwatch.Stop(); + } + + + template + 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 + void TestTuplePushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector& 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 + 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 + 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 + 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 + 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 + 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 rng(EA::UnitTest::GetRandSeed()); + EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles); + EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles); + + { + eastl::vector 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/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test operator[]. + /////////////////////////////// + + TestBracket(stopwatch1, stdVectorUint64); + TestBracket(stopwatch2, eaTupleVectorUint64); + + if(i == 1) + Benchmark::AddResult("tuple_vector/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/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/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + #endif + + /////////////////////////////// + // Test insert + /////////////////////////////// + + TestInsert(stopwatch1, stdVectorUint64); + TestInsert(stopwatch2, eaTupleVectorUint64); + + if(i == 1) + Benchmark::AddResult("tuple_vector/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test erase + /////////////////////////////// + + TestErase(stopwatch1, stdVectorUint64); + TestErase(stopwatch2, eaTupleVectorUint64); + + if(i == 1) + Benchmark::AddResult("tuple_vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////////////////// + // Test move of MovableType + // Should be much faster with C++11 move. + /////////////////////////////////////////// + + std::vector stdVectorMovableType; + eastl::tuple_vector eaTupleVectorMovableType; + + TestMoveReallocate(stopwatch1, stdVectorMovableType); + TestMoveReallocate(stopwatch2, eaTupleVectorMovableType); + + if(i == 1) + Benchmark::AddResult("tuple_vector/reallocate", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + TestMoveErase(stopwatch1, stdVectorMovableType); + TestMoveErase(stopwatch2, eaTupleVectorMovableType); + + if(i == 1) + Benchmark::AddResult("tuple_vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////////////////// + // Test move of AutoRefCount + // Should be much faster with C++11 move. + /////////////////////////////////////////// + + std::vector > stdVectorAutoRefCount; + eastl::tuple_vector > eaTupleVectorAutoRefCount; + + for(size_t a = 0; a < 2048; a++) + { + stdVectorAutoRefCount.push_back(AutoRefCount(new RefCounted)); + eaTupleVectorAutoRefCount.push_back(AutoRefCount(new RefCounted)); + } + + RefCounted::msAddRefCount = 0; + RefCounted::msReleaseCount = 0; + TestMoveErase(stopwatch1, stdVectorAutoRefCount); + //EASTLTest_Printf("tuple_vector/erase std counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount); + + RefCounted::msAddRefCount = 0; + RefCounted::msReleaseCount = 0; + TestMoveErase(stopwatch2, eaTupleVectorAutoRefCount); + //EASTLTest_Printf("tuple_vector/erase EA counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount); + + if(i == 1) + Benchmark::AddResult("tuple_vector/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/push_back", stopwatch1.GetUnits(), + stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test operator[]. + /////////////////////////////// + + TestTupleBracket(stopwatch1, stdVectorUint64Padded); + TestTupleBracket(stopwatch2, eaTupleVectorUint64Padded); + + if(i == 1) + Benchmark::AddResult("tuple_vector/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/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/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), + stopwatch2.GetElapsedTime()); + #endif + + /////////////////////////////// + // Test insert + /////////////////////////////// + + TestTupleInsert(stopwatch1, stdVectorUint64Padded); + TestTupleInsert(stopwatch2, eaTupleVectorUint64Padded); + + if(i == 1) + Benchmark::AddResult("tuple_vector/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), + stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test erase + /////////////////////////////// + + TestTupleErase(stopwatch1, stdVectorUint64Padded); + TestTupleErase(stopwatch2, eaTupleVectorUint64Padded); + + if(i == 1) + Benchmark::AddResult("tuple_vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), + stopwatch2.GetElapsedTime()); + } + } +} + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/BenchmarkVector.cpp b/lib/EASTL/benchmark/source/BenchmarkVector.cpp new file mode 100644 index 000000000..93315309d --- /dev/null +++ b/lib/EASTL/benchmark/source/BenchmarkVector.cpp @@ -0,0 +1,452 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include +#include +#include + +#ifdef _MSC_VER + #pragma warning(push, 0) + #pragma warning(disable: 4350) +#endif +#include +#include +#include +#include +#ifdef _MSC_VER + #pragma warning(pop) +#endif + + +using namespace EA; + + +typedef std::vector StdVectorUint64; +typedef eastl::vector 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 + 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 + void TestPushBack(EA::StdC::Stopwatch& stopwatch, Container& c, eastl::vector& 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 + 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 + 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 + 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 + 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 + 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 + void TestMoveReallocate(EA::StdC::Stopwatch& stopwatch, Container& c) + { + stopwatch.Restart(); + while(c.size() < 8192) + c.resize(c.capacity() + 1); + stopwatch.Stop(); + } + + + template + 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 rng(EA::UnitTest::GetRandSeed()); + EA::StdC::Stopwatch stopwatch1(EA::StdC::Stopwatch::kUnitsCPUCycles); + EA::StdC::Stopwatch stopwatch2(EA::StdC::Stopwatch::kUnitsCPUCycles); + + { + eastl::vector 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/push_back", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test operator[]. + /////////////////////////////// + + TestBracket(stopwatch1, stdVectorUint64); + TestBracket(stopwatch2, eaVectorUint64); + + if(i == 1) + Benchmark::AddResult("vector/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/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/sort", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + #endif + + /////////////////////////////// + // Test insert + /////////////////////////////// + + TestInsert(stopwatch1, stdVectorUint64); + TestInsert(stopwatch2, eaVectorUint64); + + if(i == 1) + Benchmark::AddResult("vector/insert", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////// + // Test erase + /////////////////////////////// + + TestErase(stopwatch1, stdVectorUint64); + TestErase(stopwatch2, eaVectorUint64); + + if(i == 1) + Benchmark::AddResult("vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////////////////// + // Test move of MovableType + // Should be much faster with C++11 move. + /////////////////////////////////////////// + + std::vector stdVectorMovableType; + eastl::vector eaVectorMovableType; + + TestMoveReallocate(stopwatch1, stdVectorMovableType); + TestMoveReallocate(stopwatch2, eaVectorMovableType); + + if(i == 1) + Benchmark::AddResult("vector/reallocate", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + TestMoveErase(stopwatch1, stdVectorMovableType); + TestMoveErase(stopwatch2, eaVectorMovableType); + + if(i == 1) + Benchmark::AddResult("vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + + + /////////////////////////////////////////// + // Test move of AutoRefCount + // Should be much faster with C++11 move. + /////////////////////////////////////////// + + std::vector > stdVectorAutoRefCount; + eastl::vector > eaVectorAutoRefCount; + + for(size_t a = 0; a < 2048; a++) + { + stdVectorAutoRefCount.push_back(AutoRefCount(new RefCounted)); + eaVectorAutoRefCount.push_back(AutoRefCount(new RefCounted)); + } + + RefCounted::msAddRefCount = 0; + RefCounted::msReleaseCount = 0; + TestMoveErase(stopwatch1, stdVectorAutoRefCount); + EASTLTest_Printf("vector/erase std counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount); + + RefCounted::msAddRefCount = 0; + RefCounted::msReleaseCount = 0; + TestMoveErase(stopwatch2, eaVectorAutoRefCount); + EASTLTest_Printf("vector/erase EA counts: %d %d\n", RefCounted::msAddRefCount, RefCounted::msReleaseCount); + + if(i == 1) + Benchmark::AddResult("vector/erase", stopwatch1.GetUnits(), stopwatch1.GetElapsedTime(), stopwatch2.GetElapsedTime()); + } + } +} + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/EASTLBenchmark.cpp b/lib/EASTL/benchmark/source/EASTLBenchmark.cpp new file mode 100644 index 000000000..8e4d3ae8d --- /dev/null +++ b/lib/EASTL/benchmark/source/EASTLBenchmark.cpp @@ -0,0 +1,291 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#include +#include + +#ifdef _MSC_VER + #pragma warning(push, 0) +#endif +#include +#include +#include +#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 + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/EASTLBenchmark.h b/lib/EASTL/benchmark/source/EASTLBenchmark.h new file mode 100644 index 000000000..a0833e621 --- /dev/null +++ b/lib/EASTL/benchmark/source/EASTLBenchmark.h @@ -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 +#ifdef _MSC_VER + #pragma function(strlen, strcmp, strcpy, strcat, memcpy, memcmp, memset) +#endif + + +#include +#include +#include +#include +#include + + +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 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 + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/benchmark/source/main.cpp b/lib/EASTL/benchmark/source/main.cpp new file mode 100644 index 000000000..59ff5a969 --- /dev/null +++ b/lib/EASTL/benchmark/source/main.cpp @@ -0,0 +1,194 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include "EASTLBenchmark.h" +#include "EASTLTest.h" +#if !EASTL_OPENSOURCE + #include +#endif +#include +#include +#include +#include +#include +#include +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; +} + + + + + + + + + + diff --git a/lib/EASTL/doc/Benchmarks.md b/lib/EASTL/doc/Benchmarks.md new file mode 100644 index 000000000..c41cdb65b --- /dev/null +++ b/lib/EASTL/doc/Benchmarks.md @@ -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 33061345 6497757 5.09 * +algorithm/copy/vector 5844906 4876076 1.20 * +algorithm/copy/vector 1634346 166065 9.84 * +algorithm/copy_backward/vector 4515974 4638892 0.97 +algorithm/copy_backward/vector 1821168 121746 14.96 * +algorithm/count/vector 17048884 2720766 6.27 * +algorithm/equal_range/vector 1111147812 448756888 2.48 * +algorithm/fill/bool[] 1728722 91936 18.80 * +algorithm/fill/char[]/'d' 1299200 33745 38.50 * +algorithm/fill/vector/'d' 10205092 33796 100.00 * +algorithm/fill/vector/0 10200748 33805 100.00 * +algorithm/fill/vector 10416538 1399687 7.44 * +algorithm/fill/vector 10221837 1307700 7.82 * +algorithm/fill_n/bool[] 1399033 34196 40.91 * +algorithm/fill_n/char[] 1299225 33754 38.49 * +algorithm/fill_n/vector 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 32824195 5253587 6.25 * +algorithm/lex_cmp/vector 29852034 202658 100.00 * +algorithm/lower_bound/vector 798624462 350027935 2.28 * +algorithm/min_element/vector 21675298 5314676 4.08 * +algorithm/rand_shuffle/vector 84236190 43677506 1.93 * +algorithm/reverse/list 3007292 2105799 1.43 * +algorithm/reverse/vector 2974618 2124796 1.40 * +algorithm/search/string 16228158 3594268 4.52 * +algorithm/search_n/string 16926985 1522096 11.12 * +algorithm/unique/vector 54206243 9988002 5.43 * +algorithm/unique/vector 26940079 1741991 15.47 * +algorithm/unique/vector 47621344 5213127 9.13 * +algorithm/upper_bound/vector 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/erase 743801706 335646802 2.22 * +deque/insert 742331809 341912866 2.17 * +deque/iteration 29097030 16315827 1.78 * +deque/operator[] 49859598 24026313 2.08 * +deque/push_back 424807033 34497608 12.31 * +deque/push_front 402313373 38006322 10.59 * +deque/sort 725101017 581796551 1.25 * + +hash_map/clear 559462 961019 0.58 * +hash_map/count 53377807 8091448 6.60 * +hash_map/erase pos 613573 858084 0.72 * +hash_map/erase range 5488748 461134 11.90 * +hash_map/erase val 35760096 16379858 2.18 * +hash_map/find 43490335 10324823 4.21 * +hash_map/find_as/char* 49343818 8617139 5.73 * +hash_map/insert 107420281 168690439 0.64 * +hash_map/iteration 2456356 1255153 1.96 * +hash_map/operator[] 47209502 12581624 3.75 * + +hash_map/clear 533172 546449 0.98 +hash_map/count 28667432 2899997 9.89 * +hash_map/erase pos 683239 538289 1.27 * +hash_map/erase range 9632676 253037 38.07 * +hash_map/erase val 25466026 7752188 3.29 * +hash_map/find 20048253 4678502 4.29 * +hash_map/insert 71085798 37686187 1.89 * +hash_map/iteration 1460318 1338317 1.09 +hash_map/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)/make_heap 91770743 21724870 4.22 * +heap (vector)/pop_heap 1175599317 284007398 4.14 * +heap (vector)/push_heap 207804541 45918046 4.53 * +heap (vector)/sort_heap 970394145 208321477 4.66 * + +list/ctor(it) 805539509 760938607 1.06 +list/ctor(n) 80959236 75106995 1.08 +list/erase 1052543704 1044976137 1.01 +list/find 97785267 75970884 1.29 * +list/insert 873895175 807051107 1.08 +list/push_back 812797710 780742425 1.04 +list/remove 1850600714 1436980599 1.29 * +list/reverse 180270465 80466636 2.24 * +list/size/1 440148 599642 0.73 * +list/size/10 439433 1329817 0.33 * EASTL intentionally implements list::size as O(n). +list/size/100 439595 11030060 0.04 * EASTL intentionally implements list::size as O(n). +list/splice 177106094 69383027 2.55 * + +map/clear 508283 470807 1.08 +map/count 43145354 14280357 3.02 * +map/equal_range 38594004 16520447 2.34 * +map/erase/key 33948082 16123175 2.11 * +map/erase/pos 578332 455201 1.27 * MS uses a code bloating implementation of erase. +map/erase/range 387345 284538 1.36 * +map/find 22897224 12766100 1.79 * +map/insert 61665800 47286928 1.30 * +map/iteration 1977202 745391 2.65 * +map/lower_bound 19892941 12260928 1.62 * +map/operator[] 24199084 15429634 1.57 * +map/upper_bound 19842409 12064441 1.64 * + +set/clear 1027625 1000901 1.03 +set/count 39730182 13329565 2.98 * +set/equal_range 34681649 14768827 2.35 * +set/erase range 841458 602030 1.40 * +set/erase/pos 1380485 1084303 1.27 * MS uses a code bloating implementation of erase. +set/erase/val 31617425 13344023 2.37 * +set/find 19582428 10788864 1.82 * +set/insert 61434014 48232086 1.27 * +set/iteration 1512057 667820 2.26 * +set/lower_bound 18394885 10402785 1.77 * +set/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 87962047 15004677 5.86 * +sort/q_sort/vector/sorted 21396523 3341163 6.40 * +sort/q_sort/vector 80334589 10429161 7.70 * +sort/q_sort/vector/sorted 22133295 3230553 6.85 * +sort/q_sort/vector 72195388 5940302 12.15 * +sort/q_sort/vector/sorted 19635171 995495 19.72 * + +string/compare 523013373 534722089 0.98 +string/erase/pos,n 3446597 3439492 1.00 +string/find/p,pos,n 383873158 441902786 0.87 * +string/find_first_not_of/p,pos,n 174157 134131 1.30 * +string/find_first_of/p,pos,n 11715423 8520944 1.37 * +string/find_last_of/p,pos,n 1871556 1226457 1.53 * +string/insert/pos,p 3624877 3357058 1.08 +string/iteration 6766787933 581916665 11.63 * +string/operator[] 4820827 2335579 2.06 * +string/push_back 59812962 6757466 8.85 * +string/replace/pos,n,p,n 4371279 4459713 0.98 +string/reserve 2307530 1919386 1.20 * +string/rfind/p,pos,n 734826 372615 1.97 * +string/size 41608 28866 1.44 * +string/swap 1033932 1490994 0.69 * + +string/compare 63086797 64194771 0.98 +string/erase/pos,n 2045687 1960270 1.04 +string/find/p,pos,n 123872549 471364764 0.26 * +string/find_first_not_of/p,pos,n 140013 130271 1.07 +string/find_first_of/p,pos,n 8051906 8749994 0.92 +string/find_last_of/p,pos,n 1318835 1230715 1.07 +string/insert/pos,p 1770610 1724234 1.03 +string/iteration 28112136 2544475 11.05 * +string/operator[] 4810525 2255841 2.13 * +string/push_back 54869634 6127447 8.95 * +string/replace/pos,n,p,n 2737578 2847900 0.96 +string/reserve 1123395 394902 2.84 * +string/rfind/p,pos,n 737299 368518 2.00 * +string/size 42245 26801 1.58 * +string/swap 1036142 1491028 0.69 * + +vector/erase 56417135 55770251 1.01 +vector/insert 56617761 56100468 1.01 +vector/iteration 10413895 1291269 8.06 * +vector/operator[] 23507193 3479390 6.76 * +vector/push_back 34687939 13806627 2.51 * +vector/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 2783546 2750660 1.01 +algorithm/copy/vector 6474025 4972738 1.30 * +algorithm/copy/vector 157267 173162 0.91 +algorithm/copy_backward/vector 4836406 4374780 1.11 * +algorithm/copy_backward/vector 104780 120912 0.87 * +algorithm/count/vector 1368440 1368696 1.00 +algorithm/equal_range/vector 114199387 102783938 1.11 * +algorithm/fill/bool[] 253215 27353 9.26 * +algorithm/fill/char[]/'d' 253164 27404 9.24 * +algorithm/fill/vector/'d' 253105 27362 9.25 * +algorithm/fill/vector/0 253275 27353 9.26 * +algorithm/fill/vector 397001 394323 1.01 +algorithm/fill/vector 547196 642362 0.85 * +algorithm/fill_n/bool[] 229177 27361 8.38 * +algorithm/fill_n/char[] 228845 27404 8.35 * +algorithm/fill_n/vector 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 2610494 2642183 0.99 +algorithm/lex_cmp/vector 697595 167866 4.16 * +algorithm/lower_bound/vector 62462233 58146664 1.07 +algorithm/min_element/vector 4350385 2671227 1.63 * +algorithm/rand_shuffle/vector 10868261 11300818 0.96 +algorithm/reverse/list 483718 470024 1.03 +algorithm/reverse/vector 476739 484322 0.98 +algorithm/search/string 2560387 1259496 2.03 * +algorithm/search_n/string 2770991 458524 6.04 * +algorithm/unique/vector 4194520 4658910 0.90 * +algorithm/unique/vector 538730 787924 0.68 * +algorithm/unique/vector 3169829 2575636 1.23 * +algorithm/upper_bound/vector 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/erase 90444165 37113253 2.44 * +deque/insert 93299349 36175167 2.58 * +deque/iteration 2756414 2122076 1.30 * +deque/operator[] 5117969 4632075 1.10 +deque/push_back 30300757 3060357 9.90 * +deque/push_front 25498529 2808392 9.08 * +deque/sort 142283047 111292464 1.28 * + +hash_map/clear 146769 389699 0.38 * +hash_map/count 13059434 3460324 3.77 * +hash_map/erase pos 184246 331925 0.56 * +hash_map/erase range 382432 167237 2.29 * +hash_map/erase val 6187898 3302114 1.87 * +hash_map/find 11289369 3459024 3.26 * +hash_map/find_as/char* 13559192 3662387 3.70 * +hash_map/insert 17514012 14095176 1.24 * +hash_map/iteration 801014 218450 3.67 * +hash_map/operator[] 11457065 3690385 3.10 * + +hash_map/clear 141865 265379 0.53 * +hash_map/count 1766045 703613 2.51 * +hash_map/erase pos 172337 218458 0.79 * +hash_map/erase range 537846 102340 5.26 * +hash_map/erase val 2220132 1441787 1.54 * +hash_map/find 1612994 1043953 1.55 * +hash_map/insert 7141547 4348056 1.64 * +hash_map/iteration 199512 169328 1.18 * +hash_map/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)/make_heap 11458442 11510819 1.00 +heap (vector)/pop_heap 122897267 119061132 1.03 +heap (vector)/push_heap 21688481 21176220 1.02 +heap (vector)/sort_heap 90867380 88869523 1.02 + +list/ctor(it) 74591104 69845817 1.07 +list/ctor(n) 6243998 5838582 1.07 +list/erase 299509298 206013676 1.45 * +list/find 40927185 14514243 2.82 * +list/insert 71277251 47234534 1.51 * +list/push_back 73780527 44116725 1.67 * +list/remove 786197776 326434612 2.41 * +list/reverse 49283128 25029678 1.97 * +list/size/1 159741 139400 1.15 * +list/size/10 159324 346579 0.46 * EASTL intentionally implements list::size as O(n). +list/size/100 159188 97235419 0.00 * EASTL intentionally implements list::size as O(n). +list/splice 63548584 19322931 3.29 * + +map/clear 167408 170501 0.98 +map/count 10213685 4748346 2.15 * +map/equal_range 9515053 5677558 1.68 * +map/erase/key 6646260 4302300 1.54 * +map/erase/pos 297135 327938 0.91 MS uses a code bloating implementation of erase. +map/erase/range 148614 163702 0.91 +map/find 5637531 4767055 1.18 * +map/insert 9591128 9030349 1.06 +map/iteration 323595 325261 0.99 +map/lower_bound 5398239 4784089 1.13 * +map/operator[] 5631250 5141166 1.10 +map/upper_bound 5436336 4762431 1.14 * + +set/clear 155983 156026 1.00 +set/count 9635965 4392146 2.19 * +set/equal_range 8504157 5247832 1.62 * +set/erase range 140488 119408 1.18 * +set/erase/pos 260678 286697 0.91 MS uses a code bloating implementation of erase. +set/erase/val 6008225 4012825 1.50 * +set/find 5145432 4381945 1.17 * +set/insert 8087129 8697251 0.93 +set/iteration 271507 304538 0.89 * +set/lower_bound 4666228 4404250 1.06 +set/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 9674828 5430199 1.78 * +sort/q_sort/vector/sorted 606908 1111647 0.55 * +sort/q_sort/vector 6284194 3423452 1.84 * +sort/q_sort/vector/sorted 711629 569364 1.25 * +sort/q_sort/vector 5453379 2916146 1.87 * +sort/q_sort/vector/sorted 537047 419144 1.28 * + +string/compare 435083295 251985824 1.73 * +string/erase/pos,n 3454842 3451858 1.00 +string/find/p,pos,n 401954723 165298157 2.43 * +string/find_first_not_of/p,pos,n 131452 65374 2.01 * +string/find_first_of/p,pos,n 11657444 4144515 2.81 * +string/find_last_of/p,pos,n 1604248 567571 2.83 * +string/insert/pos,p 3398734 3355460 1.01 +string/iteration 218856504 218771844 1.00 +string/operator[] 714161 240023 2.98 * +string/push_back 34968235 2444897 14.30 * +string/replace/pos,n,p,n 4226693 4198498 1.01 +string/reserve 1901765 390805 4.87 * +string/rfind/p,pos,n 195483 150985 1.29 * +string/size 11169 11245 0.99 +string/swap 1459280 419807 3.48 * + +string/compare 63071275 77209580 0.82 * +string/erase/pos,n 2008652 1944494 1.03 +string/find/p,pos,n 123201023 167536164 0.74 * +string/find_first_not_of/p,pos,n 93372 67864 1.38 * +string/find_first_of/p,pos,n 7542492 3375758 2.23 * +string/find_last_of/p,pos,n 933972 583576 1.60 * +string/insert/pos,p 1737213 1750847 0.99 +string/iteration 893834 899130 0.99 +string/operator[] 817879 313437 2.61 * +string/push_back 20857734 2004410 10.41 * +string/replace/pos,n,p,n 2578696 2607655 0.99 +string/reserve 915127 85289 10.73 * +string/rfind/p,pos,n 196103 148894 1.32 * +string/size 11619 11220 1.04 +string/swap 1461056 419874 3.48 * + +vector/erase 55235116 55284587 1.00 +vector/insert 55166046 55142755 1.00 +vector/iteration 553954 509719 1.09 +vector/operator[] 1284239 798516 1.61 * +vector/push_back 5399549 3867959 1.40 * +vector/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 5661170 5689517 1.00 +algorithm/copy/vector 5573815 5124428 1.09 +algorithm/copy/vector 148273 125782 1.18 * +algorithm/copy_backward/vector 5429791 4834510 1.12 * +algorithm/copy_backward/vector 156765 163038 0.96 +algorithm/count/vector 2730922 2730072 1.00 +algorithm/equal_range/vector 639366489 452896251 1.41 * +algorithm/fill/bool[] 1299326 27361 47.49 * +algorithm/fill/char[]/'d' 27378 27361 1.00 +algorithm/fill/vector/'d' 34459 27361 1.26 * +algorithm/fill/vector/0 1299224 27361 47.48 * +algorithm/fill/vector 1400647 1400145 1.00 +algorithm/fill/vector 1308779 1309085 1.00 +algorithm/fill_n/bool[] 1299156 27352 47.50 * +algorithm/fill_n/char[] 1299258 27369 47.47 * +algorithm/fill_n/vector 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 5060968 4799882 1.05 +algorithm/lex_cmp/vector 1668354 189490 8.80 * +algorithm/lower_bound/vector 450240945 353437573 1.27 * +algorithm/min_element/vector 5861744 5326371 1.10 +algorithm/rand_shuffle/vector 40780449 45780090 0.89 * +algorithm/reverse/list 2657678 2130627 1.25 * +algorithm/reverse/vector 2666424 2124889 1.25 * +algorithm/search/string 3110379 3613460 0.86 * +algorithm/search_n/string 3061665 1521261 2.01 * +algorithm/unique/vector 12423684 9485439 1.31 * +algorithm/unique/vector 3718699 1726596 2.15 * +algorithm/unique/vector 6205110 4591631 1.35 * +algorithm/upper_bound/vector 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/erase 651933790 326443043 2.00 * +deque/insert 659786183 333304660 1.98 * +deque/iteration 23734592 16173706 1.47 * +deque/operator[] 59126816 23911774 2.47 * +deque/push_back 58056988 31859266 1.82 * +deque/push_front 57780891 31743199 1.82 * +deque/sort 818414195 596568113 1.37 * + +hash_map/clear 3422133 2204517 1.55 * +hash_map/count 9869545 8624924 1.14 * +hash_map/erase pos 3256350 2069299 1.57 * +hash_map/erase range 3230203 1151392 2.81 * +hash_map/erase val 16860362 15939778 1.06 +hash_map/find 10286971 9920910 1.04 +hash_map/find_as/char* 118136025 9458468 12.49 * +hash_map/insert 188948336 174490082 1.08 +hash_map/iteration 4037049 2021036 2.00 * +hash_map/operator[] 11472127 12887699 0.89 * + +hash_map/clear 2522264 1331848 1.89 * +hash_map/count 3210739 2897063 1.11 * +hash_map/erase pos 1862281 1304783 1.43 * +hash_map/erase range 698079 579606 1.20 * +hash_map/erase val 8806722 7041298 1.25 * +hash_map/find 3604875 4709645 0.77 * +hash_map/insert 40785711 40376342 1.01 +hash_map/iteration 3064088 1508834 2.03 * +hash_map/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)/make_heap 22215557 22277063 1.00 +heap (vector)/pop_heap 275392171 277340039 0.99 +heap (vector)/push_heap 51479442 47342577 1.09 +heap (vector)/sort_heap 214474736 218497540 0.98 + +list/ctor(it) 767753795 753421427 1.02 +list/ctor(n) 74185322 73386245 1.01 +list/erase 1021003824 1033873589 0.99 +list/find 77666072 74917622 1.04 +list/insert 788071150 774188737 1.02 +list/push_back 760490154 737327348 1.03 +list/remove 1682511938 1434771006 1.17 * +list/reverse 87237327 80394623 1.09 +list/size/1 3828111 599530 6.39 * +list/size/10 9600605 1329535 7.22 * EASTL intentionally implements list::size as O(n). +list/size/100 62952334 15022551 4.19 * EASTL intentionally implements list::size as O(n). +list/splice 96536412 60804817 1.59 * + +map/clear 1142127 1099066 1.04 +map/count 19659726 14647548 1.34 * +map/equal_range 36680687 18219086 2.01 * +map/erase/key 28892154 16037774 1.80 * +map/erase/pos 1209643 1185495 1.02 +map/erase/range 715402 670539 1.07 +map/find 21020992 13429575 1.57 * +map/insert 59530871 51120640 1.16 * +map/iteration 972825 1191946 0.82 * +map/lower_bound 18852651 12495034 1.51 * +map/operator[] 22889573 16676736 1.37 * +map/upper_bound 18603584 12406922 1.50 * + +set/clear 919555 882988 1.04 +set/count 17561110 12461084 1.41 * +set/equal_range 31522488 15230282 2.07 * +set/erase range 687582 564765 1.22 * +set/erase/pos 1044352 1045355 1.00 +set/erase/val 25525304 12940774 1.97 * +set/find 17140751 10704866 1.60 * +set/insert 56035051 45555664 1.23 * +set/iteration 682669 640831 1.07 +set/lower_bound 16339932 10475740 1.56 * +set/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 17476629 14953285 1.17 * +sort/q_sort/vector/sorted 6667034 3327435 2.00 * +sort/q_sort/vector 15391357 10820848 1.42 * +sort/q_sort/vector/sorted 6617122 3232949 2.05 * +sort/q_sort/vector 8343906 6014846 1.39 * +sort/q_sort/vector/sorted 3039430 1003127 3.03 * + +string/compare 1489709846 532664000 2.80 * +string/erase/pos,n 3528690 3439864 1.03 +string/find/p,pos,n 2521448321 443752189 5.68 * +string/find_first_not_of/p,pos,n 661206 137419 4.81 * +string/find_first_of/p,pos,n 54746434 8521335 6.42 * +string/find_last_of/p,pos,n 10607778 1212414 8.75 * +string/insert/pos,p 3445016 3360126 1.03 +string/iteration 580955636 579452556 1.00 +string/operator[] 2206353 1987809 1.11 * +string/push_back 22421368 6007808 3.73 * +string/replace/pos,n,p,n 5138454 4464786 1.15 * +string/reserve 4922413418 335622 100.00 * +string/rfind/p,pos,n 1440308 380578 3.78 * +string/size 25355 25398 1.00 +string/swap 2122704 1490823 1.42 * + +string/compare 77222134 77443134 1.00 +string/erase/pos,n 1965344 1956521 1.00 +string/find/p,pos,n 2468091951 474205522 5.20 * +string/find_first_not_of/p,pos,n 660960 130211 5.08 * +string/find_first_of/p,pos,n 55020899 9240171 5.95 * +string/find_last_of/p,pos,n 10576210 1239053 8.54 * +string/insert/pos,p 1822756 1750880 1.04 +string/iteration 2617889 2540148 1.03 +string/operator[] 2254794 2256443 1.00 +string/push_back 12463022 5210321 2.39 * +string/replace/pos,n,p,n 3744862 2855260 1.31 * +string/reserve 1372046888 218815 100.00 * +string/rfind/p,pos,n 1446232 366902 3.94 * +string/size 26859 25431 1.06 +string/swap 2123350 1490509 1.42 * + +vector/erase 55164013 56417449 0.98 +vector/insert 55872973 56432664 0.99 +vector/iteration 1329102 1324623 1.00 +vector/operator[] 5264738 3136746 1.68 * +vector/push_back 14903245 13171175 1.13 * +vector/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 2741046 2731441 1.00 +algorithm/copy/vector 6065923 5085142 1.19 * +algorithm/copy/vector 158304 165555 0.96 +algorithm/copy_backward/vector 4710258 4896476 0.96 +algorithm/copy_backward/vector 146030 142630 1.02 +algorithm/count/vector 1395921 1406334 0.99 +algorithm/equal_range/vector 211692764 118969493 1.78 * +algorithm/fill/bool[] 366078 33737 10.85 * +algorithm/fill/char[]/'d' 33736 33771 1.00 +algorithm/fill/vector/'d' 28466 33720 0.84 * +algorithm/fill/vector/0 366086 33728 10.85 * +algorithm/fill/vector 466250 401591 1.16 * +algorithm/fill/vector 521603 693481 0.75 * +algorithm/fill_n/bool[] 599709 33762 17.76 * +algorithm/fill_n/char[] 599573 33711 17.79 * +algorithm/fill_n/vector 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 2623559 2643551 0.99 +algorithm/lex_cmp/vector 960899 183608 5.23 * +algorithm/lower_bound/vector 60630534 56531528 1.07 +algorithm/min_element/vector 4209022 2768527 1.52 * +algorithm/rand_shuffle/vector 13762010 15969052 0.86 * +algorithm/reverse/list 673387 731825 0.92 +algorithm/reverse/vector 634576 754511 0.84 * +algorithm/search/string 1262599 1387608 0.91 +algorithm/search_n/string 1166242 458592 2.54 * +algorithm/unique/vector 4912193 5336317 0.92 +algorithm/unique/vector 809387 809081 1.00 +algorithm/unique/vector 4371814 2414255 1.81 * +algorithm/upper_bound/vector 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/erase 127108651 115258113 1.10 +deque/insert 137727889 116552332 1.18 * +deque/iteration 7144182 6009899 1.19 * +deque/operator[] 34241222 20535039 1.67 * +deque/push_back 6585800 3932126 1.67 * +deque/push_front 6805865 3993513 1.70 * +deque/sort 395352323 348778188 1.13 * + +hash_map/clear 426640 447015 0.95 +hash_map/count 4359344 3883089 1.12 * +hash_map/erase pos 584392 458142 1.28 * +hash_map/erase range 221034 196078 1.13 * +hash_map/erase val 3539867 3790813 0.93 +hash_map/find 3966831 3811910 1.04 +hash_map/find_as/char* 11591612 4243710 2.73 * +hash_map/insert 16763887 16719194 1.00 +hash_map/iteration 909968 478609 1.90 * +hash_map/operator[] 4360041 4108313 1.06 + +hash_map/clear 302634 283722 1.07 +hash_map/count 916487 907426 1.01 +hash_map/erase pos 388042 321385 1.21 * +hash_map/erase range 122680 116280 1.06 +hash_map/erase val 1710931 1729529 0.99 +hash_map/find 1089462 1346527 0.81 * +hash_map/insert 4560310 5072350 0.90 * +hash_map/iteration 960117 495354 1.94 * +hash_map/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)/make_heap 13191456 13089711 1.01 +heap (vector)/pop_heap 148555656 144787742 1.03 +heap (vector)/push_heap 28696689 26618830 1.08 +heap (vector)/sort_heap 112473989 114018643 0.99 + +list/ctor(it) 80186731 74006287 1.08 +list/ctor(n) 6232311 6128007 1.02 +list/erase 344556374 212877808 1.62 * +list/find 39859075 14591347 2.73 * +list/insert 86935153 56138233 1.55 * +list/push_back 79569180 46700641 1.70 * +list/remove 785786758 324201016 2.42 * +list/reverse 45248186 24852759 1.82 * +list/size/1 219844 219496 1.00 +list/size/10 519563 519579 1.00 EASTL intentionally implements list::size as O(n). +list/size/100 4567194 101230266 0.05 * EASTL intentionally implements list::size as O(n). +list/splice 68321087 23601687 2.89 * + +map/clear 168011 180540 0.93 +map/count 4830439 5139287 0.94 +map/equal_range 8700090 6158531 1.41 * +map/erase/key 6696776 4617038 1.45 * +map/erase/pos 309273 333183 0.93 +map/erase/range 137419 136068 1.01 +map/find 4773498 4931352 0.97 +map/insert 9651877 9311699 1.04 +map/iteration 372946 416364 0.90 * +map/lower_bound 4784234 4915797 0.97 +map/operator[] 5040254 5183147 0.97 +map/upper_bound 4724292 4915984 0.96 + +set/clear 165300 173289 0.95 +set/count 4958654 4885086 1.02 +set/equal_range 8434134 5698681 1.48 * +set/erase range 145554 133960 1.09 +set/erase/pos 299914 324760 0.92 +set/erase/val 6506155 4335034 1.50 * +set/find 4866879 4556043 1.07 +set/insert 8340523 8957257 0.93 +set/iteration 294465 343442 0.86 * +set/lower_bound 4548095 4756498 0.96 +set/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 7331530 7017260 1.04 +sort/q_sort/vector/sorted 1601629 1247120 1.28 * +sort/q_sort/vector 7071643 7067869 1.00 +sort/q_sort/vector/sorted 2136390 1703799 1.25 * +sort/q_sort/vector 3292891 2943627 1.12 * +sort/q_sort/vector/sorted 653693 473612 1.38 * + +string/compare 356579259 432760228 0.82 * +string/erase/pos,n 3430422 3428645 1.00 +string/find/p,pos,n 229263402 225830975 1.02 +string/find_first_not_of/p,pos,n 187391 81404 2.30 * +string/find_first_of/p,pos,n 4411831 4413532 1.00 +string/find_last_of/p,pos,n 731655 726155 1.01 +string/insert/pos,p 3408628 3319726 1.03 +string/iteration 309993861 310333547 1.00 +string/operator[] 580839 579904 1.00 +string/push_back 3983338 2975553 1.34 * +string/replace/pos,n,p,n 4361095 4211504 1.04 +string/reserve 935141729 247010 100.00 * +string/rfind/p,pos,n 248956 223397 1.11 * +string/size 13311 13107 1.02 +string/swap 519129 579445 0.90 * + +string/compare 76695559 76828015 1.00 +string/erase/pos,n 1951566 1947282 1.00 +string/find/p,pos,n 185878944 185605039 1.00 +string/find_first_not_of/p,pos,n 196877 81600 2.41 * +string/find_first_of/p,pos,n 4147685 4145356 1.00 +string/find_last_of/p,pos,n 605897 598222 1.01 +string/insert/pos,p 1781592 1768264 1.01 +string/iteration 921502 921272 1.00 +string/operator[] 361250 359873 1.00 +string/push_back 3363288 2530493 1.33 * +string/replace/pos,n,p,n 2682600 2633130 1.02 +string/reserve 672517501 78387 100.00 * +string/rfind/p,pos,n 226202 200013 1.13 * +string/size 11280 11109 1.02 +string/swap 519393 559759 0.93 + +vector/erase 55184856 55192217 1.00 +vector/insert 56764267 55682726 1.02 +vector/iteration 423122 424039 1.00 +vector/operator[] 1189397 860991 1.38 * +vector/push_back 5626609 4027317 1.40 * +vector/sort 49227036 49231362 1.00 +``` + +---------------------------------------------- +End of document diff --git a/lib/EASTL/doc/BestPractices.md b/lib/EASTL/doc/BestPractices.md new file mode 100644 index 000000000..cadb7fa79 --- /dev/null +++ b/lib/EASTL/doc/BestPractices.md @@ -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\.](#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\ 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 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 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 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 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 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, EASTLAllocatorType, deque > 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 intSlist; + +intSlist.push_front(37); +``` + +### Avoid redundant end() and size() in loops. + +Instead of writing code like this: + +```cpp +for(deque::iterator it = d.begin(); it != d.end(); ++it) + + ... +``` + +write code like this: + +```cpp +for(deque::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 | +|------|------|------|------| +| array1 | string
vector
deque
intrusive_list
intrusive_slist
vector_map
vector_multimap
vector_set
vector_multiset
bit_vector
hash_map
hash_multimap
hash_set
hash_multiset
intrusive_hash_map
intrusive_hash_multimap
intrusive_hash_set
intrusive_hash_multiset | array
list
slist
intrusive_list
intrusive_slist
map
multimap
set
multiset
hash_map2
hash_multimap2
hash_set2
hash_multiset2
intrusive_hash_map
intrusive_hash_multimap
intrusive_hash_set
intrusive_hash_multiset | string
vector
deque
vector_map
vector_multimap
vector_set
vector_multiset
bit_vector | + +* 1 Arrays can be neither resized nor reallocated. +* 2 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::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(" ");`
`puts(s.c_str());` | `puts(" ");` | +| `s.sprintf("u");` | s = "u";` | +| `char array[32];`
`sprintf(array, "%d", 10);`
`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 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 | 1a | 1 | 1 at end, else n | 1 at end, else n | n | n log(n) | +| vector_set | 1 | 1a | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 | +| vector_multiset | 1 | 1a | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 | +| vector_map | 1 | 1a | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 | +| vector_multimap | 1 | 1a | 1 | 1 at end, else n | 1 at end, else n | log(n) | 1 | +| deque | 1 | 1a | 1 | 1 at begin or end, else n / 2 | 1 at begin or end, else n / 2 | n | n log(n) | +| bit_vector | 1 | 1a | 1 | 1 at end, else n | 1 at end, else n | n | n log(n) | +| string, cow_string | 1 | 1a | 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). +* a 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 v(37); // Reserve space to hold up to 37 items. + + or + +vector 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 + + 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(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 v; + +... + + v.set_capacity(); +``` + +An example of resizing to zero and completely freeing the memory of a vector is the following: + +```cpp +vector 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 WPtr; + + list 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 WPtr; // WidgetDelete is a custom destroyer. + + list 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::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::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::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. + +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 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 v; + + + + quick_sort((uint64_t*)v.data(), (uint64_t*)(v.data() + v.size())); +``` + +### Search hash_map 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 hashMap; + + hash_map::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 + + DoSomething(T t) { + + assert(is_floating_point::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 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 +* +1 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 collisionList(allocator("collision list"));` + +or + +```cpp +list 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 +* + +**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 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, StackAllocator> wSet(pStackAllocator); + + + + 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 str("hello world"); + + fixed_substring 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 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 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 diff --git a/lib/EASTL/doc/Bonus/tuple_vector_readme.md b/lib/EASTL/doc/Bonus/tuple_vector_readme.md new file mode 100644 index 000000000..f406ac5a4 --- /dev/null +++ b/lib/EASTL/doc/Bonus/tuple_vector_readme.md @@ -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 entityVec; +``` + +...the `tuple_vector` equivalent of this can be defined as: + +``` +tuple_vector 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 operator[](size_type) +tuple at(size_type) +tuple iterator::operator*() +tuple move_iterator::operator*() +tuple data() + +// extract the Ith tuple element pointer from the tuple_vector +template +T* get() +// 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 +T* get() +// e.g. bool* get(), float* get(), and Vec3* get() +``` + +And `push_back(...)` has the following overloads, accepting either values or tuples as needed. + +``` +tuple push_back() +push_back(const bool&, const float&, const Vec3&) +push_back(tuple) +push_back(bool&&, float&&, Vec3&&) +push_back(tuple) +``` +...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` have typedefs available in the case of not wanting to use +automatic type deduction: +``` +typedef eastl::tuple value_tuple; +typedef eastl::tuple reference_tuple; +typedef eastl::tuple const_reference_tuple; +typedef eastl::tuple ptr_tuple; +typedef eastl::tuple const_ptr_tuple; +typedef eastl::tuple 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 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` has the following counterpart: + + eastl::fixed_vector + +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` available as well: + + eastl::fixed_tuple_vector + +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 + +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 + +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/erase ` | 1.7 ms | 1.7 ms | 1.00 +`tuple_vector/erase ` | 104.6 ms | 106.3 ms | 0.98 +`tuple_vector/reallocate ` | 1.3 ms | 1.7 ms | 0.77 - + | | | +`tuple_vector/erase ` | 3.4 ms | 3.5 ms | 0.98 +`tuple_vector/insert ` | 3.4 ms | 3.4 ms | 0.99 +`tuple_vector/iteration ` | 56.3 us | 81.4 us | 0.69 - +`tuple_vector/operator[] ` | 67.4 us | 61.8 us | 1.09 +`tuple_vector/push_back ` | 1.3 ms | 818.3 us | 1.53 + +`tuple_vector/sort ` | 5.8 ms | 7.3 ms | 0.80 + | | | +`tuple_vector/erase ` | 34.7 ms | 32.9 ms | 1.05 +`tuple_vector/insert ` | 41.0 ms | 32.6 ms | 1.26 +`tuple_vector/iteration ` | 247.1 us | 80.5 us | 3.07 + +`tuple_vector/operator[]` | 695.7 us | 81.1 us | 8.58 + +`tuple_vector/push_back ` | 10.0 ms | 6.0 ms | 1.67 + +`tuple_vector/sort ` | 8.2 ms | 10.1 ms | 0.81 + | | | +`vector/erase ` | 1.3 ms | 1.2 ms | 1.05 +`vector/erase ` | 104.4 ms | 109.4 ms | 0.95 +`vector/reallocate ` | 1.5 ms | 1.5 ms | 0.95 + | | | +`vector/erase ` | 4.3 ms | 3.6 ms | 1.20 +`vector/insert ` | 4.8 ms | 4.8 ms | 1.01 +`vector/iteration ` | 71.5 us | 77.3 us | 0.92 +`vector/operator[] ` | 90.7 us | 87.2 us | 1.04 +`vector/push_back ` | 1.6 ms | 1.2 ms | 1.38 + +`vector/sort ` | 7.7 ms | 8.2 ms | 0.93 + +First off, `tuple_vector`'s performance versus `std::vector` 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` 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` 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 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 entityVec; +``` +...and then access each tuple element by typename like +`entityVec.get()`; or, creating an enumerated value to replace +the indices... + +``` +enum EntityTypeEnum +{ + entityX = 0, + entityY = 1, + entityZ = 2, + entityLifetime = 3 +}; + +tuple_vector entityVec; +``` + +...and then access each tuple element by the enumerated value: +`entityVec.get()`. + +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 entityVec; +``` +and be able to reference the tuple elements not just by typename or index, but +through their corresponding symbol, like `entityVec.get()`. 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. diff --git a/lib/EASTL/doc/CMake/EASTL_Project_Integration.md b/lib/EASTL/doc/CMake/EASTL_Project_Integration.md new file mode 100644 index 000000000..4b014f9e8 --- /dev/null +++ b/lib/EASTL/doc/CMake/EASTL_Project_Integration.md @@ -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]; +} +``` diff --git a/lib/EASTL/doc/Design.md b/lib/EASTL/doc/Design.md new file mode 100644 index 000000000..5877bb780 --- /dev/null +++ b/lib/EASTL/doc/Design.md @@ -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: + +* 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. +* 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 container + +{ + +public: + + typedef container 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 reverse_iterator; + + typedef reverse_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 + +bool operator==(const container& a, const container& b); + + + +template + +bool operator!=(const container& a, const +container& +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 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 + + struct rebind { typedef allocator other; }; + + + + allocator() throw(); + + allocator(const allocator&) throw(); + + template + + allocator(const allocator&) throw(); + + + + ~allocator() +throw(); + + + + pointer + address(reference x) const; + + const_pointer address(const_reference x) +const; + + pointer allocate(size_type, typename +allocator::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 but instead use allocator\::rebind\::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 diff --git a/lib/EASTL/doc/EASTL-n2271.pdf b/lib/EASTL/doc/EASTL-n2271.pdf new file mode 100644 index 000000000..8a1b0b9eb Binary files /dev/null and b/lib/EASTL/doc/EASTL-n2271.pdf differ diff --git a/lib/EASTL/doc/EASTL.natvis b/lib/EASTL/doc/EASTL.natvis new file mode 100644 index 000000000..2fb311b19 --- /dev/null +++ b/lib/EASTL/doc/EASTL.natvis @@ -0,0 +1,635 @@ + + + + + + + + ({(void*)mPair.mFirst} = {*mPair.mFirst}) + ({nullptr}) + + (void*)mPair.mFirst + *mPair.mFirst + + + + + ({(void*)mpValue} = {*mpValue}) + ({nullptr}) + + (void*)mpValue + *mpValue + mpRefCount->mRefCount + mpRefCount->mWeakRefCount + + + + + {((mpRefCount && mpRefCount->mRefCount) ? mpValue : nullptr)} + + mpRefCount && mpRefCount->mRefCount ? mpValue : nullptr + + + + + [{$T2}] {{}} + [{$T2}] {{ {*mValue} }} + [{$T2}] {{ {*mValue}, {*(mValue+1)} }} + [{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)} }} + [{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)}, {*(mValue+3)} }} + [{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)}, {*(mValue+3)}, {*(mValue+4)} }} + [{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)}, {*(mValue+3)}, {*(mValue+4)}, {*(mValue+5)} }} + [{$T2}] {{ {*mValue}, {*(mValue+1)}, {*(mValue+2)}, {*(mValue+3)}, {*(mValue+4)}, {*(mValue+5)}, ... }} + + $T2 + + $T2 + mValue + + + + + + "{mPair.mFirst.heap.mpBegin,sb}" + "{mPair.mFirst.sso.mData,sb}" + + mPair.mFirst.heap.mnSize + (mPair.mFirst.heap.mnCapacity & ~kHeapMask) + mPair.mFirst.heap.mpBegin,sb + + mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize + SSOLayout::SSO_CAPACITY + mPair.mFirst.sso.mData,sb + + !!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask) + + + + + + {mPair.mFirst.heap.mpBegin,su} + {mPair.mFirst.sso.mData,su} + + mPair.mFirst.heap.mnSize + (mPair.mFirst.heap.mnCapacity & ~kHeapMask) + mPair.mFirst.heap.mpBegin,su + + mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize + SSOLayout::SSO_CAPACITY + mPair.mFirst.sso.mData,su + + !!(mPair.mFirst.sso.mRemainingSizeField.mnRemainingSize & kSSOMask) + + + + + ({first}, {second}) + + first + second + + + + + [{mnSize}] {{}} + [{mnSize}] {{ {*mpData} }} + [{mnSize}] {{ {*mpData}, {*(mpData+1)} }} + [{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)} }} + [{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)}, {*(mpData+3)} }} + [{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)}, {*(mpData+3)}, {*(mpData+4)} }} + [{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)}, {*(mpData+3)}, {*(mpData+4)}, {*(mpData+5)} }} + [{mnSize}] {{ {*mpData}, {*(mpData+1)}, {*(mpData+2)}, {*(mpData+3)}, {*(mpData+4)}, {*(mpData+5)}, ... }} + + mnSize + + mnSize + mpData + + + + + + [{mpEnd - mpBegin}] {{}} + [{mpEnd - mpBegin}] {{ {*mpBegin} }} + [{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)} }} + [{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)} }} + [{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)}, {*(mpBegin+3)} }} + [{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)}, {*(mpBegin+3)}, {*(mpBegin+4)} }} + [{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)}, {*(mpBegin+3)}, {*(mpBegin+4)}, {*(mpBegin+5)} }} + [{mpEnd - mpBegin}] {{ {*mpBegin}, {*(mpBegin+1)}, {*(mpBegin+2)}, {*(mpBegin+3)}, {*(mpBegin+4)}, {*(mpBegin+5)}, ... }} + + mpEnd - mpBegin + mCapacityAllocator.mFirst - mpBegin + + mpEnd - mpBegin + mpBegin + + + + + + + [0] {{}} + + + [1] {{ {*mItBegin.mpCurrent} }} + + + [{(mItEnd.mpCurrentArrayPtr - mItBegin.mpCurrentArrayPtr) * $T3 + (mItEnd.mpCurrent-mItEnd.mpBegin) - (mItBegin.mpCurrent-mItBegin.mpBegin)}] + {{ + {*mItBegin.mpCurrent}, + ... + }} + + + (mItEnd.mpCurrentArrayPtr - mItBegin.mpCurrentArrayPtr) * $T3 + (mItEnd.mpCurrent-mItEnd.mpBegin) - (mItBegin.mpCurrent-mItBegin.mpBegin) + + (mItEnd.mpCurrentArrayPtr - mItBegin.mpCurrentArrayPtr) * $T3 + (mItEnd.mpCurrent-mItEnd.mpBegin) - (mItBegin.mpCurrent-mItBegin.mpBegin) + mItBegin.mpCurrentArrayPtr[(mItBegin.mpCurrent-mItBegin.mpBegin + $i) / $T3][(mItBegin.mpCurrent-mItBegin.mpBegin + $i) % $T3] + + + + + + {*mpCurrent} + + *mpCurrent + *(*(mpCurrentArrayPtr-1) + (mpEnd-mpBegin) - 1) + *(mpCurrent-1) + **(mpCurrentArrayPtr+1) + *(mpCurrent+1) + mpCurrent == mpBegin + mpCurrent+1 == mpEnd + + + + + + + {c} + + c + + + + + + [0] {{}} + + + [1] {{ {((eastl::ListNode<$T1>*)mNodeAllocator.mFirst.mpNext)->mValue} }} + + + [2] + {{ + {((eastl::ListNode<$T1>*)mNodeAllocator.mFirst.mpNext)->mValue}, + {((eastl::ListNode<$T1>*)mNodeAllocator.mFirst.mpNext->mpNext)->mValue} + }} + + + [?] + {{ + {((eastl::ListNode<$T1>*)mNodeAllocator.mFirst.mpNext)->mValue}, + {((eastl::ListNode<$T1>*)mNodeAllocator.mFirst.mpNext->mpNext)->mValue}, + ... + }} + + + + Content of lists will repeat indefinitely. Keep that in mind! + + + mNodeAllocator.mFirst.mpNext + mpNext + ((eastl::ListNode<$T1>*)this)->mValue + + + + + + {mValue} + + mValue + *(eastl::ListNode<$T1>*)mpNext + *(eastl::ListNode<$T1>*)mpPrev + + Content of lists will repeat indefinitely. Keep that in mind! + + + The rest of the list follows: + + + (eastl::ListNode<$T1>*)mpNext->mpNext + (eastl::ListNode<$T1>*)mpNext + mValue + + + + + + {*mpNode} + + mpNode + + + + + + [0] {{}} + + + [1] + {{ + {((eastl::SListNode<$T1>*)mNode.mpNext)->mValue} + }} + + + [2] + {{ + {((eastl::SListNode<$T1>*)mNode.mpNext)->mValue}, + {((eastl::SListNode<$T1>*)mNode.mpNext->mpNext)->mValue} + }} + + + [?] + {{ + {((eastl::SListNode<$T1>*)mNode.mpNext)->mValue}, + {((eastl::SListNode<$T1>*)mNode.mpNext->mpNext)->mValue}, + ... + }} + + + + mNode.mpNext + mpNext + ((eastl::SListNode<$T1>*)this)->mValue + + + + + + {mValue} + + mValue + *(eastl::SListNode<$T1>*)mpNext + + The rest of the list follows: + + + mpNext == nullptr ? nullptr : (eastl::SListNode<$T1>*)mpNext->mpNext + (eastl::SListNode<$T1>*)mpNext + mValue + + + + + + {*mpNode} + + *mpNode + + + + + [0] {{}} + [1] {{ {mAnchor.mpNext} }} + [?] {{ {mAnchor.mpNext}, ... }} + + + Content of intrusive lists will repeat indefinitely. Keep that in mind! + + + mAnchor.mpNext + mpNext + *this + + + + + + {*mpNode} + + *mpNode + + + + + + + [0] {{}} + + + [1] + {{ + {((eastl::rbtree_node<$T1>*)mAnchor.mpNodeLeft)->mValue} + }} + + + [{mnSize}] + {{ + {((eastl::rbtree_node<$T1>*)mAnchor.mpNodeLeft)->mValue}, + ... + }} + + + mnSize + + mnSize + mAnchor.mpNodeParent + mpNodeLeft + mpNodeRight + ((eastl::rbtree_node<$T1>*)this)->mValue + + + + + + + [0] {{}} + + + [1] + {{ + {((eastl::rbtree_node<$T2>*)mAnchor.mpNodeLeft)->mValue} + }} + + + [{mnSize}] + {{ + {((eastl::rbtree_node<$T2>*)mAnchor.mpNodeLeft)->mValue}, + ... + }} + + + mnSize + + mnSize + mAnchor.mpNodeParent + mpNodeLeft + mpNodeRight + ((eastl::rbtree_node<$T2>*)this)->mValue + + + + + + {mValue} + + mValue + + It is possible to expand parents that do not exist. + + *(eastl::rbtree_node<$T2>*)(mpNodeParent.value & (~uintptr_t(1))) + *(eastl::rbtree_node<$T2>*)mpNodeLeft + *(eastl::rbtree_node<$T2>*)mpNodeRight + + + + + {*mpNode} + + mpNode + + + + + + [{mnElementCount}] {{}} + [{mnElementCount}] {{ ... }} + + + mnBucketCount + mpBucketArray + + + + + + {mValue}, {*mpNext} + {mValue} + + + + this + mpNext + mValue + + + + + + {mpNode->mValue} + + mpNode->mValue + + + + + {*(mIterator-1)} + + mIterator-1 + + + + + {{count = {kSize}}} + + kSize + + + + + + kSize + + + bBitValue = ((mWord[iWord] >> iBitInWord) % 2) != 0 ? true : false + bBitValue + iBitInWord++ + + iWord++ + iBitInWord = 0 + + + + + + + + {c} + + c + + + + + {mpBegin,[mnCount]} + mpBegin,[mnCount] + + + + ({mFirst}, {mSecond}) + ({mSecond}) + ({mFirst}) + (empty) + (empty) + ({mFirst}, {mSecond}) + + + + + nullopt + {value()} + + value() + + + + + {$T1} to {$T2}} + + + + + {mRep} nanoseconds + + + + {mRep} microseconds + + + + {mRep} milliseconds + + + + {mRep} seconds + + + + {mRep} minutes + + + + {mRep} hours + + + + {mRep} duration with ratio = [{$T2} : {$T3}] + + + + + + empty + {mInvokeFuncPtr} + + + + + {*val} + + + + + empty + {m_storage.external_storage} + + + + + + {mFlag.mAtomic} + + + + + [valueless_by_exception] + {{ index=0, value={($T1*)mStorage.mBuffer.mCharData}} + {{ index=1, value={($T2*)mStorage.mBuffer.mCharData}} + {{ index=2, value={($T3*)mStorage.mBuffer.mCharData}} + {{ index=3, value={($T4*)mStorage.mBuffer.mCharData}} + {{ index=4, value={($T5*)mStorage.mBuffer.mCharData}} + {{ index=5, value={($T6*)mStorage.mBuffer.mCharData}} + {{ index=6, value={($T7*)mStorage.mBuffer.mCharData}} + {{ index=7, value={($T8*)mStorage.mBuffer.mCharData}} + {{ index=8, value={($T9*)mStorage.mBuffer.mCharData}} + {{ index=9, value={($T10*)mStorage.mBuffer.mCharData}} + {{ index=10, value={($T11*)mStorage.mBuffer.mCharData}} + {{ index=11, value={($T12*)mStorage.mBuffer.mCharData}} + {{ index=12, value={($T13*)mStorage.mBuffer.mCharData}} + {{ index=13, value={($T14*)mStorage.mBuffer.mCharData}} + {{ index=14, value={($T15*)mStorage.mBuffer.mCharData}} + {{ index=15, value={($T16*)mStorage.mBuffer.mCharData}} + {{ index=16, value={($T17*)mStorage.mBuffer.mCharData}} + {{ index=17, value={($T18*)mStorage.mBuffer.mCharData}} + {{ index=18, value={($T19*)mStorage.mBuffer.mCharData}} + {{ index=19, value={($T20*)mStorage.mBuffer.mCharData}} + {{ index=20, value={($T21*)mStorage.mBuffer.mCharData}} + {{ index=21, value={($T22*)mStorage.mBuffer.mCharData}} + {{ index=22, value={($T23*)mStorage.mBuffer.mCharData}} + {{ index=23, value={($T24*)mStorage.mBuffer.mCharData}} + {{ index=24, value={($T25*)mStorage.mBuffer.mCharData}} + {{ index=25, value={($T26*)mStorage.mBuffer.mCharData}} + {{ index=26, value={($T27*)mStorage.mBuffer.mCharData}} + {{ index=27, value={($T28*)mStorage.mBuffer.mCharData}} + {{ index=28, value={($T29*)mStorage.mBuffer.mCharData}} + {{ index=29, value={($T30*)mStorage.mBuffer.mCharData}} + {{ index=30, value={($T31*)mStorage.mBuffer.mCharData}} + + index() + ($T1*)mStorage.mBuffer.mCharData + ($T2*)mStorage.mBuffer.mCharData + ($T3*)mStorage.mBuffer.mCharData + ($T4*)mStorage.mBuffer.mCharData + ($T5*)mStorage.mBuffer.mCharData + ($T6*)mStorage.mBuffer.mCharData + ($T7*)mStorage.mBuffer.mCharData + ($T8*)mStorage.mBuffer.mCharData + ($T9*)mStorage.mBuffer.mCharData + ($T10*)mStorage.mBuffer.mCharData + ($T11*)mStorage.mBuffer.mCharData + ($T12*)mStorage.mBuffer.mCharData + ($T13*)mStorage.mBuffer.mCharData + ($T14*)mStorage.mBuffer.mCharData + ($T15*)mStorage.mBuffer.mCharData + ($T16*)mStorage.mBuffer.mCharData + ($T17*)mStorage.mBuffer.mCharData + ($T18*)mStorage.mBuffer.mCharData + ($T19*)mStorage.mBuffer.mCharData + ($T20*)mStorage.mBuffer.mCharData + ($T21*)mStorage.mBuffer.mCharData + ($T22*)mStorage.mBuffer.mCharData + ($T23*)mStorage.mBuffer.mCharData + ($T24*)mStorage.mBuffer.mCharData + ($T25*)mStorage.mBuffer.mCharData + ($T26*)mStorage.mBuffer.mCharData + ($T27*)mStorage.mBuffer.mCharData + ($T28*)mStorage.mBuffer.mCharData + ($T29*)mStorage.mBuffer.mCharData + ($T30*)mStorage.mBuffer.mCharData + ($T31*)mStorage.mBuffer.mCharData + + + + + + + + diff --git a/lib/EASTL/doc/FAQ.md b/lib/EASTL/doc/FAQ.md new file mode 100644 index 000000000..1444c4839 --- /dev/null +++ b/lib/EASTL/doc/FAQ.md @@ -0,0 +1,2290 @@ +# EASTL FAQ + +We provide a FAQ (frequently asked questions) list here for a number of commonly asked questions about EASTL and STL in general. Feel free to suggest new FAQ additions based on your own experience. + +## Information + +1. [What is EASTL?](#info1-what-is-eastl) +2. [What uses are EASTL suitable for?](#info2-what-uses-are-eastl-suitable-for) +3. [How does EASTL differ from standard C++ STL?](#info3-how-does-eastl-differ-from-standard-c-stl) +4. [Is EASTL thread-safe?](#info4-is-eastl-thread-safe) +5. [What platforms/compilers does EASTL support?](#info5-what-platformscompilers-does-eastl-support) +6. [Why is there EASTL when there is the STL?](#info6-why-is-there-eastl-when-there-is-the-stl) +7. [Can I mix EASTL with standard C++ STL?](#info7-can-i-mix-eastl-with-standard-c-stl) +8. [Where can I learn more about STL and EASTL?](#info8-where-can-i-learn-more-about-stl-and-eastl) +9. [What is the legal status of EASTL?](#info9-what-is-the-legal-status-of-eastl) +10. [Does EASTL deal with compiler exception handling settings?](#info10-does-eastl-deal-with-compiler-exception-handling-settings) +11. [What C++ language features does EASTL use (e.g. virtual functions)?](#info11-what-c-language-features-does-eastl-use-eg-virtual-functions) +12. [What compiler warning levels does EASTL support?](#info12-what-compiler-warning-levels-does-eastl-support) +13. [Is EASTL compatible with Lint?](#info13-is-eastl-compatible-with-lint) +14. [What compiler settings do I need to compile EASTL?](#info14-what-compiler-settings-do-i-need-to-compile-eastl) +15. [How hard is it to incorporate EASTL into my project?](#info15-how-hard-is-it-to-incorporate-eastl-into-my-project) +16. [Should I use EASTL instead of std STL or instead of my custom library?](#info16-should-i-use-eastl-instead-of-std-stl-or-instead-of-my-custom-library) +17. [I think I've found a bug. What do I do?](#info17-i-think-ive-found-a-bug-what-do-i-do) +18. [Can EASTL be used by third party EA developers?](#info18-can-eastl-be-used-by-third-party-ea-developers) + +## Performance + +1. [How efficient is EASTL compared to standard C++ STL implementations?](#perf1-how-efficient-is-eastl-compared-to-standard-c-stl-implementations) +2. [How efficient is EASTL in general?](#perf2-how-efficient-is-eastl-in-general) +3. [Strings don't appear to use the "copy-on-write" optimization. Why not?](#perf3-strings-dont-appear-to-use-the-copy-on-write-cow-optimization-why-not) +4. [Does EASTL cause code bloat, given that it uses templates?](#perf4-does-eastl-cause-code-bloat-given-that-it-uses-templates) +5. [Don't STL and EASTL containers fragment memory?](#perf5-dont-stl-and-eastl-containers-fragment-memory) +6. [I don't see container optimizations for equivalent scalar types such as pointer types. Why?](#perf6-i-dont-see-container-optimizations-for-equivalent-scalar-types-such-as-pointer-types-why) +7. [I've seen some STL's provide a default quick "node allocator" as the default allocator. Why doesn't EASTL do this?](#perf7-ive-seen-some-stls-provide-a-default-quick-node-allocator-as-the-default-allocator-why-doesnt-eastl-do-this) +8. [Templates sometimes seem to take a long time to compile. Why do I do about that?](#perf8-templates-sometimes-seem-to-take-a-long-time-to-compile-why-do-i-do-about-that) +9. [How do I assign a custom allocator to an EASTL container?](#cont8-how-do-i-assign-a-custom-allocator-to-an-eastl-container) +10. [How well does EASTL inline?](#perf10-how-well-does-eastl-inline) +11. [How do I control function inlining?](#perf11-how-do-i-control-function-inlining) +12. [C++ / EASTL seems to bloat my .obj files much more than C does.](#perf12-c--eastl-seems-to-bloat-my-obj-files-much-more-than-c-does) +13. [What are the best compiler settings for EASTL?](#perf13-what-are-the-best-compiler-settings-for-eastl) + +## Problems + +1. [I'm getting screwy behavior in sorting algorithms or sorted containers. What's wrong?](#prob1-im-getting-screwy-behavior-in-sorting-algorithms-or-sorted-containers-whats-wrong) +2. [I am getting compiler warnings (e.g. C4244, C4242 or C4267) that make no sense. Why?](#prob2-i-am-getting-compiler-warnings-eg-c4244-c4242-or-c4267-that-make-no-sense-why) +3. [I am getting compiler warning C4530, which complains about exception handling and "unwind semantics." What gives?](#prob3-i-am-getting-compiler-warning-c4530-which-complains-about-exception-handling-and-unwind-semantics-what-gives) +4. [Why are tree-based containers hard to read with a debugger?](#prob4-why-are-tree-based-eastl-containers-hard-to-read-with-a-debugger) +5. [The EASTL source code is sometimes rather complicated looking. Why is that?](#prob5-the-eastl-source-code-is-sometimes-rather-complicated-looking-why-is-that) +6. [When I get compilation errors, they are very long and complicated looking. What do I do?](#prob6-when-i-get-compilation-errors-they-are-very-long-and-complicated-looking-what-do-i-do) +7. [Templates sometimes seem to take a long time to compile. Why do I do about that?](#prob7-templates-sometimes-seem-to-take-a-long-time-to-compile-why-do-i-do-about-that) +8. [I get the compiler error: "template instantiation depth exceeds maximum of 17. use -ftemplate-depth-NN to increase the maximum"](#prob8-i-get-the-compiler-error-template-instantiation-depth-exceeds-maximum-of-17-use--ftemplate-depth-nn-to-increase-the-maximum) +9. [I'm getting errors about min and max while compiling.](#prob9-im-getting-errors-about-min-and-max-while-compiling) +10. [C++ / EASTL seems to bloat my .obj files much more than C does.](#prob10-c--eastl-seems-to-bloat-my-obj-files-much-more-than-c-does) +11. [I'm getting compiler errors regarding operator new being previously defined.](#prob11-im-getting-compiler-errors-regarding-placement-operator-new-being-previously-defined) +12. [I'm getting errors related to wchar_t string functions such as wcslen().](#prob12-im-getting-errors-related-to-wchar_t-string--functions-such-as-wcslen) +13. [I'm getting compiler warning C4619: there is no warning number Cxxxx (e.g. C4217).](#prob13-im-getting-compiler-warning-c4619-there-is-no-warning-number-cxxxx-eg-c4217) +14. [My stack-based fixed_vector is not respecting the object alignment requirements.](#prob14-my-stack-based-fixed_vector-is-not-respecting-the-object-alignment-requirements) +15. [I am getting compiler errors when using GCC under XCode (Macintosh/iphone).](#prob15-i-am-getting-compiler-errors-when-using-gcc-under-xcode-macintoshiphone) +16. [I am getting linker errors about Vsnprintf8 or Vsnprintf16.](#prob16-i-am-getting-linker-errors-about-vsnprintf8-or-vsnprintf16) +17. [I am getting compiler errors about UINT64_C or UINT32_C.](#prob17-i-am-getting-compiler-errors-about-uint64_c-or-uint32_c) +18. [I am getting a crash with a global EASTL container.](#prob18-i-am-getting-a-crash-with-a-global-eastl-container) +19. [Why doesn't EASTL support passing NULL to functions with pointer arguments?](#prob19-why-doesnt-eastl-support-passing-null-string-functions) + +## Debug + +1. [How do I get VC++ mouse-overs to view templated data?](#debug1-how-do-i-set-the-vc-debugger-to-display-eastl-container-data-with-tooltips) +2. [How do I view containers if the visualizer/tooltip support is not present?](#debug2-how-do-i-view-containers-if-the-visualizertooltip-support-is-not-present) +3. [The EASTL source code is sometimes rather complicated looking. Why is that?](#debug3-the-eastl-source-code-is-sometimes-rather-complicated-looking-why-is-that) +4. [When I get compilation errors, they are very long and complicated looking. What do I do?](#debug4-when-i-get-compilation-errors-they-are-very-long-and-complicated-looking-what-do-i-do) +5. [How do I measure hash table balancing?](#debug5-how-do-i-measure-hash-table-balancing) + +## Containers + +1. [Why do some containers have "fixed" versions (e.g. fixed_list) but others(e.g. deque) don't have fixed versions?](#cont1-why-do-some-containers-have-fixed-versions-eg-fixed_list-but-otherseg-deque-dont-have-fixed-versions) +2. [Can I mix EASTL with standard C++ STL?](#cont2-can-i-mix-eastl-with-standard-c-stl) +3. [Why are there so many containers?](#cont3-why-are-there-so-many-containers) +4. [Don't STL and EASTL containers fragment memory?](#cont4-dont-stl-and-eastl-containers-fragment-memory) +5. [I don't see container optimizations for equivalent scalar types such as pointer types. Why?](#cont5-i-dont-see-container-optimizations-for-equivalent-scalar-types-such-as-pointer-types-why) +6. [What about alternative container and algorithm implementations (e.g. treaps, skip lists, avl trees)?](#cont6-what-about-alternative-container-and-algorithm-implementations-eg-treaps-skip-lists-avl-trees) +7. [Why are containers hard to read with a debugger?](#cont7-why-are-tree-based-eastl-containers-hard-to-read-with-a-debugger) +8. [How do I assign a custom allocator to an EASTL container?](#cont8-how-do-i-assign-a-custom-allocator-to-an-eastl-container) +9. [How do I set the VC++ debugger to display EASTL container data with tooltips?](#cont9-how-do-i-set-the-vc-debugger-to-display-eastl-container-data-with-tooltips) +10. [How do I use a memory pool with a container?](#cont10-how-do-i-use-a-memory-pool-with-a-container) +11. [How do I write a comparison (operator<()) for a struct that contains two or more members?](#cont11-how-do-i-write-a-comparison-operator-for-a-struct-that-contains-two-or-more-members) +12. [Why doesn't container X have member function Y?](#cont12-why-doesnt-container-x-have-member-function-y) +13. [How do I search a hash_map of strings via a char pointer efficiently? If I use map.find("hello") it creates a temporary string, which is inefficient.](#cont13-how-do-i-search-a-hash_map-of-strings-via-a-char-pointer-efficiently-if-i-use-mapfindhello-it-creates-a-temporary-string-which-is-inefficient) +14. [Why are set and hash_set iterators const (i.e. const_iterator)?](#cont14-why-are-set-and-hash_set-iterators-const-ie-const_iterator) +15. [How do I prevent my hash container from re-hashing?](#cont15-how-do-i-prevent-my-hash-container-from-re-hashing) +16. [Which uses less memory, a map or a hash_map?](#cont16-which-uses-less-memory-a-map-or-a-hash_map) +17. [How do I write a custom hash function?](#cont17-how-do-i-write-a-custom-hash-function) +18. [How do I write a custom compare function for a map or set?](#cont18-how-do-i-write-a-custom-compare-function-for-a-map-or-set) +19. [How do I force my vector or string capacity down to the size of the container?](#cont19-how-do-i-force-my-vector-or-string-capacity-down-to-the-size-of-the-container) +20. [How do I iterate a container while (selectively) removing items from it?](#cont20-how-do-i-iterate-a-container-while-selectively-removing-items-from-it) +21. [How do I store a pointer in a container?](#cont21-how-do-i-store-a-pointer-in-a-container) +22. [How do I make a union of two containers? difference? intersection?](#cont22-how-do-i-make-a-union-of-two-containers-difference-intersection) +23. [How do I override the default global allocator?](#cont23-how-do-i-override-the-default-global-allocator) +24. [How do I do trick X with the string container?](#cont24-how-do-i-do-trick-x-with-the-string-container) +25. [How do EASTL smart pointers compare to Boost smart pointers?](#cont25-how-do-eastl-smart-pointers-compare-to-boost-smart-pointers) +26. [How do your forward-declare an EASTL container?](#cont26-how-do-your-forward-declare-an-eastl-container) +27. [How do I make two containers share a memory pool?](#cont27-how-do-i-make-two-containers-share-a-memory-pool) +28. [Can I use a std (STL) allocator with EASTL?](#cont28-can-i-use-a-std-stl-allocator-with-eastl) +29. [What are the requirements of classes stored in containers?](#what-are-the-requirements-of-classes-stored-in-containers) + +## Algorithms + +1. [I'm getting screwy behavior in sorting algorithms or sorted containers. What's wrong?](#algo1-im-getting-screwy-behavior-in-sorting-algorithms-or-sorted-containers-whats-wrong) +2. [How do I write a comparison (operator<()) for a struct that contains two or more members?](#algo2-how-do-i-write-a-comparison-operator-for-a-struct-that-contains-two-or-more-members) +3. [How do I sort something in reverse order?](#algo3-how-do-i-sort-something-in-reverse-order) +4. [I'm getting errors about min and max while compiling.](#algo4-im-getting-errors-about-min-and-max-while-compiling) +5. [Why don't algorithms take a container as an argument instead of iterators? A container would be more convenient.](#algo5-why-dont-algorithms-take-a-container-as-an-argument-instead-of-iterators-a-container-would-be-more-convenient) +6. [Given a container of pointers, how do I find an element by value (instead of by pointer)?](#algo6-given-a-container-of-pointers-how-do-i-find-an-element-by-value-instead-of-by-pointer) +7. [When do stored objects need to support opertor < vs. when do they need to support operator ==?](#algo7-when-do-stored-objects-need-to-support-operator--vs-when-do-they-need-to-support-operator-) +8. [How do I sort via pointers or array indexes instead of objects directly?](#algo8-how-do-i-sort-via-pointers-or-array-indexes-instead-of-objects-directly) + +## Iterators + +1. [What's the difference between iterator, const iterator, and const_iterator?](#iter1-whats-the-difference-between-iterator-const-iterator-and-const_iterator) +2. [How do I tell from an iterator what type of thing it is iterating?](#iter2-how-do-i-tell-from-an-iterator-what-type-of-thing-it-is-iterating) +3. [How do I iterate a container while (selectively) removing items from it?](#iter3-how-do-i-iterate-a-container-while-selectively-removing-items-from-it) +4. [What is an insert_iterator?](#iter4-what-is-an-insert_iterator) + +## Information + +### Info.1 What is EASTL? + +EASTL refers to "EA Standard Template Library." It is a C++ template library that is analogous to the template facilities of the C++ standard library, which are often referred to as the STL. EASTL consists of the following systems: + +* Containers +* Iterators +* Algorithms +* Utilities +* Smart pointers +* Type traits + +Of these, the last two (smart pointers and type traits) do not have analogs in standard C++. With respect to the other items, EASTL provides extensions and optimizations over the equivalents in standard C++ STL. + +EASTL is a professional-level implementation which outperforms commercial implementations (where functionality overlaps) and is significantly easier to read and debug. + +### Info.2 What uses are EASTL suitable for? + +EASTL is suitable for any place where templated containers and algorithms would be appropriate. Thus any C++ tools could use it and many C++ game runtimes could use it, especially 2005+ generation game platforms. EASTL has optimizations that make it more suited to the CPUs and memory systems found on console platforms. Additionally, EASTL has some type-traits and iterator-traits-derived template optimizations that make it generally more efficient than home-brew templated containers. + +### Info.3 How does EASTL differ from standard C++ STL? + +There are three kinds of ways that EASTL differs from standard STL: + +* EASTL equivalents to STL sometimes differ. +* EASTL implementations sometimes differ from STL implementations of the same thing. +* EASTL has functionality that doesn't exist in STL. + +With respect to item #1, the changes are such that they benefit game development and and not the type that could silently hurt you if you were more familiar with STL interfaces. + +With respect to item #2, where EASTL implementations differ from STL implementations it is almost always due to improvements being made in the EASTL versions or tradeoffs being made which are considered better for game development. + +With respect to item #3, there are a number of facilities that EASTL has that STL doesn't have, such as intrusive_list and slist containers, smart pointers, and type traits. All of these are facilities that assist in making more efficient game code and data. + +Ways in which EASTL is better than standard STL: + +* Has higher performance in release builds, sometimes dramatically so. +* Has significantly higher performance in debug builds, due to less call overhead. +* Has extended per-container functionality, particularly for game development. +* Has additional containers that are useful for high performance game development. +* Is easier to read, trace, and debug. +* Memory allocation is much simpler and more controllable. +* Has higher portability, as there is a single implementation for all platforms. +* Has support of object alignment, whereas such functionality is not natively supported by STL. +* We have control over it, so we can modify it as we like. +* Has stricter standards for container design and behavior, particularly as this benefits game development. + +Ways in which EASTL is worse than standard STL: + +* Standard STL implementations are currently very reliable and weather-worn, whereas EASTL is less tested. +* Standard STL is automatically available with just about every C++ compiler vendor's library. +* Standard STL is supported by the compiler vendor and somewhat by the Internet community. + +#### EASTL coverage of std STL + +* list +* vector +* deque +* string +* set +* multiset +* map +* multimap +* bitset +* queue +* stack +* priority_queue +* memory +* numeric +* algorithm (all but inplace_merge, prev_permutation, next_permutation, nth_element, includes, unique_copy) +* utility +* functional +* iterator + +EASTL additions/amendments to std STL + +* allocators work in a simpler way. +* exception handling can be disabled. +* all containers expose/declare their node size, so you can make a node allocator for them. +* all containers have reset(), which unilaterally forgets their contents. +* all containers have validate() and validate_iterator() functions. +* all containers understand and respect object alignment requirements. +* all containers guarantee no memory allocation upon being newly created as empty. +* all containers and their iterators can be viewed in a debugger (no other STL does this, believe it or not). +* linear containers guarantee linear memory. +* vector has push_back(void). +* vector has a data() function. +* vector is actually a vector of type bool. +* vector and string have set_capacity(). +* string has sprintf(), append_sprintf(), trim(), compare_i(), make_lower(), make_upper(). +* deque allows you to specify the subarray size. +* list has a push_front(void) and push_back(void) function. +* hash_map, hash_set, etc. have find_as(). + +EASTL coverage of TR1 (tr1 refers to proposed additions for the next C++ standard library, ~2008) + +* array +* type_traits (there are about 30 of these) +* unordered_set (EASTL calls it hash_set) +* unordered_multiset +* unordered_map +* unordered_multimap +* shared_ptr, shared_array, weak_ptr, scoped_ptr, scoped_array, intrusive_ptr + +EASTL additional functionality (not found elsewhere) + +* fixed_list +* fixed_slist +* fixed_vector +* fixed_string +* fixed_substring +* fixed_set +* fixed_multiset +* fixed_map +* fixed_multimap +* fixed_hash_set +* fixed_hash_multiset +* fixed_hash_map +* fixed_hash_multimap +* vector_set +* vector_multiset +* vector_map +* vector_multimap +* intrusive_list +* intrusive_slist +* intrusive_sdlist +* intrusive_hash_set +* intrusive_hash_multiset +* intrusive_hash_map +* intrusive_hash_multimap +* slist (STLPort's STL has this) +* heap +* linked_ptr, linked_array +* sparse_matrix (this is not complete as of this writing) +* ring_buffer +* compressed_pair +* call_traits +* binary_search_i, change_heap, find_first_not_of, find_last_of, find_last_not_of, identical +* comb_sort, bubble_sort, selection_sort, shaker_sort, bucket_sort +* equal_to_2, not_equal_to_2, str_equal_to, str_equal_to_i + +### Info.4 Is EASTL thread-safe? + +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 individual 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 expected 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. + +### Info.5 What platforms/compilers does EASTL support? + +EASTL's support depends entirely on the compiler and not on the platform. EASTL works on any C++ compiler that completely conforms the C++ language standard. Additionally, EASTL is 32 bit and 64 bit compatible. Since EASTL does not use the C or C++ standard library (with a couple small exceptions), it doesn't matter what kind of libraries are provided (or not provided) by the compiler vendor. However, given that we need to work with some compilers that aren't 100% conforming to the language standard, it will be useful to make a list here of these that are supported and those that are not: + +| Compiler | Status | Notes | +|---------|--------|-------| +| GCC 2.9x | Supported | However, GCC 2.9x has some issues that you may run into that cause you to use EASTL facilities differently than a fully compliant compiler would allow. | +| GCC 3.x+ | Supported | This compiler is used by the Mac OSX, and Linux platforms. | +| MSVC 6.0 | Not supported | This compiler is too weak in the area of template and namespace support. | +| MSVC 7.0+ | Supported | This compiler is used by the PC and Win CE platforms | +| Borland 5.5+ | Not supported | Borland can successfully compile many parts of EASTL, but not all parts. | +| EDG | Supported | This is the compiler front end to some other compilers, such as Intel, and Comeau C++. | +| IBM XL 5.0+ | Supported | This compiler is sometimes used by PowerPC platforms such as Mac OSX and possibly future console platforms. | + +### Info.6 Why is there EASTL when there is the STL? + +The STL is largely a fine library for general purpose C++. However, we can improve upon it for our uses and gain other advantages as well. The primary motivations for the existence of EASTL are the following: + +* 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. +* 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. +* Note that there isn't actually anything in the C++ standard called "STL." STL is a term that merely refers to the templated portion of the C++ standard library. + +### Info.7 Can I mix EASTL with standard C++ STL? + +This is possible to some degree, though the extent depends on the implementation of C++ STL. One of things that makes interoperability is something called iterator categories. Containers and algorithms recognize iterator types via their category and STL iterator categories are not recognized by EASTL and vice versa. + +Things that you definitely can do: + +* #include both EASTL and standard STL headers from the same .cpp file. +* Use EASTL containers to hold STL containers. +* Construct an STL reverse_iterator from an EASTL iterator. +* Construct an EASTL reverse_iterator from an STL iterator. + +Things that you probably will be able to do, though a given std STL implementation may prevent it: + +* Use STL containers in EASTL algorithms. +* Use EASTL containers in STL algorithms. +* Construct or assign to an STL container via iterators into an EASTL container. +* Construct or assign to an EASTL container via iterators into an STL container. +* +Things that you would be able to do if the given std STL implementation is bug-free: + +* Use STL containers to hold EASTL containers. Unfortunately, VC7.x STL has a confirmed bug that prevents this. Similarly, STLPort versions prior to v5 have a similar but. + +Things that you definitely can't do: + +* Use an STL allocator directly with an EASTL container (though you can use one indirectly). +* Use an EASTL allocator directly with an STL container (though you can use one indirectly). + +### Info.8 Where can I learn more about STL and EASTL? + +EASTL is close enough in philosophy and functionality to standard C++ STL that most of what you read about STL applies to EASTL. This is particularly useful with respect to container specifications. It would take a lot of work to document EASTL containers and algorithms in fine detail, whereas most standard STL documentation applies as-is to EASTL. We won't cover the differences here, as that's found in another FAQ entry. + +That being said, we provide a list of sources for STL documentation that may be useful to you, especially if you are less familiar with the concepts of STL and template programming in general. + +* The SGI STL web site. Includes a good STL reference. +* CodeProject STL introduction. +* Scott Meyers Effective STL book. +* The Microsoft online STL documentation. Microsoft links go bad every couple months, so try searching for STL at the * Microsoft MSDN site. +* The Dinkumware online STL documentation. +* The C++ standard, which is fairly readable. You can buy an electronic version for about $18 and in the meantime you can make do with draft revisions of it off the Internet by searching for "c++ draft standard". +* STL performance tips, by Pete Isensee +* STL algorithms vs. hand-written loops, by Scott Meyers. + +### Info.9 What is the legal status of EASTL? + +EASTL is usable for all uses within Electronic Arts, both for internal usage and for shipping products for all platforms. All source code was written by a single EA engineer. Any externally derived code would be explicitly stated as such and approved by the legal department if such code ever gets introduced. As of EASTL v1.0, the red_black_tree.cpp file contains two functions derived from the original HP STL and have received EA legal approval for usage in any product. + +### Info.10 Does EASTL deal with compiler exception handling settings? + +EASTL has automatic knowledge of the compiler's enabling/disabling of exceptions. If your compiler is set to disable exceptions, EASTL automatically detects so and executes without them. Also, you can force-enable or force-disable that setting to override the automatic behavior by #defining EASTL_EXCEPTIONS_ENABLED to 0 or 1. See EASTL's config.h for more information. + +### Info.11 What C++ language features does EASTL use (e.g. virtual functions)? + +EASTL uses the following C++ language features: + +* Template functions, classes, member functions. +* Multiple inheritance. +* Namespaces. +* Operator overloading. + +EASTL does not use the following C++ language features: + +* Virtual functions / interfaces. +* RTTI (dynamic_cast). +* Global and static variables. There are a couple class static const variables, but they act much like enums. +* Volatile declarations +* Template export. +* Virtual inheritance. + +EASTL may use the following C++ language features: + +* Try/catch. This is an option that the user can enable and it defaults to whatever the compiler is set to use. +* Floating point math. Hash containers have one floating point calculation, but otherwise floating point is not used. + +Notes: + +* EASTL uses rather little of the standard C or C++ library and uses none of the C++ template library (STL) and iostream library. The memcpy family of functions is one example EASTL C++ library usage. +* EASTL never uses global new / delete / malloc / free. All allocations are done via user-specified allocators, though a default allocator definition is available. + + +### Info.12 What compiler warning levels does EASTL support? + +For VC++ EASTL should compile without warnings on level 4, and should compile without warnings for "warnings disabled by default" except C4242, C4514, C4710, C4786, and C4820. These latter warnings are somewhat draconian and most EA projects have little choice but to leave them disabled. + +For GCC, EASTL should compile without warnings with -Wall. Extensive testing beyond that hasn't been done. + +However, due to the nature of templated code generation and due to the way compilers compile templates, unforeseen warnings may occur in user code that may or may not be addressible by modifying EASTL. + +### Info.13 Is EASTL compatible with Lint? + +As of EASTL 1.0, minimal lint testing has occurred. Testing with the November 2005 release of Lint (8.00t) demonstrated bugs in Lint that made its analysisnot very useful. For example, Lint seems to get confused about the C++ typename keyword and spews many errors with code that uses it. We will work with the makers of Lint to get this resolved so that Lint can provide useful information about EASTL. + +### Info.14 What compiler settings do I need to compile EASTL? + +EASTL consists mostly of header files with templated C++ code, but there are also a few .cpp files that need to be compiled and linked in order to use some of the modules. EASTL will compile in just about any environment. As mentioned elsewhere in this FAQ, EASTL can be compiled at the highest warning level of most compilers, transparently deals with compiler exception handling settings, is savvy to most or all compilation language options (e.g. wchar_t is built-in or not, for loop variables are local or not), and has almost no platform-specific or compiler-specific code. For the most part, you can just drop it in and it will work. The primary thing that needs to be in place is that EASTL .cpp files need to be compiled with the same struct padding/alignment settings as other code in the project. This of course is the same for just about any C++ source code library. + +See the Performance section of this FAQ for a discussion of the optimal compiler settings for EASTL performance. + +### Info.15 How hard is it to incorporate EASTL into my project? + +It's probably trivial. + +EASTL has only one dependency: EABase. And EASTL auto-configures itself for most compiler environments and for the most typical configuration choices. Since it is fairly highly warning-free, you won't likely need to modify your compiler warning settings, even if they're pretty stict. EASTL has a few .cpp files which need to be compiled if you want to use the modules associated with those files. You can just compile those files with your regular compiler settings. Alternatively, you can use one of the EASTL project files. + +In its default configuration, the only thing you need to provide to make EASTL work is to define implementations of the following operator new functions: + +```cpp +#include + +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); +``` +The flags and debugFlags arguments correspond to PPMalloc/RenderWare GeneralAllocator/GeneralAllocatorDebug Malloc equivalents. + +### Info.16 Should I use EASTL instead of std STL or instead of my custom library? + +There are reasons you may want to use EASTL; there are reasons you may not want to use it. Ditto for std STL or any other library. Here we present a list of reasons (+ and -) for why you might want to use one or another. However, it should be noted that while EASTL contains functionality found in std STL, it has another ~40% of functionality not found in std STL, so EASTL and std STL (and whatever other template library you may have) are not mutually exclusive. + +**EASTL** +* \+ Has higher performance than any commercial STL, especially on console platforms. +* \+ Has extended functionality tailored for game development. +* \+ Is highly configurable, and we own it so it can be amended at will. Std STL is owned by a third party committee. +* \+ Is much easier to read and debug than other similar libraries, especiallly std STL. + + +* \- Is highly unit tested, but does not have the same level as std STL. +* \- Is more complicated than many users' lite template libraries, and may put off some beginners. +* \- EASTL + +**Std STL** + +* \+ Is highly portable; your STL code will likely compile and run anywhere. +* \+ Works without the need to install or download any package to use it. It just works. +* \+ Is highly reliable and supported by the compiler vendor. You can have confidence in it. +* \+ Some std STL versions (e.g. STLPort, VC8 STL) have better runtime debug checking than EASTL. + + +* \- Has (sometimes greatly) variable implementations, behavior, and performance between implementations. +* \- Is usually hard to read and debug. +* \- Doesn't support some of the needs of game development, such as aligned allocations, named allocations, intrusive containers, etc. +* \- Is not as efficient as EASTL, especially on console platforms. + +**Your own library** +(please forgive us for implying there may be weaknesses in your libraries) + +* \+ You have control over it and can make it work however you want. +* \+ You can fix bugs in it on the spot and have the fix in your codebase immediately. +* \+ Your own library can be highly integrated into your application code or development environment. + + +* \- Many custom libraries don't have the same level of testing as libraries such as std STL or EASTL. +* \- Many custom libraries don't have the same breadth or depth as std STL or especially EASTL. +* \- Many custom libraries don't have the level of performance tuning that std STL or especially EASTL has. + +### Info.17 I think I've found a bug. What do I do? + +**Verify that you indeed have a bug** + +There are various levels of bugs that can occur, which include the following: + +* Compiler warnings generated by EASTL. +* Compiler errors generated by EASTL (failure to compile well-formed code). +* Runtime misbehavior by EASTL (function does the wrong thing). +* Runtime crash or data corruption by EASTL. +* Mismatch between EASTL documentation and behavior. +* Mismatch between EASTL behavior and user's expections (mis-design). + +Any of the above items can be the fault of EASTL. However, the first four can also be the fault of the user. Your primary goal in verifying a potential bug is to determine if it is an EASTL bug or a user bug. Template errors can sometimes be hard to diagnose. It's probably best if you first show the problem to somebody you know to make sure you are not missing something obvious. Creating a reproducible case may be useful in helping convince yourself, but as is mentioned below, this is not required in order to report the bug. + +**Report the bug** + +The first place to try is the standard EA centralized tech support site. As of this writing (10/2005), that tech site is http://eatech/. Due to the frequent technology churn that seems to occur within Electronic Arts, the bug reporting system in place when you read this may not be the one that was in place when this FAQ entry was written. If the tech site route fails, consider directly contacting the maintainer of the EASTL package. + +In reporting a bug, it is nice if there is a simple reproducible case that can be presented. However, such a case requires time to create, and so you are welcome to initially simply state what you think the bug is without producing a simple reproducible case. It may be that this is a known bug or it may be possible to diagnose the bug without a reproducible case. If more information is needed then the step of trying to produce a reproducible case may be necessary. + +### Info.18 Can EASTL be used by third party EA developers? + +EASTL and other core technologies authored by EA (and not licensed from other companies) can be used in source and binary form by designated 3rd parties. The primary case where there is an issue is if the library contains platform specific code for a platform that the 3rd party is not licensed for. In that case the platform-specific code would need to be removed. This doesn’t apply to EASTL, nor many of the other core tech packages. + +## Performance + +### Perf.1 How efficient is EASTL compared to standard C++ STL implementations? + +With respect to the functionality that is equivalent between EASTL and standard STL, the short answer to this is that EASTL is as at least as efficient as othe STL implementations and in a number of aspects is more so. EASTL has functionality such as intrusive_list and linked_ptr that don't exist in standard STL but are explicitly present to provide significant optimizations over standard STL. + +The medium length answer is that EASTL is significantly more efficient than Dinkumware STL, and Microsoft Windows STL. EASTL is generally more efficient than Metrowerks STL, but Metrowerks has a few tricks up its sleeve which EASTL doesn't currently implement. EASTL is roughly equal in efficiency to STLPort and GCC 3.x+ STL, though EASTL has some optimizations that these do not. + +The long answer requires a breakdown of the functionality between various versions of the STL. + +### Perf.2 How efficient is EASTL in general? + +This question is related to the question, "How efficient are templates?" If you understand the effects of templates then you can more or less see the answer for EASTL. Templates are more efficient than the alternative when they are used appropriately, but can be less efficient than the alternative when used under circumstances that don't call for them. The strength of templates is that the compiler sees all the code and data types at compile time and can often reduce statements to smaller and faster code than with conventional non-templated code. The weakness of templates is that the sometimes produce more code and can result in what is often called "code bloat". However, it's important to note that unused template functions result in no generated nor linked code, so if you have a templated class with 100 functions but you only use one, only that one function will be compiled. + +EASTL is a rather efficient implementation of a template library and pulls many tricks of the trade in terms of squeezing optimal performance out of the compiler. The only way to beat it is to write custom code for the data types you are working with, and even then people are sometimes surprised to find that their hand-implemented algorithm works no better or even worse than the EASTL equivalent. But certainly there are ways to beat templates, especially if you resort to assembly language programming and some kinds of other non-generic tricks. + +### Perf.3 Strings don't appear to use the "copy-on-write" (CoW) optimization. Why not? + +**Short answer** +CoW provides a benefit for a small percentage of uses but provides a disadvantage for the large majority of uses. + +**Long answer** +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. + +References + +This is a good starting HTML reference on the topic: + 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 + +### Perf.4 Does EASTL cause code bloat, given that it uses templates? + +The reason that templated functions and classes might cause an increase in code size because each template instantiation theoretically creates a unique piece of code. For example, when you compile this code: + +```cpp +template +const T min(const T a, const T b) + { return b < a ? b : a; } + +int i = min(3, 4); +double d = min(3.0, 4.0); +``` + +the compiler treats it as if you wrote this: + +```cpp +int min(const int a, const int b) + { return b < a ? b : a; } + +double min(const double a, const double b) + { return b < a ? b : a; } +``` + +Imagine this same effect happening with containers such as list and map and you can see how it is that templates can cause code proliferation. + +A couple things offset the possibility of code proliferation: inlining and folding. In practice the above 'min' function would be converted to inlined functions by the compiler which occupy only a few CPU instructions. In many of the simplest cases the inlined version actually occupies less code than the code required to push parameters on the stack and execute a function call. And they will execute much faster as well. + +Code folding (a.k.a. "COMDAT folding", "duplicate stripping", "ICF" / "identical code folding") is a compiler optimization whereby the compiler realizes that two independent functions have compiled to the same code and thus can be reduced to a single function. The Microsoft VC++ compiler (Since VS2005), and GCC (v 4.5+) can do these kinds of optimizations on all platforms. This can result, for example, in all templated containers of pointers (e.g. vector, vector, etc.) to be linked as a single implementation. This folding occurs at a function level and so individual member functions can be folded while other member functions are not. A side effect of this optimization is that you aren't likely to gain much much declaring containers of void* instead of the pointer type actually contained. + +The above two features reduce the extent of code proliferation, but certainly don't eliminate it. What you need to think about is how much code might be generated vs. what your alternatives are. Containers like vector can often inline completely away, whereas more complicated containers such as map can only partially be inlined. In the case of map, if you need an such a container for your Widgets, what alternatives do you have that would be more efficient than instantiating a map? This is up to you to answer. + +It's important to note that C++ compilers will throw away any templated functions that aren't used, including unused member functions of templated classes. However, some argue that by having many functions available to the user that users will choose to use that larger function set rather than stick with a more restricted set. + +Also, don't be confused by syntax bloat vs. code bloat. In looking at templated libraries such as EASTL you will notice that there is sometimes a lot of text in the definition of a template implementation. But the actual underlying code is what you need to be concerned about. + +There is a good Usenet discussion on this topic at: http://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/2b00649a935997f5 + +### Perf.5 Don't STL and EASTL containers fragment memory? + +They only fragment memory if you use them in a way that does so. This is no different from any other type of container used in a dynamic way. There are various solutions to this problem, and EASTL provides additional help as well: + +* For vectors, use the reserve function (or the equivalent constructor) to set aside a block of memory for the container. The container will not reallocate memory unless you try grow beyond the capacity you reserve. +* EASTL has "fixed" variations of containers which allow you to specify a fixed block of memory which the container uses for its memory. The container will not allocate any memory with these types of containers and all memory will be cache-friendly due to its locality. +* You can assign custom allocators to containers instead of using the default global allocator. You would typically use an allocator that has its own private pool of memory. +* Where possible, add all a container's elements to it at once up front instead of adding them over time. This avoids memory fragmentation and increase cache coherency. + +### Perf.6 I don't see container optimizations for equivalent scalar types such as pointer types. Why? + +Metrowerks (and no other, as of this writing) STL has some container specializations for type T* which maps them to type void*. The idea is that a user who declares a list of Widget* and a list of Gadget* will generate only one container: a list of void*. As a result, code generation will be smaller. Often this is done only in optimized builds, as such containers are harder to view in debug builds due to type information being lost. + +The addition of this optimization is under consideration for EASTL, though it might be noted that optimizing compilers such as VC++ are already capable of recognizing duplicate generated code and folding it automatically as part of link-time code generation (LTCG) (a.k.a. "whole program optimization"). This has been verified with VC++, as the following code and resulting disassembly demonstrate: + +```cpp +eastl::list intPtrList; +eastl::list toPtrList; + +eastl_size_t n1 = intPtrList.size(); +eastl_size_t n2 = toPtrList.size(); + +0042D288 lea edx,[esp+14h] +0042D28C call eastl::list::size (414180h) +0042D291 push eax +0042D292 lea edx,[esp+24h] +0042D296 call eastl::list::size (414180h) +``` + +Note that in the above case the compiler folded the two implementations of size() into a single implementation. + +### Perf.7 I've seen some STL's provide a default quick "node allocator" as the default allocator. Why doesn't EASTL do this? + +**Short answer** + +This is a bad, misguided idea. + +**Long answer** + +These node allocators implement a heap for all of STL with buckets for various sizes of allocations and implemented fixed-size pools for each of these buckets. These pools are attractive at first because they do well in STL comparison benchmarks, especially when thread safety is disabled. Such benchmarks make it impossible to truly compare STL implementations because you have two different allocators in use and in some cases allocator performance can dominate the benchmark. However, the real problem with these node allocators is that they badly fragment and waste memory. The technical discussion of this topic is outside the scope of this FAQ, but you can learn more about it by researching memory management on the Internet. Unfortunately, the people who implement STL libraries are generally not experts on the topic of memory management. A better approach, especially for game development, is for the user to decide when fixed-size pools are appropriate and use them via custom allocator assignment to containers. + +### Perf.8 Templates sometimes seem to take a long time to compile. Why do I do about that? + +C++ compilers are generally slower than C compilers, and C++ templates are generally slower to compile than regular C++ code. EASTL has some extra functionality (such as type_traits and algorithm specializations) that is not found in most other template libraries and significantly improves performance and usefulness but adds to the amount of code that needs to be compiled. Ironically, we have a case where more source code generates faster and smaller object code. + +The best solution to the problem is to use pre-compiled headers, which are available on all modern ~2002+) compilers, such as VC6.0+, GCC 3.2+, and Metrowerks 7.0+. In terms of platforms this means all 2002+ platforms. + +Some users have been speeding up build times by creating project files that put all the source code in one large .cpp file. This has an effect similar to pre-compiled headers. It can go even faster than pre-compiled headers but has downsides in the way of convenience and portability. + +### Perf.10 How well does EASTL inline? + +EASTL is written in such as way as to be easier to inline than typical templated libraries such as STL. How is this so? It is so because EASTL reduces the inlining depth of many functions, particularly the simple ones. In doing so it makes the implementation less "academic" but entirely correct. An example of this is the vector operator[] function, which is implemented like so with Microsoft STL: + +```cpp +reference operator[](size_type n) { + return *(begin() + n); +} +``` + +EASTL implements the function directly, like so: + +```cpp +reference operator[](size_type n) { + return *(mpBegin + n); +} +``` + +Both implementations are correct, but hte EASTL implementation will run faster in debug builds, be easier to debug, and will be more likely to be inlined when the usage of this function is within a hierarchy of other functions being inlined. It is not so simple to say that the Microsoft version will always inline in an optimized build, as it could be part of a chain and cause the max depth to be exceeded. + +That being said, EASTL appears to inline fairly well under most circumstances, including with GCC, which is the poorest of the compilers in its ability to inline well. + +### Perf.11 How do I control function inlining? + +Inlining is an important topic for templated code, as such code often relies on the compiler being able to do good function inlining for maximum performance. GCC, VC++, and Metrowerks are discussed here. We discuss compilation-level inlining and function-level inling here, though the latter is likely to be of more use to the user of EASTL, as it can externally control how EASTL is inlined. A related topic is GCC's template expansion depth, discussed elsewhere in this FAQ. We provide descriptions of inlining options here but don't currently have any advice on how to best use these with EASTL. + +Compilation-Level Inlining -- VC++ + +VC++ has some basic functionality to control inlining, and the compiler is pretty good at doing aggressive inlining when optimizing on for all platforms. + +> **#pragma inline_depth( [0... 255] )** +> +> Controls the number of times inline expansion can occur by controlling the number of times that a series of function calls can be expanded (from 0 to 255 times). This pragma controls the inlining of functions marked inline and or inlined automatically under the /Ob2 option. The inline_depth pragma controls the number of times a series of function calls can be expanded. For example, if the inline depth is 4, and if A calls B and B then calls C, all three calls will be expanded inline. However, if the closest inline expansion is 2, only A and B are expanded, and C remains as a function call. + +> **#pragma inline_recursion( [{on | off}] )** +> +> Controls the inline expansion of direct or mutually recursive function calls. Use this pragma to control functions marked as inline and or functions that the compiler automatically expands under the /Ob2 option. Use of this pragma requires an /Ob compiler option setting of either 1 or 2. The default state for inline_recursion is off. The inline_recursion pragma controls how recursive functions are expanded. If inline_recursion is off, and if an inline function calls itself (either directly or indirectly), the function is expanded only once. If inline_recursion is on, the function is expanded multiple times until it reaches the value set by inline_depth, the default value of 8, or a capacity limit. + +Compilation-Level Inlining -- GCC + +GCC has a large set of options to control function inlining. Some options are available only in GCC 3.0 and later and thus not present on older platforms. + + +> **-fno-default-inline** +> +> Do not make member functions inline by default merely because they are defined inside the class scope (C++ only). Otherwise, when you specify -O, member functions defined inside class scope are compiled inline by default; i.e., you don't need to add 'inline' in front of the member function name. +> +> **-fno-inline** +> +> Don't pay attention to the inline keyword. Normally this option is used to keep the compiler from expanding any functions inline. Note that if you are not optimizing, no functions can be expanded inline. +> +> **-finline-functions** +> +> Integrate all simple functions into their callers. The compiler heuristically decides which functions are simple enough to be worth integrating in this way. If all calls to a given function are integrated, and the function is declared static, then the function is normally not output as assembler code in its own right. Enabled at level -O3. +> +> **-finline-limit=n** +> +> By default, GCC limits the size of functions that can be inlined. This flag allows the control of this limit for functions that are explicitly marked as inline (i.e., marked with the inline keyword or defined within the class definition in c++). n is the size of functions that can be inlined in number of pseudo instructions (not counting parameter handling). pseudo-instructions are an internal representation of function size. The default value of n is 600. Increasing this value can result in more inlined code at the cost of compilation time and memory consumption. Decreasing usually makes the compilation faster and less code will be inlined (which presumably means slower programs). This option is particularly useful for programs that use inlining heavily such as those based on recursive templates with C++. +> +> Inlining is actually controlled by a number of parameters, which may be specified individually by using --param name=value. The -finline-limit=n option sets some of these parameters as follows: +> +> ``` +> max-inline-insns-single +> is set to n/2. +> max-inline-insns-auto +> is set to n/2. +> min-inline-insns +> is set to 130 or n/4, whichever is smaller. +> max-inline-insns-rtl +> is set to n. +> ``` +> +> See --param below for a documentation of the individual parameters controlling inlining. +> +> **-fkeep-inline-functions** +> +> Emit all inline functions into the object file, even if they are inlined where used. +> +> **--param name=value** +> +> In some places, GCC uses various constants to control the amount of optimization that is done. For example, GCC will not inline functions that contain more that a certain number of instructions. You can control some of these constants on the command-line using the --param option. +> +> max-inline-insns-single +> Several parameters control the tree inliner used in gcc. This number sets the maximum number of instructions (counted in GCC's internal representation) in a single function that the tree inliner will consider for inlining. This only affects functions declared inline and methods implemented in a class declaration (C++). The default value is 450. +> +> max-inline-insns-auto +> When you use -finline-functions (included in -O3), a lot of functions that would otherwise not be considered for inlining by the compiler will be investigated. To those functions, a different (more restrictive) limit compared to functions declared inline can be applied. The default value is 90. +> +>large-function-insns +> The limit specifying really large functions. For functions larger than this limit after inlining inlining is constrained by --param large-function-growth. This parameter is useful primarily to avoid extreme compilation time caused by non-linear algorithms used by the backend. This parameter is ignored when -funit-at-a-time is not used. The default value is 2700. +> +> large-function-growth +> Specifies maximal growth of large function caused by inlining in percents. This parameter is ignored when -funit-at-a-time is not used. The default value is 100 which limits large function growth to 2.0 times the original size. +> +> inline-unit-growth +> Specifies maximal overall growth of the compilation unit caused by inlining. This parameter is ignored when -funit-at-a-time is not used. The default value is 50 which limits unit growth to 1.5 times the original size. +> +> max-inline-insns-recursive +> max-inline-insns-recursive-auto +> Specifies maximum number of instructions out-of-line copy of self recursive inline function can grow into by performing recursive inlining. For functions declared inline --param max-inline-insns-recursive is taken into acount. For function not declared inline, recursive inlining happens only when -finline-functions (included in -O3) is enabled and --param max-inline-insns-recursive-auto is used. The default value is 450. +> +> max-inline-recursive-depth +> max-inline-recursive-depth-auto +> Specifies maximum recursion depth used by the recursive inlining. For functions declared inline --param max-inline-recursive-depth is taken into acount. For function not declared inline, recursive inlining happens only when -finline-functions (included in -O3) is enabled and --param max-inline-recursive-depth-auto is used. The default value is 450. +> +> inline-call-cost +> Specify cost of call instruction relative to simple arithmetics operations (having cost of 1). Increasing this cost disqualify inlinining of non-leaf functions and at same time increase size of leaf function that is believed to reduce function size by being inlined. In effect it increase amount of inlining for code having large abstraction penalty (many functions that just pass the argumetns to other functions) and decrease inlining for code with low abstraction penalty. Default value is 16. +> +> **-finline-limit=n** +> +> By default, GCC limits the size of functions that can be inlined. This flag allows the control of this limit for functions that are explicitly marked as inline (i.e., marked with the inline keyword or defined within the class definition in c++). n is the size of functions that can be inlined in number of pseudo instructions (not counting parameter handling). The default value of n is 600. Increasing this value can result in more inlined code at the cost of compilation time and memory consumption. Decreasing usually makes the compilation faster and less code will be inlined (which presumably means slower programs). This option is particularly useful for programs that use inlining heavily such as those based on recursive templates with C++. + +Inlining is actually controlled by a number of parameters, which may be specified individually by using --param name=value. The -finline-limit=n option sets some of these parameters as follows: + +``` +max-inline-insns-single + is set to n/2. +max-inline-insns-auto + is set to n/2. +min-inline-insns + is set to 130 or n/4, whichever is smaller. +max-inline-insns-rtl + is set to n. +``` + +See below for a documentation of the individual parameters controlling inlining. + +Note: pseudo instruction represents, in this particular context, an abstract measurement of function's size. In no way, it represents a count of assembly instructions and as such its exact meaning might change from one release to an another. + +GCC additionally has the -Winline compiler warning, which emits a warning whenever a function declared as inline was not inlined. + +Compilation-Level Inlining -- Metrowerks + +Metrowerks has a number of pragmas (and corresponding compiler settings) to control inlining. These include always_inline, inline_depth, inline_max_size, and inline max_total_size. + +> ``` +> #pragma always_inline on | off | reset +> ``` +> +> Controls the use of inlined functions. If you enable this pragma, the compiler ignores all inlining limits and attempts to inline all functions where it is legal to do so. This pragma is deprecated. Use the inline_depth pragma instead. +> +> ``` +> #pragma inline_depth(n) +> #pragma inline_depth(smart) +> ``` +> +> Controls how many passes are used to expand inline function. Sets the number of passes used to expand inline function calls. The number n is an integer from 0 to 1024 or the smart specifier. It also represents the distance allowed in the call chain from the last function up. For example, if d is the total depth of a call chain, then functions below (d-n) are inlined if they do not exceed the inline_max_size and inline_max_total_size settings which are discussed directly below. +> +> ``` +> #pragma inline_max_size(n); +> #pragma inline_max_total_size(n); +> ``` +> +> The first pragma sets the maximum function size to be considered for inlining; the second sets the maximum size to which a function is allowed to grow after the functions it calls are inlined. Here, n is the number of statements, operands, and operators in the function, which turns out to be roughly twice the number of instructions generated by the function. However, this number can vary from function to function. For the inline_max_size pragma, the default value of n is 256; for the inline_max_total_size pragma, the default value of n is 10000. The smart specifier is the default mode, with four passes where the passes 2-4 are limited to small inline functions. All inlineable functions are expanded if inline_depth is set to 1-1024. + +Function-Level Inlining -- VC++ + +> To force inline usage under VC++, you use this: +> +> ``` +> __forceinline void foo(){ ... } +> ``` +> +> It should be noted that __forceinline has no effect if the compiler is set to disable inlining. It merely tells the compiler that when inlining is enabled that it shouldn't use its judgment to decide if the function should be inlined but instead to always inline it. +> +> To disable inline usage under VC++, you need to use this: +> +> ``` +> #pragma inline_depth(0) // Disable inlining. +> void foo() { ... } +> #pragma inline_depth() // Restore default. +> ``` +> +> The above is essentially specifying compiler-level inlining control within the code for a specific function. + +**Function-Level Inlining -- GCC / Metrowerks** + +> To force inline usage under GCC 3.1+, you use this: +> +> `inline void foo() __attribute__((always_inline)) { ... }` +> +> or +> +> `inline __attribute__((always_inline)) void foo() { ... }` +> +> To disable inline usage under GCC 3+, you use this: +> +> `void foo() __attribute__((noinline)) { ... }` +> +> or +> +> `inline __attribute__((noinline)) void foo() { ... }` + +EABase has some wrappers for this, such as EA_FORCE_INLINE. + +### Perf.12 C++ / EASTL seems to bloat my .obj files much more than C does. + +There is no need to worry. The way most C++ compilers compile templates, they compile all seen template code into the current .obj module, which results in larger .obj files and duplicated template code in multiple .obj files. However, the linker will (and in fact must) select only a single version of any given function for the application, and these linked functions will usually be located contiguously. + +Additionally, the debug information for template definitions is usually larger than that for non-templated C++ definitions, which itself is sometimes larger than C defintions due to name decoration. + +### Perf.13 What are the best compiler settings for EASTL? + +We will discuss various aspects of this topic here. As of this writing, more EASTL research on this topic has been done on Microsoft compiler platforms (e.g. Win32) than GCC platforms. Thus currently this discussion focuses on VC++ optimization. Some of the concepts are applicable to GCC, though. EASTL has been sucessfully compiled and tested (the EASTL unit test) on our major development platforms with the highest optimization settings enabled, including GCC's infamous -O3 level. + +**Optimization Topics** + +* Function inlining. +* Optimization for speed vs. optimization for size. +* Link-time code generation (LTCG). +* Profile-guided optimization (PGO). + +**Function inlining** + +EASTL is a template library and inlining is important for optimal speed. Compilers have various options for enabling inlining and those options are discussed in this FAQ in detail. Most users will want to enable some form of inlining when compiling EASTL and other templated libraries. For users that are most concerned about the compiler's inlining increasing code size may want to try the 'inline only functions marked as inline' compiler option. Here is a table of normalized results from the benchmark project (Win32 platform): +| Inlining Disabled | Inline only 'inline' | Inline any | +|------|------|------|------| +| **Application size** | 100K | 86K | 86K | +| **Execution time** | 100 | 75 | 75 | + +The above execution times are highly simplified versions of the actual benchmark data but convey a sense of the general average behaviour that can be expected. In practice, simple functions such as vector::operator[] will execute much faster with inlining enabled but complex functions such as map::insert may execute no faster within inlining enabled. + +**Optimization for Speed / Size** + +Optimization for speed results in the compiler inlining more code than it would otherwise. This results in the inlined code executing faster than if it was not inlined. As mentioned above, basic function inlining can result in smaller code as well as faster code, but after a certain point highly inlined code becomes greater in size than less inlined code and the performance advantages of inlining start to lessen. The EASTL Benchmark project is a medium sized application that is about 80% templated and thus acts as a decent measure of the practical tradeoff between speed and size. Here is a table of normalized results from the benchmark project (Windows platform): +| Size | Speed | Speed + LTCG | Speed + LTCG + PGO | +|------|------|------|------| +| **Application size** | 80K | 100K | 98K | 98K | +| **Execution time** | 100 | 90 | 83 | 75 | + +What the above table is saying is that if you are willing to have your EASTL code be 20% larger, it will be 10% faster. Note that it doesn't mean that your app will be 20% larger, only the templated code in it like EASTL will be 20% larger. + +**Link-time code generation (LTCG)** + +LTCG is a mechanism whereby the compiler compiles the application as if it was all in one big .cpp file instead of separate .cpp files that don't see each other. Enabling LTCG optimizations is done by simply setting some compiler and linker settings and results in slower link times. The benchmark results are presented above and for the EASTL Benchmark project show some worthwhile improvement. + +**Profile-guided optimization (PGO)** + +PGO is a mechanism whereby the compiler uses profiling information from one or more runs to optimize the compilation and linking of an application. Enabling PGO optimizations is done by setting some linker settings and doing some test runs of the application, then linking the app with the test run results. Doing PGO optimizations is a somewhat time-consuming task but the benchmark results above demonstrate that for the EASTL Benchmark project that PGO is worth the effort. + +## Problems + +### Prob.1 I'm getting screwy behavior in sorting algorithms or sorted containers. What's wrong? + +It may possible that you are seeing floating point roundoff problems. Many STL algorithms require object comparisons to act consistently. However, floating point values sometimes compare differently between uses because in one situation a value might be in 32 bit form in system memory, whereas in anther situation that value might be in an FPU register with a different precision. These are difficult problems to track down and aren't the fault of EASTL or whatever similar library you might be using. There are various solutions to the problem, but the important thing is to find a way to force the comparisons to be consistent. + +The code below was an example of this happening, whereby the object pA->mPos was stored in system memory while pB->mPos was stored in a register and comparisons were inconsistent and a crash ensued. + +```cpp +class SortByDistance : public binary_function +{ +private: + Vector3 mOrigin; + +public: + SortByDistance(Vector3 origin) { + mOrigin = origin; + } + + bool operator()(WorldTreeObject* pA, WorldTreeObject* pB) const { + return ((WorldObject*)pA)->mPos - mOrigin).GetLength() + < ((WorldObject*)pB)->mPos - mOrigin).GetLength(); + } +}; +``` + +Another thing to watch out for is the following mistake: + +```cpp +struct ValuePair +{ + uint32_t a; + uint32_t b; +}; + +// Improve speed by casting the struct to uint64_t +bool operator<(const ValuePair& vp1, const ValuePair& vp2) + { return *(uint64_t*)&vp1 < *(uint64_t*)&vp2; } +``` + +The problem is that the ValuePair struct has 32 bit alignment but the comparison assumes 64 bit alignment. The code above has been observed to crash on the PowerPC 64-based machines. The resolution is to declare ValuePair as having 64 bit alignment. + +### Prob.2 I am getting compiler warnings (e.g. C4244, C4242 or C4267) that make no sense. Why? + +One cause of this occurs with VC++ when you have code compiled with the /Wp64 (detect 64 bit portability issues) option. This causes pointer types to have a hidden flag called __w64 attached to them by the compiler. So 'ptrdiff_t' is actually known by the compiler as '__w64 int', while 'int' is known by the compilers as simply 'int'. A problem occurs here when you use templates. For example, let's say we have this templated function + +``` cpp +template +T min(const T a, const T b) { + return b < a ? b : a; +} +``` + +If you compile this code: + +```cpp +ptrdiff_t a = min(ptrdiff_t(0), ptrdiff_t(1)); +int b = min((int)0, (int)1); +``` + +You will get the following warning for the second line, which is somewhat nonsensical: + +`warning C4244: 'initializing' : conversion from 'const ptrdiff_t' to 'int', possible loss of data` + +This could probably be considered a VC++ bug, but in the meantime you have little choice but to ignore the warning or disable it. + +### Prob.3 I am getting compiler warning C4530, which complains about exception handling and "unwind semantics." What gives? + +VC++ has a compiler option (/EHsc) that allows you to enable/disable exception handling stack unwinding but still enable try/catch. This is useful because it can save a lot in the way of code generation for your application. Disabling stack unwinding will decrease the size of your executable on at least the Win32 platform by 10-12%. + +If you have stack unwinding disabled, but you have try/catch statements, VC++ will generate the following warning: + +`warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc` + +As of EASTL v1.0, this warning has been disabled within EASTL for EASTL code. However, non-EASTL code such as std STL code may still cause this warning to be triggered. In this case there is not much you can do about this other than to disable the warning. + +### Prob.4 Why are tree-based EASTL containers hard to read with a debugger? + +**Short answer** + +Maximum performance and design mandates. + +**Long answer** + +You may notice that when you have a tree-based container (e.g. set, map) in the debugger that it isn't automatically able to recognize the tree nodes as containing instances of your contained object. You can get the debugger to do what you want with casting statements in the debug watch window, but this is not an ideal solution. The reason this is happening is that node-based containers always use an anonymous node type as the base class for container nodes. This is primarily done for performance, as it allows the node manipulation code to exist as a single non-templated library of functions and it saves memory because containers will have one or two base nodes as container 'anchors' and you don't want to allocate a node of the size of the user data when you can just use a base node. See list.h for an example of this and some additional in-code documentation on this. + +Additionally, EASTL has the design mandate that an empty container constructs no user objects. This is both for performance reasons and because it doing so would skew the user's tracking of object counts and might possibly break some expectation the user has about object lifetimes. + +Currently this debug issue exists only with tree-based containers. Other node-based containers such as list and slist use a trick to get around this problem in debug builds. + +See [Debug.2](#debug2-how-do-i-view-containers-if-the-visualizertooltip-support-is-not-present) for more. + +### Prob.5 The EASTL source code is sometimes rather complicated looking. Why is that? + +**Short answer** + +Maximum performance. + +**Long answer** +EASTL uses templates, type_traits, iterator categories, redundancy reduction, and branch reduction in order to achieve optimal performance. A side effect of this is that there are sometimes a lot of template parameters and multiple levels of function calls due to template specialization. The ironic thing about this is that this makes the code (an optimized build, at least) go faster, not slower. In an optimized build the compiler will see through the calls and template parameters and generate a direct optimized inline version. + +As an example of this, take a look at the implementation of the copy implementation in algorithm.h. If you are copying an array of scalar values or other trivially copyable values, the compiler will see how the code directs this to the memcpy function and will generate nothing but a memcpy in the final code. For non-memcpyable data types the compiler will automatically understand that in do the right thing. + +EASTL's primary objective is maximal performance, and it has been deemed worthwhile to make the code a little less obvious in order to achieve this goal. Every case where EASTL does something in an indirect way is by design and usually this is for the purpose of achieving the highest possible performance. + +### Prob.6 When I get compilation errors, they are very long and complicated looking. What do I do? + +Assuming the bugs are all worked out of EASTL, these errors really do indicate that you have something wrong. EASTL is intentionally very strict about types, as it tries to minimize the chance of users errors. Unfortunately, there is no simple resolution to the problem of long compiler errors other than to deal with them. On the other hand, once you've dealt with them a few times, you tend to realize that most of time they are the same kinds of errors and + +Top five approaches to dealing with long compilation errors: + +1. Look at the line where the compilation error occurred and ignore the text of the error and just look at obvious things that might be wrong. +2. Consider the most common typical causes of templated compilation errors and consider if any of these might be your problem. Usually one of them are. +3. Either read through the error (it's not as hard as it may look on the surface) or copy the error to a text file and remove the extraneous +4. Compile the code under GCC instead of MSVC, as GCC warnings and errors tend to be more helpful than MSVC's. Possibly also consider compiling an isolated version under Comeau C++'s free online compiler at www.comeaucomputing.com or the Dinkumware online compiler at http://dinkumware.com/exam/. +5. Try using an STL filter (http://www.bdsoft.com/tools/stlfilt.html) which automatically boils down template errors to simpler forms. We haven't tried this yet with EASTL. Also there is the more generic TextFilt (http://textfilt.sourceforge.net/). + +Top five causes of EASTL compilation errors: + +1. const-correctness. Perhaps a quarter of container template errors are due to the user not specifying const correctly. +2. Missing hash function. hash_map, hash_set, etc. require that you either specify a hash function or one exists for your class. See functional.h for examples of declarations of hash functions for common data types. +3. Missing operators. Various containers and algorithms require that certain operators exist for your contained classes. For example, list requires that you can test contained objects for equivalence (i.e. operator==), while map requires that you can test contained objects for "less-ness" (operator <). If you define a Widget class and don't have a way to compare two Widgets, you will get errors when trying to put them into a map. +4. Specifying the wrong data type. For example, it is a common mistake to forget that when you insert into a map, you need to insert a pair of objects and not just your key or value type. +5. Incorrect template parameters. When declaring a template instantiation (e.g. map >) you simply need to get the template parameters correct. Also note that when you have ">>" next to each other that you need to separate them by one space (e.g. "> >"). + +### Prob.7 Templates sometimes seem to take a long time to compile. Why do I do about that? + +C++ compilers are generally slower than C compilers, and C++ templates are generally slower to compile than regular C++ code. EASTL has some extra functionality (such as type_traits and algorithm specializations) that is not found in most other template libraries and significantly improves performance and usefulness but adds to the amount of code that needs to be compiled. Ironically, we have a case where more source code generates faster and smaller object code. + +The best solution to the problem is to use pre-compiled headers, which are available on all modern ~2002+) compilers, such as VC6.0+, GCC 3.2+, and Metrowerks 7.0+. In terms of platforms this means all 2002+ platforms. + +Some users have been speeding up build times by creating project files that put all the source code in one large .cpp file. This has an effect similar to pre-compiled headers. It can go even faster than pre-compiled headers but has downsides in the way of convenience and portability. + +### Prob.8 I get the compiler error: "template instantiation depth exceeds maximum of 17. use -ftemplate-depth-NN to increase the maximum". + +This is a GCC error that occurs when a templated function calls a templated function which calls a templated function, etc. past a depth of 17. You can use the GCC command line argument -ftemplate-depth-40 (or some other high number) to get around this. As note below, the syntax starting with GCC 4.5 has changed slightly. + +The primary reason you would encounter this with EASTL is type traits that are used by algorithms. The type traits library is a (necessarily) highly templated set of types and functions which adds at most about nine levels of inlining. The copy and copy_backward algorithms have optimized pathways that add about four levels of inlining. If you have just a few more layers on top of that in container or user code then the default limit of 17 can be exceeded. We are investigating ways to reduce the template depth in the type traits library, but only so much can be done, as most compilers don't support type traits natively. Metrowerks is the current exception. + +From the GCC documentation: + +``` +-ftemplate-depth-n + +Set the maximum instantiation depth for template classes to n. +A limit on the template instantiation depth is needed to detect +endless recursions during template class instantiation ANSI/ISO +C++ conforming programs must not rely on a maximum depth greater than 17. +Note that starting with GCC 4.5 the syntax is -ftemplate-depth=N instead of -ftemplate-depth-n. +``` + +### Prob.9 I'm getting errors about min and max while compiling. + +You need to define NOMINMAX under VC++ when this occurs, as it otherwise defines min and max macros that interfere. There may be equivalent issues with other compilers. Also, VC++ has a specific header file which defines min and max macros but which doesn't pay attention to NOMINMAX and so in that case there is nothing to do but not include that file or to undefine min and max. minmax.h is not a standard file and its min and max macros are not standard C or C++ macros or functions. + +### Prob.10 C++ / EASTL seems to bloat my .obj files much more than C does. + +There is no need to worry. The way most C++ compilers compile templates, they compile all seen template code into the current .obj module, which results in larger .obj files and duplicated template code in multiple .obj files. However, the linker will (and must) select only a single version of any given function for the application, and these linked functions will usually be located contiguously. + +### Prob.11 I'm getting compiler errors regarding placement operator new being previously defined. + +This can happen if you are attempting to define your own versions of placement new/delete. The C++ language standard does not allow the user to override these functions. Section 18.4.3 of the standard states: + +> Placement forms +> 1. These functions are reserved, a C++ program may not define functions that displace the versions in the Standard C++ library. + +You may find that #defining __PLACEMENT_NEW_INLINE seems to fix your problems under VC++, but it can fail under some circumstances and is not portable and fails with other compilers, which don't have an equivalent workaround. + +### Prob.12 I'm getting errors related to wchar_t string functions such as wcslen(). + +EASTL requires EABase-related items that the following be so. If not, then EASTL gets confused about what types it can pass to wchar_t related functions. + +* The #define EA_WCHAR_SIZE is equal to sizeof(wchar_t). +* If sizeof(wchar_t) == 2, then char16_t is typedef'd to wchar_t. +* If sizeof(wchar_t) == 4, then char32_t is typedef'd to wchar_t. + +EABase v2.08 and later automatically does this for most current generation and all next generation platforms. With GCC 2.x, the user may need to predefine EA_WCHAR_SIZE to the appropriate value, due to limitations with the GCC compiler. Note that GCC defaults to sizeof(wchar_t) ==4, but it can be changed to 2 with the -fshort_wchar compiler command line argument. If you are using EASTL without EABase, you will need to make sure the above items are correctly defined. + +### Prob.13 I'm getting compiler warning C4619: there is no warning number Cxxxx (e.g. C4217). + +Compiler warning C4619 is a VC++ warning which is saying that the user is attempting to enable or disable a warning which the compiler doesn't recognize. This warning only occurs if the user has the compiler set to enable warnings that are normally disabled, regardless of the warning level. The problem, however, is that there is no easy way for user code to tell what compiler warnings any given compiler version will recognize. That's why Microsoft normally disables this warning. + +The only practical solution we have for this is for the user to disable warning 4619 globally or an a case-by-case basis. EA build systems such as nant/framework 2's eaconfig will usually disable 4619. In general, global enabling of 'warnings that are disabled by default' often result in quandrys such as this. + +### Prob.14 My stack-based fixed_vector is not respecting the object alignment requirements. + +EASTL fixed_* containers rely on the compiler-supplied alignment directives, such as that implemented by EA_PREFIX_ALIGN. This is normally a good thing because it allows the memory to be local with the container. However, as documented by Microsoft at http://msdn2.microsoft.com/en-us/library/83ythb65(VS.71).aspx, this doesn't work for stack variables. The two primary means of working around this are: + +* Use something like AlignedObject<> from the EAStdC package's EAAllocator.h file. +* Use eastl::vector with a custom allocator and have it provide aligned memory. EASTL automatically recognizes that the objects are aligned and will call the aligned version of your allocator allocate() function. You can get this aligned memory from the stack, if you need it, somewhat like how AlignedObject<> works. + +### Prob.15 I am getting compiler errors when using GCC under XCode (Macintosh/iphone). + +The XCode environment has a compiler option which causes it to evaluate include directories recursively. So if you specify /a/b/c as an include directory, it will consider all directories underneath c to also be include directories. This option is enabled by default, though many XCode users disable it, as it is a somewhat dangerous option. The result of enabling this option with EASTL is that is used by the compiler when you say #include . The solution is to disable this compiler option. It's probably a good idea to disable this option anyway, as it typically causes problems for users yet provides minimal benefits. + +### Prob.16 I am getting linker errors about Vsnprintf8 or Vsnprintf16. + +EASTL requires the user to provide a function called Vsnprintf8 if the string::sprintf function is used. vsnprintf is not a standard C function, but most C standard libraries provide some form of it, though in some ways their implementations differ, especially in what the return value means. Also, most implementations of vsnprintf are slow, mostly due to mutexes related to locale functionality. And you can't really use vendor vsnprintf on an SPU due to the heavy standard library size. EASTL is stuck because it doesn't want to depend on something with these problems. EAStdC provides a single consistent fast lightweight, yet standards-conforming, implementation in the form of Vsnprintf(char8_t*, ...), but EASTL can't have a dependency on EAStdC. So the user must provide an implementation, even if all it does is call EAStdC's Vsnprintf or the vendor vsnprintf for that matter. + +Example of providing Vsnprintf8 via EAStdC: + +```cpp +#include + +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); +} +``` + +Example of providing Vsnprintf8 via C libraries: + +```cpp +#include + +int Vsnprintf8(char8_t* p, size_t n, const char8_t* pFormat, va_list arguments) +{ + #ifdef _MSC_VER + return vsnprintf_s(p, n, _TRUNCATE, pFormat, arguments); + #else + return vsnprintf(p, n, pFormat, arguments); + #endif +} + +int Vsnprintf16(char16_t* p, size_t n, const char16_t* pFormat, va_list arguments) +{ + #ifdef _MSC_VER + return vsnwprintf_s(p, n, _TRUNCATE, pFormat, arguments); + #else + return vsnwprintf(p, n, pFormat, arguments); // Won't work on Unix because its libraries implement wchar_t as int32_t. + #endif +} +``` + +### Prob.17 I am getting compiler errors about UINT64_C or UINT32_C. + +This is usually an order-of-include problem that comes about due to the implementation of __STDC_CONSTANT_MACROS in C++ Standard libraries. The C++ header file defineds UINT64_C only if __STDC_CONSTANT_MACROS has been defined by the user or the build system; the compiler doesn't automatically define it. The failure you are seeing occurs because user code is #including a system header before #including EABase and without defining __STDC_CONSTANT_MACROS itself or globally. EABase defines __STDC_CONSTANT_MACROS and #includes the appropriate system header. But if the system header was already previously #included and __STDC_CONSTANT_MACROS was not defined, then UINT64_C doesn't get defined by anybody. + +The real solution that the C++ compiler and standard library wants is for the app to globally define __STDC_CONSTANT_MACROS itself in the build. + +### Prob.18 I am getting a crash with a global EASTL container. + +This usually due to compiler's lack of support for global (and static) C++ class instances. The crash is happening because the global variable exists but its constructor was not called on application startup and it's member data is zeroed bytes. To handle this you need to manually initialize such variables. There are two primary ways: + +Failing code: + +```cpp +eastl::list gIntList; // Global variable. + +void DoSomething() +{ + gIntList.push_back(1); // Crash. gIntList was never constructed. +} +``` + +Declaring a pointer solution: + +```cpp +eastl::list* gIntList = NULL; + +void DoSomething() +{ + if(!gIntList) // Or move this to an init function. + gIntList = new eastl::list; + + gIntList->push_back(1); // Success +} +``` + +Manual constructor call solution: + +```cpp +eastl::list gIntList; + +void InitSystem() +{ + new(&gIntList) eastl::list; +} + +void DoSomething() +{ + gIntList.push_back(1); // Success +} +``` + +### Prob.19 Why doesn't EASTL support passing NULL string functions? + +The primary argument is to make functions safer for use. Why crash on NULL pointer access when you can make the code safe? That's a good argument. The counter argument, which EASTL currently makes, is: + +> It breaks consistency with the C++ STL library and C libraries, which require strings to be valid. +> +> It makes the coder slower and bigger for all users, though few need NULL checks. +The specification for how to handle NULL is simple for some cases but not simple for others. Operator < below a case where the proper handling of it in a consistent way is not simple, as all comparison code (<, >, ==, !=, >=, <=) in EASTL must universally and consistently handle the case where either or both sides are NULL. A NULL string seems similar to an empty string, but doesn't always work out so simply. +> +> What about other invalid string pointers? NULL is merely one invalid value of many, with its only distinction being that sometimes it's intentionally NULL (as opposed to being NULL due to not being initialized). +How and where to implement the NULL checks in such a way as to do it efficiently is not always simple, given that public functions call public functions. +> +> It's arguable (and in fact the the intent of the C++ standard library) that using pointers that are NULL is a user/app mistake. If we really want to be safe then we should be using string objects for everything. You may not entirely buy this argument in practice, but on the other hand one might ask why is the caller of EASTL using a NULL pointer in the first place? The answer of course is that somebody gave it to him. + +## Debug + +### Debug.1 How do I set the VC++ debugger to display EASTL container data with tooltips? + +See [Cont.9](#cont9-how-do-i-set-the-vc-debugger-to-display-eastl-container-data-with-tooltips) + +### Debug.2 How do I view containers if the visualizer/tooltip support is not present? + +Here is a table of answers about how to manually inspect containers in the debugger. + +| Container | Approach | +|------|------| +| slist
fixed_slist | slist is a singly-linked list. Look at the slist mNode variable. You can walk the list by looking at mNode.mpNext, etc. | +| list
fixed_list | list is a doubly-linked list. Look at the list mNode variable. You can walk the list forward by looking at mNode.mpNext, etc. and backward by looking at mpPrev, etc. | +| intrusive_list
intrusive_slist† | Look at the list mAnchor node. This lets you walk forward and backward in the list via mpNext and mpPrev. | +| array | View the array mValue member in the debugger. It's simply a C style array. | +| vector
fixed_vector | View the vector mpBegin value in the debugger. If the string is long, use ", N" to limit the view length, as with someVector.mpBegin, 32 | +| vector_set
vector_multiset
vector_map
vector_multimap | These are containers that are implemented as a sorted vector, deque, or array. They are searched via a standard binary search. You can view them the same way you view a vector or deque. | +| deque | deque is implemented as an array of arrays, where the arrays implement successive equally-sized segments of the deque. The mItBegin deque member points the deque begin() position. | +| bitvector | Look at the bitvector mContainer variable. If it's a vector, then see vector above. | +| bitset | Look at the bitset mWord variable. The bitset is nothing but one or more uint32_t mWord items. | +| set
multiset
fixed_set
fixed_multiset | The set containers are implemented as a tree of elements. The set mAnchor.mpNodeParent points to the top of the tree; the mAnchor.mpNodeLeft points to the far left node of the tree (set begin()); the mAnchor.mpNodeRight points to the right of the tree (set end()). | +| map
multimap
fixed_map
fixed_multimap | The map containers are implemented as a tree of pairs, where pair.first is the map key and pair.second is the map value. The map mAnchor.mpNodeParent points to the top of the tree; the mAnchor.mpNodeLeft points to the far left node of the tree (map begin()); the mAnchor.mpNodeRight points to the right of the tree (map end()). | +| hash_map
hash_multimap
fixed_hash_map
fixed_hash_multimap | hash tables in EASTL are implemented as an array of singly-linked lists. The array is the mpBucketArray member. Each element in the list is a pair, where the first element of the pair is the map key and the second is the map value. | +| intrusive_hash_map
intrusive_hash_multimap
intrusive_hash_set
intrusive_hash_multiset | intrusive hash tables in EASTL are implemented very similarly to regular hash tables. See the hash_map and hash_set entries for more info. | +| hash_set
hash_multiset
fixed_hash_set
fixed_hash_map | hash tables in EASTL are implemented as an array of singly-linked lists. The array is the mpBucketArray member. | +| basic_string
fixed_string
fixed_substring | View the string mpBegin value in the debugger. If the string is long, use ", N" to limit the view length, as with someString.mpBegin, 32 | +| heap | A heap is an array of data (e.g. EASTL vector) which is organized in a tree whereby the highest priority item is array[0], The next two highest priority items are array[1] and [2]. Underneath [1] in priority are items [3] and [4], and underneath item [2] in priority are items [5] and [6]. etc. | +| stack | View the stack member c value in the debugger. That member will typically be a list or deque. | +| queue | View the queue member c value in the debugger. That member will typically be a list or deque. | +| priority_queue | View the priority_queue member c value in the debugger. That member will typically be a vector or deque which is organized as a heap. See the heap section above for how to view a heap. | +| smart_ptr | View the mpValue member. | + +### Debug.3 The EASTL source code is sometimes rather complicated looking. Why is that? + +**Short answer** + +Maximum performance. + +**Long answer** + +EASTL uses templates, type_traits, iterator categories, redundancy reduction, and branch reduction in order to achieve optimal performance. A side effect of this is that there are sometimes a lot of template parameters and multiple levels of function calls due to template specialization. The ironic thing about this is that this makes the code (an optimized build, at least) go faster, not slower. In an optimized build the compiler will see through the calls and template parameters and generate a direct optimized inline version. + +As an example of this, take a look at the implementation of the copy implementation in algorithm.h. If you are copying an array of scalar values or other trivially copyable values, the compiler will see how the code directs this to the memcpy function and will generate nothing but a memcpy in the final code. For non-memcpyable data types the compiler will automatically understand that in do the right thing. + +EASTL's primary objective is maximal performance, and it has been deemed worthwhile to make the code a little less obvious in order to achieve this goal. Every case where EASTL does something in an indirect way is by design and usually this is for the purpose of achieving the highest possible performance. + +### Debug.4 When I get compilation errors, they are very long and complicated looking. What do I do? + +Assuming the bugs are all worked out of EASTL, these errors really do indicate that you have something wrong. EASTL is intentionally very strict about types, as it tries to minimize the chance of users errors. Unfortunately, there is no simple resolution to the problem of long compiler errors other than to deal with them. On the other hand, once you've dealt with them a few times, you tend to realize that most of time they are the same kinds of errors and + +Top five approaches to dealing with long compilation errors: + +1.Look at the line where the compilation error occurred and ignore the text of the error and just look at obvious things that might be wrong. +2. Consider the most common typical causes of templated compilation errors and consider if any of these might be your problem. Usually one of them are. +3. Either read through the error (it's not as hard as it may look on the surface) or copy the error to a text file and remove the extraneous +4. Compile the code under GCC instead of MSVC, as GCC warnings and errors tend to be more helpful than MSVC's. Possibly also consider compiling an isolated version under Comeau C++'s free online compiler at www.comeaucomputing.com or the Dinkumware online compiler at http://dinkumware.com/exam/. +5. Try using an STL filter (http://www.bdsoft.com/tools/stlfilt.html) which automatically boils down template errors to simpler forms. We haven't tried this yet with EASTL. Also there is the more generic TextFilt (http://textfilt.sourceforge.net/). + +Top five causes of EASTL compilation errors: + +1. const-correctness. Perhaps a quarter of container template errors are due to the user not specifying const correctly. +2. Missing hash function. hash_map, hash_set, etc. require that you either specify a hash function or one exists for your class. See functional.h for examples of declarations of hash functions for common data types. +3. Missing operators. Various containers and algorithms require that certain operators exist for your contained classes. For example, list requires that you can test contained objects for equivalence (i.e. operator==), while map requires that you can test contained objects for "less-ness" (operator <). If you define a Widget class and don't have a way to compare two Widgets, you will get errors when trying to put them into a map. +4. Specifying the wrong data type. For example, it is a common mistake to forget that when you insert into a map, you need to insert a pair of objects and not just your key or value type. +5. Incorrect template parameters. When declaring a template instantiation (e.g. map >) you simply need to get the template parameters correct. Also note that when you have ">>" next to each other that you need to separate them by one space (e.g. "> >"). + +### Debug.5 How do I measure hash table balancing? + +The following functionality lets you spelunk hash container layout. + +* There is the load_factor function which tells you the overall hashtable load, but doesn't tell you if a load is unevenly distributed. +* You can control the load factor and thus the automated bucket redistribution with set_load_factor. +* The local_iterator begin(size_type n) and local_iterator end(size_type) functions lets you iterate each bucket individually. You can use this to examine the elements in a bucket. +* You can use the above to get the size of any bucket, but there is also simply the bucket_size(size_type n) function. +* The bucket_count function tells you the count of buckets. So with this you can completely visualize the layout of the hash table. +* There is also iterator find_by_hash(hash_code_t c), for what it's worth. + +The following function draws an ASCII bar graph of the hash table for easy visualization of bucket distribution: + +```cpp +#include +#include +#include + +template +void VisualizeHashTableBuckets(const HashTable& h) +{ + eastl_size_t bucketCount = h.bucket_count(); + eastl_size_t largestBucketSize = 0; + + for(eastl_size_t i = 0; i < bucketCount; i++) + largestBucketSize = eastl::max_alt(largestBucketSize, h.bucket_size(i)); + + YourPrintFunction("\n --------------------------------------------------------------------------------\n"); + + for(eastl_size_t i = 0; i < bucketCount; i++) + { + const eastl_size_t k = h.bucket_size(i) * 80 / largestBucketSize; + + char buffer[16]; + sprintf(buffer, "%3u|", (unsigned)i); + YourPrintFunction(buffer); + + for(eastl_size_t j = 0; j < k; j++) + YourPrintFunction("*"); + + YourPrintFunction("\n"); + } + + YourPrintFunction(" --------------------------------------------------------------------------------\n"); +} +``` + +This results in a graph that looks like the following (with one horizontal bar per bucket). This hashtable has a large number of collisions in each of its 10 buckets. + +``` + ------------------------------------------------------ + 0|******************************************** + 1|************************************************ + 2|*************************************** + 3|******************************************** + 4|***************************************************** + 5|************************************************* + 6|**************************************** + 7|*********************************************** + 8|******************************************** + 9|************************************** +10|******************************************** + ----------------------------------------------------- +``` + +## Containers + +### Cont.1 Why do some containers have "fixed" versions (e.g. fixed_list) but others(e.g. deque) don't have fixed versions? + +Recall that fixed containers are those that are implemented via a single contiguous block of memory and don't use a general purpose heap to allocate memory from. For example, fixed_list is a list container that implements its list by a user-configurable fixed block of memory. Such containers have an upper limit to how many items they can hold, but have the advantage of being more efficient with memory use and memory access coherency. + +The reason why some containers don't have fixed versions is that such functionality doesn't make sense with these containers. Containers which don't have fixed versions include: + +``` +array, deque, bitset, stack, queue, priority_queue, +intrusive_list, intrusive_hash_map, intrusive_hash_set, +intrusive_hash_multimap, intrusive_hash_multimap, +vector_map, vector_multimap, vector_set, vector_multiset. +``` + +Some of these containers are adapters which wrap other containers and thus there is no need for a fixed version because you can just wrap a fixed container. In the case of intrusive containers, the user is doing the allocation and so there are no memory allocations. In the case of array, the container is a primitive type which doesn't allocate memory. In the case of deque, it's primary purpose for being is to dynamically resize and thus the user would likely be better of using a fixed_vector. + +### Cont.2 Can I mix EASTL with standard C++ STL? + +This is possible to some degree, though the extent depends on the implementation of C++ STL. One of things that makes interoperability is something called iterator categories. Containers and algorithms recognize iterator types via their category and STL iterator categories are not recognized by EASTL and vice versa. + +Things that you definitely can do: + +* #include both EASTL and standard STL headers from the same .cpp file. +* Use EASTL containers to hold STL containers. +* Construct an STL reverse_iterator from an EASTL iterator. +* Construct an EASTL reverse_iterator from an STL iterator. + +Things that you probably will be able to do, though a given std STL implementation may prevent it: + +* Use STL containers in EASTL algorithms. +* Use EASTL containers in STL algorithms. +* Construct or assign to an STL container via iterators into an EASTL container. +* Construct or assign to an EASTL container via iterators into an STL container. + +Things that you would be able to do if the given std STL implementation is bug-free: + +* Use STL containers to hold EASTL containers. Unfortunately, VC7.x STL has a confirmed bug that prevents this. Similarly, STLPort versions prior to v5 have a similar but. + +Things that you definitely can't do: + +* Use an STL allocator directly with an EASTL container (though you can use one indirectly). +* Use an EASTL allocator directly with an STL container (though you can use one indirectly). + +### Cont.3 Why are there so many containers? + +EASTL has a large number of container types (e.g vector, list, set) and often has a number of variations of given types (list, slist, intrusive_list, fixed_list). The reason for this is that each container is tuned and to a specific need and there is no single container that works for all needs. The more the user is concerned about squeezing the most performance out of their system, the more the individual container variations become significant. It's important to note that having additional container types generally does not mean generating additional code or code bloat. Templates result in generated code regardless of what templated class they come from, and so for the most part you get optimal performance by choosing the optimal container for your needs. + +### Cont.4 Don't STL and EASTL containers fragment memory? + +They only fragment memory if you use them in a way that does so. This is no different from any other type of container used in a dynamic way. There are various solutions to this problem, and EASTL provides additional help as well: + +For vectors, use the reserve function (or the equivalent constructor) to set aside a block of memory for the container. The container will not reallocate memory unless you try grow beyond the capacity you reserve. +EASTL has "fixed" variations of containers which allow you to specify a fixed block of memory which the container uses for its memory. The container will not allocate any memory with these types of containers and all memory will be cache-friendly due to its locality. +You can assign custom allocators to containers instead of using the default global allocator. You would typically use an allocator that has its own private pool of memory. +Where possible, add all a container's elements to it at once up front instead of adding them over time. This avoids memory fragmentation and increase cache coherency. + +### Cont.5 I don't see container optimizations for equivalent scalar types such as pointer types. Why? + +Metrowerks (and no other, as of this writing) STL has some container specializations for type T* which maps them to type void*. The idea is that a user who declares a list of Widget* and a list of Gadget* will generate only one container: a list of void*. As a result, code generation will be smaller. Often this is done only in optimized builds, as such containers are harder to view in debug builds due to type information being lost. + +The addition of this optimization is under consideration for EASTL, though it might be noted that optimizing compilers such as VC++ are already capable of recognizing duplicate generated code and folding it automatically as part of link-time code generation (LTCG) (a.k.a. "whole program optimization"). This has been verified with VC++, as the following code and resulting disassembly demonstrate: + +```cpp +eastl::list intPtrList; +eastl::list toPtrList; + +eastl_size_t n1 = intPtrList.size(); +eastl_size_t n2 = toPtrList.size(); + +0042D288 lea edx,[esp+14h] +0042D28C call eastl::list::size (414180h) +0042D291 push eax +0042D292 lea edx,[esp+24h] +0042D296 call eastl::list::size (414180h) +``` + +Note that in the above case the compiler folded the two implementations of size() into a single implementation. + +### Cont.6 What about alternative container and algorithm implementations (e.g. treaps, skip lists, avl trees)? + +EASTL chooses to implement some alternative containers and algorithms and not others. It's a matter of whether or not the alternative provides truly complementary or improved functionality over existing containers. The following is a list of some implemented and non-implemented alternatives and the rationale behind each: + +Implemented: + +* intrusive_list, etc. -- Saves memory and improves cache locality. +* vector_map, etc. -- Saves memory and improves cache locality. +* ring_buffer -- Useful for some types of operations and has no alternative. +* shell_sort -- Useful sorting algorithm. +* sparse_matrix -- Useful for some types of operations and has no alternative. + +Not implemented: + +* skip lists (alternative to red-black tree) -- These use more memory and usually perform worse than rbtrees. +* treap (alternative to red-black tree) -- These are easier and smaller than rbtrees, but perform worse. +* avl tree (alternative to red-black tree) -- These have slightly better search performance than rbtrees, but significantly worse * * insert/remove performance. +* btree (alternative to red-black tree) -- These are no better than rbtrees. + +If you have an idea of something that should be implemented, please suggest it or even provide at least a prototypical implementation. + +### Cont.7 Why are tree-based EASTL containers hard to read with a debugger? + +**Short answer** + +Maximum performance and design mandates. + +**Long answer** + +You may notice that when you have a tree-based container (e.g. set, map) in the debugger that it isn't automatically able to recognize the tree nodes as containing instances of your contained object. You can get the debugger to do what you want with casting statements in the debug watch window, but this is not an ideal solution. The reason this is happening is that node-based containers always use an anonymous node type as the base class for container nodes. This is primarily done for performance, as it allows the node manipulation code to exist as a single non-templated library of functions and it saves memory because containers will have one or two base nodes as container 'anchors' and you don't want to allocate a node of the size of the user data when you can just use a base node. See list.h for an example of this and some additional in-code documentation on this. + +Additionally, EASTL has the design mandate that an empty container constructs no user objects. This is both for performance reasons and because it doing so would skew the user's tracking of object counts and might possibly break some expectation the user has about object lifetimes. + +Currently this debug issue exists only with tree-based containers. Other node-based containers such as list and slist use a trick to get around this problem in debug builds. + +### Cont.8 How do I assign a custom allocator to an EASTL container? + +There are two ways of doing this: + +1. Use the set_allocator function that is present in each container. +2. Specify a new allocator type via the Allocator template parameter that is present in each container. + +For item #1, EASTL expects that you provide an instance of an allocator of the type that EASTL recognizes. This is simple but has the disadvantage that all such allocators must be of the same class. The class would need to have C++ virtual functions in order to allow a given instance to act differently from another instance. + +For item #2, you specify that the container use your own allocator class. The advantage of this is that your class can be implemented any way you want and doesn't require virtual functions for differentiation from other instances. Due to the way C++ works your class would necessarily have to use the same member function names as the default allocator class type. In order to make things easier, we provide a skeleton allocator here which you can copy and fill in with your own implementation. + +```cpp +class custom_allocator +{ +public: + custom_allocator(const char* pName = EASTL_NAME_VAL("custom allocator")) + { + #if EASTL_NAME_ENABLED + mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME; + #endif + + // Possibly do something here. + } + + custom_allocator(const allocator& x, const char* pName = EASTL_NAME_VAL("custom allocator")); + { + #if EASTL_NAME_ENABLED + mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME; + #endif + + // Possibly copy from x here. + } + + ~custom_allocator(); + { + // Possibly do something here. + } + + custom_allocator& operator=(const custom_allocator& x) + { + // Possibly copy from x here. + return *this; + } + + void* allocate(size_t n, int flags = 0) + { + // Implement the allocation here. + } + + void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0) + { + // Implement the allocation here. + } + + void deallocate(void* p, size_t n) + { + // Implement the deallocation here. + } + + const char* get_name() const + { + #if EASTL_NAME_ENABLED + return mpName; + #else + return "custom allocator"; + #endif + } + + void set_name(const char* pName) + { + #if EASTL_NAME_ENABLED + mpName = pName; + #endif + } + +protected: + // Possibly place instance data here. + + #if EASTL_NAME_ENABLED + const char* mpName; // Debug name, used to track memory. + #endif +}; + + +inline bool operator==(const allocator& a, const allocator& b) +{ + // Provide a comparison here. +} + +inline bool operator!=(const allocator& a, const allocator& b) +{ + // Provide a negative comparison here. +} +``` + +Here's an example of how to use the above custom allocator: + +```cpp +// Declare a Widget list and have it default construct. +list widgetList; + +// Declare a Widget list and have it construct with a copy of some global allocator. +list widgetList2(gSomeGlobalAllocator); + +// Declare a Widget list and have it default construct, but assign +// an underlying implementation after construction. +list widgetList; +widgetList.get_allocator().mpIAllocator = new WidgetAllocatorImpl; +``` + +### Cont.9 How do I set the VC++ debugger to display EASTL container data with tooltips? + +Visual Studio supports this via the AutoExp.dat file, an example of which is [present](./html/AutoExp.dat) with this documentation. + +Sometimes the AutoExp.dat doesn't seem to work. Avery Lee's explanation: + +> If I had to take a guess, the problem is most likely in the cast to the concrete node type. These are always tricky because, for some strange reason, the debugger is whitespace sensitive with regard to specifying template types. You might try manually checking one of the routines of the specific map instantiation and checking that the placement of whitespace and const within the template expression still matches exactly. In some cases the compiler uses different whitespace rules depending on the value type which makes it impossible to correctly specify a single visualizer – this was the case for eastl::list<>, for which I was forced to include sections for both cases. The downside is that you have a bunch of (error) entries either way. + +### Cont.10 How do I use a memory pool with a container? + +Using custom memory pools is a common technique for decreasing memory fragmentation and increasing memory cache locality. EASTL gives you the flexibility of defining your own memory pool systems for containers. There are two primary ways of doing this: + +* Assign a custom allocator to a container. eastl::fixed_pool provides an implementation. +* Use one of the EASTL fixed containers, such as fixed_list. + +**Custom Allocator** + +In the custom allocator case, you will want to create a memory pool and assign it to the container. For purely node-based containers such as list, slist, map, set, multimap, and multiset, your pool simply needs to be able to allocate list nodes. Each of these containers has a member typedef called node_type which defines the type of node allocated by the container. 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): + +```cpp +typedef list WidgetList; // Declare your WidgetList type. + +MemoryPool myPool(sizeof(WidgetList::node_type), 100); // Make a pool of 100 Widget nodes. +WidgetList myList(&myPool); // Create a list that uses the pool. +``` + +In the case of containers that are array-based, such as vector and basic_string, memory pools don't work very well as these containers work on a realloc-basis instead of by adding incremental nodes. What what want to do with these containers is assign a sufficient block of memory to them and reserve() the container's capacity to the size of the memory. + +In the case of mixed containers which are partly array-based and partly node based, such as hash containers and deque, you can use a memory pool for the nodes but will need a single array block to supply for the buckets (hash containers and deque both use a bucket-like system). + +You might consider using eastl::fixed_pool as such an allocator, as it provides such functionality and allows the user to provide the actual memory used for the pool. Here is some example code: + +```cpp +char buffer[256]; + +list myList; +myList.get_allocator().init(buffer, 256); +Fixed Container +In the fixed container case, the container does all the work for you. To use a list which implements a private pool of memory, just declare it like so: + +fixed_list fixedList; // Declare a fixed_list that can hold 100 Widgets +``` + +### Cont.11 How do I write a comparison (operator<()) for a struct that contains two or more members? + +See [Algo.2](#algo2-how-do-i-write-a-comparison-operator-for-a-struct-that-contains-two-or-more-members). + +### Cont.12 Why doesn't container X have member function Y? + +Why don't the list or vector containers have a find() function? Why doesn't the vector container have a sort() function? Why doesn't the string container have a mid() function? These are common examples of such questions. + +The answer usually boils down to two reasons: + +* The functionality exists in a more centralized location elsewhere, such as the algorithms. +* The functionality can be had by using other member functions. + +In the case of find and sort functions not being part of containers, the find algorithm and sort algorithm are centralized versions that apply to any container. Additionally, the algorithms allow you to specify a sub-range of the container on which to apply the algorithm. So in order to find an element in a list, you would do this: + +`list::iterator i = find(list.begin(), list.end(), 3);` + +And in order to sort a vector, you would do this: + +```cpp +quick_sort(v.begin(), v.end()); // Sort the entire array. + +quick_sort(&v[3], &v[8]); // Sort the items at the indexes in the range of [3, 8). +``` + +In the case of functionality that can be had by using other member functions, note that EASTL follows the philosophy that duplicated functionality should not exist in a container, with exceptions being made for cases where mistakes and unsafe practices commonly happen if the given function isn't present. In the case of string not having a mid function, this is because there is a string constructor that takes a sub-range of another string. So to make a string out of the middle of another, you would do this: + +`string strMid(str, 3, 5); // Make a new string of the characters from the source range of [3, 3+5).` + +It might be noted that the EASTL string class is unique among EASTL containers in that it sometimes violates the minimum functionality rule. This is so because the std C++ string class similarly does so and EASTL aims to be compatible. + +### Cont.13 How do I search a hash_map of strings via a char pointer efficiently? If I use map.find("hello") it creates a temporary string, which is inefficient. + +The problem is illustrated with this example: + +```cpp +map swMap; + ... +map::iterator it = swMap.find("blue"); // A temporary string object is created here. +``` + +In this example, the find function expects a string object and not a string literal and so (silently!) creates a temporary string object for the duration of the find. There are two solutions to this problem: + +* Make the map a map of char pointers instead of string objects. Don't forget to write a custom compare or else the default comparison function will compare pointer values instead of string contents. +* Use the EASTL hash_map::find_as function, which allows you to find an item in a hash container via an alternative key than the one the hash table uses. + +### Cont.14 Why are set and hash_set iterators const (i.e. const_iterator)? + +The situation is illustrated with this example: + +```cpp +set intSet; + +intSet.insert(1); +set::iterator i = intSet.begin(); +*i = 2; // Error: iterator i is const. +``` + +In this example, the iterator is a regular iterator and not a const_iterator, yet the compiler gives an error when trying to change the iterator value. The reason this is so is that a set is an ordered container and changing the value would make it 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. + +### Cont.15 How do I prevent my hash container from re-hashing? + +If you want to make a hashtable never re-hash (i.e. increase/reallocate its bucket count), call set_max_load_factor with a very high value such as 100000.f. + +Similarly, you can control the bucket growth factor with the rehash_policy function. By default, when buckets reallocate, they reallocate to about twice their previous count. You can control that value as with the example code here: + +```cpp +hash_set hashSet; +hashSet.rehash_policy().mfGrowthFactor = 1.5f +``` + +### Cont.16 Which uses less memory, a map or a hash_map? + +A hash_map will virtually always use less memory. A hash_map will use an average of two pointers per stored element, while a map uses three pointers per stored element. + +### Cont.17 How do I write a custom hash function? + +You can look at the existing hash functions in functional.h, but we provide a couple examples here. + +To write a specific hash function for a Widget class, you would do this: + +```cpp +struct WidgetHash { + size_t operator()(const Widget& w) const + { return w.id; } +}; + +hash_set widgetHashSet; +``` + +To write a generic (templated) hash function for a set of similar classes (in this case that have an id member), you would do this: + +```cpp +template +struct GeneralHash { + size_t operator()(const T& t) const + { return t.id; } +}; + +hash_set > widgetHashSet; +hash_set > doggetHashSet; +``` + +### Cont.18 How do I write a custom compare function for a map or set? + +The sorted containers require that an operator< exist for the stored values or that the user provide a suitable custom comparison function. A custom can be implemented like so: + +```cpp +struct WidgetLess { + bool operator()(const Widget& w1, const Widget& w2) const + { return w.id < w2.id; } +}; + +set wSet; +``` + +It's important that your comparison function must be consistent in its behaviour, else the container will either be unsorted or a crash will occur. This concept is called "strict weak ordering." + +### Cont.19 How do I force my vector or string capacity down to the size of the container? + +You can simply use the set_capacity() member function which is present in both vector and string. This is a function that is not present in std STL vector and string functions. + +```cpp +eastl::vector x; +x.set_capacity(); // Shrink x's capacity to be equal to its size. + +eastl::vector x; +x.set_capacity(0); // Completely clear x. +``` + +To compact your vector or string in a way that would also work with std STL you need to do the following. + +How to shrink a vector's capacity to be equal to its size: + +```cpp +std::vector x; +std::vector(x).swap(x); // Shrink x's capacity. +``` + +How to completely clear a std::vector (size = 0, capacity = 0, no allocation): + +```cpp +std::vector x; +std::vector().swap(x); // Completely clear x. +``` + +### Cont.20 How do I iterate a container while (selectively) removing items from it? + +All EASTL containers have an erase function which takes an iterator as an argument and returns an iterator to the next item. Thus, you can erase items from a container while iterating it like so: + +```cpp +set intSet; + +set::iterator i = intSet.begin(); + +while(i != intSet.end()) +{ + if(*i & 1) // Erase all odd integers from the container. + i = intSet.erase(i); + else + ++i; +} +``` + +### Cont.21 How do I store a pointer in a container? + +The problem with storing pointers in containers is that clearing the container will not free the pointers automatically. There are two conventional resolutions to this problem: + +Manually free pointers when removing them from containers. +Store the pointer as a smart pointer instead of a "raw"pointer. +The advantage of the former is that it makes the user's intent obvious and prevents the possibility of smart pointer "thrashing" with some containers. The disadvantage of the former is that it is more tedicous and error-prone. + +The advantage of the latter is that your code will be cleaner and will always be error-free. The disadvantage is that it is perhaps slightly obfuscating and with some uses of some containers it can cause smart pointer thrashing, whereby a resize of a linear container (e.g. vector) can cause shared pointers to be repeatedly incremented and decremented with no net effect. + +It's important that you use a shared smart pointer and not an unshared one such as C++ auto_ptr, as the latter will result in crashes upon linear container resizes. Here we provide an example of how to create a list of smart pointers: + +```cpp +list< shared_ptr > wList; + +wList.push_back(shared_ptr(new Widget)); +wList.pop_back(); // The Widget will be freed. +``` + +### Cont.22 How do I make a union of two containers? difference? intersection? + +The best way to accomplish this is to sort your container (or use a sorted container such as set) and then apply the set_union, set_difference, or set_intersection algorithms. + +### Cont.23 How do I override the default global allocator? + +There are multiple ways to accomplish this. The allocation mechanism is defined in EASTL/internal/config.h and in allocator.h/cpp. Overriding the default global allocator means overriding these files, overriding what these files refer to, or changing these files outright. Here is a list of things you can do, starting with the simplest: + +* Simply provide the following versions of operator new (which EASTL requires, actually): +```cpp +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); +``` +* Predefine the config.h macros for EASTLAlloc, EASTLFree, etc. See config.h for this. +* Override config.h entirely via EASTL_USER_CONFIG_HEADER. See config.h for this. +* Provide your own version of allocator.h/cpp +* Provide your own version of config.h. + +If you redefine the allocator class, you can make it work however you want. + +Note that config.h defines EASTLAllocatorDefault, which returns the default allocator instance. As documented in config.h, this is not a global allocator which implements all container allocations but is the allocator that is used when EASTL needs to allocate memory internally. There are very few cases where EASTL allocates memory internally, and in each of these it is for a sensible reason that is documented to behave as such. + +### Cont.24 How do I do trick X with the string container? + +There seem to be many things users want to do with strings. Perhaps the most commonly requested EASTL container extensions are string class shortcut functions. While some of these requests are being considered, we provide some shortcut functions here. + +**find_and_replace** + +```cpp +template +void find_and_replace(String& s, const typename String::value_type* pFind, const typename String::value_type* pReplace) +{ + for(size_t i; (i = source.find(pFind)) != T::npos; ) + s.replace(i, eastl::CharStrlen(pFind), pReplace); +} + +Example: + find_and_replace(s, "hello", "hola"); +``` + +**trim front (multiple chars)** + +```cpp +template +void trim_front(String& s, const typename String::value_type* pValues) +{ + s.erase(0, s.find_first_not_of(pValues)); +} + +Example: + trim_front(s, " \t\n\r"); +``` + +**trim back (multiple chars)** + +```cpp +template +void trim_front(String& s, const typename String::value_type* pValues) +{ + s.resize(s.find_last_not_of(pValues) + 1); +} + +Example: + trim_back(s, " \t\n\r"); +``` + +**prepend** + +```cpp +template +void prepend(String& s, const typename String::value_type* p) +{ + s.insert(0, p); +} + +Example: + prepend(s, "log: "); +``` + +**begins_with** + +```cpp +template +bool begins_with(const String& s, const typename String::value_type* p) +{ + return s.compare(0, eastl::CharStrlen(p), p) == 0; +} + +Example: + if(begins_with(s, "log: ")) ... +``` + +**ends_with** + +```cpp +template +bool ends_with(const String& s, const typename String::value_type* p) +{ + const typename String::size_type n1 = s.size(); + const typename String::size_type n2 = eastl::CharStrlen(p); + return ((n1 >= n2) && s.compare(n1 - n2, n2, p) == 0); +} + +Example: + if(ends_with(s, "test.")) ... +``` + +**tokenize** + +Here is a simple tokenization function that acts very much like the C strtok function. + +```cpp +template +size_t tokenize(const String& s, const typename String::value_type* pDelimiters, + String* resultArray, size_t resultArraySize) +{ + size_t n = 0; + typename String::size_type lastPos = s.find_first_not_of(pDelimiters, 0); + typename String::size_type pos = s.find_first_of(pDelimiters, lastPos); + + while((n < resultArraySize) && (pos != String::npos) || (lastPos != String::npos)) + { + resultArray[n++].assign(s, lastPos, pos - lastPos); + lastPos = s.find_first_not_of(pDelimiters, pos); + pos = s.find_first_of(pDelimiters, lastPos); + } + + return n; +} + +Example: + string resultArray[32]; +tokenize(s, " \t", resultArray, 32)); +``` + +### Cont.25 How do EASTL smart pointers compare to Boost smart pointers? + +EASTL's smart pointers are nearly identical to Boost (including all that crazy member template and dynamic cast functionality in shared_ptr), but are not using the Boost source code. EA legal has already stated that it is fine to have smart pointer classes with the same names and functionality as those present in Boost. EA legal specifically looked at the smart pointer classes in EASTL for this. There are two differences between EASTL smart pointers and Boost smart pointers: + +* EASTL smart pointers don't have thread safety built-in. It was deemed that this is too much overhead and that thread safety is something best done at a higher level. By coincidence the C++ library proposal to add shared_ptr also omits the thread safety feature. FWIW, I put a thread-safe shared_ptr in EAThread, though it doesn't attempt to do all the fancy member template things that Boost shared_ptr does. Maybe I'll add that some day if people care. +* EASTL shared_ptr object deletion goes through a deletion object instead of through a virtual function interface. 95% of the time this makes no difference (aside from being more efficient), but the primary case where it matters is when you have shared_ptr and assign to is something like "new Widget". The problem is that shared_ptr doesn't know what destructor to call and so doesn't call a destructor unless you specify a custom destructor object as part of the template specification. I don't know what to say about this one, as it is less safe, but forcing everybody to have the overhead of additional templated classes and virtual destruction functions doesn't seem to be in the spirit of high performance or lean game development. + +There is the possibility of making a shared_ptr_boost which is completely identical to Boost shared_ptr. So perhaps that will be done some day. + +### Cont.26 How do your forward-declare an EASTL container? + +Here is are some examples of how to do this: + +```cpp +namespace eastl +{ + template class basic_string; + typedef basic_string string8; // Forward declare EASTL's string8 type. + + template class vector; + typedef vector CharArray; + + template class hash_set; + + template class map; +} +``` + +The forward declaration can be used to declare a pointer or reference to such a class. It cannot be used to declare an instance of a class or refer to class data, static or otherwise. Nevertheless, forward declarations for pointers and references are useful for reducing the number of header files a header file needs to include. + +### Cont.27 How do I make two containers share a memory pool? + +EASTL (and std STL) allocators are specified by value semantics and not reference semantics. Value semantics is more powerful (because a value can also be a reference, but not the other way around), but is not always what people expects if they're used to writing things the other way. + +Here is some example code: + +```cpp +struct fixed_pool_reference +{ +public: + fixed_pool_reference() + { + mpFixedPool = NULL; + } + + fixed_pool_reference(eastl::fixed_pool& fixedPool) + { + mpFixedPool = &fixedPool; + } + + fixed_pool_reference(const fixed_pool_reference& x) + { + mpFixedPool = x.mpFixedPool; + } + + fixed_pool_reference& operator=(const fixed_pool_reference& x) + { + mpFixedPool = x.mpFixedPool; + return *this; + } + + void* allocate(size_t /*n*/, int /*flags*/ = 0) + { + return mpFixedPool->allocate(); + } + + void* allocate(size_t /*n*/, size_t /*alignment*/, size_t /*offset*/, int /*flags*/ = 0) + { + return mpFixedPool->allocate(); + } + + void deallocate(void* p, size_t /*n*/) + { + return mpFixedPool->deallocate(p); + } + + const char* get_name() const + { + return "fixed_pool_reference"; + } + + void set_name(const char* /*pName*/) + { + } + +protected: + friend bool operator==(const fixed_pool_reference& a, const fixed_pool_reference& b); + friend bool operator!=(const fixed_pool_reference& a, const fixed_pool_reference& b); + + eastl::fixed_pool* mpFixedPool; +}; + +inline bool operator==(const fixed_pool_reference& a, const fixed_pool_reference& b) +{ + return (a.mpFixedPool == b.mpFixedPool); +} + +inline bool operator!=(const fixed_pool_reference& a, const fixed_pool_reference& b) +{ + return (a.mpFixedPool != b.mpFixedPool); +} +``` + +Example usage of the above: + +```cpp +typedef eastl::list IntList; + +IntList::node_type buffer[2]; +eastl::fixed_pool myPool(buffer, sizeof(buffer), sizeof(Int::node_type), 2); + +IntList myList1(myPool); +IntList myList2(myPool); + +myList1.push_back(37); +myList2.push_back(39); +``` + +### Cont.28 Can I use a std (STL) allocator with EASTL? + +No. EASTL allocators are similar in interface to std STL allocators, but not 100% compatible. If it was possible to make them compatible with std STL allocators but also match the design of EASTL then compatibility would exist. The primary reasons for lack of compatibility are: + +* EASTL allocators have a different allocate function signature. +* EASTL allocators have as many as four extra required functions: ctor(name), get_name(), set_name(), allocate(size, align, offset). +* EASTL allocators have an additional allocate function specifically for aligned allocations, as listed directly above. + +### What are the requirements of classes stored in containers? + +Class types stored in containers must have: + +* a public copy constructor +* a public assignment operator +* a public destructor +* an operator < that compares two such classes (sorted containers only). +* an operator == that compares two such classes (hash containers only). + +Recall that the compiler generates basic versions these functions for you when you don't implement them yourself, so you can omit any of the above if the compiler-generated version is sufficient. + +For example, the following code will act incorrectly, because the user forgot to implement an assignment operator. The compiler-generated assignment operator will assign the refCount value, which the user doesn't want, and which will be called by the vector during resizing. + +```cpp +struct NotAPod +{ + NotAPod(const NotAPod&) {} // Intentionally don't copy the refCount + + int refCount; // refCounts should not be copied between NotAPod instances. +}; + +eastl::vector v; +``` + +## Algorithms + +### Algo.1 I'm getting screwy behavior in sorting algorithms or sorted containers. What's wrong? + +It may possible that you are seeing floating point roundoff problems. Many STL algorithms require object comparisons to act consistently. However, floating point values sometimes compare differently between uses because in one situation a value might be in 32 bit form in system memory, whereas in anther situation that value might be in an FPU register with a different precision. These are difficult problems to track down and aren't the fault of EASTL or whatever similar library you might be using. There are various solutions to the problem, but the important thing is to find a way to force the comparisons to be consistent. + +The code below was an example of this happening, whereby the object pA->mPos was stored in system memory while pB->mPos was stored in a register and comparisons were inconsistent and a crash ensued. + +```cpp +class SortByDistance : public binary_function +{ +private: + Vector3 mOrigin; + +public: + SortByDistance(Vector3 origin) { + mOrigin = origin; + } + + bool operator()(WorldTreeObject* pA, WorldTreeObject* pB) const { + return ((WorldObject*)pA)->mPos - mOrigin).GetLength() + < ((WorldObject*)pB)->mPos - mOrigin).GetLength(); + } +}; +``` + +### Algo.2 How do I write a comparison (operator<()) for a struct that contains two or more members? + +For a struct with two members such as the following: + +```cpp +struct X { + Blah m1; + Blah m2; +}; +``` + +You would write the comparison function like this: + +```cpp +bool operator<(const X& a, const X& b) { + return (a.m1 == b.m1) ? (a.m2 < b.m2) : (a.m1 < b.m1); +} +``` + +or, using only operator < but more instructions: + +```cpp +bool operator<(const X& a, const X& b) { + return (a.m1 < b.m1) || (!(b.m1 < a.m1) && (a.m2 < b.m2)); +} +``` + +For a struct with three members, you would have: + +```cpp +bool operator<(const X& a, const X& b) { + if(a.m1 != b.m1) + return (a.m1 < b.m1); + if(a.m2 != b.m2) + return (a.m2 < b.m2); + return (a.mType < b.mType); +} +``` + +And a somewhat messy implementation if you wanted to use only operator <. + +Note also that you can use the above technique to implement operator < for spatial types such as vectors, points, and rectangles. You would simply treat the members of the stuct as an array of values and ignore the fact that they have spatial meaning. All operator < cares about is that things order consistently. + +```cpp +bool operator<(const Point2D& a, const Point2D& b) { + return (a.x == b.x) ? (a.y < b.y) : (a.x < b.x); +} +``` + +### Algo.3 How do I sort something in reverse order? + +Normally sorting puts the lowest value items first in the sorted range. You can change this by simply reversing the comparison. For example: + +`sort(intVector.begin(), intVector.end(), greater());` + +It's important that you use operator > instead of >=. The comparison function must return false for every case where values are equal. + +### Algo.4 I'm getting errors about min and max while compiling. + +You need to define NOMINMAX under VC++ when this occurs, as it otherwise defines min and max macros that interfere. There may be equivalent issues with other compilers. Also, VC++ has a specific header file which defines min and max macros but which doesn't pay attention to NOMINMAX and so in that case there is nothing to do but not include that file or to undefine min and max. minmax.h is not a standard file and its min and max macros are not standard C or C++ macros or functions. + +### Algo.5 Why don't algorithms take a container as an argument instead of iterators? A container would be more convenient. + +Having algorithms that use containers instead of algorithms would reduce reduce functionality with no increase in performance. This is because the use of iterators allows for the application of algorithms to sub-ranges of containers and allows for the application of algorithms to containers aren't formal C++ objects, such as C-style arrays. + +Providing additional algorithms that use containers would introduce redundancy with respect to the existing algorithms that use iterators. + +### Algo.6 Given a container of pointers, how do I find an element by value (instead of by pointer)? + +Functions such as find_if help you find a T element in a container of Ts. But if you have a container of pointers such as vector, these functions will enable you to find an element that matches a given Widget* pointer, but they don't let you find an element that matches a given Widget object. + +You can write your own iterating 'for' loop and compare values, or you can use a generic function object to do the work if this is a common task: + +```cpp +template +struct dereferenced_equal +{ + const T& mValue; + + dereferenced_equal(const T& value) : mValue(value) { } + bool operator==(const T* pValue) const { return *pValue == mValue; } +}; + +... + +find_if(container.begin(), container.end(), dereferenced_equal(someWidget)); +``` + +### Algo.7 When do stored objects need to support operator < vs. when do they need to support operator ==? + +Any object which is sorted needs to have operator < defined for it, implicitly via operator < or explicitly via a user-supplied Compare function. Sets and map containers require operator <, while sort, binary search, and min/max algorithms require operator <. + +Any object which is compareed for equality needs to have operator == defined for it, implicitly via operator == or explicitly via a user-supplied BinaryPredicate function. Hash containers required operator ==, while many of the algorithms other than those mentioned above for operator < require operator ==. + +Some algorithms and containers require neither < nor ==. Interestingly, no algorithm or container requires both < and ==. + +### Algo.8 How do I sort via pointers or array indexes instead of objects directly? + +Pointers + +```cpp +vector toArray; +vector topArray; + +for(eastl_size_t i = 0; i < 32; i++) + toArray.push_back(TestObject(rng.RandLimit(20))); +for(eastl_size_t i = 0; i < 32; i++) // This needs to be a second loop because the addresses might change in the first loop due to container resizing. + topArray.push_back(&toArray[i]); + +struct TestObjectPtrCompare +{ + bool operator()(TestObject* a, TestObject* b) + { return a->mX < a->mX; } +}; + +quick_sort(topArray.begin(), topArray.end(), TestObjectPtrCompare()); +``` + +Array indexes + +```cpp +vector toArray; +vector toiArray; + +for(eastl_size_t i = 0; i < 32; i++) +{ + toArray.push_back(TestObject(rng.RandLimit(20))); + toiArray.push_back(i); +} + +struct TestObjectIndexCompare +{ + vector* mpArray; + + TestObjectIndexCompare(vector* pArray) : mpArray(pArray) { } + TestObjectIndexCompare(const TestObjectIndexCompare& x) : mpArray(x.mpArray){ } + TestObjectIndexCompare& operator=(const TestObjectIndexCompare& x) { mpArray = x.mpArray; return *this; } + + bool operator()(eastl_size_t a, eastl_size_t b) + { return (*mpArray)[a] < (*mpArray)[b]; } +}; + +quick_sort(toiArray.begin(), toiArray.end(), TestObjectIndexCompare(&toArray)); +``` + +Array indexes (simpler version using toArray as a global variable) + +```cpp +vector toArray; +vector toiArray; + +for(eastl_size_t i = 0; i < 32; i++) +{ + toArray.push_back(TestObject(rng.RandLimit(20))); + toiArray.push_back(i); +} + +struct TestObjectIndexCompare +{ + bool operator()(eastl_size_t a, eastl_size_t b) + { return toArray[a] < toArray[b]; } +}; + +quick_sort(toiArray.begin(), toiArray.end(), TestObjectIndexCompare(&toArray)); +``` + +## Iterators + +### Iter.1 What's the difference between iterator, const iterator, and const_iterator? + +An iterator can be modified and item it points to can be modified. +A const iterator cannot be modified, but the items it points to can be modified. +A const_iterator can be modified, but the items it points to cannot be modified. +A const const_iterator cannot be modified, nor can the items it points to. + +This situation is much like with char pointers: + +| Iterator type | Pointer equivalent | +|------|------| +| iterator | char* | +| const iterator | char* const | +| const_iterator | const char* | +| const const_iterator | const char* const | + +### Iter.2 How do I tell from an iterator what type of thing it is iterating? + +Use the value_type typedef from iterator_traits, as in this example + +```cpp +template +void DoSomething(Iterator first, Iterator last) +{ + typedef typename iterator_traits::value_type; + + // use value_type +} +``` + +### Iter.3 How do I iterate a container while (selectively) removing items from it? + +All EASTL containers have an erase function which takes an iterator as an argument and returns an iterator to the next item. Thus, you can erase items from a container while iterating it like so: + +```cpp +set intSet; +set::iterator i = intSet.begin(); + +while(i != intSet.end()) +{ + if(*i & 1) // Erase all odd integers from the container. + i = intSet.erase(i); + else + ++i; +} +``` + +### Iter.4 What is an insert_iterator? + +An insert_iterator is a utility class which is like an iterator except that when you assign a value to it, the insert_iterator inserts the value into the container (via insert()) and increments the iterator. Similarly, there are front_insert_iterator and back_insert_iterator, which are similar to insert_iterator except that assigning a value to them causes then to call push_front and push_back, respectively, on the container. These utilities may seem a slightly abstract, but they have uses in generic programming. + +---------------------------------------------- +End of document diff --git a/lib/EASTL/doc/Glossary.md b/lib/EASTL/doc/Glossary.md new file mode 100644 index 000000000..550209d2b --- /dev/null +++ b/lib/EASTL/doc/Glossary.md @@ -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 but is implemented via one bit per entry. STL vector 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 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."

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.

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:
`template class vector;`
`template void min(int, int);`
`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 (operator()) 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 iterator_tag. | +| iterator_tag | See iterator category. | +| 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::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. 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. 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.

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 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 diff --git a/lib/EASTL/doc/Gotchas.md b/lib/EASTL/doc/Gotchas.md new file mode 100644 index 000000000..aefe362ef --- /dev/null +++ b/lib/EASTL/doc/Gotchas.md @@ -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, 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 + + + diff --git a/lib/EASTL/doc/Introduction.md b/lib/EASTL/doc/Introduction.md new file mode 100644 index 000000000..9fa818830 --- /dev/null +++ b/lib/EASTL/doc/Introduction.md @@ -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 \ No newline at end of file diff --git a/lib/EASTL/doc/Maintenance.md b/lib/EASTL/doc/Maintenance.md new file mode 100644 index 000000000..82bdb8082 --- /dev/null +++ b/lib/EASTL/doc/Maintenance.md @@ -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 +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 +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 +inline void reverse(BidirectionalIterator first, BidirectionalIterator last) +{ + typedef typename iterator_traits::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 diff --git a/lib/EASTL/doc/Modules.md b/lib/EASTL/doc/Modules.md new file mode 100644 index 000000000..fe13f0c01 --- /dev/null +++ b/lib/EASTL/doc/Modules.md @@ -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
fixed_slist | Singly-linked list.
fixed_slist is a version which is implemented via a fixed block of contiguous memory.| +| list
fixed_list | Doubly-linked list. | +| intrusive_list
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
fixed_vector | Resizable array container. +| vector_set
vector_multiset | Set implemented via a vector instead of a tree. Speed and memory use is improved but resizing is slower. | +| vector_map
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
multiset
fixed_set
fixed_multiset | A set is a sorted unique collection, multiset is sorted but non-unique collection. | +| map
multimap
fixed_map
fixed_multimap | A map is a sorted associative collection implemented via a tree. It is also known as dictionary. | +| hash_map
hash_multimap
fixed_hash_map
fixed_hash_multimap | Map implemented via a hash table. | +| intrusive_hash_map
intrusive_hash_multimap
intrusive_hash_set
intrusive_hash_multiset | hash_map whereby the contained item provides the node implementation, much like intrusive_list. | +| hash_set
hash_multiset
fixed_hash_set
fixed_hash_map | Set implemented via a hash table. +| basic_string
fixed_string
fixed_substring | basic_string is a character string/array.
fixed_substring is a string which is a reference to a range within another string or character array.
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 (Key == T) | 16 | 20 | 4 | 8 | b | 1 | - | 1 | 1 | 1 | - | +| intrusive_hash_multimap | T (Key == T)  | 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 diff --git a/lib/EASTL/doc/html/EASTL Benchmarks.html b/lib/EASTL/doc/html/EASTL Benchmarks.html new file mode 100644 index 000000000..70ff23f84 --- /dev/null +++ b/lib/EASTL/doc/html/EASTL Benchmarks.html @@ -0,0 +1,330 @@ + + + + EASTL Benchmarks + + + + + + + + + + +

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. + + + +
  3. 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.
  4. + + + +
  5. 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).
  6. + + + +
+ + + +

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.
+ + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PlatformCompilerSTL typeBuildResults
Win32VC++ 7.1Microsoft (Dinkumware)DebugDetail
Win32VC++ 7.1Microsoft (Dinkumware)ReleaseDetail
Win32VC++ 7.1STLPortDebugDetail
Win32VC++ 7.1STLPortReleaseDetail
+ + + + + + + + + + + + + +
+
+
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
+ + + +
+ + + +
+ + + +
+ + + +
+ + + + + diff --git a/lib/EASTL/doc/html/EASTL Best Practices.html b/lib/EASTL/doc/html/EASTL Best Practices.html new file mode 100644 index 000000000..bc0792e89 --- /dev/null +++ b/lib/EASTL/doc/html/EASTL Best Practices.html @@ -0,0 +1,1001 @@ + + + + EASTL Best Practices + + + + + + + +

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.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1Consider intrusive containers.
2Consider fixed-size containers.
3Consider custom allocators.
4Consider hash tables instead of maps.
5Consider a vector_map (a.k.a. sorted vector) for unchanging data.
6Consider slist instead of list.
7Avoid redundant end() and size() in loops.
8Iterate containers instead of using operator[].
9Learn to use the string class appropriately.
10Cache list size if you want size() to be O(1).
11Use empty() instead of size() when possible.
12Know your container efficiencies.
13Use vector::reserve.
14Use vector::set_capacity to trim memory usage.
15Use swap() instead of a manually implemented version.
16Consider storing pointers instead of objects.
17Consider smart pointers instead of raw pointers.
18Use iterator pre-increment instead of post-increment.
19Make temporary references so the code can be traced/debugged.
20Consider bitvector or bitset instead of vector<bool>.
21Vectors can be treated as contiguous memory.
22Search hash_map<string> via find_as() instead of find().
23Take advantage of type_traits (e.g. EASTL_DECLARE_TRIVIAL_RELOCATE).
24Name containers to track memory usage.
25Learn the algorithms.
26Pass and return containers by reference instead of value.
27Consider using reset_lose_memory() for fast container teardown.
28Consider using fixed_substring instead of copying strings.
29Consider using vector::push_back(void).
+

Detail

+

1 + 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:

+

class Widget : public intrusive_list_node
+{ };
+
+intrusive_list<Widget> widgetList;
+widgetList.push_back(someWidget);

+

+

2 + 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:

+

fixed_set<int, 25> intSet; // Create a set capable of holding 25 elements.
+intSet.push_back(37);

+

+

3 + 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:

+

list<int> intList(pSomeAllocator, "graphics/intList");
+intList.push_back(37);

+

4 +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:

+

hash_map<int, const char*> stringTable;
+stringTable[37] = "hello";

+

5 + 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:

+

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:

+

vector_set<int, less<int>, EASTLAllocatorType, deque<int> > intSet;
+intSet.insert(37);

+

6 + 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:

+

slist<int> intSlist;
+intSlist.push_front(37);

+

7 +Avoid redundant end() and size() in loops.

+

Instead of writing code like this:
+

+
for(deque<int>::iterator it = d.begin(); it != d.end(); ++it)
+    ...
+write code like this:
+
+
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. By "constant end value" we mean containers which can be modified but end always remains the same.
+ + + + + + + + + + + + + + + +
Constant beginNon-constant beginConstant endNon-constant end
array1string
+ vector
+ deque
+ intrusive_list
+ intrusive_slist
+ vector_map
+ vector_multimap
+ vector_set
+ vector_multiset
+ bit_vector
+ hash_map
+ hash_multimap
+ hash_set
+ hash_multiset
+ intrusive_hash_map
+ intrusive_hash_multimap
+ intrusive_hash_set
+ intrusive_hash_multiset
array
+ list
+ slist
+ intrusive_list
+ intrusive_slist
+ map
+ multimap
+ set
+ multiset
+ hash_map2
+ hash_multimap2
+ hash_set2
+ hash_multiset2
+ intrusive_hash_map
+ intrusive_hash_multimap
+ intrusive_hash_set
+ intrusive_hash_multiset
string
+ vector
+ deque
+ vector_map
+ vector_multimap
+ vector_set
+ vector_multiset
+ bit_vector
+
1 Arrays can be neither resized nor reallocated.
+ 2 Constant end if the hashtable can't/won't re-hash. Non-constant if it can re-hash.
+

8 +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:

+

for(unsigned i = 0, iEnd = intVector.size(); i != iEnd; ++i)
+    intVector[i] = 37;

+

you can execute more efficiently by doing this:

+

for(vector<int>::iterator it = intVector.begin(), itEnd = intVector.end(); it != itEnd; ++it)
+    *it = 37;

+

9 +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 writtenWhat 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(" ");
+ puts(s.c_str());
+
+
puts(" ");

+ s.sprintf("u");
+
+
s = "u";

+ char array[32];
+ sprintf(array, "%d", 10);
+ 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.

+

10 +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:
+

+
list<int> intList;
+ size_t    n = 0;
+
+ intList.push_back(37);
+ ++n;
+ intList.pop_front();
+ --n;
+

11 +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.

+

12 +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() efficiencysize() efficiencyoperator[] efficiency

insert() efficiency

erase() efficiency

find() efficiency

sort efficiency

slist1O(n)-O(1)O(1)O(n)O(n+)

list

1n-

1

1

n

n log(n)

intrusive_slist1n-111n+
intrusive_list1n-111n log(n)
array111--nn log(n)
vector11a11 at end, else n1 at end, else nnn log(n)
vector_set11a11 at end, else n1 at end, else nlog(n)1
vector_multiset11a11 at end, else n1 at end, else nlog(n)1
vector_map11a11 at end, else n1 at end, else nlog(n)1
vector_multimap11a11 at end, else n1 at end, else nlog(n)1
deque11a11 at begin or end,
+ else n / 2
1 at begin or end,
+ else n / 2
nn log(n)
bit_vector11a11 at end, else n1 at end, else nnn log(n)
string, cow_string11a11 at end, else n1 at end, else nnn log(n)
set11-log(n)log(n)log(n)1
multiset11-log(n)log(n)log(n)1
map11log(n)log(n)log(n)log(n)1
multimap11-log(n)log(n)log(n)1
hash_set11-111-
hash_multiset11-1
11-
hash_map11-111-
hash_multimap11-111-
intrusive_hash_set11-111-
intrusive_hash_multiset11-111-
intrusive_hash_map11-111-
intrusive_hash_multimap11-111-
+


+ 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).
  • +
  • a 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.
  • +
+

13 +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:
+

+
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: +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. +

14 +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:

+vector<Widget> v;
+...
+
v.set_capacity();
+An example of resizing to zero and completely freeing the memory of a vector is the following:
+
+
vector<Widget> v;
+ ...
+
v.set_capacity(0);
+

15 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.

+

16 +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.

+

17 + 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:

+

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:

+

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.

+

18 + 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:

+

for(set<int>::iterator it(intSet.begin()), itEnd(intSet.end()); it != itEnd; ++it)
+     *it = 37;

+

19 + 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:

+

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:

+

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;
+ }

+

20 + 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.

+

21 +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:

+

struct Widget {
+     uint32_t x;
+     uint32_t y;
+ };
+
+ vector<Widget> v;
+
+ quick_sort((uint64_t*)v.data(), (uint64_t*)(v.data() + v.size()));

+

22 +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:

+

hash_map<string, int> hashMap;
+ hash_map<string, int>::iterator it = hashMap.find_as("hello"); // Using default hash and compare.

+

23 +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:
+
+
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.
+
+
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
  • +
+1 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. +

24 +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 container 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
+ 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.

+

25 +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
  • +
+

26 +Pass and return containers by reference instead of value.

+

If you aren't paying attention you might accidentally write code like this:

+

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.

+

27 +Consider using reset_lose_memory() 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:

+

pStackAllocator->push_bookmark();
+ hash_set<Widget, less<Widget>, StackAllocator> wSet(pStackAllocator);
+<use wSet>
+ wSet.reset_lose_memory();
+ pStackAllocator->pop_bookmark();

+

+

28 +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:

+

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.

+

29 + 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:

+

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:

+

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:

+

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
+
+
+
+

+ + diff --git a/lib/EASTL/doc/html/EASTL Design.html b/lib/EASTL/doc/html/EASTL Design.html new file mode 100644 index 000000000..479dacc80 --- /dev/null +++ b/lib/EASTL/doc/html/EASTL Design.html @@ -0,0 +1,424 @@ + + + + EASTL Design + + + + + + +

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: +

+
    +
  • 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.
  • +
  • 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. +
  3. Correctness
  4. +
  5. Portability
  6. +
  7. Readability
  8. +
+

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.
+

+
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:
+

+
// 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.
+

+
// 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 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.

+

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:

+

string a("hello");
+ string b(a);

+

the "hello" will be shared between a and b. If you then say this:

+

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

+
+

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

+
+
+End of document
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/lib/EASTL/doc/html/EASTL FAQ.html b/lib/EASTL/doc/html/EASTL FAQ.html new file mode 100644 index 000000000..04b157858 --- /dev/null +++ b/lib/EASTL/doc/html/EASTL FAQ.html @@ -0,0 +1,2385 @@ + + + + EASTL FAQ + + + + + + + +

EASTL FAQ

+

We provide a FAQ (frequently asked questions) list here for a number of commonly asked questions about EASTL and STL in +general. Feel free to suggest new FAQ additions based on your own experience.

+

Information

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1What is EASTL?
2What uses are EASTL suitable for?
3
How does EASTL differ from standard C++ +STL?
4Is EASTL thread-safe?
5What platforms/compilers does EASTL support?
6Why is there EASTL when there is the STL?
7Can I mix EASTL with standard C++ STL?
8Where can I learn more about STL and EASTL?
9What is the legal status of EASTL?
10Does EASTL deal with compiler exception handling settings?
11What C++ language features does EASTL use (e.g. virtual functions)?
12What compiler warning levels does EASTL support?
13Is EASTL compatible with Lint?
14What compiler settings do I need to compile EASTL?
15How hard is it to incorporate EASTL into my project?
16Should I use EASTL instead of std STL or instead of my custom library?
17I think I've found a bug. What do I do?
18Can EASTL be used by third party EA developers?
+

Performance +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1How efficient is EASTL compared to standard C++ STL implementations?
2How efficient is EASTL in general?
3Strings don't appear to use the "copy-on-write" optimization. Why not?
4Does EASTL cause code bloat, given that it uses templates?
5Don't STL and EASTL containers fragment memory?
6I don't see container optimizations for equivalent scalar types such as pointer types. +Why?
7I've seen some STL's provide a default quick "node allocator" as the default allocator. Why +doesn't EASTL do this?
8Templates sometimes seem to take a long time to compile. Why do I do about that?
9How do I assign a custom allocator to an EASTL container?
10How well does EASTL inline?
11How do I control function inlining?
12C++ / EASTL seems to bloat my .obj files much more than C does.
13What are the best compiler settings for EASTL?
+

Problems

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1I'm getting screwy behavior in sorting algorithms or sorted containers. What's wrong?
2I am getting compiler warnings (e.g. C4244, C4242 or C4267) that make no sense. Why?
3I am getting compiler warning C4530, which complains about exception handling and "unwind +semantics." What gives?
4Why are tree-based containers hard to read with a debugger?
5The EASTL source code is sometimes rather complicated looking. Why is that?
6When I get compilation errors, they are very long and complicated looking. What do I do?
7Templates sometimes seem to take a long time to compile. Why do I do about that?
8I get the compiler error: "template instantiation depth exceeds maximum of 17. use +-ftemplate-depth-NN to increase the maximum"
9I'm getting errors about min and max while compiling.
10C++ / EASTL seems to bloat my .obj files much more than C does.
11I'm getting compiler errors regarding operator new being previously defined.
12I'm getting errors related to wchar_t string  functions such as wcslen.
13I'm getting compiler warning C4619: there is no warning number Cxxxx (e.g. C4217).
14My stack-based fixed_vector is not respecting the object alignment requirements.
15I am getting compiler errors when using GCC under XCode (Macintosh/iphone).
16I am getting linker errors about Vsnprintf8 or Vsnprintf16.
17I am getting compiler errors about UINT64_C or UINT32_C.
18I am getting a crash with a global EASTL container.
19Why doesn't EASTL support passing NULL to functions with pointer arguments?
+

Debug

+ + + + + + + + + + + + + + + + + + + + + + + +
1How do I get VC++ mouse-overs to view templated data?
2How do I view containers if the visualizer/tooltip support is not present?
3The EASTL source code is sometimes rather complicated looking. Why is that?
4When I get compilation errors, they are very long and complicated looking. What do I +do?
5How do I measure hash table balancing?
+

Containers

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1Why do some containers have "fixed" versions (e.g. fixed_list) but others(e.g. deque) don't have +fixed versions?
2Can I mix EASTL with standard C++ STL?
3Why are there so many containers?
4Don't STL and EASTL containers fragment memory?
5I don't see container optimizations for equivalent scalar types such as pointer types. +Why?
6What about alternative container and algorithm implementations (e.g. treaps, skip lists, avl +trees)?
7Why are containers hard to read with a debugger?
8How do I assign a custom allocator to an EASTL container?
9How do I set the VC++ debugger to display EASTL container data with tooltips?
10How do I use a memory pool with a container?
11How do I write a comparison (operator<()) for a struct that contains two or more +members?
12Why doesn't container X have member function Y?
13How do I search a hash_map of strings via a char pointer efficiently? If I use map.find("hello") +it creates a temporary string, which is inefficient.
14Why are set and hash_set iterators const (i.e. const_iterator)?
15How do I prevent my hash container from re-hashing?
16Which uses less memory, a map or a hash_map?
17How do I write a custom hash function?
18How do I write a custom compare function for a map or set?
19How do I force my vector or string capacity down to the size of the container?
20How do I iterate a container while (selectively) removing items from it?
21How do I store a pointer in a container?
22How do I make a union of two containers? difference? intersection?
23How do I override the default global allocator?
24How do I do trick X with the string class?
25How do EASTL smart pointers compare to Boost smart pointers?
26How do your forward-declare an EASTL container?
27How do I make two containers share a memory pool?
28Can I use a std (STL) allocator with EASTL?
29 What are the requirements of classes stored in containers?
+

Algorithms

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1I'm getting screwy behavior in sorting algorithms or sorted containers. What's wrong?
2How do I write a comparison (operator<()) for a struct that contains two or more +members?
3How do I sort something in reverse order?
4I'm getting errors about min and max while compiling.
5Why don't algorithms take a container as an argument instead of iterators? A container would be +more convenient.
6Given a container of pointers, how do I find an element by value (instead of by +pointer)?
7When do stored objects need to support opertor +< vs. when do they need to support operator +==?
8How do I sort via pointers or array indexes instead of objects directly?
+

Iterators

+ + + + + + + + + + + + + + + + + + + +
1What's the difference between iterator, const iterator, and const_iterator?
2How do I tell from an iterator what type of thing it is iterating?
3How do I iterate a container while (selectively) removing items from it?
4What is an insert_iterator?
+


+ Information +

+

Info.1 +What is EASTL?

+

EASTL refers to "EA Standard Template Library." It is a C++ template library that is analogous to the template facilities of the C++ standard library, which are often referred to as the STL. EASTL consists of the following systems:

+
    +
  • Containers
  • +
  • Iterators
  • +
  • Algorithms
  • +
  • Utilities
  • +
  • Smart pointers
  • +
  • Type traits
  • +
+

EASTL provides extensions and optimizations over the equivalents in standard C++ STL.

+

EASTL is a professional-level implementation which outperforms commercial implementations (where functionality overlaps) and is significantly easier to read and debug.

+

Info.2 +What uses are EASTL suitable for?

+

EASTL is suitable for any place where templated containers and algorithms would be appropriate. Thus any C++ tools could use it and many C++ game runtimes could use it, especially 2005+ generation game platforms. EASTL has optimizations that make it more suited to the CPUs and memory systems found on console platforms. Additionally, EASTL has some type-traits and iterator-traits-derived template optimizations that make it generally more efficient than home-brew templated containers.

+

Info.3 +How does EASTL differ from standard C++ STL?

+

There are three kinds of ways that EASTL differs from standard STL:

+
    +
  1. EASTL equivalents to STL sometimes differ.
  2. +
  3. EASTL implementations sometimes differ from STL implementations of the same thing.
  4. +
  5. EASTL has functionality that doesn't exist in STL.
  6. +
+

With respect to item #1, the changes are such that they benefit game development and not the type that could silently hurt you if you were more familiar with STL interfaces.

+

With respect to item #2, where EASTL implementations differ from STL implementations it is almost always due to improvements being made in the EASTL versions or tradeoffs being made which are considered better for game development.

+

With respect to item #3, there are a number of facilities that EASTL has that STL doesn't have, such as intrusive_list and slist containers, smart pointers, and type traits. All of these are facilities that assist in making more efficient game code and data.

+

Ways in which EASTL is better than standard STL:

+
    +
  • Has higher performance in release builds, sometimes dramatically so.
  • +
  • Has significantly higher performance in debug builds, due to less call overhead.
  • +
  • Has extended per-container functionality, particularly for game development.
  • +
  • Has additional containers that are useful for high performance game development.
  • +
  • Is easier to read, trace, and debug.
  • +
  • Memory allocation is much simpler and more controllable.
  • +
  • Has higher portability, as there is a single implementation for all platforms.
  • +
  • Has support of object alignment, whereas such functionality is not natively supported by STL.
  • +
  • We have control over it, so we can modify it as we like.
  • +
  • Has stricter standards for container design and behavior, particularly as this benefits game development.
  • +
+

Ways in which EASTL is worse than standard STL:

+
    +
  • Standard STL implementations are currently very reliable and weather-worn, whereas EASTL is less tested.
  • +
  • Standard STL is automatically available with just about every C++ compiler vendor's library.
  • +
  • Standard STL is supported by the compiler vendor and somewhat by the Internet community.
  • +
+

EASTL coverage of std STL

+
    +
  • list
  • +
  • vector
  • +
  • deque
  • +
  • string
  • +
  • set
  • +
  • multiset
  • +
  • map
  • +
  • multimap
  • +
  • bitset
  • +
  • queue
  • +
  • stack
  • +
  • priority_queue
  • +
  • memory
  • +
  • numeric
  • +
  • algorithm (all but inplace_merge, prev_permutation, next_permutation, nth_element, includes, unique_copy)
  • +
  • utility
  • +
  • functional
  • +
  • iterator
  • +
  • string_view
  • +
  • variant
  • +
  • any
  • +
  • optional
  • +
+

EASTL additions/amendments to std STL

+
    +
  • allocators work in a simpler way.
  • +
  • exception handling can be disabled.
  • +
  • all containers expose/declare their node size, so you can make a node allocator for them.
  • +
  • all containers have reset_lose_memory(), which unilaterally forgets their contents.
  • +
  • all containers have validate() and validate_iterator() functions.
  • +
  • all containers understand and respect object alignment requirements.
  • +
  • all containers guarantee no memory allocation upon being newly created as empty.
  • +
  • all containers and their iterators can be viewed in a debugger (no other STL does this, believe it or not).
  • +
  • linear containers guarantee linear memory.
  • +
  • vector has push_back(void).
  • +
  • vector has a data() function.
  • +
  • vector<bool> is actually a vector of type bool.
  • +
  • vector and string have set_capacity().
  • +
  • string has sprintf(), append_sprintf(), trim(), compare_i(), make_lower(), make_upper().
  • +
  • deque allows you to specify the subarray size.
  • +
  • list has a push_back(void) and push_back(void) function.
  • +
  • hash_map, hash_set, etc. have find_as().
  • +
+

EASTL coverage of TR1 (tr1 refers to proposed additions for the next C++ standard library, ~2008)

+
    +
  • array
  • +
  • type_traits (there are about 30 of these)
  • +
  • unordered_set (EASTL calls it hash_set)
  • +
  • unordered_multiset
  • +
  • unordered_map
  • +
  • unordered_multimap
  • +
  • shared_ptr, shared_array, weak_ptr, scoped_ptr, scoped_array, intrusive_ptr
  • +
+

EASTL additional functionality (not found elsewhere)

+
    +
  • fixed_list
  • +
  • fixed_slist
  • +
  • fixed_vector
  • +
  • fixed_string
  • +
  • fixed_substring
  • +
  • fixed_set
  • +
  • fixed_multiset
  • +
  • fixed_map
  • +
  • fixed_multimap
  • +
  • fixed_hash_set
  • +
  • fixed_hash_multiset
  • +
  • fixed_hash_map
  • +
  • fixed_hash_multimap
  • +
  • fixed_function
  • +
  • vector_set
  • +
  • vector_multiset
  • +
  • vector_map
  • +
  • vector_multimap
  • +
  • intrusive_list
  • +
  • intrusive_slist
  • +
  • intrusive_sdlist
  • +
  • intrusive_hash_set
  • +
  • intrusive_hash_multiset
  • +
  • intrusive_hash_map
  • +
  • intrusive_hash_multimap
  • +
  • slist (STLPort's STL has this)
  • +
  • heap
  • +
  • linked_ptr, linked_array
  • +
  • sparse_matrix (this is not complete as of this writing)
  • +
  • ring_buffer
  • +
  • compressed_pair
  • +
  • call_traits
  • +
  • binary_search_i, change_heap, find_first_not_of, find_last_of, find_last_not_of, identical
  • +
  • comb_sort, bubble_sort, selection_sort, shaker_sort, bucket_sort
  • +
  • equal_to_2, not_equal_to_2, str_equal_to, str_equal_to_i
    +
  • +
+

Info.4 +Is EASTL thread-safe? +

+

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 individual 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 expected 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.

+

Info.5 +What platforms/compilers does EASTL support?

+

EASTL's support depends entirely on the compiler and not on the platform. EASTL works on any C++ compiler that completely conforms the C++ language standard. Additionally, EASTL is 32 bit and 64 bit compatible. Since EASTL does not use the C or C++ standard library (with a couple small exceptions), it doesn't matter what kind of libraries are provided (or not provided) by the compiler vendor. However, given that we need to work with some compilers that aren't 100% conforming to the language standard, it will be useful to make a list here of these that are supported and those that are not:

+
+ + + + + + + + + + + + + + + + + + + + + +
CompilerStatusNotes
GCC 3.x+Not SupportedNot officially supported due to migration to Clang.
MSVC 12.0+SupportedThis compiler is used by the Windows based platforms
Clang 4.0+SupportedThis compiler is used by the Linux based platforms
+
+

Info.6 +Why is there EASTL when there is the STL?

+

The STL is largely a fine library for general purpose C++. However, we can improve upon it for our uses and gain other advantages as well. The primary motivations for the existence of EASTL are the following:

+
    +
  • 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.
  • +
  • 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 puts an emphasis on correctness before performance, whereas sometimes you can get significant performance gains by making things less academically 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.
  • +
+

Note that there isn't actually anything in the C++ standard called "STL." STL is a term that merely refers to the templated portion of the C++ standard library.

+

Info.7 +Can I mix EASTL with standard C++ STL?

+

This is possible to some degree, though the extent depends on the implementation of C++ STL. One of things that makes interoperability is something called iterator categories. Containers and algorithms recognize iterator types via their category and STL iterator categories are not recognized by EASTL and vice versa.
+
+Things that you definitely can do:

+
    +
  • #include both EASTL and standard STL headers from the same .cpp file.
  • +
  • Use EASTL containers to hold STL containers.
  • +
  • Construct an STL reverse_iterator from an EASTL iterator.
  • +
  • Construct an EASTL reverse_iterator from an STL iterator.
  • +
+

Things that you probably will be able to do, though a given std STL implementation may prevent it: +

+
    +
  • Use STL containers in EASTL algorithms.
  • +
  • Use EASTL containers in STL algorithms.
  • +
  • Construct or assign to an STL container via iterators into an EASTL container.
  • +
  • Construct or assign to an EASTL container via iterators into an STL container.
  • +
+

Things that you would be able to do if the given std STL implementation is bug-free: +

+
    +
  • Use STL containers to hold EASTL containers. Unfortunately, VC7.x STL has a confirmed bug that prevents this. Similarly, STLPort versions prior to v5 have a similar but.
  • +
+

Things that you definitely can't do:

+
    +
  • Use an STL allocator directly with an EASTL container (though you can use one indirectly).
  • +
  • Use an EASTL allocator directly with an STL container (though you can use one indirectly).
  • +
+

Info.8 +Where can I learn more about STL and EASTL? +

+

EASTL is close enough in philosophy and functionality to standard C++ STL that most of what you read about STL applies to EASTL. This is particularly useful with respect to container specifications. It would take a lot of work to document EASTL containers and algorithms in fine detail, whereas most standard STL documentation applies as-is to EASTL. We won't cover the differences here, as that's found in another FAQ entry.

+

That being said, we provide a list of sources for STL documentation that may be useful to you, especially if you are less familiar with the concepts of STL and template programming in general.

+
    +
  • The SGI STL web site. Includes a good STL reference.
  • +
  • CodeProject STL introduction.
  • +
  • Scott Meyers Effective STL book.
  • +
  • The Microsoft online STL documentation. Microsoft links go bad every couple months, so try searching for STL at the Microsoft MSDN site.
  • +
  • The Dinkumware online STL documentation. 
  • +
  • The C++ standard, which is fairly readable. You can buy an electronic version for about $18 and in the meantime you can make do with draft revisions of it off the Internet by searching for "c++ draft standard".
  • +
  • STL performance tips, by Pete Isensee
  • +
  • STL algorithms vs. hand-written loops, by Scott Meyers.
  • +
  • cppreference.com
  • +
  • isocpp.org
  • +
+

Info.9 +What is the legal status of EASTL?

+

EASTL is usable for all uses within Electronic Arts, both for internal usage and for shipping products for all platforms. Any externally derived code would be explicitly stated as such and approved by the legal department if such code ever gets introduced. As of EASTL v1.0, the red_black_tree.cpp file contains two functions derived from the original HP STL and have received EA legal approval for usage in any product.

+

Info.10 +Does EASTL deal with compiler exception handling settings?

+

EASTL has automatic knowledge of the compiler's enabling/disabling of exceptions. If your compiler is set to disable exceptions, EASTL automatically detects so and executes without them. Also, you can force-enable or force-disable that setting to override the automatic behavior by #defining EASTL_EXCEPTIONS_ENABLED to 0 or 1. See EASTL's config.h for more information.

+

Info.11 + What C++ language features does EASTL use (e.g. virtual +functions)?

+

EASTL uses the following C++ language features:

+
    +
  • Template functions, classes, member functions.
  • +
  • Multiple inheritance.
  • +
  • Namespaces.
  • +
  • Operator overloading.
  • +
+

EASTL does not use the following C++ language features: +

+
    +
  • Virtual functions / interfaces.
  • +
  • RTTI (dynamic_cast).
  • +
  • Global and static variables. There are a couple class static const variables, but they act much like enums.
  • +
  • Volatile declarations
  • +
  • Template export.
  • +
  • Virtual inheritance.
  • +
+

EASTL may use the following C++ language features: +

+
    +
  • Try/catch. This is an option that the user can enable and it defaults to whatever the compiler is set to use.
  • +
  • Floating point math. Hash containers have one floating point calculation, but otherwise floating point is not used.
  • +
+

Notes: +

+
    +
  • EASTL uses rather little of the standard C or C++ library and uses none of the C++ template library (STL) and iostream library. The memcpy family of functions is one example EASTL C++ library usage.
  • +
  • EASTL never uses global new / delete / malloc / free. All allocations are done via user-specified allocators, though a default allocator definition is available.
  • +
+

Info.12 + What compiler warning levels does EASTL support? +

+

For VC++ EASTL should compile without warnings on level 4, and should compile without warnings for "warnings disabled by default" except C4242, C4514, C4710, C4786, and C4820. These latter warnings are somewhat draconian and most EA projects have little choice but to leave them disabled.

+

For GCC, EASTL should compile without warnings with -Wall. Extensive testing beyond that hasn't been done.

+

However, due to the nature of templated code generation and due to the way compilers compile templates, unforeseen warnings may occur in user code that may or may not be addressable by modifying EASTL.

+

Info.13 + Is EASTL compatible with Lint? +

+

As of EASTL 1.0, minimal lint testing has occurred. Testing with the November 2005 release of Lint (8.00t) demonstrated bugs in Lint that made its analysis not very useful. For example, Lint seems to get confused about the C++ typename keyword and spews many errors with code that uses it. We will work with the makers of Lint to get this resolved so that Lint can provide useful information about EASTL.

+

Info.14 + What compiler settings do I need to compile EASTL? +

+

EASTL consists mostly of header files with templated C++ code, but there are also a few .cpp files that need to be compiled and linked in order to use some of the modules. EASTL will compile in just about any environment. As mentioned elsewhere in this FAQ, EASTL can be compiled at the highest warning level of most compilers, transparently deals with compiler exception handling settings, is savvy to most or all compilation language options (e.g. wchar_t is built-in or not, for loop variables are local or not), and has almost no platform-specific or compiler-specific code. For the most part, you can just drop it in and it will work. The primary thing that needs to be in place is that EASTL .cpp files need to be compiled with the same struct padding/alignment settings as other code in the project. This of course is the same for just about any C++ source code library.

+

See the Performance section of this FAQ for a discussion of the optimal compiler settings for EASTL performance.

+

Info.15 +How hard is it to incorporate EASTL into my project?

+

It's probably trivial.
+
+EASTL has only one dependency: EABase. And EASTL auto-configures itself for most compiler environments and for the most typical configuration choices. Since it is fairly highly warning-free, you won't likely need to modify your compiler warning settings, even if they're pretty strict. EASTL has a few .cpp files which need to be compiled if you want to use the modules associated with those files. You can just compile those files with your regular compiler settings. Alternatively, you can use one of the EASTL project files.
+
+In its default configuration, the only thing you need to provide to make EASTL work is to define implementations of the following operator new functions:

+
#include <new>
+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);
+The flags and debugFlags arguments correspond to PPMalloc/RenderWare GeneralAllocator/GeneralAllocatorDebug Malloc equivalents.
+

Info.16 +Should I use EASTL instead of std STL or instead of my custom library?

+

There are reasons you may want to use EASTL; there are reasons you may not want to use it. Ditto for std STL or any other library. Here we present a list of reasons (+ and -) for why you might want to use one or another. However, it should be noted that while EASTL contains functionality found in std STL, it has another ~40% of functionality not found in std STL, so EASTL and std STL (and whatever other template library you may have) are not mutually exclusive.
+
+EASTL
+

+
+ Has higher performance than any commercial STL, especially on console platforms.
+ + Has extended functionality tailored for game development.
+ + Is highly configurable, and we own it so it can be amended at will. Std STL is owned by a third party committee.
+ + Is much easier to read and debug than other similar libraries, especially std STL.
+
+ - Is highly unit tested, but does not have the same level as std STL.
+ - Is more complicated than many users' lite template libraries, and may put off some beginners.
+- EASTL  
+

Std STL +

+
+ Is highly portable; your STL code will likely compile and run anywhere.
+ + Works without the need to install or download any package to use it. It just works.
+ + Is highly reliable and supported by the compiler vendor. You can have confidence in it.
+ + Some std STL versions (e.g. STLPort, VC8 STL) have better runtime debug checking than EASTL.
+
+ - Has (sometimes greatly) variable implementations, behavior, and performance between implementations.
+ - Is usually hard to read and debug.
+ - Doesn't support some of the needs of game development, such as aligned allocations, named allocations, intrusive containers, etc.
+- Is not as efficient as EASTL, especially on console platforms.
+

Your own library +

+
(please forgive us for implying there may be weaknesses in your libraries)
+ +

+ + You have control over it and can make it work however you want.
+ + You can fix bugs in it on the spot and have the fix in your codebase immediately.
+ + Your own library can be highly integrated into your application code or development environment.
+
+ - Many custom libraries don't have the same level of testing as libraries such as std STL or EASTL.
+ - Many custom libraries don't have the same breadth or depth as std STL or especially EASTL.
+- Many custom libraries don't have the level of performance tuning that std STL or especially EASTL has.
+

Info.17 +I think I've found a bug. What do I do?

+

Verify that you indeed have a bug
+There are various levels of bugs that can occur, which include the following:

+
    +
  1. Compiler warnings generated by EASTL.
  2. +
  3. Compiler errors generated by EASTL (failure to compile well-formed code).
  4. +
  5. Runtime misbehavior by EASTL (function does the wrong thing).
  6. +
  7. Runtime crash or data corruption by EASTL.
  8. +
  9. Mismatch between EASTL documentation and behavior.
  10. +
  11. Mismatch between EASTL behavior and user's expectations (mis-design).
  12. +
+

Any of the above items can be the fault of EASTL. However, the first four can also be the fault of the user. Your primary goal in verifying a potential bug is to determine if it is an EASTL bug or a user bug. Template errors can sometimes be hard to diagnose. It's probably best if you first show the problem to somebody you know to make sure you are not missing something obvious. Creating a reproducible case may be useful in helping convince yourself, but as is mentioned below, this is not required in order to report the bug.
+
+ Report the bug
+The first place to try is the standard EA centralized tech support site. As of this writing (10/2005), that tech site is http://eatech/. Due to the frequent technology churn that seems to occur within Electronic Arts, the bug reporting system in place when you read this may not be the one that was in place when this FAQ entry was written. If the tech site route fails, consider directly contacting the maintainer of the EASTL package.
+
+In reporting a bug, it is nice if there is a simple reproducible case that can be presented. However, such a case requires time to create, and so you are welcome to initially simply state what you think the bug is without producing a simple reproducible case. It may be that this is a known bug or it may be possible to diagnose the bug without a reproducible case. If more information is needed then the step of trying to produce a reproducible case may be necessary.

+

Info.18 + Can EASTL be used by third party EA developers?

+

EASTL and other core technologies authored by EA (and not licensed from other companies) can be used in source and binary form by designated 3rd parties. The primary case where there is an issue is if the library contains platform specific code for a platform that the 3rd party is not licensed for. In that case the platform-specific code would need to be removed. This doesn’t apply to EASTL, nor many of the other core tech packages.

+

Performance +

+ +

Perf.1 How efficient is EASTL compared to standard C++ STL implementations?

+

With respect to the functionality that is equivalent between EASTL and standard STL, the short answer to this is that EASTL is as at least as efficient as other STL implementations and in a number of aspects is more so. EASTL has functionality such as intrusive_list and linked_ptr that don't exist in standard STL but are explicitly present to provide significant optimizations over standard STL.

+

The medium length answer is that EASTL is significantly more efficient than Dinkumware STL, and Microsoft Windows STL. EASTL is generally more efficient than Metrowerks STL, but Metrowerks has a few tricks up its sleeve which EASTL doesn't currently implement. EASTL is roughly equal in efficiency to STLPort and GCC 3.x+ STL, though EASTL has some optimizations that these do not.

+

The long answer requires a breakdown of the functionality between various versions of the STL.

+ +

Perf.2 How efficient is EASTL in general?

+

This question is related to the question, "How efficient are templates?" If you understand the effects of templates then you can more or less see the answer for EASTL. Templates are more efficient than the alternative when they are used appropriately, but can be less efficient than the alternative when used under circumstances that don't call for them. The strength of templates is that the compiler sees all the code and data types at compile time and can often reduce statements to smaller and faster code than with conventional non-templated code. The weakness of templates is that the sometimes produce more code and can result in what is often called "code bloat". However, it's important to note that unused template functions result in no generated nor linked code, so if you have a templated class with 100 functions but you only use one, only that one function will be compiled.

+

EASTL is a rather efficient implementation of a template library and pulls many tricks of the trade in terms of squeezing optimal performance out of the compiler. The only way to beat it is to write custom code for the data types you are working with, and even then people are sometimes surprised to find that their hand-implemented algorithm works no better or even worse than the EASTL equivalent. But certainly there are ways to beat templates, especially if you resort to assembly language programming and some kinds of other non-generic tricks.

+ +

Perf.3 Strings don't appear to use the "copy-on-write" (CoW) optimization. Why not?

+

+Short answer
+CoW provides a benefit for a small percentage of uses but provides a disadvantage for the large majority of uses.
+
+Long answer
+The primary benefit of CoW is that it allows for the sharing of string data between two string objects. Thus if you say this: +

string a("hello");
+string b(a);
+the "hello" will be shared between a and b. If you then say this: +
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 and 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.

+

References

+

This is a good starting HTML reference on the topic:
+    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

+ +

Perf.4 Does EASTL cause code bloat, given that it uses templates?

+

The reason that templated functions and classes might cause an increase in code size +because each template instantiation theoretically creates a unique piece of code. For example, when you compile this +code:

+
template <typename T>
+const T min(const T a, const T b)
+    { return b < a ? b : a; }
+
+int    i = min<int>(3, 4);
+double d = min<double>(3.0, 4.0);
+

the compiler treats it as if you wrote this:

+
int min(const int a, const int b)
+    { return b < a ? b : a; }
+double min(const double a, const double b) +    { return b < a ? b : a; }
+

Imagine this same effect happening with containers such as list and map and you can see how it is that templates can cause code proliferation.

+

A couple things offset the possibility of code proliferation: inlining and folding. In practice the above 'min' function would be converted to inlined functions by the compiler which occupy only a few CPU instructions. In many of the simplest cases the inlined version actually occupies less code than the code required to push parameters on the stack and execute a function call. And they will execute much faster as well.

+

Code folding (a.k.a. "COMDAT folding", "duplicate stripping", "ICF" / "identical code folding") is a compiler optimization whereby the compiler realizes that two independent functions have compiled to the same code and thus can be reduced to a single function. The Microsoft VC++ compiler (Since VS2005), and GCC (v 4.5+) can do these kinds of optimizations on all platforms. This can result, for example, in all templated containers of pointers (e.g. vector<char*>, vector<Widget*>, etc.) to be linked as a single implementation. This folding occurs at a function level and so individual member functions can be folded while other member functions are not. A side effect of this optimization is that you aren't likely to gain much much declaring containers of void* instead of the pointer type actually contained.

+

The above two features reduce the extent of code proliferation, but certainly don't eliminate it. What you need to think about is how much code might be generated vs. what your alternatives are. Containers like vector can often inline completely away, whereas more complicated containers such as map can only partially be inlined. In the case of map, if you need such a container for your Widgets, what alternatives do you have that would be more efficient than instantiating a map? This is up to you to answer.

+

It's important to note that C++ compilers will throw away any templated functions that aren't used, including unused member functions of templated classes. However, some argue that by having many functions available to the user that users will choose to use that larger function set rather than stick with a more restricted set.

+

Also, don't be confused by syntax bloat vs. code bloat. In looking at templated libraries such as EASTL you will notice that there is sometimes a lot of text in the definition of a template implementation. But the actual underlying code is what you need to be concerned about.

+

There is a good Usenet discussion on this topic at: http://groups.google.com/group/comp.lang.c++.moderated/browse_frm/thread/2b00649a935997f5

+

Perf.5 +Don't STL and EASTL containers fragment memory?

+

They only fragment memory if you use them in a way that does so. This is no different from any other type of container used in a dynamic way. There are various solutions to this problem, and EASTL provides additional help as well:

+
    +
  • For vectors, use the reserve function (or the equivalent constructor) to set aside a block of memory for the container. The container will not reallocate memory unless you try grow beyond the capacity you reserve.
  • +
  • EASTL has "fixed" variations of containers which allow you to specify a fixed block of memory which the container uses for its memory. The container will not allocate any memory with these types of containers and all memory will be cache-friendly due to its locality.
  • +
  • You can assign custom allocators to containers instead of using the default global allocator. You would typically use an allocator that has its own private pool of memory.
  • +
  • Where possible, add all a container's elements to it at once up front instead of adding them over time. This avoids memory fragmentation and increase cache coherency.
  • +
+ +

Perf.6 I don't see container optimizations for equivalent scalar types such as pointer types. Why?

+

Metrowerks (and no other, as of this writing) STL has some container specializations for type +T* which maps them to type void*. The idea is that a user who declares a list of Widget* and a list of Gadget* +will generate only one container: a list of void*. As a result, code generation will be smaller. Often this is +done only in optimized builds, as such containers are harder to view in debug builds due to type information being lost.
+
+The addition of this optimization is under consideration for EASTL, though it might be noted that optimizing +compilers such as VC++ are already capable of recognizing duplicate generated code and folding it automatically +as part of link-time code generation (LTCG) (a.k.a. "whole program optimization"). This has been verified +with VC++, as the following code and resulting disassembly demonstrate:

+
eastl::list<int*>        intPtrList;
+eastl::list<TestObject*> toPtrList;
+
+eastl_size_t n1 = intPtrList.size();
+eastl_size_t n2 = toPtrList.size();
+
+0042D288  lea         edx,[esp+14h]
+0042D28C  call        eastl::list<TestObject>::size (414180h)
+0042D291  push        eax 
+0042D292  lea         edx,[esp+24h]
+0042D296  call        eastl::list<TestObject>::size (414180h)
+

Note that in the above case the compiler folded the two implementations of size() into a single implementation.

+ +

Perf.7 +I've seen some STL's provide a default quick "node allocator" as the default allocator. Why doesn't EASTL do this?

+

Short answer
+
This is a bad, misguided idea.

+

Long answer
+These node allocators implement a heap for all of STL with buckets for various sizes of allocations and implemented fixed-size pools for each of these buckets. These pools are attractive at first because they do well in STL comparison benchmarks, especially when thread safety is disabled. Such benchmarks make it impossible to truly compare STL implementations because you have two different allocators in use and in some cases allocator performance can dominate the benchmark. However, the real problem with these node allocators is that they badly fragment and waste memory. The technical discussion of this topic is outside the scope of this FAQ, but you can learn more about it by researching memory management on the Internet. Unfortunately, the people who implement STL libraries are generally not experts on the topic of memory management. A better approach, especially for game development, is for the user to decide when fixed-size pools are appropriate and use them via custom allocator assignment to containers.

+

Perf.8 Templates sometimes seem to take a long time to compile. Why do I do about that? +

+

C++ compilers are generally slower than C compilers, and C++ templates are generally slower to compile than regular C++ code. EASTL has some extra functionality (such as type_traits and algorithm specializations) that is not found in most other template libraries and significantly improves performance and usefulness but adds to the amount of code that needs to be compiled. Ironically, we have a case where more source code generates faster and smaller object code.

+

The best solution to the problem is to use pre-compiled headers, which are available on all modern ~2002+) compilers, such as VC6.0+, GCC 3.2+, and Metrowerks 7.0+. In terms of platforms this means all 2002+ platforms.

+

Some users have been speeding up build times by creating project files that put all the source code in one large .cpp file. This has an effect similar to pre-compiled headers. It can go even faster than pre-compiled headers but has downsides in the way of convenience and portability.

+

Perf.10 +How well does EASTL inline?

+

EASTL is written in such as way as to be easier to inline than typical templated libraries such as STL. How is this so? It is so because EASTL reduces the inlining depth of many functions, particularly the simple ones. In doing so it makes the implementation less "academic" but entirely correct. An example of this is the vector operator[] function, which is implemented like so with Microsoft STL:

+
reference operator[](size_type n) {
+   return *(begin() + n);
+}
+EASTL implements the function directly, like so: +
reference operator[](size_type n) {
+    return *(mpBegin + n);
+}
+Both implementations are correct, but the EASTL implementation will run faster in debug builds, be easier to debug, and will be more likely to be inlined when the usage of this function is within a hierarchy of other functions being inlined. It is not so simple to say that the Microsoft version will always inline in an optimized build, as it could be part of a chain and cause the max depth to be exceeded.
+
+That being said, EASTL appears to inline fairly well under most circumstances, including with GCC, which is the poorest of the compilers in its ability to inline well.
+

Perf.11 +How do I control function inlining?

+

Inlining is an important topic for templated code, as such code often relies on the compiler being able to do good function inlining for maximum performance. GCC, VC++, and Metrowerks are discussed here. We discuss compilation-level inlining and function-level inlining here, though the latter is likely to be of more use to the user of EASTL, as it can externally control how EASTL is inlined. A related topic is GCC's template expansion depth, discussed elsewhere in this FAQ. We provide descriptions of inlining options here but don't currently have any advice on how to best use these with EASTL.

+

Compilation-Level Inlining -- VC++

+

VC++ has some basic functionality to control inlining, and the compiler is pretty good at doing aggressive inlining when optimizing on for all platforms.

+
+

#pragma inline_depth( [0... 255] )

+

Controls the number of times inline expansion can occur by controlling the number of times that a series of function calls can be expanded (from 0 to 255 times). This pragma controls the inlining of functions marked inline and or inlined automatically under the /Ob2 option. The inline_depth pragma controls the number of times a series of function calls can be expanded. For example, if the inline depth is 4, and if A calls B and B then calls C, all three calls will be expanded inline. However, if the closest inline expansion is 2, only A and B are expanded, and C remains as a function call.

+

#pragma inline_recursion( [{on | off}] )

+

Controls the inline expansion of direct or mutually recursive function calls. Use this pragma to control functions marked as inline and or functions that the compiler automatically expands under the /Ob2 option. Use of this pragma requires an /Ob compiler option setting of either 1 or 2. The default state for inline_recursion is off. The inline_recursion pragma controls how recursive functions are expanded. If inline_recursion is off, and if an inline function calls itself (either directly or indirectly), the function is expanded only once. If inline_recursion is on, the function is expanded multiple times until it reaches the value set by inline_depth, the default value of 8, or a capacity limit.

+
+

Compilation-Level Inlining -- GCC

+

GCC has a large set of options to control function inlining. Some options are available only  in GCC 3.0 and later and thus not present on older platforms.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-fno-default-inlineDo not make member functions inline by default merely because they are defined inside the class scope (C++ only). Otherwise, when you specify -O, member functions defined inside class scope are compiled inline by default; i.e., you don't need to add `inline' in front of the member function name.
-fno-inlineDon't pay attention to the inline keyword. Normally this option is used to keep the compiler from expanding any functions inline. Note that if you are not optimizing, no functions can be expanded inline.
-finline-functionsIntegrate all simple functions into their callers. The compiler heuristically decides which functions are simple enough to be worth integrating in this way. If all calls to a given function are integrated, and the function is declared static, then the function is normally not output as assembler code in its own right. Enabled at level -O3.
-finline-limit=nBy default, GCC limits the size of functions that can be inlined. This flag allows the control of this limit for functions that are explicitly marked as inline (i.e., marked with the inline keyword or defined within the class definition in c++). n is the size of functions that can be inlined in number of pseudo instructions (not counting parameter handling). pseudo-instructions are an internal representation of function size. The default value of n is 600. Increasing this value can result in more inlined code at the cost of compilation time and memory consumption. Decreasing usually makes the compilation faster and less code will be inlined (which presumably means slower programs). This option is particularly useful for programs that use inlining heavily such as those based on recursive templates with C++.
+
+ Inlining is actually controlled by a number of parameters, which may be specified individually by using --param name=value. The -finline-limit=n option sets some of these parameters as follows:
+
+ max-inline-insns-single
+    is set to n/2.
+ max-inline-insns-auto
+    is set to n/2.
+ min-inline-insns
+    is set to 130 or n/4, whichever is smaller.
+ max-inline-insns-rtl
+    is set to n.
+
+ See --param below for a documentation of the individual parameters controlling inlining.
-fkeep-inline-functionsEmit all inline functions into the object file, even if they are inlined where used.
--param name=valueIn some places, GCC uses various constants to control the amount of optimization that is done. For example, GCC will not inline functions that contain more that a certain number of instructions. You can control some of these constants on the command-line using the --param option. 
+
+ max-inline-insns-single
+ Several parameters control the tree inliner used in gcc. This number sets the maximum number of instructions (counted in GCC's internal representation) in a single function that the tree inliner will consider for inlining. This only affects functions declared inline and methods implemented in a class declaration (C++). The default value is 450.
+
+ max-inline-insns-auto
+ When you use -finline-functions (included in -O3), a lot of functions that would otherwise not be considered for inlining by the compiler will be investigated. To those functions, a different (more restrictive) limit compared to functions declared inline can be applied. The default value is 90.
+
+ large-function-insns
+ The limit specifying really large functions. For functions larger than this limit after inlining inlining is constrained by --param large-function-growth. This parameter is useful primarily to avoid extreme compilation time caused by non-linear algorithms used by the backend. This parameter is ignored when -funit-at-a-time is not used. The default value is 2700.
+
+ large-function-growth
+ Specifies maximal growth of large function caused by inlining in percents. This parameter is ignored when -funit-at-a-time is not used. The default value is 100 which limits large function growth to 2.0 times the original size.
+
+ inline-unit-growth
+ Specifies maximal overall growth of the compilation unit caused by inlining. This parameter is ignored when -funit-at-a-time is not used. The default value is 50 which limits unit growth to 1.5 times the original size.
+
+ max-inline-insns-recursive
+ max-inline-insns-recursive-auto
+ Specifies maximum number of instructions out-of-line copy of self recursive inline function can grow into by performing recursive inlining. For functions declared inline --param max-inline-insns-recursive is taken into acount. For function not declared inline, recursive inlining happens only when -finline-functions (included in -O3) is enabled and --param max-inline-insns-recursive-auto is used. The default value is 450.
+
+ max-inline-recursive-depth
+ max-inline-recursive-depth-auto
+ Specifies maximum recursion depth used by the recursive inlining. For functions declared inline --param max-inline-recursive-depth is taken into acount. For function not declared inline, recursive inlining happens only when -finline-functions (included in -O3) is enabled and --param max-inline-recursive-depth-auto is used. The default value is 450.
+
+ inline-call-cost
+ Specify cost of call instruction relative to simple arithmetics operations (having cost of 1). Increasing this cost disqualify inlining of non-leaf functions and at same time increase size of leaf function that is believed to reduce function size by being inlined. In effect it increase amount of inlining for code having large abstraction penalty (many functions that just pass the arguments to other functions) and decrease inlining for code with low abstraction penalty. Default value is 16.
-finline-limit=n By default, GCC limits the size of functions that can be inlined. This flag allows the control of this limit for functions that are explicitly marked as inline (i.e., marked with the inline keyword or defined within the class definition in c++). n is the size of functions that can be inlined in number of pseudo instructions (not counting parameter handling). The default value of n is 600. Increasing this value can result in more inlined code at the cost of compilation time and memory consumption. Decreasing usually makes the compilation faster and less code will be inlined (which presumably means slower programs). This option is particularly useful for programs that use inlining heavily such as those based on recursive templates with C++.
+
+

Inlining is actually controlled by a number of parameters, which may be specified individually by using --param name=value. The -finline-limit=n option sets some of these parameters as follows:

+
+
+
+
max-inline-insns-single
+
is set to n/2.
+
+
max-inline-insns-auto
+
is set to n/2.
+
+
min-inline-insns
+
is set to 130 or n/4, whichever is smaller.
+
+
max-inline-insns-rtl
+
is set to n.
+
+
+
+

See below for a documentation of the individual parameters controlling inlining.

+

Note: pseudo instruction represents, in this particular context, an abstract measurement of function's size. In no way, it represents a count of assembly instructions and as such its exact meaning might change from one release to an another.

+

GCC additionally has the -Winline compiler warning, which emits a warning whenever a function declared as inline was not inlined.

+

Compilation-Level Inlining -- Metrowerks

+

Metrowerks has a number of pragmas (and corresponding compiler settings) to control inlining. These include always_inline, inline_depth, inline_max_size, and inline max_total_size.

+
+

#pragma always_inline on | off | reset

+

Controls the use of inlined functions. If you enable this pragma, the compiler ignores all inlining limits and attempts to inline all functions where it is legal to do so. This pragma is deprecated. Use the inline_depth pragma instead.
+
+ #pragma inline_depth(n)
+ #pragma inline_depth(smart)

+

Controls how many passes are used to expand inline function. Sets the number of passes used to expand inline function calls. The number n is an integer from 0 to 1024 or the smart specifier. It also represents the distance allowed in the call chain from the last function up. For example, if d is the total depth of a call chain, then functions below (d-n) are inlined if they do not exceed the inline_max_size and inline_max_total_size settings which are discussed directly below.
+
+ #pragma inline_max_size(n);
+ #pragma inline_max_total_size(n);

+

The first pragma sets the maximum function size to be considered for inlining; the second sets the maximum size to which a function is allowed to grow after the functions it calls are inlined. Here, n is the number of statements, operands, and operators in the function, which
+ turns out to be roughly twice the number of instructions generated by the function. However, this number can vary from function to function. For the inline_max_size pragma, the default value of n is 256; for the inline_max_total_size pragma, the default value of n is 10000. The smart specifier is the default mode, with four passes where the passes 2-4 are limited to small inline functions. All inlineable functions are expanded if inline_depth is set to 1-1024.

+
+

Function-Level Inlining -- VC++

+
+

To force inline usage under VC++, you use this:

+

    __forceinline void foo(){ ... }

+

It should be noted that __forceinline has no effect if the compiler is set to disable inlining. It merely tells the compiler that when inlining is enabled that it shouldn't use its judgment to decide if the function should be inlined but instead to always inline it.
+
+ To disable inline usage under VC++, you need to use this:

+

    #pragma inline_depth(0) // Disable inlining.
+     void foo() { ... }
+     #pragma inline_depth()  // Restore default.

+

The above is essentially specifying compiler-level inlining control within the code for a specific function.

+
+

Function-Level Inlining -- GCC / Metrowerks

+
+

To force inline usage under GCC 3.1+, you use this:

+

    inline void foo() __attribute__((always_inline)) { ... }
+        
or
+     inline __attribute__((always_inline)) void foo() { ... }

+

To disable inline usage under GCC 3+, you use this:

+

    void foo() __attribute__((noinline)) { ... }
+
        or
+     inline __attribute__((noinline)) void foo() { ... }

+

EABase has some wrappers for this, such as EA_FORCE_INLINE.

+
+

Perf.12 + C++ / EASTL seems to bloat my .obj files much more than C does. +

+

There is no need to worry. The way most C++ compilers compile templates, they compile all seen template code into the current .obj module, which results in larger .obj files and duplicated template code in multiple .obj files. However, the linker will (and in fact must) select only a single version of any given function for the application, and these linked functions will usually be located contiguously.

+

Additionally, the debug information for template definitions is usually larger than that for non-templated C++ definitions, which itself is sometimes larger than C definitions due to name decoration.

+

Perf.13 +What are the best compiler settings for EASTL?

+

We will discuss various aspects of this topic here. As of this writing, more EASTL research on this topic has been done on Microsoft compiler platforms (e.g. Win32) than GCC platforms. Thus currently this discussion focuses on VC++ optimization. Some of the concepts are applicable to GCC, though. EASTL has been successfully compiled and tested (the EASTL unit test) on our major development platforms with the highest optimization settings enabled, including GCC's infamous -O3 level.
+
+Optimization Topics

+
    +
  • Function inlining.
  • +
  • Optimization for speed vs. optimization for size.
  • +
  • Link-time code generation (LTCG).
  • +
  • Profile-guided optimization (PGO).
  • +
+

Function inlining
+ EASTL is a template library and inlining is important for optimal speed. Compilers have various options for enabling inlining and those options are discussed in this FAQ in detail. Most users will want to enable some form of inlining when compiling EASTL and other templated libraries. For users that are most concerned about the compiler's inlining increasing code size may want to try the 'inline only functions marked as inline' compiler option. Here is a table of normalized results from the benchmark project (Win32 platform):
+

+ + + + + + + + + + + + + + + + + + + + + +
Inlining DisabledInline only 'inline'Inline any
Application size100K86K86K
Execution time1007575
+


+The above execution times are highly simplified versions of the actual benchmark data but convey a sense of the general average behaviour that can be expected. In practice, simple functions such as vector::operator[] will execute much faster with inlining enabled but complex functions such as map::insert may execute no faster within inlining enabled.

+

Optimization for Speed / Size
+ Optimization for speed results in the compiler inlining more code than it would otherwise. This results in the inlined code executing faster than if it was not inlined. As mentioned above, basic function inlining can result in smaller code as well as faster code, but after a certain point highly inlined code becomes greater in size than less inlined code and the performance advantages of inlining start to lessen. The EASTL Benchmark project is a medium sized application that is about 80% templated and thus acts as a decent measure of the practical tradeoff between speed and size. Here is a table of normalized results from the benchmark project (Windows platform):
+

+ + + + + + + + + + + + + + + + + + + + + + + + +
SizeSpeedSpeed + LTCGSpeed + LTCG + PGO
Application size80K100K98K98K
Execution time100908375
+


+What the above table is saying is that if you are willing to have your EASTL code be 20% larger, it will be 10% faster. Note that it doesn't mean that your app will be 20% larger, only the templated code in it like EASTL will be 20% larger.

+

Link-time code generation (LTCG)
+ LTCG is a mechanism whereby the compiler compiles the application as if it was all in one big .cpp file instead of separate .cpp files that don't see each other. Enabling LTCG optimizations is done by simply setting some compiler and linker settings and results in slower link times. The benchmark results are presented above and for the EASTL Benchmark project show some worthwhile improvement.

+

Profile-guided optimization (PGO)
+ PGO is a mechanism whereby the compiler uses profiling information from one or more runs to optimize the compilation and linking of an application. Enabling PGO optimizations is done by setting some linker settings and doing some test runs of the application, then linking the app with the test run results. Doing PGO optimizations is a somewhat time-consuming task but the benchmark results above demonstrate that for the EASTL Benchmark project that PGO is worth the effort.

+

Problems

+

Prob.1 +I'm getting screwy behavior in sorting algorithms or sorted containers. What's wrong?

+

It may possible that you are seeing floating point roundoff problems. Many STL algorithms require object comparisons to act consistently. However, floating point values sometimes compare differently between uses because in one situation a value might be in 32 bit form in system memory, whereas in anther situation that value might be in an FPU register with a different precision. These are difficult problems to track down and aren't the fault of EASTL or whatever similar library you might be using. There are various solutions to the problem, but the important thing is to find a way to force the comparisons to be consistent.

+

The code below was an example of this happening, whereby the object pA->mPos was stored in system memory while pB->mPos was stored in a register and comparisons were inconsistent and a crash ensued.
+

+
class SortByDistance : public binary_function<WorldTreeObject*, WorldTreeObject*, bool>
+{
+private:
+    Vector3 mOrigin;
+
+public:
+    SortByDistance(Vector3 origin) {
+        mOrigin = origin;
+    }
+
+    bool operator()(WorldTreeObject* pA, WorldTreeObject* pB) const {
+         return ((WorldObject*)pA)->mPos - mOrigin).GetLength()
+              < ((WorldObject*)pB)->mPos - mOrigin).GetLength();
+    }
+};
+

Another thing to watch out for is the following mistake:
+

+
struct ValuePair
+{
+    uint32_t a;
+    uint32_t b;
+};
+
+// Improve speed by casting the struct to uint64_t
+bool operator<(const ValuePair& vp1, const ValuePair& vp2)
+    { return *(uint64_t*)&vp1 < *(uint64_t*)&vp2; }
+

The problem is that the ValuePair struct has 32 bit alignment but the comparison assumes 64 bit alignment. The code above has been observed to crash on the PowerPC 64-based machines. The resolution is to declare ValuePair as having 64 bit alignment.
+ +

+

Prob.2 I am getting compiler warnings (e.g. C4244, C4242 or C4267) that make no sense. Why?

+One cause of this occurs with VC++ when you have code compiled with the /Wp64 (detect 64 bit portability issues) option. This causes pointer types to have a hidden flag called __w64 attached to them by the compiler. So 'ptrdiff_t' is actually known by the compiler as '__w64 int', while 'int' is known by the compilers as simply 'int'. A problem occurs here when you use templates. For example, let's say we have this templated function +
template <typename T>
+T min(const T a, const T b) {
+    return b < a ? b : a;
+}
+If you compile this code: +
ptrdiff_t a = min(ptrdiff_t(0), ptrdiff_t(1));
+int       b = min((int)0, (int)1);
+You will get the following warning for the second line, which is somewhat nonsensical: +
warning C4244: 'initializing' : conversion from 'const ptrdiff_t' to 'int', possible loss of data
+

This could probably be considered a VC++ bug, but in the meantime you have little choice but to ignore the warning or disable it.

+ +

Prob.3 +I am getting compiler warning C4530, which complains about exception handling and "unwind semantics." What gives?

+

VC++ has a compiler option (/EHsc) that allows you to enable/disable exception handling stack unwinding but still enable try/catch. This is useful because it can save a lot in the way of code generation for your application. Disabling stack unwinding will decrease the size of your executable on at least the Win32 platform by 10-12%.
+
+If you have stack unwinding disabled, but you have try/catch statements, VC++ will generate the following warning:

+
warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
+

As of EASTL v1.0, this warning has been disabled within EASTL for EASTL code. However, non-EASTL code such as std STL code may still cause this warning to be triggered. In this case there is not much you can do about this other than to disable the warning.

+

Prob.4 + Why are tree-based EASTL containers hard to read with a +debugger?

+

Short answer
+
Maximum performance and design mandates.

+

Long answer
+You may notice that when you have a tree-based container (e.g. set, map)  in the debugger that it isn't automatically able to recognize the tree nodes as containing instances of your contained object. You can get the debugger to do what you want with casting statements in the debug watch window, but this is not an ideal solution. The reason this is happening is that node-based containers always use an anonymous node type as the base class for container nodes. This is primarily done for performance, as it allows the node manipulation code to exist as a single non-templated library of functions and it saves memory because containers will have one or two base nodes as container 'anchors' and you don't want to allocate a node of the size of the user data when you can just use a base node. See list.h for an example of this and some additional in-code documentation on this.

+

Additionally, EASTL has the design mandate that an empty container constructs no user objects. This is both for performance reasons and because it doing so would skew the user's tracking of object counts and might possibly break some expectation the user has about object lifetimes.

+

Currently this debug issue exists only with tree-based containers. Other node-based containers such as list and slist use a trick to get around this problem in debug builds.

+

See Debug.2 for more. +

Prob.5 +The EASTL source code is sometimes rather complicated looking. Why is that?

+

Short answer
+Maximum performance.

+

Long answer
+ EASTL uses templates, type_traits, iterator categories, redundancy reduction, and branch reduction in order to achieve optimal performance. A side effect of this is that there are sometimes a lot of template parameters and multiple levels of function calls due to template specialization. The ironic thing about this is that this makes the code (an optimized build, at least) go faster, not slower. In an optimized build the compiler will see through the calls and template parameters and generate a direct optimized inline version.

+

As an example of this, take a look at the implementation of the copy implementation in algorithm.h. If you are copying an array of scalar values or other trivially copyable values, the compiler will see how the code directs this to the memcpy function and will generate nothing but a memcpy in the final code. For non-memcpyable data types the compiler will automatically understand that in do the right thing.

+

EASTL's primary objective is maximal performance, and it has been deemed worthwhile to make the code a little less obvious in order to achieve this goal. Every case where EASTL does something in an indirect way is by design and usually this is for the purpose of achieving the highest possible performance.

+

Prob.6 +When I get compilation errors, they are very long and complicated looking. What do I do?

+

Assuming the bugs are all worked out of EASTL, these errors really do indicate that you have something wrong. EASTL is intentionally very strict about types, as it tries to minimize the chance of users errors. Unfortunately, there is no simple resolution to the problem of long compiler errors other than to deal with them. On the other hand, once you've dealt with them a few times, you tend to realize that most of time they are the same kinds of errors and

+

Top five approaches to dealing with long compilation errors:

+
    +
  1. Look at the line where the compilation error occurred and ignore the text of the error and just look at obvious things that might be wrong.
  2. +
  3. Consider the most common typical causes of templated compilation errors and consider if any of these might be your problem. Usually one of them are.
  4. +
  5. Either read through the error (it's not as hard as it may look on the surface) or copy the error to a text file and remove the extraneous
  6. +
  7. Compile the code under GCC instead of MSVC, as GCC warnings and errors tend to be more helpful than MSVC's. Possibly also consider compiling an isolated version under Comeau C++'s free online compiler at www.comeaucomputing.com or the Dinkumware online compiler at http://dinkumware.com/exam/. 
  8. +
  9. Try using an STL filter (http://www.bdsoft.com/tools/stlfilt.html) which automatically boils down template errors to simpler forms. We haven't tried this yet with EASTL. Also there is the more generic TextFilt (http://textfilt.sourceforge.net/).
  10. +
+

Top five causes of EASTL compilation errors:

+
    +
  1. const-correctness. Perhaps a quarter of container template errors are due to the user not specifying const correctly.
  2. +
  3. Missing hash function. hash_map, hash_set, etc. require that you either specify a hash function or one exists for your class. See functional.h for examples of declarations of hash functions for common data types.
  4. +
  5. Missing operators. Various containers and algorithms require that certain operators exist for your contained classes. For example, list requires that you can test contained objects for equivalence (i.e. operator==), while map requires that you can test contained objects for "less-ness" (operator <). If you define a Widget class and don't have a way to compare two Widgets, you will get errors when trying to put them into a map.
  6. +
  7. Specifying the wrong data type. For example, it is a common mistake to forget that when you insert into a map, you need to insert a pair of objects and not just your key or value type.
  8. +
  9. Incorrect template parameters. When declaring a template instantiation (e.g. map<int, int, less<int> >) you simply need to get the template parameters correct. Also note that when you have ">>" next to each other that you need to separate them by one space (e.g. "> >").
  10. +
+

Prob.7 + Templates sometimes seem to take a long time to compile. Why do I do about that? +

+

C++ compilers are generally slower than C compilers, and C++ templates are generally slower to compile than regular C++ code. EASTL has some extra functionality (such as type_traits and algorithm specializations) that is not found in most other template libraries and significantly improves performance and usefulness but adds to the amount of code that needs to be compiled. Ironically, we have a case where more source code generates faster and smaller object code.

+

The best solution to the problem is to use pre-compiled headers, which are available on all modern ~2002+) compilers, such as VC6.0+, GCC 3.2+, and Metrowerks 7.0+. In terms of platforms this means all 2002+ platforms.

+

Some users have been speeding up build times by creating project files that put all the source code in one large .cpp file. This has an effect similar to pre-compiled headers. It can go even faster than pre-compiled headers but has downsides in the way of convenience and portability.

+

Prob.8 +I get the compiler error: "template instantiation depth exceeds maximum of 17. use -ftemplate-depth-NN to increase the maximum". 

+

This is a GCC error that occurs when a templated function calls a templated function which calls a templated function, etc. past a depth of 17. You can use the GCC command line argument -ftemplate-depth-40 (or some other high number) to get around this. As note below, the syntax starting with GCC 4.5 has changed slightly.

+

The primary reason you would encounter this with EASTL is type traits that are used by algorithms. The type traits library is a (necessarily) highly templated set of types and functions which adds at most about nine levels of inlining. The copy and copy_backward algorithms have optimized pathways that add about four levels of inlining. If you have just a few more layers on top of that in container or user code then the default limit of 17 can be exceeded. We are investigating ways to reduce the template depth in the type traits library, but only so much can be done, as most compilers don't support type traits natively. Metrowerks is the current exception.

+

From the GCC documentation:

+
-ftemplate-depth-n
+
+Set the maximum instantiation depth for template classes to n. 
+A limit on the template instantiation depth is needed to detect 
+endless recursions during template class instantiation ANSI/ISO 
+C++ conforming programs must not rely on a maximum depth greater than 17.
+
+ +

Note that starting with GCC 4.5 the syntax is -ftemplate-depth=N instead of -ftemplate-depth-n.

+

Prob.9 + I'm getting errors about min and max while compiling.

+

You need to define NOMINMAX under VC++ when this occurs, as it otherwise defines min and max macros that interfere. There may be equivalent issues with other compilers. Also, VC++ has a specific <minmax.h> header file which defines min and max macros but which doesn't pay attention to NOMINMAX and so in that case there is nothing to do but not include that file or to undefine min and max. minmax.h is not a standard file and its min and max macros are not standard C or C++ macros or functions.

+

Prob.10 +C++ / EASTL seems to bloat my .obj files much more than C does.

+

There is no need to worry. The way most C++ compilers compile templates, they compile all +seen template code into the current .obj module, which results in larger .obj files and duplicated template code in +multiple .obj files. However, the linker will (and must) select only a single version of any given function for the +application, and these linked functions will usually be located contiguously.

+

Prob.11 + I'm getting compiler errors regarding placement operator new +being previously defined.

+

This can happen if you are attempting to define your own versions of placement new/delete. The C++ language standard does not allow the user to override these functions. Section 18.4.3 of the standard states:

+

     Placement forms
+     1. These functions are reserved, a C++ program may not define functions that displace the versions in the Standard C++ library.

+

You may find that #defining __PLACEMENT_NEW_INLINE seems to fix your problems under VC++, but it can fail under some circumstances and is not portable and fails with other compilers, which don't have an equivalent workaround.

+

Prob.12 +I'm getting errors related to wchar_t string  functions such as wcslen().

+

EASTL requires EABase-related items that the following be so. If not, then EASTL gets confused about what types it can pass to wchar_t related functions.

+
    +
  • The #define EA_WCHAR_SIZE is equal to sizeof(wchar_t).
  • +
  • If sizeof(wchar_t) == 2, then char16_t is typedef'd to wchar_t.
  • +
  • If sizeof(wchar_t) == 4, then char32_t is typedef'd to wchar_t.
  • +
+

EABase v2.08 and later automatically does this for most current generation and all next generation platforms. With GCC 2.x, the user may need to predefine EA_WCHAR_SIZE to the appropriate value, due to limitations with the GCC compiler. Note that GCC defaults to sizeof(wchar_t) ==4, but it can be changed to 2 with the -fshort_wchar compiler command line argument. If you are using EASTL without EABase, you will need to make sure the above items are correctly defined.

+

Prob.13 + I'm getting compiler warning C4619: there is no warning number Cxxxx +(e.g. C4217).

+

Compiler warning C4619 is a VC++ warning which is saying that the user is attempting to enable or disable a warning which the compiler doesn't recognize. This warning only occurs if the user has the compiler set to enable warnings that are normally disabled, regardless of the warning level. The problem, however, is that there is no easy way for user code to tell what compiler warnings any given compiler version will recognize. That's why Microsoft normally disables this warning.

+

The only practical solution we have for this is for the user to disable warning 4619 globally or an a case-by-case basis. EA build systems such as nant/framework 2's eaconfig will usually disable 4619. In general, global enabling of 'warnings that are disabled by default' often result in quandrys such as this.

+

Prob.14 +My stack-based fixed_vector is not respecting the object alignment requirements.

+

EASTL fixed_* containers rely on the compiler-supplied alignment directives, such as that implemented by EA_PREFIX_ALIGN. This is normally a good thing because it allows the memory to be local with the container. However, as documented by Microsoft at http://msdn2.microsoft.com/en-us/library/83ythb65(VS.71).aspx, this doesn't work for stack variables. The two primary means of working around this are:

+
    +
  • Use something like AlignedObject<> from the EAStdC package's EAAllocator.h file.
  • +
  • Use eastl::vector with a custom allocator and have it provide aligned memory. EASTL automatically recognizes that the objects are aligned and will call the aligned version of your allocator allocate() function. You can get this aligned memory from the stack, if you need it, somewhat like how AlignedObject<> works.
  • +
+

Prob.15 I am getting compiler errors when using GCC under XCode (Macintosh/iphone).

+

The XCode environment has a compiler option which causes it to evaluate include directories recursively. So if you specify /a/b/c as an include directory, it will consider all directories underneath c to also be include directories. This option is enabled by default, though many XCode users disable it, as it is a somewhat dangerous option. The result of enabling this option with EASTL is that <EASTL/string.h> is used by the compiler when you say #include <string.h>. The solution is to disable this compiler option. It's probably a good idea to disable this option anyway, as it typically causes problems for users yet provides minimal benefits.

+

Prob.16 I am getting linker errors about Vsnprintf8 or Vsnprintf16.

+

EASTL requires the user to provide a function called Vsnprintf8 if the string::sprintf function is used. vsnprintf is not a standard C function, but most C standard libraries provide some form of it, though in some ways their implementations differ, especially in what the return value means. Also, most implementations of vsnprintf are slow, mostly due to mutexes related to locale functionality. And you can't really use vendor vsnprintf on an SPU due to the heavy standard library size. EASTL is stuck because it doesn't want to depend on something with these problems. EAStdC provides a single consistent fast lightweight, yet standards-conforming, implementation in the form of Vsnprintf(char8_t*, ...), but EASTL can't have a dependency on EAStdC. So the user must provide an implementation, even if all it does is call EAStdC's Vsnprintf or the vendor vsnprintf for that matter.

+

Example of providing Vsnprintf8 via EAStdC:

+
#include <EAStdC/EASprintf.h>
+   
+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);
+}
+

Example of providing Vsnprintf8 via C libraries:

+
#include <stdio.h>
+   
+int Vsnprintf8(char8_t* p, size_t n, const char8_t* pFormat, va_list arguments)
+{
+    #ifdef _MSC_VER
+        return vsnprintf_s(p, n, _TRUNCATE, pFormat, arguments);
+    #else
+        return vsnprintf(p, n, pFormat, arguments);
+    #endif
+}
+
+int Vsnprintf16(char16_t* p, size_t n, const char16_t* pFormat, va_list arguments)
+{
+    #ifdef _MSC_VER
+        return vsnwprintf_s(p, n, _TRUNCATE, pFormat, arguments);
+    #else
+        return vsnwprintf(p, n, pFormat, arguments); // Won't work on Unix because its libraries implement wchar_t as int32_t.
+    #endif
+}
+

Prob.17 I am getting compiler errors about UINT64_C or UINT32_C.

+

This is usually an order-of-include problem that comes about due to the implementation of __STDC_CONSTANT_MACROS in C++ Standard libraries. The C++ <stdint.h> header file defineds UINT64_C only if __STDC_CONSTANT_MACROS has been defined by the user or the build system; the compiler doesn't automatically define it. The failure you are seeing occurs because user code is #including a system header before #including EABase and without defining __STDC_CONSTANT_MACROS itself or globally. EABase defines __STDC_CONSTANT_MACROS and #includes the appropriate system header. But if the system header was already previously #included and __STDC_CONSTANT_MACROS was not defined, then UINT64_C doesn't get defined by anybody.

+

The real solution that the C++ compiler and standard library wants is for the app to globally define __STDC_CONSTANT_MACROS itself in the build.

+

Prob.18 I am getting a crash with a global EASTL container.

+

This usually due to compiler's lack of support for global (and static) C++ class instances. The crash is happening because the global variable exists but its constructor was not called on application startup and it's member data is zeroed bytes. To handle this you need to manually initialize such variables. There are two primary ways:

+

Failing code:

+
eastl::list<int> gIntList; // Global variable.
+   
+void DoSomething()
+{
+    gIntList.push_back(1); // Crash. gIntList was never constructed.
+}
+

Declaring a pointer solution:

+
eastl::list<int>* gIntList = NULL;
+   
+void DoSomething()
+{
+    if(!gIntList) // Or move this to an init function.
+        gIntList = new eastl::list<int>;
+
+    gIntList->push_back(1); // Success
+}
+

Manual constructor call solution:

+
eastl::list<int> gIntList;
+   
+void InitSystem()
+{
+    new(&gIntList) eastl::list<int>;
+}
+
+void DoSomething()
+{
+    gIntList.push_back(1); // Success
+}
+

Prob.19 Why doesn't EASTL support passing NULL string functions?

+

+

The primary argument is to make functions safer for use. Why crash on NULL pointer access when you can make the code safe? That's a good argument. The counter argument, which EASTL currently makes, is:

+
    +
  • It breaks consistency with the C++ STL library and C libraries, which require strings to be valid.
  • +
  • It makes the coder slower and bigger for all users, though few need NULL checks.
  • +
  • The specification for how to handle NULL is simple for some cases but not simple for others. Operator < below a case where the proper handling of it in a consistent way is not simple, as all comparison code (<, >, ==, !=, >=, <=) in EASTL must universally and consistently handle the case where either or both sides are NULL. A NULL string seems similar to an empty string, but doesn't always work out so simply.
  • +
  • What about other invalid string pointers? NULL is merely one invalid value of many, with its only distinction being that sometimes it's intentionally NULL (as opposed to being NULL due to not being initialized).
  • +
  • How and where to implement the NULL checks in such a way as to do it efficiently is not always simple, given that public functions call public functions.
  • +
  • It's arguable (and in fact the intent of the C++ standard library) that using pointers that are NULL is a user/app mistake. If we really want to be safe then we should be using string objects for everything. You may not entirely buy this argument in practice, but on the other hand one might ask why is the caller of EASTL using a NULL pointer in the first place? The answer of course is that somebody gave it to him.
  • +
+

Debug

+

Debug.1 +How do I set the VC++ debugger to display EASTL container data with tooltips?

+

See Cont.9

+

Debug.2 +How do I view containers if the visualizer/tooltip support is not present?

+ +

Here is a table of answers about how to manually inspect containers in the debugger.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 ContainerApproach
slist
+ fixed_slist
slist is a singly-linked list. Look at the slist mNode variable. You can walk the list by looking at mNode.mpNext, etc.
list
+ fixed_list
list is a doubly-linked list. Look at the list mNode variable. You can walk the list forward by looking at mNode.mpNext, etc. and backward by looking at mpPrev, etc.
intrusive_list
+ intrusive_slist
Look at the list mAnchor node. This lets you walk forward and backward in the list via mpNext and mpPrev.
arrayView the array mValue member in the debugger. It's simply a C style array.
vector
+ fixed_vector
View the vector mpBegin value in the debugger. If the string is long, use ", N" to limit the view length, as with someVector.mpBegin, 32
vector_set
+ vector_multiset
+ vector_map
+ vector_multimap
These are containers that are implemented as a sorted vector, deque, or array. They are searched via a standard binary search. You can view them the same way you view a vector or deque.
deque
deque is implemented as an array of arrays, where the arrays implement successive equally-sized segments of the deque. The mItBegin deque member points the deque begin() position.
bitvectorLook at the bitvector mContainer variable. If it's a vector, then see vector above.
bitsetLook at the bitset mWord variable. The bitset is nothing but one or more uint32_t mWord items.
set
+ multiset
+ fixed_set
+ fixed_multiset
The set containers are implemented as a tree of elements. The set mAnchor.mpNodeParent points to the top of the tree; the mAnchor.mpNodeLeft points to the far left node of the tree (set begin()); the mAnchor.mpNodeRight points to the right of the tree (set end()).
map
+ multimap
+ fixed_map
+ fixed_multimap
The map containers are implemented as a tree of pairs, where pair.first is the map key and pair.second is the map value. The map mAnchor.mpNodeParent points to the top of the tree; the mAnchor.mpNodeLeft points to the far left node of the tree (map begin()); the mAnchor.mpNodeRight points to the right of the tree (map end()).
hash_map
+ hash_multimap
+ fixed_hash_map
+ fixed_hash_multimap
hash tables in EASTL are implemented as an array of singly-linked lists. The array is the mpBucketArray member. Each element in the list is a pair, where the first element of the pair is the map key and the second is the map value.
intrusive_hash_map
+ intrusive_hash_multimap
+ intrusive_hash_set
+ intrusive_hash_multiset
intrusive hash tables in EASTL are implemented very similarly to regular hash tables. See the hash_map and hash_set entries for more info.
hash_set
+ hash_multiset
+ fixed_hash_set
+ fixed_hash_map
hash tables in EASTL are implemented as an array of singly-linked lists. The array is the mpBucketArray member.
basic_string
+ fixed_string
+ fixed_substring
View the string mpBegin value in the debugger. If the string is long, use ", N" to limit the view length, as with someString.mpBegin, 32
heap
A heap is an array of data (e.g. EASTL vector) which is organized in a tree whereby the highest priority item is array[0], The next two highest priority items are array[1] and [2]. Underneath [1] in priority are items [3] and [4], and underneath item [2] in priority are items [5] and [6]. etc.
stack
View the stack member c value in the debugger. That member will typically be a list or deque.
queue
View the queue member c value in the debugger. That member will typically be a list or deque.
priority_queue
View the priority_queue member c value in the debugger. That member will typically be a vector or deque which is organized as a heap. See the heap section above for how to view a heap.
smart_ptrView the mpValue member.
+
+

Debug.3 +The EASTL source code is sometimes rather complicated looking. Why is that?

+

Short answer
+Maximum performance.

+

Long answer
+ EASTL uses templates, type_traits, iterator categories, redundancy reduction, and branch reduction in order to achieve optimal performance. A side effect of this is that there are sometimes a lot of template parameters and multiple levels of function calls due to template specialization. The ironic thing about this is that this makes the code (an optimized build, at least) go faster, not slower. In an optimized build the compiler will see through the calls and template parameters and generate a direct optimized inline version.

+

As an example of this, take a look at the implementation of the copy implementation in algorithm.h. If you are copying an array of scalar values or other trivially copyable values, the compiler will see how the code directs this to the memcpy function and will generate nothing but a memcpy in the final code. For non-memcpyable data types the compiler will automatically understand that in do the right thing.

+

EASTL's primary objective is maximal performance, and it has been deemed worthwhile to make the code a little less obvious in order to achieve this goal. Every case where EASTL does something in an indirect way is by design and usually this is for the purpose of achieving the highest possible performance.

+

Debug.4 +When I get compilation errors, they are very long and complicated looking. What do I do?

+

Assuming the bugs are all worked out of EASTL, these errors really do indicate that you have something wrong. EASTL is intentionally very strict about types, as it tries to minimize the chance of users errors. Unfortunately, there is no simple resolution to the problem of long compiler errors other than to deal with them. On the other hand, once you've dealt with them a few times, you tend to realize that most of time they are the same kinds of errors and
+
+Top five approaches to dealing with long compilation errors:

+
    +
  1. Look at the line where the compilation error occurred and ignore the text of the error and just look at obvious things that might be wrong.
  2. +
  3. Consider the most common typical causes of templated compilation errors and consider if any of these might be your problem. Usually one of them are.
  4. +
  5. Either read through the error (it's not as hard as it may look on the surface) or copy the error to a text file and remove the extraneous
  6. +
  7. Compile the code under GCC instead of MSVC, as GCC warnings and errors tend to be more helpful than MSVC's. Possibly also consider compiling an isolated version under Comeau C++'s free online compiler at www.comeaucomputing.com or the Dinkumware online compiler at http://dinkumware.com/exam/. 
  8. +
  9. Try using an STL filter (http://www.bdsoft.com/tools/stlfilt.html) which automatically boils down template errors to simpler forms. We haven't tried this yet with EASTL. Also there is the more generic TextFilt (http://textfilt.sourceforge.net/).
  10. +
+

Top five causes of EASTL compilation errors:

+
    +
  1. const-correctness. Perhaps a quarter of container template errors are due to the user not specifying const correctly.
  2. +
  3. Missing hash function. hash_map, hash_set, etc. require that you either specify a hash function or one exists for your class. See functional.h for examples of declarations of hash functions for common data types.
  4. +
  5. Missing operators. Various containers and algorithms require that certain operators exist for your contained classes. For example, list requires that you can test contained objects for equivalence (i.e. operator==), while map requires that you can test contained objects for "less-ness" (operator <). If you define a Widget class and don't have a way to compare two Widgets, you will get errors when trying to put them into a map.
  6. +
  7. Specifying the wrong data type. For example, it is a common mistake to forget that when you insert into a map, you need to insert a pair of objects and not just your key or value type.
  8. +
  9. Incorrect template parameters. When declaring a template instantiation (e.g. map<int, int, less<int> >) you simply need to get the template parameters correct. Also note that when you have ">>" next to each other that you need to separate them by one space (e.g. "> >").
  10. +
+

Debug.5 +How do I measure hash table balancing?

+

The following functionality lets you spelunk hash container layout.

+
    +
  • There is the load_factor function which tells you the overall hashtable load, but doesn't tell you if a load is unevenly distributed.
  • +
  • You can control the load factor and thus the automated bucket redistribution with set_load_factor.
  • +
  • The local_iterator begin(size_type n) and local_iterator end(size_type) functions lets you iterate each bucket individually. You can use this to examine the elements in a bucket.
  • +
  • You can use the above to get the size of any bucket, but there is also simply the bucket_size(size_type n) function.
  • +
  • The bucket_count function tells you the count of buckets. So with this you can completely visualize the layout of the hash table.
  • +
  • There is also iterator find_by_hash(hash_code_t c), for what it's worth.
  • +
+

The following function draws an ASCII bar graph of the hash table for easy visualization of bucket distribution:

+
+

#include <EASTL/hash_map.h>
+ #include <EASTL/algorithm.h>
+ #include <stdio.h>
+
+ template <typename HashTable>
+ void VisualizeHashTableBuckets(const HashTable& h)
+ {
+    eastl_size_t bucketCount       = h.bucket_count();
+    eastl_size_t largestBucketSize = 0;
+
+    for(eastl_size_t i = 0; i < bucketCount; i++)
+        largestBucketSize = eastl::max_alt(largestBucketSize, h.bucket_size(i));
+
+    YourPrintFunction("\n --------------------------------------------------------------------------------\n");
+
+    for(eastl_size_t i = 0; i < bucketCount; i++)
+    {
+        const eastl_size_t k = h.bucket_size(i) * 80 / largestBucketSize;
+
+        char buffer[16];
+        sprintf(buffer, "%3u|", (unsigned)i);
+        YourPrintFunction(buffer);
+
+        for(eastl_size_t j = 0; j < k; j++)
+            YourPrintFunction("*");
+
+        YourPrintFunction("\n");
+    }
+
+    YourPrintFunction(" --------------------------------------------------------------------------------\n");
+ }

+
+

This results in a graph that looks like the following (with one horizontal bar per bucket). This hashtable has a large number of collisions in each of its 10 buckets. +

+

   ------------------------------------------------------
+ 0|********************************************
+ 1|************************************************
+ 2|***************************************
+ 3|********************************************
+ 4|*****************************************************
+ 5|*************************************************
+ 6|****************************************
+ 7|***********************************************
+ 8|********************************************
+ 9|**************************************
+ 10|********************************************
+   -----------------------------------------------------
+

+

Containers

+

Cont.1 +Why do some containers have "fixed" versions (e.g. fixed_list) but others(e.g. deque) don't have fixed versions?

+

Recall that fixed containers are those that are implemented via a single contiguous block of memory and don't use a general purpose heap to allocate memory from. For example, fixed_list is a list container that implements its list by a user-configurable fixed block of memory. Such containers have an upper limit to how many items they can hold, but have the advantage of being more efficient with memory use and memory access coherency.

+

The reason why some containers don't have fixed versions is that such functionality doesn't make sense with these containers. Containers which don't have fixed versions include:

+
array, deque, bitset, stack, queue, priority_queue,
+intrusive_list, intrusive_hash_map, intrusive_hash_set,
+intrusive_hash_multimap, intrusive_hash_multimap,
+vector_map, vector_multimap, vector_set, vector_multiset.
+

Some of these containers are adapters which wrap other containers and thus there is no need for a fixed version because you can just wrap a fixed container. In the case of intrusive containers, the user is doing the allocation and so there are no memory allocations. In the case of array, the container is a primitive type which doesn't allocate memory. In the case of deque, it's primary purpose for being is to dynamically resize and thus the user would likely be better of using a fixed_vector.

+

Cont.2 +Can I mix EASTL with standard C++ STL?

+

This is possible to some degree, though the extent depends on the implementation of C++ STL. One of things that makes interoperability is something called iterator categories. Containers and algorithms recognize iterator types via their category and STL iterator categories are not recognized by EASTL and vice versa.

+

Things that you definitely can do:

+
    +
  • #include both EASTL and standard STL headers from the same .cpp file.
  • +
  • Use EASTL containers to hold STL containers.
  • +
  • Construct an STL reverse_iterator from an EASTL iterator.
  • +
  • Construct an EASTL reverse_iterator from an STL iterator.
  • +
+

Things that you probably will be able to do, though a given std STL implementation may prevent it: +

+
    +
  • Use STL containers in EASTL algorithms.
  • +
  • Use EASTL containers in STL algorithms.
  • +
  • Construct or assign to an STL container via iterators into an EASTL container.
  • +
  • Construct or assign to an EASTL container via iterators into an STL container.
  • +
+

Things that you would be able to do if the given std STL implementation is bug-free: +

+
    +
  • Use STL containers to hold EASTL containers. Unfortunately, VC7.x STL has a confirmed bug that prevents this. Similarly, STLPort versions prior to v5 have a similar but.
  • +
+

Things that you definitely can't do: +

+
    +
  • Use an STL allocator directly with an EASTL container (though you can use one indirectly).
  • +
  • Use an EASTL allocator directly with an STL container (though you can use one indirectly).
  • +
+

Cont.3 +Why are there so many containers?

+

EASTL has a large number of container types (e.g vector, list, set) and often has a number of variations of given types (list, slist, intrusive_list, fixed_list). The reason for this is that each container is tuned and to a specific need and there is no single container that works for all needs. The more the user is concerned about squeezing the most performance out of their system, the more the individual container variations become significant. It's important to note that having additional container types generally does not mean generating additional code or code bloat. Templates result in generated code regardless of what templated class they come from, and so for the most part you get optimal performance by choosing the optimal container for your needs.

+

Cont.4 +Don't STL and EASTL containers fragment memory?

+

They only fragment memory if you use them in a way that does so. This is no different from any other type of container used in a dynamic way. There are various solutions to this problem, and EASTL provides additional help as well:

+
    +
  • For vectors, use the reserve function (or the equivalent constructor) to set aside a block of memory for the container. The container will not reallocate memory unless you try grow beyond the capacity you reserve.
  • +
  • EASTL has "fixed" variations of containers which allow you to specify a fixed block of memory which the container uses for its memory. The container will not allocate any memory with these types of containers and all memory will be cache-friendly due to its locality.
  • +
  • You can assign custom allocators to containers instead of using the default global allocator. You would typically use an allocator that has its own private pool of memory.
  • +
  • Where possible, add all a container's elements to it at once up front instead of adding them over time. This avoids memory fragmentation and increase cache coherency.
  • +
+

Cont.5 + I don't see container optimizations for equivalent scalar types such +as pointer types. Why?

+

Metrowerks (and no other, as of this writing) STL has some container specializations for type T* which maps them to type void*. The idea is that a user who declares a list of Widget* and a list of Gadget* will generate only one container: a list of void*. As a result, code generation will be smaller. Often this is done only in optimized builds, as such containers are harder to view in debug builds due to type information being lost.
+
+The addition of this optimization is under consideration for EASTL, though it might be noted that optimizing compilers such as VC++ are already capable of recognizing duplicate generated code and folding it automatically as part of link-time code generation (LTCG) (a.k.a. "whole program optimization"). This has been verified with VC++, as the following code and resulting disassembly demonstrate:

+
eastl::list<int*>        intPtrList;
+eastl::list<TestObject*> toPtrList;
+
+eastl_size_t n1 = intPtrList.size();
+eastl_size_t n2 = toPtrList.size();
+
+0042D288  lea         edx,[esp+14h]
+0042D28C  call        eastl::list<TestObject>::size (414180h)
+0042D291  push        eax 
+0042D292  lea         edx,[esp+24h]
+0042D296  call        eastl::list<TestObject>::size (414180h)
+Note that in the above case the compiler folded the two implementations of size() into a single implementation.
+

Cont.6 +What about alternative container and algorithm implementations (e.g. treaps, skip lists, avl trees)?

+

EASTL chooses to implement some alternative containers and algorithms and not others. It's a matter of whether or not the alternative provides truly complementary or improved functionality over existing containers. The following is a list of some implemented and non-implemented alternatives and the rationale behind each:

+

Implemented:

+
    +
  • intrusive_list, etc. -- Saves memory and improves cache locality.
  • +
  • vector_map, etc. -- Saves memory and improves cache locality.
  • +
  • ring_buffer -- Useful for some types of operations and has no alternative.
  • +
  • shell_sort -- Useful sorting algorithm.
  • +
  • sparse_matrix -- Useful for some types of operations and has no alternative.
  • +
+

Not implemented: +

+
    +
  • skip lists (alternative to red-black tree) -- These use more memory and usually perform worse than rbtrees.
  • +
  • treap (alternative to red-black tree) -- These are easier and smaller than rbtrees, but perform worse.
  • +
  • avl tree (alternative to red-black tree) -- These have slightly better search performance than rbtrees, but significantly worse insert/remove performance.
  • +
  • btree (alternative to red-black tree) --  These are no better than rbtrees.
  • +
+

If you have an idea of something that should be implemented, please suggest it or even provide at least a prototypical implementation.

+

Cont.7 +Why are tree-based EASTL containers hard to read with a debugger?

+

Short answer
+
Maximum performance and design mandates.

+

Long answer
+You may notice that when you have a tree-based container (e.g. set, map)  in the debugger that it isn't automatically able to recognize the tree nodes as containing instances of your contained object. You can get the debugger to do what you want with casting statements in the debug watch window, but this is not an ideal solution. The reason this is happening is that node-based containers always use an anonymous node type as the base class for container nodes. This is primarily done for performance, as it allows the node manipulation code to exist as a single non-templated library of functions and it saves memory because containers will have one or two base nodes as container 'anchors' and you don't want to allocate a node of the size of the user data when you can just use a base node. See list.h for an example of this and some additional in-code documentation on this.

+

Additionally, EASTL has the design mandate that an empty container constructs no user objects. This is both for performance reasons and because it doing so would skew the user's tracking of object counts and might possibly break some expectation the user has about object lifetimes.

+

Currently this debug issue exists only with tree-based containers. Other node-based containers such as list and slist use a trick to get around this problem in debug builds.

+

Cont.8 +How do I assign a custom allocator to an EASTL container?

+

There are two ways of doing this:

+
    +
  1. Use the set_allocator function that is present in each container.
  2. +
  3. Specify a new allocator type via the Allocator template parameter that is present in each container.
  4. +
+

For item #1, EASTL expects that you provide an instance of an allocator of the type that EASTL recognizes. This is simple but has the disadvantage that all such allocators must be of the same class. The class would need to have C++ virtual functions in order to allow a given instance to act differently from another instance.

+

For item #2, you specify that the container use your own allocator class. The advantage of this is that your class can be implemented any way you want and doesn't require virtual functions for differentiation from other instances. Due to the way C++ works your class would necessarily have to use the same member function names as the default allocator class type. In order to make things easier, we provide a skeleton allocator here which you can copy and fill in with your own implementation.

+
class custom_allocator
+{
+public:
+    custom_allocator(const char* pName = EASTL_NAME_VAL("custom allocator"))
+    {
+        #if EASTL_NAME_ENABLED
+            mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
+        #endif
+
+        // Possibly do something here.
+    }
+
+    custom_allocator(const allocator& x, const char* pName = EASTL_NAME_VAL("custom allocator"));
+    {
+        #if EASTL_NAME_ENABLED
+            mpName = pName ? pName : EASTL_ALLOCATOR_DEFAULT_NAME;
+        #endif
+
+        // Possibly copy from x here.
+    }
+
+    ~custom_allocator();
+    {
+        // Possibly do something here.
+    }
+
+    custom_allocator& operator=(const custom_allocator& x)
+    {
+        // Possibly copy from x here.
+        return *this;
+    }
+
+    void* allocate(size_t n, int flags = 0)
+    {
+        // Implement the allocation here.
+    }
+
+    void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0)
+    {
+        // Implement the allocation here.
+    }
+
+    void deallocate(void* p, size_t n)
+    {
+        // Implement the deallocation here.
+    }
+
+    const char* get_name() const
+    {
+        #if EASTL_NAME_ENABLED
+            return mpName;
+        #else
+            return "custom allocator";
+        #endif
+    }
+
+    void set_name(const char* pName)
+    {
+        #if EASTL_NAME_ENABLED
+            mpName = pName;
+        #endif
+    }
+
+protected:
+    // Possibly place instance data here.
+
+    #if EASTL_NAME_ENABLED
+        const char* mpName; // Debug name, used to track memory.
+    #endif
+};
+
+
+inline bool operator==(const allocator& a, const allocator& b)
+{
+    // Provide a comparison here.
+}
+
+inline bool operator!=(const allocator& a, const allocator& b)
+{
+    // Provide a negative comparison here.
+}
+

Here's an example of how to use the above custom allocator:

+
// Declare a Widget list and have it default construct.
+list<Widget, custom_allocator> widgetList;
+
+// Declare a Widget list and have it construct with a copy of some global allocator.
+list<Widget, custom_allocator> widgetList2(gSomeGlobalAllocator);
+
+// Declare a Widget list and have it default construct, but assign
+// an underlying implementation after construction.
+list<Widget, custom_allocator> widgetList;
+widgetList.get_allocator().mpIAllocator = new WidgetAllocatorImpl;
+ +

Cont.9 How do I set the VC++ debugger to display EASTL container data with tooltips?

+

Visual Studio supports this via the AutoExp.dat file, an example of which is present with this documentation.

+

Sometimes the AutoExp.dat doesn't seem to work. Avery Lee's explanation:

+
+

If I had to take a guess, the problem is most likely in the cast to the concrete node type. These are always tricky because, for some strange reason, the debugger is whitespace sensitive with regard to specifying template types. You might try manually checking one of the routines of the specific map instantiation and checking that the placement of whitespace and const within the template expression still matches exactly. In some cases the compiler uses different whitespace rules depending on the value type which makes it impossible to correctly specify a single visualizer – this was the case for eastl::list<>, for which I was forced to include sections for both cases. The downside is that you have a bunch of (error) entries either way.

+
+

Cont.10 +How do I use a memory pool with a container?

+

Using custom memory pools is a common technique for decreasing memory fragmentation and increasing memory cache locality. EASTL gives you the flexibility of defining your own memory pool systems for containers. There are two primary ways of doing this:

+
    +
  • Assign a custom allocator to a container. eastl::fixed_pool provides an implementation.
  • +
  • Use one of the EASTL fixed containers, such as fixed_list.
  • +
+

Custom Allocator
+In the custom allocator case, you will want to create a memory pool and assign it to the container. For purely node-based containers such as list, slist, map, set, multimap, and multiset, your pool simply needs to be able to allocate list nodes. Each of these containers has a member typedef called node_type which defines the type of node allocated by the container. 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<Widget, MemoryPool> WidgetList;           // Declare your WidgetList type.
+
+MemoryPool myPool(sizeof(WidgetList::node_type), 100); // Make a pool of 100 Widget nodes.
+WidgetList myList(&myPool);                            // Create a list that uses the pool.
+

In the case of containers that are array-based, such as vector and basic_string, memory pools don't work very well as these containers work on a realloc-basis instead of by adding incremental nodes. What we want to do with these containers is assign a sufficient block of memory to them and reserve() the container's capacity to the size of the memory.

+

In the case of mixed containers which are partly array-based and partly node-based, such as hash containers and deque, you can use a memory pool for the nodes but will need a single array block to supply for the buckets (hash containers and deque both use a bucket-like system).

+

You might consider using eastl::fixed_pool as such an allocator, as it provides such functionality and allows the user to provide the actual memory used for the pool. Here is some example code:

+
char buffer[256];
+
+list<Widget, fixed_pool> myList;
+myList.get_allocator().init(buffer, 256);
+

Fixed Container
+In the fixed container case, the container does all the work for you. To use a list which implements a private pool of memory, just declare it like so:

+
fixed_list<Widget, 100> fixedList; // Declare a fixed_list that can hold 100 Widgets
+

Cont.11 +How do I write a comparison (operator<()) for a struct that contains two or more members? 

+

See Algo.2

+

Cont.12 +Why doesn't container X have member function Y?

+

Why don't the list or vector containers have a find() function? Why doesn't the vector container have a sort() function? Why doesn't the string container have a mid() function? These are common examples of such questions.

+

The answer usually boils down to two reasons:

+
    +
  • The functionality exists in a more centralized location elsewhere, such as the algorithms.
  • +
  • The functionality can be had by using other member functions.
  • +
+

In the case of find and sort functions not being part of containers, the find algorithm and sort algorithm are centralized versions that apply to any container. Additionally, the algorithms allow you to specify a sub-range of the container on which to apply the algorithm. So in order to find an element in a list, you would do this:
+

+
list<int>::iterator i = find(list.begin(), list.end(), 3);
+

And in order to sort a vector, you would do this:
+

+
quick_sort(v.begin(), v.end());   // Sort the entire array. +
+quick_sort(&v[3], &v[8]);         // Sort the items at the indexes in the range of [3, 8).
+

In the case of functionality that can be had by using other member functions, +note that EASTL follows the philosophy that duplicated functionality should not exist in a container, +with exceptions being made for cases where mistakes and unsafe practices commonly happen if the given +function isn't present. In the case of string not having a mid function, this is because there is a +string constructor that takes a sub-range of another string. So to make a string out of the middle of +another, you would do this:

+
string strMid(str, 3, 5); // Make a new string of the characters from the source range of [3, 3+5).
+

It might be noted that the EASTL string class is unique among EASTL containers in that it sometimes violates the minimum functionality rule. This is so because the std C++ string class similarly does so and EASTL aims to be compatible.

+

Cont.13 +How do I search a hash_map of strings via a char pointer efficiently? If I use map.find("hello") it creates a temporary string, which is inefficient.

+

The problem is illustrated with this example:

+
map<string, Widget> swMap;
+  ...
+map<string, Widget>::iterator it = swMap.find("blue"); // A temporary string object is created here.
+

In this example, the find function expects a string object and not a string literal and so (silently!) creates a temporary string object for the duration of the find. There are two solutions to this problem: +

+
    +
  • Make the map a map of char pointers instead of string objects. Don't forget to write a custom compare or else the default comparison function will compare pointer values instead of string contents.
  • +
  • Use the EASTL hash_map::find_as function, which allows you to find an item in a hash container via an alternative key than the one the hash table uses.
  • +
+

Cont.14 +Why are set and hash_set iterators const (i.e. const_iterator)?

+

The situation is illustrated with this example:

+
set<int> intSet;
+
+intSet.insert(1);
+set<int>::iterator i = intSet.begin();
+*i = 2; // Error: iterator i is const.
+

In this example, the iterator is a regular iterator and not a const_iterator, yet the compiler gives an error when trying to change the iterator value. The reason this is so is that a set is an ordered container and changing the value would make it 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.

+ +

Cont.15 +How do I prevent my hash container from re-hashing?

+

If you want to make a hashtable never re-hash (i.e. increase/reallocate its bucket count), +call set_max_load_factor with a very high value such as 100000.f.

+

Similarly, you can control the bucket growth factor with the rehash_policy function. +By default, when buckets reallocate, they reallocate to about twice their previous count. +You can control that value as with the example code here:

+
hash_set<int> hashSet;
+hashSet.rehash_policy().mfGrowthFactor = 1.5f
+ +

+ Cont.16 +Which uses less memory, a map or a hash_map? +

+

A hash_map will virtually always use less memory. A hash_map will use an average of two pointers per stored element, while a map uses three pointers per stored element.

+

Cont.17 +How do I write a custom hash function?

+

You can look at the existing hash functions in functional.h, but we provide a couple examples here.

+

To write a specific hash function for a Widget class, you would do this:

+
struct WidgetHash {
+    size_t operator()(const Widget& w) const
+        { return w.id; }
+};
+
+hash_set<Widget, WidgetHash> widgetHashSet;
+

To write a generic (templated) hash function for a set of similar classes (in this case that have an id member), you would do this:
+

+
template <typename T>
+struct GeneralHash {
+    size_t operator()(const T& t) const
+        { return t.id; }
+};
+
+hash_set<Widget, GeneralHash<Widget> > widgetHashSet;
+hash_set<Dogget, GeneralHash<Dogget> > doggetHashSet;
+ +

Cont.18 +How do I write a custom compare function for a map or set?

+

The sorted containers require that an operator< exist for the stored values or that the user provide a suitable custom comparison function. A custom can be implemented like so:
+

+
struct WidgetLess { +    bool operator()(const Widget& w1, const Widget& w2) const +        { return w.id < w2.id; } +}; + +set<Widget, WidgetLess> wSet;
+

It's important that your comparison function must be consistent in its behaviour, else the container will either be unsorted or a crash will occur. This concept is called "strict weak ordering."

+

Cont.19 +How do I force my vector or string capacity down to the size of the container?

+

You can simply use the set_capacity() member function which is present in both vector and string. This is a function that is not present in std STL vector and string functions.

+
eastl::vector<Widget> x;
+x.set_capacity();   // Shrink x's capacity to be equal to its size.
+
+eastl::vector<Widget> x;
+x.set_capacity(0);  // Completely clear x.
+

To compact your vector or string in a way that would also work with std STL you need to do the following.

+

How to shrink a vector's capacity to be equal to its size:

+
std::vector<Widget> x;
+std::vector<Widget>(x).swap(x); // Shrink x's capacity.
+How to completely clear a std::vector (size = 0, capacity = 0, no allocation):
+
std::vector<Widget> x;
+std::vector<Widget>().swap(x); // Completely clear x.
+
+

Cont.20 +How do I iterate a container while (selectively) removing items from it?

+

All EASTL containers have an erase function which takes an iterator as an argument and returns an iterator to the next item. Thus, you can erase items from a container while iterating it like so:

+
set<int> intSet;
+set<int>::iterator i = intSet.begin();
+while(i != intSet.end()) +{ + if(*i & 1)  // Erase all odd integers from the container. +        i = intSet.erase(i); +    else +        ++i; +}
+

Cont.21 +How do I store a pointer in a container?

+

The problem with storing pointers in containers is that clearing the container will not +free the pointers automatically. There are two conventional resolutions to this problem:

+
    +
  • Manually free pointers when removing them from containers. 
  • +
  • Store the pointer as a smart pointer instead of a "raw"pointer.
  • +
+

The advantage of the former is that it makes the user's intent obvious and prevents the possibility of smart pointer "thrashing" with some containers. The disadvantage of the former is that it is more tedicous and error-prone.

+

The advantage of the latter is that your code will be cleaner and will always be error-free. The disadvantage is that it is perhaps slightly obfuscating and with some uses of some containers it can cause smart pointer thrashing, whereby a resize of a linear container (e.g. vector) can cause shared pointers to be repeatedly incremented and decremented with no net effect.

+

It's important that you use a shared smart pointer and not an unshared one such as C++ auto_ptr, as the latter will result in crashes upon linear container resizes. Here we provide an example of how to create a list of smart pointers:

+
list< shared_ptr<Widget> > wList;
+
+wList.push_back(shared_ptr<Widget>(new Widget));
+wList.pop_back(); // The Widget will be freed.
+

Cont.22 +How do I make a union of two containers? difference? intersection?

+

The best way to accomplish this is to sort your container (or use a sorted container such as set) and then apply the set_union, set_difference, or set_intersection algorithms.

+

Cont.23 +How do I override the default global allocator? 

+

There are multiple ways to accomplish this. The allocation mechanism is defined in EASTL/internal/config.h and in allocator.h/cpp. Overriding the default global allocator means overriding these files, overriding what these files refer to, or changing these files outright. Here is a list of things you can do, starting with the simplest:

+
    +
  • Simply provide the following versions of operator new (which EASTL requires, actually):
    +     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);
  • +
  • Predefine the config.h macros for EASTLAlloc, EASTLFree, etc. See config.h for this.
  • +
  • Override config.h entirely via EASTL_USER_CONFIG_HEADER. See config.h for this.
  • +
  • Provide your own version of allocator.h/cpp
  • +
  • Provide your own version of config.h. 
  • +
+

If you redefine the allocator class, you can make it work however you want.

+

Note that config.h defines EASTLAllocatorDefault, which returns the default allocator instance. As documented in config.h, this is not a global allocator which implements all container allocations but is the allocator that is used when EASTL needs to allocate memory internally. There are very few cases where EASTL allocates memory internally, and in each of these it is for a sensible reason that is documented to behave as such.

+

Cont.24 +How do I do trick X with the string container?

+

There seem to be many things users want to do with strings. Perhaps the most commonly requested EASTL container extensions are string class shortcut functions. While some of these requests are being considered, we provide some shortcut functions here.
+
+find_and_replace

+
template <typename String>
+void find_and_replace(String& s, const typename String::value_type* pFind, const typename String::value_type* pReplace)    
+{
+    for(size_t i; (i = source.find(pFind)) != T::npos; )
+        s.replace(i, eastl::CharStrlen(pFind), pReplace);
+}
+
+Example:
+    find_and_replace(s, "hello", "hola");
+

trim front (multiple chars)

+
template <typename String>
+void trim_front(String& s, const typename String::value_type* pValues)
+{
+    s.erase(0, s.find_first_not_of(pValues));
+}
+
+Example:
+    trim_front(s, " \t\n\r");
+

trim back (multiple chars)

+
template <typename String>
+void trim_front(String& s, const typename String::value_type* pValues)
+{
+    s.resize(s.find_last_not_of(pValues) + 1);
+}
+
+Example:
+    trim_back(s, " \t\n\r");
+

prepend

+
template <typename String>
+void prepend(String& s, const typename String::value_type* p)
+{
+    s.insert(0, p);
+}
+
+Example:
+    prepend(s, "log: ");
+

begins_with +

+
template <typename String>
+bool begins_with(const String& s, const typename String::value_type* p)
+{
+    return s.compare(0, eastl::CharStrlen(p), p) == 0;
+}
+
+Example:
+    if(begins_with(s, "log: ")) ...
+

ends_with

+
template <typename String>
+bool ends_with(const String& s, const typename String::value_type* p)
+{
+    const typename String::size_type n1 = s.size();
+    const typename String::size_type n2 = eastl::CharStrlen(p);
+    return ((n1 >= n2) && s.compare(n1 - n2, n2, p) == 0);
+}
+
+Example:
+    if(ends_with(s, "test.")) ...
+

tokenize
+Here is a simple tokenization function that acts very much like the C strtok function. 

+
template <typename String>
+size_t tokenize(const String& s, const typename String::value_type* pDelimiters,
+                String* resultArray, size_t resultArraySize)
+{
+    size_t n = 0;
+    typename String::size_type lastPos = s.find_first_not_of(pDelimiters, 0);
+    typename String::size_type pos     = s.find_first_of(pDelimiters, lastPos);
+
+    while((n < resultArraySize) && (pos != String::npos) || (lastPos != String::npos))
+    {
+        resultArray[n++].assign(s, lastPos, pos - lastPos);
+        lastPos = s.find_first_not_of(pDelimiters, pos);
+        pos     = s.find_first_of(pDelimiters, lastPos);
+    }
+
+    return n;
+}
+
+Example:
+   string resultArray[32];
+tokenize(s, " \t", resultArray, 32));
+ +

Cont.25 How do EASTL smart pointers compare to Boost smart pointers? 

+

EASTL's smart pointers are nearly identical to Boost (including all that crazy member template and dynamic cast functionality in shared_ptr), but are not using the Boost source code. EA legal has already stated that it is fine to have smart pointer classes with the same names and functionality as those present in Boost. EA legal specifically looked at the smart pointer classes in EASTL for this. There are two differences between EASTL smart pointers and Boost smart pointers:

+
    +
  • EASTL smart pointers don't have thread safety built-in. It was deemed that this is too much overhead and that thread safety is something best done at a higher level. By coincidence the C++ library proposal to add shared_ptr also omits the thread safety feature. FWIW, I put a thread-safe shared_ptr in EAThread, though it doesn't attempt to do all the fancy member template things that Boost shared_ptr does. Maybe I'll add that some day if people care.
  • +
+
    +
  • EASTL shared_ptr object deletion goes through a deletion object instead of through a virtual function interface. 95% of the time this makes no difference (aside from being more efficient), but the primary case where it matters is when you have shared_ptr<void> and assign to is something like "new Widget". The problem is that shared_ptr<void> doesn't know what destructor to call and so doesn't call a destructor unless you specify a custom destructor object as part of the template specification. I don't know what to say about this one, as it is less safe, but forcing everybody to have the overhead of additional templated classes and virtual destruction functions doesn't seem to be in the spirit of high performance or lean game development.
  • +
+

There is the possibility of making a shared_ptr_boost which is completely identical to Boost shared_ptr. So perhaps that will be done some day.

+

Cont.26 +How do your forward-declare an EASTL container?

+

Here is are some examples of how to do this:

+
namespace eastl
+{
+    template <typename T, typename Allocator> class basic_string;
+    typedef basic_string<char, allocator> string8;   // Forward declare EASTL's string8 type.
+
+    template <typename T, typename Allocator> class vector;
+    typedef vector<char, allocator> CharArray;
+
+    template <typename Value, typename Hash, typename Predicate, typename Allocator, bool bCacheHashCode> class hash_set;
+
+    template <typename Key, typename T, typename Compare, typename Allocator> class map;
+}
+

The forward declaration can be used to declare a pointer or reference to such a class. It cannot be used to declare an instance of a class or refer to class data, static or otherwise. Nevertheless, forward declarations for pointers and references are useful for reducing the number of header files a header file needs to include.

+

Cont.27 +How do I make two containers share a memory pool?

+

EASTL (and std STL) allocators are specified by value semantics and not reference semantics. Value semantics is more powerful (because a value can also be a reference, but not the other way around), but is not always what people expects if they're used to writing things the other way.

+

Here is some example code:

+
struct fixed_pool_reference
{
public:
fixed_pool_reference()
{
mpFixedPool = NULL;
}

fixed_pool_reference(eastl::fixed_pool& fixedPool)
{
mpFixedPool = &fixedPool;
}

fixed_pool_reference(const fixed_pool_reference& x)
{
mpFixedPool = x.mpFixedPool;
}

fixed_pool_reference& operator=(const fixed_pool_reference& x)
{
mpFixedPool = x.mpFixedPool;
return *this;
}

void* allocate(size_t /*n*/, int /*flags*/ = 0)
{
return mpFixedPool->allocate();
}

void* allocate(size_t /*n*/, size_t /*alignment*/, size_t /*offset*/, int /*flags*/ = 0)
{
return mpFixedPool->allocate();
}

void deallocate(void* p, size_t /*n*/)
{
return mpFixedPool->deallocate(p);
}

const char* get_name() const
{
return "fixed_pool_reference";
}

void set_name(const char* /*pName*/)
{
}

protected:
friend bool operator==(const fixed_pool_reference& a, const fixed_pool_reference& b);
friend bool operator!=(const fixed_pool_reference& a, const fixed_pool_reference& b);

eastl::fixed_pool* mpFixedPool;
}; + +inline bool operator==(const fixed_pool_reference& a, const fixed_pool_reference& b) +{ + return (a.mpFixedPool == b.mpFixedPool);
} + +inline bool operator!=(const fixed_pool_reference& a, const fixed_pool_reference& b) +{ + return (a.mpFixedPool != b.mpFixedPool); +}
+

Example usage of the above:

+
typedef eastl::list<int, fixed_pool_reference> IntList;
+
+IntList::node_type buffer[2];
+eastl::fixed_pool  myPool(buffer, sizeof(buffer), sizeof(Int::node_type), 2);
+
+IntList myList1(myPool);
+IntList myList2(myPool);
+           
+myList1.push_back(37);
+myList2.push_back(39);
+

Cont.28 +Can I use a std (STL) allocator with EASTL?

+

No. EASTL allocators are similar in interface to std STL allocators, but not 100% compatible. If it was possible to make them compatible with std STL allocators but also match the design of EASTL then compatibility would exist. The primary reasons for lack of compatibility are:

+
    +
  • EASTL allocators have a different allocate function signature.
  • +
  • EASTL allocators have as many as four extra required functions: ctor(name), get_name(), set_name(), allocate(size, align, offset).
  • +
  • EASTL allocators have an additional allocate function specifically for aligned allocations, as listed directly above.
  • +
+

What are the requirements of classes stored in containers?

+

Class types stored in containers must have:

+
    +
  • a public copy constructor
  • +
  • a public assignment operator
  • +
  • a public destructor
  • +
  • an operator < that compares two such classes (sorted containers only).
  • +
  • an operator == that compares two such classes (hash containers only).
  • +
+

Recall that the compiler generates basic versions these functions for you when you don't implement them yourself, so you can omit any of the above if the compiler-generated version is sufficient.

+

For example, the following code will act incorrectly, because the user forgot to implement an assignment operator. The compiler-generated assignment operator will assign the refCount value, which the user doesn't want, and which will be called by the vector during resizing.

+
struct NotAPod
+{
+   NotAPod(const NotAPod&) {} // Intentionally don't copy the refCount 
+  int refCount; // refCounts should not be copied between NotAPod instances. +}; + +eastl::vector<NotAPod> v;
+

Algorithms

+

Algo.1 + I'm getting screwy behavior in sorting algorithms or sorted +containers. What's wrong?

+

It may possible that you are seeing floating point roundoff problems. Many STL algorithms require object comparisons to act consistently. However, floating point values sometimes compare differently between uses because in one situation a value might be in 32 bit form in system memory, whereas in anther situation that value might be in an FPU register with a different precision. These are difficult problems to track down and aren't the fault of EASTL or whatever similar library you might be using. There are various solutions to the problem, but the important thing is to find a way to force the comparisons to be consistent.

+

The code below was an example of this happening, whereby the object pA->mPos was stored in system memory while pB->mPos was stored in a register and comparisons were inconsistent and a crash ensued.

+
class SortByDistance : public binary_function<WorldTreeObject*, WorldTreeObject*, bool>
+{
+private:
+    Vector3 mOrigin;
+
+public:
+    SortByDistance(Vector3 origin) {
+        mOrigin = origin;
+    }
+
+    bool operator()(WorldTreeObject* pA, WorldTreeObject* pB) const {
+        return ((WorldObject*)pA)->mPos - mOrigin).GetLength()
+             < ((WorldObject*)pB)->mPos - mOrigin).GetLength();
+    }
+};
+ +

Algo.2 +How do I write a comparison (operator<()) for a struct that contains two or more members? 

+

For a struct with two members such as the following:

+
struct X {
+    Blah m1;
+    Blah m2;
+};
+

You would write the comparison function like this:

+
bool operator<(const X& a, const X& b) {
+    return (a.m1 == b.m1) ? (a.m2 < b.m2) : (a.m1 < b.m1);
+}
+

or, using only operator < but more instructions:

+
bool operator<(const X& a, const X& b) {
+    return (a.m1 < b.m1) || (!(b.m1 < a.m1) && (a.m2 < b.m2));
+}
+

For a struct with three members, you would have:

+
bool operator<(const X& a, const X& b) {
+    if(a.m1 != b.m1)
+        return (a.m1 < b.m1);
+    if(a.m2 != b.m2)
+        return (a.m2 < b.m2);
+    return (a.mType < b.mType);
+}
+

And a somewhat messy implementation if you wanted to use only operator <.

+

Note also that you can use the above technique to implement operator < for spatial types such as vectors, points, and rectangles. You would simply treat the members of the struct as an array of values and ignore the fact that they have spatial meaning. All operator < cares about is that things order consistently.

+
bool operator<(const Point2D& a, const Point2D& b) {
+    return (a.x == b.x) ? (a.y < b.y) : (a.x < b.x);
+}
+

Algo.3 +How do I sort something in reverse order?

+

Normally sorting puts the lowest value items first in the sorted range. You can change this by simply reversing the comparison. For example:
+

+
sort(intVector.begin(), intVector.end(), greater<int>());
+

It's important that you use operator > instead of >=. The comparison function must return false for every case where values are equal.

+

Algo.4 +I'm getting errors about min and max while compiling.

+

You need to define NOMINMAX under VC++ when this occurs, as it otherwise defines min and max macros that interfere. There may be equivalent issues with other compilers. Also, VC++ has a specific <minmax.h> header file which defines min and max macros but which doesn't pay attention to NOMINMAX and so in that case there is nothing to do but not include that file or to undefine min and max. minmax.h is not a standard file and its min and max macros are not standard C or C++ macros or functions.

+

Algo.5 +Why don't algorithms take a container as an argument instead of iterators? A container would be more convenient.

+

Having algorithms that use containers instead of algorithms would reduce reduce functionality with no increase in performance. This is because the use of iterators allows for the application of algorithms to sub-ranges of containers and allows for the application of algorithms to containers aren't formal C++ objects, such as C-style arrays.

+

Providing additional algorithms that use containers would introduce redundancy with respect to the existing algorithms that use iterators.

+

Algo.6 +Given a container of pointers, how do I find an element by value (instead of by pointer)?

+

Functions such as find_if help you find a T element in a container of Ts. But if you have a container of pointers such as vector<Widget*>, these functions will enable you to find an element that matches a given Widget* pointer, but they don't let you find an element that matches a given Widget object.

+

You can write your own iterating 'for' loop and compare values, or you can use a generic function object to do the work if this is a common task:

+
template<typename T>
+struct dereferenced_equal
+{
+    const T& mValue;
+
+    dereferenced_equal(const T& value) : mValue(value) { }     
+    bool operator==(const T* pValue) const { return *pValue == mValue; }
+};
+
+...
+
+find_if(container.begin(), container.end(), dereferenced_equal<Widget>(someWidget));
+ +

Algo.7 +When do stored objects need to support operator < vs. when do they need to support operator ==?

+

Any object which is sorted needs to have operator < defined for it, implicitly via operator < or explicitly via a user-supplied Compare function. Sets and map containers require operator <, while sort, binary search, and min/max algorithms require operator <.

+

Any object which is compared for equality needs to have operator == defined for it, implicitly via operator == or explicitly via a user-supplied BinaryPredicate function. Hash containers required operator ==, while many of the algorithms other than those mentioned above for operator < require operator ==.

+

Some algorithms and containers require neither < nor ==. Interestingly, no algorithm or container requires both < and ==.

+

Algo.8 How do I sort via pointers or array indexes instead of objects directly?

+

Pointers

+
vector<TestObject>  toArray;
+vector<TestObject*> topArray;
+
+for(eastl_size_t i = 0; i < 32; i++)
+   toArray.push_back(TestObject(rng.RandLimit(20)));
+for(eastl_size_t i = 0; i < 32; i++) // This needs to be a second loop because the addresses might change in the first loop due to container resizing.
+   topArray.push_back(&toArray[i]);
+
+struct TestObjectPtrCompare
+{
+    bool operator()(TestObject* a, TestObject* b)
+        { return a->mX < a->mX; }
+};
+
+quick_sort(topArray.begin(), topArray.end(), TestObjectPtrCompare());
+

Array indexes

+
vector<TestObject>   toArray;
+vector<eastl_size_t> toiArray;
+
+for(eastl_size_t i = 0; i < 32; i++)
+{
+    toArray.push_back(TestObject(rng.RandLimit(20)));
+    toiArray.push_back(i);
+}
+
+struct TestObjectIndexCompare
+{
+    vector* mpArray;
+
+    TestObjectIndexCompare(vector<TestObject>* pArray) : mpArray(pArray) { }
+    TestObjectIndexCompare(const TestObjectIndexCompare& x) : mpArray(x.mpArray){ }
+    TestObjectIndexCompare& operator=(const TestObjectIndexCompare& x) { mpArray = x.mpArray; return *this; }
+
+    bool operator()(eastl_size_t a, eastl_size_t b)
+       { return (*mpArray)[a] < (*mpArray)[b]; }
+};
+
+quick_sort(toiArray.begin(), toiArray.end(), TestObjectIndexCompare(&toArray));
+
+

Array indexes (simpler version using toArray as a global variable)

+
vector<TestObject>   toArray;
+vector<eastl_size_t> toiArray;
+
+for(eastl_size_t i = 0; i < 32; i++)
+{
+    toArray.push_back(TestObject(rng.RandLimit(20)));
+    toiArray.push_back(i);
+}
+
+struct TestObjectIndexCompare
+{
+    bool operator()(eastl_size_t a, eastl_size_t b)
+       { return toArray[a] < toArray[b]; }
+};
+
+quick_sort(toiArray.begin(), toiArray.end(), TestObjectIndexCompare(&toArray));
+

Iterators

+

Iter.1 +What's the difference between iterator, const iterator, and const_iterator?

+

An iterator can be modified and item it points to can be modified.
+A const iterator cannot be modified, but the items it points to can be modified.
+A const_iterator can be modified, but the items it points to cannot be modified.
+A const const_iterator cannot be modified, nor can the items it points to.

+

This situation is much like with char pointers:

+
+ + + + + + + + + + + + + + + + + + + + + + + +
Iterator typePointer equivalent
iteratorchar*
const iteratorchar* const
const_iteratorconst char*
const const_iteratorconst char* const
+
+

Iter.2 How do I tell from an iterator what type of thing it is iterating?

+

Use the value_type typedef from iterator_traits, as in this example

+
template <typename Iterator>
+void DoSomething(Iterator first, Iterator last)
+{
+    typedef typename iterator_traits<Iterator>::value_type;
+
+    // use value_type
+}
+

Iter.3 +How do I iterate a container while (selectively) removing items from it?

+

All EASTL containers have an erase function which takes an iterator as an +argument and returns an iterator to the next item. Thus, you can erase items from a container +while iterating it like so:

+
set<int> intSet;
+set<int>::iterator i = intSet.begin();
+
+while(i != intSet.end())
+{
+    if(*i & 1) // Erase all odd integers from the container.
+        i = intSet.erase(i);
+    else
+        ++i;
+}
+

Iter.4 +What is an insert_iterator?

+

An insert_iterator is a utility class which is like an iterator except that when you assign a value to it, the insert_iterator inserts the value into the container (via insert()) and increments the iterator. Similarly, there are front_insert_iterator and back_insert_iterator, which are similar to insert_iterator except that assigning a value to them causes then to call push_front and push_back, respectively, on the container. These utilities may seem a slightly abstract, but they have uses in generic programming.
+

+
+End of document
+
+
+
+
+ + diff --git a/lib/EASTL/doc/html/EASTL Glossary.html b/lib/EASTL/doc/html/EASTL Glossary.html new file mode 100644 index 000000000..bd4b86560 --- /dev/null +++ b/lib/EASTL/doc/html/EASTL Glossary.html @@ -0,0 +1,490 @@ + + + + EASTL Glossary + + + + + + +

EASTL Glossary

+

This document provides definitions to various terms related to EASTL. Items that are capitalized are items that are +used as template parameters.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
adapterAn 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 containerAn 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.
arrayAn array is a C++ container which directly implements a C-style fixed array but which adds STL container semantics +to it.
basic_stringA templated string class which is usually used to store char or wchar_t strings.
beginThe function used by all conventional containers to return the first item in the container.
BidirectionalIteratorAn 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).
BinaryPredicateA function which takes two arguments and returns true if some criteria is met (e.g. they are equal).
binder1st, binder2ndThese 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 vectorA 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.
bitsetAn 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.
capacityRefers 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_iteratorAn iterator whose iterated items are cannot be modified. A const_iterator is akin to a const pointer such as 'const +char*'.
containerA 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 constructorA 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);
CompareA function which takes two arguments and returns the lesser of the two.
dequeThe name deque is pronounced "deck" and stands for "double-ended queue."
+
+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.
+
+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_typeThe 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.
emptyThe 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.
elementAn element refers to a member of a container.
endThe function used by all conventional containers to return one-past the last item in the container.
equal_rangeequal_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 instantiationExplicit 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:
+    template class vector<char>;
+    template void min<int>(int, int);
+    template void min(int, int);
ForwardIteratorAn input iterator which is like InputIterator except it can be reset back to the beginning.
FunctionA function which takes one argument and applies some operation to the target.
function object, functorA function object or functor is a class that has the function-call operator (operator()) +defined.
GeneratorA function which takes no arguments and returns a value (which will usually be assigned to an object).
hash_map, hash_multimap, hash_set, hash_multisetThe 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.
heapA 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.
InputIteratorAn 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_ptrintrusive_ptr is a smart pointer which doesn't allocate memory but instead uses the contained object to manage +lifetime via addref and release functions.
iteratorAn 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 categoryAn 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 iterator_tag.
iterator_tagSee iterator category.
key_type, KeyA 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 compareA lexicographical compare is a comparison of two containers that compares them element by element, much like the C +strcmp function compares two strings.
linked_ptrA 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.
listA 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_boundlower_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.
mapMap 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_typeA 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 templateA 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.
multisetMultiset 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.
nodeA 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).
nposnpos is used by the string class to identify a non-existent index. Some string functions return npos to indicate +that the function failed.
rel_opsrel_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_iteratorA 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.
OutputIteratorAn output iterator (iterator you write to) which allows writing each element only once in only in a forward +direction.
PODPOD 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. 
PredicateA function which takes one argument returns true if the argument meets some criteria.
priority_queueA priority_queue is an adapter container which implements a heap via a random access container such as vector or +deque.
queueA 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.
RandomAccessIteratorAn input iterator which can be addressed like an array. It is a superset of all other input iterators.
red-black treeA 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.
scalarA scalar is a data type which is implemented via a numerical value. In C++ this means integers, floating point +values, enumerations, and pointers. 
scoped_ptrA scoped_ptr is a smart pointer which is the same as C++ auto_ptr except that it cannot be copied.
setSet 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.
sequenceA 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.
sizeAll 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_typeThe 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.
skip listA skip-list is a type of container which is an alternative to a binary tree for finding data.
shared_ptrA 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.
slistAn slist is like a list but is singly-linked instead of doubly-linked. It can only be iterated in a +forward-direction.
smart pointerSmart 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.
spliceSplicing refers to the moving of a subsequence of one Sequence into another Sequence.
stackA stack is a adapter container which implements LIFO (last-in, first, out) access via another container such as a +list or deque.
STLStandard Template Library. 
StrictWeakOrderingA BinaryPredicate that compares two objects, returning true if the first precedes the second. Like Compare but has +additional requirements. Used for sorting routines.
+
+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.
stringSee basic_string.
TT is the template parameter name used by most containers to identify the contained element type. 
template parameterA 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 specializationA 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.
treapA 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 traitsType 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.
typenameTypename 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.
UnaryOperationA function which takes one argument and returns a value (which will usually be assigned to second object).
upper_boundupper_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, ValueA 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.
vectorA 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_multisetThese 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_ptrA 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
+
+
+
+
+
+
+
+
+ + diff --git a/lib/EASTL/doc/html/EASTL Gotchas.html b/lib/EASTL/doc/html/EASTL Gotchas.html new file mode 100644 index 000000000..daa8f7aa7 --- /dev/null +++ b/lib/EASTL/doc/html/EASTL Gotchas.html @@ -0,0 +1,175 @@ + + + + EASTL Gotchas + + + + + + + +

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.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1map::operator[] can create elements.
2char* converts to string silently.
3char* is compared by ptr and not by contents.
4Iterators can be invalidated by container mutations.
5Vector resizing may cause ctor/dtor cascades.
6Vector and string insert/push_back/resize can reallocate.
7Deriving from containers may not work.
8set::iterator is const_iterator.
9Inserting elements means copying by value.
10Containers of pointers can leak if you aren't careful.
11Containers of auto_ptrs can crash.
12Remove algorithms don't actually remove elements.
13list::size() is O(n).
14vector and deque::size() may incur integer division.
15Be careful making custom Compare functions.
16Comparisons involving floating point are dangerous.
17Writing beyond string::size and vector::size is dangerous.
18Container operator=() doesn't copy allocators.
+

Detail

+

1 +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[].

+

2 +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.

+

3 +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.

+

4 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.

+

5 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.

+

6 +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.

+

7 +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.

+

8 +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.

+

9 +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.

+

10 + 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.

+

11 +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.

+

12 +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.

+

13 +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.

+

14 + 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.

+

15 + 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.

+

16 + 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.

+

17 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:

+

string mDataDir;
+
+ mDataDir.reserve(kMaxPathLength);
+ strcpy(&mDataDir[0], "blah/blah/blah");
+mDataDir.resize(strlen(&mDataDir[0])); // Overwrites your blah/... with 00000...

+

This following code is OK:

+

string mDataDir;
+
+ mDataDir.resize(kMaxPathLength);
+ strcpy(&mDataDir[0], "blah/blah/blah");
+mDataDir.resize(strlen(&mDataDir[0]));

+

18 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
+
+
+
+
+ + diff --git a/lib/EASTL/doc/html/EASTL Introduction.html b/lib/EASTL/doc/html/EASTL Introduction.html new file mode 100644 index 000000000..0e9b23cb9 --- /dev/null +++ b/lib/EASTL/doc/html/EASTL Introduction.html @@ -0,0 +1,47 @@ + + + + EASTL Introduction + + + + + + +

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
+
+
+
+
+
+
+ + diff --git a/lib/EASTL/doc/html/EASTL Maintenance.html b/lib/EASTL/doc/html/EASTL Maintenance.html new file mode 100644 index 000000000..aaca9556d --- /dev/null +++ b/lib/EASTL/doc/html/EASTL Maintenance.html @@ -0,0 +1,292 @@ + + + + EASTL Maintenance + + + + + + + +

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:

+
template <class InputIterator, class T>
+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. +
  3. Correctness (doesn't have bugs)
  4. +
  5. Portability (works on all required platforms with minimal specialized code)
  6. +
  7. Readability (code is legible and comments are present and useful)
  8. +
+

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 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:

+
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.
  • +
+
+
+
+
+
+
+ + diff --git a/lib/EASTL/doc/html/EASTL Modules.html b/lib/EASTL/doc/html/EASTL Modules.html new file mode 100644 index 000000000..620937ea9 --- /dev/null +++ b/lib/EASTL/doc/html/EASTL Modules.html @@ -0,0 +1,666 @@ + + + + EASTL Modules + + + + + + + +

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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 ModuleDescription
configConfiguration header. Allows for changing some compile-time options.
slist
+fixed_slist
Singly-linked list.
+fixed_slist is a version which is implemented via a fixed block of contiguous memory.
list
+fixed_list
Doubly-linked list.
intrusive_list
+intrusive_slist
List whereby the contained item provides the node implementation.
arrayWrapper for a C-style array which extends it to act like an STL container.
vector
+fixed_vector
Resizable array container.
vector_set
+vector_multiset
Set implemented via a vector instead of a tree. Speed and memory use is improved but resizing is slower.
vector_map
+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_vectorImplements a vector of bool, but the actual storage is done with one bit per bool. Not the same thing as a +bitset.
bitsetImplements 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
+multiset
+fixed_set
+fixed_multiset
A set is a sorted unique collection, multiset is sorted but non-unique collection.
map
+multimap
+fixed_map
+fixed_multimap
A map is a sorted associative collection implemented via a tree. It is also known as dictionary.
hash_map
+hash_multimap
+fixed_hash_map
+fixed_hash_multimap
Map implemented via a hash table.
intrusive_hash_map
+intrusive_hash_multimap
+intrusive_hash_set
+intrusive_hash_multiset
hash_map whereby the contained item provides the node implementation, much like intrusive_list.
hash_set
+hash_multiset
+fixed_hash_set
+fixed_hash_map
Set implemented via a hash table.
basic_string
+fixed_string
+fixed_substring
basic_string is a character string/array.
+fixed_substring is a string which is a reference to a range within another string or character array.
+cow_string is a string which implements copy-on-write.
algorithmmin/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.
numericNumeric 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_traitsType 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_ptrSmart 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() efficiencyoperator[] efficiency +

Insert efficiency

+
+

Erase via Iterator efficiency

+
+

Find efficiency

+
+

Sort efficiency

+
slistT81648fn-11nn+
+

list

+
+

T

+
1224 +

8

+
16 +

b

+
n- +

1

+
+

1

+
+

n

+
+

n log(n)

+
intrusive_slistT4848fn-11nn+
intrusive_listT816816bn-11nn log(n)
arrayT0000r11--nn log(n)
vectorT163200r111 at end, else n1 at end, else nnn log(n)
vector_setT163200r111 at end, else n1 at end, else nlog(n)1
vector_multisetT163200r111 at end, else n1 at end, else nlog(n)1
vector_mapKey, T163200r111 at end, else n1 at end, else nlog(n)1
vector_multimapKey, T163200r111 at end, else n1 at end, else nlog(n)1
dequeT448400r111 at begin or end,
+else n / 2
1 at begin or end,
+else n / 2
nn log(n)
bit_vectorbool81600r111 at end, else n1 at end, else nnn log(n)
string (all types)T163200r111 at end, else n1 at end, else nnn log(n)
setT24441628b1-log(n)log(n)log(n)1
multisetT24441628b1-log(n)log(n)log(n)1
mapKey, T24441628b1log(n)log(n)log(n)log(n)1
multimapKey, T24441628b1-log(n)log(n)log(n)1
hash_setT162048b1-111-
hash_multisetT162048b1-1
11-
hash_mapKey, T162048b1-111-
hash_multimapKey, T162048b1-111-
intrusive_hash_setT162048b1-111-
intrusive_hash_multisetT162048b1-111-
intrusive_hash_mapT (Key == T)162048b1-111-
intrusive_hash_multimapT (Key == T) 162048b1-111-
+
    +
  • - 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. 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.
  • +
  • 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/lib/EASTL/doc/html/EASTLDoc.css b/lib/EASTL/doc/html/EASTLDoc.css new file mode 100644 index 000000000..b2656d858 --- /dev/null +++ b/lib/EASTL/doc/html/EASTLDoc.css @@ -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; +} diff --git a/lib/EASTL/doc/quick-reference.pdf b/lib/EASTL/doc/quick-reference.pdf new file mode 100644 index 000000000..b62ff9d10 Binary files /dev/null and b/lib/EASTL/doc/quick-reference.pdf differ diff --git a/lib/EASTL/include/EASTL/algorithm.h b/lib/EASTL/include/EASTL/algorithm.h new file mode 100644 index 000000000..f7ebcffa6 --- /dev/null +++ b/lib/EASTL/include/EASTL/algorithm.h @@ -0,0 +1,4221 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This file implements some of the primary algorithms from the C++ STL +// algorithm library. These versions are just like that STL versions and so +// are redundant. They are provided solely for the purpose of projects that +// either cannot use standard C++ STL or want algorithms that have guaranteed +// identical behaviour across platforms. +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// Definitions +// +// You will notice that we are very particular about the templated typenames +// we use here. You will notice that we follow the C++ standard closely in +// these respects. Each of these typenames have a specific meaning; +// this is why we don't just label templated arguments with just letters +// such as T, U, V, A, B. Here we provide a quick reference for the typenames +// we use. See the C++ standard, section 25-8 for more details. +// -------------------------------------------------------------- +// typename Meaning +// -------------------------------------------------------------- +// T The value type. +// Compare A function which takes two arguments and returns the lesser of the two. +// Predicate A function which takes one argument returns true if the argument meets some criteria. +// BinaryPredicate A function which takes two arguments and returns true if some criteria is met (e.g. they are equal). +// StrickWeakOrdering A BinaryPredicate that compares two objects, returning true if the first precedes the second. Like Compare but has additional requirements. Used for sorting routines. +// Function A function which takes one argument and applies some operation to the target. +// Size A count or size. +// Generator A function which takes no arguments and returns a value (which will usually be assigned to an object). +// UnaryOperation A function which takes one argument and returns a value (which will usually be assigned to second object). +// BinaryOperation A function which takes two arguments and returns a value (which will usually be assigned to a third object). +// InputIterator An input iterator (iterator you read from) which allows reading each element only once and only in a forward direction. +// ForwardIterator An input iterator which is like InputIterator except it can be reset back to the beginning. +// BidirectionalIterator An input iterator which is like ForwardIterator except it can be read in a backward direction as well. +// RandomAccessIterator An input iterator which can be addressed like an array. It is a superset of all other input iterators. +// OutputIterator An output iterator (iterator you write to) which allows writing each element only once in only in a forward direction. +// +// Note that with iterators that a function which takes an InputIterator will +// also work with a ForwardIterator, BidirectionalIterator, or RandomAccessIterator. +// The given iterator type is merely the -minimum- supported functionality the +// iterator must support. +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// Optimizations +// +// There are a number of opportunities for opptimizations that we take here +// in this library. The most obvious kinds are those that subsitute memcpy +// in the place of a conventional loop for data types with which this is +// possible. The algorithms here are optimized to a higher level than currently +// available C++ STL algorithms from vendors such as Microsoft. This is especially +// so for game programming on console devices, as we do things such as reduce +// branching relative to other STL algorithm implementations. However, the +// proper implementation of these algorithm optimizations is a fairly tricky +// thing. +// +// The various things we look to take advantage of in order to implement +// optimizations include: +// - Taking advantage of random access iterators. +// - Taking advantage of POD (plain old data) data types. +// - Taking advantage of type_traits in general. +// - Reducing branching and taking advantage of likely branch predictions. +// - Taking advantage of issues related to pointer and reference aliasing. +// - Improving cache coherency during memory accesses. +// - Making code more likely to be inlinable by the compiler. +// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// Supported Algorithms +// +// Algorithms that we implement are listed here. Note that these items are not +// all within this header file, as we split up the header files in order to +// improve compilation performance. Items marked with '+' are items that are +// extensions which don't exist in the C++ standard. +// +// ------------------------------------------------------------------------------- +// Algorithm Notes +// ------------------------------------------------------------------------------- +// adjacent_find +// adjacent_find +// all_of C++11 +// any_of C++11 +// none_of C++11 +// binary_search +// binary_search +// +binary_search_i +// +binary_search_i +// +change_heap Found in heap.h +// +change_heap Found in heap.h +// clamp +// copy +// copy_if C++11 +// copy_n C++11 +// copy_backward +// count +// count_if +// equal +// equal +// equal_range +// equal_range +// fill +// fill_n +// find +// find_end +// find_end +// find_first_of +// find_first_of +// +find_first_not_of +// +find_first_not_of +// +find_last_of +// +find_last_of +// +find_last_not_of +// +find_last_not_of +// find_if +// find_if_not +// for_each +// generate +// generate_n +// +identical +// +identical +// iter_swap +// lexicographical_compare +// lexicographical_compare +// lower_bound +// lower_bound +// make_heap Found in heap.h +// make_heap Found in heap.h +// min +// min +// max +// max +// +min_alt Exists to work around the problem of conflicts with min/max #defines on some systems. +// +min_alt +// +max_alt +// +max_alt +// +median +// +median +// merge Found in sort.h +// merge Found in sort.h +// min_element +// min_element +// max_element +// max_element +// mismatch +// mismatch +// move +// move_backward +// nth_element Found in sort.h +// nth_element Found in sort.h +// partial_sort Found in sort.h +// partial_sort Found in sort.h +// push_heap Found in heap.h +// push_heap Found in heap.h +// pop_heap Found in heap.h +// pop_heap Found in heap.h +// random_shuffle +// remove +// remove_if +// remove_copy +// remove_copy_if +// +remove_heap Found in heap.h +// +remove_heap Found in heap.h +// replace +// replace_if +// replace_copy +// replace_copy_if +// reverse_copy +// reverse +// random_shuffle +// rotate +// rotate_copy +// search +// search +// search_n +// set_difference +// set_difference +// set_difference_2 +// set_difference_2 +// set_decomposition +// set_decomposition +// set_intersection +// set_intersection +// set_symmetric_difference +// set_symmetric_difference +// set_union +// set_union +// sort Found in sort.h +// sort Found in sort.h +// sort_heap Found in heap.h +// sort_heap Found in heap.h +// stable_sort Found in sort.h +// stable_sort Found in sort.h +// swap +// swap_ranges +// transform +// transform +// unique +// unique +// upper_bound +// upper_bound +// is_permutation +// is_permutation +// next_permutation +// next_permutation +// +// Algorithms from the C++ standard that we don't implement are listed here. +// Most of these items are absent because they aren't used very often. +// They also happen to be the more complicated than other algorithms. +// However, we can implement any of these functions for users that might +// need them. +// includes +// includes +// inplace_merge +// inplace_merge +// partial_sort_copy +// partial_sort_copy +// paritition +// prev_permutation +// prev_permutation +// search_n +// stable_partition +// unique_copy +// unique_copy +// +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ALGORITHM_H +#define EASTL_ALGORITHM_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS(); + + #if defined(EA_COMPILER_MSVC) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + #include + #endif + + #include + #include // memcpy, memcmp, memmove + +EA_RESTORE_ALL_VC_WARNINGS(); + +#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 + + + +/////////////////////////////////////////////////////////////////////////////// +// min/max workaround +// +// MSVC++ has #defines for min/max which collide with the min/max algorithm +// declarations. The following may still not completely resolve some kinds of +// problems with MSVC++ #defines, though it deals with most cases in production +// game code. +// +#if EASTL_NOMINMAX + #ifdef min + #undef min + #endif + #ifdef max + #undef max + #endif +#endif + + + + +namespace eastl +{ + /// min_element + /// + /// min_element finds the smallest element in the range [first, last). + /// It returns the first iterator i in [first, last) such that no other + /// iterator in [first, last) points to a value smaller than *i. + /// The return value is last if and only if [first, last) is an empty range. + /// + /// Returns: The first iterator i in the range [first, last) such that + /// for any iterator j in the range [first, last) the following corresponding + /// condition holds: !(*j < *i). + /// + /// Complexity: Exactly 'max((last - first) - 1, 0)' applications of the + /// corresponding comparisons. + /// + template + ForwardIterator min_element(ForwardIterator first, ForwardIterator last) + { + if(first != last) + { + ForwardIterator currentMin = first; + + while(++first != last) + { + if(*first < *currentMin) + currentMin = first; + } + return currentMin; + } + return first; + } + + + /// min_element + /// + /// min_element finds the smallest element in the range [first, last). + /// It returns the first iterator i in [first, last) such that no other + /// iterator in [first, last) points to a value smaller than *i. + /// The return value is last if and only if [first, last) is an empty range. + /// + /// Returns: The first iterator i in the range [first, last) such that + /// for any iterator j in the range [first, last) the following corresponding + /// conditions hold: compare(*j, *i) == false. + /// + /// Complexity: Exactly 'max((last - first) - 1, 0)' applications of the + /// corresponding comparisons. + /// + template + ForwardIterator min_element(ForwardIterator first, ForwardIterator last, Compare compare) + { + if(first != last) + { + ForwardIterator currentMin = first; + + while(++first != last) + { + if(compare(*first, *currentMin)) + currentMin = first; + } + return currentMin; + } + return first; + } + + + /// max_element + /// + /// max_element finds the largest element in the range [first, last). + /// It returns the first iterator i in [first, last) such that no other + /// iterator in [first, last) points to a value greater than *i. + /// The return value is last if and only if [first, last) is an empty range. + /// + /// Returns: The first iterator i in the range [first, last) such that + /// for any iterator j in the range [first, last) the following corresponding + /// condition holds: !(*i < *j). + /// + /// Complexity: Exactly 'max((last - first) - 1, 0)' applications of the + /// corresponding comparisons. + /// + template + ForwardIterator max_element(ForwardIterator first, ForwardIterator last) + { + if(first != last) + { + ForwardIterator currentMax = first; + + while(++first != last) + { + if(*currentMax < *first) + currentMax = first; + } + return currentMax; + } + return first; + } + + + /// max_element + /// + /// max_element finds the largest element in the range [first, last). + /// It returns the first iterator i in [first, last) such that no other + /// iterator in [first, last) points to a value greater than *i. + /// The return value is last if and only if [first, last) is an empty range. + /// + /// Returns: The first iterator i in the range [first, last) such that + /// for any iterator j in the range [first, last) the following corresponding + /// condition holds: compare(*i, *j) == false. + /// + /// Complexity: Exactly 'max((last - first) - 1, 0)' applications of the + /// corresponding comparisons. + /// + template + ForwardIterator max_element(ForwardIterator first, ForwardIterator last, Compare compare) + { + if(first != last) + { + ForwardIterator currentMax = first; + + while(++first != last) + { + if(compare(*currentMax, *first)) + currentMax = first; + } + return currentMax; + } + return first; + } + + + #if EASTL_MINMAX_ENABLED + + /// min + /// + /// Min returns the lesser of its two arguments; it returns the first + /// argument if neither is less than the other. The two arguments are + /// compared with operator <. + /// + /// This min and our other min implementations are defined as returning: + /// b < a ? b : a + /// which for example may in practice result in something different than: + /// b <= a ? b : a + /// in the case where b is different from a (though they compare as equal). + /// We choose the specific ordering here because that's the ordering + /// done by other STL implementations. + /// + /// Some compilers (e.g. VS20003 - VS2013) generate poor code for the case of + /// scalars returned by reference, so we provide a specialization for those cases. + /// The specialization returns T by value instead of reference, which is + /// not that the Standard specifies. The Standard allows you to use + /// an expression like &max(x, y), which would be impossible in this case. + /// However, we have found no actual code that uses min or max like this and + /// this specialization causes no problems in practice. Microsoft has acknowledged + /// the problem and may fix it for a future VS version. + /// + template + inline EA_CONSTEXPR typename eastl::enable_if::value, T>::type + min(T a, T b) + { + return b < a ? b : a; + } + + template + inline EA_CONSTEXPR typename eastl::enable_if::value, const T&>::type + min(const T& a, const T& b) + { + return b < a ? b : a; + } + + inline EA_CONSTEXPR float min(float a, float b) { return b < a ? b : a; } + inline EA_CONSTEXPR double min(double a, double b) { return b < a ? b : a; } + inline EA_CONSTEXPR long double min(long double a, long double b) { return b < a ? b : a; } + + #endif // EASTL_MINMAX_ENABLED + + + /// min_alt + /// + /// This is an alternative version of min that avoids any possible + /// collisions with Microsoft #defines of min and max. + /// + /// See min(a, b) for detailed specifications. + /// + template + inline EA_CONSTEXPR typename eastl::enable_if::value, T>::type + min_alt(T a, T b) + { + return b < a ? b : a; + } + + template + inline typename eastl::enable_if::value, const T&>::type + min_alt(const T& a, const T& b) + { + return b < a ? b : a; + } + + inline EA_CONSTEXPR float min_alt(float a, float b) { return b < a ? b : a; } + inline EA_CONSTEXPR double min_alt(double a, double b) { return b < a ? b : a; } + inline EA_CONSTEXPR long double min_alt(long double a, long double b) { return b < a ? b : a; } + + + #if EASTL_MINMAX_ENABLED + + /// min + /// + /// Min returns the lesser of its two arguments; it returns the first + /// argument if neither is less than the other. The two arguments are + /// compared with the Compare function (or function object), which + /// takes two arguments and returns true if the first is less than + /// the second. + /// + /// See min(a, b) for detailed specifications. + /// + /// Example usage: + /// struct A{ int a; }; + /// struct Struct{ bool operator()(const A& a1, const A& a2){ return a1.a < a2.a; } }; + /// + /// A a1, a2, a3; + /// a3 = min(a1, a2, Struct()); + /// + /// Example usage: + /// struct B{ int b; }; + /// inline bool Function(const B& b1, const B& b2){ return b1.b < b2.b; } + /// + /// B b1, b2, b3; + /// b3 = min(b1, b2, Function); + /// + template + inline const T& + min(const T& a, const T& b, Compare compare) + { + return compare(b, a) ? b : a; + } + + #endif // EASTL_MINMAX_ENABLED + + + /// min_alt + /// + /// This is an alternative version of min that avoids any possible + /// collisions with Microsoft #defines of min and max. + /// + /// See min(a, b) for detailed specifications. + /// + template + inline const T& + min_alt(const T& a, const T& b, Compare compare) + { + return compare(b, a) ? b : a; + } + + + #if EASTL_MINMAX_ENABLED + + /// max + /// + /// Max returns the greater of its two arguments; it returns the first + /// argument if neither is greater than the other. The two arguments are + /// compared with operator < (and not operator >). + /// + /// This min and our other min implementations are defined as returning: + /// a < b ? b : a + /// which for example may in practice result in something different than: + /// a <= b ? b : a + /// in the case where b is different from a (though they compare as equal). + /// We choose the specific ordering here because that's the ordering + /// done by other STL implementations. + /// + template + inline EA_CONSTEXPR typename eastl::enable_if::value, T>::type + max(T a, T b) + { + return a < b ? b : a; + } + + template + inline EA_CONSTEXPR typename eastl::enable_if::value, const T&>::type + max(const T& a, const T& b) + { + return a < b ? b : a; + } + + inline EA_CONSTEXPR float max(float a, float b) { return a < b ? b : a; } + inline EA_CONSTEXPR double max(double a, double b) { return a < b ? b : a; } + inline EA_CONSTEXPR long double max(long double a, long double b) { return a < b ? b : a; } + + #endif // EASTL_MINMAX_ENABLED + + + /// max_alt + /// + /// This is an alternative version of max that avoids any possible + /// collisions with Microsoft #defines of min and max. + /// + template + inline EA_CONSTEXPR typename eastl::enable_if::value, T>::type + max_alt(T a, T b) + { + return a < b ? b : a; + } + + template + inline EA_CONSTEXPR typename eastl::enable_if::value, const T&>::type + max_alt(const T& a, const T& b) + { + return a < b ? b : a; + } + + inline EA_CONSTEXPR float max_alt(float a, float b) { return a < b ? b : a; } + inline EA_CONSTEXPR double max_alt(double a, double b) { return a < b ? b : a; } + inline EA_CONSTEXPR long double max_alt(long double a, long double b) { return a < b ? b : a; } + + + #if EASTL_MINMAX_ENABLED + /// max + /// + /// Min returns the lesser of its two arguments; it returns the first + /// argument if neither is less than the other. The two arguments are + /// compared with the Compare function (or function object), which + /// takes two arguments and returns true if the first is less than + /// the second. + /// + template + inline const T& + max(const T& a, const T& b, Compare compare) + { + return compare(a, b) ? b : a; + } + #endif + + + /// max_alt + /// + /// This is an alternative version of max that avoids any possible + /// collisions with Microsoft #defines of min and max. + /// + template + inline const T& + max_alt(const T& a, const T& b, Compare compare) + { + return compare(a, b) ? b : a; + } + + + /// min(std::initializer_list) + /// + template + T min(std::initializer_list ilist) + { + return *eastl::min_element(ilist.begin(), ilist.end()); + } + + /// min(std::initializer_list, Compare) + /// + template + T min(std::initializer_list ilist, Compare compare) + { + return *eastl::min_element(ilist.begin(), ilist.end(), compare); + } + + + /// max(std::initializer_list) + /// + template + T max(std::initializer_list ilist) + { + return *eastl::max_element(ilist.begin(), ilist.end()); + } + + /// max(std::initializer_list, Compare) + /// + template + T max(std::initializer_list ilist, Compare compare) + { + return *eastl::max_element(ilist.begin(), ilist.end(), compare); + } + + + /// minmax_element + /// + /// Returns: make_pair(first, first) if [first, last) is empty, otherwise make_pair(m, M), + /// where m is the first iterator in [first,last) such that no iterator in the range + /// refers to a smaller element, and where M is the last iterator in [first,last) such + /// that no iterator in the range refers to a larger element. + /// + /// Complexity: At most max([(3/2)*(N - 1)], 0) applications of the corresponding predicate, + /// where N is distance(first, last). + /// + template + eastl::pair + minmax_element(ForwardIterator first, ForwardIterator last, Compare compare) + { + eastl::pair result(first, first); + + if(!(first == last) && !(++first == last)) + { + if(compare(*first, *result.first)) + { + result.second = result.first; + result.first = first; + } + else + result.second = first; + + while(++first != last) + { + ForwardIterator i = first; + + if(++first == last) + { + if(compare(*i, *result.first)) + result.first = i; + else if(!compare(*i, *result.second)) + result.second = i; + break; + } + else + { + if(compare(*first, *i)) + { + if(compare(*first, *result.first)) + result.first = first; + + if(!compare(*i, *result.second)) + result.second = i; + } + else + { + if(compare(*i, *result.first)) + result.first = i; + + if(!compare(*first, *result.second)) + result.second = first; + } + } + } + } + + return result; + } + + + template + eastl::pair + minmax_element(ForwardIterator first, ForwardIterator last) + { + typedef typename eastl::iterator_traits::value_type value_type; + + return eastl::minmax_element(first, last, eastl::less()); + } + + + + /// minmax + /// + /// Requires: Type T shall be LessThanComparable. + /// Returns: pair(b, a) if b is smaller than a, and pair(a, b) otherwise. + /// Remarks: Returns pair(a, b) when the arguments are equivalent. + /// Complexity: Exactly one comparison. + /// + + // The following optimization is a problem because it changes the return value in a way that would break + // users unless they used auto (e.g. auto result = minmax(17, 33); ) + // + // template + // inline EA_CONSTEXPR typename eastl::enable_if::value, eastl::pair >::type + // minmax(T a, T b) + // { + // return (b < a) ? eastl::make_pair(b, a) : eastl::make_pair(a, b); + // } + // + // template + // inline typename eastl::enable_if::value, eastl::pair >::type + // minmax(const T& a, const T& b) + // { + // return (b < a) ? eastl::make_pair(b, a) : eastl::make_pair(a, b); + // } + + // It turns out that the following conforming definition of minmax generates a warning when used with VC++ up + // to at least VS2012. The VS2012 version of minmax is a broken and non-conforming definition, and we don't + // want to do that. We could do it for scalars alone, though we'd have to decide if we are going to do that + // for all compilers, because it changes the return value from a pair of references to a pair of values. + template + inline eastl::pair + minmax(const T& a, const T& b) + { + return (b < a) ? eastl::make_pair(b, a) : eastl::make_pair(a, b); + } + + + template + eastl::pair + minmax(const T& a, const T& b, Compare compare) + { + return compare(b, a) ? eastl::make_pair(b, a) : eastl::make_pair(a, b); + } + + + + template + eastl::pair + minmax(std::initializer_list ilist) + { + typedef typename std::initializer_list::iterator iterator_type; + eastl::pair iteratorPair = eastl::minmax_element(ilist.begin(), ilist.end()); + return eastl::make_pair(*iteratorPair.first, *iteratorPair.second); + } + + template + eastl::pair + minmax(std::initializer_list ilist, Compare compare) + { + typedef typename std::initializer_list::iterator iterator_type; + eastl::pair iteratorPair = eastl::minmax_element(ilist.begin(), ilist.end(), compare); + return eastl::make_pair(*iteratorPair.first, *iteratorPair.second); + } + + template + inline T&& median_impl(T&& a, T&& b, T&& c) + { + if(eastl::less()(a, b)) + { + if(eastl::less()(b, c)) + return eastl::forward(b); + else if(eastl::less()(a, c)) + return eastl::forward(c); + else + return eastl::forward(a); + } + else if(eastl::less()(a, c)) + return eastl::forward(a); + else if(eastl::less()(b, c)) + return eastl::forward(c); + return eastl::forward(b); + } + + /// median + /// + /// median finds which element of three (a, b, d) is in-between the other two. + /// If two or more elements are equal, the first (e.g. a before b) is chosen. + /// + /// Complexity: Either two or three comparisons will be required, depending + /// on the values. + /// + template + inline const T& median(const T& a, const T& b, const T& c) + { + return median_impl(a, b, c); + } + + /// median + /// + /// median finds which element of three (a, b, d) is in-between the other two. + /// If two or more elements are equal, the first (e.g. a before b) is chosen. + /// + /// Complexity: Either two or three comparisons will be required, depending + /// on the values. + /// + template + inline T&& median(T&& a, T&& b, T&& c) + { + return eastl::forward(median_impl(eastl::forward(a), eastl::forward(b), eastl::forward(c))); + } + + + template + inline T&& median_impl(T&& a, T&& b, T&& c, Compare compare) + { + if(compare(a, b)) + { + if(compare(b, c)) + return eastl::forward(b); + else if(compare(a, c)) + return eastl::forward(c); + else + return eastl::forward(a); + } + else if(compare(a, c)) + return eastl::forward(a); + else if(compare(b, c)) + return eastl::forward(c); + return eastl::forward(b); + } + + + /// median + /// + /// median finds which element of three (a, b, d) is in-between the other two. + /// If two or more elements are equal, the first (e.g. a before b) is chosen. + /// + /// Complexity: Either two or three comparisons will be required, depending + /// on the values. + /// + template + inline const T& median(const T& a, const T& b, const T& c, Compare compare) + { + return median_impl(a, b, c, compare); + } + + /// median + /// + /// median finds which element of three (a, b, d) is in-between the other two. + /// If two or more elements are equal, the first (e.g. a before b) is chosen. + /// + /// Complexity: Either two or three comparisons will be required, depending + /// on the values. + /// + template + inline T&& median(T&& a, T&& b, T&& c, Compare compare) + { + return eastl::forward(median_impl(eastl::forward(a), eastl::forward(b), eastl::forward(c), compare)); + } + + + + + /// all_of + /// + /// Returns: true if the unary predicate p returns true for all elements in the range [first, last) + /// + template + inline bool all_of(InputIterator first, InputIterator last, Predicate p) + { + for(; first != last; ++first) + { + if(!p(*first)) + return false; + } + return true; + } + + + /// any_of + /// + /// Returns: true if the unary predicate p returns true for any of the elements in the range [first, last) + /// + template + inline bool any_of(InputIterator first, InputIterator last, Predicate p) + { + for(; first != last; ++first) + { + if(p(*first)) + return true; + } + return false; + } + + + /// none_of + /// + /// Returns: true if the unary predicate p returns true for none of the elements in the range [first, last) + /// + template + inline bool none_of(InputIterator first, InputIterator last, Predicate p) + { + for(; first != last; ++first) + { + if(p(*first)) + return false; + } + return true; + } + + + /// adjacent_find + /// + /// Returns: The first iterator i such that both i and i + 1 are in the range + /// [first, last) for which the following corresponding conditions hold: *i == *(i + 1). + /// Returns last if no such iterator is found. + /// + /// Complexity: Exactly 'find(first, last, value) - first' applications of the corresponding predicate. + /// + template + inline ForwardIterator + adjacent_find(ForwardIterator first, ForwardIterator last) + { + if(first != last) + { + ForwardIterator i = first; + + for(++i; i != last; ++i) + { + if(*first == *i) + return first; + first = i; + } + } + return last; + } + + + + /// adjacent_find + /// + /// Returns: The first iterator i such that both i and i + 1 are in the range + /// [first, last) for which the following corresponding conditions hold: predicate(*i, *(i + 1)) != false. + /// Returns last if no such iterator is found. + /// + /// Complexity: Exactly 'find(first, last, value) - first' applications of the corresponding predicate. + /// + template + inline ForwardIterator + adjacent_find(ForwardIterator first, ForwardIterator last, BinaryPredicate predicate) + { + if(first != last) + { + ForwardIterator i = first; + + for(++i; i != last; ++i) + { + if(predicate(*first, *i)) + return first; + first = i; + } + } + return last; + } + + + /// shuffle + /// + /// New for C++11 + /// Randomizes a sequence of values via a user-supplied UniformRandomNumberGenerator. + /// The difference between this and the original random_shuffle function is that this uses the more + /// advanced and flexible UniformRandomNumberGenerator interface as opposed to the more + /// limited RandomNumberGenerator interface of random_shuffle. + /// + /// Effects: Shuffles the elements in the range [first, last) with uniform distribution. + /// + /// Complexity: Exactly '(last - first) - 1' swaps. + /// + /// Example usage: + /// struct Rand{ eastl_size_t operator()(eastl_size_t n) { return (eastl_size_t)(rand() % n); } }; // Note: The C rand function is poor and slow. + /// Rand randInstance; + /// shuffle(pArrayBegin, pArrayEnd, randInstance); + /// + // See the C++11 Standard, 26.5.1.3, Uniform random number generator requirements. + // Also http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution + + template + void shuffle(RandomAccessIterator first, RandomAccessIterator last, UniformRandomNumberGenerator&& urng) + { + if(first != last) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::make_unsigned::type unsigned_difference_type; + typedef typename eastl::uniform_int_distribution uniform_int_distribution; + typedef typename uniform_int_distribution::param_type uniform_int_distribution_param_type; + + uniform_int_distribution uid; + + for(RandomAccessIterator i = first + 1; i != last; ++i) + iter_swap(i, first + uid(urng, uniform_int_distribution_param_type(0, i - first))); + } + } + + + /// random_shuffle + /// + /// Randomizes a sequence of values. + /// + /// Effects: Shuffles the elements in the range [first, last) with uniform distribution. + /// + /// Complexity: Exactly '(last - first) - 1' swaps. + /// + /// Example usage: + /// eastl_size_t Rand(eastl_size_t n) { return (eastl_size_t)(rand() % n); } // Note: The C rand function is poor and slow. + /// pointer_to_unary_function randInstance(Rand); + /// random_shuffle(pArrayBegin, pArrayEnd, randInstance); + /// + /// Example usage: + /// struct Rand{ eastl_size_t operator()(eastl_size_t n) { return (eastl_size_t)(rand() % n); } }; // Note: The C rand function is poor and slow. + /// Rand randInstance; + /// random_shuffle(pArrayBegin, pArrayEnd, randInstance); + /// + template + inline void random_shuffle(RandomAccessIterator first, RandomAccessIterator last, RandomNumberGenerator&& rng) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + + // We must do 'rand((i - first) + 1)' here and cannot do 'rand(last - first)', + // as it turns out that the latter results in unequal distribution probabilities. + // http://www.cigital.com/papers/download/developer_gambling.php + + for(RandomAccessIterator i = first + 1; i < last; ++i) + iter_swap(i, first + (difference_type)rng((eastl_size_t)((i - first) + 1))); + } + + + /// random_shuffle + /// + /// Randomizes a sequence of values. + /// + /// Effects: Shuffles the elements in the range [first, last) with uniform distribution. + /// + /// Complexity: Exactly '(last - first) - 1' swaps. + /// + /// Example usage: + /// random_shuffle(pArrayBegin, pArrayEnd); + /// + /// *** Disabled until we decide if we want to get into the business of writing random number generators. *** + /// + /// template + /// inline void random_shuffle(RandomAccessIterator first, RandomAccessIterator last) + /// { + /// for(RandomAccessIterator i = first + 1; i < last; ++i) + /// iter_swap(i, first + SomeRangedRandomNumberGenerator((i - first) + 1)); + /// } + + + + + + + /// move_n + /// + /// Same as move(InputIterator, InputIterator, OutputIterator) except based on count instead of iterator range. + /// + template + inline OutputIterator + move_n_impl(InputIterator first, Size n, OutputIterator result, EASTL_ITC_NS::input_iterator_tag) + { + for(; n > 0; --n) + *result++ = eastl::move(*first++); + return result; + } + + template + inline OutputIterator + move_n_impl(RandomAccessIterator first, Size n, OutputIterator result, EASTL_ITC_NS::random_access_iterator_tag) + { + return eastl::move(first, first + n, result); // Take advantage of the optimizations present in the move algorithm. + } + + + template + inline OutputIterator + move_n(InputIterator first, Size n, OutputIterator result) + { + typedef typename eastl::iterator_traits::iterator_category IC; + return eastl::move_n_impl(first, n, result, IC()); + } + + + + /// copy_n + /// + /// Same as copy(InputIterator, InputIterator, OutputIterator) except based on count instead of iterator range. + /// Effects: Copies exactly count values from the range beginning at first to the range beginning at result, if count > 0. Does nothing otherwise. + /// Returns: Iterator in the destination range, pointing past the last element copied if count>0 or first otherwise. + /// Complexity: Exactly count assignments, if count > 0. + /// + template + inline OutputIterator + copy_n_impl(InputIterator first, Size n, OutputIterator result, EASTL_ITC_NS::input_iterator_tag) + { + for(; n > 0; --n) + *result++ = *first++; + return result; + } + + template + inline OutputIterator + copy_n_impl(RandomAccessIterator first, Size n, OutputIterator result, EASTL_ITC_NS::random_access_iterator_tag) + { + return eastl::copy(first, first + n, result); // Take advantage of the optimizations present in the copy algorithm. + } + + + template + inline OutputIterator + copy_n(InputIterator first, Size n, OutputIterator result) + { + typedef typename eastl::iterator_traits::iterator_category IC; + return eastl::copy_n_impl(first, n, result, IC()); + } + + + /// copy_if + /// + /// Effects: Assigns to the result iterator only if the predicate is true. + /// + template + inline OutputIterator + copy_if(InputIterator first, InputIterator last, OutputIterator result, Predicate predicate) + { + // This implementation's performance could be improved by taking a more complicated approach like with the copy algorithm. + for(; first != last; ++first) + { + if(predicate(*first)) + *result++ = *first; + } + + return result; + } + + + + + // Implementation moving copying both trivial and non-trivial data via a lesser iterator than random-access. + template + struct move_and_copy_backward_helper + { + template + static BidirectionalIterator2 move_or_copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + { + while(first != last) + *--resultEnd = *--last; + return resultEnd; // resultEnd now points to the beginning of the destination sequence instead of the end. + } + }; + + // Specialization for moving non-trivial data via a lesser iterator than random-access. + template + struct move_and_copy_backward_helper + { + template + static BidirectionalIterator2 move_or_copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + { + while(first != last) + *--resultEnd = eastl::move(*--last); + return resultEnd; // resultEnd now points to the beginning of the destination sequence instead of the end. + } + }; + + // Specialization for moving non-trivial data via a random-access iterator. It's theoretically faster because the compiler can see the count when its a compile-time const. + template<> + struct move_and_copy_backward_helper + { + template + static BidirectionalIterator2 move_or_copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + + for(difference_type n = (last - first); n > 0; --n) + *--resultEnd = eastl::move(*--last); + return resultEnd; // resultEnd now points to the beginning of the destination sequence instead of the end. + } + }; + + // Specialization for copying non-trivial data via a random-access iterator. It's theoretically faster because the compiler can see the count when its a compile-time const. + // This specialization converts the random access BidirectionalIterator1 last-first to an integral type. There's simple way for us to take advantage of a random access output iterator, + // as the range is specified by the input instead of the output, and distance(first, last) for a non-random-access iterator is potentially slow. + template <> + struct move_and_copy_backward_helper + { + template + static BidirectionalIterator2 move_or_copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + + for(difference_type n = (last - first); n > 0; --n) + *--resultEnd = *--last; + return resultEnd; // resultEnd now points to the beginning of the destination sequence instead of the end. + } + }; + + // Specialization for when we can use memmove/memcpy. See the notes above for what conditions allow this. + template + struct move_and_copy_backward_helper + { + template + static T* move_or_copy_backward(const T* first, const T* last, T* resultEnd) + { + return (T*)memmove(resultEnd - (last - first), first, (size_t)((uintptr_t)last - (uintptr_t)first)); + // We could use memcpy here if there's no range overlap, but memcpy is rarely much faster than memmove. + } + }; + + template + inline BidirectionalIterator2 move_and_copy_backward_chooser(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + { + typedef typename eastl::iterator_traits::iterator_category IIC; + typedef typename eastl::iterator_traits::iterator_category OIC; + typedef typename eastl::iterator_traits::value_type value_type_input; + typedef typename eastl::iterator_traits::value_type value_type_output; + + const bool canBeMemmoved = eastl::is_trivially_copyable::value && + eastl::is_same::value && + (eastl::is_pointer::value || eastl::is_same::value) && + (eastl::is_pointer::value || eastl::is_same::value); + + return eastl::move_and_copy_backward_helper::move_or_copy_backward(first, last, resultEnd); // Need to chose based on the input iterator tag and not the output iterator tag, because containers accept input ranges of iterator types different than self. + } + + + // We have a second layer of unwrap_iterator calls because the original iterator might be something like move_iterator > (i.e. doubly-wrapped). + template + inline BidirectionalIterator2 move_and_copy_backward_unwrapper(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + { + return BidirectionalIterator2(eastl::move_and_copy_backward_chooser(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), eastl::unwrap_iterator(resultEnd))); // Have to convert to BidirectionalIterator2 because result.base() could be a T* + } + + + /// move_backward + /// + /// The elements are moved in reverse order (the last element is moved first), but their relative order is preserved. + /// After this operation the elements in the moved-from range will still contain valid values of the + /// appropriate type, but not necessarily the same values as before the move. + /// Returns the beginning of the result range. + /// Note: When moving between containers, the dest range must be valid; this function doesn't resize containers. + /// Note: If result is within [first, last), move must be used instead of move_backward. + /// + /// Example usage: + /// eastl::move_backward(myArray.begin(), myArray.end(), myDestArray.end()); + /// + /// Reference implementation: + /// template + /// BidirectionalIterator2 move_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + /// { + /// while(last != first) + /// *--resultEnd = eastl::move(*--last); + /// return resultEnd; + /// } + /// + template + inline BidirectionalIterator2 move_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + { + return eastl::move_and_copy_backward_unwrapper(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), resultEnd); + } + + + /// copy_backward + /// + /// copies memory in the range of [first, last) to the range *ending* with result. + /// + /// Effects: Copies elements in the range [first, last) into the range + /// [result - (last - first), result) starting from last 1 and proceeding to first. + /// For each positive integer n <= (last - first), performs *(result n) = *(last - n). + /// + /// Requires: result shall not be in the range [first, last). + /// + /// Returns: result - (last - first). That is, returns the beginning of the result range. + /// + /// Complexity: Exactly 'last - first' assignments. + /// + template + inline BidirectionalIterator2 copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last, BidirectionalIterator2 resultEnd) + { + const bool isMove = eastl::is_move_iterator::value; EA_UNUSED(isMove); + + return eastl::move_and_copy_backward_unwrapper(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), resultEnd); + } + + + /// count + /// + /// Counts the number of items in the range of [first, last) which equal the input value. + /// + /// Effects: Returns the number of iterators i in the range [first, last) for which the + /// following corresponding conditions hold: *i == value. + /// + /// Complexity: At most 'last - first' applications of the corresponding predicate. + /// + /// Note: The predicate version of count is count_if and not another variation of count. + /// This is because both versions would have three parameters and there could be ambiguity. + /// + template + inline typename eastl::iterator_traits::difference_type + count(InputIterator first, InputIterator last, const T& value) + { + typename eastl::iterator_traits::difference_type result = 0; + + for(; first != last; ++first) + { + if(*first == value) + ++result; + } + return result; + } + + + // C++ doesn't define a count with predicate, as it can effectively be synthesized via count_if + // with an appropriate predicate. However, it's often simpler to just have count with a predicate. + template + inline typename eastl::iterator_traits::difference_type + count(InputIterator first, InputIterator last, const T& value, Predicate predicate) + { + typename eastl::iterator_traits::difference_type result = 0; + + for(; first != last; ++first) + { + if(predicate(*first, value)) + ++result; + } + return result; + } + + + /// count_if + /// + /// Counts the number of items in the range of [first, last) which match + /// the input value as defined by the input predicate function. + /// + /// Effects: Returns the number of iterators i in the range [first, last) for which the + /// following corresponding conditions hold: predicate(*i) != false. + /// + /// Complexity: At most 'last - first' applications of the corresponding predicate. + /// + /// Note: The non-predicate version of count_if is count and not another variation of count_if. + /// This is because both versions would have three parameters and there could be ambiguity. + /// + template + inline typename eastl::iterator_traits::difference_type + count_if(InputIterator first, InputIterator last, Predicate predicate) + { + typename eastl::iterator_traits::difference_type result = 0; + + for(; first != last; ++first) + { + if(predicate(*first)) + ++result; + } + return result; + } + + + /// find + /// + /// finds the value within the unsorted range of [first, last). + /// + /// Returns: The first iterator i in the range [first, last) for which + /// the following corresponding conditions hold: *i == value. + /// Returns last if no such iterator is found. + /// + /// Complexity: At most 'last - first' applications of the corresponding predicate. + /// This is a linear search and not a binary one. + /// + /// Note: The predicate version of find is find_if and not another variation of find. + /// This is because both versions would have three parameters and there could be ambiguity. + /// + template + inline InputIterator + find(InputIterator first, InputIterator last, const T& value) + { + while((first != last) && !(*first == value)) // Note that we always express value comparisons in terms of < or ==. + ++first; + return first; + } + + + // C++ doesn't define a find with predicate, as it can effectively be synthesized via find_if + // with an appropriate predicate. However, it's often simpler to just have find with a predicate. + template + inline InputIterator + find(InputIterator first, InputIterator last, const T& value, Predicate predicate) + { + while((first != last) && !predicate(*first, value)) + ++first; + return first; + } + + + + /// find_if + /// + /// finds the value within the unsorted range of [first, last). + /// + /// Returns: The first iterator i in the range [first, last) for which + /// the following corresponding conditions hold: pred(*i) != false. + /// Returns last if no such iterator is found. + /// If the sequence of elements to search for (i.e. first2 - last2) is empty, + /// the find always fails and last1 will be returned. + /// + /// Complexity: At most 'last - first' applications of the corresponding predicate. + /// + /// Note: The non-predicate version of find_if is find and not another variation of find_if. + /// This is because both versions would have three parameters and there could be ambiguity. + /// + template + inline InputIterator + find_if(InputIterator first, InputIterator last, Predicate predicate) + { + while((first != last) && !predicate(*first)) + ++first; + return first; + } + + + + /// find_if_not + /// + /// find_if_not works the same as find_if except it tests for if the predicate + /// returns false for the elements instead of true. + /// + template + inline InputIterator + find_if_not(InputIterator first, InputIterator last, Predicate predicate) + { + for(; first != last; ++first) + { + if(!predicate(*first)) + return first; + } + return last; + } + + + + + /// find_first_of + /// + /// find_first_of is similar to find in that it performs linear search through + /// a range of ForwardIterators. The difference is that while find searches + /// for one particular value, find_first_of searches for any of several values. + /// Specifically, find_first_of searches for the first occurrance in the + /// range [first1, last1) of any of the elements in [first2, last2). + /// This function is thus similar to the strpbrk standard C string function. + /// If the sequence of elements to search for (i.e. first2-last2) is empty, + /// the find always fails and last1 will be returned. + /// + /// Effects: Finds an element that matches one of a set of values. + /// + /// Returns: The first iterator i in the range [first1, last1) such that for some + /// integer j in the range [first2, last2) the following conditions hold: *i == *j. + /// Returns last1 if no such iterator is found. + /// + /// Complexity: At most '(last1 - first1) * (last2 - first2)' applications of the + /// corresponding predicate. + /// + template + ForwardIterator1 + find_first_of(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2) + { + for(; first1 != last1; ++first1) + { + for(ForwardIterator2 i = first2; i != last2; ++i) + { + if(*first1 == *i) + return first1; + } + } + return last1; + } + + + /// find_first_of + /// + /// find_first_of is similar to find in that it performs linear search through + /// a range of ForwardIterators. The difference is that while find searches + /// for one particular value, find_first_of searches for any of several values. + /// Specifically, find_first_of searches for the first occurrance in the + /// range [first1, last1) of any of the elements in [first2, last2). + /// This function is thus similar to the strpbrk standard C string function. + /// + /// Effects: Finds an element that matches one of a set of values. + /// + /// Returns: The first iterator i in the range [first1, last1) such that for some + /// integer j in the range [first2, last2) the following conditions hold: pred(*i, *j) != false. + /// Returns last1 if no such iterator is found. + /// + /// Complexity: At most '(last1 - first1) * (last2 - first2)' applications of the + /// corresponding predicate. + /// + template + ForwardIterator1 + find_first_of(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + BinaryPredicate predicate) + { + for(; first1 != last1; ++first1) + { + for(ForwardIterator2 i = first2; i != last2; ++i) + { + if(predicate(*first1, *i)) + return first1; + } + } + return last1; + } + + + /// find_first_not_of + /// + /// Searches through first range for the first element that does not belong the second input range. + /// This is very much like the C++ string find_first_not_of function. + /// + /// Returns: The first iterator i in the range [first1, last1) such that for some + /// integer j in the range [first2, last2) the following conditions hold: !(*i == *j). + /// Returns last1 if no such iterator is found. + /// + /// Complexity: At most '(last1 - first1) * (last2 - first2)' applications of the + /// corresponding predicate. + /// + template + ForwardIterator1 + find_first_not_of(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2) + { + for(; first1 != last1; ++first1) + { + if(eastl::find(first2, last2, *first1) == last2) + break; + } + + return first1; + } + + + + /// find_first_not_of + /// + /// Searches through first range for the first element that does not belong the second input range. + /// This is very much like the C++ string find_first_not_of function. + /// + /// Returns: The first iterator i in the range [first1, last1) such that for some + /// integer j in the range [first2, last2) the following conditions hold: pred(*i, *j) == false. + /// Returns last1 if no such iterator is found. + /// + /// Complexity: At most '(last1 - first1) * (last2 - first2)' applications of the + /// corresponding predicate. + /// + template + inline ForwardIterator1 + find_first_not_of(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + BinaryPredicate predicate) + { + typedef typename eastl::iterator_traits::value_type value_type; + + for(; first1 != last1; ++first1) + { + if(eastl::find_if(first2, last2, eastl::bind1st(predicate, *first1)) == last2) + break; + } + + return first1; + } + + + template + inline BidirectionalIterator1 + find_last_of(BidirectionalIterator1 first1, BidirectionalIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2) + { + if((first1 != last1) && (first2 != last2)) + { + BidirectionalIterator1 it1(last1); + + while((--it1 != first1) && (eastl::find(first2, last2, *it1) == last2)) + ; // Do nothing + + if((it1 != first1) || (eastl::find(first2, last2, *it1) != last2)) + return it1; + } + + return last1; + } + + + template + BidirectionalIterator1 + find_last_of(BidirectionalIterator1 first1, BidirectionalIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + BinaryPredicate predicate) + { + typedef typename eastl::iterator_traits::value_type value_type; + + if((first1 != last1) && (first2 != last2)) + { + BidirectionalIterator1 it1(last1); + + while((--it1 != first1) && (eastl::find_if(first2, last2, eastl::bind1st(predicate, *it1)) == last2)) + ; // Do nothing + + if((it1 != first1) || (eastl::find_if(first2, last2, eastl::bind1st(predicate, *it1)) != last2)) + return it1; + } + + return last1; + } + + + template + inline BidirectionalIterator1 + find_last_not_of(BidirectionalIterator1 first1, BidirectionalIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2) + { + if((first1 != last1) && (first2 != last2)) + { + BidirectionalIterator1 it1(last1); + + while((--it1 != first1) && (eastl::find(first2, last2, *it1) != last2)) + ; // Do nothing + + if((it1 != first1) || (eastl::find( first2, last2, *it1) == last2)) + return it1; + } + + return last1; + } + + + template + inline BidirectionalIterator1 + find_last_not_of(BidirectionalIterator1 first1, BidirectionalIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + BinaryPredicate predicate) + { + typedef typename eastl::iterator_traits::value_type value_type; + + if((first1 != last1) && (first2 != last2)) + { + BidirectionalIterator1 it1(last1); + + while((--it1 != first1) && (eastl::find_if(first2, last2, eastl::bind1st(predicate, *it1)) != last2)) + ; // Do nothing + + if((it1 != first1) || (eastl::find_if(first2, last2, eastl::bind1st(predicate, *it1))) != last2) + return it1; + } + + return last1; + } + + + + + /// for_each + /// + /// Calls the Function function for each value in the range [first, last). + /// Function takes a single parameter: the current value. + /// + /// Effects: Applies function to the result of dereferencing every iterator in + /// the range [first, last), starting from first and proceeding to last 1. + /// + /// Returns: function. + /// + /// Complexity: Applies function exactly 'last - first' times. + /// + /// Note: If function returns a result, the result is ignored. + /// + template + inline Function + for_each(InputIterator first, InputIterator last, Function function) + { + for(; first != last; ++first) + function(*first); + return function; + } + + /// for_each_n + /// + /// Calls the Function function for each value in the range [first, first + n). + /// Function takes a single parameter: the current value. + /// + /// Effects: Applies function to the result of dereferencing every iterator in + /// the range [first, first + n), starting from first and proceeding to last 1. + /// + /// Returns: first + n. + /// + /// Complexity: Applies function exactly 'first + n' times. + /// + /// Note: + //// * If function returns a result, the result is ignored. + //// * If n < 0, behaviour is undefined. + /// + template + EA_CPP14_CONSTEXPR inline InputIterator + for_each_n(InputIterator first, Size n, Function function) + { + for (Size i = 0; i < n; ++first, i++) + function(*first); + return first; + } + + + /// generate + /// + /// Iterates the range of [first, last) and assigns to each element the + /// result of the function generator. Generator is a function which takes + /// no arguments. + /// + /// Complexity: Exactly 'last - first' invocations of generator and assignments. + /// + template + inline void + generate(ForwardIterator first, ForwardIterator last, Generator generator) + { + for(; first != last; ++first) // We cannot call generate_n(first, last-first, generator) + *first = generator(); // because the 'last-first' might not be supported by the + } // given iterator. + + + /// generate_n + /// + /// Iterates an interator n times and assigns the result of generator + /// to each succeeding element. Generator is a function which takes + /// no arguments. + /// + /// Complexity: Exactly n invocations of generator and assignments. + /// + template + inline OutputIterator + generate_n(OutputIterator first, Size n, Generator generator) + { + for(; n > 0; --n, ++first) + *first = generator(); + return first; + } + + + /// transform + /// + /// Iterates the input range of [first, last) and the output iterator result + /// and assigns the result of unaryOperation(input) to result. + /// + /// Effects: Assigns through every iterator i in the range [result, result + (last1 - first1)) + /// a new corresponding value equal to unaryOperation(*(first1 + (i - result)). + /// + /// Requires: op shall not have any side effects. + /// + /// Returns: result + (last1 - first1). That is, returns the end of the output range. + /// + /// Complexity: Exactly 'last1 - first1' applications of unaryOperation. + /// + /// Note: result may be equal to first. + /// + template + inline OutputIterator + transform(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation unaryOperation) + { + for(; first != last; ++first, ++result) + *result = unaryOperation(*first); + return result; + } + + + /// transform + /// + /// Iterates the input range of [first, last) and the output iterator result + /// and assigns the result of binaryOperation(input1, input2) to result. + /// + /// Effects: Assigns through every iterator i in the range [result, result + (last1 - first1)) + /// a new corresponding value equal to binaryOperation(*(first1 + (i - result), *(first2 + (i - result))). + /// + /// Requires: binaryOperation shall not have any side effects. + /// + /// Returns: result + (last1 - first1). That is, returns the end of the output range. + /// + /// Complexity: Exactly 'last1 - first1' applications of binaryOperation. + /// + /// Note: result may be equal to first1 or first2. + /// + template + inline OutputIterator + transform(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, BinaryOperation binaryOperation) + { + for(; first1 != last1; ++first1, ++first2, ++result) + *result = binaryOperation(*first1, *first2); + return result; + } + + + /// equal + /// + /// Returns: true if for every iterator i in the range [first1, last1) the + /// following corresponding conditions hold: predicate(*i, *(first2 + (i - first1))) != false. + /// Otherwise, returns false. + /// + /// Complexity: At most last1 first1 applications of the corresponding predicate. + /// + /// To consider: Make specializations of this for scalar types and random access + /// iterators that uses memcmp or some trick memory comparison function. + /// We should verify that such a thing results in an improvement. + /// + template + EA_CPP14_CONSTEXPR inline bool equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2) + { + for(; first1 != last1; ++first1, ++first2) + { + if(!(*first1 == *first2)) // Note that we always express value comparisons in terms of < or ==. + return false; + } + return true; + } + + /* Enable the following if there was shown to be some benefit. A glance and Microsoft VC++ memcmp + shows that it is not optimized in any way, much less one that would benefit us here. + + inline bool equal(const bool* first1, const bool* last1, const bool* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + + inline bool equal(const char* first1, const char* last1, const char* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + + inline bool equal(const unsigned char* first1, const unsigned char* last1, const unsigned char* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + + inline bool equal(const signed char* first1, const signed char* last1, const signed char* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + + #ifndef EA_WCHAR_T_NON_NATIVE + inline bool equal(const wchar_t* first1, const wchar_t* last1, const wchar_t* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + #endif + + inline bool equal(const int16_t* first1, const int16_t* last1, const int16_t* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + + inline bool equal(const uint16_t* first1, const uint16_t* last1, const uint16_t* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + + inline bool equal(const int32_t* first1, const int32_t* last1, const int32_t* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + + inline bool equal(const uint32_t* first1, const uint32_t* last1, const uint32_t* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + + inline bool equal(const int64_t* first1, const int64_t* last1, const int64_t* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + + inline bool equal(const uint64_t* first1, const uint64_t* last1, const uint64_t* first2) + { return (memcmp(first1, first2, (size_t)((uintptr_t)last1 - (uintptr_t)first1)) == 0); } + */ + + + + /// equal + /// + /// Returns: true if for every iterator i in the range [first1, last1) the + /// following corresponding conditions hold: pred(*i, *(first2 + (i first1))) != false. + /// Otherwise, returns false. + /// + /// Complexity: At most last1 first1 applications of the corresponding predicate. + /// + template + inline bool + equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, BinaryPredicate predicate) + { + for(; first1 != last1; ++first1, ++first2) + { + if(!predicate(*first1, *first2)) + return false; + } + return true; + } + + + + /// identical + /// + /// Returns true if the two input ranges are equivalent. + /// There is a subtle difference between this algorithm and + /// the 'equal' algorithm. The equal algorithm assumes the + /// two ranges are of equal length. This algorithm efficiently + /// compares two ranges for both length equality and for + /// element equality. There is no other standard algorithm + /// that can do this. + /// + /// Returns: true if the sequence of elements defined by the range + /// [first1, last1) is of the same length as the sequence of + /// elements defined by the range of [first2, last2) and if + /// the elements in these ranges are equal as per the + /// equal algorithm. + /// + /// Complexity: At most 'min((last1 - first1), (last2 - first2))' applications + /// of the corresponding comparison. + /// + template + bool identical(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2) + { + while((first1 != last1) && (first2 != last2) && (*first1 == *first2)) + { + ++first1; + ++first2; + } + return (first1 == last1) && (first2 == last2); + } + + + /// identical + /// + template + bool identical(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, BinaryPredicate predicate) + { + while((first1 != last1) && (first2 != last2) && predicate(*first1, *first2)) + { + ++first1; + ++first2; + } + return (first1 == last1) && (first2 == last2); + } + + + + /// lexicographical_compare + /// + /// Returns: true if the sequence of elements defined by the range + /// [first1, last1) is lexicographically less than the sequence of + /// elements defined by the range [first2, last2). Returns false otherwise. + /// + /// Complexity: At most 'min((last1 - first1), (last2 - first2))' applications + /// of the corresponding comparison. + /// + /// Note: If two sequences have the same number of elements and their + /// corresponding elements are equivalent, then neither sequence is + /// lexicographically less than the other. If one sequence is a prefix + /// of the other, then the shorter sequence is lexicographically less + /// than the longer sequence. Otherwise, the lexicographical comparison + /// of the sequences yields the same result as the comparison of the first + /// corresponding pair of elements that are not equivalent. + /// + template + inline bool + lexicographical_compare(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) + { + for(; (first1 != last1) && (first2 != last2); ++first1, ++first2) + { + if(*first1 < *first2) + return true; + if(*first2 < *first1) + return false; + } + return (first1 == last1) && (first2 != last2); + } + + inline bool // Specialization for const char*. + lexicographical_compare(const char* first1, const char* last1, const char* first2, const char* last2) + { + const ptrdiff_t n1(last1 - first1), n2(last2 - first2); + const int result = memcmp(first1, first2, (size_t)eastl::min_alt(n1, n2)); + return result ? (result < 0) : (n1 < n2); + } + + inline bool // Specialization for char*. + lexicographical_compare(char* first1, char* last1, char* first2, char* last2) + { + const ptrdiff_t n1(last1 - first1), n2(last2 - first2); + const int result = memcmp(first1, first2, (size_t)eastl::min_alt(n1, n2)); + return result ? (result < 0) : (n1 < n2); + } + + inline bool // Specialization for const unsigned char*. + lexicographical_compare(const unsigned char* first1, const unsigned char* last1, const unsigned char* first2, const unsigned char* last2) + { + const ptrdiff_t n1(last1 - first1), n2(last2 - first2); + const int result = memcmp(first1, first2, (size_t)eastl::min_alt(n1, n2)); + return result ? (result < 0) : (n1 < n2); + } + + inline bool // Specialization for unsigned char*. + lexicographical_compare(unsigned char* first1, unsigned char* last1, unsigned char* first2, unsigned char* last2) + { + const ptrdiff_t n1(last1 - first1), n2(last2 - first2); + const int result = memcmp(first1, first2, (size_t)eastl::min_alt(n1, n2)); + return result ? (result < 0) : (n1 < n2); + } + + inline bool // Specialization for const signed char*. + lexicographical_compare(const signed char* first1, const signed char* last1, const signed char* first2, const signed char* last2) + { + const ptrdiff_t n1(last1 - first1), n2(last2 - first2); + const int result = memcmp(first1, first2, (size_t)eastl::min_alt(n1, n2)); + return result ? (result < 0) : (n1 < n2); + } + + inline bool // Specialization for signed char*. + lexicographical_compare(signed char* first1, signed char* last1, signed char* first2, signed char* last2) + { + const ptrdiff_t n1(last1 - first1), n2(last2 - first2); + const int result = memcmp(first1, first2, (size_t)eastl::min_alt(n1, n2)); + return result ? (result < 0) : (n1 < n2); + } + + #if defined(_MSC_VER) // If using the VC++ compiler (and thus bool is known to be a single byte)... + //Not sure if this is a good idea. + //inline bool // Specialization for const bool*. + //lexicographical_compare(const bool* first1, const bool* last1, const bool* first2, const bool* last2) + //{ + // const ptrdiff_t n1(last1 - first1), n2(last2 - first2); + // const int result = memcmp(first1, first2, (size_t)eastl::min_alt(n1, n2)); + // return result ? (result < 0) : (n1 < n2); + //} + // + //inline bool // Specialization for bool*. + //lexicographical_compare(bool* first1, bool* last1, bool* first2, bool* last2) + //{ + // const ptrdiff_t n1(last1 - first1), n2(last2 - first2); + // const int result = memcmp(first1, first2, (size_t)eastl::min_alt(n1, n2)); + // return result ? (result < 0) : (n1 < n2); + //} + #endif + + + + /// lexicographical_compare + /// + /// Returns: true if the sequence of elements defined by the range + /// [first1, last1) is lexicographically less than the sequence of + /// elements defined by the range [first2, last2). Returns false otherwise. + /// + /// Complexity: At most 'min((last1 -first1), (last2 - first2))' applications + /// of the corresponding comparison. + /// + /// Note: If two sequences have the same number of elements and their + /// corresponding elements are equivalent, then neither sequence is + /// lexicographically less than the other. If one sequence is a prefix + /// of the other, then the shorter sequence is lexicographically less + /// than the longer sequence. Otherwise, the lexicographical comparison + /// of the sequences yields the same result as the comparison of the first + /// corresponding pair of elements that are not equivalent. + /// + /// Note: False is always returned if range 1 is exhausted before range 2. + /// The result of this is that you can't do a successful reverse compare + /// (e.g. use greater<> as the comparison instead of less<>) unless the + /// two sequences are of identical length. What you want to do is reverse + /// the order of the arguments in order to get the desired effect. + /// + template + inline bool + lexicographical_compare(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, Compare compare) + { + for(; (first1 != last1) && (first2 != last2); ++first1, ++first2) + { + if(compare(*first1, *first2)) + return true; + if(compare(*first2, *first1)) + return false; + } + return (first1 == last1) && (first2 != last2); + } + + + /// mismatch + /// + /// Finds the first position where the two ranges [first1, last1) and + /// [first2, first2 + (last1 - first1)) differ. The two versions of + /// mismatch use different tests for whether elements differ. + /// + /// Returns: A pair of iterators i and j such that j == first2 + (i - first1) + /// and i is the first iterator in the range [first1, last1) for which the + /// following corresponding condition holds: !(*i == *(first2 + (i - first1))). + /// Returns the pair last1 and first2 + (last1 - first1) if such an iterator + /// i is not found. + /// + /// Complexity: At most last1 first1 applications of the corresponding predicate. + /// + template + inline eastl::pair + mismatch(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2) // , InputIterator2 last2) + { + while((first1 != last1) && (*first1 == *first2)) // && (first2 != last2) <- C++ standard mismatch function doesn't check first2/last2. + { + ++first1; + ++first2; + } + + return eastl::pair(first1, first2); + } + + + /// mismatch + /// + /// Finds the first position where the two ranges [first1, last1) and + /// [first2, first2 + (last1 - first1)) differ. The two versions of + /// mismatch use different tests for whether elements differ. + /// + /// Returns: A pair of iterators i and j such that j == first2 + (i - first1) + /// and i is the first iterator in the range [first1, last1) for which the + /// following corresponding condition holds: pred(*i, *(first2 + (i - first1))) == false. + /// Returns the pair last1 and first2 + (last1 - first1) if such an iterator + /// i is not found. + /// + /// Complexity: At most last1 first1 applications of the corresponding predicate. + /// + template + inline eastl::pair + mismatch(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, // InputIterator2 last2, + BinaryPredicate predicate) + { + while((first1 != last1) && predicate(*first1, *first2)) // && (first2 != last2) <- C++ standard mismatch function doesn't check first2/last2. + { + ++first1; + ++first2; + } + + return eastl::pair(first1, first2); + } + + + /// lower_bound + /// + /// Finds the position of the first element in a sorted range that has a value + /// greater than or equivalent to a specified value. + /// + /// Effects: Finds the first position into which value can be inserted without + /// violating the ordering. + /// + /// Returns: The furthermost iterator i in the range [first, last) such that + /// for any iterator j in the range [first, i) the following corresponding + /// condition holds: *j < value. + /// + /// Complexity: At most 'log(last - first) + 1' comparisons. + /// + /// Optimizations: We have no need to specialize this implementation for random + /// access iterators (e.g. contiguous array), as the code below will already + /// take advantage of them. + /// + template + ForwardIterator + lower_bound(ForwardIterator first, ForwardIterator last, const T& value) + { + typedef typename eastl::iterator_traits::difference_type DifferenceType; + + DifferenceType d = eastl::distance(first, last); // This will be efficient for a random access iterator such as an array. + + while(d > 0) + { + ForwardIterator i = first; + DifferenceType d2 = d >> 1; // We use '>>1' here instead of '/2' because MSVC++ for some reason generates significantly worse code for '/2'. Go figure. + + eastl::advance(i, d2); // This will be efficient for a random access iterator such as an array. + + if(*i < value) + { + // Disabled because std::lower_bound doesn't specify (23.3.3.3, p3) this can be done: EASTL_VALIDATE_COMPARE(!(value < *i)); // Validate that the compare function is sane. + first = ++i; + d -= d2 + 1; + } + else + d = d2; + } + return first; + } + + + /// lower_bound + /// + /// Finds the position of the first element in a sorted range that has a value + /// greater than or equivalent to a specified value. The input Compare function + /// takes two arguments and returns true if the first argument is less than + /// the second argument. + /// + /// Effects: Finds the first position into which value can be inserted without + /// violating the ordering. + /// + /// Returns: The furthermost iterator i in the range [first, last) such that + /// for any iterator j in the range [first, i) the following corresponding + /// condition holds: compare(*j, value) != false. + /// + /// Complexity: At most 'log(last - first) + 1' comparisons. + /// + /// Optimizations: We have no need to specialize this implementation for random + /// access iterators (e.g. contiguous array), as the code below will already + /// take advantage of them. + /// + template + ForwardIterator + lower_bound(ForwardIterator first, ForwardIterator last, const T& value, Compare compare) + { + typedef typename eastl::iterator_traits::difference_type DifferenceType; + + DifferenceType d = eastl::distance(first, last); // This will be efficient for a random access iterator such as an array. + + while(d > 0) + { + ForwardIterator i = first; + DifferenceType d2 = d >> 1; // We use '>>1' here instead of '/2' because MSVC++ for some reason generates significantly worse code for '/2'. Go figure. + + eastl::advance(i, d2); // This will be efficient for a random access iterator such as an array. + + if(compare(*i, value)) + { + // Disabled because std::lower_bound doesn't specify (23.3.3.1, p3) this can be done: EASTL_VALIDATE_COMPARE(!compare(value, *i)); // Validate that the compare function is sane. + first = ++i; + d -= d2 + 1; + } + else + d = d2; + } + return first; + } + + + + /// upper_bound + /// + /// Finds the position of the first element in a sorted range that has a + /// value that is greater than a specified value. + /// + /// Effects: Finds the furthermost position into which value can be inserted + /// without violating the ordering. + /// + /// Returns: The furthermost iterator i in the range [first, last) such that + /// for any iterator j in the range [first, i) the following corresponding + /// condition holds: !(value < *j). + /// + /// Complexity: At most 'log(last - first) + 1' comparisons. + /// + template + ForwardIterator + upper_bound(ForwardIterator first, ForwardIterator last, const T& value) + { + typedef typename eastl::iterator_traits::difference_type DifferenceType; + + DifferenceType len = eastl::distance(first, last); + + while(len > 0) + { + ForwardIterator i = first; + DifferenceType len2 = len >> 1; // We use '>>1' here instead of '/2' because MSVC++ for some reason generates significantly worse code for '/2'. Go figure. + + eastl::advance(i, len2); + + if(!(value < *i)) // Note that we always express value comparisons in terms of < or ==. + { + first = ++i; + len -= len2 + 1; + } + else + { + // Disabled because std::upper_bound doesn't specify (23.3.3.2, p3) this can be done: EASTL_VALIDATE_COMPARE(!(*i < value)); // Validate that the compare function is sane. + len = len2; + } + } + return first; + } + + + /// upper_bound + /// + /// Finds the position of the first element in a sorted range that has a + /// value that is greater than a specified value. The input Compare function + /// takes two arguments and returns true if the first argument is less than + /// the second argument. + /// + /// Effects: Finds the furthermost position into which value can be inserted + /// without violating the ordering. + /// + /// Returns: The furthermost iterator i in the range [first, last) such that + /// for any iterator j in the range [first, i) the following corresponding + /// condition holds: compare(value, *j) == false. + /// + /// Complexity: At most 'log(last - first) + 1' comparisons. + /// + template + ForwardIterator + upper_bound(ForwardIterator first, ForwardIterator last, const T& value, Compare compare) + { + typedef typename eastl::iterator_traits::difference_type DifferenceType; + + DifferenceType len = eastl::distance(first, last); + + while(len > 0) + { + ForwardIterator i = first; + DifferenceType len2 = len >> 1; // We use '>>1' here instead of '/2' because MSVC++ for some reason generates significantly worse code for '/2'. Go figure. + + eastl::advance(i, len2); + + if(!compare(value, *i)) + { + first = ++i; + len -= len2 + 1; + } + else + { + // Disabled because std::upper_bound doesn't specify (23.3.3.2, p3) this can be done: EASTL_VALIDATE_COMPARE(!compare(*i, value)); // Validate that the compare function is sane. + len = len2; + } + } + return first; + } + + + /// equal_range + /// + /// Effects: Finds the largest subrange [i, j) such that the value can be inserted + /// at any iterator k in it without violating the ordering. k satisfies the + /// corresponding conditions: !(*k < value) && !(value < *k). + /// + /// Complexity: At most '2 * log(last - first) + 1' comparisons. + /// + template + pair + equal_range(ForwardIterator first, ForwardIterator last, const T& value) + { + typedef pair ResultType; + typedef typename eastl::iterator_traits::difference_type DifferenceType; + + DifferenceType d = eastl::distance(first, last); + + while(d > 0) + { + ForwardIterator i(first); + DifferenceType d2 = d >> 1; // We use '>>1' here instead of '/2' because MSVC++ for some reason generates significantly worse code for '/2'. Go figure. + + eastl::advance(i, d2); + + if(*i < value) + { + EASTL_VALIDATE_COMPARE(!(value < *i)); // Validate that the compare function is sane. + first = ++i; + d -= d2 + 1; + } + else if(value < *i) + { + EASTL_VALIDATE_COMPARE(!(*i < value)); // Validate that the compare function is sane. + d = d2; + last = i; + } + else + { + ForwardIterator j(i); + + return ResultType(eastl::lower_bound(first, i, value), + eastl::upper_bound(++j, last, value)); + } + } + return ResultType(first, first); + } + + + /// equal_range + /// + /// Effects: Finds the largest subrange [i, j) such that the value can be inserted + /// at any iterator k in it without violating the ordering. k satisfies the + /// corresponding conditions: compare(*k, value) == false && compare(value, *k) == false. + /// + /// Complexity: At most '2 * log(last - first) + 1' comparisons. + /// + template + pair + equal_range(ForwardIterator first, ForwardIterator last, const T& value, Compare compare) + { + typedef pair ResultType; + typedef typename eastl::iterator_traits::difference_type DifferenceType; + + DifferenceType d = eastl::distance(first, last); + + while(d > 0) + { + ForwardIterator i(first); + DifferenceType d2 = d >> 1; // We use '>>1' here instead of '/2' because MSVC++ for some reason generates significantly worse code for '/2'. Go figure. + + eastl::advance(i, d2); + + if(compare(*i, value)) + { + EASTL_VALIDATE_COMPARE(!compare(value, *i)); // Validate that the compare function is sane. + first = ++i; + d -= d2 + 1; + } + else if(compare(value, *i)) + { + EASTL_VALIDATE_COMPARE(!compare(*i, value)); // Validate that the compare function is sane. + d = d2; + last = i; + } + else + { + ForwardIterator j(i); + + return ResultType(eastl::lower_bound(first, i, value, compare), + eastl::upper_bound(++j, last, value, compare)); + } + } + return ResultType(first, first); + } + + + /// replace + /// + /// Effects: Substitutes elements referred by the iterator i in the range [first, last) + /// with new_value, when the following corresponding conditions hold: *i == old_value. + /// + /// Complexity: Exactly 'last - first' applications of the corresponding predicate. + /// + /// Note: The predicate version of replace is replace_if and not another variation of replace. + /// This is because both versions would have the same parameter count and there could be ambiguity. + /// + template + inline void + replace(ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value) + { + for(; first != last; ++first) + { + if(*first == old_value) + *first = new_value; + } + } + + + /// replace_if + /// + /// Effects: Substitutes elements referred by the iterator i in the range [first, last) + /// with new_value, when the following corresponding conditions hold: predicate(*i) != false. + /// + /// Complexity: Exactly 'last - first' applications of the corresponding predicate. + /// + /// Note: The predicate version of replace_if is replace and not another variation of replace_if. + /// This is because both versions would have the same parameter count and there could be ambiguity. + /// + template + inline void + replace_if(ForwardIterator first, ForwardIterator last, Predicate predicate, const T& new_value) + { + for(; first != last; ++first) + { + if(predicate(*first)) + *first = new_value; + } + } + + + /// remove_copy + /// + /// Effects: Copies all the elements referred to by the iterator i in the range + /// [first, last) for which the following corresponding condition does not hold: + /// *i == value. + /// + /// Requires: The ranges [first, last) and [result, result + (last - first)) shall not overlap. + /// + /// Returns: The end of the resulting range. + /// + /// Complexity: Exactly 'last - first' applications of the corresponding predicate. + /// + template + inline OutputIterator + remove_copy(InputIterator first, InputIterator last, OutputIterator result, const T& value) + { + for(; first != last; ++first) + { + if(!(*first == value)) // Note that we always express value comparisons in terms of < or ==. + { + *result = eastl::move(*first); + ++result; + } + } + return result; + } + + + /// remove_copy_if + /// + /// Effects: Copies all the elements referred to by the iterator i in the range + /// [first, last) for which the following corresponding condition does not hold: + /// predicate(*i) != false. + /// + /// Requires: The ranges [first, last) and [result, result + (last - first)) shall not overlap. + /// + /// Returns: The end of the resulting range. + /// + /// Complexity: Exactly 'last - first' applications of the corresponding predicate. + /// + template + inline OutputIterator + remove_copy_if(InputIterator first, InputIterator last, OutputIterator result, Predicate predicate) + { + for(; first != last; ++first) + { + if(!predicate(*first)) + { + *result = eastl::move(*first); + ++result; + } + } + return result; + } + + + /// remove + /// + /// Effects: Eliminates all the elements referred to by iterator i in the + /// range [first, last) for which the following corresponding condition + /// holds: *i == value. + /// + /// Returns: The end of the resulting range. + /// + /// Complexity: Exactly 'last - first' applications of the corresponding predicate. + /// + /// Note: The predicate version of remove is remove_if and not another variation of remove. + /// This is because both versions would have the same parameter count and there could be ambiguity. + /// + /// 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. + /// + /// Example usage: + /// vector intArray; + /// ... + /// intArray.erase(remove(intArray.begin(), intArray.end(), 4), intArray.end()); // Erase all elements of value 4. + /// + template + inline ForwardIterator + remove(ForwardIterator first, ForwardIterator last, const T& value) + { + first = eastl::find(first, last, value); + if(first != last) + { + ForwardIterator i(first); + return eastl::remove_copy(++i, last, first, value); + } + return first; + } + + + /// remove_if + /// + /// Effects: Eliminates all the elements referred to by iterator i in the + /// range [first, last) for which the following corresponding condition + /// holds: predicate(*i) != false. + /// + /// Returns: The end of the resulting range. + /// + /// Complexity: Exactly 'last - first' applications of the corresponding predicate. + /// + /// Note: The predicate version of remove_if is remove and not another variation of remove_if. + /// This is because both versions would have the same parameter count and there could be ambiguity. + /// + /// 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. + /// + /// Example usage: + /// vector intArray; + /// ... + /// intArray.erase(remove(intArray.begin(), intArray.end(), bind2nd(less(), (int)3)), intArray.end()); // Erase all elements less than 3. + /// + template + inline ForwardIterator + remove_if(ForwardIterator first, ForwardIterator last, Predicate predicate) + { + first = eastl::find_if(first, last, predicate); + if(first != last) + { + ForwardIterator i(first); + return eastl::remove_copy_if(++i, last, first, predicate); + } + return first; + } + + + /// replace_copy + /// + /// Effects: Assigns to every iterator i in the range [result, result + (last - first)) + /// either new_value or *(first + (i - result)) depending on whether the following + /// corresponding conditions hold: *(first + (i - result)) == old_value. + /// + /// Requires: The ranges [first, last) and [result, result + (last - first)) shall not overlap. + /// + /// Returns: result + (last - first). + /// + /// Complexity: Exactly 'last - first' applications of the corresponding predicate. + /// + /// Note: The predicate version of replace_copy is replace_copy_if and not another variation of replace_copy. + /// This is because both versions would have the same parameter count and there could be ambiguity. + /// + template + inline OutputIterator + replace_copy(InputIterator first, InputIterator last, OutputIterator result, const T& old_value, const T& new_value) + { + for(; first != last; ++first, ++result) + *result = (*first == old_value) ? new_value : *first; + return result; + } + + + /// replace_copy_if + /// + /// Effects: Assigns to every iterator i in the range [result, result + (last - first)) + /// either new_value or *(first + (i - result)) depending on whether the following + /// corresponding conditions hold: predicate(*(first + (i - result))) != false. + /// + /// Requires: The ranges [first, last) and [result, result+(lastfirst)) shall not overlap. + /// + /// Returns: result + (last - first). + /// + /// Complexity: Exactly 'last - first' applications of the corresponding predicate. + /// + /// Note: The predicate version of replace_copy_if is replace_copy and not another variation of replace_copy_if. + /// This is because both versions would have the same parameter count and there could be ambiguity. + /// + template + inline OutputIterator + replace_copy_if(InputIterator first, InputIterator last, OutputIterator result, Predicate predicate, const T& new_value) + { + for(; first != last; ++first, ++result) + *result = predicate(*first) ? new_value : *first; + return result; + } + + + + + // reverse + // + // We provide helper functions which allow reverse to be implemented more + // efficiently for some types of iterators and types. + // + template + inline void reverse_impl(BidirectionalIterator first, BidirectionalIterator last, EASTL_ITC_NS::bidirectional_iterator_tag) + { + for(; (first != last) && (first != --last); ++first) // We are not allowed to use operator <, <=, >, >= with a + eastl::iter_swap(first, last); // generic (bidirectional or otherwise) iterator. + } + + template + inline void reverse_impl(RandomAccessIterator first, RandomAccessIterator last, EASTL_ITC_NS::random_access_iterator_tag) + { + if(first != last) + { + for(; first < --last; ++first) // With a random access iterator, we can use operator < to more efficiently implement + eastl::iter_swap(first, last); // this algorithm. A generic iterator doesn't necessarily have an operator < defined. + } + } + + /// reverse + /// + /// Reverses the values within the range [first, last). + /// + /// Effects: For each nonnegative integer i <= (last - first) / 2, + /// applies swap to all pairs of iterators first + i, (last i) - 1. + /// + /// Complexity: Exactly '(last - first) / 2' swaps. + /// + template + inline void reverse(BidirectionalIterator first, BidirectionalIterator last) + { + typedef typename eastl::iterator_traits::iterator_category IC; + eastl::reverse_impl(first, last, IC()); + } + + + + /// reverse_copy + /// + /// Copies the range [first, last) in reverse order to the result. + /// + /// Effects: Copies the range [first, last) to the range + /// [result, result + (last - first)) such that for any nonnegative + /// integer i < (last - first) the following assignment takes place: + /// *(result + (last - first) - i) = *(first + i) + /// + /// Requires: The ranges [first, last) and [result, result + (last - first)) + /// shall not overlap. + /// + /// Returns: result + (last - first). That is, returns the end of the output range. + /// + /// Complexity: Exactly 'last - first' assignments. + /// + template + inline OutputIterator + reverse_copy(BidirectionalIterator first, BidirectionalIterator last, OutputIterator result) + { + for(; first != last; ++result) + *result = *--last; + return result; + } + + + + /// search + /// + /// Search finds a subsequence within the range [first1, last1) that is identical to [first2, last2) + /// when compared element-by-element. It returns an iterator pointing to the beginning of that + /// subsequence, or else last1 if no such subsequence exists. As such, it is very much like + /// the C strstr function, with the primary difference being that strstr uses 0-terminated strings + /// whereas search uses an end iterator to specify the end of a string. + /// + /// Returns: The first iterator i in the range [first1, last1 - (last2 - first2)) such that for + /// any nonnegative integer n less than 'last2 - first2' the following corresponding condition holds: + /// *(i + n) == *(first2 + n). Returns last1 if no such iterator is found. + /// + /// Complexity: At most (last1 first1) * (last2 first2) applications of the corresponding predicate. + /// + template + ForwardIterator1 + search(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2) + { + if(first2 != last2) // If there is anything to search for... + { + // We need to make a special case for a pattern of one element, + // as the logic below prevents one element patterns from working. + ForwardIterator2 temp2(first2); + ++temp2; + + if(temp2 != last2) // If what we are searching for has a length > 1... + { + ForwardIterator1 cur1(first1); + ForwardIterator2 p2; + + while(first1 != last1) + { + // The following loop is the equivalent of eastl::find(first1, last1, *first2) + while((first1 != last1) && !(*first1 == *first2)) + ++first1; + + if(first1 != last1) + { + p2 = temp2; + cur1 = first1; + + if(++cur1 != last1) + { + while(*cur1 == *p2) + { + if(++p2 == last2) + return first1; + + if(++cur1 == last1) + return last1; + } + + ++first1; + continue; + } + } + return last1; + } + + // Fall through to the end. + } + else + return eastl::find(first1, last1, *first2); + } + + return first1; + + + #if 0 + /* Another implementation which is a little more simpler but executes a little slower on average. + typedef typename eastl::iterator_traits::difference_type difference_type_1; + typedef typename eastl::iterator_traits::difference_type difference_type_2; + + const difference_type_2 d2 = eastl::distance(first2, last2); + + for(difference_type_1 d1 = eastl::distance(first1, last1); d1 >= d2; ++first1, --d1) + { + ForwardIterator1 temp1 = first1; + + for(ForwardIterator2 temp2 = first2; ; ++temp1, ++temp2) + { + if(temp2 == last2) + return first1; + if(!(*temp1 == *temp2)) + break; + } + } + + return last1; + */ + #endif + } + + + /// search + /// + /// Search finds a subsequence within the range [first1, last1) that is identical to [first2, last2) + /// when compared element-by-element. It returns an iterator pointing to the beginning of that + /// subsequence, or else last1 if no such subsequence exists. As such, it is very much like + /// the C strstr function, with the only difference being that strstr uses 0-terminated strings + /// whereas search uses an end iterator to specify the end of a string. + /// + /// Returns: The first iterator i in the range [first1, last1 - (last2 - first2)) such that for + /// any nonnegative integer n less than 'last2 - first2' the following corresponding condition holds: + /// predicate(*(i + n), *(first2 + n)) != false. Returns last1 if no such iterator is found. + /// + /// Complexity: At most (last1 first1) * (last2 first2) applications of the corresponding predicate. + /// + template + ForwardIterator1 + search(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + BinaryPredicate predicate) + { + typedef typename eastl::iterator_traits::difference_type difference_type_1; + typedef typename eastl::iterator_traits::difference_type difference_type_2; + + difference_type_2 d2 = eastl::distance(first2, last2); + + if(d2 != 0) + { + ForwardIterator1 i(first1); + eastl::advance(i, d2); + + for(difference_type_1 d1 = eastl::distance(first1, last1); d1 >= d2; --d1) + { + if(eastl::equal(first1, i, first2, predicate)) + return first1; + if(d1 > d2) // To do: Find a way to make the algorithm more elegant. + { + ++first1; + ++i; + } + } + return last1; + } + return first1; // Just like with strstr, we return first1 if the match string is empty. + } + + + + // search_n helper functions + // + template + ForwardIterator // Generic implementation. + search_n_impl(ForwardIterator first, ForwardIterator last, Size count, const T& value, EASTL_ITC_NS::forward_iterator_tag) + { + if(count <= 0) + return first; + + Size d1 = (Size)eastl::distance(first, last); // Should d1 be of type Size, ptrdiff_t, or iterator_traits::difference_type? + // The problem with using iterator_traits::difference_type is that + if(count > d1) // ForwardIterator may not be a true iterator but instead something like a pointer. + return last; + + for(; d1 >= count; ++first, --d1) + { + ForwardIterator i(first); + + for(Size n = 0; n < count; ++n, ++i, --d1) + { + if(!(*i == value)) // Note that we always express value comparisons in terms of < or ==. + goto not_found; + } + return first; + + not_found: + first = i; + } + return last; + } + + template inline + RandomAccessIterator // Random access iterator implementation. Much faster than generic implementation. + search_n_impl(RandomAccessIterator first, RandomAccessIterator last, Size count, const T& value, EASTL_ITC_NS::random_access_iterator_tag) + { + if(count <= 0) + return first; + else if(count == 1) + return eastl::find(first, last, value); + else if(last > first) + { + RandomAccessIterator lookAhead; + RandomAccessIterator backTrack; + + Size skipOffset = (count - 1); + Size tailSize = (Size)(last - first); + Size remainder; + Size prevRemainder; + + for(lookAhead = first + skipOffset; tailSize >= count; lookAhead += count) + { + tailSize -= count; + + if(*lookAhead == value) + { + remainder = skipOffset; + + for(backTrack = lookAhead - 1; *backTrack == value; --backTrack) + { + if(--remainder == 0) + return (lookAhead - skipOffset); // success + } + + if(remainder <= tailSize) + { + prevRemainder = remainder; + + while(*(++lookAhead) == value) + { + if(--remainder == 0) + return (backTrack + 1); // success + } + tailSize -= (prevRemainder - remainder); + } + else + return last; // failure + } + + // lookAhead here is always pointing to the element of the last mismatch. + } + } + + return last; // failure + } + + + /// search_n + /// + /// Returns: The first iterator i in the range [first, last count) such that + /// for any nonnegative integer n less than count the following corresponding + /// conditions hold: *(i + n) == value, pred(*(i + n),value) != false. + /// Returns last if no such iterator is found. + /// + /// Complexity: At most '(last1 - first1) * count' applications of the corresponding predicate. + /// + template + ForwardIterator + search_n(ForwardIterator first, ForwardIterator last, Size count, const T& value) + { + typedef typename eastl::iterator_traits::iterator_category IC; + return eastl::search_n_impl(first, last, count, value, IC()); + } + + + /// binary_search + /// + /// Returns: true if there is an iterator i in the range [first last) that + /// satisfies the corresponding conditions: !(*i < value) && !(value < *i). + /// + /// Complexity: At most 'log(last - first) + 2' comparisons. + /// + /// Note: The reason binary_search returns bool instead of an iterator is + /// that search_n, lower_bound, or equal_range already return an iterator. + /// However, there are arguments that binary_search should return an iterator. + /// Note that we provide binary_search_i (STL extension) to return an iterator. + /// + /// To use search_n to find an item, do this: + /// iterator i = search_n(begin, end, 1, value); + /// To use lower_bound to find an item, do this: + /// iterator i = lower_bound(begin, end, value); + /// if((i != last) && !(value < *i)) + /// + /// It turns out that the above lower_bound method is as fast as binary_search + /// would be if it returned an iterator. + /// + template + inline bool + binary_search(ForwardIterator first, ForwardIterator last, const T& value) + { + // To do: This can be made slightly faster by not using lower_bound. + ForwardIterator i(eastl::lower_bound(first, last, value)); + return ((i != last) && !(value < *i)); // Note that we always express value comparisons in terms of < or ==. + } + + + /// binary_search + /// + /// Returns: true if there is an iterator i in the range [first last) that + /// satisfies the corresponding conditions: compare(*i, value) == false && + /// compare(value, *i) == false. + /// + /// Complexity: At most 'log(last - first) + 2' comparisons. + /// + /// Note: See comments above regarding the bool return value of binary_search. + /// + template + inline bool + binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare compare) + { + // To do: This can be made slightly faster by not using lower_bound. + ForwardIterator i(eastl::lower_bound(first, last, value, compare)); + return ((i != last) && !compare(value, *i)); + } + + + /// binary_search_i + /// + /// Returns: iterator if there is an iterator i in the range [first last) that + /// satisfies the corresponding conditions: !(*i < value) && !(value < *i). + /// Returns last if the value is not found. + /// + /// Complexity: At most 'log(last - first) + 2' comparisons. + /// + template + inline ForwardIterator + binary_search_i(ForwardIterator first, ForwardIterator last, const T& value) + { + // To do: This can be made slightly faster by not using lower_bound. + ForwardIterator i(eastl::lower_bound(first, last, value)); + if((i != last) && !(value < *i)) // Note that we always express value comparisons in terms of < or ==. + return i; + return last; + } + + + /// binary_search_i + /// + /// Returns: iterator if there is an iterator i in the range [first last) that + /// satisfies the corresponding conditions: !(*i < value) && !(value < *i). + /// Returns last if the value is not found. + /// + /// Complexity: At most 'log(last - first) + 2' comparisons. + /// + template + inline ForwardIterator + binary_search_i(ForwardIterator first, ForwardIterator last, const T& value, Compare compare) + { + // To do: This can be made slightly faster by not using lower_bound. + ForwardIterator i(eastl::lower_bound(first, last, value, compare)); + if((i != last) && !compare(value, *i)) + return i; + return last; + } + + + /// unique + /// + /// Given a sorted range, this function removes duplicated items. + /// Note that if you have a container then you will probably want + /// to call erase on the container with the return value if your + /// goal is to remove the duplicated items from the container. + /// + /// Effects: Eliminates all but the first element from every consecutive + /// group of equal elements referred to by the iterator i in the range + /// [first, last) for which the following corresponding condition holds: + /// *i == *(i - 1). + /// + /// Returns: The end of the resulting range. + /// + /// Complexity: If the range (last - first) is not empty, exactly (last - first) + /// applications of the corresponding predicate, otherwise no applications of the predicate. + /// + /// Example usage: + /// vector intArray; + /// ... + /// intArray.erase(unique(intArray.begin(), intArray.end()), intArray.end()); + /// + template + ForwardIterator unique(ForwardIterator first, ForwardIterator last) + { + first = eastl::adjacent_find(first, last); + + if(first != last) // We expect that there are duplicated items, else the user wouldn't be calling this function. + { + ForwardIterator dest(first); + + for(++first; first != last; ++first) + { + if(!(*dest == *first)) // Note that we always express value comparisons in terms of < or ==. + *++dest = *first; + } + return ++dest; + } + return last; + } + + + /// unique + /// + /// Given a sorted range, this function removes duplicated items. + /// Note that if you have a container then you will probably want + /// to call erase on the container with the return value if your + /// goal is to remove the duplicated items from the container. + /// + /// Effects: Eliminates all but the first element from every consecutive + /// group of equal elements referred to by the iterator i in the range + /// [first, last) for which the following corresponding condition holds: + /// predicate(*i, *(i - 1)) != false. + /// + /// Returns: The end of the resulting range. + /// + /// Complexity: If the range (last - first) is not empty, exactly (last - first) + /// applications of the corresponding predicate, otherwise no applications of the predicate. + /// + template + ForwardIterator unique(ForwardIterator first, ForwardIterator last, BinaryPredicate predicate) + { + first = eastl::adjacent_find(first, last, predicate); + + if(first != last) // We expect that there are duplicated items, else the user wouldn't be calling this function. + { + ForwardIterator dest(first); + + for(++first; first != last; ++first) + { + if(!predicate(*dest, *first)) + *++dest = *first; + } + return ++dest; + } + return last; + } + + + + // find_end + // + // We provide two versions here, one for a bidirectional iterators and one for + // regular forward iterators. Given that we are searching backward, it's a bit + // more efficient if we can use backwards iteration to implement our search, + // though this requires an iterator that can be reversed. + // + template + ForwardIterator1 + find_end_impl(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + EASTL_ITC_NS::forward_iterator_tag, EASTL_ITC_NS::forward_iterator_tag) + { + if(first2 != last2) // We have to do this check because the search algorithm below will return first1 (and not last1) if the first2/last2 range is empty. + { + for(ForwardIterator1 result(last1); ; ) + { + const ForwardIterator1 resultNext(eastl::search(first1, last1, first2, last2)); + + if(resultNext != last1) // If another sequence was found... + { + first1 = result = resultNext; + ++first1; + } + else + return result; + } + } + return last1; + } + + template + BidirectionalIterator1 + find_end_impl(BidirectionalIterator1 first1, BidirectionalIterator1 last1, + BidirectionalIterator2 first2, BidirectionalIterator2 last2, + EASTL_ITC_NS::bidirectional_iterator_tag, EASTL_ITC_NS::bidirectional_iterator_tag) + { + typedef eastl::reverse_iterator reverse_iterator1; + typedef eastl::reverse_iterator reverse_iterator2; + + reverse_iterator1 rresult(eastl::search(reverse_iterator1(last1), reverse_iterator1(first1), + reverse_iterator2(last2), reverse_iterator2(first2))); + if(rresult.base() != first1) // If we found something... + { + BidirectionalIterator1 result(rresult.base()); + + eastl::advance(result, -eastl::distance(first2, last2)); // We have an opportunity to optimize this, as the + return result; // search function already calculates this distance. + } + return last1; + } + + /// find_end + /// + /// Finds the last occurrence of the second sequence in the first sequence. + /// As such, this function is much like the C string function strrstr and it + /// is also the same as a reversed version of 'search'. It is called find_end + /// instead of the possibly more consistent search_end simply because the C++ + /// standard algorithms have such naming. + /// + /// Returns an iterator between first1 and last1 if the sequence is found. + /// returns last1 (the end of the first seqence) if the sequence is not found. + /// + template + inline ForwardIterator1 + find_end(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2) + { + typedef typename eastl::iterator_traits::iterator_category IC1; + typedef typename eastl::iterator_traits::iterator_category IC2; + + return eastl::find_end_impl(first1, last1, first2, last2, IC1(), IC2()); + } + + + + + // To consider: Fold the predicate and non-predicate versions of + // this algorithm into a single function. + template + ForwardIterator1 + find_end_impl(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + BinaryPredicate predicate, + EASTL_ITC_NS::forward_iterator_tag, EASTL_ITC_NS::forward_iterator_tag) + { + if(first2 != last2) // We have to do this check because the search algorithm below will return first1 (and not last1) if the first2/last2 range is empty. + { + for(ForwardIterator1 result = last1; ; ) + { + const ForwardIterator1 resultNext(eastl::search(first1, last1, first2, last2, predicate)); + + if(resultNext != last1) // If another sequence was found... + { + first1 = result = resultNext; + ++first1; + } + else + return result; + } + } + return last1; + } + + template + BidirectionalIterator1 + find_end_impl(BidirectionalIterator1 first1, BidirectionalIterator1 last1, + BidirectionalIterator2 first2, BidirectionalIterator2 last2, + BinaryPredicate predicate, + EASTL_ITC_NS::bidirectional_iterator_tag, EASTL_ITC_NS::bidirectional_iterator_tag) + { + typedef eastl::reverse_iterator reverse_iterator1; + typedef eastl::reverse_iterator reverse_iterator2; + + reverse_iterator1 rresult(eastl::search + (reverse_iterator1(last1), reverse_iterator1(first1), + reverse_iterator2(last2), reverse_iterator2(first2), + predicate)); + if(rresult.base() != first1) // If we found something... + { + BidirectionalIterator1 result(rresult.base()); + eastl::advance(result, -eastl::distance(first2, last2)); + return result; + } + return last1; + } + + + /// find_end + /// + /// Effects: Finds a subsequence of equal values in a sequence. + /// + /// Returns: The last iterator i in the range [first1, last1 - (last2 - first2)) + /// such that for any nonnegative integer n < (last2 - first2), the following + /// corresponding conditions hold: pred(*(i+n),*(first2+n)) != false. Returns + /// last1 if no such iterator is found. + /// + /// Complexity: At most (last2 - first2) * (last1 - first1 - (last2 - first2) + 1) + /// applications of the corresponding predicate. + /// + template + inline ForwardIterator1 + find_end(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2, + BinaryPredicate predicate) + { + typedef typename eastl::iterator_traits::iterator_category IC1; + typedef typename eastl::iterator_traits::iterator_category IC2; + + return eastl::find_end_impl + (first1, last1, first2, last2, predicate, IC1(), IC2()); + } + + + /// set_difference + /// + /// set_difference iterates over both input ranges and copies elements present + /// in the first range but not the second to the output range. + /// + /// Effects: Copies the elements of the range [first1, last1) which are not + /// present in the range [first2, last2) to the range beginning at result. + /// The elements in the constructed range are sorted. + /// + /// Requires: The input ranges must be sorted. + /// Requires: The output range shall not overlap with either of the original ranges. + /// + /// Returns: The end of the output range. + /// + /// Complexity: At most (2 * ((last1 - first1) + (last2 - first2)) - 1) comparisons. + /// + template + OutputIterator set_difference(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) + { + while((first1 != last1) && (first2 != last2)) + { + if(*first1 < *first2) + { + *result = *first1; + ++first1; + ++result; + } + else if(*first2 < *first1) + ++first2; + else + { + ++first1; + ++first2; + } + } + + return eastl::copy(first1, last1, result); + } + + + template + OutputIterator set_difference(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result, Compare compare) + { + while((first1 != last1) && (first2 != last2)) + { + if(compare(*first1, *first2)) + { + EASTL_VALIDATE_COMPARE(!compare(*first2, *first1)); // Validate that the compare function is sane. + *result = *first1; + ++first1; + ++result; + } + else if(compare(*first2, *first1)) + { + EASTL_VALIDATE_COMPARE(!compare(*first1, *first2)); // Validate that the compare function is sane. + ++first2; + } + else + { + ++first1; + ++first2; + } + } + + return eastl::copy(first1, last1, result); + } + + + /// set_difference_2 + /// + /// set_difference_2 iterates over both input ranges and copies elements present + /// in the first range but not the second to the first output range and copies + /// elements present in the second range but not in the first to the second output + /// range. + /// + /// Effects: Copies the elements of the range [first1, last1) which are not + /// present in the range [first2, last2) to the first output range beginning at + /// result1 AND copies the element of range [first2, last2) which are not present + /// in the range [first1, last) to the second output range beginning at result2. + /// The elements in the constructed range are sorted. + /// + /// Requires: The input ranges must be sorted. + /// Requires: The output ranges shall not overlap with either of the original ranges. + /// + /// Returns: Nothing. + /// + /// Complexity: At most (2 * ((last1 - first1) + (last2 - first2)) - 1) comparisons. + /// + template + void set_difference_2(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result1, OutputIterator result2, Compare compare) + { + while ((first1 != last1) && (first2 != last2)) + { + if (compare(*first1, *first2)) + { + EASTL_VALIDATE_COMPARE(!compare(*first2, *first1)); // Validate that the compare function is sane. + *result1++ = *first1++; + } + else if (compare(*first2, *first1)) + { + EASTL_VALIDATE_COMPARE(!compare(*first1, *first2)); // Validate that the compare function is sane. + *result2++ = *first2++; + } + else + { + ++first1; + ++first2; + } + } + + eastl::copy(first2, last2, result2); + eastl::copy(first1, last1, result1); + } + + /// set_difference_2 + /// + /// set_difference_2 with the default comparison object is eastl::less<>. + /// + template + void set_difference_2(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result1, OutputIterator result2) + { + eastl::set_difference_2(first1, last1, first2, last2, result1, result2, eastl::less<>{}); + } + + + /// set_symmetric_difference + /// + /// set_difference iterates over both input ranges and copies elements present + /// in the either range but not the other to the output range. + /// + /// Effects: Copies the elements of the range [first1, last1) which are not + /// present in the range [first2, last2), and the elements of the range [first2, last2) + /// which are not present in the range [first1, last1) to the range beginning at result. + /// The elements in the constructed range are sorted. + /// + /// Requires: The input ranges must be sorted. + /// Requires: The resulting range shall not overlap with either of the original ranges. + /// + /// Returns: The end of the constructed range. + /// + /// Complexity: At most (2 * ((last1 - first1) + (last2 - first2)) - 1) comparisons. + /// + template + OutputIterator set_symmetric_difference(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) + { + while((first1 != last1) && (first2 != last2)) + { + if(*first1 < *first2) + { + *result = *first1; + ++first1; + ++result; + } + else if(*first2 < *first1) + { + *result = *first2; + ++first2; + ++result; + } + else + { + ++first1; + ++first2; + } + } + + return eastl::copy(first2, last2, eastl::copy(first1, last1, result)); + } + + + template + OutputIterator set_symmetric_difference(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result, Compare compare) + { + while((first1 != last1) && (first2 != last2)) + { + if(compare(*first1, *first2)) + { + EASTL_VALIDATE_COMPARE(!compare(*first2, *first1)); // Validate that the compare function is sane. + *result = *first1; + ++first1; + ++result; + } + else if(compare(*first2, *first1)) + { + EASTL_VALIDATE_COMPARE(!compare(*first1, *first2)); // Validate that the compare function is sane. + *result = *first2; + ++first2; + ++result; + } + else + { + ++first1; + ++first2; + } + } + + return eastl::copy(first2, last2, eastl::copy(first1, last1, result)); + } + + + /// set_intersection + /// + /// set_intersection over both ranges and copies elements present in + /// both ranges to the output range. + /// + /// Effects: Constructs a sorted intersection of the elements from the + /// two ranges; that is, the set of elements that are present in both of the ranges. + /// + /// Requires: The input ranges must be sorted. + /// Requires: The resulting range shall not overlap with either of the original ranges. + /// + /// Returns: The end of the constructed range. + /// + /// Complexity: At most 2 * ((last1 - first1) + (last2 - first2)) - 1) comparisons. + /// + /// Note: The copying operation is stable; if an element is present in both ranges, + /// the one from the first range is copied. + /// + template + OutputIterator set_intersection(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) + { + while((first1 != last1) && (first2 != last2)) + { + if(*first1 < *first2) + ++first1; + else if(*first2 < *first1) + ++first2; + else + { + *result = *first1; + ++first1; + ++first2; + ++result; + } + } + + return result; + } + + + template + OutputIterator set_intersection(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result, Compare compare) + { + while((first1 != last1) && (first2 != last2)) + { + if(compare(*first1, *first2)) + { + EASTL_VALIDATE_COMPARE(!compare(*first2, *first1)); // Validate that the compare function is sane. + ++first1; + } + else if(compare(*first2, *first1)) + { + EASTL_VALIDATE_COMPARE(!compare(*first1, *first2)); // Validate that the compare function is sane. + ++first2; + } + else + { + *result = *first1; + ++first1; + ++first2; + ++result; + } + } + + return result; + } + + + + /// set_union + /// + /// set_union iterates over both ranges and copies elements present in + /// both ranges to the output range. + /// + /// Effects: Constructs a sorted union of the elements from the two ranges; + /// that is, the set of elements that are present in one or both of the ranges. + /// + /// Requires: The input ranges must be sorted. + /// Requires: The resulting range shall not overlap with either of the original ranges. + /// + /// Returns: The end of the constructed range. + /// + /// Complexity: At most (2 * ((last1 - first1) + (last2 - first2)) - 1) comparisons. + /// + /// Note: The copying operation is stable; if an element is present in both ranges, + /// the one from the first range is copied. + /// + template + OutputIterator set_union(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result) + { + while((first1 != last1) && (first2 != last2)) + { + if(*first1 < *first2) + { + *result = *first1; + ++first1; + } + else if(*first2 < *first1) + { + *result = *first2; + ++first2; + } + else + { + *result = *first1; + ++first1; + ++first2; + } + ++result; + } + + return eastl::copy(first2, last2, eastl::copy(first1, last1, result)); + } + + + template + OutputIterator set_union(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator result, Compare compare) + { + while((first1 != last1) && (first2 != last2)) + { + if(compare(*first1, *first2)) + { + EASTL_VALIDATE_COMPARE(!compare(*first2, *first1)); // Validate that the compare function is sane. + *result = *first1; + ++first1; + } + else if(compare(*first2, *first1)) + { + EASTL_VALIDATE_COMPARE(!compare(*first1, *first2)); // Validate that the compare function is sane. + *result = *first2; + ++first2; + } + else + { + *result = *first1; + ++first1; + ++first2; + } + ++result; + } + + return eastl::copy(first2, last2, eastl::copy(first1, last1, result)); + } + + + /// set_decomposition + /// + /// set_decomposition iterates over both ranges and copies elements to one of the three + /// categories of output ranges. + /// + /// Effects: Constructs three sorted containers of the elements from the two ranges. + /// * OutputIterator1 is elements only in Container1. + /// * OutputIterator2 is elements only in Container2. + /// * OutputIterator3 is elements that are in both Container1 and Container2. + /// + /// Requires: The input ranges must be sorted. + /// Requires: The resulting ranges shall not overlap with either of the original ranges. + /// + /// Returns: The end of the constructed range of elements in both Container1 and Container2. + /// + /// Complexity: At most (2 * ((last1 - first1) + (last2 - first2)) - 1) comparisons. + /// + template + OutputIterator3 set_decomposition(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + OutputIterator1 result1, OutputIterator2 result2, OutputIterator3 result3, Compare compare) + { + while ((first1 != last1) && (first2 != last2)) + { + if (compare(*first1, *first2)) + { + EASTL_VALIDATE_COMPARE(!compare(*first2, *first1)); // Validate that the compare function is sane. + *result1++ = *first1++; + } + else if (compare(*first2, *first1)) + { + EASTL_VALIDATE_COMPARE(!compare(*first1, *first2)); // Validate that the compare function is sane. + *result2++ = *first2++; + } + else + { + *result3++ = *first1++; + ++first2; + } + } + + eastl::copy(first1, last1, result1); + eastl::copy(first2, last2, result2); + + return result3; + } + + /// set_decomposition + /// + /// set_decomposition with the default comparison object is eastl::less<>. + /// + template + OutputIterator3 set_decomposition(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, + OutputIterator1 result1, OutputIterator2 result2, OutputIterator3 result3) + { + return eastl::set_decomposition(first1, last1, first2, last2, result1, result2, result3, eastl::less<>{}); + } + + + /// is_permutation + /// + template + bool is_permutation(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + + // Skip past any equivalent initial elements. + while((first1 != last1) && (*first1 == *first2)) + { + ++first1; + ++first2; + } + + if(first1 != last1) + { + const difference_type first1Size = eastl::distance(first1, last1); + ForwardIterator2 last2 = first2; + eastl::advance(last2, first1Size); + + for(ForwardIterator1 i = first1; i != last1; ++i) + { + if(i == eastl::find(first1, i, *i)) + { + const difference_type c = eastl::count(first2, last2, *i); + + if((c == 0) || (c != eastl::count(i, last1, *i))) + return false; + } + } + } + + return true; + } + + /// is_permutation + /// + template + bool is_permutation(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, BinaryPredicate predicate) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + + // Skip past any equivalent initial elements. + while((first1 != last1) && predicate(*first1, *first2)) + { + ++first1; + ++first2; + } + + if(first1 != last1) + { + const difference_type first1Size = eastl::distance(first1, last1); + ForwardIterator2 last2 = first2; + eastl::advance(last2, first1Size); + + for(ForwardIterator1 i = first1; i != last1; ++i) + { + if(i == eastl::find(first1, i, *i, predicate)) + { + const difference_type c = eastl::count(first2, last2, *i, predicate); + + if((c == 0) || (c != eastl::count(i, last1, *i, predicate))) + return false; + } + } + } + + return true; + } + + + /// next_permutation + /// + /// mutates the range [first, last) to the next permutation. Returns true if the + /// new range is not the final permutation (sorted like the starting permutation). + /// Permutations start with a sorted range, and false is returned when next_permutation + /// results in the initial sorted range, or if the range has <= 1 element. + /// Note that elements are compared by operator < (as usual) and that elements deemed + /// equal via this are not rearranged. + /// + /// http://marknelson.us/2002/03/01/next-permutation/ + /// Basically we start with an ordered range and reverse it's order one specifically + /// chosen swap and reverse at a time. It happens that this require going through every + /// permutation of the range. We use the same variable names as the document above. + /// + /// To consider: Significantly improved permutation/combination functionality: + /// http://home.roadrunner.com/~hinnant/combinations.html + /// + /// Example usage: + /// vector intArray; + /// // + /// sort(intArray.begin(), intArray.end()); + /// do { + /// // + /// } while(next_permutation(intArray.begin(), intArray.end())); + /// + + template + bool next_permutation(BidirectionalIterator first, BidirectionalIterator last, Compare compare) + { + if(first != last) // If there is anything in the range... + { + BidirectionalIterator i = last; + + if(first != --i) // If the range has more than one item... + { + for(;;) + { + BidirectionalIterator ii(i), j; + + if(compare(*--i, *ii)) // Find two consecutive values where the first is less than the second. + { + j = last; + while(!compare(*i, *--j)) // Find the final value that's greater than the first (it may be equal to the second). + {} + eastl::iter_swap(i, j); // Swap the first and the final. + eastl::reverse(ii, last); // Reverse the ranget from second to last. + return true; + } + + if(i == first) // There are no two consecutive values where the first is less than the second, meaning the range is in reverse order. The reverse ordered range is always the last permutation. + { + eastl::reverse(first, last); + break; // We are done. + } + } + } + } + + return false; + } + + template + bool next_permutation(BidirectionalIterator first, BidirectionalIterator last) + { + typedef typename eastl::iterator_traits::value_type value_type; + + return eastl::next_permutation(first, last, eastl::less()); + } + + + + /// rotate + /// + /// Effects: For each non-negative integer i < (last - first), places the element from the + /// position first + i into position first + (i + (last - middle)) % (last - first). + /// + /// Returns: first + (last - middle). That is, returns where first went to. + /// + /// Remarks: This is a left rotate. + /// + /// Requires: [first,middle) and [middle,last) shall be valid ranges. ForwardIterator shall + /// satisfy the requirements of ValueSwappable (17.6.3.2). The type of *first shall satisfy + /// the requirements of MoveConstructible (Table 20) and the requirements of MoveAssignable. + /// + /// Complexity: At most last - first swaps. + /// + /// Note: While rotate works on ForwardIterators (e.g. slist) and BidirectionalIterators (e.g. list), + /// you can get much better performance (O(1) instead of O(n)) with slist and list rotation by + /// doing splice operations on those lists instead of calling this rotate function. + /// + /// http://www.cs.bell-labs.com/cm/cs/pearls/s02b.pdf / http://books.google.com/books?id=kse_7qbWbjsC&pg=PA14&lpg=PA14&dq=Programming+Pearls+flipping+hands + /// http://books.google.com/books?id=tjOlkl7ecVQC&pg=PA189&lpg=PA189&dq=stepanov+Elements+of+Programming+rotate + /// http://stackoverflow.com/questions/21160875/why-is-stdrotate-so-fast + /// + /// Strategy: + /// - We handle the special case of (middle == first) and (middle == last) no-ops + /// up front in the main rotate entry point. + /// - There's a basic ForwardIterator implementation (rotate_general_impl) which is + /// a fallback implementation that's not as fast as others but works for all cases. + /// - There's a slightly better BidirectionalIterator implementation. + /// - We have specialized versions for rotating elements that are is_trivially_move_assignable. + /// These versions will use memmove for when we have a RandomAccessIterator. + /// - We have a specialized version for rotating by only a single position, as that allows us + /// (with any iterator type) to avoid a lot of logic involved with algorithms like "flipping hands" + /// and achieve near optimal O(n) behavior. it turns out that rotate-by-one is a common use + /// case in practice. + /// + namespace Internal + { + template + ForwardIterator rotate_general_impl(ForwardIterator first, ForwardIterator middle, ForwardIterator last) + { + using eastl::swap; + + ForwardIterator current = middle; + + do { + swap(*first++, *current++); + + if(first == middle) + middle = current; + } while(current != last); + + ForwardIterator result = first; + current = middle; + + while(current != last) + { + swap(*first++, *current++); + + if(first == middle) + middle = current; + else if(current == last) + current = middle; + } + + return result; // result points to first + (last - middle). + } + + + template + ForwardIterator move_rotate_left_by_one(ForwardIterator first, ForwardIterator last) + { + typedef typename eastl::iterator_traits::value_type value_type; + + value_type temp(eastl::move(*first)); + ForwardIterator result = eastl::move(eastl::next(first), last, first); // Note that while our template type is BidirectionalIterator, if the actual + *result = eastl::move(temp); // iterator is a RandomAccessIterator then this move will be a memmove for trivial types. + + return result; // result points to the final element in the range. + } + + + template + BidirectionalIterator move_rotate_right_by_one(BidirectionalIterator first, BidirectionalIterator last) + { + typedef typename eastl::iterator_traits::value_type value_type; + + BidirectionalIterator beforeLast = eastl::prev(last); + value_type temp(eastl::move(*beforeLast)); + BidirectionalIterator result = eastl::move_backward(first, beforeLast, last); // Note that while our template type is BidirectionalIterator, if the actual + *first = eastl::move(temp); // iterator is a RandomAccessIterator then this move will be a memmove for trivial types. + + return result; // result points to the first element in the range. + } + + template + struct rotate_helper + { + template + static ForwardIterator rotate_impl(ForwardIterator first, ForwardIterator middle, ForwardIterator last) + { return Internal::rotate_general_impl(first, middle, last); } + }; + + template <> + struct rotate_helper + { + template + static ForwardIterator rotate_impl(ForwardIterator first, ForwardIterator middle, ForwardIterator last) + { + if(eastl::next(first) == middle) // If moving trivial types by a single element, memcpy is fast for that case. + return Internal::move_rotate_left_by_one(first, last); + return Internal::rotate_general_impl(first, middle, last); + } + }; + + template <> + struct rotate_helper + { + template + static BidirectionalIterator rotate_impl(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last) + { return Internal::rotate_general_impl(first, middle, last); } // rotate_general_impl outperforms the flipping hands algorithm. + + /* + // Simplest "flipping hands" implementation. Disabled because it's slower on average than rotate_general_impl. + template + static BidirectionalIterator rotate_impl(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last) + { + eastl::reverse(first, middle); + eastl::reverse(middle, last); + eastl::reverse(first, last); + return first + (last - middle); // This can be slow for large ranges because operator + and - are O(n). + } + + // Smarter "flipping hands" implementation, but still disabled because benchmarks are showing it to be slower than rotate_general_impl. + template + static BidirectionalIterator rotate_impl(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last) + { + // This is the "flipping hands" algorithm. + eastl::reverse_impl(first, middle, EASTL_ITC_NS::bidirectional_iterator_tag()); // Reverse the left side. + eastl::reverse_impl(middle, last, EASTL_ITC_NS::bidirectional_iterator_tag()); // Reverse the right side. + + // Reverse the entire range. + while((first != middle) && (middle != last)) + { + eastl::iter_swap(first, --last); + ++first; + } + + if(first == middle) // Finish reversing the entire range. + { + eastl::reverse_impl(middle, last, bidirectional_iterator_tag()); + return last; + } + else + { + eastl::reverse_impl(first, middle, bidirectional_iterator_tag()); + return first; + } + } + */ + }; + + template <> + struct rotate_helper + { + template + static BidirectionalIterator rotate_impl(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last) + { + if(eastl::next(first) == middle) // If moving trivial types by a single element, memcpy is fast for that case. + return Internal::move_rotate_left_by_one(first, last); + if(eastl::next(middle) == last) + return Internal::move_rotate_right_by_one(first, last); + return Internal::rotate_general_impl(first, middle, last); + } + }; + + template + inline Integer greatest_common_divisor(Integer x, Integer y) + { + do { + Integer t = (x % y); + x = y; + y = t; + } while(y); + + return x; + } + + template <> + struct rotate_helper + { + // This is the juggling algorithm, using move operations. + // In practice this implementation is about 25% faster than rotate_general_impl. We may want to + // consider sticking with just rotate_general_impl and avoid the code generation of this function. + template + static RandomAccessIterator rotate_impl(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last) + { + typedef typename iterator_traits::difference_type difference_type; + typedef typename iterator_traits::value_type value_type; + + const difference_type m1 = (middle - first); + const difference_type m2 = (last - middle); + const difference_type g = Internal::greatest_common_divisor(m1, m2); + value_type temp; + + for(RandomAccessIterator p = first + g; p != first;) + { + temp = eastl::move(*--p); + RandomAccessIterator p1 = p; + RandomAccessIterator p2 = p + m1; + do + { + *p1 = eastl::move(*p2); + p1 = p2; + const difference_type d = (last - p2); + + if(m1 < d) + p2 += m1; + else + p2 = first + (m1 - d); + } while(p2 != p); + + *p1 = eastl::move(temp); + } + + return first + m2; + } + }; + + template <> + struct rotate_helper + { + // Experiments were done which tested the performance of using an intermediate buffer + // to do memcpy's to as opposed to executing a swapping algorithm. It turns out this is + // actually slower than even rotate_general_impl, partly because the average case involves + // memcpy'ing a quarter of the element range twice. Experiments were done with various kinds + // of PODs with various element counts. + + template + static RandomAccessIterator rotate_impl(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last) + { + if(eastl::next(first) == middle) // If moving trivial types by a single element, memcpy is fast for that case. + return Internal::move_rotate_left_by_one(first, last); + if(eastl::next(middle) == last) + return Internal::move_rotate_right_by_one(first, last); + if((last - first) < 32) // For small ranges rotate_general_impl is faster. + return Internal::rotate_general_impl(first, middle, last); + return Internal::rotate_helper::rotate_impl(first, middle, last); + } + }; + + } // namespace Internal + + + template + ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last) + { + if(middle != first) + { + if(middle != last) + { + typedef typename eastl::iterator_traits::iterator_category IC; + typedef typename eastl::iterator_traits::value_type value_type; + + return Internal::rotate_helper::value || // This is the best way of telling if we can move types via memmove, but without a conforming C++11 compiler it usually returns false. + eastl::is_pod::value || // This is a more conservative way of telling if we can move types via memmove, and most compilers support it, but it doesn't have as full of coverage as is_trivially_move_assignable. + eastl::is_scalar::value> // This is the most conservative means and works with all compilers, but works only for scalars. + ::rotate_impl(first, middle, last); + } + + return first; + } + + return last; + } + + + + /// rotate_copy + /// + /// Similar to rotate except writes the output to the OutputIterator and + /// returns an OutputIterator to the element past the last element copied + /// (i.e. result + (last - first)) + /// + template + OutputIterator rotate_copy(ForwardIterator first, ForwardIterator middle, ForwardIterator last, OutputIterator result) + { + return eastl::copy(first, middle, eastl::copy(middle, last, result)); + } + + + + /// clamp + /// + /// Returns a reference to a clamped value within the range of [lo, hi]. + /// + /// http://en.cppreference.com/w/cpp/algorithm/clamp + /// + template + EA_CONSTEXPR const T& clamp(const T& v, const T& lo, const T& hi, Compare comp) + { + // code collapsed to a single line due to constexpr requirements + return [&] { EASTL_ASSERT(!comp(hi, lo)); }(), + comp(v, lo) ? lo : comp(hi, v) ? hi : v; + } + + template + EA_CONSTEXPR const T& clamp(const T& v, const T& lo, const T& hi) + { + return eastl::clamp(v, lo, hi, eastl::less<>()); + } + + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/allocator.h b/lib/EASTL/include/EASTL/allocator.h new file mode 100644 index 000000000..ad20e4d8a --- /dev/null +++ b/lib/EASTL/include/EASTL/allocator.h @@ -0,0 +1,395 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ALLOCATOR_H +#define EASTL_ALLOCATOR_H + + +#include +#include +#include + + +#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. + /// 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); + bool operator!=(const allocator& a, const allocator& b); + + + + /// 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; } + inline bool operator!=(const dummy_allocator&, const dummy_allocator&) { return false; } + + + + /// 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 + 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 + 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 + 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. + } + + + inline bool operator!=(const allocator&, const allocator&) + { + return false; // All allocators are considered equal, as they merely use global new/delete. + } + + + } // namespace eastl + + +#endif // EASTL_USER_DEFINED_ALLOCATOR + + + +namespace eastl +{ + + template + 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 + 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(result)& ~(alignment - 1)) == reinterpret_cast(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(resultMinusOffset)& ~(alignment - 1)) == reinterpret_cast(resultMinusOffset)); + } + return result; + } + +} + + +#endif // Header include guard + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/allocator_malloc.h b/lib/EASTL/include/EASTL/allocator_malloc.h new file mode 100644 index 000000000..78f4f69de --- /dev/null +++ b/lib/EASTL/include/EASTL/allocator_malloc.h @@ -0,0 +1,130 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ALLOCATOR_MALLOC_H +#define EASTL_ALLOCATOR_MALLOC_H + + +#include +#include +#include + + +// 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 // memalign, posix_memalign. + #define EASTL_ALIGNED_MALLOC_AVAILABLE 1 + + #if EA_HAS_INCLUDE_AVAILABLE + #if EA_HAS_INCLUDE() + #include + #elif EA_HAS_INCLUDE() + #include + #endif + #elif defined(EA_PLATFORM_BSD) + #include + #elif defined(__clang__) + #if __has_include() + #include + #elif __has_include() + #include + #endif + #else + #include + #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 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 + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/any.h b/lib/EASTL/include/EASTL/any.h new file mode 100644 index 000000000..c2ef63880 --- /dev/null +++ b/lib/EASTL/include/EASTL/any.h @@ -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 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 +#include +#if EASTL_RTTI_ENABLED + #include +#endif +#if EASTL_EXCEPTIONS_ENABLED + #include +#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 + void* DefaultConstruct(Args&&... args) + { + auto* pMem = EASTLAllocatorDefault()->allocate(sizeof(T), alignof(T), 0); + + return ::new(pMem) T(eastl::forward(args)...); + } + + template + void DefaultDestroy(T* p) + { + p->~T(); + + EASTLAllocatorDefault()->deallocate(static_cast(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::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 + using use_internal_storage = bool_constant + < + is_nothrow_move_constructible::value + && (sizeof(T) <= sizeof(storage)) && + (alignment_of::value % alignment_of::value == 0) + >; + + + ////////////////////////////////////////////////////////////////////////////////////////// + // non-member friend functions + // + template friend const ValueType* any_cast(const any* pAny) EA_NOEXCEPT; + template friend ValueType* any_cast(any* pAny) EA_NOEXCEPT; + template friend ValueType any_cast(const any& operand); + template friend ValueType any_cast(any& operand); + template friend ValueType any_cast(any&& operand); + + //Adding Unsafe any cast operations + template friend const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT; + template friend ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT; + + + ////////////////////////////////////////////////////////////////////////////////////////// + // internal storage handler + // + template + struct storage_handler_internal + { + template + static void construct(storage& s, V&& v) + { + ::new(&s.internal_storage) T(eastl::forward(v)); + } + + template + static void construct_inplace(storage& s, Args... args) + { + ::new(&s.internal_storage) T(eastl::forward(args)...); + } + + template + static void construct_inplace(storage& s, std::initializer_list il, Args&&... args) + { + ::new(&s.internal_storage) NT(il, eastl::forward(args)...); + } + + static inline void destroy(any& refAny) + { + T& t = *static_cast(static_cast(&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(*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(*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 + struct storage_handler_external + { + template + static inline void construct(storage& s, V&& v) + { + s.external_storage = Internal::DefaultConstruct(eastl::forward(v)); + } + + template + static inline void construct_inplace(storage& s, Args... args) + { + s.external_storage = Internal::DefaultConstruct(eastl::forward(args)...); + } + + template + static inline void construct_inplace(storage& s, std::initializer_list il, Args&&... args) + { + s.external_storage = Internal::DefaultConstruct(il, eastl::forward(args)...); + } + + static inline void destroy(any& refAny) + { + Internal::DefaultDestroy(static_cast(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(pThis->m_storage.external_storage); + } + break; + + case storage_operation::DESTROY: + { + EASTL_ASSERT(pThis); + destroy(*const_cast(pThis)); + } + break; + + case storage_operation::COPY: + { + EASTL_ASSERT(pThis); + EASTL_ASSERT(pOther); + construct(pOther->m_storage, *static_cast(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(*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 + using storage_handler = typename conditional::value, + storage_handler_internal, + storage_handler_external>::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 + any(ValueType&& value, + typename eastl::enable_if::type, any>::value>::type* = 0) + { + typedef decay_t DecayedValueType; + static_assert(is_copy_constructible::value, "ValueType must be copy-constructible"); + storage_handler::construct(m_storage, eastl::forward(value)); + m_handler = &storage_handler::handler_func; + } + + template + explicit any(in_place_type_t, Args&&... args) + { + typedef storage_handler> StorageHandlerT; + static_assert(eastl::is_constructible::value, "T must be constructible with Args..."); + + StorageHandlerT::construct_inplace(m_storage, eastl::forward(args)...); + m_handler = &StorageHandlerT::handler_func; + } + + template + explicit any(in_place_type_t, + std::initializer_list il, + Args&&... args, + typename eastl::enable_if&, Args...>::value, + void>::type* = 0) + { + typedef storage_handler> StorageHandlerT; + + StorageHandlerT::construct_inplace(m_storage, il, eastl::forward(args)...); + m_handler = &StorageHandlerT::handler_func; + } + + // 20.7.3.2, assignments + template + any& operator=(ValueType&& value) + { + static_assert(is_copy_constructible>::value, "ValueType must be copy-constructible"); + any(eastl::forward(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 + void emplace(Args&&... args) + { + typedef storage_handler> StorageHandlerT; + static_assert(eastl::is_constructible::value, "T must be constructible with Args..."); + + reset(); + StorageHandlerT::construct_inplace(m_storage, eastl::forward(args)...); + m_handler = &StorageHandlerT::handler_func; + } + + template + typename eastl::enable_if&, Args...>::value, void>::type + emplace(std::initializer_list il, Args&&... args) + { + typedef storage_handler> StorageHandlerT; + + reset(); + StorageHandlerT::construct_inplace(m_storage, il, eastl::forward(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(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 + inline ValueType any_cast(const any& operand) + { + static_assert(eastl::is_reference::value || eastl::is_copy_constructible::value, + "ValueType must be a reference or copy constructible"); + + auto* p = any_cast::type>::type>(&operand); + + if(p == nullptr) + Internal::DoBadAnyCast(); + + return *p; + } + + template + inline ValueType any_cast(any& operand) + { + static_assert(eastl::is_reference::value || eastl::is_copy_constructible::value, + "ValueType must be a reference or copy constructible"); + + auto* p = any_cast::type>(&operand); + + if(p == nullptr) + Internal::DoBadAnyCast(); + + return *p; + } + + template + inline ValueType any_cast(any&& operand) + { + static_assert(eastl::is_reference::value || eastl::is_copy_constructible::value, + "ValueType must be a reference or copy constructible"); + + auto* p = any_cast::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 + inline const ValueType* any_cast(const any* pAny) EA_NOEXCEPT + { + return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler>::handler_func) + #if EASTL_RTTI_ENABLED + && pAny->type() == typeid(typename remove_reference::type) + #endif + ) ? + static_cast(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) : + nullptr; + } + + template + inline ValueType* any_cast(any* pAny) EA_NOEXCEPT + { + return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler>::handler_func) + #if EASTL_RTTI_ENABLED + && pAny->type() == typeid(typename remove_reference::type) + #endif + ) ? + static_cast(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) : + nullptr; + } + + //Unsafe operations - use with caution + template + inline const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT + { + return unsafe_any_cast(const_cast(pAny)); + } + + template + inline ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT + { + return static_cast(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)); + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // make_any + // + #if EASTL_VARIADIC_TEMPLATES_ENABLED + template + inline any make_any(Args&&... args) + { + return any(eastl::in_place, eastl::forward(args)...); + } + + template + inline any make_any(std::initializer_list il, Args&&... args) + { + return any(eastl::in_place, il, eastl::forward(args)...); + } + #endif + +} // namespace eastl + +#endif // EASTL_ANY_H diff --git a/lib/EASTL/include/EASTL/array.h b/lib/EASTL/include/EASTL/array.h new file mode 100644 index 000000000..cb8ed5c67 --- /dev/null +++ b/lib/EASTL/include/EASTL/array.h @@ -0,0 +1,582 @@ +///////////////////////////////////////////////////////////////////////////// +// 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 +#include +#include +#include +#include + +#if EASTL_EXCEPTIONS_ENABLED + EA_DISABLE_ALL_VC_WARNINGS() + #include // 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 a = { { 0, 1, 2, 3, 4 } }; // Strict compilers such as GCC require the double brackets. + /// a[2] = 4; + /// for(array::iterator i = a.begin(); i < a.end(); ++i) + /// *i = 0; + /// + template + struct array + { + public: + typedef array 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 reverse_iterator; + typedef eastl::reverse_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 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); + + 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 array(T, U...) -> array; + #endif + + + /////////////////////////////////////////////////////////////////////// + // array + /////////////////////////////////////////////////////////////////////// + + + template + inline void array::fill(const value_type& value) + { + eastl::fill_n(&mValue[0], N, value); + } + + + template + inline void array::swap(this_type& x) EA_NOEXCEPT_IF(eastl::is_nothrow_swappable::value) + { + eastl::swap_ranges(&mValue[0], &mValue[N], &x.mValue[0]); + } + + + template + EA_CPP14_CONSTEXPR inline typename array::iterator + array::begin() EA_NOEXCEPT + { + return &mValue[0]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_iterator + array::begin() const EA_NOEXCEPT + { + return &mValue[0]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_iterator + array::cbegin() const EA_NOEXCEPT + { + return &mValue[0]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::iterator + array::end() EA_NOEXCEPT + { + return &mValue[N]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_iterator + array::end() const EA_NOEXCEPT + { + return &mValue[N]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_iterator + array::cend() const EA_NOEXCEPT + { + return &mValue[N]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::reverse_iterator + array::rbegin() EA_NOEXCEPT + { + return reverse_iterator(&mValue[N]); + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_reverse_iterator + array::rbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(&mValue[N]); + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_reverse_iterator + array::crbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(&mValue[N]); + } + + + template + EA_CPP14_CONSTEXPR inline typename array::reverse_iterator + array::rend() EA_NOEXCEPT + { + return reverse_iterator(&mValue[0]); + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_reverse_iterator + array::rend() const EA_NOEXCEPT + { + return const_reverse_iterator(static_cast(&mValue[0])); + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_reverse_iterator + array::crend() const EA_NOEXCEPT + { + return const_reverse_iterator(static_cast(&mValue[0])); + } + + + template + EA_CPP14_CONSTEXPR inline typename array::size_type + array::size() const EA_NOEXCEPT + { + return (size_type)N; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::size_type + array::max_size() const EA_NOEXCEPT + { + return (size_type)N; + } + + + template + EA_CPP14_CONSTEXPR inline bool array::empty() const EA_NOEXCEPT + { + return (N == 0); + } + + + template + EA_CPP14_CONSTEXPR inline typename array::reference + array::operator[](size_type i) + { + return mValue[i]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_reference + array::operator[](size_type i) const + { + return mValue[i]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::reference + array::front() + { + return mValue[0]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_reference + array::front() const + { + return mValue[0]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::reference + array::back() + { + return mValue[N - 1]; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_reference + array::back() const + { + return mValue[N - 1]; + } + + + template + EA_CPP14_CONSTEXPR inline T* array::data() EA_NOEXCEPT + { + return mValue; + } + + + template + EA_CPP14_CONSTEXPR inline const T* array::data() const EA_NOEXCEPT + { + return mValue; + } + + + template + EA_CPP14_CONSTEXPR inline typename array::const_reference array::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(mValue[i]); + } + + + template + EA_CPP14_CONSTEXPR inline typename array::reference array::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(mValue[i]); + } + + + template + inline bool array::validate() const + { + return true; // There is nothing to do. + } + + + template + inline int array::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 + EA_CPP14_CONSTEXPR inline bool operator==(const array& a, const array& b) + { + return eastl::equal(&a.mValue[0], &a.mValue[N], &b.mValue[0]); + } + + + template + EA_CPP14_CONSTEXPR inline bool operator<(const array& a, const array& b) + { + return eastl::lexicographical_compare(&a.mValue[0], &a.mValue[N], &b.mValue[0], &b.mValue[N]); + } + + + template + EA_CPP14_CONSTEXPR inline bool operator!=(const array& a, const array& b) + { + return !eastl::equal(&a.mValue[0], &a.mValue[N], &b.mValue[0]); + } + + + template + EA_CPP14_CONSTEXPR inline bool operator>(const array& a, const array& b) + { + return eastl::lexicographical_compare(&b.mValue[0], &b.mValue[N], &a.mValue[0], &a.mValue[N]); + } + + + template + EA_CPP14_CONSTEXPR inline bool operator<=(const array& a, const array& b) + { + return !eastl::lexicographical_compare(&b.mValue[0], &b.mValue[N], &a.mValue[0], &a.mValue[N]); + } + + + template + EA_CPP14_CONSTEXPR inline bool operator>=(const array& a, const array& b) + { + return !eastl::lexicographical_compare(&a.mValue[0], &a.mValue[N], &b.mValue[0], &b.mValue[N]); + } + + + template + inline void swap(array& a, array& b) + { + eastl::swap_ranges(&a.mValue[0], &a.mValue[N], &b.mValue[0]); + } + + + /////////////////////////////////////////////////////////////////////// + // to_array + /////////////////////////////////////////////////////////////////////// + namespace internal + { + template + EA_CONSTEXPR auto to_array(T (&a)[N], index_sequence) + { + return eastl::array, N>{{a[I]...}}; + } + + template + EA_CONSTEXPR auto to_array(T (&&a)[N], index_sequence) + { + return eastl::array, N>{{eastl::move(a[I])...}}; + } + } + + template + EA_CONSTEXPR eastl::array, N> to_array(T (&a)[N]) + { + static_assert(eastl::is_constructible_v, "element type T must be copy-initializable"); + static_assert(!eastl::is_array_v, "passing multidimensional arrays to to_array is ill-formed"); + return internal::to_array(a, eastl::make_index_sequence{}); + } + + template + EA_CONSTEXPR eastl::array, N> to_array(T (&&a)[N]) + { + static_assert(eastl::is_move_constructible_v, "element type T must be move-constructible"); + static_assert(!eastl::is_array_v, "passing multidimensional arrays to to_array is ill-formed"); + return internal::to_array(eastl::move(a), eastl::make_index_sequence{}); + } + +#if EASTL_TUPLE_ENABLED + + template + class tuple_size> : public integral_constant + { + }; + + template + class tuple_size> : public integral_constant + { + }; + + template + class tuple_element> + { + public: + using type = T; + }; + + template + class tuple_element> + { + public: + using type = const T; + }; + + template + struct GetArray + { + template + static EA_CONSTEXPR T& getInternal(array& a) + { + return a[I]; + } + + template + static EA_CONSTEXPR const T& getInternal(const array& a) + { + return a[I]; + } + + template + static EA_CONSTEXPR T&& getInternal(array&& a) + { + return eastl::forward(a[I]); + } + }; + + template + EA_CONSTEXPR tuple_element_t>& get(array& p) + { + return GetArray::getInternal(p); + } + + template + EA_CONSTEXPR const tuple_element_t>& get(const array& p) + { + return GetArray::getInternal(p); + } + + template + EA_CONSTEXPR tuple_element_t>&& get(array&& p) + { + return GetArray::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 + + template + class std::tuple_size<::eastl::array> : public ::eastl::integral_constant + { + }; + + template + struct std::tuple_element> + { + static_assert(I < N, "index is out of bounds"); + using type = T; + }; +#endif // EA_COMPILER_NO_STRUCTURED_BINDING + + +#endif // Header include guard + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/atomic.h b/lib/EASTL/include/EASTL/atomic.h new file mode 100644 index 000000000..27117e9c3 --- /dev/null +++ b/lib/EASTL/include/EASTL/atomic.h @@ -0,0 +1,1772 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_H +#define EASTL_ATOMIC_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// Below is the documentation of the API of the eastl::atomic library. +// This includes class and free functions. +// Anything marked with a '+' in front of the name is an extension to the std API. +// + + +///////////////////////////////////////////////////////////////////////////////// +// +// eastl::atomic memory_order API +// +// See below for full explanations on the memory orders and their guarantees. +// +// - eastl::memory_order_relaxed +// - eastl::memory_order_acquire +// - eastl::memory_order_release +// - eastl::memory_order_acq_rel +// - eastl::memory_order_seq_cst +// - +eastl::memory_order_read_depends +// + + +///////////////////////////////////////////////////////////////////////////////// +// +// eastl::atomic class API +// +// All jargon and prerequisite knowledge is explained below. +// +// Unless otherwise specified all orders except read_depends is a valid order +// on the given operation. +// Unless otherwise specified all operations are valid on all types T. +// If no order is provided, seq_cst memory ordering is used for the operation. +// +// - atomic() : Value-initializes the underlying object as T{}. +// +// - atomic(T) : Initializes the underlying object with a copy of T. +// +// - T operator=(T) : Atomically assigns T as store(T, seq_cst). +// +// - is_lock_free() : true if the operations are lockfree. Always true for eastl. +// +// - store(T, order) : Atomically stores T affecting memory according to order. +// : Valid orders are relaxed, release, and seq_cst. +// +// - T load(order) : Atomically loads T affecting memory according to order. +// : Valid orders are relaxed, acquire, and seq_cst. +// : If T is a pointer type, read_depends is another valid order. +// +// - operator T() : Atomically loads T as load(T, seq_cst). +// +// - T exchange(T, order) : Atomically performs a RMW that replaces the current value with T. +// : Memory is affected according to order. +// : Returns the previous value stored before the RMW operation. +// +// - bool compare_exchange_weak(T&, T, successOrder, failOrder) +// : Atomically compares the value stored with that of T& and if equal replaces it with T. +// : This is a RMW operation. +// : If the comparison fails, loads the observed value into T&. This is a load operation. +// : Memory is affected in the RMW operation according to successOrder. +// : Memory is affected in the load operation according to failOrder. +// : failOrder cannot be a stronger order than successOrder. +// : Returns true or false if the comparison succeeded and T was stored into the atomic object. +// : +// : The weak variant may fail even if the observed value of the atomic object equals T&. +// : This can yield performance gains on platforms with ld/str exclusive pair instructions especially +// : when the compare_exchange operation is done in a loop. +// : Only the bool return value can be used to determine if the operation was successful. +// +// - bool compare_exchange_weak(T&, T, order) +// : Same as the above except that order is used for both the RMW and the load operation. +// : If order == acq_rel then the order of the load operation equals acquire. +// : If order == release then the order of the load operation equals relaxed. +// +// - bool compare_exchange_strong(T&, T, successOrder, failOrder) +// - bool compare_exchange_strong(T&, T, order) +// : This operation is the same as the above weak variants +// : expect that it will not fail spuriously if the value stored equals T&. +// +// The below operations are only valid for Integral types. +// +// - T fetch_add(T, order) +// : Atomically performs a RMW that increments the value stored with T. +// : Returns the previous value stored before the RMW operation. +// - T fetch_sub(T, order) +// : Atomically performs a RMW that decrements the value stored with T. +// : Returns the previous value stored before the RMW operation. +// - T fetch_and(T, order) +// : Atomically performs a RMW that bit-wise and's the value stored with T. +// : Returns the previous value stored before the RMW operation. +// - T fetch_or(T, order) +// : Atomically performs a RMW that bit-wise or's the value stored with T. +// : Returns the previous value stored before the RMW operation. +// - T fetch_xor(T, order) +// : Atomically performs a RMW that bit-wise xor's the value stored with T. +// : Returns the previous value stored before the RMW operation. +// +// - +T add_fetch(T, order) +// : Atomically performs a RMW that increments the value stored with T. +// : Returns the new updated value after the operation. +// - +T sub_fetch(T, order) +// : Atomically performs a RMW that decrements the value stored with T. +// : Returns the new updated value after the operation. +// - +T and_fetch(T, order) +// : Atomically performs a RMW that bit-wise and's the value stored with T. +// : Returns the new updated value after the operation. +// - +T or_fetch(T, order) +// : Atomically performs a RMW that bit-wise or's the value stored with T. +// : Returns the new updated value after the operation. +// - +T xor_fetch(T, order) +// : Atomically performs a RMW that bit-wise xor's the value stored with T. +// : Returns the new updated value after the operation. +// +// - T operator++/--() +// : Atomically increments or decrements the atomic value by one. +// : Returns the previous value stored before the RMW operation. +// : Memory is affected according to seq_cst ordering. +// +// - T ++/--operator() +// : Atomically increments or decrements the atomic value by one. +// : Returns the new updated value after the RMW operation. +// : Memory is affected according to seq_cst ordering. +// +// - T operator+=/-=/&=/|=/^=(T) +// : Atomically adds, subtracts, bitwise and/or/xor the atomic object with T. +// : Returns the new updated value after the operation. +// : Memory is affected according to seq_cst ordering. +// +// +// The below operations are only valid for Pointer types +// +// - T* fetch_add(ptrdiff_t val, order) +// : Atomically performs a RMW that increments the value store with sizeof(T) * val +// : Returns the previous value stored before the RMW operation. +// - T* fetch_sub(ptrdiff_t val, order) +// : Atomically performs a RMW that decrements the value store with sizeof(T) * val +// : Returns the previous value stored before the RMW operation. +// +// - +T* add_fetch(ptrdiff_t val, order) +// : Atomically performs a RMW that increments the value store with sizeof(T) * val +// : Returns the new updated value after the operation. +// - +T* sub_fetch(ptrdiff_t val, order) +// : Atomically performs a RMW that decrements the value store with sizeof(T) * val +// : Returns the new updated value after the operation. +// +// - T* operator++/--() +// : Atomically increments or decrements the atomic value by sizeof(T) * 1. +// : Returns the previous value stored before the RMW operation. +// : Memory is affected according to seq_cst ordering. +// +// - T* ++/--operator() +// : Atomically increments or decrements the atomic value by sizeof(T) * 1. +// : Returns the new updated value after the RMW operation. +// : Memory is affected according to seq_cst ordering. +// +// +// - +EASTL_ATOMIC_HAS_[len]BIT Macro Definitions +// These macros provide the ability to compile-time switch on the availability of support for the specific +// bit width of an atomic object. +// Example: +// +// #if defined(EASTL_ATOMIC_HAS_128BIT) +// #endif +// +// Indicates the support for 128-bit atomic operations on an eastl::atomic object. +// + + +///////////////////////////////////////////////////////////////////////////////// +// +// eastl::atomic_flag class API +// +// Unless otherwise specified all orders except read_depends is a valid order +// on the given operation. +// +// - atomic_flag() : Initializes the flag to false. +// +// - clear(order) +// : Atomically stores the value false to the flag. +// : Valid orders are relaxed, release, and seq_cst. +// +// - bool test_and_set(order) +// : Atomically exchanges flag with true and returns the previous value that was held. +// +// - bool test(order) +// : Atomically loads the flag value. +// : Valid orders are relaxed, acquire, and seq_cst. +// + + +///////////////////////////////////////////////////////////////////////////////// +// +// eastl::atomic standalone free function API +// +// All class methods have a standalone free function that takes a pointer to the +// atomic object as the first argument. These functions just call the correct method +// on the atomic object for the given operation. +// These functions come in two variants, a non-explicit and an explicit variant +// that take on the form atomic_op() and atomic_op_explicit() respectively. +// The non-explicit variants take no order arguments and thus are all seq_cst. +// The explicit variants take an order argument. +// Only the standalone functions that do not have a class method equivalent pair will be +// documented here which includes all new extensions to the std API. +// +// - +compiler_barrier() +// : Read-Write Compiler Barrier. +// - +compiler_barrier_data_dependency(const T&) +// : Read-Write Compiler Barrier. +// : Applies a fake input dependency on const T& so the compiler believes said variable is used. +// : Useful for example when writing benchmark or testing code with local variables that must not get dead-store eliminated. +// - +cpu_pause() +// : Prevents speculative memory order violations in spin-wait loops. +// : Allows giving up core resources, execution units, to other threads while in spin-wait loops. +// - atomic_thread_fence(order) +// : Read docs below. +// - atomic_signal_fence(order) +// : Prevents reordering with a signal handler. +// - +atomic_load_cond(const eastl::atomic*, Predicate) +// : continuously loads the atomic object until Predicate is true +// : will properly ensure the spin-wait loop is optimal +// : very useful when needing to spin-wait for some condition to be true which is common is many lock-free algorithms +// : Memory is affected according to seq_cst ordering. +// - +atomic_load_cond_explicit(const eastl::atomic*, Predicate, Order) +// : Same as above but takes an order for how memory is affected +// + + +///////////////////////////////////////////////////////////////////////////////// +// +// Deviations from the standard. This does not include new features added: +// +// 1. +// Description: Atomics are always lock free +// Reasoning : We don't want people to fall into performance traps where implicit locking +// is done. If your user defined type is large enough to not support atomic +// instructions then your user code should do the locking. +// +// 2. +// Description: Atomic objects can not be volatile +// Reasoning : Volatile objects do not make sense in the context of eastl::atomic. +// Use the given memory orders to get the ordering you need. +// Atomic objects have to become visible on the bus. See below for details. +// +// 3. +// Description: Consume memory order is not supported +// Reasoning : See below for the reasoning. +// +// 4. +// Description: ATOMIC_INIT() macros and the ATOMIC_LOCK_FREE macros are not implemented +// Reasoning : Use the is_lock_free() method instead of the macros. +// ATOMIC_INIT() macros aren't needed since the default constructor value initializes. +// +// 5. +// Description: compare_exchange failure memory order cannot be stronger than success memory order +// Reasoning : Besides the argument that it ideologically does not make sense that a failure +// of the atomic operation shouldn't have a stricter ordering guarantee than the +// success of it; if that is required then just make the whole operation stronger. +// This ability was added and allowed in C++17 only which makes supporting multiple +// C++ versions harder when using the compiler provided intrinsics since their behaviour +// is reliant on the C++ version being compiled. Also makes it harder to reason about code +// using these atomic ops since C++ versions vary the behaviour. We have also noticed +// that versions of compilers that say they support C++17 do not properly adhere to this +// new requirement in their intrinsics. Thus we will not support this. +// +// 6. +// Description: All memory orders are distinct types instead of enum values +// Reasoning : This will not affect how the API is used in user code. +// It allows us to statically assert on invalid memory orders since they are compile-time types +// instead of potentially runtime enum values. +// Allows for more efficient code gen without the use of switch statements or if-else conditionals +// on the memory order enum values on compilers that do not provide intrinsics that take in a +// memory order, such as MSVC, especially in debug and debug-opt builds. +// + + +///////////////////////////////////////////////////////////////////////////////// +// +// ******** DISCLAIMER ******** +// +// This documentation is not meant to provide rigorous proofs on the memory models +// of specific architectures or the C++ memory model introduced in C++11. It is not +// meant to provide formal mathematical definitions and logic that shows that a given +// implementation adheres to the C++ memory model. This isn't meant to be some infallible +// oracle on memory models, barriers, observers, and architecture implementation details. +// What I do hope a reader gets out of this is the following. An understanding of the C++ +// memory model and how that relates to implementations on various architectures. Various +// phenomena and ways that compilers and architectures can steer away from a sequentially +// consistent system. To provide examples on how to use this library with common patterns +// that will be seen in many code bases. Lastly I would like to provide insight and +// further readings into the lesser known topics that aren't shared outside people +// who live in this space and why certain things are done the way they are +// such as cumulativity of memory barriers as one example. Sometimes specifying barriers +// as LDLD/LDST/STST/STLD doesn't actually cut it, and finer grain semantics are needed +// to describe cumulativity of memory barriers. +// +// ******** Layout of the Documentation ******** +// +// This document will first go through a variety of different hardware architectures with examples of the various kinds of +// reordering that is allowed by these architectures. We will use the memory barriers provided by the hardware to "fix" these +// examples. +// Then we will introduce the C++ memory model and revisit the examples using the platform agnostic abstract memory model to "fix" +// them. +// The hope here is that we get a sense of the various types of architectures and weak memory consistency provided by them and thus +// an appreciation for the design of the C++ abstract memory model. +// +// ******** REFERENCES ******** +// [1] Dekker's mutual exclusion algorithm made RW-safe +// [2] Handling Memory Ordering in Multithreaded Applications with Oracle Solaris +// [3] Evaluating the Cost of Atomic Operations on Modern Architectures +// [4] A Tutorial Introduction to the ARM and POWER Relaxed Memory Models +// [5] Memory Barriers: a Hardware View for Software Hackers +// [6] Memory Model = Instruction Reordering + Store Atomicity +// [7] ArMOR: Defending Against Memory Consistency Model Mismatches in Heterogeneous Architectures +// [8] Weak Memory Models: Balancing Definitional Simplicity and Implementation Flexibility +// [9] Repairing Sequential Consistency in C/C++11 +// [10] A high-level operational semantics for hardware weak memory models +// [11] x86-TSO: A Rigorous and Usable Programmer's Model for x86 Multiprocessors +// [12] Simplifying ARM Concurrency: Multicopy-Atomic Axiomatic and Operational Models for ARMv8 +// [13] Mixed-size Concurrency: ARM, POWER, C/C++11, and SC +// [14] P0668R4: Revising the C++ memory model +// [15] Constructing a Weak Memory Model +// [16] The Superfluous Load Queue +// [17] P0190R1: Proposal for New memory_order_consume Definition +// +// ******** What does it mean to be Atomic? ******** +// +// The word atomic has been overloaded and can mean a lot of different things depending on the context, +// so let's digest it. +// +// The first attribute for something to be atomic is that concurrent stores and loads +// must not tear or shear. This means if two threads write 0x01 and 0x02 at the same time +// then the only values that should ever be observed is 0x01 or 0x02. We can only see +// the whole write of 0x01 or 0x02, not 0x03 as an example. Many algorithms rely on +// this property; only very few such a Dekker's algorithm for mutual exclusion don't. +// Well actually a recent paper, [1], showed that Dekker's isn't safe without atomic +// loads and stores so this property is pretty fundamental and also hard to prove that +// your algorithm is safe without this property on loads and stores. +// +// We need to ensure the compiler emits a single load instruction. +// If we are doing 64-bit loads on a 32-bit platform, we need to ensure the load is one +// instruction instead of 2 32-bit loads into two registers. +// Another example is if we have this struct, struct { int32_t i; int32_t k; }, even on +// a 64-bit system we have to ensure the compiler does one 64-bit load and not two +// 32-bit loads for each individual member. +// +// We also need to ensure the correct instruction is emitted. A general load instruction +// to do a 64-bit load on a 32-bit platform may perform a 64-bit load but it may not +// be atomic, it may be turned into two 32-bit loads behind the scenes in the cpu. +// For example on ARMv7 we would have to use ldrexd not ldrd for 64-bit loads +// on a 32-bit ARMv7 core. +// +// An operation may be considered atomic if multiple sub-operations are done as one +// transactional unit. This is commonly known as a Read-Modify-Write, RMW, operation. +// Take a simple add operation; it is actually a load from memory into a register, +// a modification of said register and then a store back to memory. If two threads +// concurrently execute this add operation on the same memory location; any interleaving +// of the 3 sub-operations is possible. It is possible that if the initial value is 0, +// the result may be 1 because each thread executed in lockstep both loading 0, adding 1 +// and then storing 1. A RMW operation may be considered atomic if the whole sequence of +// sub-operations are serialized as one transactional unit. +// +// Atomicity may also refer to the order in which memory operations are observed and the +// dependencies between memory operations to different memory locations. As a quick example +// into the very thing we will be deep diving into that is not very intuitive. If I do, [STORE(A, 2); STORE(B, 1);], +// in one thread and another thread does, [r0 = LOAD(B); r1 = LOAD(A);]; if r0 == 1, thus we observed +// the store to B, will we observe r1 == 2. Our intuition tells us that well A was stored +// first and then B, so if I read the new value of B then I must also read the new value +// of A since the store to A happened before B so if I can see B then I must be able to +// see everything before B which includes A. +// This highlights the ordering of memory operations and why memory barriers and memory +// models are so heavily attached to atomic operations because one could classify something +// is atomic if the dependency highlighted in the above example is allowed to be maintained. +// +// This is what people mean when you hear that volatile does NOT mean atomicity of the operation. +// Usually people imply a lot of implicit assumptions when they mark a variable as volatile. +// All volatile gives us is the ability to tell the compiler it may not assume anything +// about the state of that memory location. This means the compiler must always emit a load +// or store instruction, cannot perform constant folding, dead-store elimination, or +// do any sort of code movement on volatile variables. +// +// ******** Preliminary Basics ******** +// +// It is expected that the reader understands what a cache is, how it is organized and how data +// is chunked into cachelines. It is helpful if the reader understands basic cache coherency +// protocols such as MSI or MESI. +// It is expected the reader understands alignment, especially natural alignment +// of the processor and why alignment is important for data access. +// The reader should have some understanding of how a processor executes instructions, +// basics of what Out-of-Order execution means and basics of what speculative execution means. +// It is expected that the reader has an understanding of threading, multi-threaded programming +// and the use of concurrency primitives such as mutexes. +// Memory Barrier, Barrier, Memory Fence and Fence are all interchangeable synonyms. +// +// Independent memory operations can be performed or observed, depending on your perspective, +// in any order as long as the local cpu thinks its execution is happening in program order. +// This can be a problem for inter-cpu communications and thus we need some way to enforce +// that the compiler does not reorder instructions and that the cpu also does not reorder +// instructions. This is what a barrier is, it is an enforcement of ordering on memory instructions, +// so as the name suggests a barrier. Barriers can be one-sided or both-sided which means +// the barrier enforces a partial order above or below or on both sides of said barrier. +// +// Processors will use tricks such as out-of-order execution, memory instruction buffering and +// combining, speculative loads and speculative execution, branch prediction and many types of caching even +// in various interconnects from the cpu to the memory itself. One key thing to note is that cpus +// do not physically reorder the instruction stream. Instructions are dispatched and retired +// in-order but executed out-of-order. Memory barriers will prevent these tricks from happening +// by controlling the interaction of multiple cpus. +// +// Compilers will morph your code and physically move instructions around as long as the program +// has the same observed behaviour. This is becoming increasingly true with more optimization techniques +// such as Link Time Optimization becoming the norm where once people assumed compilers couldn't assume +// something outside the given TU and now because they have the whole program view they know everything. +// This means the compiler does indeed alter the instruction stream +// and compiler barriers are a way to tell them to not move any memory instructions across the barrier. +// This does not prevent a compiler from doing optimizations such as constant folding, merging of +// overlapping loads, or even dead store elimination. Compiler barriers are also very cheap and +// have zero impact on anything that the compiler knows isn't visible in memory such as local variables +// whose addresses do not escape the function even if their address is taken. You can think of it +// in terms of a sequence point as used with "volatile" qualified variables to denote a place in code where +// things must be stable and the compiler doesn't cache any variables in registers or do any reordering. +// +// Memory Barriers come in many flavours that instill a partial or full ordering on memory operations. +// Some memory operations themselves have implicit ordering guarantees already, for example +// Total-Store Order, TSO, architectures like x86 guarantee that a store operation cannot be reordered with a +// previous store operation thus a memory barrier that only orders stores is not needed +// on this architecture other than ensuring the compiler doesn't do any shenanigans. +// Considering we have 4 permutations of memory operations; a common way to describe an ordering +// is via Load-Load/LDLD, Load-Store/LDST, Store-Store/STST or Store-Load/STLD notation. You read this +// notation as follows; STLD memory barrier means a load cannot be reordered with a previous store. +// For example, on TSO architecture we can say all stores provide a STST memory barrier, +// since a store cannot be reordered with a previous store. +// +// Memory Barriers in itself are not a magic bullet, they come with caveats that must be known. +// Each cpu architecture also has its own flavours and guarantees provided by said memory barriers. +// There is no guarantee that memory instructions specified before a memory barrier will complete, +// be written to memory or fully propagated throughout the rest of the system, when the memory barrier +// instruction completes. The memory barrier creates a point in that local cpus queue of memory instructions +// whereby they must not cross. There is no guarantee that using a memory barrier on one cpu will have +// any effect at all on another remote cpu's observed view of memory. This also implies that executing +// a memory barrier does not hinder, incur, stall or enforce any other cpus to serialize with each other cpu. +// In order for a remote cpu to observe the correct effects it must also use a matching memory barrier. +// This means code communicating in 2 threads through memory must both be employing the use of memory barriers. +// For example, a store memory barrier that only orders stores, STST, in one thread must be paired with a load memory barrier +// that only orders loads, LDLD, in the other thread trying to observe those stores in the correct order. +// +// ******** Memory Types && Devices ******** +// +// eastl::atomic and accompanying memory barriers ONLY ORDER MEMORY to cpu-to-cpu communication through whatever the +// processor designates as normal cacheable memory. It does not order memory to devices. It does not provide any DMA ordering guarantees. +// It does not order memory with other memory types such as Write Combining. It strictly orders memory only to shared memory that is used +// to communicate between cpus only. +// +// ******** Sequentially Consistent Machine ******** +// +// The most intuitive as well as the model people naturally expect a concurrent system to have is Sequential Consistency. +// You may have or definitely have heard this term if you dealt with any type of distributed system. Lamport's definition +// articulates this consistency model the best. +// Leslie Lamport: "the result of any execution is the same as if the operations of all the processors were executed in some +// sequential order, and the operations of each individual processor appear in this sequence in the order +// specified by its program". +// +// A Sequentially Consistent machine is modelled as follows: +// +// ------------ ------------ +// | Thread 0 | ... | Thread N | +// ------------ ------------ +// | | | | +// | | | | +// ---------------------------------------- +// | | +// | Shared Memory | +// | | +// ---------------------------------------- +// +// This is a sequentially consistent machine. Each thread is executing instructions in program order which does loads and stores +// that are serialized in some order to the shared memory. This means all communication is done through the shared memory with one cpu +// doing one access at a time. This system has a couple key properties. +// +// 1. There is no local cpu memory reordering. Each cpu executes instructions in program order and all loads and stores must complete, +// be visible in the shared memory or be visible in a register before starting the next instruction. +// 2. Each memory operation becomes visible to all cpus at the same time. If a store hits the shared memory, then all subsequent loads +// from every other cpu will always see the latest store. +// +// A Sequentially Consistent machine has, Single-Copy Store Atomicity: All stores must become visible to all cores in the system at the same time. +// +// ******** Adding Caches ******** +// +// Caches by nature implicitly add the potential for memory reordering. A centralized shared snoopy bus that we all learned in school +// makes it easy to implement sequential consistency with caches. Writes and reads are all serialized in a total order via the cache bus transaction +// ordering. Every modern day bus is not inorder, and most certainly not a shared centralized bus. Cache coherency guarantees that all memory operations +// will be propagated eventually to all parties, but it doesn't guarantee in what order or in what time frame. Once you add +// caches, various levels of caching and various interconnects between remote cpus, you inevitably run into the issue where +// some cpus observe the effects of a store before other cpus. Obviously we have weakly-ordered and strongly-ordered cpus with +// caches so why is that? The short answer is, where is the onus put, is it on the programmer or the hardware. Does the hardware +// have dependency tracking, is it able to determine when a memory order violation occurs such as rolling back its speculative execution +// and also how far along the chain of interconnects does the hardware wait before it determines that the memory operation has +// been acknowledged or is considered to satisfy its memory ordering guarantees. Again this is a very high level view of the system +// as a whole, but the takeaway is yes; caches do add the potential for reordering but other supporting hardware determines whether +// that is observable by the programmer. There is also some debate whether weakly-ordered processors are actually more performant +// than strongly-ordered cpus eluding to the fact that the hardware has a better picture of what is a violation versus the programmer +// having to emit far more barriers on weakly-ordered architectures in multi-threaded code which may actually not be needed because the +// hardware didn't commit a violation but it may have and we as the programmer cannot rely on may haves. +// +// ******** Store Buffers ******** +// +// Obviously having all stores serialize results in unnecessary stalls. Store buffers alleviate this issue. +// Store buffers are simple fixed size structures that sit between the cpu and the memory hierarchy. This allows +// each cpu to record its write in the store buffer and then move onto the next instruction. The store buffer will +// eventually be flushed to the resulting memory hierarchy in FIFO order. How and when this flushing occurs is irrelevant to the +// understanding of a store buffer. A read from an address will grab the most recent write to the same address in the store buffer. +// +// The introduction of a store buffer is our first dive into weaker memory consistency. The addition of this hardware turns the consistency model weaker, +// into one that is commonly known as TSO, Total-Store Order. This is the exact model used by x86 cpus and we will see what this means +// and what new effects are observed with the addition of the store buffer. Below is a diagram of how the machine may now look. +// This type of store buffer is known as a FIFO store buffer, FIFO write buffer, or Load/Store Queue in some literature. This type of +// store buffer introduces STLD reordering but still prevents STST reordering. We will take a look at another type of store buffer later. +// Even with this store buffer, stores to the same address can still be merged so that only the latest store is written to the cache assuming +// no other intermediary stores happen. x86 cpus do write merging even for consecutive stores, i.e. storing to A and A+1 can be merged into one two-byte store. +// +// ------------ ------------ +// | Thread 0 | ... | Thread N | +// ------------ ------------ +// | | | | +// | | | | +// | Store | | Store | +// | Buffer | | Buffer | +// | | | | +// ---------------------------------------- +// | | +// | Shared Memory | +// | | +// ---------------------------------------- +// +// ---- Store-Buffering / Dekker's Example ---- +// This is a very common litmus test that showcases the introduction of STLD reordering. It is called Store-Buffering example because it is the only weaker +// behaviour observed under TSO and also called Dekker's Example as it famously breaks Dekker's mutual exclusion algorithm. +// +// --------------------------- +// Initial State: +// x = 0; y = 0; +// --------------------------- +// Thread 0 | Thread 1 +// --------------------------- +// STORE(x, 1) | STORE(y, 1) +// r0 = LOAD(y) | r1 = LOAD(x) +// --------------------------- +// Observed: r0 = 0 && r1 = 0 +// --------------------------- +// +// We would normally assume that any interleaving of the two threads cannot possibly end up with both loads reading 0. We assume that the observed outcome +// of r0 = 0 && r1 = 0 to be impossible, clearly that is not the case. Let's start by understanding the example with no reordering possible. Both threads +// run and their first instruction is to write the value 1 into either x or y, the next instruction then loads from the opposite variable. This means no +// matter the interleaving, one of the loads always executes after the other thread's store to that variable. +// We could observe r0 = 1 && r1 = 1 if both threads execute in lockstep. +// We could observe r0 = 0 && r1 = 1 if thread 0 executes and then thread 1 executes. +// We could observe r0 = 1 && r1 = 0 if thread 1 executes and then thread 0 executes. +// Since the stores always execute before that load in the other thread, one thread must always at least observe a store, so let's see why store buffers break this. +// +// What will happen is that STORE(x, 1) is stored to the store buffer but not made globally visible yet. +// STORE(y, 1) is written to the store buffer and also is not made globally visible yet. +// Both loads now read the initial state of x and y which is 0. We got the r0 = 0 && r1 = 0 outcome and just observed a Store-Load reordering. +// It has appeared as if the loads have been reordered with the previous stores and thus executed before the stores. +// Notice even if we execute the instructions in order, a series of other hardware side effects made it appear as if the instructions have been reordered. +// We can solve this by placing a Store-Load barrier after the store and before the load as follows. +// +// --------------------------- +// Thread 0 | Thread 1 +// --------------------------- +// STORE(x, 1) | STORE(y, 1) +// STLD BARRIER | STLD BARRIER +// r0 = LOAD(y) | r1 = LOAD(x) +// --------------------------- +// +// This STLD barrier effectively will flush the store buffer into the memory hierarchy ensuring all stores in the buffer are visible to all other cpus at the same time +// before executing the load instruction. Again nothing prevents a potential hardware from speculatively executing the load even with the STLD barrier, the hardware will have to do +// a proper rollback if it detected a memory order violation otherwise it can continue on with its speculative load. The barrier just delimits a stability point. +// +// Most hardware does not provide granular barrier semantics such as STLD. Most provide a write memory barrier which only orders stores, STST, a read memory barrier +// which only orders loads, LDLD, and then a full memory barrier which is all 4 permutations. So on x86 we will have to use the mfence, memory fence, instruction +// which is a full memory barrier to get our desired STLD requirements. +// +// TSO also has the property that we call, Multi-Copy Store Atomicity. This means a cpu sees its own stores before they become visible to other cpus, +// by forwarding them from the store buffer, but a store becomes visible to all other cpus at the same time when flushed from the store buffer. +// +// +// Let's look at a non-FIFO store buffer now as seen in ARM cpus as an example and we will use a standard Message Passing example to see how it manifests in even weaker consistency. +// A store buffer on ARM as an example allows write merging even with adjacent stores, is not a FIFO queue, any stores in the small hardware hash table may be ejected at any point +// due to a collision eviction or the availability of cachelines in the cache hierarchy meaning that stores may bypass the buffer entirely if that cacheline is already owned by that cpu. +// There is no guarantee that stores will be completed in order as in the FIFO case. +// +// --------------------------- +// Initial State: +// x = 0; y = 0; +// --------------------------- +// Thread 0 | Thread 1 +// --------------------------- +// STORE(x, 1) | while(LOAD(y) == 0); +// STORE(y, 1) | r0 = LOAD(x) +// --------------------------- +// Observed: r0 = 0 +// --------------------------- +// +// This is a classic Message Passing example that is very commonly used in production code. We store some values and then set a flag, STORE(y, 1) in this case. +// The other thread waits until the flag is observed and then reads the value out of x. If we observed the flag then we should obviously see all stores before the flag was set. +// Given our familiarity with TSO consistency above we know this definitely works on TSO and it is impossible to observe the load of x returning 0 under that consistency model. +// Let's see how this breaks with a non-FIFO store buffer. +// +// Thread 0 executes the STORE(x, 1) but the cacheline for x is not in thread 0's cache so we write to the store buffer and wait for the cacheline. +// Thread 1 executes the LOAD(y) and it also does not have y in its cacheline so it waits before completing the load. +// Thread 0 moves on to STORE(y, 1). It owns this cacheline, hypothetically, so it may bypass the store buffer and store directly to the cache. +// Thread 0 receives a message that Thread 1 needs y's cacheline, so it transfers the now modified cacheline to Thread 1. +// Thread 1 completes the load with the updated value of y = 1 and branches out of the while loop since we saw the new value of y. +// Thread 1 executes LOAD(x) which will return 0 since Thread 0 still hasn't flushed its store buffer waiting for x's cacheline. +// Thread 0 receives x's cacheline and now flushes x = 1 to the cache. Thread 1 will also have invalidated its cacheline for x that it brought in via the previous load. +// +// We have now fallen victim to STST reordering, allowing Thread 1 to observe a load of x returning 0. Not only does this store buffer allow STLD reordering due to the nature of +// buffering stores, but it also allows another reordering; that of Store-Store reordering. It was observed as if Thread 0 executed STORE(y, 1) before STORE(x, 1) which completely +// broke our simple message passing scenario. +// +// --------------------------- +// Thread 0 | Thread 1 +// --------------------------- +// STORE(x, 1) | while(LOAD(y) == 0); +// STST BARRIER | +// STORE(y, 1) | r0 = LOAD(x) +// --------------------------- +// +// The STST memory barrier effectively ensures that the cpu will flush its store buffer before executing any subsequent stores. That is not entirely true, the cpu is still allowed +// to continue and execute stores to the store buffer as long as it doesn't flush them to the cache before the previous stores are flushed to the cache. If nothing becomes +// globally visible out of order then we are good. +// The example above will change how the processor executes due to the STST memory barrier. Thread 0 will execute STORE(y, 1), write to the store buffer and mark all current entries. Even though it owns the cacheline +// it cannot write the store to the cache until all marked entries, which are all the previous stores, are flushed to the cache. We have now fixed the message passing code by adding +// a STST or write memory barrier and thus it is no longer possible to observe the load of x returning 0. +// +// ******** Invalidation Queues ******** +// +// Due to the cache coherency protocol in play, a write to a cacheline will have to send invalidation messages to all other cpus that may have that cacheline as well. +// Immediately executing and responding to invalidation messages can cause quite a stall especially if the cache is busy at the moment with other requests. +// The longer we wait to invalidate the cacheline, the longer the remote cpu doing the write is stalled waiting on us. We don't like this very much. +// Invalidation Queues are just that, we queue up the action of actually invalidating the cacheline but immediately respond to the request saying we did it anyway. +// Now the remote cpu thinks we invalidated said cacheline but actually it may very well still be in our cache ready to be read from. We just got weaker again, let's +// see how this manifests in code by starting from the end of our previous example. +// +// --------------------------- +// Initial State: +// x = 0; y = 0; +// --------------------------- +// Thread 0 | Thread 1 +// --------------------------- +// STORE(x, 1) | while(LOAD(y) == 0); +// STST BARRIER | +// STORE(y, 1) | r0 = LOAD(x) +// --------------------------- +// Observed: r0 = 0 +// --------------------------- +// +// Thread 1 receives the invalidate x's cacheline message and queues it because it is busy. +// Thread 1 receives the invalidate y's cacheline message, but we don't have that cacheline so acknowledge immediately. +// Thread 1 executes LOAD(y), loads in y's cacheline and branches out of the loop. +// Thread 1 executes LOAD(x), and loads from the cache the old value of x because the invalidation message is still sitting in the invalidation queue. +// +// We have just again observed the load of x returning 0 but from a different type of reordering now on the reader side. +// This is a form of LDLD, Load-Load, reordering as it appears as if LOAD(x) was executed before LOAD(y). This can be fixed as follows. +// +// --------------------------- +// Thread 0 | Thread 1 +// --------------------------- +// STORE(x, 1) | while(LOAD(y) == 0); +// STST BARRIER | LDLD BARRIER +// STORE(y, 1) | r0 = LOAD(x) +// --------------------------- +// +// The LDLD memory barrier essentially marks all entries currently in the invalidation queue. Any subsequent load must wait until all the marked entries have been +// processed. This ensures once we observe y = 1, we process all entries that came before y and that way we observe all the stores that happened before y. +// The insertion of the read memory barrier creates the required memory barrier pairing as discussed above and ensures that now our code executes as expected. +// +// It must be made clear that these are not the only hardware structure additions or ways that can relax STST, STLD and LDLD orderings. These are merely +// 2 structures that are common and ones that I choose to use as examples of how hardware can reduce ordering guarantees. Knowing how the hardware does this +// isn't always entirely clear but having a model that tells us what operations can be reordered is all we need to be able to reason about our code when executing on that hardware. +// +// ******** Load Buffering ******** +// +// The analog of the Store Buffering example, this litmus test has two threads read from two different locations and then write to the other locations. +// The outcome of having LDST reordering is allowed and observable on many processors such as ARM. +// +// --------------------------- +// Initial State: +// x = 0; y = 0; +// --------------------------- +// Thread 0 | Thread 1 +// --------------------------- +// r0 = LOAD(x) | r1 = LOAD(y) +// STORE(y, 1) | STORE(x, 1) +// --------------------------- +// Observed: r0 = 1 && r1 = 1 +// --------------------------- +// +// This is possible because the processor does not have to wait for the other cpu's cacheline to arrive before storing into the cache. +// Assume Thread 0 owns y's cacheline and Thread 1 owns x's cacheline. +// The processor may execute the load and thus buffer the load waiting for the cacheline to arrive. +// The processor may continue onto the store and since each cpu owns their respective cacheline, store the result into the cache. +// The cpus now receive the cachelines for x and y with the now modified value. +// We have just observed the loads returning 1 and thus observed LDST reordering. +// +// To forbid such outcome it suffices to add any full memory barrier to both threads or a local Read-After-Write/Read-To-Write dependency or a control dependency. +// +// ------------------------------- +// Thread 0 | Thread 1 +// ------------------------------- +// r0 = LOAD(x) | r1 = LOAD(y) +// if (r0 == 1) | if (r1 == 1) +// STORE(y, 1) | STORE(x, 1) +// ------------------------------- +// +// ----------------------------------------------------- +// Thread 0 | Thread 1 +// ----------------------------------------------------- +// r0 = LOAD(x) | r1 = LOAD(y) +// STORE(&(y + r0 - r1), 1) | STORE(&(x + r1 - r1), 1) +// ----------------------------------------------------- +// +// Both fixes above ensure that both writes cannot be committed, made globally visible, until their program source code order preceding reads have been fully satisfied. +// +// ******** Compiler Barriers ******** +// +// Compiler barriers are both-sided barriers that prevent loads and stores from moving down past the compiler barrier and +// loads and stores from moving up above the compiler barrier. Here we will see the various ways our code may be subject +// to compiler optimizations and why compiler barriers are needed. Note as stated above, compiler barriers may not +// prevent all compiler optimizations or transformations. Compiler barriers are usually implemented by reloading all +// variables that are currently cached in registers and flushing all stores in registers back to memory. +// This list isn't exhaustive but will hopefully try to outline what compiler barriers protect against and what they don't. +// +// Compiler may reorder loads. +// LOAD A; LOAD B; -> LOAD B; LOAD A; +// LOAD A; operation on A; LOAD B; operation on B; -> LOAD A; LOAD B; operation on A; operation on B +// +// Insert a compiler barrier in between the two loads to guarantee that they are kept in order. +// LOAD A; COMPILER_BARRIER; LOAD B; +// LOAD A; operation on A; COMPILER_BARRIER; LOAD B; operation on B; +// +// The same with stores. +// STORE(A, 1); STORE(B, 1); -> STORE(B, 1); STORE(A, 1); +// operations and STORE result into A; operations and STORE result int B; -> all operations; STORE result into B; STORE result into A; +// +// Insert a compiler barrier in between the two stores to guarantee that they are kept in order. +// It is not required that the multiple stores to A before the barrier are not merged into one final store. +// It is not required that the store to B after the barrier be written to memory, it may be cached in a register for some indeterminate +// amount of time as an example. +// STORE(A, 1); COMPILER_BARRIER; STORE(B, 1); +// +// The compiler is allowed to merge overlapping loads and stores. +// Inserting a compiler barrier here will not prevent the compiler from doing this optimization as doing one wider load/store is +// technically still abiding by the guarantee that the loads/stores are not reordered with each other. +// LOAD A[0]; LOAD A[1]; -> A single wider LOAD instruction +// STORE(A[0], 1); STORE(A[1], 2); -> A single wider STORE instruction +// +// Compilers do not have to reload the values pointers point to. This is especially common with RISC architectures with lots +// of general purpose registers or even compiler optimizations such as inlining or Link-Time Optimization. +// int i = *ptr; Do bunch of operations; if (*ptr) { do more; } +// It is entirely possible the compiler may remove the last if statement because it can keep the *ptr in a register +// and it may infer from the operations done on i that i is never 0. +// +// int i = *ptr; Do bunch of operations; COMPILER_BARRIER; if (*ptr) { do more; } +// Inserting a compiler barrier at that location will cause the compiler to have reload *ptr thus keeping the if statement assuming +// no other optimizations take place, such as the compiler knowing that *ptr is always greater than 0. +// +// The compiler is within its rights to also merge and reload loads as much as it pleases. +// +// while (int tmp = LOAD(A)) +// process_tmp(tmp) +// +// Will be merged and transformed to +// +// if (int tmp = LOAD(A)) +// for (;;) process_tmp(tmp) +// +// Inserting a compiler barrier will ensure that LOAD(A) is always reloaded and thus the unwanted transformation is avoided. +// +// while (int tmp = LOAD(A)) +// { +// process_tmp(tmp) +// COMPILER_BARRIER +// } +// +// Under heavy register pressure scenarios, say the loop body was larger, the compiler may reload A as follows. +// Compiler barriers cannot prevent this from happening, even if we put it after process_tmp as above; +// the compiler still kept those loads above the barrier so it satisfied its contract even though it reloaded +// from A more than once. +// +// while (int tmp = LOAD(A)) +// process_tmp(LOAD(A)) +// +// In the above transformation it is possible that another cpu stores 0 into A. When we reload A for process_tmp, we pass 0 +// to process_tmp() which it would actually never expect to observe. Because if we observed 0, the while loop condition +// would never be satisfied. If the compiler under register pressure instead stored and loaded tmp from its stack slot, that is fine +// because we are just storing and loading the original observed value from A. Obviously that is slower than just reloading from +// A again so an optimizing compiler may not do the stack slot store. This is an unwanted transformation which eastl::atomic prevents +// even on relaxed loads. +// +// The compiler is allowed to do dead-store elimination if it knows that value has already been stored, or that only the last store +// needs to be stored. The compiler does not assume or know that these variables are shared variables. +// +// STORE(A, 1); STORE(A, 1); +// OPERATIONS; -> OPERATIONS; +// STORE(A, 1); +// +// The compiler is well within its rights to omit the second store to A. Assuming we are doing some fancy lockfree communication +// with another cpu and the last store is meant to ensure the ending value is 1 even if another cpu changed A in between; that +// assumption will not be satisfied. A compiler barrier will not prevent the last store from being dead-store removed. +// +// STORE(A, 1); +// OPERATIONS; +// STORE(A, 2); +// +// Assuming these stores are meant to denote some state changes to communicate with a remote cpu. The compiler is allowed to +// transform this as follows without a compiler barrier. Insert a compiler barrier between the two stores to prevent the transformation. +// Something like this will also require memory barriers, but that is not the point of this section. +// +// STORE(A, 2); +// OPERATIONS; +// +// The compiler is also allowed to invent stores as it may please. +// First on many RISC architectures storing an immediate value either involves loading the immediate from the .data section +// or combing a variety of load upper immediate and add or or immediate instructions to get our constant in a register and then +// doing a single 32-bit store instruction from said register. Some ISAs have 16-bit stores with immediate value so that a store +// may be broken into 2 16-bit store immediate values causing shearing. To reduce instruction dependencies it may also decide +// to do two add immediates and then two 16-bit stores again causing shearing. +// +// lui $t0, 1 # t0 == 0x00010000 +// ori $a0, $t0, 8 # t0 == 0x00010008 +// strw $t0, 0($a1) # store t0 into address at a1 +// -> +// ori $a0, $t0, 1 # t0 == 0x00000001 +// ori $a0, $t1, 8 # t0 == 0x00000008 +// strhw $t0, 0($a1) # store t0 lower half at a1 +// strhw $t1, 2($a1) # store t1 upper half at a1 +// +// The above shows a potential transformation that a compiler barrier cannot solve for us. +// +// A compiler may also introduce stores to save on branching. Let's see. +// +// if (a) +// STORE(X, 10); +// else +// STORE(X, 20); +// +// STORE(X, 20); +// if (a) +// STORE(X, 10); +// +// This is a very common optimization as it saves a potentially more expensive branch instruction but breaks multi-threaded code. +// This is also another case where a compiler barrier doesn't give us the granularity we need. +// The branches may even be completely removed with the compiler instead choosing to use conditional move operations which would +// actually be compliant since there would be one store only done, an extra store wouldn't have been added. +// +// You are now probably thinking that compiler barriers are useful and are definitely needed to tell the compiler to calm down +// and guarantee our hardware guarantees are valid because the code we wrote is the instructions that were emitted. +// But there are definitely lots of caveats where compiler barriers do not at all provide the guarantees we still need. +// This where eastl::atomic comes into play, and under the relaxed memory ordering section it will be explained +// what the standard guarantees and how we achieve those guarantees, like ensuring the compiler never does dead-store elimination or reloads. +// +// ******** Control Dependencies ******** +// +// Control dependencies are implicit local cpu ordering of memory instructions due to branching instructions, specifically +// only conditional branches. The problem is compilers do not understand control dependencies, and control dependencies +// are incredibly hard to understand. This is meant to make the reader aware they exist and to never use them +// because they shouldn't be needed at all with eastl::atomic. Also control dependencies are categorized as LDLD or LDST, +// store control dependencies inherently do not make sense since the conditional branch loads and compares two values. +// +// A LDLD control dependency is an anti-pattern since it is not guaranteed that any architecture will detect the memory-order violation. +// r0 = LOAD(A); +// if (r0) +// r1 = LOAD(B) +// +// Given those sequence of instructions, it is entirely possible that a cpu attempts to speculatively predict and load the value of B +// before the branch instruction has finished executing. It is entirely allowed that the cpu loads from B, assume B is in cache and A +// is not in cache, before A. It is allowed, that even if the cpu was correct in it's prediction that it doesn't reload B and change the +// fact that it speculatively got lucky. +// +// This is also what the x86 pause instruction inserted into spin wait loops is meant to solve. +// LOOP: +// r0 = LOAD(A); +// if (!r0) pause; goto LOOP; +// +// In the above spin loop, after a couple of iterations the processor will fill the pipeline with speculated cmp and load instructions. +// x86 will catch a memory order violation if it sees that an external store was done to A and thus must flush the entire +// pipeline of all the speculated load A. Pause instruction tells the cpu to not do speculative loads so that the pipeline is not +// filled with all said speculative load instructions. This ensures we do not incur the costly pipeline flushes from memory order +// violations which are likely to occur in tight spin wait loops. This also allows other threads on the same physical core to use the +// core's resources better since our speculative nature won't be hogging it all. +// +// A LDST control dependency is a true dependency in which the cpu cannot make a store visible to the system and other cpus until it +// knows its prediction is correct. Thus a LDST ordering is guaranteed and can be always relied upon as in the following example. +// +// r0 = LOAD(A); +// if (r0) +// STORE(B, 1); +// +// The fun part comes in with how does the compiler actually break all of this. +// First is that if the compiler can ensure that the value of A in the LDST example is always not zero, then it is always within its +// rights to completely remove the if statement which would lend us with no control dependency. +// +// Things get more fun when we deal with conditionals with else and else if statements where the compiler might be able to employ +// invariant code motion optimizations. Take this example. +// +// r0 = LOAD(A); +// r1 = LOAD(B); +// if (r0) +// STORE(B, 1); +// /* MORE CODE */ +// else if (r1) +// STORE(B, 1); +// /* MORE CODE */ +// else +// STORE(B, 1); +// /* MORE CODE */ +// +// If we were trying to be smart and entirely rely on the control dependency to ensure order, ya well just don't the compiler +// is always smarter. The compiler is well within its rights to move all the STORE(B, 1) up and above all the conditionals breaking +// our reliance on the LDST control dependency. +// +// Things can get even more complicated especially in C++ when values may come from constexpr, inline, inline constexpr, static const, etc, +// variables and thus the compiler will do all sorts of transformations to reduce, remove, augment and change all your conditional code since +// it knows the values of the expressions or even parts of it at compile time. Even more aggressive optimizations like LTO might break code that was being cautious. +// Even adding simple short circuiting logic or your classic likely/unlikely macros can alter conditionals in ways you didn't expect. +// In short know enough about control dependencies to know not to ever use them. +// +// ******** Multi-Copy Store Atomicity && Barrier Cumulativity ******** +// +// Single-Copy Store Atomicity: All stores must become visible to all cores in the system at the same time. +// +// Multi-Copy Store Atomicity : This means a cpu sees its own stores before they become visible to other cpus, by forwarding them from the store buffer, +// but a store becomes visible to all other cpus at the same time when flushed from the store buffer. +// +// Non-Atomic Store Atomicity : A store becomes visible to different cpus at different times. +// +// Those are the above variations of Store Atomicity. Most processors have Non-Atomic Store Atomicity and thus you must program to that lowest common denominator. +// We can use barriers, with some caveats, to restore Multi-Copy Store Atomicity to a Non-Atomic system though we need to define a new granular definition for +// memory barriers to define this behaviour. Simple LDLD/LDST/STST/STLD definition is not enough to categorize memory barriers at this level. Let's start off +// with a simple example that breaks under a Non-Atomic Store Atomicity system and what potential hardware features allow this behaviour to be observed. +// +// NOTE: For all the below examples we assume no compile reordering and that the processor also executes the instructions with no local reorderings to make the examples simpler, +// to only show off the effects of Multi-Copy Store Atomicity. This is why we don't add any address dependencies, or mark explicit LDLD/LDST memory barriers. +// Thus you may assume all LDLD and LDST pairs have an address dependency between them, so that they are not reordered by the compiler or the local cpu. +// +// --------------------------------------------------------------------------------------------------------- +// Write-To-Read Causality, WRC, Litmus Test +// --------------------------------------------------------------------------------------------------------- +// Initial State: +// X = 0; Y = 0; +// --------------------------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 | Thread 2 +// --------------------------------------------------------------------------------------------------------- +// STORE(X, 1) | r0 = LOAD(X) | r1 = LOAD(Y) +// | STORE(Y, r0) | r2 = LOAD(X) +// --------------------------------------------------------------------------------------------------------- +// Observed: r0 = 1 && r1 = 1 && r2 = 0 +// --------------------------------------------------------------------------------------------------------- +// +// Let's go over this example in detail and whether the outcome shown above can be observed. In this example Thread 0 stores 1 into X. If Thread 1 observes the write to X, +// it stores the observed value into Y. Thread 2 loads from Y then X. This means if the load from Y returns 1, then we intuitively know the global store order +// was 1 to X and then 1 to Y. So is it possible then that the load from X in Thread 2 can return 0 in that case? Under a Multi-Copy Store Atomicity system, that would be +// impossible because once 1 was stored to X all cpus see that store so if Thread 2 saw the store to Y which can only happen after the store to X was observed, then +// Thread 2 must also have observed the store to X and return 1. As you may well have figured out, it is possible under a Non-Atomic Store Atomicity system to still +// observe the load from X returning 0 even if the above load from Y returned 1 in Thread 2. This completely breaks our intuition of causality. Let's now understand what hardware may cause this. +// +// This is possible on cpus that have Simultaneous Multi-Threading, SMT or HyperThreading in Intel parlance, which share resources such as store buffers or L1 cache. +// We are accustomed to the x86 way of SMT where each logical core shares Execution Units on the physical core but each logical core has their own statically partitioned +// cache and store buffer that is not visible to the other cpus. It is possible on cpus like ARMv7 or POWER, POWER9 supports 4 and even 8 threads per physical core, so +// to save on die space though yet enable this large number of threads per physical core it is common for these logical cores to all use the same store buffer or L1 cache +// per physical core on these processors. Let's take the above example and rerun it with this knowledge to get the observed behaviour outlined above. +// +// Assume Thread 0, Thread 1, and Thread 2 run on cpu 0, cpu 1, and cpu 2 respectively. Assume that cpu 0 and cpu 1 are two logical cores on the same physical core so this processor +// has an SMT value of 2. Thread 0 will store 1 into X. This store may be in the store buffer or in the L1 cache that cpu 1 also shares with cpu 0, thus cpu 1 has early access to cpu 0's stores. +// Thread 1 loads X which it observed as 1 early and then stores 1 into Y. Thread 2 may see the load from Y returning 1 but now the load from X returning 0 all because cpu 1 got early +// access to cpu 0 store due to sharing a L1 cache or store buffer. +// We will come back on how to fix this example with the proper memory barriers for the Non-Atomic Store Atomicity systems, but we need to detour first. +// +// We need to take a deeper dive into memory barriers to understand how to restore Multi-Copy Store Atomicity from a Non-Atomic Store Atomicity system. +// Let's start with a motivating example and we will be using the POWER architecture throughout this example because it encompasses all the possible observable behaviour. +// ARMv7 technically allows Non-Atomic Store Atomicity behaviour but no consumer ARMv7 chip actually observes this behaviour. +// ARMv8 reworked its model to specifically say it is a Multi-Copy Store Atomicity system. +// POWER is one of the last few popular consumer architectures that are guaranteed to have Non-Atomic Store Atomicity observable behaviour, thus we will be using it for the following examples. +// +// To preface, POWER has two types of memory barriers called lwsync and sync. The following table lists the guarantees provided by TSO, x86, and the lwsync instruction. +// The table gives a hint as to why using our previous definition of LDLD/LDST/STST/STLD isn't granular enough to categorize memory barrier instructions. +// +// TSO: | POWER lwsync memory barrier: +// LDLD : YES | LDLD : YES +// LDST : YES | LDST : YES +// STST : YES | STST : YES +// STLD : NO | STLD : NO +// A cumulative : YES | A cumulative : YES +// B cumulative : YES | B cumulative : YES +// IRIW : YES | IRIW : NO +// +// The TSO memory model provided by x86 seems to be exactly the same as POWER if we add lwsync memory barrier instructions in between each of the memory instructions. +// This provides us the exact same ordering guarantees as the TSO memory model. If we just looked at the 4 permutations of reorderings we would be inclined to assume that +// TSO has the exact same ordering as sprinkling lwsync in our code in between every pair of memory instructions. That is not the case because memory barrier causality and cumulativity differ in subtle ways. +// In this case they differ by the implicit guarantees from the TSO memory model versus those provided by the POWER lwsync memory barrier. +// So the lwsync memory barrier prevents reordering with instructions that have causality but does not prevent reordering with instructions that are completely independent. +// Let's dive into these concepts a bit more. +// +// Non-Atomic Store Atomicity architectures are prone to behaviours such as the non-causal outcome of the WRC test above. Architectures such as POWER defines memory barriers to enforce +// ordering with respect to memory accesses in remote cpus other than the cpu actually issuing the memory barrier. This is known as memory barrier cumulativity. +// How does the memory barrier issued on my cpu affect the view of memory accesses done by remote cpuss. +// +// Cumulative memory barriers are defined as follows - Take your time this part is very non-trivial: +// A-Cumulative: We denote group A as the set of memory instructions in this cpu or other cpus that are ordered before the memory barrier in this cpu. +// A-Cumulativity requires that memory instructions from any cpu that have performed prior to a memory load before the memory barrier on this cpu are also members of group A. +// B-Cumulative: We denote group B as the set of memory instructions in this cpu or other cpus that are ordered after the memory barrier in this cpu. +// B-Cumulativity requires that memory instructions from any cpu that perform after a load and including the load in that cpu that returns the value of a store in group B are +// also members of group B. +// IRIW : enforces a global ordering even for memory instructions that have no causality. The memory instructions are completely independent. +// +// --------------------------------------------------------------------------------------------------------- +// WRC Litmus Test +// --------------------------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 | Thread 2 +// --------------------------------------------------------------------------------------------------------- +// {i} : STORE(X, 1) | {ii} : r0 = LOAD(X) | {v} : r1 = LOAD(Y) +// | {iii} : lwsync | +// | {iv} : STORE(Y, r0) | {vi} : r2 = LOAD(X) +// --------------------------------------------------------------------------------------------------------- +// Outcome: r0 = 1 && r1 = 1 && r2 = 1 +// +// Group A of {iii} : {i} && {ii} +// +// Group B of {iii} : {iv} && {v} && {vi} +// --------------------------------------------------------------------------------------------------------- +// +// Using the WRC test again and inserting a POWER lwsync, don't concern yourself with why the memory barrier was inserted at that spot right now, we now see the distinctions of group A and group B. +// It demonstrates the A and B Cumulative nature of the lwsync instruction, {iii}. First group A, initially consists of {ii} and group B initially consists of {iv} from the local cpu that issued the lwsync. +// Since {ii} reads from {i} and assume {i} happens before {ii}, by definition of A-Cumulativity {i} is included in group A. +// Similarly {v} reads from {iv} and assume {iv} happens before {v}, then {v} is included in group B by definition of B-Cumulativity. +// {vi} is also included in group B since it happens after {v} by definition of B-Cumulativity. +// +// WRC litmus test represents a scenario where only a A-Cumulative memory barrier is needed. The lwsync not only provides the needed local LDST memory barrier for the local thread but also ensures +// that any write Thread 1 has read from before the memory barrier is kept in order with any write Thread 1 does after the memory barrier as far as any other thread observes. +// In other words it ensures that any write that has propagated to Thread 1 before the memory barrier is propagated to any other thread before the second store after the memory barrier in Thread 1 +// can propagate to other threads in the system. This is exactly the definition of A-Cumulativity and what we need to ensure that causality is maintained in the WRC Litmus Test example. +// With that lwsync in place it is now impossible to observe r0 = 1 && r1 = 1 && r2 = 0. The lwsync has restored causal ordering. Let's look at an example that requires B-Cumulativity. +// +// --------------------------------------------------------------------------------------------------------- +// Example 2 from POWER manual +// --------------------------------------------------------------------------------------------------------- +// Initial State: +// X = 0; Y = 0; Z = 0 +// --------------------------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 | Thread 2 +// --------------------------------------------------------------------------------------------------------- +// STORE(X, 1) | r0 = LOAD(Y) | r1 = LOAD(Z) +// STORE(Y, 1) | STORE(Z, r0) | r2 = LOAD(X) +// --------------------------------------------------------------------------------------------------------- +// Observed: r0 = 1 && r1 = 1 && r2 = 0 +// --------------------------------------------------------------------------------------------------------- +// +// This example is very similar to WRC except that we kinda extended the Message Passing through an additional shared variable instead. +// Think of this as Thread 0 writing some data into X, setting flag Y, Thread 1 waiting for flag Y then writing flag Z, and finally Thread 2 waiting for flag Z before reading the data. +// Take a minute to digest the above example and think about where a memory barrier, lwsync, should be placed. Don't peek at the solution below. +// +// --------------------------------------------------------------------------------------------------------- +// Example 2 from POWER manual +// --------------------------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 | Thread 2 +// --------------------------------------------------------------------------------------------------------- +// STORE(X, 1) | r0 = LOAD(Y) | r1 = LOAD(Z) +// lwsync | | +// STORE(Y, 1) | STORE(Z, r0) | r2 = LOAD(X) +// --------------------------------------------------------------------------------------------------------- +// +// First the lwsync provides the needed local STST memory barrier for the local thread, thus the lwsync here ensures that the store to X propagates to Thread 1 before the store to Y. +// B-Cumulativity applied to all operations after the memory barrier ensure that the store to X is +// kept in order with respect to the store to Z as far as all other threads participating in the dependency chain are concerned. This is the exact definition of B-Cumulativity. +// With this one lwsync the outcome outlined above is impossible to observe. If r0 = 1 && r1 = 1 then r2 must be properly observed to be 1. +// +// We know that lwsync only provides A-Cumulativity and B-Cumulativity. Now we will look at examples that have no causality constraints thus we need to grab heavier memory barriers +// that ensures in short we will say makes a store become visible to all processors, even those not on the dependency chains. Let's get to the first example. +// +// --------------------------------------------------------------------------------------------------------- +// Independent Reads of Independent Writes, IRIW, coined by Doug Lea +// --------------------------------------------------------------------------------------------------------- +// Initial State: +// X = 0; Y = 0; +// --------------------------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 | Thread 2 | Thread 3 +// --------------------------------------------------------------------------------------------------------- +// STORE(X, 1) | r0 = LOAD(X) | STORE(Y, 1) | r2 = LOAD(Y) +// | r1 = LOAD(Y) | | r3 = LOAD(X) +// --------------------------------------------------------------------------------------------------------- +// Observed: r0 = 1 && r1 = 0 && r2 = 1 && r3 = 0 +// --------------------------------------------------------------------------------------------------------- +// +// The IRIW example above clearly shows that writes can be propagated to different cpus in completely different orders. +// Thread 1 sees the store to X but not the store to Y while Thread 3 sees the store to Y but not the store to X, the complete opposite. +// Also to the keen eye you may have noticed this example is a slight modification of the Store Buffer example so try to guess where the memory barriers would go. +// +// --------------------------------------------------------------------------------------------------------- +// Independent Reads of Independent Writes, IRIW, coined by Doug Lea +// --------------------------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 | Thread 2 | Thread 3 +// --------------------------------------------------------------------------------------------------------- +// STORE(X, 1) | r0 = LOAD(X) | STORE(Y, 1) | r2 = LOAD(Y) +// | sync | | sync +// | r1 = LOAD(Y) | | r3 = LOAD(X) +// --------------------------------------------------------------------------------------------------------- +// +// To ensure that the above observation is forbidden we need to add a full sync memory barrier on both the reading threads. Think of sync as restoring sequential consistency. +// The sync memory barrier ensures that any writes that Thread 1 has read from before the memory barrier are fully propagated to all threads before the reads are satisfied after the memory barrier. +// The same can be said for Thread 3. This is why the sync memory barrier is needed because there is no partial causal ordering here or anything that can be considered for our A and B Cumulativity definitions. +// We must ensure that all writes have been propagated to all cpus before proceeding. This gives way to the difference between sync and lwsync with regards to visibility of writes and cumulativity. +// sync guarantees that all program-order previous stores must have been propagated to all other cpus before the memory instructions after the memory barrier. +// lwsync does not ensure that stores before the memory barrier have actually propagated to any other cpu before memory instructions after the memory barrier, but it will keep stores before and after the +// lwsync in order as far as other cpus are concerned that are within the dependency chain. +// +// Fun fact while ARMv7 claims to be Non-Atomic Store Atomicity no mainstream ARM implementation that I have seen has shown cases of Non-Atomic Store Atomicity. +// It's allowed by the ARMv7 memory model and thus you have to program to that. ARMv8 changes this and states that it has Multi-Copy Store Atomicity. +// +// ******** Release-Acquire Semantics ******** +// +// The most useful and common cases where Release-Acquire Semantics are used in every day code is in message passing and mutexes. Let's get onto some examples and the C++ definition of Release-Acquire. +// +// ACQUIRE: +// An Acquire operation is a one-way memory barrier whereby all loads and stores after the acquire operation cannot move up and above the acquire operation. +// Loads and stores before the acquire operation can move down past the acquire operation. An acquire operation should always be paired with a Release operation on the SAME atomic object. +// +// RELEASE: +// A Release operation is a one-way memory barrier whereby all loads and stores before the release operation cannot move down and below the release operation. +// Loads and stores after the release operation can move up and above the release operation. A release operation should always be paired with an Acquire operation on the SAME atomic object. +// +// Release-Acquire pair does not create a full memory barrier but it guarantees that all memory instructions before a Release operation on an atomic object M are visible after an Acquire +// operation on that same atomic object M. Thus these semantics usually are enough to preclude the need for any other memory barriers. +// The synchronization is established only between the threads Releasing and Acquiring the same atomic object M. +// +// --------------------------------------------------- +// Critical Section +// --------------------------------------------------- +// Thread 0 | Thread 1 +// --------------------------------------------------- +// mtx.lock() - Acquire | mtx.lock() - Acquire +// STORE(X, 1) | r0 = LOAD(X) +// mtx.unlock() - Release | mtx.unlock() - Release +// --------------------------------------------------- +// +// A mutex only requires Release-Acquire semantics to protect the critical section. We do not care if operations above the lock leak into the critical section or that operations below the unlock leak into the +// critical section because they are outside the protected region of the lock()/unlock() pair. Release-Acquire semantics does guarantee that everything inside the critical section cannot leak out. +// Thus all accesses of all previous critical sections for the mutex are guaranteed to have completed and be visible when the mutex is handed off to the next party due to the Release-Acquire chaining. +// This also means that mutexes do not provide or restore Multi-Copy Store Atomicity to any memory instructions outside the mutex, like the IRIW example since it does not emit full memory barriers. +// +// ------------------------------------------------------ +// Message Passing +// ------------------------------------------------------ +// Thread 0 | Thread 1 +// ------------------------------------------------------ +// STORE(DATA, 1) | while (!LOAD_ACQUIRE(FLAG)) +// | +// STORE_RELEASE(FLAG, 1) | r0 = LOAD(DATA) +// ------------------------------------------------------ +// +// This is a common message passing idiom that also shows the use of Release-Acquire semantics. It should be obvious by the definitions outlined above why this works. +// An Acquire operation attached to a load needs to provide a LDLD and LDST memory barrier according to our definition of acquire. This is provided by default on x86 TSO thus no memory barrier is emitted. +// A Release operation attached to a store needs to provide a STST and LDST memory barrier according to our definition of release. This is provided by default on x86 TSO thus no memory barrier is emitted. +// +// A couple of things of note here. One is that by attaching the semantics of a memory model directly to the memory instruction/operation itself we can take advantage of the fact the some processors +// already provide guarantees between memory instructions and thus we do not have to emit memory barriers. Another thing of note is that the memory model is directly attached to the operation, +// so you must do the Release-Acquire pairing on the SAME object which in this case is the FLAG variable. Doing an Acquire or Release on a separate object has no guarantee to observe an Acquire or Release on a different object. +// This better encapsulates the meaning of the code and also allows the processor to potentially do more optimizations since a stand alone memory barrier will order all memory instructions of a given type before and after the barrier. +// Where as the memory ordering attached to the load or store tells the processor that it only has to order memory instructions in relation to that specific load or store with the given memory order. +// +// +// --------------------------------------------------------------------------------------------------------- +// Release Attached to a Store VS. Standalone Fence +// --------------------------------------------------------------------------------------------------------- +// STORE(DATA, 1) | STORE(DATA, 1) +// | ATOMIC_THREAD_FENCE_RELEASE() +// STORE_RELEASE(FLAG, 1) | STORE_RELAXED(FLAG, 1) +// STORE_RELAXED(VAR, 2) | STORE_RELAXED(VAR, 2) +// --------------------------------------------------------------------------------------------------------- +// ARMv8 Assembly +// --------------------------------------------------------------------------------------------------------- +// str 1, DATA | str 1, DATA +// | dmb ish +// stlr 1, FLAG | str 1, FLAG +// str 2, VAR | str 2, VAR +// --------------------------------------------------------------------------------------------------------- +// +// In the above example the release is attached to the FLAG variable, thus synchronization only needs to be guaranteed for that atomic variable. +// It is entirely possible for the VAR relaxed store to be reordered above the release store. +// In the fence version, since the fence is standalone, there is no notion where the release is meant to be attached to thus the fence must prevent all subsequent relaxed stores +// from being reordered above the fence. The fence provides a stronger guarantee whereby now the VAR relaxed store cannot be moved up and above the release operation. +// Also notice the ARMv8 assembly is different, the release fence must use the stronger dmb ish barrier instead of the dedicated release store instruction. +// We dive more into fences provided by eastl::atomic below. +// +// Release-Acquire semantics also have the property that it must chain through multiple dependencies which is where our knowledge from the previous section comes into play. +// Everything on the Release-Acquire dependency chain must be visible to the next hop in the chain. +// +// --------------------------------------------------------------------------------------------------------- +// Example 2 from POWER manual +// --------------------------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 | Thread 2 +// --------------------------------------------------------------------------------------------------------- +// STORE(X, 1) | r0 = LOAD_ACQUIRE(Y) | r1 = LOAD_ACQUIRE(Z) +// STORE_RELEASE(Y, 1) | STORE_RELEASE(Z, r0) | r2 = LOAD(X) +// --------------------------------------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------------------------------------- +// Write-To-Read Causality, WRC, Litmus Test +// --------------------------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 | Thread 2 +// --------------------------------------------------------------------------------------------------------- +// STORE(X, 1) | r0 = LOAD(X) | r1 = LOAD_ACQUIRE(Y) +// | STORE_RELEASE(Y, r0) | r2 = LOAD(X) +// --------------------------------------------------------------------------------------------------------- +// +// You may notice both of these examples from the previous section. We replaced the standalone POWER memory barrier instructions with Release-Acquire semantics attached directly to the operations where we want causality preserved. +// We have transformed those examples to use the eastl::atomic memory model. +// Take a moment to digest these examples in relation to the definition of Release-Acquire semantics. +// +// The Acquire chain can be satisfied by reading the value from the store release or any later stored headed by that release operation. The following examples will make this clearer. +// +// ------------------------------------------------------ +// Release Sequence Headed +// ------------------------------------------------------ +// Initial State: +// DATA = 0; FLAG = 0; +// ------------------------------------------------------ +// Thread 0 | Thread 1 +// ------------------------------------------------------ +// STORE(DATA, 1) | r0 = LOAD_ACQUIRE(FLAG) +// | +// STORE_RELEASE(FLAG, 1) | r1 = LOAD(DATA) +// STORE_RELAXED(FLAG, 3) | +// ------------------------------------------------------ +// Observed: r0 = 3 && r1 = 0 +// ------------------------------------------------------ +// +// In the above example we may read the value 3 from FLAG which was not the release store, but it was headed by that release store. Thus we observed a later store and therefore it is still valid to then observe r1 = 1. +// The stores to FLAG from the STORE_RELEASE up to but not including the next STORE_RELEASE operation make up the release sequence headed by the first release store operation. Any store on that sequence can be used to enforce +// causality on the load acquire. +// +// ******** Consume is currently not useful ******** +// +// Consume is a weaker form of an acquire barrier and creates the Release-Consume barrier pairing. +// Consume states that a load operation on an atomic object M cannot allow any loads or stores dependent on the value loaded by the operation to be reordered before the operation. +// To understand consume we must first understand dependent loads. +// You might encounter this being called a data dependency or an address dependency in some literature. +// +// -------------------------------------------------------------- +// Address Dependency +// -------------------------------------------------------------- +// Initial State: +// DATA = 0; PTR = nullptr; +// -------------------------------------------------------------- +// Thread 0 | Thread 1 +// -------------------------------------------------------------- +// STORE(DATA, 1) | r0 = LOAD(PTR) - typeof(r0) = int* +// | +// STORE(PTR, &DATA) | r1 = LOAD(r0) - typeof(r1) = int +// -------------------------------------------------------------- +// +// There is a clear dependency here where we cannot load from *int until we actually read the int* from memory. +// Now it is possible for Thread 1's load from *ptr to be observed before the store to DATA, therefore it can lead to r0 = &DATA && r1 = 0. +// While this is a failure of causality, it is allowed by some cpus such as the DEC Alpha and I believe Blackfin as well. +// Thus a data dependency memory barrier must be inserted between the data dependent loads in Thread 1. Note that this would equate to a nop on any processor other than the DEC Alpha. +// +// This can occur for a variety of hardware reasons. We learned about invalidation queues. It is possible that the invalidation for DATA gets buffered in Thread 1. DEC Alpha allows the Thread 1 +// load from PTR to continue without marking the entries in its invalidation queue. Thus the subsequent load is allowed to return the old cached value of DATA instead of waiting for the +// marked entries in the invalidation queue to be processed. It is a design decision of the processor not to do proper dependency tracking here and instead relying on the programmer to insert memory barriers. +// +// This data dependent ordering guarantee is useful because in places where we were using an Acquire memory barrier we can reduce it to this Consume memory barrier without any hardware barriers actually emitted on every modern processor. +// Let's take the above example, translate it to Acquire and Consume memory barriers and then translate it to the ARMv7 assembly and see the difference. +// +// --------------------------------------------------------------- --------------------------------------------------------------- +// Address Dependency - Release-Acquire Address Dependency - Release-Acquire - ARMv7 Assembly +// --------------------------------------------------------------- --------------------------------------------------------------- +// Thread 0 | Thread 1 Thread 0 | Thread 1 +// --------------------------------------------------------------- --------------------------------------------------------------- +// STORE(DATA, 1) | r0 = LOAD_ACQUIRE(PTR) STORE(DATA, 1) | r0 = LOAD(PTR) +// | dmb ish | dmb ish +// STORE_RELEASE(PTR, &DATA) | r1 = LOAD(r0) STORE(PTR, &DATA) | r1 = LOAD(r0) +// --------------------------------------------------------------- --------------------------------------------------------------- +// +// To get Release-Acquire semantics on ARMv7 we need to emit dmb ish; memory barriers. +// +// --------------------------------------------------------------- --------------------------------------------------------------- +// Address Dependency - Release-Consume Address Dependency - Release-Consume - ARMv7 Assembly +// --------------------------------------------------------------- --------------------------------------------------------------- +// Thread 0 | Thread 1 Thread 0 | Thread 1 +// --------------------------------------------------------------- --------------------------------------------------------------- +// STORE(DATA, 1) | r0 = LOAD_CONSUME(PTR) STORE(DATA, 1) | r0 = LOAD(PTR) +// | dmb ish | +// STORE_RELEASE(PTR, &DATA) | r1 = LOAD(r0) STORE(PTR, &DATA) | r1 = LOAD(r0) +// --------------------------------------------------------------- --------------------------------------------------------------- +// +// Data Dependencies can not only be created by read-after-write/RAW on registers, but also by RAW on memory locations too. Let's look at some more elaborate examples. +// +// --------------------------------------------------------------- --------------------------------------------------------------- +// Address Dependency on Registers - Release-Consume - ARMv7 Address Dependency on Memory - Release-Consume - ARMv7 +// --------------------------------------------------------------- --------------------------------------------------------------- +// Thread 0 | Thread 1 Thread 0 | Thread 1 +// --------------------------------------------------------------- --------------------------------------------------------------- +// STORE(DATA, 1) | r0 = LOAD(PTR) STORE(DATA, 1) | r0 = LOAD(PTR) +// | r1 = r0 + 0 | STORE(TEMP, r0) +// dmb ish | r2 = r1 - 0 dmb ish | r1 = LOAD(TEMP) +// STORE(PTR, &DATA) | r3 = LOAD(r2) STORE(PTR, &DATA) | r2 = LOAD(r1) +// --------------------------------------------------------------- --------------------------------------------------------------- +// +// The above shows a more elaborate example of how data dependent dependencies flow through RAW chains either through memory or through registers. +// +// Notice by identifying that this is a data dependent operation and asking for a consume ordering, we can completely eliminate the memory barrier on Thread 1 since we know ARMv7 does not reorder data dependent loads. Neat. +// Unfortunately every major compiler upgrades a consume to an acquire ordering, because the consume ordering in the standard has a stronger guarantee and requires the compiler to do complicated dependency tracking. +// Dependency chains in source code must be mapped to dependency chains at the machine instruction level until a std::kill_dependency in the source code. +// +// ---------------------------------------------------------------- +// Non-Address Dependency && Multiple Chains +// ---------------------------------------------------------------- +// Initial State: +// std::atomic FLAG; int DATA[1] = 0; +// ---------------------------------------------------------------- +// Thread 0 | Thread 1 +// ---------------------------------------------------------------- +// STORE(DATA[0], 1) | int f = LOAD_CONSUME(FLAG) +// | int x = f +// | if (x) return Func(x); +// | +// STORE_RELEASE(FLAG, 1) | Func(int y) return DATA[y - y] +// ---------------------------------------------------------------- +// +// This example is really concise but there is a lot going on. Let's digest it. +// First is that the standard allows consume ordering even on what we will call not true machine level dependencies like a ptr load and then a load from that ptr as shown in the previous examples. +// Here the dependency is between two ints, and the dependency chain on Thread 1 is as follows. f -> x -> y -> DATA[y - y]. The standard requires that source code dependencies on the loaded value +// from consume flow thru assignments and even thru function calls. Also notice we added a dependency on the dereference of DATA with the value loaded from consume which while it does nothing actually abides by the standard +// by enforcing a source code data dependent load on the consume operation. You may see this referred to as artificial data dependencies in other texts. +// If we assume the compiler is able to track all these dependencies, the question is how do we enforce these dependencies at the machine instruction level. Let's go back to our ptr dependent load example. +// +// ---------------------------------------------------------------- +// addi r0, pc, offset; +// ldr r1, 0(r0); +// ldr r2, 0(r1); +// ---------------------------------------------------------------- +// +// The above pseudo assembly does a pc relative calculation to find the address of ptr. We then load ptr and then continue the dependency chain by loading the int from the loaded ptr. +// Thus r0 has type of int**, which we use to load r1 an int* which we use to load our final value of r2 which is the int. +// The key observation here is that most instructions provided by most architectures only allow moving from a base register + offset into a destination register. +// This allows for trivial capturing of data dependent loads through pointers. But how do we capture the data dependency of DATA[y - y]. We would need something like this. +// +// ---------------------------------------------------------------- +// sub r1, r0, r0; // Assume r0 holds y from the Consume Operation +// add r3, r1, r2; // Assume r2 holds the address of DATA[0] +// ldr r4, 0(r3); +// ---------------------------------------------------------------- +// +// We cannot use two registers as both arguments to the load instruction. Thus to accomplish this you noticed we had to add indirect data dependencies through registers to compute the final address from the consume +// load of y and then load from the final computed address. The compiler would have to recognize all these dependencies and enforce that they be maintained in the generated assembly. +// The compiler must ensure the entire syntactic, source code, data-dependency chain is enforced in the generated assembly, no matter how long such chain may be. +// Because of this and other issues, every major compiler unilaterally promotes consume to an acquire operation across the board. Read reference [15] for more information. +// This completely removes the actual usefulness of consume for the pointer dependent case which is used quite heavily in concurrent read heavy data structures where updates are published via pointer swaps. +// +// ******** read_depends use case - Release-ReadDepends Semantics ******** +// +// eastl::atomic provides a weaker read_depends operation that only encapsulates the pointer dependency case above. Loading from a pointer and then loading the value from the loaded pointer. +// The read_depends operation can be used on loads from only an eastl::atomic type. The return pointer of the load must and can only be used to then further load values. And that is it. +// If you are unsure, upgrade this load to an acquire operation. +// +// MyStruct* ptr = gAtomicPtr.load(memory_order_read_depends); +// int a = ptr->a; +// int b = ptr->b; +// return a + b; +// +// The loads from ptr after the gAtomicPtr load ensure that the correct values of a and b are observed. This pairs with a Release operation on the writer side by releasing gAtomicPtr. +// +// +// As said above the returned pointer from a .load(memory_order_read_depends) can only be used to then further load values. +// Dereferencing(*) and Arrow Dereferencing(->) are valid operations on return values from .load(memory_order_read_depends). +// +// MyStruct* ptr = gAtomicPtr.load(memory_order_read_depends); +// int a = ptr->a; - VALID +// int a = *ptr; - VALID +// +// Since dereferencing is just indexing via some offset from some base address, this also means addition and subtraction of constants is ok. +// +// int* ptr = gAtomicPtr.load(memory_order_read_depends); +// int a = *(ptr + 1) - VALID +// int a = *(ptr - 1) - VALID +// +// Casts also work correctly since casting is just offsetting a pointer depending on the inheritance hierarchy or if using intrusive containers. +// +// ReadDependsIntrusive** intrusivePtr = gAtomicPtr.load(memory_order_read_depends); +// ReadDependsIntrusive* ptr = ((ReadDependsIntrusive*)(((char*)intrusivePtr) - offsetof(ReadDependsIntrusive, next))); +// +// Base* basePtr = gAtomicPtr.load(memory_order_read_depends); +// Dervied* derivedPtr = static_cast(basePtr); +// +// Both of the above castings from the result of the load are valid for this memory order. +// +// You can reinterpret_cast the returned pointer value to a uintptr_t to set bits, clear bits, or xor bits but the pointer must be casted back before doing anything else. +// +// int* ptr = gAtomicPtr.load(memory_order_read_depends); +// ptr = reinterpret_cast(reinterpret_cast(ptr) & ~3); +// +// Do not use any equality or relational operator (==, !=, >, <, >=, <=) results in the computation of offsets before dereferencing. +// As we learned above in the Control Dependencies section, CPUs will not order Load-Load Control Dependencies. Relational and equality operators are often compiled using branches. +// It doesn't have to be compiled to branched, condition instructions could be used. Or some architectures provide comparison instructions such as set less than which do not need +// branches when using the result of the relational operator in arithmetic statements. Then again short circuiting may need to introduct branches since C++ guarantees the +// rest of the expression must not be evaluated. +// The following odd code is forbidden. +// +// int* ptr = gAtomicPtr.load(memory_order_read_depends); +// int* ptr2 = ptr + (ptr >= 0); +// int a = *ptr2; +// +// Only equality comparisons against nullptr are allowed. This is becase the compiler cannot assume that the address of the loaded value is some known address and substitute our loaded value. +// int* ptr = gAtomicPtr.load(memory_order_read_depends); +// if (ptr == nullptr); - VALID +// if (ptr != nullptr); - VALID +// +// Thus the above sentence that states: +// The return pointer of the load must and can only be used to then further load values. And that is it. +// must be respected by the programmer. This memory order is an optimization added for efficient read heavy pointer swapping data structures. IF you are unsure, use memory_order_acquire. +// +// ******** Relaxed && eastl::atomic guarantees ******** +// +// We saw various ways that compiler barriers do not help us and that we need something more granular to make sure accesses are not mangled by the compiler to be considered atomic. +// Ensuring these guarantees like preventing dead-store elimination or the splitting of stores into smaller sub stores is where the C/C++11 +// standard comes into play to define what it means to operate on an atomic object. +// These basic guarantees are provided via new compiler intrinsics on gcc/clang that provide explicit indication to the compiler. +// Or on msvc by casting the underlying atomic T to a volatile T*, providing stronger compiler guarantees than the standard requires. +// Essentially volatile turns off all possible optimizations on that variable access and ensures all volatile variables cannot be +// reordered across sequence points. Again we are not using volatile here to guarantee atomicity, we are using it in its very intended purpose +// to tell the compiler it cannot assume anything about the contents of that variable. Now let's dive into the base guarantees of eastl::atomic. +// +// The standard defines the following for all operations on an atomic object M. +// +// Write-Write Coherence: +// If an operation A modifies an atomic object M(store), happens before an operation B that modifies M(store), then A shall be earlier than B in the modification order of M. +// +// Read-Read Coherence: +// If a value computation A on an atomic object M(load), happens before a value computation B on M(load), and A takes its value from a side effect X on M(from a previous store to M), then the value +// computed by B shall either be the value stored by X or some later side effect Y on M, where Y follows X in the modification order of M. +// +// Read-Write Coherence: +// If a value computation A on an atomic object M(load), happens before an operation B that modifies M(store), then A shall take its value from a side effect X on M, where X precedes B in the modification +// order of M. +// +// Write-Read Coherence: +// If a side effect X on an atomic object M(store), happens before a value computation B on M(load), then the evaluation of B must take its value from X or from some side effect Y that follows X in the +// modification order of M. +// +// What does all this mean. This is just a pedantic way of saying that the preceding coherence requirements disallow compiler reordering of atomic operations to a single atomic object. +// This means all operations must be emitted by the compiler. Stores cannot be dead-store eliminated even if they are the only stores. +// Loads cannot have common subexpression elimination performed on them even if they are the only loads. +// Loads and Stores to the same atomic object cannot be reordered by the compiler. +// Compiler cannot introduce extra loads or stores to the atomic object. +// Compiler also cannot reload from an atomic object, it must save and store to a stack slot. +// Essentially this provides all the necessary guarantees needed when treating an object as atomic from the compilers point of view. +// +// ******** Same Address LoadLoad Reordering ******** +// +// It is expected that same address operations cannot and are not reordered with each other. It is expected that operations to the same address have sequential consistency because +// they are to the same address. If you picture a cpu executing instructions, how is it possible to reorder instructions to the same address and yet keep program behaviour the same. +// Same Address LoadLoad Reordering is one weakening that is possible to do and keep observed program behaviour for a single-threaded program. +// More formally, A and B are two memory instructions onto the same address P, where A is program ordered before B. If A and B are both loads then their order need not be ordered. +// If B is a store then it cannot retire the store before A instruction completes. If A is a store and B is a load, then B must get its value forwarded from the store buffer or observe a later store +// from the cache. Thus Same Address LDST, STST, STLD cannot be reordered but Same Address LDLD can be reordered. +// Intel Itanium and SPARC RMO cpus allow and do Same Address LoadLoad Reordering. +// Let's look at an example. +// +// --------------------------- +// Same Address LoadLoad +// --------------------------- +// Initial State: +// x = 0; +// --------------------------- +// Thread 0 | Thread 1 +// --------------------------- +// STORE(x, 1) | r0 = LOAD(x) +// | r1 = LOAD(x) +// --------------------------- +// Observed: r0 = 1 && r0 = 0 +// --------------------------- +// +// Notice in the above example it has appeared as if the two loads from the same address have been reordered. If we first observed the new store of 1, then the next load should not observe a value in the past. +// Many programmers, expect same address sequential consistency, all accesses to a single address appear to execute in a sequential order. +// Notice this violates the Read-Read Coherence for all atomic objects defined by the std and thus provided by eastl::atomic. +// +// All operations on eastl::atomic irrelevant of the memory ordering of the operation provides Same Address Sequential Consistency since it must abide by the coherence rules above. +// +// ******** eastl::atomic_thread_fence ******** +// +// eastl::atomic_thread_fence(relaxed) : Provides no ordering guarantees +// eastl::atomic_thread_fence(acquire) : Prevents all prior loads from being reordered with all later loads and stores, LDLD && LDST memory barrier +// eastl::atomic_thread_fence(release) : Prevents all prior loads and stores from being reordered with all later stores, STST && LDST memory barrier +// eastl::atomic_thread_fence(acq_rel) : Union of acquire and release, LDLD && STST && LDST memory barrier +// eastl::atomic_thread_fence(seq_cst) : Full memory barrier that provides a single total order +// +// See Reference [9] and Fence-Fence, Atomic-Fence, Fence-Atomic Synchronization, Atomics Order and Consistency in the C++ std. +// +// ******** Atomic && Fence Synchronization ******** +// +// --------------------------- +// Fence-Fence Synchronization +// --------------------------- +// A release fence A synchronizes-with an acquire fence B if there exist operations X and Y on the same atomic object M, such that fence A is sequenced-before operation X and X modifies M, +// operation Y is sequenced-before B and Y reads the value written by X. +// In this case all non-atomic and relaxed atomic stores that are sequenced-before fence A will happen-before all non-atomic and relaxed atomic loads after fence B. +// +// ---------------------------- +// Atomic-Fence Synchronization +// ---------------------------- +// An atomic release operation A on atomic object M synchronizes-with an acquire fence B if there exists some atomic operation X on atomic object M, such that X is sequenced-before B and reads +// the value written by A. +// In this case all non-atomic and relaxed atomic stores that are sequenced-before atomic release operation A will happen-before all non-atomic and relaxed atomic loads after fence B. +// +// ---------------------------- +// Fence-Atomic Synchronization +// ---------------------------- +// A release fence A synchronizes-with an atomic acquire operation B on an atomic object M if there exists an atomic operation X such that A is sequenced-before X, X modifies M and B reads the +// value written by X. +// In this case all non-atomic and relaxed atomic stores that are sequenced-before fence A will happen-before all non-atomic and relaxed atomic loads after atomic acquire operation B. +// +// This can be used to add synchronization to a series of several relaxed atomic operations, as in the following trivial example. +// +// ---------------------------------------------------------------------------------------- +// Initial State: +// x = 0; +// eastl::atomic y = 0; +// z = 0; +// eastl::atomic w = 0; +// ---------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 +// ---------------------------------------------------------------------------------------- +// x = 2 | r0 = y.load(memory_order_relaxed); +// z = 2 | r1 = w.load(memory_order_relaxed); +// atomic_thread_fence(memory_order_release); | atomic_thread_fence(memory_order_acquire); +// y.store(1, memory_order_relaxed); | r2 = x +// w.store(1, memory_order_relaxed); | r3 = z +// ---------------------------------------------------------------------------------------- +// Observed: r0 = 1 && r1 = 1 && r2 = 0 && r3 = 0 +// ---------------------------------------------------------------------------------------- +// +// ******** Atomic vs Standalone Fence ******** +// +// A sequentially consistent fence is stronger than a sequentially consistent operation because it is not tied to a specific atomic object. +// An atomic fence must provide synchronization with ANY atomic object whereas the ordering on the atomic object itself must only provide +// that ordering on that SAME atomic object. Thus this can provide cheaper guarantees on architectures with dependency tracking hardware. +// Let's look at a concrete example that will make this all clear. +// +// ---------------------------------------------------------------------------------------- +// Initial State: +// eastl::atomic y = 0; +// eastl::atomic z = 0; +// ---------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 +// ---------------------------------------------------------------------------------------- +// z.store(2, memory_order_relaxed); | r0 = y.load(memory_order_relaxed); +// atomic_thread_fence(memory_order_seq_cst); | atomic_thread_fence(memory_order_seq_cst); +// y.store(1, memory_order_relaxed); | r1 = z.load(memory_order_relaxed); +// ---------------------------------------------------------------------------------------- +// Observed: r0 = 1 && r1 = 0 +// ---------------------------------------------------------------------------------------- +// +// Here the two sequentially consistent fences synchronize-with each other thus ensuring that if we observe r0 = 1 then we also observe that r1 = 2. +// In the above example if we observe r0 = 1 it is impossible to observe r1 = 0. +// +// ---------------------------------------------------------------------------------------- +// Initial State: +// eastl::atomic x = 0; +// eastl::atomic y = 0; +// eastl::atomic z = 0; +// ---------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 +// ---------------------------------------------------------------------------------------- +// z.store(2, memory_order_relaxed); | r0 = y.load(memory_order_relaxed); +// x.fetch_add(1, memory_order_seq_cst); | x.fetch_add(1, memory_order_seq_cst); +// y.store(1, memory_order_relaxed); | r1 = z.load(memory_order_relaxed); +// ---------------------------------------------------------------------------------------- +// Observed: r0 = 1 && r1 = 0 +// ---------------------------------------------------------------------------------------- +// +// Here the two fetch_add sequentially consistent operations on x synchronize-with each other ensuring that if we observe r0 = 1 then we cannot observer r1 = 0; +// The thing to take note here is that we synchronized on the SAME atomic object, that being the atomic object x. +// Note that replacing the x.fetch_add() in Thread 1 with a sequentially consistent operation on another atomic object or a sequentially consistent fence can lead to +// observing r1 = 0 even if we observe r0 = 1. For example the following code may fail. +// +// ---------------------------------------------------------------------------------------- +// Initial State: +// eastl::atomic x = 0; +// eastl::atomic y = 0; +// eastl::atomic z = 0; +// ---------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 +// ---------------------------------------------------------------------------------------- +// z.store(2, memory_order_relaxed); | r0 = y.load(memory_order_relaxed); +// | x.fetch_add(1, memory_order_seq_cst); +// y.fetch_add(1, memory_order_seq_cst); | r1 = z.load(memory_order_relaxed); +// ---------------------------------------------------------------------------------------- +// Observed: r0 = 1 && r1 = 0 +// ---------------------------------------------------------------------------------------- +// +// ---------------------------------------------------------------------------------------- +// Initial State: +// eastl::atomic x = 0; +// eastl::atomic y = 0; +// eastl::atomic z = 0; +// ---------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 +// ---------------------------------------------------------------------------------------- +// z.store(2, memory_order_relaxed); | r0 = y.load(memory_order_relaxed); +// x.fetch_add(1, memory_order_seq_cst); | atomic_thread_fence(memory_order_seq_cst); +// y.store(1, memory_order_relaxed); | r1 = z.load(memory_order_relaxed); +// ---------------------------------------------------------------------------------------- +// Observed: r0 = 1 && r1 = 0 +// ---------------------------------------------------------------------------------------- +// +// In this example it is entirely possible that we observe r0 = 1 && r1 = 0 even though we have source code causality and sequentially consistent operations. +// Observability is tied to the atomic object on which the operation was performed and the thread fence doesn't synchronize-with the fetch_add because +// there is no load above the fence that reads the value from the fetch_add. +// +// ******** Sequential Consistency Semantics ******** +// +// See section, Order and consistency, in the C++ std and Reference [9]. +// +// A load with memory_order_seq_cst performs an acquire operation +// A store with memory_order_seq_cst performs a release operation +// A RMW with memory_order_seq_cst performs both an acquire and a release operation +// +// All memory_order_seq_cst operations exhibit the below single total order in which all threads observe all modifications in the same order +// +// Paraphrasing, there is a single total order on all memory_order_seq_cst operations, S, such that each sequentially consistent operation B that loads a value from +// atomic object M observes either the result of the last sequentially consistent modification A on M, or some modification on M that isn't memory_order_seq_cst. +// For atomic modifications A and B on an atomic object M, B occurs after A in the total order of M if: +// there is a memory_order_seq_cst fence X whereby A is sequenced before X, and X precedes B, +// there is a memory_order_seq_cst fence Y whereby Y is sequenced before B, and A precedes Y, +// there are memory_order_seq_cst fences X and Y such that A is sequenced before X, Y is sequenced before B, and X precedes Y. +// +// Let's look at some examples using memory_order_seq_cst. +// +// ------------------------------------------------------------ +// Store-Buffer +// ------------------------------------------------------------ +// Initial State: +// x = 0; y = 0; +// ------------------------------------------------------------ +// Thread 0 | Thread 1 +// ------------------------------------------------------------ +// STORE_RELAXED(x, 1) | STORE_RELAXED(y, 1) +// ATOMIC_THREAD_FENCE(SEQ_CST) | ATOMIC_THREAD_FENCE(SEQ_CST) +// r0 = LOAD_RELAXED(y) | r1 = LOAD_RELAXED(x) +// ------------------------------------------------------------ +// Observed: r0 = 0 && r1 = 0 +// ------------------------------------------------------------ +// +// ------------------------------------------------------------ +// Store-Buffer +// ------------------------------------------------------------ +// Initial State: +// x = 0; y = 0; +// ------------------------------------------------------------ +// Thread 0 | Thread 1 +// ------------------------------------------------------------ +// STORE_SEQ_CST(x, 1) | STORE_SEQ_CST(y, 1) +// r0 = LOAD_SEQ_CST(y) | r1 = LOAD_SEQ_CST(x) +// ------------------------------------------------------------ +// Observed: r0 = 0 && r1 = 0 +// ------------------------------------------------------------ +// +// Both solutions above are correct to ensure that the end results cannot lead to both r0 and r1 returning 0. Notice that the second one requires memory_order_seq_cst on both +// operations to ensure they are in the total order, S, for all memory_order_seq_cst operations. The other example uses the stronger guarantee provided by a sequentially consistent fence. +// +// ------------------------------------------------------------------------------------------------ +// Read-To-Write Causality +// ------------------------------------------------------------------------------------------------ +// Initial State: +// x = 0; y = 0; +// ------------------------------------------------------------------------------------------------ +// Thread 0 | Thread 1 | Thread 2 +// ------------------------------------------------------------------------------------------------ +// STORE_SEQ_CST(x, 1) | r0 = LOAD_RELAXED(x) | STORE_RELAXED(y, 1) +// | ATOMIC_THREAD_FENCE(SEQ_CST) | ATOMIC_THREAD_FENCE(SEQ_CST) +// | r1 = LOAD_RELAXED(y) | r2 = LOAD_RELAXED(x) +// ------------------------------------------------------------------------------------------------ +// Observed: r0 = 1 && r1 = 0 && r2 = 0 +// ------------------------------------------------------------------------------------------------ +// +// You'll notice this example is an in between example of the Store-Buffer and IRIW examples we have seen earlier. The store in Thread 0 needs to be sequentially consistent so it synchronizes with the +// thread fence in Thread 1. C++20 due to Reference [9], increased the strength of sequentially consistent fences has been increased to allow for the following. +// +// ------------------------------------------------------------------------------------------------ +// Read-To-Write Causality - C++20 +// ------------------------------------------------------------------------------------------------ +// Initial State: +// x = 0; y = 0; +// ------------------------------------------------------------------------------------------------ +// Thread 0 | Thread 1 | Thread 2 +// ------------------------------------------------------------------------------------------------ +// STORE_RELAXED(x, 1) | r0 = LOAD_RELAXED(x) | STORE_RELAXED(y, 1) +// | ATOMIC_THREAD_FENCE(SEQ_CST) | ATOMIC_THREAD_FENCE(SEQ_CST) +// | r1 = LOAD_RELAXED(y) | r2 = LOAD_RELAXED(x) +// ------------------------------------------------------------------------------------------------ +// Observed: r0 = 1 && r1 = 0 && r2 = 0 +// ------------------------------------------------------------------------------------------------ +// +// Notice we were able to turn the store in Thread 0 into a relaxed store and still properly observe either r1 or r2 returning 1. +// Note that all implementations of the C++11 standard for every architecture even now allows the C++20 behaviour. +// The C++20 standard memory model was brought up to recognize that all current implementations are able to implement them stronger. +// +// ******** False Sharing ******** +// +// As we know operations work on the granularity of a cacheline. A RMW operation obviously must have some help from the cache to ensure the entire operation +// is seen as one whole unit. Conceptually we can think of this as the cpu's cache taking a lock on the cacheline, the cpu doing the read-modify-write operation on the +// locked cacheline, and then releasing the lock on the cacheline. This means during that time any other cpu needing that cacheline must wait for the lock to be released. +// +// If we have two atomic objects doing RMW operations and they are within the same cacheline, they are unintentionally contending and serializing with each other even +// though they are two completely separate objects. This gives us the common name to this phenomona called false sharing. +// You can cacheline align your structure or the eastl::atomic object to prevent false sharing. +// +// ******** union of eastl::atomic ******** +// +// union { eastl::atomic atomic8; eastl::atomic atomic32; }; +// +// While we know that operations operate at the granularity of a processor's cacheline size and so we may expect that storing and loading +// from different width atomic variables at the same address to not cause weird observable behaviour but it may. +// Store Buffers allow smaller stores to replace parts of larger loads that are forwarded from a store buffer. +// This means if there is 2 bytes of modified data in the store buffer that overlaps with a 4 byte load, the 2 bytes will be forwarded +// from the store buffer. This is even documented behaviour of the x86 store buffer in the x86 architecture manual. +// This behaviour can cause processors to observe values that have never and will never be visible on the bus to other processors. +// The use of a union with eastl::atomic is not wrong but your code must be able to withstand these effects. +// +// Assume everything starts out initially as zero. +// +// ------------------------------------------------------------------------------------------------------- +// Thread 0 | Thread 1 | Thread 2 +// -------------------------------------------------------------------------------------------------------- +// cmpxchg 0 -> 0x11111111 | cmpxchg 0x11111111 -> 0x22222222 | mov byte 0x33; mov 4 bytes into register; +// --------------------------------------------------------------------------------------------------------- +// +// After all operations complete, the value in memory at that location is, 0x22222233. +// It is possible that the 4 byte load in thread 2 actually returns 0x11111133. +// Now 0x11111133 is an observed value that no other cpu could observe because it was never globally visible on the data bus. +// +// If the value in memory is 0x22222233 then the first cmpxchg succeeded, then the second cmpxchg succeeded and finally our +// byte to memory was stored, yet our load returned 0x11111133. This is because store buffer contents can be forwarded to overlapping loads. +// It is possible that the byte store got put in the store buffer. Our load happened after the first cmpxchg with the byte forwarded. +// This behaviour is fine as long as your algorithm is able to cope with this kind of store buffer forwarding effects. +// +// Reference [13] is a great read on more about this topic of mixed-size concurrency. +// + + +///////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include + + +#endif /* EASTL_ATOMIC_H */ diff --git a/lib/EASTL/include/EASTL/bit.h b/lib/EASTL/include/EASTL/bit.h new file mode 100644 index 000000000..64efe4879 --- /dev/null +++ b/lib/EASTL/include/EASTL/bit.h @@ -0,0 +1,65 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_BIT_H +#define EASTL_BIT_H + +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include +#include // 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::value + && eastl::is_trivially_copyable::value + > + > + EA_CONSTEXPR To bit_cast(const From& from) EA_NOEXCEPT + { + return __builtin_bit_cast(To, from); + } + + #else + + template::value + && eastl::is_trivially_copyable::value + > + > + inline To bit_cast(const From& from) EA_NOEXCEPT + { + typename eastl::aligned_storage::type to; + ::memcpy(eastl::addressof(to), eastl::addressof(from), sizeof(To)); + return reinterpret_cast(to); + } + + #endif // EASTL_CONSTEXPR_BIT_CAST_SUPPORTED + +} // namespace eastl + +#endif // EASTL_BIT_H diff --git a/lib/EASTL/include/EASTL/bitset.h b/lib/EASTL/include/EASTL/bitset.h new file mode 100644 index 000000000..8778372f6 --- /dev/null +++ b/lib/EASTL/include/EASTL/bitset.h @@ -0,0 +1,2232 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This file implements a bitset much like the C++ std::bitset class. +// The primary distinctions between this list and std::bitset are: +// - bitset is more efficient than some other std::bitset implementations, +// notably the bitset that comes with Microsoft and other 1st party platforms. +// - bitset is savvy to an environment that doesn't have exception handling, +// as is sometimes the case with console or embedded environments. +// - bitset is savvy to environments in which 'unsigned long' is not the +// most efficient integral data type. std::bitset implementations use +// unsigned long, even if it is an inefficient integer type. +// - bitset removes as much function calls as practical, in order to allow +// debug builds to run closer in speed and code footprint to release builds. +// - bitset doesn't support string functionality. We can add this if +// it is deemed useful. +// +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_BITSET_H +#define EASTL_BITSET_H + + +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS(); + +#include +#include + +EA_RESTORE_ALL_VC_WARNINGS(); + +#if EASTL_EXCEPTIONS_ENABLED + EA_DISABLE_ALL_VC_WARNINGS(); + + #include // std::out_of_range, std::length_error. + + EA_RESTORE_ALL_VC_WARNINGS(); +#endif + +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 +{ + // To consider: Enable this for backwards compatibility with any user code that might be using BitsetWordType: + // #define BitsetWordType EASTL_BITSET_WORD_TYPE_DEFAULT + + + /// BITSET_WORD_COUNT + /// + /// Defines the number of words we use, based on the number of bits. + /// nBitCount refers to the number of bits in a bitset. + /// WordType refers to the type of integer word which stores bitet data. By default it is BitsetWordType. + /// + #if !defined(__GNUC__) || (__GNUC__ >= 3) // GCC 2.x can't handle the simpler declaration below. + #define BITSET_WORD_COUNT(nBitCount, WordType) (nBitCount == 0 ? 1 : ((nBitCount - 1) / (8 * sizeof(WordType)) + 1)) + #else + #define BITSET_WORD_COUNT(nBitCount, WordType) ((nBitCount - 1) / (8 * sizeof(WordType)) + 1) + #endif + + + /// EASTL_DISABLE_BITSET_ARRAYBOUNDS_WARNING + /// Before GCC 4.7 the '-Warray-bounds' buggy and was very likely to issue false positives for loops that are + /// difficult to evaluate. + /// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=45978 + /// + #if defined(__GNUC__) && (EA_COMPILER_VERSION > 4007) && defined(EA_PLATFORM_ANDROID) // Earlier than GCC 4.7 + #define EASTL_DISABLE_BITSET_ARRAYBOUNDS_WARNING 1 + #else + #define EASTL_DISABLE_BITSET_ARRAYBOUNDS_WARNING 0 + #endif + + + + /// BitsetBase + /// + /// This is a default implementation that works for any number of words. + /// + template // Templated on the number of words used to hold the bitset and the word type. + struct BitsetBase + { + typedef WordType word_type; + typedef BitsetBase this_type; + #if EASTL_BITSET_SIZE_T + typedef size_t size_type; + #else + typedef eastl_size_t size_type; + #endif + + enum { + kBitsPerWord = (8 * sizeof(word_type)), + kBitsPerWordMask = (kBitsPerWord - 1), + kBitsPerWordShift = ((kBitsPerWord == 8) ? 3 : ((kBitsPerWord == 16) ? 4 : ((kBitsPerWord == 32) ? 5 : (((kBitsPerWord == 64) ? 6 : 7))))) + }; + + public: + word_type mWord[NW]; + + public: + BitsetBase(); + BitsetBase(uint32_t value); // This exists only for compatibility with std::bitset, which has a 'long' constructor. + //BitsetBase(uint64_t value); // Disabled because it causes conflicts with the 32 bit version with existing user code. Use from_uint64 to init from a uint64_t instead. + + void operator&=(const this_type& x); + void operator|=(const this_type& x); + void operator^=(const this_type& x); + + void operator<<=(size_type n); + void operator>>=(size_type n); + + void flip(); + void set(); + void set(size_type i, bool value); + void reset(); + + bool operator==(const this_type& x) const; + + bool any() const; + size_type count() const; + + void from_uint32(uint32_t value); + void from_uint64(uint64_t value); + + unsigned long to_ulong() const; + uint32_t to_uint32() const; + uint64_t to_uint64() const; + + word_type& DoGetWord(size_type i); + word_type DoGetWord(size_type i) const; + + size_type DoFindFirst() const; + size_type DoFindNext(size_type last_find) const; + + size_type DoFindLast() const; // Returns NW * kBitsPerWord (the bit count) if no bits are set. + size_type DoFindPrev(size_type last_find) const; // Returns NW * kBitsPerWord (the bit count) if no bits are set. + + }; // class BitsetBase + + + + /// BitsetBase<1, WordType> + /// + /// This is a specialization for a bitset that fits within one word. + /// + template + struct BitsetBase<1, WordType> + { + typedef WordType word_type; + typedef BitsetBase<1, WordType> this_type; + #if EASTL_BITSET_SIZE_T + typedef size_t size_type; + #else + typedef eastl_size_t size_type; + #endif + + enum { + kBitsPerWord = (8 * sizeof(word_type)), + kBitsPerWordMask = (kBitsPerWord - 1), + kBitsPerWordShift = ((kBitsPerWord == 8) ? 3 : ((kBitsPerWord == 16) ? 4 : ((kBitsPerWord == 32) ? 5 : (((kBitsPerWord == 64) ? 6 : 7))))) + }; + + public: + word_type mWord[1]; // Defined as an array of 1 so that bitset can treat this BitsetBase like others. + + public: + BitsetBase(); + BitsetBase(uint32_t value); + //BitsetBase(uint64_t value); // Disabled because it causes conflicts with the 32 bit version with existing user code. Use from_uint64 instead. + + void operator&=(const this_type& x); + void operator|=(const this_type& x); + void operator^=(const this_type& x); + + void operator<<=(size_type n); + void operator>>=(size_type n); + + void flip(); + void set(); + void set(size_type i, bool value); + void reset(); + + bool operator==(const this_type& x) const; + + bool any() const; + size_type count() const; + + void from_uint32(uint32_t value); + void from_uint64(uint64_t value); + + unsigned long to_ulong() const; + uint32_t to_uint32() const; + uint64_t to_uint64() const; + + word_type& DoGetWord(size_type); + word_type DoGetWord(size_type) const; + + size_type DoFindFirst() const; + size_type DoFindNext(size_type last_find) const; + + size_type DoFindLast() const; // Returns 1 * kBitsPerWord (the bit count) if no bits are set. + size_type DoFindPrev(size_type last_find) const; // Returns 1 * kBitsPerWord (the bit count) if no bits are set. + + }; // BitsetBase<1, WordType> + + + + /// BitsetBase<2, WordType> + /// + /// This is a specialization for a bitset that fits within two words. + /// The difference here is that we avoid branching (ifs and loops). + /// + template + struct BitsetBase<2, WordType> + { + typedef WordType word_type; + typedef BitsetBase<2, WordType> this_type; + #if EASTL_BITSET_SIZE_T + typedef size_t size_type; + #else + typedef eastl_size_t size_type; + #endif + + enum { + kBitsPerWord = (8 * sizeof(word_type)), + kBitsPerWordMask = (kBitsPerWord - 1), + kBitsPerWordShift = ((kBitsPerWord == 8) ? 3 : ((kBitsPerWord == 16) ? 4 : ((kBitsPerWord == 32) ? 5 : (((kBitsPerWord == 64) ? 6 : 7))))) + }; + + public: + word_type mWord[2]; + + public: + BitsetBase(); + BitsetBase(uint32_t value); + //BitsetBase(uint64_t value); // Disabled because it causes conflicts with the 32 bit version with existing user code. Use from_uint64 instead. + + void operator&=(const this_type& x); + void operator|=(const this_type& x); + void operator^=(const this_type& x); + + void operator<<=(size_type n); + void operator>>=(size_type n); + + void flip(); + void set(); + void set(size_type i, bool value); + void reset(); + + bool operator==(const this_type& x) const; + + bool any() const; + size_type count() const; + + void from_uint32(uint32_t value); + void from_uint64(uint64_t value); + + unsigned long to_ulong() const; + uint32_t to_uint32() const; + uint64_t to_uint64() const; + + word_type& DoGetWord(size_type); + word_type DoGetWord(size_type) const; + + size_type DoFindFirst() const; + size_type DoFindNext(size_type last_find) const; + + size_type DoFindLast() const; // Returns 2 * kBitsPerWord (the bit count) if no bits are set. + size_type DoFindPrev(size_type last_find) const; // Returns 2 * kBitsPerWord (the bit count) if no bits are set. + + }; // BitsetBase<2, WordType> + + + + + /// bitset + /// + /// Implements a bitset much like the C++ std::bitset. + /// + /// As of this writing we don't implement a specialization of bitset<0>, + /// as it is deemed an academic exercise that nobody would actually + /// use and it would increase code space and provide little practical + /// benefit. Note that this doesn't mean bitset<0> isn't supported; + /// it means that our version of it isn't as efficient as it would be + /// if a specialization was made for it. + /// + /// - N can be any unsigned (non-zero) value, though memory usage is + /// linear with respect to N, so large values of N use large amounts of memory. + /// - WordType must be one of [uint16_t, uint32_t, uint64_t, uint128_t] + /// and the compiler must support the type. By default the WordType is + /// the largest native register type that the target platform supports. + /// + template + class bitset : private BitsetBase + { + public: + typedef BitsetBase base_type; + typedef bitset this_type; + typedef WordType word_type; + typedef typename base_type::size_type size_type; + + enum + { + kBitsPerWord = (8 * sizeof(word_type)), + kBitsPerWordMask = (kBitsPerWord - 1), + kBitsPerWordShift = ((kBitsPerWord == 8) ? 3 : ((kBitsPerWord == 16) ? 4 : ((kBitsPerWord == 32) ? 5 : (((kBitsPerWord == 64) ? 6 : 7))))), + kSize = N, // The number of bits the bitset holds + kWordSize = sizeof(word_type), // The size of individual words the bitset uses to hold the bits. + kWordCount = BITSET_WORD_COUNT(N, WordType) // The number of words the bitset uses to hold the bits. sizeof(bitset) == kWordSize * kWordCount. + }; + + using base_type::mWord; + using base_type::DoGetWord; + using base_type::DoFindFirst; + using base_type::DoFindNext; + using base_type::DoFindLast; + using base_type::DoFindPrev; + using base_type::to_ulong; + using base_type::to_uint32; + using base_type::to_uint64; + using base_type::count; + using base_type::any; + + public: + /// reference + /// + /// A reference is a reference to a specific bit in the bitset. + /// The C++ standard specifies that this be a nested class, + /// though it is not clear if a non-nested reference implementation + /// would be non-conforming. + /// + class reference + { + protected: + friend class bitset; + + word_type* mpBitWord; + size_type mnBitIndex; + + reference(){} // The C++ standard specifies that this is private. + + public: + reference(const bitset& x, size_type i); + + reference& operator=(bool value); + reference& operator=(const reference& x); + + bool operator~() const; + operator bool() const // Defined inline because CodeWarrior fails to be able to compile it outside. + { return (*mpBitWord & (static_cast(1) << (mnBitIndex & kBitsPerWordMask))) != 0; } + + reference& flip(); + }; + + public: + friend class reference; + + bitset(); + bitset(uint32_t value); + //bitset(uint64_t value); // Disabled because it causes conflicts with the 32 bit version with existing user code. Use from_uint64 instead. + + // We don't define copy constructor and operator= because + // the compiler-generated versions will suffice. + + this_type& operator&=(const this_type& x); + this_type& operator|=(const this_type& x); + this_type& operator^=(const this_type& x); + + this_type& operator<<=(size_type n); + this_type& operator>>=(size_type n); + + this_type& set(); + this_type& set(size_type i, bool value = true); + + this_type& reset(); + this_type& reset(size_type i); + + this_type& flip(); + this_type& flip(size_type i); + this_type operator~() const; + + reference operator[](size_type i); + bool operator[](size_type i) const; + + const word_type* data() const; + word_type* data(); + + void from_uint32(uint32_t value); + void from_uint64(uint64_t value); + + //unsigned long to_ulong() const; // We inherit this from the base class. + //uint32_t to_uint32() const; + //uint64_t to_uint64() const; + + //size_type count() const; // We inherit this from the base class. + size_type size() const; + + bool operator==(const this_type& x) const; + bool operator!=(const this_type& x) const; + + bool test(size_type i) const; + //bool any() const; // We inherit this from the base class. + bool all() const; + bool none() const; + + this_type operator<<(size_type n) const; + this_type operator>>(size_type n) const; + + // Finds the index of the first "on" bit, returns kSize if none are set. + size_type find_first() const; + + // Finds the index of the next "on" bit after last_find, returns kSize if none are set. + size_type find_next(size_type last_find) const; + + // Finds the index of the last "on" bit, returns kSize if none are set. + size_type find_last() const; + + // Finds the index of the last "on" bit before last_find, returns kSize if none are set. + size_type find_prev(size_type last_find) const; + + }; // bitset + + + + + + + + /// BitsetCountBits + /// + /// This is a fast trick way to count bits without branches nor memory accesses. + /// + inline uint32_t BitsetCountBits(uint64_t x) + { + // GCC 3.x's implementation of UINT64_C is broken and fails to deal with + // the code below correctly. So we make a workaround for it. Earlier and + // later versions of GCC don't have this bug. + + #if defined(__GNUC__) && (__GNUC__ == 3) + x = x - ((x >> 1) & 0x5555555555555555ULL); + x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL); + x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + return (uint32_t)((x * 0x0101010101010101ULL) >> 56); + #else + x = x - ((x >> 1) & UINT64_C(0x5555555555555555)); + x = (x & UINT64_C(0x3333333333333333)) + ((x >> 2) & UINT64_C(0x3333333333333333)); + x = (x + (x >> 4)) & UINT64_C(0x0F0F0F0F0F0F0F0F); + return (uint32_t)((x * UINT64_C(0x0101010101010101)) >> 56); + #endif + } + + inline uint32_t BitsetCountBits(uint32_t x) + { + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + return (uint32_t)((x * 0x01010101) >> 24); + } + + inline uint32_t BitsetCountBits(uint16_t x) + { + return BitsetCountBits((uint32_t)x); + } + + inline uint32_t BitsetCountBits(uint8_t x) + { + return BitsetCountBits((uint32_t)x); + } + + + // const static char kBitsPerUint16[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; + #define EASTL_BITSET_COUNT_STRING "\0\1\1\2\1\2\2\3\1\2\2\3\2\3\3\4" + + + inline uint32_t GetFirstBit(uint8_t x) + { + if(x) + { + uint32_t n = 1; + + if((x & 0x0000000F) == 0) { n += 4; x >>= 4; } + if((x & 0x00000003) == 0) { n += 2; x >>= 2; } + + return (uint32_t)(n - (x & 1)); + } + + return 8; + } + + inline uint32_t GetFirstBit(uint16_t x) // To do: Update this to use VC++ _BitScanForward, _BitScanForward64; GCC __builtin_ctz, __builtin_ctzl. VC++ __lzcnt16, __lzcnt, __lzcnt64 requires recent CPUs (2013+) and probably can't be used. http://en.wikipedia.org/wiki/Haswell_%28microarchitecture%29#New_features + { + if(x) + { + uint32_t n = 1; + + if((x & 0x000000FF) == 0) { n += 8; x >>= 8; } + if((x & 0x0000000F) == 0) { n += 4; x >>= 4; } + if((x & 0x00000003) == 0) { n += 2; x >>= 2; } + + return (uint32_t)(n - (x & 1)); + } + + return 16; + } + + inline uint32_t GetFirstBit(uint32_t x) + { + if(x) + { + uint32_t n = 1; + + if((x & 0x0000FFFF) == 0) { n += 16; x >>= 16; } + if((x & 0x000000FF) == 0) { n += 8; x >>= 8; } + if((x & 0x0000000F) == 0) { n += 4; x >>= 4; } + if((x & 0x00000003) == 0) { n += 2; x >>= 2; } + + return (n - (x & 1)); + } + + return 32; + } + + inline uint32_t GetFirstBit(uint64_t x) + { + if(x) + { + uint32_t n = 1; + + if((x & 0xFFFFFFFF) == 0) { n += 32; x >>= 32; } + if((x & 0x0000FFFF) == 0) { n += 16; x >>= 16; } + if((x & 0x000000FF) == 0) { n += 8; x >>= 8; } + if((x & 0x0000000F) == 0) { n += 4; x >>= 4; } + if((x & 0x00000003) == 0) { n += 2; x >>= 2; } + + return (n - ((uint32_t)x & 1)); + } + + return 64; + } + + + #if EASTL_INT128_SUPPORTED + inline uint32_t GetFirstBit(eastl_uint128_t x) + { + if(x) + { + uint32_t n = 1; + + if((x & UINT64_C(0xFFFFFFFFFFFFFFFF)) == 0) { n += 64; x >>= 64; } + if((x & 0xFFFFFFFF) == 0) { n += 32; x >>= 32; } + if((x & 0x0000FFFF) == 0) { n += 16; x >>= 16; } + if((x & 0x000000FF) == 0) { n += 8; x >>= 8; } + if((x & 0x0000000F) == 0) { n += 4; x >>= 4; } + if((x & 0x00000003) == 0) { n += 2; x >>= 2; } + + return (n - ((uint32_t)x & 1)); + } + + return 128; + } + #endif + + inline uint32_t GetLastBit(uint8_t x) + { + if(x) + { + uint32_t n = 0; + + if(x & 0xFFF0) { n += 4; x >>= 4; } + if(x & 0xFFFC) { n += 2; x >>= 2; } + if(x & 0xFFFE) { n += 1; } + + return n; + } + + return 8; + } + + inline uint32_t GetLastBit(uint16_t x) + { + if(x) + { + uint32_t n = 0; + + if(x & 0xFF00) { n += 8; x >>= 8; } + if(x & 0xFFF0) { n += 4; x >>= 4; } + if(x & 0xFFFC) { n += 2; x >>= 2; } + if(x & 0xFFFE) { n += 1; } + + return n; + } + + return 16; + } + + inline uint32_t GetLastBit(uint32_t x) + { + if(x) + { + uint32_t n = 0; + + 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 n; + } + + return 32; + } + + inline uint32_t GetLastBit(uint64_t x) + { + if(x) + { + uint32_t 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 n; + } + + return 64; + } + + #if EASTL_INT128_SUPPORTED + inline uint32_t GetLastBit(eastl_uint128_t x) + { + if(x) + { + uint32_t n = 0; + + eastl_uint128_t mask(UINT64_C(0xFFFFFFFF00000000)); // There doesn't seem to exist compiler support for INT128_C() by any compiler. EAStdC's int128_t supports it though. + mask <<= 64; + + if(x & mask) { n += 64; x >>= 64; } + if(x & UINT64_C(0xFFFFFFFF00000000)) { n += 32; x >>= 32; } + if(x & UINT64_C(0x00000000FFFF0000)) { n += 16; x >>= 16; } + if(x & UINT64_C(0x00000000FFFFFF00)) { n += 8; x >>= 8; } + if(x & UINT64_C(0x00000000FFFFFFF0)) { n += 4; x >>= 4; } + if(x & UINT64_C(0x00000000FFFFFFFC)) { n += 2; x >>= 2; } + if(x & UINT64_C(0x00000000FFFFFFFE)) { n += 1; } + + return n; + } + + return 128; + } + #endif + + + + + /////////////////////////////////////////////////////////////////////////// + // BitsetBase + // + // We tried two forms of array access here: + // for(word_type *pWord(mWord), *pWordEnd(mWord + NW); pWord < pWordEnd; ++pWord) + // *pWord = ... + // and + // for(size_t i = 0; i < NW; i++) + // mWord[i] = ... + // + // For our tests (~NW < 16), the latter (using []) access resulted in faster code. + /////////////////////////////////////////////////////////////////////////// + + template + inline BitsetBase::BitsetBase() + { + reset(); + } + + + template + inline BitsetBase::BitsetBase(uint32_t value) + { + // This implementation assumes that sizeof(value) <= sizeof(word_type). + //EASTL_CT_ASSERT(sizeof(value) <= sizeof(word_type)); Disabled because we now have support for uint8_t and uint16_t word types. It would be nice to have a runtime assert that tested this. + + reset(); + mWord[0] = static_cast(value); + } + + + /* + template + inline BitsetBase::BitsetBase(uint64_t value) + { + reset(); + + #if(EA_PLATFORM_WORD_SIZE == 4) + mWord[0] = static_cast(value); + + EASTL_CT_ASSERT(NW > 2); // We can assume this because we have specializations of BitsetBase for <1> and <2>. + //if(NW > 1) // NW is a template constant, but it would be a little messy to take advantage of it's const-ness. + mWord[1] = static_cast(value >> 32); + #else + mWord[0] = static_cast(value); + #endif + } + */ + + + template + inline void BitsetBase::operator&=(const this_type& x) + { + for(size_t i = 0; i < NW; i++) + mWord[i] &= x.mWord[i]; + } + + + template + inline void BitsetBase::operator|=(const this_type& x) + { + for(size_t i = 0; i < NW; i++) + mWord[i] |= x.mWord[i]; + } + + + template + inline void BitsetBase::operator^=(const this_type& x) + { + for(size_t i = 0; i < NW; i++) + mWord[i] ^= x.mWord[i]; + } + + + template + inline void BitsetBase::operator<<=(size_type n) + { + const size_type nWordShift = (size_type)(n >> kBitsPerWordShift); + + if(nWordShift) + { + for(int i = (int)(NW - 1); i >= 0; --i) + mWord[i] = (nWordShift <= (size_type)i) ? mWord[i - nWordShift] : (word_type)0; + } + + if(n &= kBitsPerWordMask) + { + for(size_t i = (NW - 1); i > 0; --i) + mWord[i] = (word_type)((mWord[i] << n) | (mWord[i - 1] >> (kBitsPerWord - n))); + mWord[0] <<= n; + } + + // We let the parent class turn off any upper bits. + } + + + template + inline void BitsetBase::operator>>=(size_type n) + { + const size_type nWordShift = (size_type)(n >> kBitsPerWordShift); + + if(nWordShift) + { + for(size_t i = 0; i < NW; ++i) + mWord[i] = ((nWordShift < (NW - i)) ? mWord[i + nWordShift] : (word_type)0); + } + + if(n &= kBitsPerWordMask) + { + for(size_t i = 0; i < (NW - 1); ++i) + mWord[i] = (word_type)((mWord[i] >> n) | (mWord[i + 1] << (kBitsPerWord - n))); + mWord[NW - 1] >>= n; + } + } + + + template + inline void BitsetBase::flip() + { + for(size_t i = 0; i < NW; i++) + mWord[i] = ~mWord[i]; + // We let the parent class turn off any upper bits. + } + + + template + inline void BitsetBase::set() + { + for(size_t i = 0; i < NW; i++) + mWord[i] = static_cast(~static_cast(0)); + // We let the parent class turn off any upper bits. + } + + + template + inline void BitsetBase::set(size_type i, bool value) + { + if(value) + mWord[i >> kBitsPerWordShift] |= (static_cast(1) << (i & kBitsPerWordMask)); + else + mWord[i >> kBitsPerWordShift] &= ~(static_cast(1) << (i & kBitsPerWordMask)); + } + + + template + inline void BitsetBase::reset() + { + if(NW > 16) // This is a constant expression and should be optimized away. + { + // This will be fastest if compiler intrinsic function optimizations are enabled. + memset(mWord, 0, sizeof(mWord)); + } + else + { + for(size_t i = 0; i < NW; i++) + mWord[i] = 0; + } + } + + + template + inline bool BitsetBase::operator==(const this_type& x) const + { + for(size_t i = 0; i < NW; i++) + { + if(mWord[i] != x.mWord[i]) + return false; + } + return true; + } + + + template + inline bool BitsetBase::any() const + { + for(size_t i = 0; i < NW; i++) + { + if(mWord[i]) + return true; + } + return false; + } + + + template + inline typename BitsetBase::size_type + BitsetBase::count() const + { + size_type n = 0; + + for(size_t i = 0; i < NW; i++) + { + #if defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 304) && !defined(EA_PLATFORM_ANDROID) // GCC 3.4 or later + #if(EA_PLATFORM_WORD_SIZE == 4) + n += (size_type)__builtin_popcountl(mWord[i]); + #else + n += (size_type)__builtin_popcountll(mWord[i]); + #endif + #elif defined(__GNUC__) && (__GNUC__ < 3) + n += BitsetCountBits(mWord[i]); // GCC 2.x compiler inexplicably blows up on the code below. + #else + // todo: use __popcnt16, __popcnt, __popcnt64 for msvc builds + // https://msdn.microsoft.com/en-us/library/bb385231(v=vs.140).aspx + for(word_type w = mWord[i]; w; w >>= 4) + n += EASTL_BITSET_COUNT_STRING[w & 0xF]; + + // Version which seems to run slower in benchmarks: + // n += BitsetCountBits(mWord[i]); + #endif + + } + return n; + } + + + template + inline void BitsetBase::from_uint32(uint32_t value) + { + reset(); + mWord[0] = static_cast(value); + } + + + template + inline void BitsetBase::from_uint64(uint64_t value) + { + reset(); + + #if(EA_PLATFORM_WORD_SIZE == 4) + mWord[0] = static_cast(value); + + EASTL_CT_ASSERT(NW > 2); // We can assume this because we have specializations of BitsetBase for <1> and <2>. + //if(NW > 1) // NW is a template constant, but it would be a little messy to take advantage of it's const-ness. + mWord[1] = static_cast(value >> 32); + #else + mWord[0] = static_cast(value); + #endif + } + + + template + inline unsigned long BitsetBase::to_ulong() const + { + #if EASTL_EXCEPTIONS_ENABLED + for(size_t i = 1; i < NW; ++i) + { + if(mWord[i]) + throw std::overflow_error("BitsetBase::to_ulong"); + } + #endif + return (unsigned long)mWord[0]; // Todo: We need to deal with the case whereby sizeof(word_type) < sizeof(unsigned long) + } + + + template + inline uint32_t BitsetBase::to_uint32() const + { + #if EASTL_EXCEPTIONS_ENABLED + // Verify that high words or bits are not set and thus that to_uint32 doesn't lose information. + for(size_t i = 1; i < NW; ++i) + { + if(mWord[i]) + throw std::overflow_error("BitsetBase::to_uint32"); + } + + #if(EA_PLATFORM_WORD_SIZE > 4) // if we have 64 bit words... + if(mWord[0] >> 32) + throw std::overflow_error("BitsetBase::to_uint32"); + #endif + #endif + + return (uint32_t)mWord[0]; + } + + + template + inline uint64_t BitsetBase::to_uint64() const + { + #if EASTL_EXCEPTIONS_ENABLED + // Verify that high words are not set and thus that to_uint64 doesn't lose information. + + EASTL_CT_ASSERT(NW > 2); // We can assume this because we have specializations of BitsetBase for <1> and <2>. + for(size_t i = 2; i < NW; ++i) + { + if(mWord[i]) + throw std::overflow_error("BitsetBase::to_uint64"); + } + #endif + + #if(EA_PLATFORM_WORD_SIZE == 4) + EASTL_CT_ASSERT(NW > 2); // We can assume this because we have specializations of BitsetBase for <1> and <2>. + return (mWord[1] << 32) | mWord[0]; + #else + return (uint64_t)mWord[0]; + #endif + } + + + template + inline typename BitsetBase::word_type& + BitsetBase::DoGetWord(size_type i) + { + return mWord[i >> kBitsPerWordShift]; + } + + + template + inline typename BitsetBase::word_type + BitsetBase::DoGetWord(size_type i) const + { + return mWord[i >> kBitsPerWordShift]; + } + + + template + inline typename BitsetBase::size_type + BitsetBase::DoFindFirst() const + { + for(size_type word_index = 0; word_index < NW; ++word_index) + { + const size_type fbiw = GetFirstBit(mWord[word_index]); + + if(fbiw != kBitsPerWord) + return (word_index * kBitsPerWord) + fbiw; + } + + return (size_type)NW * kBitsPerWord; + } + + +#if EASTL_DISABLE_BITSET_ARRAYBOUNDS_WARNING +EA_DISABLE_GCC_WARNING(-Warray-bounds) +#endif + + template + inline typename BitsetBase::size_type + BitsetBase::DoFindNext(size_type last_find) const + { + // Start looking from the next bit. + ++last_find; + + // Set initial state based on last find. + size_type word_index = static_cast(last_find >> kBitsPerWordShift); + size_type bit_index = static_cast(last_find & kBitsPerWordMask); + + // To do: There probably is a more elegant way to write looping below. + if(word_index < NW) + { + // Mask off previous bits of the word so our search becomes a "find first". + word_type this_word = mWord[word_index] & (~static_cast(0) << bit_index); + + for(;;) + { + const size_type fbiw = GetFirstBit(this_word); + + if(fbiw != kBitsPerWord) + return (word_index * kBitsPerWord) + fbiw; + + if(++word_index < NW) + this_word = mWord[word_index]; + else + break; + } + } + + return (size_type)NW * kBitsPerWord; + } + +#if EASTL_DISABLE_BITSET_ARRAYBOUNDS_WARNING +EA_RESTORE_GCC_WARNING() +#endif + + + + template + inline typename BitsetBase::size_type + BitsetBase::DoFindLast() const + { + for(size_type word_index = (size_type)NW; word_index > 0; --word_index) + { + const size_type lbiw = GetLastBit(mWord[word_index - 1]); + + if(lbiw != kBitsPerWord) + return ((word_index - 1) * kBitsPerWord) + lbiw; + } + + return (size_type)NW * kBitsPerWord; + } + + + template + inline typename BitsetBase::size_type + BitsetBase::DoFindPrev(size_type last_find) const + { + if(last_find > 0) + { + // Set initial state based on last find. + size_type word_index = static_cast(last_find >> kBitsPerWordShift); + size_type bit_index = static_cast(last_find & kBitsPerWordMask); + + // Mask off subsequent bits of the word so our search becomes a "find last". + word_type mask = (~static_cast(0) >> (kBitsPerWord - 1 - bit_index)) >> 1; // We do two shifts here because many CPUs ignore requests to shift 32 bit integers by 32 bits, which could be the case above. + word_type this_word = mWord[word_index] & mask; + + for(;;) + { + const size_type lbiw = GetLastBit(this_word); + + if(lbiw != kBitsPerWord) + return (word_index * kBitsPerWord) + lbiw; + + if(word_index > 0) + this_word = mWord[--word_index]; + else + break; + } + } + + return (size_type)NW * kBitsPerWord; + } + + + + /////////////////////////////////////////////////////////////////////////// + // BitsetBase<1, WordType> + /////////////////////////////////////////////////////////////////////////// + + template + inline BitsetBase<1, WordType>::BitsetBase() + { + mWord[0] = 0; + } + + + template + inline BitsetBase<1, WordType>::BitsetBase(uint32_t value) + { + // This implementation assumes that sizeof(value) <= sizeof(word_type). + //EASTL_CT_ASSERT(sizeof(value) <= sizeof(word_type)); Disabled because we now have support for uint8_t and uint16_t word types. It would be nice to have a runtime assert that tested this. + + mWord[0] = static_cast(value); + } + + + /* + template + inline BitsetBase<1, WordType>::BitsetBase(uint64_t value) + { + #if(EA_PLATFORM_WORD_SIZE == 4) + EASTL_ASSERT(value <= 0xffffffff); + mWord[0] = static_cast(value); // This potentially loses data, but that's what the user is requesting. + #else + mWord[0] = static_cast(value); + #endif + } + */ + + + template + inline void BitsetBase<1, WordType>::operator&=(const this_type& x) + { + mWord[0] &= x.mWord[0]; + } + + + template + inline void BitsetBase<1, WordType>::operator|=(const this_type& x) + { + mWord[0] |= x.mWord[0]; + } + + + template + inline void BitsetBase<1, WordType>::operator^=(const this_type& x) + { + mWord[0] ^= x.mWord[0]; + } + + + template + inline void BitsetBase<1, WordType>::operator<<=(size_type n) + { + mWord[0] <<= n; + // We let the parent class turn off any upper bits. + } + + + template + inline void BitsetBase<1, WordType>::operator>>=(size_type n) + { + mWord[0] >>= n; + } + + + template + inline void BitsetBase<1, WordType>::flip() + { + mWord[0] = ~mWord[0]; + // We let the parent class turn off any upper bits. + } + + + template + inline void BitsetBase<1, WordType>::set() + { + mWord[0] = static_cast(~static_cast(0)); + // We let the parent class turn off any upper bits. + } + + + template + inline void BitsetBase<1, WordType>::set(size_type i, bool value) + { + if(value) + mWord[0] |= (static_cast(1) << i); + else + mWord[0] &= ~(static_cast(1) << i); + } + + + template + inline void BitsetBase<1, WordType>::reset() + { + mWord[0] = 0; + } + + + template + inline bool BitsetBase<1, WordType>::operator==(const this_type& x) const + { + return mWord[0] == x.mWord[0]; + } + + + template + inline bool BitsetBase<1, WordType>::any() const + { + return mWord[0] != 0; + } + + + template + inline typename BitsetBase<1, WordType>::size_type + BitsetBase<1, WordType>::count() const + { + #if defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 304) && !defined(EA_PLATFORM_ANDROID) // GCC 3.4 or later + #if(EA_PLATFORM_WORD_SIZE == 4) + return (size_type)__builtin_popcountl(mWord[0]); + #else + return (size_type)__builtin_popcountll(mWord[0]); + #endif + #elif defined(__GNUC__) && (__GNUC__ < 3) + return BitsetCountBits(mWord[0]); // GCC 2.x compiler inexplicably blows up on the code below. + #else + size_type n = 0; + for(word_type w = mWord[0]; w; w >>= 4) + n += EASTL_BITSET_COUNT_STRING[w & 0xF]; + return n; + #endif + } + + + template + inline void BitsetBase<1, WordType>::from_uint32(uint32_t value) + { + mWord[0] = static_cast(value); + } + + + template + inline void BitsetBase<1, WordType>::from_uint64(uint64_t value) + { + #if(EA_PLATFORM_WORD_SIZE == 4) + EASTL_ASSERT(value <= 0xffffffff); + mWord[0] = static_cast(value); // This potentially loses data, but that's what the user is requesting. + #else + mWord[0] = static_cast(value); + #endif + } + + + template + inline unsigned long BitsetBase<1, WordType>::to_ulong() const + { + #if EASTL_EXCEPTIONS_ENABLED + #if((EA_PLATFORM_WORD_SIZE > 4) && defined(EA_PLATFORM_MICROSOFT)) // If we are using 64 bit words but ulong is less than 64 bits... Microsoft platforms alone use a 32 bit long under 64 bit platforms. + // Verify that high bits are not set and thus that to_ulong doesn't lose information. + if(mWord[0] >> 32) + throw std::overflow_error("BitsetBase::to_ulong"); + #endif + #endif + + return static_cast(mWord[0]); + } + + + template + inline uint32_t BitsetBase<1, WordType>::to_uint32() const + { + #if EASTL_EXCEPTIONS_ENABLED + #if(EA_PLATFORM_WORD_SIZE > 4) // If we are using 64 bit words... + // Verify that high bits are not set and thus that to_uint32 doesn't lose information. + if(mWord[0] >> 32) + throw std::overflow_error("BitsetBase::to_uint32"); + #endif + #endif + + return static_cast(mWord[0]); + } + + + template + inline uint64_t BitsetBase<1, WordType>::to_uint64() const + { + // This implementation is the same regardless of the word size, and there is no possibility of overflow_error. + return static_cast(mWord[0]); + } + + + template + inline typename BitsetBase<1, WordType>::word_type& + BitsetBase<1, WordType>::DoGetWord(size_type) + { + return mWord[0]; + } + + + template + inline typename BitsetBase<1, WordType>::word_type + BitsetBase<1, WordType>::DoGetWord(size_type) const + { + return mWord[0]; + } + + + template + inline typename BitsetBase<1, WordType>::size_type + BitsetBase<1, WordType>::DoFindFirst() const + { + return GetFirstBit(mWord[0]); + } + + + template + inline typename BitsetBase<1, WordType>::size_type + BitsetBase<1, WordType>::DoFindNext(size_type last_find) const + { + if(++last_find < kBitsPerWord) + { + // Mask off previous bits of word so our search becomes a "find first". + const word_type this_word = mWord[0] & ((~static_cast(0)) << last_find); + + return GetFirstBit(this_word); + } + + return kBitsPerWord; + } + + + template + inline typename BitsetBase<1, WordType>::size_type + BitsetBase<1, WordType>::DoFindLast() const + { + return GetLastBit(mWord[0]); + } + + + template + inline typename BitsetBase<1, WordType>::size_type + BitsetBase<1, WordType>::DoFindPrev(size_type last_find) const + { + if(last_find > 0) + { + // Mask off previous bits of word so our search becomes a "find first". + const word_type this_word = mWord[0] & ((~static_cast(0)) >> (kBitsPerWord - last_find)); + + return GetLastBit(this_word); + } + + return kBitsPerWord; + } + + + + + /////////////////////////////////////////////////////////////////////////// + // BitsetBase<2, WordType> + /////////////////////////////////////////////////////////////////////////// + + template + inline BitsetBase<2, WordType>::BitsetBase() + { + mWord[0] = 0; + mWord[1] = 0; + } + + + template + inline BitsetBase<2, WordType>::BitsetBase(uint32_t value) + { + // This implementation assumes that sizeof(value) <= sizeof(word_type). + //EASTL_CT_ASSERT(sizeof(value) <= sizeof(word_type)); Disabled because we now have support for uint8_t and uint16_t word types. It would be nice to have a runtime assert that tested this. + + mWord[0] = static_cast(value); + mWord[1] = 0; + } + + + /* + template + inline BitsetBase<2, WordType>::BitsetBase(uint64_t value) + { + #if(EA_PLATFORM_WORD_SIZE == 4) + mWord[0] = static_cast(value); + mWord[1] = static_cast(value >> 32); + #else + mWord[0] = static_cast(value); + mWord[1] = 0; + #endif + } + */ + + + template + inline void BitsetBase<2, WordType>::operator&=(const this_type& x) + { + mWord[0] &= x.mWord[0]; + mWord[1] &= x.mWord[1]; + } + + + template + inline void BitsetBase<2, WordType>::operator|=(const this_type& x) + { + mWord[0] |= x.mWord[0]; + mWord[1] |= x.mWord[1]; + } + + + template + inline void BitsetBase<2, WordType>::operator^=(const this_type& x) + { + mWord[0] ^= x.mWord[0]; + mWord[1] ^= x.mWord[1]; + } + + + template + inline void BitsetBase<2, WordType>::operator<<=(size_type n) + { + if(n) // to avoid a shift by kBitsPerWord, which is undefined + { + if(EASTL_UNLIKELY(n >= kBitsPerWord)) // parent expected to handle high bits and n >= 64 + { + mWord[1] = mWord[0]; + mWord[0] = 0; + n -= kBitsPerWord; + } + + mWord[1] = (mWord[1] << n) | (mWord[0] >> (kBitsPerWord - n)); // Intentionally use | instead of +. + mWord[0] <<= n; + // We let the parent class turn off any upper bits. + } + } + + + template + inline void BitsetBase<2, WordType>::operator>>=(size_type n) + { + if(n) // to avoid a shift by kBitsPerWord, which is undefined + { + if(EASTL_UNLIKELY(n >= kBitsPerWord)) // parent expected to handle n >= 64 + { + mWord[0] = mWord[1]; + mWord[1] = 0; + n -= kBitsPerWord; + } + + mWord[0] = (mWord[0] >> n) | (mWord[1] << (kBitsPerWord - n)); // Intentionally use | instead of +. + mWord[1] >>= n; + } + } + + + template + inline void BitsetBase<2, WordType>::flip() + { + mWord[0] = ~mWord[0]; + mWord[1] = ~mWord[1]; + // We let the parent class turn off any upper bits. + } + + + template + inline void BitsetBase<2, WordType>::set() + { + mWord[0] = ~static_cast(0); + mWord[1] = ~static_cast(0); + // We let the parent class turn off any upper bits. + } + + + template + inline void BitsetBase<2, WordType>::set(size_type i, bool value) + { + if(value) + mWord[i >> kBitsPerWordShift] |= (static_cast(1) << (i & kBitsPerWordMask)); + else + mWord[i >> kBitsPerWordShift] &= ~(static_cast(1) << (i & kBitsPerWordMask)); + } + + + template + inline void BitsetBase<2, WordType>::reset() + { + mWord[0] = 0; + mWord[1] = 0; + } + + + template + inline bool BitsetBase<2, WordType>::operator==(const this_type& x) const + { + return (mWord[0] == x.mWord[0]) && (mWord[1] == x.mWord[1]); + } + + + template + inline bool BitsetBase<2, WordType>::any() const + { + // Or with two branches: { return (mWord[0] != 0) || (mWord[1] != 0); } + return (mWord[0] | mWord[1]) != 0; + } + + template + inline typename BitsetBase<2, WordType>::size_type + BitsetBase<2, WordType>::count() const + { + #if (defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 304)) || defined(__clang__) // GCC 3.4 or later + #if(EA_PLATFORM_WORD_SIZE == 4) + return (size_type)__builtin_popcountl(mWord[0]) + (size_type)__builtin_popcountl(mWord[1]); + #else + return (size_type)__builtin_popcountll(mWord[0]) + (size_type)__builtin_popcountll(mWord[1]); + #endif + + #else + return BitsetCountBits(mWord[0]) + BitsetCountBits(mWord[1]); + #endif + } + + + template + inline void BitsetBase<2, WordType>::from_uint32(uint32_t value) + { + mWord[0] = static_cast(value); + mWord[1] = 0; + } + + + template + inline void BitsetBase<2, WordType>::from_uint64(uint64_t value) + { + #if(EA_PLATFORM_WORD_SIZE == 4) + mWord[0] = static_cast(value); + mWord[1] = static_cast(value >> 32); + #else + mWord[0] = static_cast(value); + mWord[1] = 0; + #endif + } + + + template + inline unsigned long BitsetBase<2, WordType>::to_ulong() const + { + #if EASTL_EXCEPTIONS_ENABLED + if(mWord[1]) + throw std::overflow_error("BitsetBase::to_ulong"); + #endif + return (unsigned long)mWord[0]; // Todo: We need to deal with the case whereby sizeof(word_type) < sizeof(unsigned long) + } + + + template + inline uint32_t BitsetBase<2, WordType>::to_uint32() const + { + #if EASTL_EXCEPTIONS_ENABLED + // Verify that high words or bits are not set and thus that to_uint32 doesn't lose information. + + #if(EA_PLATFORM_WORD_SIZE == 4) + if(mWord[1]) + throw std::overflow_error("BitsetBase::to_uint32"); + #else + if(mWord[1] || (mWord[0] >> 32)) + throw std::overflow_error("BitsetBase::to_uint32"); + #endif + #endif + + return (uint32_t)mWord[0]; + } + + + template + inline uint64_t BitsetBase<2, WordType>::to_uint64() const + { + #if(EA_PLATFORM_WORD_SIZE == 4) + // There can't possibly be an overflow_error here. + + return ((uint64_t)mWord[1] << 32) | mWord[0]; + #else + #if EASTL_EXCEPTIONS_ENABLED + if(mWord[1]) + throw std::overflow_error("BitsetBase::to_uint64"); + #endif + + return (uint64_t)mWord[0]; + #endif + } + + + template + inline typename BitsetBase<2, WordType>::word_type& + BitsetBase<2, WordType>::DoGetWord(size_type i) + { + return mWord[i >> kBitsPerWordShift]; + } + + + template + inline typename BitsetBase<2, WordType>::word_type + BitsetBase<2, WordType>::DoGetWord(size_type i) const + { + return mWord[i >> kBitsPerWordShift]; + } + + + template + inline typename BitsetBase<2, WordType>::size_type + BitsetBase<2, WordType>::DoFindFirst() const + { + size_type fbiw = GetFirstBit(mWord[0]); + + if(fbiw != kBitsPerWord) + return fbiw; + + fbiw = GetFirstBit(mWord[1]); + + if(fbiw != kBitsPerWord) + return kBitsPerWord + fbiw; + + return 2 * kBitsPerWord; + } + + + template + inline typename BitsetBase<2, WordType>::size_type + BitsetBase<2, WordType>::DoFindNext(size_type last_find) const + { + // If the last find was in the first word, we must check it and then possibly the second. + if(++last_find < (size_type)kBitsPerWord) + { + // Mask off previous bits of word so our search becomes a "find first". + word_type this_word = mWord[0] & ((~static_cast(0)) << last_find); + + // Step through words. + size_type fbiw = GetFirstBit(this_word); + + if(fbiw != kBitsPerWord) + return fbiw; + + fbiw = GetFirstBit(mWord[1]); + + if(fbiw != kBitsPerWord) + return kBitsPerWord + fbiw; + } + else if(last_find < (size_type)(2 * kBitsPerWord)) + { + // The last find was in the second word, remove the bit count of the first word from the find. + last_find -= kBitsPerWord; + + // Mask off previous bits of word so our search becomes a "find first". + word_type this_word = mWord[1] & ((~static_cast(0)) << last_find); + + const size_type fbiw = GetFirstBit(this_word); + + if(fbiw != kBitsPerWord) + return kBitsPerWord + fbiw; + } + + return 2 * kBitsPerWord; + } + + + template + inline typename BitsetBase<2, WordType>::size_type + BitsetBase<2, WordType>::DoFindLast() const + { + size_type lbiw = GetLastBit(mWord[1]); + + if(lbiw != kBitsPerWord) + return kBitsPerWord + lbiw; + + lbiw = GetLastBit(mWord[0]); + + if(lbiw != kBitsPerWord) + return lbiw; + + return 2 * kBitsPerWord; + } + + + template + inline typename BitsetBase<2, WordType>::size_type + BitsetBase<2, WordType>::DoFindPrev(size_type last_find) const + { + // If the last find was in the second word, we must check it and then possibly the first. + if(last_find > (size_type)kBitsPerWord) + { + // This has the same effect as last_find %= kBitsPerWord in our case. + last_find -= kBitsPerWord; + + // Mask off previous bits of word so our search becomes a "find first". + word_type this_word = mWord[1] & ((~static_cast(0)) >> (kBitsPerWord - last_find)); + + // Step through words. + size_type lbiw = GetLastBit(this_word); + + if(lbiw != kBitsPerWord) + return kBitsPerWord + lbiw; + + lbiw = GetLastBit(mWord[0]); + + if(lbiw != kBitsPerWord) + return lbiw; + } + else if(last_find != 0) + { + // Mask off previous bits of word so our search becomes a "find first". + word_type this_word = mWord[0] & ((~static_cast(0)) >> (kBitsPerWord - last_find)); + + const size_type lbiw = GetLastBit(this_word); + + if(lbiw != kBitsPerWord) + return lbiw; + } + + return 2 * kBitsPerWord; + } + + + + /////////////////////////////////////////////////////////////////////////// + // bitset::reference + /////////////////////////////////////////////////////////////////////////// + + template + inline bitset::reference::reference(const bitset& x, size_type i) + : mpBitWord(&const_cast(x).DoGetWord(i)), + mnBitIndex(i & kBitsPerWordMask) + { // We have an issue here because the above is casting away the const-ness of the source bitset. + // Empty + } + + + template + inline typename bitset::reference& + bitset::reference::operator=(bool value) + { + if(value) + *mpBitWord |= (static_cast(1) << (mnBitIndex & kBitsPerWordMask)); + else + *mpBitWord &= ~(static_cast(1) << (mnBitIndex & kBitsPerWordMask)); + return *this; + } + + + template + inline typename bitset::reference& + bitset::reference::operator=(const reference& x) + { + if(*x.mpBitWord & (static_cast(1) << (x.mnBitIndex & kBitsPerWordMask))) + *mpBitWord |= (static_cast(1) << (mnBitIndex & kBitsPerWordMask)); + else + *mpBitWord &= ~(static_cast(1) << (mnBitIndex & kBitsPerWordMask)); + return *this; + } + + + template + inline bool bitset::reference::operator~() const + { + return (*mpBitWord & (static_cast(1) << (mnBitIndex & kBitsPerWordMask))) == 0; + } + + + //Defined inline in the class because Metrowerks fails to be able to compile it here. + //template + //inline bitset::reference::operator bool() const + //{ + // return (*mpBitWord & (static_cast(1) << (mnBitIndex & kBitsPerWordMask))) != 0; + //} + + + template + inline typename bitset::reference& + bitset::reference::flip() + { + *mpBitWord ^= static_cast(1) << (mnBitIndex & kBitsPerWordMask); + return *this; + } + + + + + /////////////////////////////////////////////////////////////////////////// + // bitset + /////////////////////////////////////////////////////////////////////////// + + template + inline bitset::bitset() + : base_type() + { + // Empty. The base class will set all bits to zero. + } + + EA_DISABLE_VC_WARNING(6313) + template + inline bitset::bitset(uint32_t value) + : base_type(value) + { + if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... (If we didn't have this check, then the code below would do the wrong thing when N == 32. + mWord[kWordCount - 1] &= ~(static_cast(~static_cast(0)) << (N & kBitsPerWordMask)); // This clears any high unused bits. + } + EA_RESTORE_VC_WARNING() + + /* + template + inline bitset::bitset(uint64_t value) + : base_type(value) + { + if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... + mWord[kWordCount - 1] &= ~(~static_cast(0) << (N & kBitsPerWordMask)); // This clears any high unused bits. + } + */ + + + template + inline typename bitset::this_type& + bitset::operator&=(const this_type& x) + { + base_type::operator&=(x); + return *this; + } + + + template + inline typename bitset::this_type& + bitset::operator|=(const this_type& x) + { + base_type::operator|=(x); + return *this; + } + + + template + inline typename bitset::this_type& + bitset::operator^=(const this_type& x) + { + base_type::operator^=(x); + return *this; + } + + + template + inline typename bitset::this_type& + bitset::operator<<=(size_type n) + { + if(EASTL_LIKELY((intptr_t)n < (intptr_t)N)) + { + EA_DISABLE_VC_WARNING(6313) + base_type::operator<<=(n); + if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... (If we didn't have this check, then the code below would do the wrong thing when N == 32. + mWord[kWordCount - 1] &= ~(static_cast(~static_cast(0)) << (N & kBitsPerWordMask)); // This clears any high unused bits. We need to do this so that shift operations proceed correctly. + EA_RESTORE_VC_WARNING() + } + else + base_type::reset(); + return *this; + } + + + template + inline typename bitset::this_type& + bitset::operator>>=(size_type n) + { + if(EASTL_LIKELY(n < N)) + base_type::operator>>=(n); + else + base_type::reset(); + return *this; + } + + + template + inline typename bitset::this_type& + bitset::set() + { + base_type::set(); // This sets all bits. + if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... (If we didn't have this check, then the code below would do the wrong thing when N == 32. + mWord[kWordCount - 1] &= ~(static_cast(~static_cast(0)) << (N & kBitsPerWordMask)); // This clears any high unused bits. We need to do this so that shift operations proceed correctly. + return *this; + } + + + template + inline typename bitset::this_type& + bitset::set(size_type i, bool value) + { + if(i < N) + base_type::set(i, value); + else + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(!(i < N))) + EASTL_FAIL_MSG("bitset::set -- out of range"); + #endif + + #if EASTL_EXCEPTIONS_ENABLED + throw std::out_of_range("bitset::set"); + #endif + } + + return *this; + } + + + template + inline typename bitset::this_type& + bitset::reset() + { + base_type::reset(); + return *this; + } + + + template + inline typename bitset::this_type& + bitset::reset(size_type i) + { + if(EASTL_LIKELY(i < N)) + DoGetWord(i) &= ~(static_cast(1) << (i & kBitsPerWordMask)); + else + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(!(i < N))) + EASTL_FAIL_MSG("bitset::reset -- out of range"); + #endif + + #if EASTL_EXCEPTIONS_ENABLED + throw std::out_of_range("bitset::reset"); + #endif + } + + return *this; + } + + + template + inline typename bitset::this_type& + bitset::flip() + { + EA_DISABLE_VC_WARNING(6313) + base_type::flip(); + if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... (If we didn't have this check, then the code below would do the wrong thing when N == 32. + mWord[kWordCount - 1] &= ~(static_cast(~static_cast(0)) << (N & kBitsPerWordMask)); // This clears any high unused bits. We need to do this so that shift operations proceed correctly. + return *this; + EA_RESTORE_VC_WARNING() + } + + + template + inline typename bitset::this_type& + bitset::flip(size_type i) + { + if(EASTL_LIKELY(i < N)) + DoGetWord(i) ^= (static_cast(1) << (i & kBitsPerWordMask)); + else + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(!(i < N))) + EASTL_FAIL_MSG("bitset::flip -- out of range"); + #endif + + #if EASTL_EXCEPTIONS_ENABLED + throw std::out_of_range("bitset::flip"); + #endif + } + return *this; + } + + + template + inline typename bitset::this_type + bitset::operator~() const + { + return this_type(*this).flip(); + } + + + template + inline typename bitset::reference + bitset::operator[](size_type i) + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(!(i < N))) + EASTL_FAIL_MSG("bitset::operator[] -- out of range"); + #endif + + return reference(*this, i); + } + + + template + inline bool bitset::operator[](size_type i) const + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(!(i < N))) + EASTL_FAIL_MSG("bitset::operator[] -- out of range"); + #endif + + return (DoGetWord(i) & (static_cast(1) << (i & kBitsPerWordMask))) != 0; + } + + + template + inline const typename bitset::word_type* bitset::data() const + { + return base_type::mWord; + } + + + template + inline typename bitset::word_type* bitset::data() + { + return base_type::mWord; + } + + + template + inline void bitset::from_uint32(uint32_t value) + { + base_type::from_uint32(value); + + if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... (If we didn't have this check, then the code below would do the wrong thing when N == 32. + mWord[kWordCount - 1] &= ~(static_cast(~static_cast(0)) << (N & kBitsPerWordMask)); // This clears any high unused bits. We need to do this so that shift operations proceed correctly. + } + + + template + inline void bitset::from_uint64(uint64_t value) + { + base_type::from_uint64(value); + + if((N & kBitsPerWordMask) || (N == 0)) // If there are any high bits to clear... (If we didn't have this check, then the code below would do the wrong thing when N == 32. + mWord[kWordCount - 1] &= ~(static_cast(~static_cast(0)) << (N & kBitsPerWordMask)); // This clears any high unused bits. We need to do this so that shift operations proceed correctly. + } + + + // template + // inline unsigned long bitset::to_ulong() const + // { + // return base_type::to_ulong(); + // } + + + // template + // inline uint32_t bitset::to_uint32() const + // { + // return base_type::to_uint32(); + // } + + + // template + // inline uint64_t bitset::to_uint64() const + // { + // return base_type::to_uint64(); + // } + + + // template + // inline typename bitset::size_type + // bitset::count() const + // { + // return base_type::count(); + // } + + + template + inline typename bitset::size_type + bitset::size() const + { + return (size_type)N; + } + + + template + inline bool bitset::operator==(const this_type& x) const + { + return base_type::operator==(x); + } + + + template + inline bool bitset::operator!=(const this_type& x) const + { + return !base_type::operator==(x); + } + + + template + inline bool bitset::test(size_type i) const + { + if(EASTL_UNLIKELY(i < N)) + return (DoGetWord(i) & (static_cast(1) << (i & kBitsPerWordMask))) != 0; + + #if EASTL_ASSERT_ENABLED + EASTL_FAIL_MSG("bitset::test -- out of range"); + #endif + + #if EASTL_EXCEPTIONS_ENABLED + throw std::out_of_range("bitset::test"); + #else + return false; + #endif + } + + + // template + // inline bool bitset::any() const + // { + // return base_type::any(); + // } + + + template + inline bool bitset::all() const + { + return count() == size(); + } + + + template + inline bool bitset::none() const + { + return !base_type::any(); + } + + + template + inline typename bitset::this_type + bitset::operator<<(size_type n) const + { + return this_type(*this).operator<<=(n); + } + + + template + inline typename bitset::this_type + bitset::operator>>(size_type n) const + { + return this_type(*this).operator>>=(n); + } + + + template + inline typename bitset::size_type + bitset::find_first() const + { + const size_type i = base_type::DoFindFirst(); + + if(i < kSize) + return i; + // Else i could be the base type bit count, so we clamp it to our size. + + return kSize; + } + + + template + inline typename bitset::size_type + bitset::find_next(size_type last_find) const + { + const size_type i = base_type::DoFindNext(last_find); + + if(i < kSize) + return i; + // Else i could be the base type bit count, so we clamp it to our size. + + return kSize; + } + + + template + inline typename bitset::size_type + bitset::find_last() const + { + const size_type i = base_type::DoFindLast(); + + if(i < kSize) + return i; + // Else i could be the base type bit count, so we clamp it to our size. + + return kSize; + } + + + template + inline typename bitset::size_type + bitset::find_prev(size_type last_find) const + { + const size_type i = base_type::DoFindPrev(last_find); + + if(i < kSize) + return i; + // Else i could be the base type bit count, so we clamp it to our size. + + return kSize; + } + + + + /////////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////////// + + template + inline bitset operator&(const bitset& a, const bitset& b) + { + // We get betting inlining when we don't declare temporary variables. + return bitset(a).operator&=(b); + } + + + template + inline bitset operator|(const bitset& a, const bitset& b) + { + return bitset(a).operator|=(b); + } + + + template + inline bitset operator^(const bitset& a, const bitset& b) + { + return bitset(a).operator^=(b); + } + + +} // namespace eastl + + +EA_RESTORE_VC_WARNING(); + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/bitvector.h b/lib/EASTL/include/EASTL/bitvector.h new file mode 100644 index 000000000..ade678235 --- /dev/null +++ b/lib/EASTL/include/EASTL/bitvector.h @@ -0,0 +1,1474 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Implements a bit vector, which is essentially a vector of bool but which +// uses bits instead of bytes. It is thus similar to the original std::vector. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Note: This code is not yet complete: it isn't tested and doesn't yet +// support containers other than vector. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_BITVECTOR_H +#define EASTL_BITVECTOR_H + + +#include +#include +#include +#include + +EA_DISABLE_VC_WARNING(4480); // nonstandard extension used: specifying underlying type for enum + +#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_BITVECTOR_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_BITVECTOR_DEFAULT_NAME + #define EASTL_BITVECTOR_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " bitvector" // Unless the user overrides something, this is "EASTL bitvector". + #endif + + /// EASTL_BITVECTOR_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_BITVECTOR_DEFAULT_ALLOCATOR + #define EASTL_BITVECTOR_DEFAULT_ALLOCATOR allocator_type(EASTL_BITVECTOR_DEFAULT_NAME) + #endif + + + + /// BitvectorWordType + /// Defines the integral data type used by bitvector. + typedef EASTL_BITSET_WORD_TYPE_DEFAULT BitvectorWordType; + + + template + class bitvector_const_iterator; + + + template + class bitvector_reference + { + public: + typedef eastl_size_t size_type; + bitvector_reference(Element* ptr, eastl_size_t i); + + bitvector_reference& operator=(bool value); + bitvector_reference& operator=(const bitvector_reference& rhs); + + operator bool() const // Defined here because some compilers fail otherwise. + { return (*mpBitWord & (Element(1) << mnBitIndex)) != 0; } + + protected: + friend class bitvector_const_iterator; + + Element* mpBitWord; + size_type mnBitIndex; + + bitvector_reference() {} + void CopyFrom(const bitvector_reference& rhs); + }; + + + + template + class bitvector_const_iterator + { + public: + typedef EASTL_ITC_NS::random_access_iterator_tag iterator_category; + typedef bitvector_const_iterator this_type; + typedef bool value_type; + typedef bitvector_reference reference_type; + typedef ptrdiff_t difference_type; + typedef Element element_type; + typedef element_type* pointer; // This is wrong. It needs to be someting that acts as a pointer to a bit. + typedef element_type& reference; // This is not right. It needs to be someting that acts as a pointer to a bit. + typedef eastl_size_t size_type; + + protected: + reference_type mReference; + + enum + { + kBitCount = (8 * sizeof(Element)) + }; + + public: + bool operator*() const; + bool operator[](difference_type n) const; + + bitvector_const_iterator(); + bitvector_const_iterator(const element_type* p, eastl_size_t i); + bitvector_const_iterator(const reference_type& referenceType); + + bitvector_const_iterator& operator++(); + bitvector_const_iterator operator++(int); + bitvector_const_iterator& operator--(); + bitvector_const_iterator operator--(int); + + bitvector_const_iterator& operator+=(difference_type dist); + bitvector_const_iterator& operator-=(difference_type dist); + bitvector_const_iterator operator+ (difference_type dist) const; + bitvector_const_iterator operator- (difference_type dist) const; + + difference_type operator-(const this_type& rhs) const; + + bitvector_const_iterator& operator= (const this_type& rhs); + + bool operator==(const this_type& rhs) const; + bool operator!=(const this_type& rhs) const; + + bool operator< (const this_type& rhs) const; + bool operator<=(const this_type& rhs) const; + bool operator> (const this_type& rhs) const; + bool operator>=(const this_type& rhs) const; + + int validate(const element_type* pStart, const element_type* pEnd, eastl_size_t nExtraBits) const; + + protected: + template + friend class bitvector; + + reference_type& get_reference_type() { return mReference; } + }; + + + + template + class bitvector_iterator : public bitvector_const_iterator + { + public: + typedef EASTL_ITC_NS::random_access_iterator_tag iterator_category; + typedef bitvector_iterator this_type; + typedef bitvector_const_iterator base_type; + typedef bool value_type; + typedef bitvector_reference reference_type; + typedef ptrdiff_t difference_type; + typedef Element element_type; + typedef element_type* pointer; // This is wrong. It needs to be someting that acts as a pointer to a bit. + typedef element_type& reference; // This is not right. It needs to be someting that acts as a pointer to a bit. + + public: + reference_type operator*() const; + reference_type operator[](difference_type n) const; + + bitvector_iterator(); + bitvector_iterator(element_type* p, eastl_size_t i); + bitvector_iterator(reference_type& referenceType); + + bitvector_iterator& operator++() { base_type::operator++(); return *this; } + bitvector_iterator& operator--() { base_type::operator--(); return *this; } + bitvector_iterator operator++(int); + bitvector_iterator operator--(int); + + bitvector_iterator& operator+=(difference_type dist) { base_type::operator+=(dist); return *this; } + bitvector_iterator& operator-=(difference_type dist) { base_type::operator-=(dist); return *this; } + bitvector_iterator operator+ (difference_type dist) const; + bitvector_iterator operator- (difference_type dist) const; + + // We need this here because we are overloading operator-, so for some reason the + // other overload of the function can't be found unless it's explicitly specified. + difference_type operator-(const base_type& rhs) const { return base_type::operator-(rhs); } + }; + + + + /// bitvector + /// + /// Implements an array of bits treated as boolean values. + /// bitvector is similar to vector but uses bits instead of bytes and + /// allows the user to use other containers such as deque instead of vector. + /// bitvector is different from bitset in that bitset is less flexible but + /// uses less memory and has higher performance. + /// + /// To consider: Rename the Element template parameter to WordType, for + /// consistency with bitset. + /// + template > + class bitvector + { + public: + typedef bitvector this_type; + typedef bool value_type; + typedef bitvector_reference reference; + typedef bool const_reference; + typedef bitvector_iterator iterator; + typedef bitvector_const_iterator const_iterator; + typedef eastl::reverse_iterator reverse_iterator; + typedef eastl::reverse_iterator const_reverse_iterator; + typedef Allocator allocator_type; + typedef Element element_type; + typedef Container container_type; + typedef eastl_size_t size_type; + typedef ptrdiff_t difference_type; + + #if defined(_MSC_VER) && (_MSC_VER >= 1400) && (_MSC_VER <= 1600) && !EASTL_STD_CPP_ONLY // _MSC_VER of 1400 means VS2005, 1600 means VS2010. VS2012 generates errors with usage of enum:size_type. + enum : size_type { // Use Microsoft enum language extension, allowing for smaller debug symbols than using a static const. Users have been affected by this. + npos = container_type::npos, + kMaxSize = container_type::kMaxSize + }; + #else + static const size_type npos = container_type::npos; /// 'npos' means non-valid position or simply non-position. + static const size_type kMaxSize = container_type::kMaxSize; /// -1 is reserved for 'npos'. It also happens to be slightly beneficial that kMaxSize is a value less than -1, as it helps us deal with potential integer wraparound issues. + #endif + + enum + { + kBitCount = 8 * sizeof(Element) + }; + + protected: + container_type mContainer; + size_type mFreeBitCount; // Unused bits in the last word of mContainer. + + public: + bitvector(); + explicit bitvector(const allocator_type& allocator); + explicit bitvector(size_type n, const allocator_type& allocator = EASTL_BITVECTOR_DEFAULT_ALLOCATOR); + bitvector(size_type n, value_type value, const allocator_type& allocator = EASTL_BITVECTOR_DEFAULT_ALLOCATOR); + bitvector(const bitvector& copy); + + template + bitvector(InputIterator first, InputIterator last); + + bitvector& operator=(const bitvector& x); + void swap(this_type& x); + + template + void assign(InputIterator first, InputIterator last); + + 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; + + bool empty() const EA_NOEXCEPT; + size_type size() const EA_NOEXCEPT; + size_type capacity() const EA_NOEXCEPT; + + void resize(size_type n, value_type value); + void resize(size_type n); + void reserve(size_type n); + void set_capacity(size_type n = npos); // Revises the capacity to the user-specified value. Resizes the container to match the capacity if the requested capacity n is less than the current size. If n == npos then the capacity is reallocated (if necessary) such that capacity == size. + + void push_back(); + void push_back(value_type value); + void pop_back(); + + reference front(); + const_reference front() const; + reference back(); + const_reference back() const; + + bool test(size_type n, bool defaultValue) const; // Returns true if the bit index is < size() and set. Returns defaultValue if the bit is >= size(). + void set(size_type n, bool value); // Resizes the container to accomodate n if necessary. + + reference at(size_type n); // throws an out_of_range exception if n is invalid. + const_reference at(size_type n) const; + + reference operator[](size_type n); // behavior is undefined if n is invalid. + const_reference operator[](size_type n) const; + + /* + Work in progress: + template iterator find_first(); // Finds the lowest "on" bit. + template iterator find_next(const_iterator it); // Finds the next lowest "on" bit after it. + template iterator find_last(); // Finds the index of the last "on" bit, returns size if none are set. + template iterator find_prev(const_iterator it); // Finds the index of the last "on" bit before last_find, returns size if none are set. + + template const_iterator find_first() const; // Finds the lowest "on" bit. + template const_iterator find_next(const_iterator it) const; // Finds the next lowest "on" bit after it. + template const_iterator find_last() const; // Finds the index of the last "on" bit, returns size if none are set. + template const_iterator find_prev(const_iterator it) const; // Finds the index of the last "on" bit before last_find, returns size if none are set. + */ + + element_type* data() EA_NOEXCEPT; + const element_type* data() const EA_NOEXCEPT; + + iterator insert(const_iterator position, value_type value); + void insert(const_iterator position, size_type n, value_type value); + + // template Not yet implemented. See below for disabled definition. + // void insert(const_iterator position, InputIterator first, InputIterator last); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + + reverse_iterator erase(const_reverse_iterator position); + reverse_iterator erase(const_reverse_iterator first, const_reverse_iterator last); + + void clear(); + void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. + + container_type& get_container(); + const container_type& get_container() const; + + bool validate() const; + int validate_iterator(const_iterator i) const; + }; + + + + + /////////////////////////////////////////////////////////////////////// + // bitvector_reference + /////////////////////////////////////////////////////////////////////// + + template + bitvector_reference::bitvector_reference(Element* p, eastl_size_t i) + : mpBitWord(p), + mnBitIndex(i) + { + } + + + template + bitvector_reference& + bitvector_reference::operator=(bool value) + { + const Element mask = (Element)(Element(1) << mnBitIndex); + + if(value) + *mpBitWord |= mask; + else + *mpBitWord &= ~mask; + + return *this; + } + + + template + bitvector_reference& + bitvector_reference::operator=(const bitvector_reference& rhs) + { + return (*this = (bool)rhs); + } + + + template + void bitvector_reference::CopyFrom(const bitvector_reference& rhs) + { + mpBitWord = rhs.mpBitWord; + mnBitIndex = rhs.mnBitIndex; + } + + + + + /////////////////////////////////////////////////////////////////////// + // bitvector_const_iterator + /////////////////////////////////////////////////////////////////////// + + template + bitvector_const_iterator::bitvector_const_iterator() + : mReference(0, 0) + { + } + + + template + bitvector_const_iterator::bitvector_const_iterator(const Element* p, eastl_size_t i) + : mReference(const_cast(p), i) // const_cast is safe here because we never let mReference leak and we don't modify it. + { + } + + + template + bitvector_const_iterator::bitvector_const_iterator(const reference_type& reference) + : mReference(reference) + { + } + + + template + bitvector_const_iterator& + bitvector_const_iterator::operator++() + { + ++mReference.mnBitIndex; + + if(mReference.mnBitIndex == kBitCount) + { + ++mReference.mpBitWord; + mReference.mnBitIndex = 0; + } + + return *this; + } + + + template + bitvector_const_iterator& + bitvector_const_iterator::operator--() + { + if(mReference.mnBitIndex == 0) + { + --mReference.mpBitWord; + mReference.mnBitIndex = kBitCount; + } + + --mReference.mnBitIndex; + return *this; + } + + + template + bitvector_const_iterator + bitvector_const_iterator::operator++(int) + { + bitvector_const_iterator copy(*this); + ++*this; + return copy; + } + + + template + bitvector_const_iterator + bitvector_const_iterator::operator--(int) + { + bitvector_const_iterator copy(*this); + --*this; + return copy; + } + + + template + bitvector_const_iterator& + bitvector_const_iterator::operator+=(difference_type n) + { + n += mReference.mnBitIndex; + + if(n >= difference_type(0)) + { + mReference.mpBitWord += n / kBitCount; + mReference.mnBitIndex = (size_type)(n % kBitCount); + } + else + { + // backwards is tricky + // figure out how many full words backwards we need to move + // n = [-1..-32] => 1 + // n = [-33..-64] => 2 + const size_type backwards = (size_type)(-n + kBitCount - 1); + mReference.mpBitWord -= backwards / kBitCount; + + // -1 => 31; backwards = 32; 31 - (backwards % 32) = 31 + // -2 => 30; backwards = 33; 31 - (backwards % 32) = 30 + // -3 => 29; backwards = 34 + // .. + // -32 => 0; backwards = 63; 31 - (backwards % 32) = 0 + // -33 => 31; backwards = 64; 31 - (backwards % 32) = 31 + mReference.mnBitIndex = (kBitCount - 1) - (backwards % kBitCount); + } + + return *this; + } + + + template + bitvector_const_iterator& + bitvector_const_iterator::operator-=(difference_type n) + { + return (*this += -n); + } + + + template + bitvector_const_iterator + bitvector_const_iterator::operator+(difference_type n) const + { + bitvector_const_iterator copy(*this); + copy += n; + return copy; + } + + + template + bitvector_const_iterator + bitvector_const_iterator::operator-(difference_type n) const + { + bitvector_const_iterator copy(*this); + copy -= n; + return copy; + } + + + template + typename bitvector_const_iterator::difference_type + bitvector_const_iterator::operator-(const this_type& rhs) const + { + return ((mReference.mpBitWord - rhs.mReference.mpBitWord) * kBitCount) + mReference.mnBitIndex - rhs.mReference.mnBitIndex; + } + + + template + bool bitvector_const_iterator::operator==(const this_type& rhs) const + { + return (mReference.mpBitWord == rhs.mReference.mpBitWord) && (mReference.mnBitIndex == rhs.mReference.mnBitIndex); + } + + + template + bool bitvector_const_iterator::operator!=(const this_type& rhs) const + { + return !(*this == rhs); + } + + + template + bool bitvector_const_iterator::operator<(const this_type& rhs) const + { + return (mReference.mpBitWord < rhs.mReference.mpBitWord) || + ((mReference.mpBitWord == rhs.mReference.mpBitWord) && (mReference.mnBitIndex < rhs.mReference.mnBitIndex)); + } + + + template + bool bitvector_const_iterator::operator<=(const this_type& rhs) const + { + return (mReference.mpBitWord < rhs.mReference.mpBitWord) || + ((mReference.mpBitWord == rhs.mReference.mpBitWord) && (mReference.mnBitIndex <= rhs.mReference.mnBitIndex)); + } + + + template + bool bitvector_const_iterator::operator>(const this_type& rhs) const + { + return !(*this <= rhs); + } + + + template + bool bitvector_const_iterator::operator>=(const this_type& rhs) const + { + return !(*this < rhs); + } + + + template + bool bitvector_const_iterator::operator*() const + { + return mReference; + } + + + template + bool bitvector_const_iterator::operator[](difference_type n) const + { + return *(*this + n); + } + + + template + bitvector_const_iterator& bitvector_const_iterator::operator= (const this_type& rhs) + { + mReference.CopyFrom(rhs.mReference); + return *this; + } + + + template + int bitvector_const_iterator::validate(const Element* pStart, const Element* pEnd, eastl_size_t nExtraBits) const + { + const Element* const pCurrent = mReference.mpBitWord; + + if(pCurrent >= pStart) + { + if(nExtraBits == 0) + { + if(pCurrent == pEnd && mReference) + return eastl::isf_valid | eastl::isf_current; + else if(pCurrent < pEnd) + return eastl::isf_valid | eastl::isf_current | eastl::isf_can_dereference; + } + else if(pCurrent == (pEnd - 1)) + { + const size_type bit = mReference.mnBitIndex; + const size_type lastbit = kBitCount - nExtraBits; + + if(bit == lastbit) + return eastl::isf_valid | eastl::isf_current; + else if(bit < lastbit) + return eastl::isf_valid | eastl::isf_current | eastl::isf_can_dereference; + } + else if(pCurrent < pEnd) + { + return eastl::isf_valid | eastl::isf_current | eastl::isf_can_dereference; + } + } + + return eastl::isf_none; + } + + + + /////////////////////////////////////////////////////////////////////// + // bitvector_iterator + /////////////////////////////////////////////////////////////////////// + + template + bitvector_iterator::bitvector_iterator() + : base_type() + { + } + + template + bitvector_iterator::bitvector_iterator(Element* p, eastl_size_t i) + : base_type(p, i) + { + } + + + template + bitvector_iterator::bitvector_iterator(reference_type& reference) + : base_type(reference) + { + } + + + template + typename bitvector_iterator::reference_type + bitvector_iterator::operator*() const + { + return base_type::mReference; + } + + + template + typename bitvector_iterator::reference_type + bitvector_iterator::operator[](difference_type n) const + { + return *(*this + n); + } + + + template + void MoveBits(bitvector_iterator start, + bitvector_iterator end, + bitvector_iterator dest) + { + // Slow implemenation; could optimize by moving a word at a time. + if(dest <= start) + { + while(start != end) + { + *dest = *start; + ++dest; + ++start; + } + } + else + { + // Need to move backwards + dest += (end - start); + + while(start != end) + { + --dest; + --end; + *dest = *end; + } + } + } + + + template + bitvector_iterator + bitvector_iterator::operator++(int) + { + bitvector_iterator copy(*this); + ++*this; + return copy; + } + + + template + bitvector_iterator + bitvector_iterator::operator--(int) + { + bitvector_iterator copy(*this); + --*this; + return copy; + } + + + template + bitvector_iterator + bitvector_iterator::operator+(difference_type n) const + { + bitvector_iterator copy(*this); + copy += n; + return copy; + } + + + template + bitvector_iterator + bitvector_iterator::operator-(difference_type n) const + { + bitvector_iterator copy(*this); + copy -= n; + return copy; + } + + + + + /////////////////////////////////////////////////////////////////////// + // bitvector + /////////////////////////////////////////////////////////////////////// + + template + template + void bitvector::assign(InputIterator first, InputIterator last) + { + // To consider: We can maybe specialize this on bitvector_iterator to do a fast bitwise copy. + // We can also specialize for random access iterators to figure out the size & reserve first. + + clear(); + + while(first != last) + { + push_back(*first); + ++first; + } + } + + + template + typename bitvector::iterator + bitvector::begin() EA_NOEXCEPT + { + return iterator(mContainer.begin(), 0); + } + + + template + typename bitvector::const_iterator + bitvector::begin() const EA_NOEXCEPT + { + return const_iterator(mContainer.begin(), 0); + } + + + template + typename bitvector::const_iterator + bitvector::cbegin() const EA_NOEXCEPT + { + return const_iterator(mContainer.begin(), 0); + } + + + template + typename bitvector::iterator + bitvector::end() EA_NOEXCEPT + { + return iterator(mContainer.end(), 0) - mFreeBitCount; + } + + + template + typename bitvector::const_iterator + bitvector::end() const EA_NOEXCEPT + { + return const_iterator(mContainer.end(), 0) - mFreeBitCount; + } + + + template + typename bitvector::const_iterator + bitvector::cend() const EA_NOEXCEPT + { + return const_iterator(mContainer.end(), 0) - mFreeBitCount; + } + + + template + bool bitvector::empty() const EA_NOEXCEPT + { + return mContainer.empty(); + } + + + template + typename bitvector::size_type + bitvector::size() const EA_NOEXCEPT + { + return (mContainer.size() * kBitCount) - mFreeBitCount; + } + + + template + typename bitvector::size_type + bitvector::capacity() const EA_NOEXCEPT + { + return mContainer.capacity() * kBitCount; + } + + + template + void bitvector::set_capacity(size_type n) + { + if(n == npos) + mContainer.set_capacity(npos); + else + mContainer.set_capacity((n + kBitCount - 1) / kBitCount); + } + + + template + typename bitvector::reverse_iterator + bitvector::rbegin() EA_NOEXCEPT + { + return reverse_iterator(end()); + } + + + template + typename bitvector::const_reverse_iterator + bitvector::rbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(end()); + } + + + template + typename bitvector::const_reverse_iterator + bitvector::crbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(end()); + } + + + template + typename bitvector::reverse_iterator + bitvector::rend() EA_NOEXCEPT + { + return reverse_iterator(begin()); + } + + + template + typename bitvector::const_reverse_iterator + bitvector::rend() const EA_NOEXCEPT + { + return const_reverse_iterator(begin()); + } + + + template + typename bitvector::const_reverse_iterator + bitvector::crend() const EA_NOEXCEPT + { + return const_reverse_iterator(begin()); + } + + + template + typename bitvector::reference + bitvector::front() + { + EASTL_ASSERT(!empty()); + return reference(&mContainer[0], 0); + } + + + template + typename bitvector::const_reference + bitvector::front() const + { + EASTL_ASSERT(!empty()); + + // To consider: make a better solution to this than const_cast. + return reference(const_cast(&mContainer[0]), 0); + } + + + template + typename bitvector::reference + bitvector::back() + { + EASTL_ASSERT(!empty()); + return *(--end()); + } + + + template + typename bitvector::const_reference + bitvector::back() const + { + EASTL_ASSERT(!empty()); + return *(--end()); + } + + + template + void bitvector::push_back() + { + if(!mFreeBitCount) + { + mContainer.push_back(); + mFreeBitCount = kBitCount; + } + + --mFreeBitCount; + } + + + template + void bitvector::push_back(value_type value) + { + push_back(); + *--end() = value; + } + + + template + void bitvector::pop_back() + { + EASTL_ASSERT(!empty()); + + if(++mFreeBitCount == kBitCount) + { + mContainer.pop_back(); + mFreeBitCount = 0; + } + } + + + template + void bitvector::reserve(size_type n) + { + const size_type wordCount = (n + kBitCount - 1) / kBitCount; + mContainer.reserve(wordCount); + } + + + template + void bitvector::resize(size_type n) + { + const size_type wordCount = (n + kBitCount - 1) / kBitCount; + const size_type extra = (wordCount * kBitCount) - n; + + mContainer.resize(wordCount); + mFreeBitCount = extra; + } + + + template + void bitvector::resize(size_type n, value_type value) + { + const size_type s = size(); + if(n < s) + resize(n); + + // Fill up to the end of a word + size_type newbits = n - s; + + while(mFreeBitCount && newbits) + { + push_back(value); + --newbits; + } + + // Fill the rest a word at a time + if(newbits) + { + element_type element(0); + if(value) + element = ~element; + + const size_type words = (n + kBitCount - 1) / kBitCount; + const size_type extra = words * kBitCount - n; + mContainer.resize(words, element); + mFreeBitCount = extra; + } + } + + + template + bool bitvector::test(size_type n, bool defaultValue) const + { + if(n < size()) + return *(begin() + (difference_type)n); + + return defaultValue; + } + + + template + void bitvector::set(size_type n, bool value) + { + if(EASTL_UNLIKELY(n >= size())) + resize(n + 1); + + *(begin() + (difference_type)n) = value; + } + + + template + typename bitvector::reference + bitvector::at(size_type n) + { + // The difference between at and operator[] is that at signals + // if the requested position is out of range by throwing an + // out_of_range exception. + + #if EASTL_EXCEPTIONS_ENABLED + if(EASTL_UNLIKELY(n >= size())) + throw std::out_of_range("bitvector::at -- out of range"); + #elif EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(n >= size())) + EASTL_FAIL_MSG("bitvector::at -- out of range"); + #endif + + return *(begin() + (difference_type)n); + } + + + template + typename bitvector::const_reference + bitvector::at(size_type n) const + { + #if EASTL_EXCEPTIONS_ENABLED + if(EASTL_UNLIKELY(n >= size())) + throw std::out_of_range("bitvector::at -- out of range"); + #elif EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(n >= size())) + EASTL_FAIL_MSG("bitvector::at -- out of range"); + #endif + + return *(begin() + (difference_type)n); + } + + + template + typename bitvector::reference + bitvector::operator[](size_type n) + { + return *(begin() + (difference_type)n); + } + + + template + typename bitvector::const_reference + bitvector::operator[](size_type n) const + { + return *(begin() + (difference_type)n); + } + + +/* + template + template + typename bitvector::iterator + bitvector::find_first() + { + return begin(); + } + + template iterator find_next(const_iterator it); + template iterator find_last(); + template iterator find_prev(const_iterator it); + + template const_iterator find_first() const; + template const_iterator find_next(const_iterator it) const; + template const_iterator find_last() const; + template const_iterator find_prev(const_iterator it) const; +*/ + + + + + template + inline typename bitvector::container_type& + bitvector::get_container() + { + return mContainer; + } + + + template + inline const typename bitvector::container_type& + bitvector::get_container() const + { + return mContainer; + } + + + template + bool bitvector::validate() const + { + if(!mContainer.validate()) + return false; + + if((unsigned)mFreeBitCount >= kBitCount) + return false; + + return true; + } + + + template + int bitvector::validate_iterator(const_iterator i) const + { + return i.validate(mContainer.begin(), mContainer.end(), mFreeBitCount); + } + + + template + typename bitvector::element_type* + bitvector::data() EA_NOEXCEPT + { + return mContainer.data(); + } + + + template + const typename bitvector::element_type* + bitvector::data() const EA_NOEXCEPT + { + return mContainer.data(); + } + + + template + typename bitvector::iterator + bitvector::insert(const_iterator position, value_type value) + { + iterator iPosition(position.get_reference_type()); // This is just a non-const version of position. + + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(validate_iterator(iPosition) & eastl::isf_valid) == 0) + EASTL_FAIL_MSG("bitvector::insert -- invalid iterator"); + #endif + + // Save because we might reallocate + const typename iterator::difference_type n = iPosition - begin(); + push_back(); + iPosition = begin() + n; + + MoveBits(iPosition, --end(), ++iterator(iPosition)); + *iPosition = value; + + return iPosition; + } + + + template + void bitvector::insert(const_iterator position, size_type n, value_type value) + { + iterator iPosition(position.get_reference_type()); // This is just a non-const version of position. + + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(validate_iterator(iPosition) & eastl::isf_valid) == 0) + EASTL_FAIL_MSG("bitvector::insert -- invalid iterator"); + #endif + + // Save because we might reallocate. + const typename iterator::difference_type p = iPosition - begin(); + resize(size() + n); + iPosition = begin() + p; + + iterator insert_end = iPosition + n; + MoveBits(iPosition, end() - n, insert_end); + + // To do: Optimize this to word-at-a-time for large inserts + while(iPosition != insert_end) + { + *iPosition = value; + ++iPosition; + } + } + + + /* + The following is a placeholder for a future implementation. It turns out that a correct implementation of + insert(pos, first, last) is a non-trivial exercise that would take a few hours to implement and test. + The reasons why involve primarily the problem of handling the case where insertion source comes from + within the container itself, and the case that first and last (note they are templated) might not refer + to iterators might refer to a value/count pair. The C++ Standard requires you to handle this case and + I (Paul Pedriana) believe that it applies even for a bitvector, given that bool is an integral type. + So you have to set up a compile-time type traits function chooser. See vector, for example. + + template + template + void bitvector::insert(const_iterator position, InputIterator first, InputIterator last) + { + iterator iPosition(position.get_reference_type()); // This is just a non-const version of position. + + // This implementation is probably broken due to not handling insertion into self. + // To do: Make a more efficient version of this. + difference_type distance = (iPosition - begin()); + + while(first != last) + { + insert(iPosition, *first); + iPosition = begin() + ++distance; + ++first; + } + } + */ + + + template + typename bitvector::iterator + bitvector::erase(const_iterator position) + { + iterator iPosition(position.get_reference_type()); // This is just a non-const version of position. + + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(validate_iterator(iPosition) & eastl::isf_can_dereference) == 0) + EASTL_FAIL_MSG("bitvector::erase -- invalid iterator"); + #endif + + MoveBits(++iterator(iPosition), end(), iPosition); + resize(size() - 1); + + // Verify that no reallocation occurred. + EASTL_ASSERT(validate_iterator(iPosition) & eastl::isf_valid); + return iPosition; + } + + + template + typename bitvector::iterator + bitvector::erase(const_iterator first, const_iterator last) + { + iterator iFirst(first.get_reference_type()); // This is just a non-const version of first. + iterator iLast(last.get_reference_type()); // This is just a non-const version of last. + + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(validate_iterator(iLast) & eastl::isf_valid) == 0) + EASTL_FAIL_MSG("bitvector::erase -- invalid iterator"); + #endif + + if(!(iFirst == iLast)) + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(validate_iterator(iFirst) & eastl::isf_can_dereference) == 0) + EASTL_FAIL_MSG("bitvector::erase -- invalid iterator"); + #endif + + const size_type eraseCount = (size_type)(iLast - iFirst); + MoveBits(iLast, end(), iFirst); + resize(size() - eraseCount); + + // Verify that no reallocation occurred. + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(validate_iterator(iFirst) & eastl::isf_valid) == 0) + EASTL_FAIL_MSG("bitvector::erase -- invalid iterator"); + #endif + } + + return iFirst; + } + + + template + typename bitvector::reverse_iterator + bitvector::erase(const_reverse_iterator position) + { + return reverse_iterator(erase((++position).base())); + } + + + template + typename bitvector::reverse_iterator + bitvector::erase(const_reverse_iterator first, const_reverse_iterator last) + { + // Version which erases in order from first to last. + // difference_type i(first.base() - last.base()); + // while(i--) + // first = erase(first); + // return first; + + // Version which erases in order from last to first, but is slightly more efficient: + return reverse_iterator(erase(last.base(), first.base())); + } + + + template + void bitvector::swap(this_type& rhs) + { + mContainer.swap(rhs.mContainer); + eastl::swap(mFreeBitCount, rhs.mFreeBitCount); + } + + + template + void bitvector::reset_lose_memory() + { + mContainer.reset_lose_memory(); // intentional memory leak. + mFreeBitCount = 0; + } + + + template + void bitvector::clear() + { + mContainer.clear(); + mFreeBitCount = 0; + } + + + template + bitvector& + bitvector::operator=(const bitvector& rhs) + { + // The following is OK if (&rhs == this) + mContainer = rhs.mContainer; + mFreeBitCount = rhs.mFreeBitCount; + + return *this; + } + + + template + bitvector::bitvector() + : mContainer(), + mFreeBitCount(0) + { + } + + + template + bitvector::bitvector(const allocator_type& allocator) + : mContainer(allocator), + mFreeBitCount(0) + { + } + + + template + bitvector::bitvector(size_type n, const allocator_type& allocator) + : mContainer((n + kBitCount - 1) / kBitCount, allocator) + { + mFreeBitCount = kBitCount - (n % kBitCount); + + if(mFreeBitCount == kBitCount) + mFreeBitCount = 0; + } + + + template + bitvector::bitvector(size_type n, value_type value, const allocator_type& allocator) + : mContainer((n + kBitCount - 1) / kBitCount, value ? ~element_type(0) : element_type(0), allocator) + { + mFreeBitCount = kBitCount - (n % kBitCount); + + if(mFreeBitCount == kBitCount) + mFreeBitCount = 0; + } + + + template + bitvector::bitvector(const bitvector& copy) + : mContainer(copy.mContainer), + mFreeBitCount(copy.mFreeBitCount) + { + } + + + template + template + bitvector::bitvector(InputIterator first, InputIterator last) + : mContainer(), + mFreeBitCount(0) + { + assign(first, last); + } + + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const bitvector& a, + const bitvector& b) + { + // To do: Replace this with a smart compare implementation. This is much slower than it needs to be. + return ((a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin())); + } + + + template + inline bool operator!=(const bitvector& a, + const bitvector& b) + { + return !operator==(a, b); + } + + + template + inline bool operator<(const bitvector& a, + const bitvector& b) + { + // To do: Replace this with a smart compare implementation. This is much slower than it needs to be. + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + + template + inline bool operator>(const bitvector& a, + const bitvector& b) + { + return b < a; + } + + + template + inline bool operator<=(const bitvector& a, + const bitvector& b) + { + return !(b < a); + } + + + template + inline bool operator>=(const bitvector& a, + const bitvector& b) + { + return !(a < b); + } + + template + inline void swap(bitvector& a, + bitvector& b) + { + a.swap(b); + } + + +} // namespace eastl + + +EA_RESTORE_VC_WARNING(); + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/bonus/adaptors.h b/lib/EASTL/include/EASTL/bonus/adaptors.h new file mode 100644 index 000000000..423cacdd1 --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/adaptors.h @@ -0,0 +1,88 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ADAPTORS_H +#define EASTL_ADAPTORS_H + + +#include +#include +#include +#include + +#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 + struct reverse_wrapper + { + template + reverse_wrapper(C&& c) + : mContainer(eastl::forward(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, "Reference collapsed deduced type must be the same as the deduced Container type!"); + } + + Container mContainer; + }; + + template + auto begin(const reverse_wrapper& w) -> decltype(eastl::rbegin(w.mContainer)) + { + return eastl::rbegin(w.mContainer); + } + + template + auto end(const reverse_wrapper& w) -> decltype(eastl::rend(w.mContainer)) + { + return eastl::rend(w.mContainer); + } + + template + reverse_wrapper reverse(Container&& c) + { + return reverse_wrapper(eastl::forward(c)); + } + +} // namespace eastl + +#if defined(_MSC_VER) && (_MSC_VER >= 1900) // VS2015+ + EA_RESTORE_VC_WARNING() +#endif +EA_RESTORE_VC_WARNING() + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/bonus/call_traits.h b/lib/EASTL/include/EASTL/bonus/call_traits.h new file mode 100644 index 000000000..0995d051e --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/call_traits.h @@ -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 +#include + +#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 + struct ct_imp2 { typedef const T& param_type; }; + + template + struct ct_imp2 { typedef const T param_type; }; + + template + struct ct_imp { typedef const T& param_type; }; + + template + struct ct_imp { typedef typename ct_imp2::param_type param_type; }; + + template + struct ct_imp { typedef T const param_type; }; + + + + template + struct call_traits + { + public: + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; + typedef typename ct_imp::value, is_arithmetic::value>::param_type param_type; + }; + + + template + struct call_traits + { + typedef T& value_type; + typedef T& reference; + typedef const T& const_reference; + typedef T& param_type; + }; + + + template + struct call_traits + { + 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 + struct call_traits + { + 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 + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/bonus/compressed_pair.h b/lib/EASTL/include/EASTL/bonus/compressed_pair.h new file mode 100644 index 000000000..379642bae --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/compressed_pair.h @@ -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 +// class compressed_pair +// { +// public: +// typedef T1 first_type; +// typedef T2 second_type; +// typedef typename call_traits::param_type first_param_type; +// typedef typename call_traits::param_type second_param_type; +// typedef typename call_traits::reference first_reference; +// typedef typename call_traits::reference second_reference; +// typedef typename call_traits::const_reference first_const_reference; +// typedef typename call_traits::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 +#include +#include + +#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': assignment operator was implicitly defined as deleted because a base class assignment operator is inaccessible or deleted +#endif + +namespace eastl +{ + + template + class compressed_pair; + + + template + struct compressed_pair_switch; + + template + struct compressed_pair_switch{ static const int value = 0; }; + + template + struct compressed_pair_switch { static const int value = 1; }; + + template + struct compressed_pair_switch { static const int value = 2; }; + + template + struct compressed_pair_switch { static const int value = 3; }; + + template + struct compressed_pair_switch { static const int value = 4; }; + + template + struct compressed_pair_switch { static const int value = 5; }; + + template + class compressed_pair_imp; + + + + template + inline void cp_swap(T& t1, T& t2) + { + T tTemp = t1; + t1 = t2; + t2 = tTemp; + } + + + // Derive from neither + template + class compressed_pair_imp + { + public: + typedef T1 first_type; + typedef T2 second_type; + typedef typename call_traits::param_type first_param_type; + typedef typename call_traits::param_type second_param_type; + typedef typename call_traits::reference first_reference; + typedef typename call_traits::reference second_reference; + typedef typename call_traits::const_reference first_const_reference; + typedef typename call_traits::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& y) + { + cp_swap(mFirst, y.first()); + cp_swap(mSecond, y.second()); + } + + private: + first_type mFirst; + second_type mSecond; + }; + + + // Derive from T1 + template + class compressed_pair_imp : private T1 + { + public: + typedef T1 first_type; + typedef T2 second_type; + typedef typename call_traits::param_type first_param_type; + typedef typename call_traits::param_type second_param_type; + typedef typename call_traits::reference first_reference; + typedef typename call_traits::reference second_reference; + typedef typename call_traits::const_reference first_const_reference; + typedef typename call_traits::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& y) + { + // No need to swap empty base class + cp_swap(mSecond, y.second()); + } + + private: + second_type mSecond; + }; + + + + // Derive from T2 + template + class compressed_pair_imp : private T2 + { + public: + typedef T1 first_type; + typedef T2 second_type; + typedef typename call_traits::param_type first_param_type; + typedef typename call_traits::param_type second_param_type; + typedef typename call_traits::reference first_reference; + typedef typename call_traits::reference second_reference; + typedef typename call_traits::const_reference first_const_reference; + typedef typename call_traits::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& y) + { + // No need to swap empty base class + cp_swap(mFirst, y.first()); + } + + private: + first_type mFirst; + }; + + + + // Derive from T1 and T2 + template + class compressed_pair_imp : private T1, private T2 + { + public: + typedef T1 first_type; + typedef T2 second_type; + typedef typename call_traits::param_type first_param_type; + typedef typename call_traits::param_type second_param_type; + typedef typename call_traits::reference first_reference; + typedef typename call_traits::reference second_reference; + typedef typename call_traits::const_reference first_const_reference; + typedef typename call_traits::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 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 + class compressed_pair_imp : private T1 + { + public: + typedef T1 first_type; + typedef T2 second_type; + typedef typename call_traits::param_type first_param_type; + typedef typename call_traits::param_type second_param_type; + typedef typename call_traits::reference first_reference; + typedef typename call_traits::reference second_reference; + typedef typename call_traits::const_reference first_const_reference; + typedef typename call_traits::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 and are not empty + template + class compressed_pair_imp + { + public: + typedef T1 first_type; + typedef T2 second_type; + typedef typename call_traits::param_type first_param_type; + typedef typename call_traits::param_type second_param_type; + typedef typename call_traits::reference first_reference; + typedef typename call_traits::reference second_reference; + typedef typename call_traits::const_reference first_const_reference; + typedef typename call_traits::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& y) + { + cp_swap(mFirst, y.first()); + cp_swap(mSecond, y.second()); + } + + private: + first_type mFirst; + second_type mSecond; + }; + + + + template + class compressed_pair + : private compressed_pair_imp::type, typename remove_cv::type>::value, + is_empty::value, + is_empty::value>::value> + { + private: + typedef compressed_pair_imp::type, typename remove_cv::type>::value, + is_empty::value, + is_empty::value>::value> base; + public: + typedef T1 first_type; + typedef T2 second_type; + typedef typename call_traits::param_type first_param_type; + typedef typename call_traits::param_type second_param_type; + typedef typename call_traits::reference first_reference; + typedef typename call_traits::reference second_reference; + typedef typename call_traits::const_reference first_const_reference; + typedef typename call_traits::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 + class compressed_pair + : private compressed_pair_imp::type, typename remove_cv::type>::value, + is_empty::value, + is_empty::value>::value> + { + private: + typedef compressed_pair_imp::type, typename remove_cv::type>::value, + is_empty::value, + is_empty::value>::value> base; + public: + typedef T first_type; + typedef T second_type; + typedef typename call_traits::param_type first_param_type; + typedef typename call_traits::param_type second_param_type; + typedef typename call_traits::reference first_reference; + typedef typename call_traits::reference second_reference; + typedef typename call_traits::const_reference first_const_reference; + typedef typename call_traits::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& y) { base::swap(y); } + }; + + + template + inline void swap(compressed_pair& x, compressed_pair& 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 + + + diff --git a/lib/EASTL/include/EASTL/bonus/fixed_ring_buffer.h b/lib/EASTL/include/EASTL/bonus/fixed_ring_buffer.h new file mode 100644 index 000000000..2bb54e470 --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/fixed_ring_buffer.h @@ -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 +#include +#include + +#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 rb = {0, 1, 2, 3, 4, 5, 6, 7}; + /// or + /// fixed_ring_buffer rb(8); // capacity doesn't need to respect sentinel + /// rb.push_back(0); + /// + /// +#if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + using fixed_ring_buffer = + ring_buffer, typename fixed_vector::overflow_allocator_type>; +#endif + +} // namespace eastl + +#endif // Header include guard + diff --git a/lib/EASTL/include/EASTL/bonus/fixed_tuple_vector.h b/lib/EASTL/include/EASTL/bonus/fixed_tuple_vector.h new file mode 100644 index 000000000..e9ce0ec07 --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/fixed_tuple_vector.h @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_FIXEDTUPLEVECTOR_H +#define EASTL_FIXEDTUPLEVECTOR_H + +#include +#include + +#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 +class fixed_tuple_vector : public TupleVecInternal::TupleVecImpl::GetTotalAllocationSize(nodeCount, 0), 1, + TupleVecInternal::TupleRecurser::GetTotalAlignment(), 0, + bEnableOverflow, EASTLAllocatorType>, make_index_sequence, Ts...> +{ +public: + typedef fixed_vector_allocator< + TupleVecInternal::TupleRecurser::GetTotalAllocationSize(nodeCount, 0), 1, + TupleVecInternal::TupleRecurser::GetTotalAlignment(), 0, + bEnableOverflow, EASTLAllocatorType> fixed_allocator_type; + typedef aligned_buffer aligned_buffer_type; + typedef fixed_tuple_vector this_type; + typedef EASTLAllocatorType overflow_allocator_type; + + typedef TupleVecInternal::TupleVecImpl, 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 + fixed_tuple_vector(move_iterator begin, move_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); + } + + template + 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 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 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 +inline void swap(fixed_tuple_vector& a, + fixed_tuple_vector& b) +{ + a.swap(b); +} + + +} // namespace eastl + +#endif // EASTL_TUPLEVECTOR_H diff --git a/lib/EASTL/include/EASTL/bonus/intrusive_sdlist.h b/lib/EASTL/include/EASTL/bonus/intrusive_sdlist.h new file mode 100644 index 000000000..1b126d438 --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/intrusive_sdlist.h @@ -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 +#include +#include + +#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 + struct IntrusiveSDListIterator + { + typedef IntrusiveSDListIterator this_type; + typedef IntrusiveSDListIterator iterator; + typedef IntrusiveSDListIterator 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 + class intrusive_sdlist : public intrusive_sdlist_base + { + public: + typedef intrusive_sdlist 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 iterator; + typedef IntrusiveSDListIterator const_iterator; + typedef eastl::reverse_iterator reverse_iterator; + typedef eastl::reverse_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 + inline IntrusiveSDListIterator::IntrusiveSDListIterator() + { + #if EASTL_DEBUG + mpNode = NULL; + #endif + } + + template + inline IntrusiveSDListIterator::IntrusiveSDListIterator(pointer pNode) + : mpNode(pNode) + { + } + + template + inline IntrusiveSDListIterator::IntrusiveSDListIterator(const iterator& x) + : mpNode(x.mpNode) + { + } + + template + inline typename IntrusiveSDListIterator::reference + IntrusiveSDListIterator::operator*() const + { + return *mpNode; + } + + template + inline typename IntrusiveSDListIterator::pointer + IntrusiveSDListIterator::operator->() const + { + return mpNode; + } + + template + inline typename IntrusiveSDListIterator::this_type& + IntrusiveSDListIterator::operator++() + { + mpNode = static_cast(mpNode->mpNext); + return *this; + } + + template + inline typename IntrusiveSDListIterator::this_type + IntrusiveSDListIterator::operator++(int) + { + this_type temp = *this; + mpNode = static_cast(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 + inline bool operator==(const IntrusiveSDListIterator& a, + const IntrusiveSDListIterator& b) + { + return a.mpNode == b.mpNode; + } + + + template + inline bool operator!=(const IntrusiveSDListIterator& a, + const IntrusiveSDListIterator& 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 + inline bool operator!=(const IntrusiveSDListIterator& a, + const IntrusiveSDListIterator& 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 + inline intrusive_sdlist::intrusive_sdlist() + { + } + + + template + inline intrusive_sdlist::intrusive_sdlist(const this_type& /*x*/) + : intrusive_sdlist_base() + { + // We intentionally ignore argument x. + } + + + template + inline typename intrusive_sdlist::this_type& intrusive_sdlist::operator=(const this_type& /*x*/) + { + return *this; // We intentionally ignore argument x. + } + + + template + inline typename intrusive_sdlist::iterator intrusive_sdlist::begin() + { return iterator(static_cast(mpNext)); } + + + template + inline typename intrusive_sdlist::const_iterator intrusive_sdlist::begin() const + { return const_iterator(static_cast(const_cast(mpNext))); } + + + template + inline typename intrusive_sdlist::const_iterator intrusive_sdlist::cbegin() const + { return const_iterator(static_cast(const_cast(mpNext))); } + + + template + inline typename intrusive_sdlist::iterator intrusive_sdlist::end() + { return iterator(static_cast(NULL)); } + + + template + inline typename intrusive_sdlist::const_iterator intrusive_sdlist::end() const + { return const_iterator(static_cast(NULL)); } + + + template + inline typename intrusive_sdlist::const_iterator intrusive_sdlist::cend() const + { return const_iterator(static_cast(NULL)); } + + + template + inline typename intrusive_sdlist::reference intrusive_sdlist::front() + { return *static_cast(mpNext); } + + + template + inline typename intrusive_sdlist::const_reference intrusive_sdlist::front() const + { return *static_cast(mpNext); } + + + template + inline void intrusive_sdlist::push_front(value_type& value) + { + value.mpNext = mpNext; + value.mppPrevNext = &mpNext; + if(mpNext) + mpNext->mppPrevNext = &value.mpNext; + mpNext = &value; + } + + + template + inline void intrusive_sdlist::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 + inline void intrusive_sdlist::pop_back() + { + node_type* pCurrent = static_cast(mpNext); + + while(pCurrent->mpNext) + pCurrent = static_cast(pCurrent->mpNext); + + *pCurrent->mppPrevNext = NULL; + } + + template + inline bool intrusive_sdlist::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 + inline typename intrusive_sdlist::iterator intrusive_sdlist::locate(value_type& value) + { + intrusive_sdlist_node* pCurrent; + + for(pCurrent = static_cast(mpNext); pCurrent; pCurrent = pCurrent->mpNext) + { + if(pCurrent == &value) + break; + } + + return iterator(static_cast(pCurrent)); + } + + + template + inline typename intrusive_sdlist::const_iterator intrusive_sdlist::locate(const T& value) const + { + const intrusive_sdlist_node* pCurrent; + + for(pCurrent = static_cast(mpNext); pCurrent; pCurrent = pCurrent->mpNext) + { + if(pCurrent == &value) + break; + } + + return const_iterator(static_cast(const_cast(pCurrent))); + } + + + template + inline typename intrusive_sdlist::iterator + intrusive_sdlist::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 + inline typename intrusive_sdlist::iterator + intrusive_sdlist::erase(iterator position) + { + *position.mpNode->mppPrevNext = position.mpNode->mpNext; + position.mpNode->mpNext->mppPrevNext = position.mpNode->mppPrevNext; + + return iterator(position.mpNode); + } + + + template + inline typename intrusive_sdlist::iterator + intrusive_sdlist::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 + inline void intrusive_sdlist::remove(value_type& value) + { + *value.mppPrevNext = value.mpNext; + if(value.mpNext) + value.mpNext->mppPrevNext = value.mppPrevNext; + } + + + template + void intrusive_sdlist::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 + void intrusive_sdlist::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 + void intrusive_sdlist::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 + void intrusive_sdlist::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 + void intrusive_sdlist::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 + inline bool intrusive_sdlist::validate() const + { + return true; // To do. + } + + + template + inline int intrusive_sdlist::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 + bool operator==(const intrusive_sdlist& a, const intrusive_sdlist& b) + { + // If we store an mSize member for intrusive_sdlist, we want to take advantage of it here. + typename intrusive_sdlist::const_iterator ia = a.begin(); + typename intrusive_sdlist::const_iterator ib = b.begin(); + typename intrusive_sdlist::const_iterator enda = a.end(); + typename intrusive_sdlist::const_iterator endb = b.end(); + + while((ia != enda) && (ib != endb) && (*ia == *ib)) + { + ++ia; + ++ib; + } + return (ia == enda) && (ib == endb); + } + + template + bool operator<(const intrusive_sdlist& a, const intrusive_sdlist& b) + { + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + template + bool operator!=(const intrusive_sdlist& a, const intrusive_sdlist& b) + { + return !(a == b); + } + + template + bool operator>(const intrusive_sdlist& a, const intrusive_sdlist& b) + { + return b < a; + } + + template + bool operator<=(const intrusive_sdlist& a, const intrusive_sdlist& b) + { + return !(b < a); + } + + template + bool operator>=(const intrusive_sdlist& a, const intrusive_sdlist& b) + { + return !(a < b); + } + + template + void swap(intrusive_sdlist& a, intrusive_sdlist& b) + { + a.swap(b); + } + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/bonus/intrusive_slist.h b/lib/EASTL/include/EASTL/bonus/intrusive_slist.h new file mode 100644 index 000000000..28d445d9c --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/intrusive_slist.h @@ -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 +#include +#include + +#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 + struct IntrusiveSListIterator + { + typedef IntrusiveSListIterator this_type; + typedef IntrusiveSListIterator iterator; + typedef IntrusiveSListIterator 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 + class intrusive_slist : public intrusive_slist_base + { + public: + typedef intrusive_slist 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 iterator; + typedef IntrusiveSListIterator 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 + inline IntrusiveSListIterator::IntrusiveSListIterator() + { + #if EASTL_DEBUG + mpNode = NULL; + #endif + } + + template + inline IntrusiveSListIterator::IntrusiveSListIterator(pointer pNode) + : mpNode(pNode) + { + } + + template + inline IntrusiveSListIterator::IntrusiveSListIterator(const iterator& x) + : mpNode(x.mpNode) + { + } + + + /////////////////////////////////////////////////////////////////////// + // intrusive_slist_base + /////////////////////////////////////////////////////////////////////// + + // To do. + + + /////////////////////////////////////////////////////////////////////// + // intrusive_slist + /////////////////////////////////////////////////////////////////////// + + // To do. + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + bool operator==(const intrusive_slist& a, const intrusive_slist& b) + { + // If we store an mSize member for intrusive_slist, we want to take advantage of it here. + typename intrusive_slist::const_iterator ia = a.begin(); + typename intrusive_slist::const_iterator ib = b.begin(); + typename intrusive_slist::const_iterator enda = a.end(); + typename intrusive_slist::const_iterator endb = b.end(); + + while((ia != enda) && (ib != endb) && (*ia == *ib)) + { + ++ia; + ++ib; + } + return (ia == enda) && (ib == endb); + } + + template + bool operator<(const intrusive_slist& a, const intrusive_slist& b) + { + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + template + bool operator!=(const intrusive_slist& a, const intrusive_slist& b) + { + return !(a == b); + } + + template + bool operator>(const intrusive_slist& a, const intrusive_slist& b) + { + return b < a; + } + + template + bool operator<=(const intrusive_slist& a, const intrusive_slist& b) + { + return !(b < a); + } + + template + bool operator>=(const intrusive_slist& a, const intrusive_slist& b) + { + return !(a < b); + } + + template + void swap(intrusive_slist& a, intrusive_slist& b) + { + a.swap(b); + } + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/bonus/list_map.h b/lib/EASTL/include/EASTL/bonus/list_map.h new file mode 100644 index 000000000..8a080d6d1 --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/list_map.h @@ -0,0 +1,932 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_LIST_MAP_H +#define EASTL_LIST_MAP_H + + +#include + + +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 + 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 + struct list_map_iterator + { + typedef list_map_iterator this_type; + typedef list_map_iterator iterator; + typedef list_map_iterator 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 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 + 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, 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 Allocator = EASTLAllocatorType> + class list_map + : protected rbtree >, Compare, Allocator, eastl::use_value_first > >, true, true> + { + public: + typedef rbtree >, Compare, Allocator, + eastl::use_value_first > >, true, true> base_type; + typedef list_map 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 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 iterator; // This is intentionally different from base_type::iterator + typedef typename eastl::list_map_iterator const_iterator; // This is intentionally different from base_type::const_iterator + typedef eastl::reverse_iterator reverse_iterator; + typedef eastl::reverse_iterator const_reverse_iterator; + typedef typename base_type::allocator_type allocator_type; + typedef typename eastl::pair 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 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 > 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 ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_LIST_MAP_DEFAULT_ALLOCATOR); + + //template + //list_map(Iterator itBegin, Iterator itEnd); + + //this_type& operator=(const this_type& x); + //this_type& operator=(std::initializer_list 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 + iterator find_as(const U& u, Compare2 compare2); + template + 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 + // void remove_if(Predicate); + // void reverse(); + // void sort(); + // template + // 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 + 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 + 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 equal_range(const key_type& key) = delete; + eastl::pair 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 + 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 + void merge(this_type& x, Compare compare) = delete; + + void unique() = delete; // Uniqueness is enforced by map functionality + + template + void unique(BinaryPredicate) = delete; // Uniqueness is enforced by map functionality + #endif + + }; // list_map + + + /////////////////////////////////////////////////////////////////////// + // list_map_data + /////////////////////////////////////////////////////////////////////// + + template + inline list_map_data::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 + inline list_map_iterator::list_map_iterator() + : mpNode(NULL) + { + // Empty + } + + + template + inline list_map_iterator::list_map_iterator(const base_node_type* pNode) + : mpNode(static_cast(const_cast(pNode))) + { + // Empty + } + + + template + inline list_map_iterator::list_map_iterator(const iterator& x) + : mpNode(const_cast(x.mpNode)) + { + // Empty + } + + + template + inline typename list_map_iterator::reference + list_map_iterator::operator*() const + { + return mpNode->mValue; + } + + + template + inline typename list_map_iterator::pointer + list_map_iterator::operator->() const + { + return &mpNode->mValue; + } + + + template + inline typename list_map_iterator::this_type& + list_map_iterator::operator++() + { + mpNode = static_cast(mpNode->mpNext); + return *this; + } + + + template + inline typename list_map_iterator::this_type + list_map_iterator::operator++(int) + { + this_type temp(*this); + mpNode = static_cast(mpNode->mpNext); + return temp; + } + + + template + inline typename list_map_iterator::this_type& + list_map_iterator::operator--() + { + mpNode = static_cast(mpNode->mpPrev); + return *this; + } + + + template + inline typename list_map_iterator::this_type + list_map_iterator::operator--(int) + { + this_type temp(*this); + mpNode = static_cast(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 + inline bool operator==(const list_map_iterator& a, + const list_map_iterator& b) + { + return a.mpNode == b.mpNode; + } + + + template + inline bool operator!=(const list_map_iterator& a, + const list_map_iterator& 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 + inline bool operator!=(const list_map_iterator& a, + const list_map_iterator& b) + { + return a.mpNode != b.mpNode; + } + + + /////////////////////////////////////////////////////////////////////// + // list_map + /////////////////////////////////////////////////////////////////////// + + template + inline list_map::list_map(const allocator_type& allocator) + : base_type(allocator) + { + mNode.mpNext = &mNode; + mNode.mpPrev = &mNode; + } + + template + inline list_map::list_map(const Compare& compare, const allocator_type& allocator) + : base_type(compare, allocator) + { + mNode.mpNext = &mNode; + mNode.mpPrev = &mNode; + } + + template + inline typename list_map::iterator + list_map::begin() EA_NOEXCEPT + { + return iterator(mNode.mpNext); + } + + template + inline typename list_map::const_iterator + list_map::begin() const EA_NOEXCEPT + { + return const_iterator(mNode.mpNext); + } + + template + inline typename list_map::const_iterator + list_map::cbegin() const EA_NOEXCEPT + { + return const_iterator(mNode.mpNext); + } + + template + inline typename list_map::iterator + list_map::end() EA_NOEXCEPT + { + return iterator(&mNode); + } + + template + inline typename list_map::const_iterator + list_map::end() const EA_NOEXCEPT + { + return const_iterator(&mNode); + } + + template + inline typename list_map::const_iterator + list_map::cend() const EA_NOEXCEPT + { + return const_iterator(&mNode); + } + + template + inline typename list_map::reverse_iterator + list_map::rbegin() EA_NOEXCEPT + { + return reverse_iterator(&mNode); + } + + template + inline typename list_map::const_reverse_iterator + list_map::rbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(&mNode); + } + + template + inline typename list_map::const_reverse_iterator + list_map::crbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(&mNode); + } + + template + inline typename list_map::reverse_iterator + list_map::rend() EA_NOEXCEPT + { + return reverse_iterator(mNode.mpNext); + } + + template + inline typename list_map::const_reverse_iterator + list_map::rend() const EA_NOEXCEPT + { + return const_reverse_iterator(mNode.mpNext); + } + + template + inline typename list_map::const_reverse_iterator + list_map::crend() const EA_NOEXCEPT + { + return const_reverse_iterator(mNode.mpNext); + } + + template + inline typename list_map::reference + list_map::front() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(static_cast(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(mNode.mpNext)->mValue; + } + + template + inline typename list_map::const_reference + list_map::front() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(static_cast(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(mNode.mpNext)->mValue; + } + + template + inline typename list_map::reference + list_map::back() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(static_cast(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(mNode.mpPrev)->mValue; + } + + template + inline typename list_map::const_reference + list_map::back() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(static_cast(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(mNode.mpPrev)->mValue; + } + + template + bool list_map::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 + bool list_map::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 + bool list_map::push_front(const key_type& key, const mapped_type& value) + { + return push_front(eastl::make_pair(key, value)); + } + + template + bool list_map::push_back(const key_type& key, const mapped_type& value) + { + return push_back(eastl::make_pair(key, value)); + } + + template + void list_map::pop_front() + { + #if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(empty())) + EASTL_FAIL_MSG("list_map::pop_front -- empty container"); + #endif + + erase(static_cast(mNode.mpNext)->mValue.first); + } + + template + void list_map::pop_back() + { + #if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(empty())) + EASTL_FAIL_MSG("list_map::pop_back -- empty container"); + #endif + + erase(static_cast(mNode.mpPrev)->mValue.first); + } + + template + inline typename list_map::iterator + list_map::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 + inline typename list_map::const_iterator + list_map::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 + template + inline typename list_map::iterator + list_map::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 + template + inline typename list_map::const_iterator + list_map::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 + inline typename list_map::size_type + list_map::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 + inline typename list_map::size_type + list_map::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 + inline typename list_map::iterator + list_map::erase(const_iterator position) + { + iterator posIter(position.mpNode); // Convert from const. + iterator eraseIter(posIter++); + erase(eraseIter->first); + return posIter; + } + + template + inline typename list_map::reverse_iterator + list_map::erase(const_reverse_iterator position) + { + return reverse_iterator(erase((++position).base())); + } + + template + void list_map::clear() + { + base_type::clear(); + + mNode.mpNext = &mNode; + mNode.mpPrev = &mNode; + } + + template + void list_map::reset_lose_memory() + { + base_type::reset_lose_memory(); + + mNode.mpNext = &mNode; + mNode.mpPrev = &mNode; + } + + template + bool list_map::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(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(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 + int list_map::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 + + + + diff --git a/lib/EASTL/include/EASTL/bonus/lru_cache.h b/lib/EASTL/include/EASTL/bonus/lru_cache.h new file mode 100644 index 000000000..46d053dcd --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/lru_cache.h @@ -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 +#include +#include + +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 map_type = eastl::unordered_map, + eastl::hash, + eastl::equal_to, + 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; + using iterator = typename map_type::iterator; + using const_iterator = typename map_type::const_iterator; + using this_type = lru_cache; + using create_callback_type = eastl::function; + using delete_callback_type = eastl::function; + + /// 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> il) + : lru_cache(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 + 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)..., 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 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 diff --git a/lib/EASTL/include/EASTL/bonus/ring_buffer.h b/lib/EASTL/include/EASTL/bonus/ring_buffer.h new file mode 100644 index 000000000..fcd8fd2c8 --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/ring_buffer.h @@ -0,0 +1,1581 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// A ring buffer is a FIFO (first-in, first-out) container which acts +// much like a queue. The difference is that a ring buffer is implemented +// via chasing pointers around a given container instead of like queue +// adds to the writes to the end of the container are reads from the begin. +// The benefit of a ring buffer is that memory allocations don't occur +// and new elements are neither added nor removed from the container. +// Elements in the container are simply assigned values in circles around +// the container. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_RING_BUFFER_H +#define EASTL_RING_BUFFER_H + + +#include +#include +#include +#include +#include + +#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_RING_BUFFER_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_RING_BUFFER_DEFAULT_NAME + #define EASTL_RING_BUFFER_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " ring_buffer" // Unless the user overrides something, this is "EASTL ring_buffer". + #endif + + /// EASTL_RING_BUFFER_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_RING_BUFFER_DEFAULT_ALLOCATOR + #define EASTL_RING_BUFFER_DEFAULT_ALLOCATOR allocator_type(EASTL_RING_BUFFER_DEFAULT_NAME) + #endif + + + /// ring_buffer_iterator + /// + /// We force this iterator to act like a random access iterator even if + /// the underlying container doesn't support random access iteration. + /// Any BidirectionalIterator can be a RandomAccessIterator; it just + /// might be inefficient in some cases. + /// + template + struct ring_buffer_iterator + { + public: + typedef ring_buffer_iterator this_type; + typedef T value_type; + typedef Pointer pointer; + typedef Reference reference; + typedef typename Container::size_type size_type; + typedef typename Container::difference_type difference_type; + typedef typename Container::iterator container_iterator; + typedef typename Container::const_iterator container_const_iterator; + typedef ring_buffer_iterator iterator; + typedef ring_buffer_iterator const_iterator; + typedef EASTL_ITC_NS::random_access_iterator_tag iterator_category; + + public: + Container* mpContainer; + container_iterator mContainerIterator; + + public: + ring_buffer_iterator(); + ring_buffer_iterator(Container* pContainer, const container_iterator& containerIterator); + ring_buffer_iterator(const iterator& x); + + ring_buffer_iterator& operator=(const iterator& x); + + reference operator*() const; + pointer operator->() const; + + this_type& operator++(); + this_type operator++(int); + + this_type& operator--(); + this_type operator--(int); + + this_type& operator+=(difference_type n); + this_type& operator-=(difference_type n); + + this_type operator+(difference_type n) const; + this_type operator-(difference_type n) const; + + protected: + void increment(difference_type n, EASTL_ITC_NS::input_iterator_tag); + void increment(difference_type n, EASTL_ITC_NS::random_access_iterator_tag); + + }; // struct ring_buffer_iterator + + + + /// ring_buffer + /// + /// Implements a ring buffer via a given container type, which would + /// typically be a vector or array, though any container which supports + /// bidirectional iteration would work. + /// + /// A ring buffer is a FIFO (first-in, first-out) container which acts + /// much like a queue. The difference is that a ring buffer is implemented + /// via chasing pointers around a container and moving the read and write + /// positions forward (and possibly wrapping around) as the container is + /// read and written via pop_front and push_back. + /// + /// The benefit of a ring buffer is that memory allocations don't occur + /// and new elements are neither added nor removed from the container. + /// Elements in the container are simply assigned values in circles around + /// the container. + /// + /// ring_buffer is different from other containers -- including adapter + /// containers -- in how iteration is done. Iteration of a ring buffer + /// starts at the current begin position, proceeds to the end of the underlying + /// container, and continues at the begin of the underlying container until + /// the ring buffer's current end position. Thus a ring_buffer does + /// indeed have a begin and an end, though the values of begin and end + /// chase each other around the container. An empty ring_buffer is one + /// in which end == begin, and a full ring_buffer is one in which + /// end + 1 == begin. + /// + /// Example of a ring buffer layout, where + indicates queued items: + /// ++++++++++--------------------------------+++++++++ + /// ^ ^ + /// end begin + /// + /// Empty ring buffer: + /// --------------------------------------------------- + /// ^ + /// begin / end + /// + /// Full ring buffer. Note that one item is necessarily unused; it is + /// analagous to a '\0' at the end of a C string: + /// +++++++++++++++++++++++++++++++++++++++++-+++++++++ + /// ^^ + /// end begin + /// + /// A push_back operation on a ring buffer assigns the new value to end. + /// If there is no more space in the buffer, this will result in begin + /// being overwritten and the begin position being moved foward one position. + /// The user can use the full() function to detect this condition. + /// Note that elements in a ring buffer are not created or destroyed as + /// their are added and removed; they are merely assigned. Only on + /// container construction and destruction are any elements created and + /// destroyed. + /// + /// The ring buffer can be used in either direction. By this we mean that + /// you can use push_back to add items and pop_front to remove them; or you can + /// use push_front to add items and pop_back to remove them. You aren't + /// limited to these operations; you can push or pop from either side + /// arbitrarily and you can insert or erase anywhere in the container. + /// + /// The ring buffer requires the user to specify a Container type, which + /// by default is vector. However, any container with bidirectional iterators + /// will work, such as list, deque, string or any of the fixed_* versions + /// of these containers, such as fixed_string. Since ring buffer works via copying + /// elements instead of allocating and freeing nodes, inserting in the middle + /// of a ring buffer based on list (instead of vector) is no more efficient. + /// + /// To use the ring buffer, its container must be resized to the desired + /// ring buffer size. Changing the size of a ring buffer may cause ring + /// buffer iterators to invalidate. + /// + /// An alternative to using a ring buffer is to use a list with a user-created + /// node pool and custom allocator. There are various tradeoffs that result from this. + /// + /// Example usage: + /// ring_buffer< int, list > rb(100); + /// rb.push_back(1); + /// + /// Example usage: + /// // Example of creating an on-screen debug log that shows 16 + /// // strings at a time and scrolls older strings away. + /// + /// // Create ring buffer of 16 strings. + /// ring_buffer< string, vector > debugLogText(16); + /// + /// // Reserve 128 chars for each line. This can make it so that no + /// // runtime memory allocations occur. + /// for(vector::iterator it = debugLogText.get_container().begin(), + /// itEnd = debugLogText.get_container().end(); it != itEnd; ++it) + /// { + /// (*it).reserve(128); + /// } + /// + /// // Add a new string, using push_front() and front() instead of + /// // push_front(str) in order to avoid creating a temporary str. + /// debugLogText.push_front(); + /// debugLogText.front() = "Player fired weapon"; + /// + template , typename Allocator = typename Container::allocator_type> + class ring_buffer + { + public: + typedef ring_buffer this_type; + typedef Container container_type; + typedef Allocator allocator_type; + + typedef typename Container::value_type value_type; + typedef typename Container::reference reference; + typedef typename Container::const_reference const_reference; + typedef typename Container::size_type size_type; + typedef typename Container::difference_type difference_type; + typedef typename Container::iterator container_iterator; + typedef typename Container::const_iterator container_const_iterator; + typedef ring_buffer_iterator iterator; + typedef ring_buffer_iterator const_iterator; + typedef eastl::reverse_iterator reverse_iterator; + typedef eastl::reverse_iterator const_reverse_iterator; + + public: // We declare public so that global comparison operators can be implemented without adding an inline level and without tripping up GCC 2.x friend declaration failures. GCC (through at least v4.0) is poor at inlining and performance wins over correctness. + Container c; // We follow the naming convention established for stack, queue, priority_queue and name this 'c'. This variable must always have a size of at least 1, as even an empty ring_buffer has an unused terminating element. + + protected: + container_iterator mBegin; // We keep track of where our begin and end are by using Container iterators. + container_iterator mEnd; + size_type mSize; + + public: + // There currently isn't a ring_buffer constructor that specifies an initial size, unlike other containers. + explicit ring_buffer(size_type cap = 0); // Construct with an initial capacity (but size of 0). + explicit ring_buffer(size_type cap, const allocator_type& allocator); + explicit ring_buffer(const Container& x); + explicit ring_buffer(const allocator_type& allocator); + ring_buffer(const this_type& x); + ring_buffer(this_type&& x); + ring_buffer(this_type&& x, const allocator_type& allocator); + ring_buffer(std::initializer_list ilist, const allocator_type& allocator = EASTL_RING_BUFFER_DEFAULT_ALLOCATOR); // This function sets the capacity to be equal to the size of the initializer list. + + // No destructor necessary. Default will do. + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list ilist); + this_type& operator=(this_type&& x); + + template + void assign(InputIterator first, InputIterator last); + + void swap(this_type& x); + + 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; + + bool empty() const EA_NOEXCEPT; + bool full() const EA_NOEXCEPT; + size_type size() const EA_NOEXCEPT; + size_type capacity() const EA_NOEXCEPT; + + void resize(size_type n); + void set_capacity(size_type n); // Sets the capacity to the given value, including values less than the current capacity. Adjusts the size downward if n < size, by throwing out the oldest elements in the buffer. + void reserve(size_type n); // Reserve a given capacity. Doesn't decrease the capacity; it only increases it (for compatibility with other containers' behavior). + + reference front(); + const_reference front() const; + + reference back(); + const_reference back() const; + + void push_back(const value_type& value); + reference push_back(); + + void push_front(const value_type& value); + reference push_front(); + + void pop_back(); + void pop_front(); + + reference operator[](size_type n); + const_reference operator[](size_type n) const; + + // To consider: + // size_type read(value_type* pDestination, size_type nCount); + // size_type read(iterator** pPosition1, iterator** pPosition2, size_type& nCount1, size_type& nCount2); + + /* To do: + template + void emplace_front(Args&&... args); + + template + void emplace_back(Args&&... args); + + template + iterator emplace(const_iterator position, Args&&... args); + */ + + iterator insert(const_iterator position, const value_type& value); + void insert(const_iterator position, size_type n, const value_type& value); + void insert(const_iterator position, std::initializer_list ilist); + + template + void insert(const_iterator position, InputIterator first, InputIterator last); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + reverse_iterator erase(const_reverse_iterator position); + reverse_iterator erase(const_reverse_iterator first, const_reverse_iterator last); + + void clear(); + + container_type& get_container(); + const container_type& get_container() const; + + bool validate() const; + int validate_iterator(const_iterator i) const; + + protected: + //size_type DoGetSize(EASTL_ITC_NS::input_iterator_tag) const; + //size_type DoGetSize(EASTL_ITC_NS::random_access_iterator_tag) const; + + }; // class ring_buffer + + + + + /////////////////////////////////////////////////////////////////////// + // ring_buffer_iterator + /////////////////////////////////////////////////////////////////////// + + template + ring_buffer_iterator::ring_buffer_iterator() + : mpContainer(NULL), mContainerIterator() + { + } + + + template + ring_buffer_iterator::ring_buffer_iterator(Container* pContainer, const container_iterator& containerIterator) + : mpContainer(pContainer), mContainerIterator(containerIterator) + { + } + + + template + ring_buffer_iterator::ring_buffer_iterator(const iterator& x) + : mpContainer(x.mpContainer), mContainerIterator(x.mContainerIterator) + { + } + + + template + ring_buffer_iterator& + ring_buffer_iterator::operator=(const iterator& x) + { + mpContainer = x.mpContainer; + mContainerIterator = x.mContainerIterator; + return *this; + } + + template + typename ring_buffer_iterator::reference + ring_buffer_iterator::operator*() const + { + return *mContainerIterator; + } + + + template + typename ring_buffer_iterator::pointer + ring_buffer_iterator::operator->() const + { + return &*mContainerIterator; + } + + + template + typename ring_buffer_iterator::this_type& + ring_buffer_iterator::operator++() + { + if(EASTL_UNLIKELY(++mContainerIterator == mpContainer->end())) + mContainerIterator = mpContainer->begin(); + return *this; + } + + + template + typename ring_buffer_iterator::this_type + ring_buffer_iterator::operator++(int) + { + const this_type temp(*this); + if(EASTL_UNLIKELY(++mContainerIterator == mpContainer->end())) + mContainerIterator = mpContainer->begin(); + return temp; + } + + + template + typename ring_buffer_iterator::this_type& + ring_buffer_iterator::operator--() + { + if(EASTL_UNLIKELY(mContainerIterator == mpContainer->begin())) + mContainerIterator = mpContainer->end(); + --mContainerIterator; + return *this; + } + + + template + typename ring_buffer_iterator::this_type + ring_buffer_iterator::operator--(int) + { + const this_type temp(*this); + if(EASTL_UNLIKELY(mContainerIterator == mpContainer->begin())) + mContainerIterator = mpContainer->end(); + --mContainerIterator; + return temp; + } + + + template + typename ring_buffer_iterator::this_type& + ring_buffer_iterator::operator+=(difference_type n) + { + typedef typename eastl::iterator_traits::iterator_category IC; + increment(n, IC()); + return *this; + } + + + template + typename ring_buffer_iterator::this_type& + ring_buffer_iterator::operator-=(difference_type n) + { + typedef typename eastl::iterator_traits::iterator_category IC; + increment(-n, IC()); + return *this; + } + + + template + typename ring_buffer_iterator::this_type + ring_buffer_iterator::operator+(difference_type n) const + { + return this_type(*this).operator+=(n); + } + + + template + typename ring_buffer_iterator::this_type + ring_buffer_iterator::operator-(difference_type n) const + { + return this_type(*this).operator+=(-n); + } + + + template + void ring_buffer_iterator::increment(difference_type n, EASTL_ITC_NS::input_iterator_tag) + { + // n cannot be negative, as input iterators don't support reverse iteration. + while(n-- > 0) + operator++(); + } + + + template + void ring_buffer_iterator::increment(difference_type n, EASTL_ITC_NS::random_access_iterator_tag) + { + // We make the assumption here that the user is incrementing from a valid + // starting position to a valid ending position. Thus *this + n yields a + // valid iterator, including if n happens to be a negative value. + + if(n >= 0) + { + const difference_type d = mpContainer->end() - mContainerIterator; + + if(n < d) + mContainerIterator += n; + else + mContainerIterator = mpContainer->begin() + (n - d); + } + else + { + // Recall that n and d here will be negative and so the logic here works as intended. + const difference_type d = mpContainer->begin() - mContainerIterator; + + if(n >= d) + mContainerIterator += n; + else + mContainerIterator = mpContainer->end() + (n - d); + } + } + + + // Random access iterators must support operator + and operator -. + // You can only add an integer to an iterator, and you cannot add two iterators. + template + inline ring_buffer_iterator + operator+(ptrdiff_t n, const ring_buffer_iterator& x) + { + return x + n; // Implement (n + x) in terms of (x + n). + } + + + // You can only add an integer to an iterator, but you can subtract two iterators. + template + inline typename ring_buffer_iterator::difference_type + operator-(const ring_buffer_iterator& a, + const ring_buffer_iterator& b) + { + typedef typename ring_buffer_iterator::difference_type difference_type; + + // To do: If container_iterator is a random access iterator, then do a simple calculation. + // Otherwise, we have little choice but to iterate from a to b and count as we go. + // See the ring_buffer::size function for an implementation of this. + + // Iteration implementation: + difference_type d = 0; + + for(ring_buffer_iterator temp(b); temp != a; ++temp) + ++d; + + return d; + } + + + // 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 + inline bool operator==(const ring_buffer_iterator& a, + const ring_buffer_iterator& b) + { + // Perhaps we should compare the container pointer as well. + // However, for valid iterators this shouldn't be necessary. + return a.mContainerIterator == b.mContainerIterator; + } + + + template + inline bool operator!=(const ring_buffer_iterator& a, + const ring_buffer_iterator& b) + { + // Perhaps we should compare the container pointer as well. + // However, for valid iterators this shouldn't be necessary. + return !(a.mContainerIterator == b.mContainerIterator); + } + + + // 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 + inline bool operator!=(const ring_buffer_iterator& a, + const ring_buffer_iterator& b) + { + return !(a.mContainerIterator == b.mContainerIterator); + } + + + + + /////////////////////////////////////////////////////////////////////// + // ring_buffer + /////////////////////////////////////////////////////////////////////// + + template + ring_buffer::ring_buffer(size_type cap) + : c() // Default construction with default allocator for the container. + { + // To do: This code needs to be amended to deal with possible exceptions + // that could occur during the resize call below. + + // We add one because the element at mEnd is necessarily unused. + c.resize(cap + 1); // Possibly we could construct 'c' with size, but c may not have such a ctor, though we rely on it having a resize function. + mBegin = c.begin(); + mEnd = mBegin; + mSize = 0; + } + + + template + ring_buffer::ring_buffer(size_type cap, const allocator_type& allocator) + : c(allocator) + { + // To do: This code needs to be amended to deal with possible exceptions + // that could occur during the resize call below. + + // We add one because the element at mEnd is necessarily unused. + c.resize(cap + 1); // Possibly we could construct 'c' with size, but c may not have such a ctor, though we rely on it having a resize function. + mBegin = c.begin(); + mEnd = mBegin; + mSize = 0; + } + + + template + ring_buffer::ring_buffer(const Container& x) + : c(x) // This copies elements from x, but unless the user is doing some tricks, the only thing that matters is that c.size() == x.size(). + { + // To do: This code needs to be amended to deal with possible exceptions + // that could occur during the resize call below. + if(c.empty()) + c.resize(1); + mBegin = c.begin(); + mEnd = mBegin; + mSize = 0; + } + + + template + ring_buffer::ring_buffer(const allocator_type& allocator) + : c(allocator) + { + // To do: This code needs to be amended to deal with possible exceptions + // that could occur during the resize call below. + + // We add one because the element at mEnd is necessarily unused. + c.resize(1); // Possibly we could construct 'c' with size, but c may not have such a ctor, though we rely on it having a resize function. + mBegin = c.begin(); + mEnd = mBegin; + mSize = 0; + } + + + template + ring_buffer::ring_buffer(const this_type& x) + : c(x.c) + { + mBegin = c.begin(); + mEnd = mBegin; + mSize = x.mSize; + + eastl::advance(mBegin, eastl::distance(const_cast(x).c.begin(), x.mBegin)); // We can do a simple distance algorithm here, as there will be no wraparound. + eastl::advance(mEnd, eastl::distance(const_cast(x).c.begin(), x.mEnd)); + } + + template + ring_buffer::ring_buffer(this_type&& x) + : c() // Default construction with default allocator for the container. + { + c.resize(1); // Possibly we could construct 'c' with size, but c may not have such a ctor, though we rely on it having a resize function. + mBegin = c.begin(); + mEnd = mBegin; + mSize = 0; + + swap(x); // We are leaving x in an unusual state by swapping default-initialized members with it, as it won't be usable and can be only destructible. + } + + template + ring_buffer::ring_buffer(this_type&& x, const allocator_type& allocator) + : c(allocator) + { + c.resize(1); // Possibly we could construct 'c' with size, but c may not have such a ctor, though we rely on it having a resize function. + mBegin = c.begin(); + mEnd = mBegin; + mSize = 0; + + if(c.get_allocator() == x.c.get_allocator()) + swap(x); // We are leaving x in an unusual state by swapping default-initialized members with it, as it won't be usable and can be only destructible. + else + operator=(x); + } + + + template + ring_buffer::ring_buffer(std::initializer_list ilist, const allocator_type& allocator) + : c(allocator) + { + c.resize((eastl_size_t)ilist.size() + 1); + mBegin = c.begin(); + mEnd = mBegin; + mSize = 0; + + assign(ilist.begin(), ilist.end()); + } + + + template + typename ring_buffer::this_type& + ring_buffer::operator=(const this_type& x) + { + if(&x != this) + { + c = x.c; + + mBegin = c.begin(); + mEnd = mBegin; + mSize = x.mSize; + + eastl::advance(mBegin, eastl::distance(const_cast(x).c.begin(), x.mBegin)); // We can do a simple distance algorithm here, as there will be no wraparound. + eastl::advance(mEnd, eastl::distance(const_cast(x).c.begin(), x.mEnd)); + } + + return *this; + } + + + template + typename ring_buffer::this_type& + ring_buffer::operator=(this_type&& x) + { + swap(x); + return *this; + } + + + template + typename ring_buffer::this_type& + ring_buffer::operator=(std::initializer_list ilist) + { + assign(ilist.begin(), ilist.end()); + return *this; + } + + + template + template + void ring_buffer::assign(InputIterator first, InputIterator last) + { + // To consider: We can make specializations of this for pointer-based + // iterators to PODs and turn the action into a memcpy. + clear(); + + for(; first != last; ++first) + push_back(*first); + } + + + template + void ring_buffer::swap(this_type& x) + { + if(&x != this) + { + const difference_type dBegin = eastl::distance(c.begin(), mBegin); // We can do a simple distance algorithm here, as there will be no wraparound. + const difference_type dEnd = eastl::distance(c.begin(), mEnd); + + const difference_type dxBegin = eastl::distance(x.c.begin(), x.mBegin); + const difference_type dxEnd = eastl::distance(x.c.begin(), x.mEnd); + + eastl::swap(c, x.c); + eastl::swap(mSize, x.mSize); + + mBegin = c.begin(); + eastl::advance(mBegin, dxBegin); // We can do a simple advance algorithm here, as there will be no wraparound. + + mEnd = c.begin(); + eastl::advance(mEnd, dxEnd); + + x.mBegin = x.c.begin(); + eastl::advance(x.mBegin, dBegin); + + x.mEnd = x.c.begin(); + eastl::advance(x.mEnd, dEnd); + } + } + + + template + typename ring_buffer::iterator + ring_buffer::begin() EA_NOEXCEPT + { + return iterator(&c, mBegin); + } + + + template + typename ring_buffer::const_iterator + ring_buffer::begin() const EA_NOEXCEPT + { + return const_iterator(const_cast(&c), mBegin); // We trust that the const_iterator will respect const-ness. + } + + + template + typename ring_buffer::const_iterator + ring_buffer::cbegin() const EA_NOEXCEPT + { + return const_iterator(const_cast(&c), mBegin); // We trust that the const_iterator will respect const-ness. + } + + + template + typename ring_buffer::iterator + ring_buffer::end() EA_NOEXCEPT + { + return iterator(&c, mEnd); + } + + + template + typename ring_buffer::const_iterator + ring_buffer::end() const EA_NOEXCEPT + { + return const_iterator(const_cast(&c), mEnd); // We trust that the const_iterator will respect const-ness. + } + + + template + typename ring_buffer::const_iterator + ring_buffer::cend() const EA_NOEXCEPT + { + return const_iterator(const_cast(&c), mEnd); // We trust that the const_iterator will respect const-ness. + } + + + template + typename ring_buffer::reverse_iterator + ring_buffer::rbegin() EA_NOEXCEPT + { + return reverse_iterator(iterator(&c, mEnd)); + } + + + template + typename ring_buffer::const_reverse_iterator + ring_buffer::rbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(const_iterator(const_cast(&c), mEnd)); + } + + + template + typename ring_buffer::const_reverse_iterator + ring_buffer::crbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(const_iterator(const_cast(&c), mEnd)); + } + + + template + typename ring_buffer::reverse_iterator + ring_buffer::rend() EA_NOEXCEPT + { + return reverse_iterator(iterator(&c, mBegin)); + } + + + template + typename ring_buffer::const_reverse_iterator + ring_buffer::rend() const EA_NOEXCEPT + { + return const_reverse_iterator(const_iterator(const_cast(&c), mBegin)); + } + + + template + typename ring_buffer::const_reverse_iterator + ring_buffer::crend() const EA_NOEXCEPT + { + return const_reverse_iterator(const_iterator(const_cast(&c), mBegin)); + } + + + template + bool ring_buffer::empty() const EA_NOEXCEPT + { + return mBegin == mEnd; + } + + + template + bool ring_buffer::full() const EA_NOEXCEPT + { + // Implementation that relies on c.size() being a fast operation: + // return mSize == (c.size() - 1); // (c.size() - 1) == capacity(); we are attempting to reduce function calls. + + // Version that has constant speed guarantees, but is still pretty fast. + const_iterator afterEnd(end()); + ++afterEnd; + return afterEnd.mContainerIterator == mBegin; + } + + + template + typename ring_buffer::size_type + ring_buffer::size() const EA_NOEXCEPT + { + return mSize; + + // Alternatives: + // return eastl::distance(begin(), end()); + // return end() - begin(); // This is more direct than using distance(). + //typedef typename eastl::iterator_traits::iterator_category IC; + //return DoGetSize(IC()); // This is more direct than using iterator math. + } + + + /* + template + typename ring_buffer::size_type + ring_buffer::DoGetSize(EASTL_ITC_NS::input_iterator_tag) const + { + // We could alternatively just use eastl::distance() here, but we happen to + // know that such code would boil down to what we have here, and we might + // as well remove function calls where possible. + difference_type d = 0; + + for(const_iterator temp(begin()), tempEnd(end()); temp != tempEnd; ++temp) + ++d; + + return (size_type)d; + } + */ + + /* + template + typename ring_buffer::size_type + ring_buffer::DoGetSize(EASTL_ITC_NS::random_access_iterator_tag) const + { + // A simpler but less efficient implementation fo this function would be: + // return eastl::distance(mBegin, mEnd); + // + // The calculation of distance here takes advantage of the fact that random + // access iterators' distances can be calculated by simple pointer calculation. + // Thus the code below boils down to a few subtractions when using a vector, + // string, or array as the Container type. + // + const difference_type dBegin = eastl::distance(const_cast(c).begin(), mBegin); // const_cast here solves a little compiler + const difference_type dEnd = eastl::distance(const_cast(c).begin(), mEnd); // argument matching problem. + + if(dEnd >= dBegin) + return dEnd - dBegin; + + return c.size() - (dBegin - dEnd); + } + */ + + + namespace Internal + { + /////////////////////////////////////////////////////////////// + // has_overflow_allocator + // + // returns true_type when the specified container type is an + // eastl::fixed_* container and therefore has an overflow + // allocator type. + // + template + struct has_overflow_allocator : false_type {}; + + template + struct has_overflow_allocator().get_overflow_allocator())>> : true_type {}; + + + /////////////////////////////////////////////////////////////// + // GetFixedContainerCtorAllocator + // + // eastl::fixed_* containers are only constructible via their + // overflow allocator type. This helper select the appropriate + // allocator from the specified container. + // + template ()()> + struct GetFixedContainerCtorAllocator + { + auto& operator()(Container& c) { return c.get_overflow_allocator(); } + }; + + template + struct GetFixedContainerCtorAllocator + { + auto& operator()(Container& c) { return c.get_allocator(); } + }; + } // namespace Internal + + + /////////////////////////////////////////////////////////////// + // ContainerTemporary + // + // Helper type which prevents utilizing excessive stack space + // when creating temporaries when swapping/copying the underlying + // ring_buffer container type. + // + template = EASTL_MAX_STACK_USAGE)> + struct ContainerTemporary + { + Container mContainer; + + ContainerTemporary(Container& parentContainer) + : mContainer(Internal::GetFixedContainerCtorAllocator{}(parentContainer)) + { + } + + Container& get() { return mContainer; } + }; + + template + struct ContainerTemporary + { + typename Container::allocator_type* mAllocator; + Container* mContainer; + + ContainerTemporary(Container& parentContainer) + : mAllocator(&parentContainer.get_allocator()) + , mContainer(new (mAllocator->allocate(sizeof(Container))) Container) + { + } + + ~ContainerTemporary() + { + mContainer->~Container(); + mAllocator->deallocate(mContainer, sizeof(Container)); + } + + Container& get() { return *mContainer; } + }; + + + template + void ring_buffer::resize(size_type n) + { + // Note that if n > size(), we just move the end position out to + // the begin + n, with the data being the old end and the new end + // being stale values from the past. This is by design, as the concept + // of arbitrarily resizing a ring buffer like this is currently deemed + // to be vague in what it intends to do. We can only assume that the + // user knows what he is doing and will deal with the stale values. + EASTL_ASSERT(c.size() >= 1); + const size_type cap = (c.size() - 1); + + mSize = n; + + if(n > cap) // If we need to grow in capacity... + { + // Given that a growing operation will always result in memory allocation, + // we currently implement this function via the usage of a temp container. + // This makes for a simple implementation, but in some cases it is less + // efficient. In particular, if the container is a node-based container like + // a (linked) list, this function would be faster if we simply added nodes + // to ourself. We would do this by inserting the nodes to be after end() + // and adjusting the begin() position if it was after end(). + + // To do: This code needs to be amended to deal with possible exceptions + // that could occur during the resize call below. + + ContainerTemporary cTemp(c); + cTemp.get().resize(n + 1); + eastl::copy(begin(), end(), cTemp.get().begin()); + eastl::swap(c, cTemp.get()); + + mBegin = c.begin(); + mEnd = mBegin; + eastl::advance(mEnd, n); // We can do a simple advance algorithm on this because we know that mEnd will not wrap around. + } + else // We could do a check here for n != size(), but that would be costly and people don't usually resize things to their same size. + { + mEnd = mBegin; + + // eastl::advance(mEnd, n); // We *cannot* use this because there may be wraparound involved. + + // To consider: Possibly we should implement some more detailed logic to optimize the code here. + // We'd need to do different behaviour dending on whether the container iterator type is a + // random access iterator or otherwise. + + while(n--) + { + if(EASTL_UNLIKELY(++mEnd == c.end())) + mEnd = c.begin(); + } + } + } + + + template + typename ring_buffer::size_type + ring_buffer::capacity() const EA_NOEXCEPT + { + EASTL_ASSERT(c.size() >= 1); // This is required because even an empty ring_buffer has one unused termination element, somewhat like a \0 at the end of a C string. + + return (c.size() - 1); // Need to subtract one because the position at mEnd is unused. + } + + + template + void ring_buffer::set_capacity(size_type n) + { + const size_type capacity = (c.size() - 1); + + if(n != capacity) // If we need to change capacity... + { + ContainerTemporary cTemp(c); + cTemp.get().resize(n + 1); + + iterator itCopyBegin = begin(); + + if(n < mSize) // If we are shrinking the capacity, to less than our size... + { + eastl::advance(itCopyBegin, mSize - n); + mSize = n; + } + + eastl::copy(itCopyBegin, end(), cTemp.get().begin()); // The begin-end range may in fact be larger than n, in which case values will be overwritten. + eastl::swap(c, cTemp.get()); + + mBegin = c.begin(); + mEnd = mBegin; + eastl::advance(mEnd, mSize); // We can do a simple advance algorithm on this because we know that mEnd will not wrap around. + } + } + + + template + void ring_buffer::reserve(size_type n) + { + // We follow the pattern of vector and only do something if n > capacity. + EASTL_ASSERT(c.size() >= 1); + + if(n > (c.size() - 1)) // If we need to grow in capacity... // (c.size() - 1) == capacity(); we are attempting to reduce function calls. + { + ContainerTemporary cTemp(c); + cTemp.get().resize(n + 1); + eastl::copy(begin(), end(), cTemp.get().begin()); + eastl::swap(c, cTemp.get()); + + mBegin = c.begin(); + mEnd = mBegin; + eastl::advance(mEnd, mSize); // We can do a simple advance algorithm on this because we know that mEnd will not wrap around. + } + } + + + template + typename ring_buffer::reference + ring_buffer::front() + { + return *mBegin; + } + + + template + typename ring_buffer::const_reference + ring_buffer::front() const + { + return *mBegin; + } + + + template + typename ring_buffer::reference + ring_buffer::back() + { + // return *(end() - 1); // Can't use this because not all iterators support operator-. + + iterator temp(end()); // To do: Find a way to construct this temporary in the return statement. + return *(--temp); // We can do it by making all our containers' iterators support operator-. + } + + + template + typename ring_buffer::const_reference + ring_buffer::back() const + { + // return *(end() - 1); // Can't use this because not all iterators support operator-. + + const_iterator temp(end()); // To do: Find a way to construct this temporary in the return statement. + return *(--temp); // We can do it by making all our containers' iterators support operator-. + } + + + /// A push_back operation on a ring buffer assigns the new value to end. + /// If there is no more space in the buffer, this will result in begin + /// being overwritten and the begin position being moved foward one position. + template + void ring_buffer::push_back(const value_type& value) + { + *mEnd = value; + + if(++mEnd == c.end()) + mEnd = c.begin(); + + if(mEnd == mBegin) + { + if(++mBegin == c.end()) + mBegin = c.begin(); + } + else + ++mSize; + } + + + /// A push_back operation on a ring buffer assigns the new value to end. + /// If there is no more space in the buffer, this will result in begin + /// being overwritten and the begin position being moved foward one position. + template + typename ring_buffer::reference + ring_buffer::push_back() + { + // We don't do the following assignment, as the value at mEnd is already constructed; + // it is merely possibly not default-constructed. However, the spirit of push_back + // is that the user intends to do an assignment or data modification after the + // push_back call. The user can always execute *back() = value_type() if he wants. + //*mEnd = value_type(); + + if(++mEnd == c.end()) + mEnd = c.begin(); + + if(mEnd == mBegin) + { + if(++mBegin == c.end()) + mBegin = c.begin(); + } + else + ++mSize; + + return back(); + } + + + template + void ring_buffer::pop_back() + { + EASTL_ASSERT(mEnd != mBegin); // We assume that size() > 0 and thus that there is something to pop. + + if(EASTL_UNLIKELY(mEnd == c.begin())) + mEnd = c.end(); + --mEnd; + --mSize; + } + + + template + void ring_buffer::push_front(const value_type& value) + { + if(EASTL_UNLIKELY(mBegin == c.begin())) + mBegin = c.end(); + + if(--mBegin == mEnd) + { + if(EASTL_UNLIKELY(mEnd == c.begin())) + mEnd = c.end(); + --mEnd; + } + else + ++mSize; + + *mBegin = value; + } + + + template + typename ring_buffer::reference + ring_buffer::push_front() + { + if(EASTL_UNLIKELY(mBegin == c.begin())) + mBegin = c.end(); + + if(--mBegin == mEnd) + { + if(EASTL_UNLIKELY(mEnd == c.begin())) + mEnd = c.end(); + --mEnd; + } + else + ++mSize; + + // See comments above in push_back for why we don't execute this: + // *mBegin = value_type(); + + return *mBegin; // Same as return front(); + } + + + template + void ring_buffer::pop_front() + { + EASTL_ASSERT(mBegin != mEnd); // We assume that mEnd > mBegin and thus that there is something to pop. + + if(++mBegin == c.end()) + mBegin = c.begin(); + --mSize; + } + + + template + typename ring_buffer::reference + ring_buffer::operator[](size_type n) + { + // return *(begin() + n); // Can't use this because not all iterators support operator+. + + // This should compile to code that is nearly as efficient as that above. + // The primary difference is the possible generation of a temporary in this case. + iterator temp(begin()); + eastl::advance(temp, n); + return *(temp.mContainerIterator); + } + + + template + typename ring_buffer::const_reference + ring_buffer::operator[](size_type n) const + { + // return *(begin() + n); // Can't use this because not all iterators support operator+. + + // This should compile to code that is nearly as efficient as that above. + // The primary difference is the possible generation of a temporary in this case. + const_iterator temp(begin()); + eastl::advance(temp, n); + return *(temp.mContainerIterator); + } + + + template + typename ring_buffer::iterator + ring_buffer::insert(const_iterator position, const value_type& value) + { + // To consider: It would be faster if we could tell that position was in the first + // half of the container and instead of moving things after the position back, + // we could move things before the position forward. + + iterator afterEnd(end()); + iterator beforeEnd(afterEnd); + + ++afterEnd; + + if(afterEnd.mContainerIterator == mBegin) // If we are at full capacity... + --beforeEnd; + else + push_back(); + + iterator itPosition(position.mpContainer, position.mContainerIterator); // We merely copy from const_iterator to iterator. + eastl::copy_backward(itPosition, beforeEnd, end()); + *itPosition = value; + + return itPosition; + } + + + template + void ring_buffer::insert(const_iterator position, size_type n, const value_type& value) + { + // To do: This can be improved with a smarter version. However, + // this is a little tricky because we need to deal with the case + // whereby n is greater than the size of the container itself. + while(n--) + insert(position, value); + } + + + template + void ring_buffer::insert(const_iterator position, std::initializer_list ilist) + { + insert(position, ilist.begin(), ilist.end()); + } + + + template + template + void ring_buffer::insert(const_iterator position, InputIterator first, InputIterator last) + { + // To do: This can possibly be improved with a smarter version. + // However, this can be tricky if distance(first, last) is greater + // than the size of the container itself. + for(; first != last; ++first, ++position) + insert(position, *first); + } + + + template + typename ring_buffer::iterator + ring_buffer::erase(const_iterator position) + { + iterator itPosition(position.mpContainer, position.mContainerIterator); // We merely copy from const_iterator to iterator. + iterator iNext(itPosition); + + eastl::copy(++iNext, end(), itPosition); + pop_back(); + + return itPosition; + } + + + template + typename ring_buffer::iterator + ring_buffer::erase(const_iterator first, const_iterator last) + { + iterator itFirst(first.mpContainer, first.mContainerIterator); // We merely copy from const_iterator to iterator. + iterator itLast(last.mpContainer, last.mContainerIterator); + + typename iterator::difference_type d = eastl::distance(itFirst, itLast); + + eastl::copy(itLast, end(), itFirst); + + while(d--) // To do: improve this implementation. + pop_back(); + + return itFirst; + } + + + template + typename ring_buffer::reverse_iterator + ring_buffer::erase(const_reverse_iterator position) + { + return reverse_iterator(erase((++position).base())); + } + + + template + typename ring_buffer::reverse_iterator + ring_buffer::erase(const_reverse_iterator first, const_reverse_iterator last) + { + // Version which erases in order from first to last. + // difference_type i(first.base() - last.base()); + // while(i--) + // first = erase(first); + // return first; + + // Version which erases in order from last to first, but is slightly more efficient: + return reverse_iterator(erase((++last).base(), (++first).base())); + } + + + template + void ring_buffer::clear() + { + // Don't clear the container; we use its valid data for our elements. + mBegin = c.begin(); + mEnd = c.begin(); + mSize = 0; + } + + + template + typename ring_buffer::container_type& + ring_buffer::get_container() + { + return c; + } + + + template + const typename ring_buffer::container_type& + ring_buffer::get_container() const + { + return c; + } + + + template + inline bool ring_buffer::validate() const + { + if(!c.validate()) // This requires that the container implement the validate function. That pretty much + return false; // means that the container is an EASTL container and not a std STL container. + + if(c.empty()) // c must always have a size of at least 1, as even an empty ring_buffer has an unused terminating element. + return false; + + if(size() > capacity()) + return false; + + if((validate_iterator(begin()) & (isf_valid | isf_current)) != (isf_valid | isf_current)) + return false; + + if((validate_iterator(end()) & (isf_valid | isf_current)) != (isf_valid | isf_current)) + return false; + + // Verify that the size calculation is consistent. + size_type n = 0; + for(const_iterator i(begin()), iEnd(end()); i != iEnd; ++i) + ++n; + if(n != mSize) + return false; + + return true; + } + + + template + inline int ring_buffer::validate_iterator(const_iterator i) const + { + // To do: Replace this with a more efficient implementation if possible. + + 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 + inline bool operator==(const ring_buffer& a, const ring_buffer& b) + { + return (a.size() == b.size()) && (a.c == b.c); + } + + + template + inline bool operator<(const ring_buffer& a, const ring_buffer& b) + { + const typename ring_buffer::size_type sizeA = a.size(); + const typename ring_buffer::size_type sizeB = b.size(); + + if(sizeA == sizeB) + return (a.c < b.c); + return sizeA < sizeB; + } + + + template + inline bool operator!=(const ring_buffer& a, const ring_buffer& b) + { + return !(a == b); + } + + + template + inline bool operator>(const ring_buffer& a, const ring_buffer& b) + { + return (b < a); + } + + + template + inline bool operator<=(const ring_buffer& a, const ring_buffer& b) + { + return !(b < a); + } + + + template + inline bool operator>=(const ring_buffer& a, const ring_buffer& b) + { + return !(a < b); + } + + + template + inline void swap(ring_buffer& a, ring_buffer& b) + { + a.swap(b); + } + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/bonus/sort_extra.h b/lib/EASTL/include/EASTL/bonus/sort_extra.h new file mode 100644 index 000000000..5f9a0c465 --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/sort_extra.h @@ -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 +#include +#include +#include +#include +#include // For backwards compatibility due to sorts moved from here to sort.h. +#include + +#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 + 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 + inline void selection_sort(ForwardIterator first, ForwardIterator last) + { + typedef eastl::less::value_type> Less; + + eastl::selection_sort(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 + 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 + inline void shaker_sort(BidirectionalIterator first, BidirectionalIterator last) + { + typedef eastl::less::value_type> Less; + + eastl::shaker_sort(first, last, Less()); + } + + + + /// bucket_sort + /// + /// Implements the BucketSort algorithm. + /// + /// Example usage: + /// const size_t kElementRange = 32; + /// vector intArray(1000); + /// + /// for(int i = 0; i < 1000; i++) + /// intArray[i] = rand() % kElementRange; + /// + /// vector< vector > bucketArray(kElementRange); + /// bucket_sort(intArray.begin(), intArray.end(), bucketArray, eastl::hash_use_self()); + /// + template + 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 + 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 + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/bonus/tuple_vector.h b/lib/EASTL/include/EASTL/bonus/tuple_vector.h new file mode 100644 index 000000000..7123c57f1 --- /dev/null +++ b/lib/EASTL/include/EASTL/bonus/tuple_vector.h @@ -0,0 +1,1592 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// 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. +// +// Consult doc/Bonus/tuple_vector_readme.md for more information. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_TUPLEVECTOR_H +#define EASTL_TUPLEVECTOR_H + +#include +#include +#include +#include +#include +#include + +#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(4244) // warning C4244: 'conversion from '___' to '___', possible loss of data +EA_DISABLE_VC_WARNING(4623) // warning C4623: default constructor was implicitly defined as deleted +EA_DISABLE_VC_WARNING(4625) // warning C4625: copy constructor was implicitly defined as deleted +EA_DISABLE_VC_WARNING(4510) // warning C4510: default constructor could not be generated + +namespace eastl +{ + /// EASTL_TUPLE_VECTOR_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_TUPLE_VECTOR_DEFAULT_NAME + #define EASTL_TUPLE_VECTOR_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " tuple-vector" // Unless the user overrides something, this is "EASTL tuple-vector". + #endif + + + /// EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR + #define EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR allocator_type(EASTL_TUPLE_VECTOR_DEFAULT_NAME) + #endif + +namespace TupleVecInternal +{ + +// forward declarations +template +struct tuplevec_element; + +template +using tuplevec_element_t = typename tuplevec_element::type; + +template +struct TupleTypes {}; + +template +class TupleVecImpl; + +template +struct TupleRecurser; + +template +struct TupleIndexRecurser; + +template +struct TupleVecLeaf; + +template +struct TupleVecIter; + +// tuplevec_element helper to be able to isolate a type given an index +template +struct tuplevec_element +{ + static_assert(I != I, "tuplevec_element index out of range"); +}; + +template +struct tuplevec_element<0, T, Ts...> +{ + tuplevec_element() = delete; // tuplevec_element should only be used for compile-time assistance, and never be instantiated + typedef T type; +}; + +template +struct tuplevec_element +{ + typedef tuplevec_element_t type; +}; + +// attempt to isolate index given a type +template +struct tuplevec_index +{ +}; + +template +struct tuplevec_index> +{ + typedef void DuplicateTypeCheck; + tuplevec_index() = delete; // tuplevec_index should only be used for compile-time assistance, and never be instantiated + static const eastl_size_t index = 0; +}; + +template +struct tuplevec_index> +{ + typedef int DuplicateTypeCheck; + static_assert(is_void>::DuplicateTypeCheck>::value, "duplicate type T in tuple_vector::get(); unique types must be provided in declaration, or only use get()"); + + static const eastl_size_t index = 0; +}; + +template +struct tuplevec_index> +{ + typedef typename tuplevec_index>::DuplicateTypeCheck DuplicateTypeCheck; + static const eastl_size_t index = tuplevec_index>::index + 1; +}; + +template +struct tuplevec_index> : public tuplevec_index> +{ +}; + + +// helper to calculate the layout of the allocations for the tuple of types (esp. to take alignment into account) +template <> +struct TupleRecurser<> +{ + typedef eastl_size_t size_type; + + // This class should never be instantiated. This is just a helper for working with static functions when anonymous functions don't work + // and provide some other utilities + TupleRecurser() = delete; + + static EA_CONSTEXPR size_type GetTotalAlignment() + { + return 0; + } + + static EA_CONSTEXPR size_type GetTotalAllocationSize(size_type capacity, size_type offset) + { + EA_UNUSED(capacity); + return offset; + } + + template + static pair DoAllocate(TupleVecImpl &vec, void** ppNewLeaf, size_type capacity, size_type offset) + { + EA_UNUSED(ppNewLeaf); + + // If n is zero, then we allocate no memory and just return NULL. + // This is fine, as our default ctor initializes with NULL pointers. + size_type alignment = TupleRecurser::GetTotalAlignment(); + void* ptr = capacity ? allocate_memory(vec.get_allocator(), offset, alignment, 0) : nullptr; + + #if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY((size_t)ptr & (alignment - 1)) != 0) + { + EASTL_FAIL_MSG("tuple_vector::DoAllocate -- memory not alignment at requested alignment"); + } + #endif + + return make_pair(ptr, offset); + } + + template + static void SetNewData(TupleVecImplType &vec, void* pData, size_type capacity, size_type offset) + { + EA_UNUSED(vec); + EA_UNUSED(pData); + EA_UNUSED(capacity); + EA_UNUSED(offset); + } +}; + +template +struct TupleRecurser : TupleRecurser +{ + typedef eastl_size_t size_type; + + static EA_CONSTEXPR size_type GetTotalAlignment() + { + return max(static_cast(alignof(T)), TupleRecurser::GetTotalAlignment()); + } + + static EA_CONSTEXPR size_type GetTotalAllocationSize(size_type capacity, size_type offset) + { + return TupleRecurser::GetTotalAllocationSize(capacity, CalculateAllocationSize(offset, capacity)); + } + + template + static pair DoAllocate(TupleVecImpl &vec, void** ppNewLeaf, size_type capacity, size_type offset) + { + size_type allocationOffset = CalculatAllocationOffset(offset); + size_type allocationSize = CalculateAllocationSize(offset, capacity); + pair allocation = TupleRecurser::template DoAllocate( + vec, ppNewLeaf, capacity, allocationSize); + ppNewLeaf[I] = (void*)((uintptr_t)(allocation.first) + allocationOffset); + return allocation; + } + + template + static void SetNewData(TupleVecImplType &vec, void* pData, size_type capacity, size_type offset) + { + size_type allocationOffset = CalculatAllocationOffset(offset); + size_type allocationSize = CalculateAllocationSize(offset, capacity); + vec.TupleVecLeaf::mpData = (T*)((uintptr_t)pData + allocationOffset); + TupleRecurser::template SetNewData(vec, pData, capacity, allocationSize); + } + +private: + static EA_CONSTEXPR size_type CalculateAllocationSize(size_type offset, size_type capacity) + { + return CalculatAllocationOffset(offset) + sizeof(T) * capacity; + } + + static EA_CONSTEXPR size_type CalculatAllocationOffset(size_type offset) { return (offset + alignof(T) - 1) & (~alignof(T) + 1); } +}; + +template +struct TupleVecLeaf +{ + typedef eastl_size_t size_type; + + void DoUninitializedMoveAndDestruct(const size_type begin, const size_type end, T* pDest) + { + T* pBegin = mpData + begin; + T* pEnd = mpData + end; + eastl::uninitialized_move_ptr_if_noexcept(pBegin, pEnd, pDest); + eastl::destruct(pBegin, pEnd); + } + + void DoInsertAndFill(size_type pos, size_type n, size_type numElements, const T& arg) + { + T* pDest = mpData + pos; + T* pDataEnd = mpData + numElements; + const T temp = arg; + const size_type nExtra = (numElements - pos); + if (n < nExtra) // If the inserted values are entirely within initialized memory (i.e. are before mpEnd)... + { + eastl::uninitialized_move_ptr(pDataEnd - n, pDataEnd, pDataEnd); + eastl::move_backward(pDest, pDataEnd - n, pDataEnd); // We need move_backward because of potential overlap issues. + eastl::fill(pDest, pDest + n, temp); + } + else + { + eastl::uninitialized_fill_n_ptr(pDataEnd, n - nExtra, temp); + eastl::uninitialized_move_ptr(pDest, pDataEnd, pDataEnd + n - nExtra); + eastl::fill(pDest, pDataEnd, temp); + } + } + + void DoInsertRange(T* pSrcBegin, T* pSrcEnd, T* pDestBegin, size_type numDataElements) + { + size_type pos = pDestBegin - mpData; + size_type n = pSrcEnd - pSrcBegin; + T* pDataEnd = mpData + numDataElements; + const size_type nExtra = numDataElements - pos; + if (n < nExtra) // If the inserted values are entirely within initialized memory (i.e. are before mpEnd)... + { + eastl::uninitialized_move_ptr(pDataEnd - n, pDataEnd, pDataEnd); + eastl::move_backward(pDestBegin, pDataEnd - n, pDataEnd); // We need move_backward because of potential overlap issues. + eastl::copy(pSrcBegin, pSrcEnd, pDestBegin); + } + else + { + eastl::uninitialized_copy(pSrcEnd - (n - nExtra), pSrcEnd, pDataEnd); + eastl::uninitialized_move_ptr(pDestBegin, pDataEnd, pDataEnd + n - nExtra); + eastl::copy(pSrcBegin, pSrcEnd - (n - nExtra), pDestBegin); + } + } + + void DoInsertValue(size_type pos, size_type numElements, T&& arg) + { + T* pDest = mpData + pos; + T* pDataEnd = mpData + numElements; + + eastl::uninitialized_move_ptr(pDataEnd - 1, pDataEnd, pDataEnd); + eastl::move_backward(pDest, pDataEnd - 1, pDataEnd); // We need move_backward because of potential overlap issues. + eastl::destruct(pDest); + ::new (pDest) T(eastl::forward(arg)); + } + + T* mpData = nullptr; +}; + +// swallow allows for parameter pack expansion of arguments as means of expanding operations performed +// if a void function is used for operation expansion, it should be wrapped in (..., 0) so that the compiler +// thinks it has a parameter to pass into the function +template +void swallow(Ts&&...) { } + +inline bool variadicAnd(bool cond) { return cond; } + +inline bool variadicAnd(bool cond, bool conds...) { return cond && variadicAnd(conds); } + +// Helper struct to check for strict compatibility between two iterators, whilst still allowing for +// conversion between TupleVecImpl::iterator and TupleVecImpl::const_iterator. +template +struct TupleVecIterCompatibleImpl : public false_type { }; + +template<> +struct TupleVecIterCompatibleImpl, TupleTypes<>> : public true_type { }; + +template +struct TupleVecIterCompatibleImpl, TupleTypes> : public integral_constant, TupleTypes>::value && + is_same::type, typename remove_const::type>::value > +{ }; + +template +struct TupleVecIterCompatible; + +template +struct TupleVecIterCompatible, TupleTypes> : + public TupleVecIterCompatibleImpl, TupleTypes> +{ }; + +// The Iterator operates by storing a persistent index internally, +// and resolving the tuple of pointers to the various parts of the original tupleVec when dereferenced. +// While resolving the tuple is a non-zero operation, it consistently generated better code than the alternative of +// storing - and harmoniously updating on each modification - a full tuple of pointers to the tupleVec's data +template +struct TupleVecIter, Ts...> + : public iterator, eastl_size_t, tuple, tuple> +{ +private: + typedef TupleVecIter, Ts...> this_type; + typedef eastl_size_t size_type; + + typedef iterator, eastl_size_t, tuple, tuple> iter_type; + + template + friend struct TupleVecIter; + + template + friend class TupleVecImpl; + + template + friend class move_iterator; +public: + typedef typename iter_type::iterator_category iterator_category; + typedef typename iter_type::value_type value_type; + typedef typename iter_type::difference_type difference_type; + typedef typename iter_type::pointer pointer; + typedef typename iter_type::reference reference; + + TupleVecIter() = default; + + template + TupleVecIter(VecImplType* tupleVec, size_type index) + : mIndex(index) + , mpData{(void*)tupleVec->TupleVecLeaf::mpData...} + { } + + template , TupleTypes>::value, bool>::type> + TupleVecIter(const TupleVecIter& other) + : mIndex(other.mIndex) + , mpData{other.mpData[Indices]...} + { + } + + bool operator==(const TupleVecIter& other) const { return mIndex == other.mIndex && mpData[0] == other.mpData[0]; } + bool operator!=(const TupleVecIter& other) const { return mIndex != other.mIndex || mpData[0] != other.mpData[0]; } + reference operator*() const { return MakeReference(); } + + this_type& operator++() { ++mIndex; return *this; } + this_type operator++(int) + { + this_type temp = *this; + ++mIndex; + return temp; + } + + this_type& operator--() { --mIndex; return *this; } + this_type operator--(int) + { + this_type temp = *this; + --mIndex; + return temp; + } + + this_type& operator+=(difference_type n) { mIndex += n; return *this; } + this_type operator+(difference_type n) const + { + this_type temp = *this; + return temp += n; + } + friend this_type operator+(difference_type n, const this_type& rhs) + { + this_type temp = rhs; + return temp += n; + } + + this_type& operator-=(difference_type n) { mIndex -= n; return *this; } + this_type operator-(difference_type n) const + { + this_type temp = *this; + return temp -= n; + } + friend this_type operator-(difference_type n, const this_type& rhs) + { + this_type temp = rhs; + return temp -= n; + } + + difference_type operator-(const this_type& rhs) const { return mIndex - rhs.mIndex; } + bool operator<(const this_type& rhs) const { return mIndex < rhs.mIndex; } + bool operator>(const this_type& rhs) const { return mIndex > rhs.mIndex; } + bool operator>=(const this_type& rhs) const { return mIndex >= rhs.mIndex; } + bool operator<=(const this_type& rhs) const { return mIndex <= rhs.mIndex; } + + reference operator[](const size_type n) const + { + return *(*this + n); + } + +private: + + value_type MakeValue() const + { + return value_type(((Ts*)mpData[Indices])[mIndex]...); + } + + reference MakeReference() const + { + return reference(((Ts*)mpData[Indices])[mIndex]...); + } + + pointer MakePointer() const + { + return pointer(&((Ts*)mpData[Indices])[mIndex]...); + } + + size_type mIndex = 0; + const void* mpData[sizeof...(Ts)]; +}; + +// TupleVecImpl +template +class TupleVecImpl, Ts...> : public TupleVecLeaf... +{ + typedef Allocator allocator_type; + typedef index_sequence index_sequence_type; + typedef TupleVecImpl this_type; + typedef TupleVecImpl const_this_type; + +public: + typedef TupleVecInternal::TupleVecIter iterator; + typedef TupleVecInternal::TupleVecIter const_iterator; + typedef eastl::reverse_iterator reverse_iterator; + typedef eastl::reverse_iterator const_reverse_iterator; + typedef eastl_size_t size_type; + typedef eastl::tuple value_tuple; + typedef eastl::tuple reference_tuple; + typedef eastl::tuple const_reference_tuple; + typedef eastl::tuple ptr_tuple; + typedef eastl::tuple const_ptr_tuple; + typedef eastl::tuple rvalue_tuple; + + TupleVecImpl() + : mDataSizeAndAllocator(0, EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR) + {} + + TupleVecImpl(const allocator_type& allocator) + : mDataSizeAndAllocator(0, allocator) + {} + + TupleVecImpl(this_type&& x) + : mDataSizeAndAllocator(0, eastl::move(x.get_allocator())) + { + swap(x); + } + + TupleVecImpl(this_type&& x, const Allocator& allocator) + : mDataSizeAndAllocator(0, allocator) + { + if (get_allocator() == x.get_allocator()) // If allocators are equivalent, then we can safely swap member-by-member + { + swap(x); + } + else + { + this_type temp(eastl::move(*this)); + temp.swap(x); + } + } + + TupleVecImpl(const this_type& x) + : mDataSizeAndAllocator(0, x.get_allocator()) + { + DoInitFromIterator(x.begin(), x.end()); + } + + template + TupleVecImpl(const TupleVecImpl& x, const Allocator& allocator) + : mDataSizeAndAllocator(0, allocator) + { + DoInitFromIterator(x.begin(), x.end()); + } + + template + TupleVecImpl(move_iterator begin, move_iterator end, const allocator_type& allocator = EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR) + : mDataSizeAndAllocator(0, allocator) + { + DoInitFromIterator(begin, end); + } + + TupleVecImpl(const_iterator begin, const_iterator end, const allocator_type& allocator = EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR) + : mDataSizeAndAllocator(0, allocator ) + { + DoInitFromIterator(begin, end); + } + + TupleVecImpl(size_type n, const allocator_type& allocator = EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR) + : mDataSizeAndAllocator(0, allocator) + { + DoInitDefaultFill(n); + } + + TupleVecImpl(size_type n, const Ts&... args) + : mDataSizeAndAllocator(0, EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR) + { + DoInitFillArgs(n, args...); + } + + TupleVecImpl(size_type n, const Ts&... args, const allocator_type& allocator) + : mDataSizeAndAllocator(0, allocator) + { + DoInitFillArgs(n, args...); + } + + TupleVecImpl(size_type n, const_reference_tuple tup, const allocator_type& allocator = EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR) + : mDataSizeAndAllocator(0, allocator) + { + DoInitFillTuple(n, tup); + } + + TupleVecImpl(const value_tuple* first, const value_tuple* last, const allocator_type& allocator = EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR) + : mDataSizeAndAllocator(0, allocator) + { + DoInitFromTupleArray(first, last); + } + + TupleVecImpl(std::initializer_list iList, const allocator_type& allocator = EASTL_TUPLE_VECTOR_DEFAULT_ALLOCATOR) + : mDataSizeAndAllocator(0, allocator) + { + DoInitFromTupleArray(iList.begin(), iList.end()); + } + +protected: + // ctor to provide a pre-allocated field of data that the container will own, specifically for fixed_tuple_vector + TupleVecImpl(const allocator_type& allocator, void* pData, size_type capacity, size_type dataSize) + : mpData(pData), mNumCapacity(capacity), mDataSizeAndAllocator(dataSize, allocator) + { + TupleRecurser::template SetNewData(*this, mpData, mNumCapacity, 0); + } + +public: + ~TupleVecImpl() + { + swallow((eastl::destruct(TupleVecLeaf::mpData, TupleVecLeaf::mpData + mNumElements), 0)...); + if (mpData) + EASTLFree(get_allocator(), mpData, internalDataSize()); + } + + void assign(size_type n, const Ts&... args) + { + if (n > mNumCapacity) + { + this_type temp(n, args..., get_allocator()); // We have little choice but to reallocate with new memory. + swap(temp); + } + else if (n > mNumElements) // If n > mNumElements ... + { + size_type oldNumElements = mNumElements; + swallow((eastl::fill(TupleVecLeaf::mpData, TupleVecLeaf::mpData + oldNumElements, args), 0)...); + swallow((eastl::uninitialized_fill_ptr(TupleVecLeaf::mpData + oldNumElements, + TupleVecLeaf::mpData + n, args), 0)...); + mNumElements = n; + } + else // else 0 <= n <= mNumElements + { + swallow((eastl::fill(TupleVecLeaf::mpData, TupleVecLeaf::mpData + n, args), 0)...); + erase(begin() + n, end()); + } + } + + void assign(const_iterator first, const_iterator last) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(!validate_iterator_pair(first, last))) + EASTL_FAIL_MSG("tuple_vector::assign -- invalid iterator pair"); +#endif + size_type newNumElements = last - first; + if (newNumElements > mNumCapacity) + { + this_type temp(first, last, get_allocator()); + swap(temp); + } + else + { + const void* ppOtherData[sizeof...(Ts)] = {first.mpData[Indices]...}; + size_type firstIdx = first.mIndex; + size_type lastIdx = last.mIndex; + if (newNumElements > mNumElements) // If n > mNumElements ... + { + size_type oldNumElements = mNumElements; + swallow((eastl::copy((Ts*)(ppOtherData[Indices]) + firstIdx, + (Ts*)(ppOtherData[Indices]) + firstIdx + oldNumElements, + TupleVecLeaf::mpData), 0)...); + swallow((eastl::uninitialized_copy_ptr((Ts*)(ppOtherData[Indices]) + firstIdx + oldNumElements, + (Ts*)(ppOtherData[Indices]) + lastIdx, + TupleVecLeaf::mpData + oldNumElements), 0)...); + mNumElements = newNumElements; + } + else // else 0 <= n <= mNumElements + { + swallow((eastl::copy((Ts*)(ppOtherData[Indices]) + firstIdx, (Ts*)(ppOtherData[Indices]) + lastIdx, + TupleVecLeaf::mpData), 0)...); + erase(begin() + newNumElements, end()); + } + } + } + + void assign(const value_tuple* first, const value_tuple* last) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(first > last || first == nullptr || last == nullptr)) + EASTL_FAIL_MSG("tuple_vector::assign from tuple array -- invalid ptrs"); +#endif + size_type newNumElements = last - first; + if (newNumElements > mNumCapacity) + { + this_type temp(first, last, get_allocator()); + swap(temp); + } + else + { + if (newNumElements > mNumElements) // If n > mNumElements ... + { + size_type oldNumElements = mNumElements; + + DoCopyFromTupleArray(begin(), begin() + oldNumElements, first); + DoUninitializedCopyFromTupleArray(begin() + oldNumElements, begin() + newNumElements, first + oldNumElements); + mNumElements = newNumElements; + } + else // else 0 <= n <= mNumElements + { + DoCopyFromTupleArray(begin(), begin() + newNumElements, first); + erase(begin() + newNumElements, end()); + } + } + } + + reference_tuple push_back() + { + size_type oldNumElements = mNumElements; + size_type newNumElements = oldNumElements + 1; + size_type oldNumCapacity = mNumCapacity; + mNumElements = newNumElements; + DoGrow(oldNumElements, oldNumCapacity, newNumElements); + swallow(::new(TupleVecLeaf::mpData + oldNumElements) Ts()...); + return back(); + } + + void push_back(const Ts&... args) + { + size_type oldNumElements = mNumElements; + size_type newNumElements = oldNumElements + 1; + size_type oldNumCapacity = mNumCapacity; + mNumElements = newNumElements; + DoGrow(oldNumElements, oldNumCapacity, newNumElements); + swallow(::new(TupleVecLeaf::mpData + oldNumElements) Ts(args)...); + } + + void push_back_uninitialized() + { + size_type oldNumElements = mNumElements; + size_type newNumElements = oldNumElements + 1; + size_type oldNumCapacity = mNumCapacity; + mNumElements = newNumElements; + DoGrow(oldNumElements, oldNumCapacity, newNumElements); + } + + reference_tuple emplace_back(Ts&&... args) + { + size_type oldNumElements = mNumElements; + size_type newNumElements = oldNumElements + 1; + size_type oldNumCapacity = mNumCapacity; + mNumElements = newNumElements; + DoGrow(oldNumElements, oldNumCapacity, newNumElements); + swallow(::new(TupleVecLeaf::mpData + oldNumElements) Ts(eastl::forward(args))...); + return back(); + } + + iterator emplace(const_iterator pos, Ts&&... args) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(validate_iterator(pos) == isf_none)) + EASTL_FAIL_MSG("tuple_vector::emplace -- invalid iterator"); +#endif + size_type firstIdx = pos - cbegin(); + size_type oldNumElements = mNumElements; + size_type newNumElements = mNumElements + 1; + size_type oldNumCapacity = mNumCapacity; + mNumElements = newNumElements; + if (newNumElements > oldNumCapacity || firstIdx != oldNumElements) + { + if (newNumElements > oldNumCapacity) + { + const size_type newCapacity = eastl::max(GetNewCapacity(oldNumCapacity), newNumElements); + + void* ppNewLeaf[sizeof...(Ts)]; + pair allocation = TupleRecurser::template DoAllocate( + *this, ppNewLeaf, newCapacity, 0); + + swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( + 0, firstIdx, (Ts*)ppNewLeaf[Indices]), 0)...); + swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( + firstIdx, oldNumElements, (Ts*)ppNewLeaf[Indices] + firstIdx + 1), 0)...); + swallow(::new ((Ts*)ppNewLeaf[Indices] + firstIdx) Ts(eastl::forward(args))...); + swallow(TupleVecLeaf::mpData = (Ts*)ppNewLeaf[Indices]...); + + EASTLFree(get_allocator(), mpData, internalDataSize()); + mpData = allocation.first; + mNumCapacity = newCapacity; + internalDataSize() = allocation.second; + } + else + { + swallow((TupleVecLeaf::DoInsertValue(firstIdx, oldNumElements, eastl::forward(args)), 0)...); + } + } + else + { + swallow(::new (TupleVecLeaf::mpData + oldNumElements) Ts(eastl::forward(args))...); + } + return begin() + firstIdx; + } + + iterator insert(const_iterator pos, size_type n, const Ts&... args) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(validate_iterator(pos) == isf_none)) + EASTL_FAIL_MSG("tuple_vector::insert -- invalid iterator"); +#endif + size_type firstIdx = pos - cbegin(); + size_type lastIdx = firstIdx + n; + size_type oldNumElements = mNumElements; + size_type newNumElements = mNumElements + n; + size_type oldNumCapacity = mNumCapacity; + mNumElements = newNumElements; + if (newNumElements > oldNumCapacity || firstIdx != oldNumElements) + { + if (newNumElements > oldNumCapacity) + { + const size_type newCapacity = eastl::max(GetNewCapacity(oldNumCapacity), newNumElements); + + void* ppNewLeaf[sizeof...(Ts)]; + pair allocation = TupleRecurser::template DoAllocate( + *this, ppNewLeaf, newCapacity, 0); + + swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( + 0, firstIdx, (Ts*)ppNewLeaf[Indices]), 0)...); + swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( + firstIdx, oldNumElements, (Ts*)ppNewLeaf[Indices] + lastIdx), 0)...); + swallow((eastl::uninitialized_fill_ptr((Ts*)ppNewLeaf[Indices] + firstIdx, (Ts*)ppNewLeaf[Indices] + lastIdx, args), 0)...); + swallow(TupleVecLeaf::mpData = (Ts*)ppNewLeaf[Indices]...); + + EASTLFree(get_allocator(), mpData, internalDataSize()); + mpData = allocation.first; + mNumCapacity = newCapacity; + internalDataSize() = allocation.second; + } + else + { + swallow((TupleVecLeaf::DoInsertAndFill(firstIdx, n, oldNumElements, args), 0)...); + } + } + else + { + swallow((eastl::uninitialized_fill_ptr(TupleVecLeaf::mpData + oldNumElements, + TupleVecLeaf::mpData + newNumElements, args), 0)...); + } + return begin() + firstIdx; + } + + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(validate_iterator(pos) == isf_none)) + EASTL_FAIL_MSG("tuple_vector::insert -- invalid iterator"); + if (EASTL_UNLIKELY(!validate_iterator_pair(first, last))) + EASTL_FAIL_MSG("tuple_vector::insert -- invalid iterator pair"); +#endif + size_type posIdx = pos - cbegin(); + size_type firstIdx = first.mIndex; + size_type lastIdx = last.mIndex; + size_type numToInsert = last - first; + size_type oldNumElements = mNumElements; + size_type newNumElements = oldNumElements + numToInsert; + size_type oldNumCapacity = mNumCapacity; + mNumElements = newNumElements; + const void* ppOtherData[sizeof...(Ts)] = {first.mpData[Indices]...}; + if (newNumElements > oldNumCapacity || posIdx != oldNumElements) + { + if (newNumElements > oldNumCapacity) + { + const size_type newCapacity = eastl::max(GetNewCapacity(oldNumCapacity), newNumElements); + + void* ppNewLeaf[sizeof...(Ts)]; + pair allocation = TupleRecurser::template DoAllocate( + *this, ppNewLeaf, newCapacity, 0); + + swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( + 0, posIdx, (Ts*)ppNewLeaf[Indices]), 0)...); + swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( + posIdx, oldNumElements, (Ts*)ppNewLeaf[Indices] + posIdx + numToInsert), 0)...); + swallow((eastl::uninitialized_copy_ptr((Ts*)(ppOtherData[Indices]) + firstIdx, + (Ts*)(ppOtherData[Indices]) + lastIdx, + (Ts*)ppNewLeaf[Indices] + posIdx), 0)...); + swallow(TupleVecLeaf::mpData = (Ts*)ppNewLeaf[Indices]...); + + EASTLFree(get_allocator(), mpData, internalDataSize()); + mpData = allocation.first; + mNumCapacity = newCapacity; + internalDataSize() = allocation.second; + } + else + { + swallow((TupleVecLeaf::DoInsertRange( + (Ts*)(ppOtherData[Indices]) + firstIdx, (Ts*)(ppOtherData[Indices]) + lastIdx, + TupleVecLeaf::mpData + posIdx, oldNumElements), 0)...); + } + } + else + { + swallow((eastl::uninitialized_copy_ptr((Ts*)(ppOtherData[Indices]) + firstIdx, + (Ts*)(ppOtherData[Indices]) + lastIdx, + TupleVecLeaf::mpData + posIdx), 0)...); + } + return begin() + posIdx; + } + + iterator insert(const_iterator pos, const value_tuple* first, const value_tuple* last) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(validate_iterator(pos) == isf_none)) + EASTL_FAIL_MSG("tuple_vector::insert -- invalid iterator"); + if (EASTL_UNLIKELY(first > last || first == nullptr || last == nullptr)) + EASTL_FAIL_MSG("tuple_vector::insert -- invalid source pointers"); +#endif + size_type posIdx = pos - cbegin(); + size_type numToInsert = last - first; + size_type oldNumElements = mNumElements; + size_type newNumElements = oldNumElements + numToInsert; + size_type oldNumCapacity = mNumCapacity; + mNumElements = newNumElements; + if (newNumElements > oldNumCapacity || posIdx != oldNumElements) + { + if (newNumElements > oldNumCapacity) + { + const size_type newCapacity = eastl::max(GetNewCapacity(oldNumCapacity), newNumElements); + + void* ppNewLeaf[sizeof...(Ts)]; + pair allocation = TupleRecurser::template DoAllocate( + *this, ppNewLeaf, newCapacity, 0); + + swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( + 0, posIdx, (Ts*)ppNewLeaf[Indices]), 0)...); + swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct( + posIdx, oldNumElements, (Ts*)ppNewLeaf[Indices] + posIdx + numToInsert), 0)...); + + swallow(TupleVecLeaf::mpData = (Ts*)ppNewLeaf[Indices]...); + + // Do this after mpData is updated so that we can use new iterators + DoUninitializedCopyFromTupleArray(begin() + posIdx, begin() + posIdx + numToInsert, first); + + EASTLFree(get_allocator(), mpData, internalDataSize()); + mpData = allocation.first; + mNumCapacity = newCapacity; + internalDataSize() = allocation.second; + } + else + { + const size_type nExtra = oldNumElements - posIdx; + void* ppDataEnd[sizeof...(Ts)] = { (void*)(TupleVecLeaf::mpData + oldNumElements)... }; + void* ppDataBegin[sizeof...(Ts)] = { (void*)(TupleVecLeaf::mpData + posIdx)... }; + if (numToInsert < nExtra) // If the inserted values are entirely within initialized memory (i.e. are before mpEnd)... + { + swallow((eastl::uninitialized_move_ptr((Ts*)ppDataEnd[Indices] - numToInsert, + (Ts*)ppDataEnd[Indices], (Ts*)ppDataEnd[Indices]), 0)...); + // We need move_backward because of potential overlap issues. + swallow((eastl::move_backward((Ts*)ppDataBegin[Indices], + (Ts*)ppDataEnd[Indices] - numToInsert, (Ts*)ppDataEnd[Indices]), 0)...); + + DoCopyFromTupleArray(pos, pos + numToInsert, first); + } + else + { + size_type numToInitialize = numToInsert - nExtra; + swallow((eastl::uninitialized_move_ptr((Ts*)ppDataBegin[Indices], + (Ts*)ppDataEnd[Indices], (Ts*)ppDataEnd[Indices] + numToInitialize), 0)...); + + DoCopyFromTupleArray(pos, begin() + oldNumElements, first); + DoUninitializedCopyFromTupleArray(begin() + oldNumElements, pos + numToInsert, first + nExtra); + } + } + } + else + { + DoUninitializedCopyFromTupleArray(pos, pos + numToInsert, first); + } + return begin() + posIdx; + } + + iterator erase(const_iterator first, const_iterator last) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(validate_iterator(first) == isf_none || validate_iterator(last) == isf_none)) + EASTL_FAIL_MSG("tuple_vector::erase -- invalid iterator"); + if (EASTL_UNLIKELY(!validate_iterator_pair(first, last))) + EASTL_FAIL_MSG("tuple_vector::erase -- invalid iterator pair"); +#endif + if (first != last) + { + size_type firstIdx = first - cbegin(); + size_type lastIdx = last - cbegin(); + size_type oldNumElements = mNumElements; + size_type newNumElements = oldNumElements - (lastIdx - firstIdx); + mNumElements = newNumElements; + swallow((eastl::move(TupleVecLeaf::mpData + lastIdx, + TupleVecLeaf::mpData + oldNumElements, + TupleVecLeaf::mpData + firstIdx), 0)...); + swallow((eastl::destruct(TupleVecLeaf::mpData + newNumElements, + TupleVecLeaf::mpData + oldNumElements), 0)...); + } + return begin() + first.mIndex; + } + + iterator erase_unsorted(const_iterator pos) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(validate_iterator(pos) == isf_none)) + EASTL_FAIL_MSG("tuple_vector::erase_unsorted -- invalid iterator"); +#endif + size_type oldNumElements = mNumElements; + size_type newNumElements = oldNumElements - 1; + mNumElements = newNumElements; + swallow((eastl::move(TupleVecLeaf::mpData + newNumElements, + TupleVecLeaf::mpData + oldNumElements, + TupleVecLeaf::mpData + (pos - begin())), 0)...); + swallow((eastl::destruct(TupleVecLeaf::mpData + newNumElements, + TupleVecLeaf::mpData + oldNumElements), 0)...); + return begin() + pos.mIndex; + } + + void resize(size_type n) + { + size_type oldNumElements = mNumElements; + size_type oldNumCapacity = mNumCapacity; + mNumElements = n; + if (n > oldNumElements) + { + if (n > oldNumCapacity) + { + DoReallocate(oldNumElements, eastl::max(GetNewCapacity(oldNumCapacity), n)); + } + swallow((eastl::uninitialized_default_fill_n(TupleVecLeaf::mpData + oldNumElements, n - oldNumElements), 0)...); + } + else + { + swallow((eastl::destruct(TupleVecLeaf::mpData + n, + TupleVecLeaf::mpData + oldNumElements), 0)...); + } + } + + void resize(size_type n, const Ts&... args) + { + size_type oldNumElements = mNumElements; + size_type oldNumCapacity = mNumCapacity; + mNumElements = n; + if (n > oldNumElements) + { + if (n > oldNumCapacity) + { + DoReallocate(oldNumElements, eastl::max(GetNewCapacity(oldNumCapacity), n)); + } + swallow((eastl::uninitialized_fill_ptr(TupleVecLeaf::mpData + oldNumElements, + TupleVecLeaf::mpData + n, args), 0)...); + } + else + { + swallow((eastl::destruct(TupleVecLeaf::mpData + n, + TupleVecLeaf::mpData + oldNumElements), 0)...); + } + } + + void reserve(size_type n) + { + DoConditionalReallocate(mNumElements, mNumCapacity, n); + } + + void shrink_to_fit() + { + this_type temp(move_iterator(begin()), move_iterator(end()), get_allocator()); + swap(temp); + } + + void clear() EA_NOEXCEPT + { + size_type oldNumElements = mNumElements; + mNumElements = 0; + swallow((eastl::destruct(TupleVecLeaf::mpData, TupleVecLeaf::mpData + oldNumElements), 0)...); + } + + void pop_back() + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(mNumElements <= 0)) + EASTL_FAIL_MSG("tuple_vector::pop_back -- container is empty"); +#endif + size_type oldNumElements = mNumElements--; + swallow((eastl::destruct(TupleVecLeaf::mpData + oldNumElements - 1, + TupleVecLeaf::mpData + oldNumElements), 0)...); + } + + void swap(this_type& x) + { + swallow((eastl::swap(TupleVecLeaf::mpData, x.TupleVecLeaf::mpData), 0)...); + eastl::swap(mpData, x.mpData); + eastl::swap(mNumElements, x.mNumElements); + eastl::swap(mNumCapacity, x.mNumCapacity); + eastl::swap(get_allocator(), x.get_allocator()); + eastl::swap(internalDataSize(), x.internalDataSize()); + } + + void assign(size_type n, const_reference_tuple tup) { assign(n, eastl::get(tup)...); } + void assign(std::initializer_list iList) { assign(iList.begin(), iList.end()); } + + void push_back(Ts&&... args) { emplace_back(eastl::forward(args)...); } + void push_back(const_reference_tuple tup) { push_back(eastl::get(tup)...); } + void push_back(rvalue_tuple tup) { emplace_back(eastl::forward(eastl::get(tup))...); } + + void emplace_back(rvalue_tuple tup) { emplace_back(eastl::forward(eastl::get(tup))...); } + void emplace(const_iterator pos, rvalue_tuple tup) { emplace(pos, eastl::forward(eastl::get(tup))...); } + + iterator insert(const_iterator pos, const Ts&... args) { return insert(pos, 1, args...); } + iterator insert(const_iterator pos, Ts&&... args) { return emplace(pos, eastl::forward(args)...); } + iterator insert(const_iterator pos, rvalue_tuple tup) { return emplace(pos, eastl::forward(eastl::get(tup))...); } + iterator insert(const_iterator pos, const_reference_tuple tup) { return insert(pos, eastl::get(tup)...); } + iterator insert(const_iterator pos, size_type n, const_reference_tuple tup) { return insert(pos, n, eastl::get(tup)...); } + iterator insert(const_iterator pos, std::initializer_list iList) { return insert(pos, iList.begin(), iList.end()); } + + iterator erase(const_iterator pos) { return erase(pos, pos + 1); } + reverse_iterator erase(const_reverse_iterator pos) { return reverse_iterator(erase((pos + 1).base(), (pos).base())); } + reverse_iterator erase(const_reverse_iterator first, const_reverse_iterator last) { return reverse_iterator(erase((last).base(), (first).base())); } + reverse_iterator erase_unsorted(const_reverse_iterator pos) { return reverse_iterator(erase_unsorted((pos + 1).base())); } + + void resize(size_type n, const_reference_tuple tup) { resize(n, eastl::get(tup)...); } + + bool empty() const EA_NOEXCEPT { return mNumElements == 0; } + size_type size() const EA_NOEXCEPT { return mNumElements; } + size_type capacity() const EA_NOEXCEPT { return mNumCapacity; } + + iterator begin() EA_NOEXCEPT { return iterator(this, 0); } + const_iterator begin() const EA_NOEXCEPT { return const_iterator((const_this_type*)(this), 0); } + const_iterator cbegin() const EA_NOEXCEPT { return const_iterator((const_this_type*)(this), 0); } + + iterator end() EA_NOEXCEPT { return iterator(this, size()); } + const_iterator end() const EA_NOEXCEPT { return const_iterator((const_this_type*)(this), size()); } + const_iterator cend() const EA_NOEXCEPT { return const_iterator((const_this_type*)(this), size()); } + + reverse_iterator rbegin() EA_NOEXCEPT { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const EA_NOEXCEPT { return const_reverse_iterator(end()); } + const_reverse_iterator crbegin() const EA_NOEXCEPT { return const_reverse_iterator(end()); } + + reverse_iterator rend() EA_NOEXCEPT { return reverse_iterator(begin()); } + const_reverse_iterator rend() const EA_NOEXCEPT { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const EA_NOEXCEPT { return const_reverse_iterator(begin()); } + + ptr_tuple data() EA_NOEXCEPT { return ptr_tuple(TupleVecLeaf::mpData...); } + const_ptr_tuple data() const EA_NOEXCEPT { return const_ptr_tuple(TupleVecLeaf::mpData...); } + + reference_tuple at(size_type n) + { +#if EASTL_EXCEPTIONS_ENABLED + if (EASTL_UNLIKELY(n >= mNumElements)) + throw std::out_of_range("tuple_vector::at -- out of range"); +#elif EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(n >= mNumElements)) + EASTL_FAIL_MSG("tuple_vector::at -- out of range"); +#endif + return reference_tuple(*(TupleVecLeaf::mpData + n)...); + } + + const_reference_tuple at(size_type n) const + { +#if EASTL_EXCEPTIONS_ENABLED + if (EASTL_UNLIKELY(n >= mNumElements)) + throw std::out_of_range("tuple_vector::at -- out of range"); +#elif EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(n >= mNumElements)) + EASTL_FAIL_MSG("tuple_vector::at -- out of range"); +#endif + return const_reference_tuple(*(TupleVecLeaf::mpData + n)...); + } + + reference_tuple operator[](size_type n) { return at(n); } + const_reference_tuple operator[](size_type n) const { return at(n); } + + reference_tuple front() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(mNumElements == 0)) // We don't allow the user to reference an empty container. + EASTL_FAIL_MSG("tuple_vector::front -- empty vector"); + #else + // We allow the user to reference an empty container. + #endif + + return at(0); + } + + const_reference_tuple front() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(mNumElements == 0)) // We don't allow the user to reference an empty container. + EASTL_FAIL_MSG("tuple_vector::front -- empty vector"); + #else + // We allow the user to reference an empty container. + #endif + + return at(0); + } + + reference_tuple back() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(mNumElements == 0)) // We don't allow the user to reference an empty container. + EASTL_FAIL_MSG("tuple_vector::back -- empty vector"); + #else + // We allow the user to reference an empty container. + #endif + + return at(size() - 1); + } + + const_reference_tuple back() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(mNumElements == 0)) // We don't allow the user to reference an empty container. + EASTL_FAIL_MSG("tuple_vector::back -- empty vector"); + #else + // We allow the user to reference an empty container. + #endif + + return at(size() - 1); + } + + template + tuplevec_element_t* get() + { + typedef tuplevec_element_t Element; + return TupleVecLeaf::mpData; + } + template + const tuplevec_element_t* get() const + { + typedef tuplevec_element_t Element; + return TupleVecLeaf::mpData; + } + + template + T* get() + { + typedef tuplevec_index> Index; + return TupleVecLeaf::mpData; + } + template + const T* get() const + { + typedef tuplevec_index> Index; + return TupleVecLeaf::mpData; + } + + this_type& operator=(const this_type& other) + { + if (this != &other) + { + clear(); + assign(other.begin(), other.end()); + } + return *this; + } + + this_type& operator=(this_type&& other) + { + if (this != &other) + { + swap(other); + } + return *this; + } + + this_type& operator=(std::initializer_list iList) + { + assign(iList.begin(), iList.end()); + return *this; + } + + bool validate() const EA_NOEXCEPT + { + if (mNumElements > mNumCapacity) + return false; + if (!(variadicAnd(mpData <= TupleVecLeaf::mpData...))) + return false; + void* pDataEnd = (void*)((uintptr_t)mpData + internalDataSize()); + if (!(variadicAnd(pDataEnd >= TupleVecLeaf::mpData...))) + return false; + return true; + } + + int validate_iterator(const_iterator iter) const EA_NOEXCEPT + { + if (!(variadicAnd(iter.mpData[Indices] == TupleVecLeaf::mpData...))) + return isf_none; + if (iter.mIndex < mNumElements) + return (isf_valid | isf_current | isf_can_dereference); + if (iter.mIndex <= mNumElements) + return (isf_valid | isf_current); + return isf_none; + } + + static bool validate_iterator_pair(const_iterator first, const_iterator last) EA_NOEXCEPT + { + return (first.mIndex <= last.mIndex) && variadicAnd(first.mpData[Indices] == last.mpData[Indices]...); + } + + template ::value, bool>::type> + int validate_iterator(Iterator iter) const EA_NOEXCEPT { return validate_iterator(unwrap_iterator(iter)); } + + template ::value, bool>::type> + static bool validate_iterator_pair(Iterator first, Iterator last) EA_NOEXCEPT { return validate_iterator_pair(unwrap_iterator(first), unwrap_iterator(last)); } + + allocator_type& get_allocator() EA_NOEXCEPT { return mDataSizeAndAllocator.second(); } + const allocator_type& get_allocator() const EA_NOEXCEPT { return mDataSizeAndAllocator.second(); } + + void set_allocator(const allocator_type& alloc) { mDataSizeAndAllocator.second() = alloc; } + +protected: + + void* mpData = nullptr; + size_type mNumElements = 0; + size_type mNumCapacity = 0; + + compressed_pair mDataSizeAndAllocator; + + size_type& internalDataSize() EA_NOEXCEPT { return mDataSizeAndAllocator.first(); } + size_type const& internalDataSize() const EA_NOEXCEPT { return mDataSizeAndAllocator.first(); } + + friend struct TupleRecurser<>; + template + friend struct TupleRecurser; + + template + void DoInitFromIterator(move_iterator begin, move_iterator end) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(!validate_iterator_pair(begin, end))) + EASTL_FAIL_MSG("tuple_vector::erase -- invalid iterator pair"); +#endif + size_type newNumElements = (size_type)(end - begin); + const void* ppOtherData[sizeof...(Ts)] = { begin.base().mpData[Indices]... }; + size_type beginIdx = begin.base().mIndex; + size_type endIdx = end.base().mIndex; + DoConditionalReallocate(0, mNumCapacity, newNumElements); + mNumElements = newNumElements; + swallow((eastl::uninitialized_move_ptr(eastl::move_iterator((Ts*)(ppOtherData[Indices]) + beginIdx), + eastl::move_iterator((Ts*)(ppOtherData[Indices]) + endIdx), + TupleVecLeaf::mpData), 0)...); + } + + void DoInitFromIterator(const_iterator begin, const_iterator end) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(!validate_iterator_pair(begin, end))) + EASTL_FAIL_MSG("tuple_vector::erase -- invalid iterator pair"); +#endif + size_type newNumElements = (size_type)(end - begin); + const void* ppOtherData[sizeof...(Ts)] = { begin.mpData[Indices]... }; + size_type beginIdx = begin.mIndex; + size_type endIdx = end.mIndex; + DoConditionalReallocate(0, mNumCapacity, newNumElements); + mNumElements = newNumElements; + swallow((eastl::uninitialized_copy_ptr((Ts*)(ppOtherData[Indices]) + beginIdx, + (Ts*)(ppOtherData[Indices]) + endIdx, + TupleVecLeaf::mpData), 0)...); + } + + void DoInitFillTuple(size_type n, const_reference_tuple tup) { DoInitFillArgs(n, eastl::get(tup)...); } + + void DoInitFillArgs(size_type n, const Ts&... args) + { + DoConditionalReallocate(0, mNumCapacity, n); + mNumElements = n; + swallow((eastl::uninitialized_fill_ptr(TupleVecLeaf::mpData, TupleVecLeaf::mpData + n, args), 0)...); + } + + void DoInitDefaultFill(size_type n) + { + DoConditionalReallocate(0, mNumCapacity, n); + mNumElements = n; + swallow((eastl::uninitialized_default_fill_n(TupleVecLeaf::mpData, n), 0)...); + } + + void DoInitFromTupleArray(const value_tuple* first, const value_tuple* last) + { +#if EASTL_ASSERT_ENABLED + if (EASTL_UNLIKELY(first > last || first == nullptr || last == nullptr)) + EASTL_FAIL_MSG("tuple_vector::ctor from tuple array -- invalid ptrs"); +#endif + size_type newNumElements = last - first; + DoConditionalReallocate(0, mNumCapacity, newNumElements); + mNumElements = newNumElements; + DoUninitializedCopyFromTupleArray(begin(), end(), first); + } + + void DoCopyFromTupleArray(iterator destPos, iterator destEnd, const value_tuple* srcTuple) + { + // assign to constructed region + while (destPos < destEnd) + { + *destPos = *srcTuple; + ++destPos; + ++srcTuple; + } + } + + void DoUninitializedCopyFromTupleArray(iterator destPos, iterator destEnd, const value_tuple* srcTuple) + { + // placement-new/copy-ctor to unconstructed regions + while (destPos < destEnd) + { + swallow(::new(eastl::get(destPos.MakePointer())) Ts(eastl::get(*srcTuple))...); + ++destPos; + ++srcTuple; + } + } + + // Try to grow the size of the container "naturally" given the number of elements being used + void DoGrow(size_type oldNumElements, size_type oldNumCapacity, size_type requiredCapacity) + { + if (requiredCapacity > oldNumCapacity) + DoReallocate(oldNumElements, GetNewCapacity(requiredCapacity)); + } + + // Reallocate to the newCapacity (IFF it's actually larger, though) + void DoConditionalReallocate(size_type oldNumElements, size_type oldNumCapacity, size_type requiredCapacity) + { + if (requiredCapacity > oldNumCapacity) + DoReallocate(oldNumElements, requiredCapacity); + } + + void DoReallocate(size_type oldNumElements, size_type requiredCapacity) + { + void* ppNewLeaf[sizeof...(Ts)]; + pair allocation = TupleRecurser::template DoAllocate( + *this, ppNewLeaf, requiredCapacity, 0); + swallow((TupleVecLeaf::DoUninitializedMoveAndDestruct(0, oldNumElements, (Ts*)ppNewLeaf[Indices]), 0)...); + swallow(TupleVecLeaf::mpData = (Ts*)ppNewLeaf[Indices]...); + + EASTLFree(get_allocator(), mpData, internalDataSize()); + mpData = allocation.first; + mNumCapacity = requiredCapacity; + internalDataSize() = allocation.second; + } + + size_type GetNewCapacity(size_type oldNumCapacity) + { + return (oldNumCapacity > 0) ? (2 * oldNumCapacity) : 1; + } +}; + +} // namespace TupleVecInternal + +// Move_iterator specialization for TupleVecIter. +// An rvalue reference of a move_iterator would normaly be "tuple &&" whereas +// what we actually want is "tuple". This specialization gives us that. +template +class move_iterator, Ts...>> +{ +public: + typedef TupleVecInternal::TupleVecIter, Ts...> iterator_type; + typedef iterator_type wrapped_iterator_type; // This is not in the C++ Standard; it's used by use to identify it as + // a wrapping iterator type. + typedef iterator_traits traits_type; + typedef typename traits_type::iterator_category iterator_category; + typedef typename traits_type::value_type value_type; + typedef typename traits_type::difference_type difference_type; + typedef typename traits_type::pointer pointer; + typedef tuple reference; + typedef move_iterator this_type; + +protected: + iterator_type mIterator; + +public: + move_iterator() : mIterator() {} + explicit move_iterator(iterator_type mi) : mIterator(mi) {} + + template + move_iterator(const move_iterator& mi) : mIterator(mi.base()) {} + + iterator_type base() const { return mIterator; } + reference operator*() const { return eastl::move(MakeReference()); } + pointer operator->() const { return mIterator; } + + this_type& operator++() { ++mIterator; return *this; } + this_type operator++(int) { + this_type tempMoveIterator = *this; + ++mIterator; + return tempMoveIterator; + } + + this_type& operator--() { --mIterator; return *this; } + this_type operator--(int) + { + this_type tempMoveIterator = *this; + --mIterator; + return tempMoveIterator; + } + + this_type operator+(difference_type n) const { return move_iterator(mIterator + n); } + this_type& operator+=(difference_type n) + { + mIterator += n; + return *this; + } + + this_type operator-(difference_type n) const { return move_iterator(mIterator - n); } + this_type& operator-=(difference_type n) + { + mIterator -= n; + return *this; + } + + difference_type operator-(const this_type& rhs) const { return mIterator - rhs.mIterator; } + bool operator<(const this_type& rhs) const { return mIterator < rhs.mIterator; } + bool operator>(const this_type& rhs) const { return mIterator > rhs.mIterator; } + bool operator>=(const this_type& rhs) const { return mIterator >= rhs.mIterator; } + bool operator<=(const this_type& rhs) const { return mIterator <= rhs.mIterator; } + + reference operator[](difference_type n) const { return *(*this + n); } + +private: + reference MakeReference() const + { + return reference(eastl::move(((Ts*)mIterator.mpData[Indices])[mIterator.mIndex])...); + } +}; + +template +inline bool operator==(const TupleVecInternal::TupleVecImpl& a, + const TupleVecInternal::TupleVecImpl& b) +{ + return ((a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin())); +} + +template +inline bool operator!=(const TupleVecInternal::TupleVecImpl& a, + const TupleVecInternal::TupleVecImpl& b) +{ + return ((a.size() != b.size()) || !eastl::equal(a.begin(), a.end(), b.begin())); +} + +template +inline bool operator<(const TupleVecInternal::TupleVecImpl& a, + const TupleVecInternal::TupleVecImpl& b) +{ + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); +} + +template +inline bool operator>(const TupleVecInternal::TupleVecImpl& a, + const TupleVecInternal::TupleVecImpl& b) +{ + return b < a; +} + +template +inline bool operator<=(const TupleVecInternal::TupleVecImpl& a, + const TupleVecInternal::TupleVecImpl& b) +{ + return !(b < a); +} + +template +inline bool operator>=(const TupleVecInternal::TupleVecImpl& a, + const TupleVecInternal::TupleVecImpl& b) +{ + return !(a < b); +} + +template +inline void swap(TupleVecInternal::TupleVecImpl& a, + TupleVecInternal::TupleVecImpl& b) +{ + a.swap(b); +} + +// A customization of swap is made for r-values of tuples-of-references - +// normally, swapping rvalues doesn't make sense, but in this case, we do want to +// swap the contents of what the tuple-of-references are referring to +// +// This is required due to TupleVecIter returning a value-type for its dereferencing, +// as opposed to an actual real reference of some sort +template +inline +typename enable_if...>::value>::type +swap(tuple&& a, tuple&& b) +{ + a.swap(b); +} + +template +inline +typename enable_if...>::value>::type +swap(tuple&& a, tuple&& b) = delete; + + +// External interface of tuple_vector +template +class tuple_vector : public TupleVecInternal::TupleVecImpl, Ts...> +{ + typedef tuple_vector this_type; + typedef TupleVecInternal::TupleVecImpl, Ts...> base_type; + using base_type::base_type; + +public: + this_type& operator=(std::initializer_list iList) + { + base_type::operator=(iList); + return *this; + } +}; + +// Variant of tuple_vector that allows a user-defined allocator type (can't mix default template params with variadics) +template +class tuple_vector_alloc + : public TupleVecInternal::TupleVecImpl, Ts...> +{ + typedef tuple_vector_alloc this_type; + typedef TupleVecInternal::TupleVecImpl, Ts...> base_type; + using base_type::base_type; + +public: + + this_type& operator=(std::initializer_list iList) + { + base_type::operator=(iList); + return *this; + } +}; + +} // namespace eastl + +EA_RESTORE_VC_WARNING() +EA_RESTORE_VC_WARNING() +EA_RESTORE_VC_WARNING() +EA_RESTORE_VC_WARNING() + +#endif // EASTL_TUPLEVECTOR_H diff --git a/lib/EASTL/include/EASTL/chrono.h b/lib/EASTL/include/EASTL/chrono.h new file mode 100644 index 000000000..5d8ca4256 --- /dev/null +++ b/lib/EASTL/include/EASTL/chrono.h @@ -0,0 +1,743 @@ +///////////////////////////////////////////////////////////////////////////// +// 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 +#include +#include +#include + + +// 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 + + #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_SONY) + #include + #include +#elif defined(EA_PLATFORM_APPLE) + #include +#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 + #endif + #include + #if (defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC)) + #include + #else + #include + #include + #endif +#endif + + +namespace eastl +{ +namespace chrono +{ + /////////////////////////////////////////////////////////////////////////////// + // treat_as_floating_point + /////////////////////////////////////////////////////////////////////////////// + template + struct treat_as_floating_point : is_floating_point {}; + + + /////////////////////////////////////////////////////////////////////////////// + // 20.12.4, duration_values + /////////////////////////////////////////////////////////////////////////////// + template + 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::max(); } + EASTL_FORCE_INLINE static EA_CONSTEXPR Rep min() { return eastl::numeric_limits::lowest(); } + }; + + + /////////////////////////////////////////////////////////////////////////////// + // duration fwd_decl + /////////////////////////////////////////////////////////////////////////////// + template > + class duration; + + + namespace Internal + { + /////////////////////////////////////////////////////////////////////////////// + // IsRatio + /////////////////////////////////////////////////////////////////////////////// + template struct IsRatio : eastl::false_type {}; + template struct IsRatio> : eastl::true_type {}; + template struct IsRatio> : eastl::true_type {}; + template struct IsRatio> : eastl::true_type {}; + template struct IsRatio> : eastl::true_type {}; + + + /////////////////////////////////////////////////////////////////////////////// + // IsDuration + /////////////////////////////////////////////////////////////////////////////// + template struct IsDuration : eastl::false_type{}; + template struct IsDuration> : eastl::true_type{}; + template struct IsDuration> : eastl::true_type{}; + template struct IsDuration> : eastl::true_type{}; + template struct IsDuration> : eastl::true_type{}; + + + /////////////////////////////////////////////////////////////////////////////// + // RatioGCD + /////////////////////////////////////////////////////////////////////////////// + template + struct RatioGCD + { + static_assert(IsRatio::value, "Period1 is not a eastl::ratio type"); + static_assert(IsRatio::value, "Period2 is not a eastl::ratio type"); + + typedef ratio::value, + eastl::Internal::lcm::value> type; + }; + }; + + + /////////////////////////////////////////////////////////////////////////////// + // 20.12.5.7, duration_cast + /////////////////////////////////////////////////////////////////////////////// + namespace Internal + { + template ::type, + typename CommonRep = typename eastl::decay::type>::type, + bool = CommonPeriod::num == 1, + bool = CommonPeriod::den == 1> + struct DurationCastImpl; + + template + struct DurationCastImpl + { + inline static ToDuration DoCast(const FromDuration& fd) + { + return ToDuration(static_cast(fd.count())); + } + }; + + template + struct DurationCastImpl + { + inline static ToDuration DoCast(const FromDuration& d) + { + return ToDuration(static_cast(static_cast(d.count()) * + static_cast(CommonPeriod::num))); + } + }; + + template + struct DurationCastImpl + { + inline static ToDuration DoCast(const FromDuration& d) + { + return ToDuration(static_cast(static_cast(d.count()) / + static_cast(CommonPeriod::den))); + } + }; + + template + struct DurationCastImpl + { + inline static ToDuration DoCast(const FromDuration& d) + { + return ToDuration(static_cast(static_cast(d.count()) * + static_cast(CommonPeriod::num) / + static_cast(CommonPeriod::den))); + } + }; + }; // namespace Internal + + + /////////////////////////////////////////////////////////////////////////////// + // duration_cast + /////////////////////////////////////////////////////////////////////////////// + template + inline typename eastl::enable_if::value, ToDuration>::type + duration_cast(const duration& d) + { + typedef typename duration::this_type FromDuration; + return Internal::DurationCastImpl::DoCast(d); + } + + + /////////////////////////////////////////////////////////////////////////////// + // duration + /////////////////////////////////////////////////////////////////////////////// + template + class duration + { + Rep mRep; + + public: + typedef Rep rep; + typedef Period period; + typedef duration 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 + inline EA_CONSTEXPR explicit duration( + const Rep2& rep2, + typename eastl::enable_if::value && + (treat_as_floating_point::value || + !treat_as_floating_point::value)>::type** = 0) + : mRep(static_cast(rep2)) {} + + + template + EA_CONSTEXPR duration(const duration& d2, + typename eastl::enable_if::value || + (eastl::ratio_divide::type::den == 1 && + !treat_as_floating_point::value), + void>::type** = 0) + : mRep(duration_cast(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::zero()); } + EA_CONSTEXPR inline static duration min() { return duration(duration_values::min()); } + EA_CONSTEXPR inline static duration max() { return duration(duration_values::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 eastl::common_type, duration>::type EASTL_FORCE_INLINE + operator+(const duration& lhs, const duration& rhs) + { + typedef typename eastl::common_type, duration>::type common_duration_t; + return common_duration_t(common_duration_t(lhs).count() + common_duration_t(rhs).count()); + } + + template + typename eastl::common_type, duration>::type EASTL_FORCE_INLINE + operator-(const duration& lhs, const duration& rhs) + { + typedef typename eastl::common_type, duration>::type common_duration_t; + return common_duration_t(common_duration_t(lhs).count() - common_duration_t(rhs).count()); + } + + template + duration::type, Period1> EASTL_FORCE_INLINE + operator*(const duration& lhs, const Rep2& rhs) + { + typedef typename duration, Period1>::type common_duration_t; + return common_duration_t(common_duration_t(lhs).count() * rhs); + } + + template + duration::type, Period2> EASTL_FORCE_INLINE + operator*(const Rep1& lhs, const duration& rhs) + { + typedef duration::type, Period2> common_duration_t; + return common_duration_t(lhs * common_duration_t(rhs).count()); + } + + template + duration::type, Period1> EASTL_FORCE_INLINE + operator/(const duration& lhs, const Rep2& rhs) + { + typedef duration::type, Period1> common_duration_t; + return common_duration_t(common_duration_t(lhs).count() / rhs); + } + + template + typename eastl::common_type, duration>::type EASTL_FORCE_INLINE + operator/(const duration& lhs, const duration& rhs) + { + typedef typename eastl::common_type, duration>::type common_duration_t; + return common_duration_t(common_duration_t(lhs).count() / common_duration_t(rhs).count()); + } + + template + duration::type, Period1> EASTL_FORCE_INLINE + operator%(const duration& lhs, const Rep2& rhs) + { + typedef duration::type, Period1> common_duration_t; + return common_duration_t(common_duration_t(lhs).count() % rhs); + } + + template + typename eastl::common_type, duration>::type EASTL_FORCE_INLINE + operator%(const duration& lhs, const duration& rhs) + { + typedef typename eastl::common_type, duration>::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 + EASTL_FORCE_INLINE bool operator==(const duration& lhs, + const duration& rhs) + { + typedef typename eastl::common_type, duration>::type common_duration_t; + return common_duration_t(lhs).count() == common_duration_t(rhs).count(); + } + + template + EASTL_FORCE_INLINE bool operator<(const duration& lhs, + const duration& rhs) + { + typedef typename eastl::common_type, duration>::type common_duration_t; + return common_duration_t(lhs).count() < common_duration_t(rhs).count(); + } + + template + EASTL_FORCE_INLINE bool operator!=(const duration& lhs, + const duration& rhs) + { + return !(lhs == rhs); + } + + template + EASTL_FORCE_INLINE bool operator<=(const duration& lhs, + const duration& rhs) + { + return !(rhs < lhs); + } + + template + EASTL_FORCE_INLINE bool operator>(const duration& lhs, + const duration& rhs) + { + return rhs < lhs; + } + + template + EASTL_FORCE_INLINE bool operator>=(const duration& lhs, + const duration& rhs) + { + return !(lhs < rhs); + } + + + /////////////////////////////////////////////////////////////////////////////// + // standard duration units + /////////////////////////////////////////////////////////////////////////////// + typedef duration nanoseconds; + typedef duration microseconds; + typedef duration milliseconds; + typedef duration seconds; + typedef duration> minutes; + typedef duration> hours; + + + /////////////////////////////////////////////////////////////////////////////// + // 20.12.6, time_point + /////////////////////////////////////////////////////////////////////////////// + template + 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 + inline EA_CONSTEXPR time_point( + const time_point& t, + typename eastl::enable_if::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 + inline EA_CONSTEXPR time_point>::type> + operator+(const time_point& lhs, const duration& rhs) + { + typedef time_point>::type> common_timepoint_t; + return common_timepoint_t(lhs.time_since_epoch() + rhs); + } + + template + inline EA_CONSTEXPR time_point>::type> + operator+(const duration& lhs, const time_point& rhs) + { + typedef time_point>::type> common_timepoint_t; + return common_timepoint_t(lhs + rhs.time_since_epoch()); + } + + template + inline EA_CONSTEXPR time_point>::type> + operator-(const time_point& lhs, const duration& rhs) + { + typedef time_point>::type> common_timepoint_t; + return common_timepoint_t(lhs.time_since_epoch() - rhs); + } + + template + inline EA_CONSTEXPR typename eastl::common_type::type operator-( + const time_point& lhs, + const time_point& rhs) + { + return lhs.time_since_epoch() - rhs.time_since_epoch(); + } + + template + inline EA_CONSTEXPR bool operator==(const time_point& lhs, + const time_point& rhs) + { + return lhs.time_since_epoch() == rhs.time_since_epoch(); + } + + template + inline EA_CONSTEXPR bool operator!=(const time_point& lhs, + const time_point& rhs) + { + return !(lhs == rhs); + } + + template + inline EA_CONSTEXPR bool operator<(const time_point& lhs, const time_point& rhs) + { + return lhs.time_since_epoch() < rhs.time_since_epoch(); + } + + template + inline EA_CONSTEXPR bool operator<=(const time_point& lhs, + const time_point& rhs) + { + return !(rhs < lhs); + } + + template + inline EA_CONSTEXPR bool operator>(const time_point& lhs, const time_point& rhs) + { + return rhs < lhs; + } + + template + inline EA_CONSTEXPR bool operator>=(const time_point& lhs, + const time_point& rhs) + { + return !(lhs < rhs); + } + + + /////////////////////////////////////////////////////////////////////////////// + // 20.12.6.7, time_point_cast + /////////////////////////////////////////////////////////////////////////////// + template + EA_CONSTEXPR time_point time_point_cast( + const time_point& t, + typename eastl::enable_if::value>::type** = 0) + { + return time_point(duration_cast(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 _XTIME_NSECS_PER_TICK + #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, nano>::type SystemClock_Period; + typedef eastl::ratio_multiply, 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 / 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 * queryCounter()); + #elif defined EA_PLATFORM_SONY + return sceKernelGetProcessTimeCounter(); + #elif defined(EA_PLATFORM_APPLE) + return mach_absolute_time(); + #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 long long rep; // signed arithmetic type representing the number of ticks in the clock's duration + typedef Internal::SystemClock_Period period; + typedef chrono::duration duration; // duration, capable of representing negative durations + typedef chrono::time_point 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 long long rep; // signed arithmetic type representing the number of ticks in the clock's duration + typedef Internal::SteadyClock_Period period; + typedef chrono::duration duration; // duration, capable of representing negative durations + typedef chrono::time_point 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 + struct common_type, chrono::duration> + { + typedef chrono::duration::type>::type, + typename chrono::Internal::RatioGCD::type> type; + }; + + + /////////////////////////////////////////////////////////////////////////////// + // time_point common_type specialization + /////////////////////////////////////////////////////////////////////////////// + template + struct common_type, chrono::time_point> + { + typedef chrono::time_point::type> type; + }; + + + /////////////////////////////////////////////////////////////////////////////// + // chrono_literals + /////////////////////////////////////////////////////////////////////////////// + #if EASTL_USER_LITERALS_ENABLED && EASTL_INLINE_NAMESPACES_ENABLED + EA_DISABLE_VC_WARNING(4455) // disable warning C4455: literal suffix identifiers that do not start with an underscore are reserved + 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> operator"" h(long double h) + { return chrono::duration>(h); } + EA_CONSTEXPR chrono::duration> operator"" min(long double m) + { return chrono::duration>(m); } + EA_CONSTEXPR chrono::duration operator"" s(long double s) + { return chrono::duration(s); } + EA_CONSTEXPR chrono::duration operator"" ms(long double ms) + { return chrono::duration(ms); } + EA_CONSTEXPR chrono::duration operator"" us(long double us) + { return chrono::duration(us); } + EA_CONSTEXPR chrono::duration operator"" ns(long double ns) + { return chrono::duration(ns); } + + } // namespace chrono_literals + }// namespace 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 diff --git a/lib/EASTL/include/EASTL/core_allocator.h b/lib/EASTL/include/EASTL/core_allocator.h new file mode 100644 index 000000000..e43749125 --- /dev/null +++ b/lib/EASTL/include/EASTL/core_allocator.h @@ -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 + +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(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 + diff --git a/lib/EASTL/include/EASTL/core_allocator_adapter.h b/lib/EASTL/include/EASTL/core_allocator_adapter.h new file mode 100644 index 000000000..d6f18275b --- /dev/null +++ b/lib/EASTL/include/EASTL/core_allocator_adapter.h @@ -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 + +#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 + /// typedef EA::Allocator::CoreAllocatorAdapter Adapter; + /// eastl::list widgetList(Adapter("UI/WidgetList", pSomeCoreAllocator)); + /// widgetList.push_back(Widget()); + /// + /// Example usage: + /// #include + /// eastl::list > widgetList; + /// widgetList.push_back(Widget()); + /// + /// Example usage: + /// #include + /// typedef EA::Allocator::CoreAllocatorAdapter Adapter; + /// typedef eastl::list WidgetList; + /// CoreAllocatorFixed 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 CoreAllocatorAdapter + { + public: + typedef CoreAllocatorAdapter 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 + bool operator==(const CoreAllocatorAdapter& a, const CoreAllocatorAdapter& b); + + template + bool operator!=(const CoreAllocatorAdapter& a, const CoreAllocatorAdapter& b); + + + + /// EASTLICoreAllocator + /// + /// Provides a standardized typedef for ICoreAllocator; + /// + /// Example usage: + /// eastl::list widgetList("UI/WidgetList", pSomeCoreAllocator); + /// widgetList.push_back(Widget()); + /// + class ICoreAllocator; + class EASTLCoreAllocatorImpl; + + typedef CoreAllocatorAdapter EASTLICoreAllocatorAdapter; + typedef CoreAllocatorAdapter 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 CoreDeleterAdapter + { + public: + typedef CoreDeleterAdapter this_type; + AllocatorType* mpCoreAllocator; + + public: + CoreDeleterAdapter(AllocatorType* pAllocator = EASTL_CORE_ALLOCATOR_ADAPTER_GET_DEFAULT_CORE_ALLOCATOR()) EA_NOEXCEPT + : mpCoreAllocator(pAllocator) {} + + ~CoreDeleterAdapter() EA_NOEXCEPT {} + + template + 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 foo(pA, EASTLCoreDeleter()); + /// + typedef CoreDeleterAdapter EASTLICoreDeleterAdapter; + typedef CoreDeleterAdapter EASTLCoreDeleterAdapter; + + } // namespace Allocator + +} // namespace EA + + + + + +/////////////////////////////////////////////////////////////////////////////// +// Inlines +/////////////////////////////////////////////////////////////////////////////// + +namespace EA +{ + namespace Allocator + { + template + inline CoreAllocatorAdapter::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 + inline CoreAllocatorAdapter::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 + inline CoreAllocatorAdapter::CoreAllocatorAdapter(const CoreAllocatorAdapter& x) + : mpCoreAllocator(x.mpCoreAllocator), mnFlags(x.mnFlags) + { + #if EASTL_NAME_ENABLED + mpName = x.mpName; + #endif + } + + template + inline CoreAllocatorAdapter::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 + inline CoreAllocatorAdapter& CoreAllocatorAdapter::operator=(const CoreAllocatorAdapter& x) + { + mpCoreAllocator = x.mpCoreAllocator; + mnFlags = x.mnFlags; + + #if EASTL_NAME_ENABLED + mpName = x.mpName; + #endif + + return *this; + } + + template + inline void* CoreAllocatorAdapter::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 + inline void* CoreAllocatorAdapter::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 + inline void CoreAllocatorAdapter::deallocate(void* p, size_t n) + { + return mpCoreAllocator->Free(p, n); + } + + template + inline AllocatorType* CoreAllocatorAdapter::get_allocator() const + { + return mpCoreAllocator; + } + + template + inline void CoreAllocatorAdapter::set_allocator(AllocatorType* pAllocator) + { + mpCoreAllocator = pAllocator; + } + + template + inline int CoreAllocatorAdapter::get_flags() const + { + return mnFlags; + } + + template + inline void CoreAllocatorAdapter::set_flags(int flags) + { + mnFlags = flags; + } + + template + inline const char* CoreAllocatorAdapter::get_name() const + { + #if EASTL_NAME_ENABLED + return mpName; + #else + return EASTL_ALLOCATOR_DEFAULT_NAME; + #endif + } + + template + inline void CoreAllocatorAdapter::set_name(const char* pName) + { + #if EASTL_NAME_ENABLED + mpName = pName; + #else + (void)pName; + #endif + } + + + + template + inline bool operator==(const CoreAllocatorAdapter& a, const CoreAllocatorAdapter& b) + { + return (a.mpCoreAllocator == b.mpCoreAllocator) && + (a.mnFlags == b.mnFlags); + } + + template + inline bool operator!=(const CoreAllocatorAdapter& a, const CoreAllocatorAdapter& b) + { + return (a.mpCoreAllocator != b.mpCoreAllocator) || + (a.mnFlags != b.mnFlags); + } + + + } // namespace Allocator + +} // namespace EA + + +#endif // EASTL_CORE_ALLOCATOR_ENABLED +#endif // Header include guard + + + + + + + + diff --git a/lib/EASTL/include/EASTL/deque.h b/lib/EASTL/include/EASTL/deque.h new file mode 100644 index 000000000..c2d55b1c6 --- /dev/null +++ b/lib/EASTL/include/EASTL/deque.h @@ -0,0 +1,2687 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////// +// deque design +// +// A deque (pronounced "deck") is a double-ended queue, though this is partially +// of a misnomer. A deque does indeed let you add and remove values from both ends +// of the container, but it's not usually used for such a thing and instead is used +// as a more flexible version of a vector. It provides operator[] (random access) +// and can insert items anywhere and not just at the front and back. +// +// While you can implement a double-ended queue via a doubly-linked list, deque is +// instead implemented as a list of arrays. The benefit of this is that memory usage +// is lower and that random access can be had with decent efficiency. +// +// Our implementation of deque is just like every other implementation of deque, +// as the C++ standard all but dictates that you make it work this way. Below +// we have a depiction of an array (or vector) of 48 items, with each node being +// a '+' character and extra capacity being a '-' character. What we have is one +// contiguous block of memory: +// +// ++++++++++++++++++++++++++++++++++++++++++++++++----------------- +// 0 47 +// +// With a deque, the same array of 48 items would be implemented as multiple smaller +// arrays of contiguous memory, each of fixed size. We will call these "sub-arrays." +// In the case here, we have six arrays of 8 nodes: +// +// ++++++++ ++++++++ ++++++++ ++++++++ ++++++++ ++++++++ +// +// With an vector, item [0] is the first item and item [47] is the last item. With a +// deque, item [0] is usually not the first item and neither is item [47]. There is +// extra capacity on both the front side and the back side of the deque. So a deque +// (of 24 items) actually looks like this: +// +// -------- -----+++ ++++++++ ++++++++ +++++--- -------- +// 0 23 +// +// To insert items at the front, you move into the capacity on the left, and to insert +// items at the back, you append items on the right. As you can see, inserting an item +// at the front doesn't require allocating new memory nor does it require moving any +// items in the container. It merely involves moving the pointer to the [0] item to +// the left by one node. +// +// We keep track of these sub-arrays by having an array of pointers, with each array +// entry pointing to each of the sub-arrays. We could alternatively use a linked +// list of pointers, but it turns out we can implement our deque::operator[] more +// efficiently if we use an array of pointers instead of a list of pointers. +// +// To implement deque::iterator, we could keep a struct which is essentially this: +// struct iterator { +// int subArrayIndex; +// int subArrayOffset; +// } +// +// In practice, we implement iterators a little differently, but in reality our +// implementation isn't much different from the above. It turns out that it's most +// simple if we also manage the location of item [0] and item [end] by using these +// same iterators. +// +// To consider: Implement the deque as a circular deque instead of a linear one. +// This would use a similar subarray layout but iterators would +// wrap around when they reached the end of the subarray pointer list. +// +////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_DEQUE_H +#define EASTL_DEQUE_H + + +#include +#include +#include +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + +#if EASTL_EXCEPTIONS_ENABLED + EA_DISABLE_ALL_VC_WARNINGS() + #include // std::out_of_range, std::length_error. + EA_RESTORE_ALL_VC_WARNINGS() +#endif + + +// 4267 - 'argument' : conversion from 'size_t' to 'const uint32_t', possible loss of data. This is a bogus warning resulting from a bug in VC++. +// 4345 - Behavior change: an object of POD type constructed with an initializer of the form () will be default-initialized +// 4480 - nonstandard extension used: specifying underlying type for enum +// 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc +// 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. +EA_DISABLE_VC_WARNING(4267 4345 4480 4530 4571); + +#if EASTL_EXCEPTIONS_ENABLED + // 4703 - potentially uninitialized local pointer variable used. VC++ is mistakenly analyzing the possibility of uninitialized variables, though it's not easy for it to do so. + // 4701 - potentially uninitialized local variable used. + EA_DISABLE_VC_WARNING(4703 4701) +#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 +{ + + /// EASTL_DEQUE_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_DEQUE_DEFAULT_NAME + #define EASTL_DEQUE_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " deque" // Unless the user overrides something, this is "EASTL deque". + #endif + + + /// EASTL_DEQUE_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_DEQUE_DEFAULT_ALLOCATOR + #define EASTL_DEQUE_DEFAULT_ALLOCATOR allocator_type(EASTL_DEQUE_DEFAULT_NAME) + #endif + + + /// DEQUE_DEFAULT_SUBARRAY_SIZE + /// + /// Defines the default number of items in a subarray. + /// Note that the user has the option of specifying the subarray size + /// in the deque template declaration. + /// + #if !defined(__GNUC__) || (__GNUC__ >= 3) // GCC 2.x can't handle the declaration below. + #define DEQUE_DEFAULT_SUBARRAY_SIZE(T) ((sizeof(T) <= 4) ? 64 : ((sizeof(T) <= 8) ? 32 : ((sizeof(T) <= 16) ? 16 : ((sizeof(T) <= 32) ? 8 : 4)))) + #else + #define DEQUE_DEFAULT_SUBARRAY_SIZE(T) 16 + #endif + + + + /// DequeIterator + /// + /// The DequeIterator provides both const and non-const iterators for deque. + /// It also is used for the tracking of the begin and end for the deque. + /// + template + struct DequeIterator + { + typedef DequeIterator this_type; + typedef DequeIterator iterator; + typedef DequeIterator const_iterator; + typedef ptrdiff_t difference_type; + typedef EASTL_ITC_NS::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef T* pointer; + typedef T& reference; + + public: + DequeIterator(); + DequeIterator(const iterator& x); + + pointer operator->() const; + reference operator*() const; + + this_type& operator++(); + this_type operator++(int); + + this_type& operator--(); + this_type operator--(int); + + this_type& operator+=(difference_type n); + this_type& operator-=(difference_type n); + + this_type operator+(difference_type n) const; + this_type operator-(difference_type n) const; + + protected: + template + friend struct DequeIterator; + + template + friend struct DequeBase; + + template + friend class deque; + + template + friend bool operator==(const DequeIterator&, + const DequeIterator&); + + template + friend bool operator!=(const DequeIterator&, + const DequeIterator&); + + template + friend bool operator!=(const DequeIterator& a, + const DequeIterator& b); + + template + friend bool operator< (const DequeIterator&, + const DequeIterator&); + + template + friend bool operator> (const DequeIterator&, + const DequeIterator&); + + template + friend bool operator<=(const DequeIterator&, + const DequeIterator&); + + template + friend bool operator>=(const DequeIterator&, + const DequeIterator&); + + template + friend typename DequeIterator::difference_type + operator-(const DequeIterator& a, + const DequeIterator& b); + + protected: + T* mpCurrent; // Where we currently point. Declared first because it's used most often. + T* mpBegin; // The beginning of the current subarray. + T* mpEnd; // The end of the current subarray. To consider: remove this member, as it is always equal to 'mpBegin + kDequeSubarraySize'. Given that deque subarrays usually consist of hundreds of bytes, this isn't a massive win. Also, now that we are implementing a zero-allocation new deque policy, mpEnd may in fact not be equal to 'mpBegin + kDequeSubarraySize'. + T** mpCurrentArrayPtr; // Pointer to current subarray. We could alternatively implement this as a list node iterator if the deque used a linked list. + + struct Increment {}; + struct Decrement {}; + struct FromConst {}; + + DequeIterator(T** pCurrentArrayPtr, T* pCurrent); + DequeIterator(const const_iterator& x, FromConst) : mpCurrent(x.mpCurrent), mpBegin(x.mpBegin), mpEnd(x.mpEnd), mpCurrentArrayPtr(x.mpCurrentArrayPtr){} + DequeIterator(const iterator& x, Increment); + DequeIterator(const iterator& x, Decrement); + + this_type copy(const iterator& first, const iterator& last, true_type); // true means that value_type has the type_trait has_trivial_relocate, + this_type copy(const iterator& first, const iterator& last, false_type); // false means it does not. + + void copy_backward(const iterator& first, const iterator& last, true_type); // true means that value_type has the type_trait has_trivial_relocate, + void copy_backward(const iterator& first, const iterator& last, false_type); // false means it does not. + + void SetSubarray(T** pCurrentArrayPtr); + }; + + + + + /// DequeBase + /// + /// The DequeBase implements memory allocation for deque. + /// See VectorBase (class vector) for an explanation of why we + /// create this separate base class. + /// + template + struct DequeBase + { + typedef T value_type; + typedef Allocator allocator_type; + 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 DequeIterator iterator; + typedef DequeIterator const_iterator; + + static const size_type npos = (size_type)-1; /// 'npos' means non-valid position or simply non-position. + static const size_type kMaxSize = (size_type)-2; /// -1 is reserved for 'npos'. It also happens to be slightly beneficial that kMaxSize is a value less than -1, as it helps us deal with potential integer wraparound issues. + + enum + { + kMinPtrArraySize = 8, /// A new empty deque has a ptrArraySize of 0, but any allocated ptrArrays use this min size. + kSubarraySize = kDequeSubarraySize /// + //kNodeSize = kDequeSubarraySize * sizeof(T) /// Disabled because it prevents the ability to do this: struct X{ eastl::deque mDequeOfSelf; }; + }; + + enum Side /// Defines the side of the deque: front or back. + { + kSideFront, /// Identifies the front side of the deque. + kSideBack /// Identifies the back side of the deque. + }; + + protected: + T** mpPtrArray; // Array of pointers to subarrays. + size_type mnPtrArraySize; // Possibly we should store this as T** mpArrayEnd. + iterator mItBegin; // Where within the subarrays is our beginning. + iterator mItEnd; // Where within the subarrays is our end. + allocator_type mAllocator; // To do: Use base class optimization to make this go away. + + public: + DequeBase(const allocator_type& allocator); + DequeBase(size_type n); + DequeBase(size_type n, const allocator_type& allocator); + ~DequeBase(); + + const allocator_type& get_allocator() const EA_NOEXCEPT; + allocator_type& get_allocator() EA_NOEXCEPT; + void set_allocator(const allocator_type& allocator); + + protected: + T* DoAllocateSubarray(); + void DoFreeSubarray(T* p); + void DoFreeSubarrays(T** pBegin, T** pEnd); + + T** DoAllocatePtrArray(size_type n); + void DoFreePtrArray(T** p, size_t n); + + iterator DoReallocSubarray(size_type nAdditionalCapacity, Side allocationSide); + void DoReallocPtrArray(size_type nAdditionalCapacity, Side allocationSide); + + void DoInit(size_type n); + + }; // DequeBase + + + + + /// deque + /// + /// Implements a conventional C++ double-ended queue. The implementation used here + /// is very much like any other deque implementations you may have seen, as it + /// follows the standard algorithm for deque design. + /// + /// Note: + /// As of this writing, deque does not support zero-allocation initial emptiness. + /// A newly created deque with zero elements will still allocate a subarray + /// pointer set. We are looking for efficient and clean ways to get around this, + /// but current efforts have resulted in less efficient and more fragile code. + /// The logic of this class doesn't lend itself to a clean implementation. + /// It turns out that deques are one of the least likely classes you'd want this + /// behaviour in, so until this functionality becomes very important to somebody, + /// we will leave it as-is. It can probably be solved by adding some extra code to + /// the Do* functions and adding good comments explaining the situation. + /// + template + class deque : public DequeBase + { + public: + typedef DequeBase base_type; + typedef deque this_type; + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef DequeIterator iterator; + typedef DequeIterator const_iterator; + typedef eastl::reverse_iterator reverse_iterator; + typedef eastl::reverse_iterator const_reverse_iterator; + typedef typename base_type::size_type size_type; + typedef typename base_type::difference_type difference_type; + typedef typename base_type::allocator_type allocator_type; + + using base_type::kSideFront; + using base_type::kSideBack; + using base_type::mpPtrArray; + using base_type::mnPtrArraySize; + using base_type::mItBegin; + using base_type::mItEnd; + using base_type::mAllocator; + using base_type::npos; + using base_type::DoAllocateSubarray; + using base_type::DoFreeSubarray; + using base_type::DoFreeSubarrays; + using base_type::DoAllocatePtrArray; + using base_type::DoFreePtrArray; + using base_type::DoReallocSubarray; + using base_type::DoReallocPtrArray; + + public: + deque(); + explicit deque(const allocator_type& allocator); + explicit deque(size_type n, const allocator_type& allocator = EASTL_DEQUE_DEFAULT_ALLOCATOR); + deque(size_type n, const value_type& value, const allocator_type& allocator = EASTL_DEQUE_DEFAULT_ALLOCATOR); + deque(const this_type& x); + deque(this_type&& x); + deque(this_type&& x, const allocator_type& allocator); + deque(std::initializer_list ilist, const allocator_type& allocator = EASTL_DEQUE_DEFAULT_ALLOCATOR); + + template + deque(InputIterator first, InputIterator last); // allocator arg removed because VC7.1 fails on the default arg. To do: Make a second version of this function without a default arg. + + ~deque(); + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list ilist); + this_type& operator=(this_type&& x); + + void swap(this_type& x); + + void assign(size_type n, const value_type& value); + void assign(std::initializer_list ilist); + + template // It turns out that the C++ std::deque specifies a two argument + void assign(InputIterator first, InputIterator last); // version of assign that takes (int size, int value). These are not + // iterators, so we need to do a template compiler trick to do the right thing. + + 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; + + bool empty() const EA_NOEXCEPT; + size_type size() const EA_NOEXCEPT; + + void resize(size_type n, const value_type& value); + void resize(size_type n); + + void shrink_to_fit(); + void set_capacity(size_type n = base_type::npos); + + reference operator[](size_type n); + const_reference operator[](size_type n) const; + + reference at(size_type n); + const_reference at(size_type n) const; + + reference front(); + const_reference front() const; + + reference back(); + const_reference back() const; + + void push_front(const value_type& value); + reference push_front(); + void push_front(value_type&& value); + + void push_back(const value_type& value); + reference push_back(); + void push_back(value_type&& value); + + void pop_front(); + void pop_back(); + + template + iterator emplace(const_iterator position, Args&&... args); + + template + void emplace_front(Args&&... args); + + template + void emplace_back(Args&&... args); + + iterator insert(const_iterator position, const value_type& value); + iterator insert(const_iterator position, value_type&& value); + void insert(const_iterator position, size_type n, const value_type& value); + iterator insert(const_iterator position, std::initializer_list ilist); + + template + void insert(const_iterator position, InputIterator first, InputIterator last); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + reverse_iterator erase(reverse_iterator position); + reverse_iterator erase(reverse_iterator first, reverse_iterator last); + + void clear(); + //void reset_lose_memory(); // Disabled until it can be implemented efficiently and cleanly. // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. + + bool validate() const; + int validate_iterator(const_iterator i) const; + + protected: + template + void DoInit(Integer n, Integer value, true_type); + + template + void DoInit(InputIterator first, InputIterator last, false_type); + + template + void DoInitFromIterator(InputIterator first, InputIterator last, EASTL_ITC_NS::input_iterator_tag); + + template + void DoInitFromIterator(ForwardIterator first, ForwardIterator last, EASTL_ITC_NS::forward_iterator_tag); + + void DoFillInit(const value_type& value); + + template + void DoAssign(Integer n, Integer value, true_type); + + template + void DoAssign(InputIterator first, InputIterator last, false_type); + + void DoAssignValues(size_type n, const value_type& value); + + template + void DoInsert(const const_iterator& position, Integer n, Integer value, true_type); + + template + void DoInsert(const const_iterator& position, const InputIterator& first, const InputIterator& last, false_type); + + template + void DoInsertFromIterator(const_iterator position, const InputIterator& first, const InputIterator& last, EASTL_ITC_NS::forward_iterator_tag); + + void DoInsertValues(const_iterator position, size_type n, const value_type& value); + + void DoSwap(this_type& x); + }; // class deque + + + + + /////////////////////////////////////////////////////////////////////// + // DequeBase + /////////////////////////////////////////////////////////////////////// + + template + DequeBase::DequeBase(const allocator_type& allocator) + : mpPtrArray(NULL), + mnPtrArraySize(0), + mItBegin(), + mItEnd(), + mAllocator(allocator) + { + // It is assumed here that the deque subclass will init us when/as needed. + } + + + template + DequeBase::DequeBase(size_type n) + : mpPtrArray(NULL), + mnPtrArraySize(0), + mItBegin(), + mItEnd(), + mAllocator(EASTL_DEQUE_DEFAULT_NAME) + { + // It's important to note that DoInit creates space for elements and assigns + // mItBegin/mItEnd to point to them, but these elements are not constructed. + // You need to immediately follow this constructor with code that constructs the values. + DoInit(n); + } + + + template + DequeBase::DequeBase(size_type n, const allocator_type& allocator) + : mpPtrArray(NULL), + mnPtrArraySize(0), + mItBegin(), + mItEnd(), + mAllocator(allocator) + { + // It's important to note that DoInit creates space for elements and assigns + // mItBegin/mItEnd to point to them, but these elements are not constructed. + // You need to immediately follow this constructor with code that constructs the values. + DoInit(n); + } + + + template + DequeBase::~DequeBase() + { + if(mpPtrArray) + { + DoFreeSubarrays(mItBegin.mpCurrentArrayPtr, mItEnd.mpCurrentArrayPtr + 1); + DoFreePtrArray(mpPtrArray, mnPtrArraySize); + mpPtrArray = nullptr; + } + } + + + template + const typename DequeBase::allocator_type& + DequeBase::get_allocator() const EA_NOEXCEPT + { + return mAllocator; + } + + + template + typename DequeBase::allocator_type& + DequeBase::get_allocator() EA_NOEXCEPT + { + return mAllocator; + } + + + template + void DequeBase::set_allocator(const allocator_type& allocator) + { + // The only time you can set an allocator is with an empty unused container, such as right after construction. + if(EASTL_LIKELY(mAllocator != allocator)) + { + if(EASTL_LIKELY(mpPtrArray && (mItBegin.mpCurrentArrayPtr == mItEnd.mpCurrentArrayPtr))) // If we are empty and so can safely deallocate the existing memory... We could also test for empty(), but that's a more expensive calculation and more involved clearing, though it would be more flexible. + { + DoFreeSubarrays(mItBegin.mpCurrentArrayPtr, mItEnd.mpCurrentArrayPtr + 1); + DoFreePtrArray(mpPtrArray, mnPtrArraySize); + + mAllocator = allocator; + DoInit(0); + } + else + { + EASTL_FAIL_MSG("DequeBase::set_allocator -- atempt to change allocator after allocating elements."); + } + } + } + + + template + T* DequeBase::DoAllocateSubarray() + { + T* p = (T*)allocate_memory(mAllocator, kDequeSubarraySize * sizeof(T), EASTL_ALIGN_OF(T), 0); + EASTL_ASSERT_MSG(p != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + + #if EASTL_DEBUG + memset((void*)p, 0, kDequeSubarraySize * sizeof(T)); + #endif + + return (T*)p; + } + + + template + void DequeBase::DoFreeSubarray(T* p) + { + if(p) + EASTLFree(mAllocator, p, kDequeSubarraySize * sizeof(T)); + } + + template + void DequeBase::DoFreeSubarrays(T** pBegin, T** pEnd) + { + while(pBegin < pEnd) + DoFreeSubarray(*pBegin++); + } + + template + T** DequeBase::DoAllocatePtrArray(size_type n) + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(n >= 0x80000000)) + EASTL_FAIL_MSG("deque::DoAllocatePtrArray -- improbably large request."); + #endif + + T** pp = (T**)allocate_memory(mAllocator, n * sizeof(T*), EASTL_ALIGN_OF(T), 0); + EASTL_ASSERT_MSG(pp != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + + #if EASTL_DEBUG + memset((void*)pp, 0, n * sizeof(T*)); + #endif + + return pp; + } + + + template + void DequeBase::DoFreePtrArray(T** pp, size_t n) + { + if(pp) + EASTLFree(mAllocator, pp, n * sizeof(T*)); + } + + + template + typename DequeBase::iterator + DequeBase::DoReallocSubarray(size_type nAdditionalCapacity, Side allocationSide) + { + // nAdditionalCapacity refers to the amount of additional space we need to be + // able to store in this deque. Typically this function is called as part of + // an insert or append operation. This is the function that makes sure there + // is enough capacity for the new elements to be copied into the deque. + // The new capacity here is always at the front or back of the deque. + // This function returns an iterator to that points to the new begin or + // the new end of the deque space, depending on allocationSide. + + if(allocationSide == kSideFront) + { + // There might be some free space (nCurrentAdditionalCapacity) at the front of the existing subarray. + const size_type nCurrentAdditionalCapacity = (size_type)(mItBegin.mpCurrent - mItBegin.mpBegin); + + if(EASTL_UNLIKELY(nCurrentAdditionalCapacity < nAdditionalCapacity)) // If we need to grow downward into a new subarray... + { + const difference_type nSubarrayIncrease = (difference_type)(((nAdditionalCapacity - nCurrentAdditionalCapacity) + kDequeSubarraySize - 1) / kDequeSubarraySize); + difference_type i; + + if(nSubarrayIncrease > (mItBegin.mpCurrentArrayPtr - mpPtrArray)) // If there are not enough pointers in front of the current (first) one... + DoReallocPtrArray((size_type)(nSubarrayIncrease - (mItBegin.mpCurrentArrayPtr - mpPtrArray)), kSideFront); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(i = 1; i <= nSubarrayIncrease; ++i) + mItBegin.mpCurrentArrayPtr[-i] = DoAllocateSubarray(); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(difference_type j = 1; j < i; ++j) + DoFreeSubarray(mItBegin.mpCurrentArrayPtr[-j]); + throw; + } + #endif + } + + return mItBegin - (difference_type)nAdditionalCapacity; + } + else // else kSideBack + { + const size_type nCurrentAdditionalCapacity = (size_type)((mItEnd.mpEnd - 1) - mItEnd.mpCurrent); + + if(EASTL_UNLIKELY(nCurrentAdditionalCapacity < nAdditionalCapacity)) // If we need to grow forward into a new subarray... + { + const difference_type nSubarrayIncrease = (difference_type)(((nAdditionalCapacity - nCurrentAdditionalCapacity) + kDequeSubarraySize - 1) / kDequeSubarraySize); + difference_type i; + + if(nSubarrayIncrease > ((mpPtrArray + mnPtrArraySize) - mItEnd.mpCurrentArrayPtr) - 1) // If there are not enough pointers after the current (last) one... + DoReallocPtrArray((size_type)(nSubarrayIncrease - (((mpPtrArray + mnPtrArraySize) - mItEnd.mpCurrentArrayPtr) - 1)), kSideBack); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(i = 1; i <= nSubarrayIncrease; ++i) + mItEnd.mpCurrentArrayPtr[i] = DoAllocateSubarray(); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(difference_type j = 1; j < i; ++j) + DoFreeSubarray(mItEnd.mpCurrentArrayPtr[j]); + throw; + } + #endif + } + + return mItEnd + (difference_type)nAdditionalCapacity; + } + } + + + template + void DequeBase::DoReallocPtrArray(size_type nAdditionalCapacity, Side allocationSide) + { + // This function is not called unless the capacity is known to require a resize. + // + // We have an array of pointers (mpPtrArray), of which a segment of them are in use and + // at either end of the array are zero or more unused pointers. This function is being + // called because we need to extend the capacity on either side of this array by + // nAdditionalCapacity pointers. However, it's possible that if the user is continually + // using push_back and pop_front then the pointer array will continue to be extended + // on the back side and unused on the front side. So while we are doing this resizing + // here we also take the opportunity to recenter the pointers and thus be balanced. + // It man turn out that we don't even need to reallocate the pointer array in order + // to increase capacity on one side, as simply moving the pointers to the center may + // be enough to open up the requires space. + // + // Balanced pointer array Unbalanced pointer array (unused space at front, no free space at back) + // ----++++++++++++---- ---------+++++++++++ + + const size_type nUnusedPtrCountAtFront = (size_type)(mItBegin.mpCurrentArrayPtr - mpPtrArray); + const size_type nUsedPtrCount = (size_type)(mItEnd.mpCurrentArrayPtr - mItBegin.mpCurrentArrayPtr) + 1; + const size_type nUsedPtrSpace = nUsedPtrCount * sizeof(void*); + const size_type nUnusedPtrCountAtBack = (mnPtrArraySize - nUnusedPtrCountAtFront) - nUsedPtrCount; + value_type** pPtrArrayBegin; + + if((allocationSide == kSideBack) && (nAdditionalCapacity <= nUnusedPtrCountAtFront)) // If we can take advantage of unused pointers at the front without doing any reallocation... + { + if(nAdditionalCapacity < (nUnusedPtrCountAtFront / 2)) // Possibly use more space than required, if there's a lot of extra space. + nAdditionalCapacity = (nUnusedPtrCountAtFront / 2); + + pPtrArrayBegin = mpPtrArray + (nUnusedPtrCountAtFront - nAdditionalCapacity); + memmove(pPtrArrayBegin, mItBegin.mpCurrentArrayPtr, nUsedPtrSpace); + + #if EASTL_DEBUG + memset(pPtrArrayBegin + nUsedPtrCount, 0, (size_t)(mpPtrArray + mnPtrArraySize) - (size_t)(pPtrArrayBegin + nUsedPtrCount)); + #endif + } + else if((allocationSide == kSideFront) && (nAdditionalCapacity <= nUnusedPtrCountAtBack)) // If we can take advantage of unused pointers at the back without doing any reallocation... + { + if(nAdditionalCapacity < (nUnusedPtrCountAtBack / 2)) // Possibly use more space than required, if there's a lot of extra space. + nAdditionalCapacity = (nUnusedPtrCountAtBack / 2); + + pPtrArrayBegin = mItBegin.mpCurrentArrayPtr + nAdditionalCapacity; + memmove(pPtrArrayBegin, mItBegin.mpCurrentArrayPtr, nUsedPtrSpace); + + #if EASTL_DEBUG + memset(mpPtrArray, 0, (size_t)((uintptr_t)pPtrArrayBegin - (uintptr_t)mpPtrArray)); + #endif + } + else + { + // In this case we will have to do a reallocation. + const size_type nNewPtrArraySize = mnPtrArraySize + eastl::max_alt(mnPtrArraySize, nAdditionalCapacity) + 2; // Allocate extra capacity. + value_type** const pNewPtrArray = DoAllocatePtrArray(nNewPtrArraySize); + + pPtrArrayBegin = pNewPtrArray + (mItBegin.mpCurrentArrayPtr - mpPtrArray) + ((allocationSide == kSideFront) ? nAdditionalCapacity : 0); + + // The following is equivalent to: eastl::copy(mItBegin.mpCurrentArrayPtr, mItEnd.mpCurrentArrayPtr + 1, pPtrArrayBegin); + // It's OK to use memcpy instead of memmove because the destination is guaranteed to non-overlap the source. + if(mpPtrArray) // Could also say: 'if(mItBegin.mpCurrentArrayPtr)' + memcpy(pPtrArrayBegin, mItBegin.mpCurrentArrayPtr, nUsedPtrSpace); + + DoFreePtrArray(mpPtrArray, mnPtrArraySize); + + mpPtrArray = pNewPtrArray; + mnPtrArraySize = nNewPtrArraySize; + } + + // We need to reset the begin and end iterators, as code that calls this expects them to *not* be invalidated. + mItBegin.SetSubarray(pPtrArrayBegin); + mItEnd.SetSubarray((pPtrArrayBegin + nUsedPtrCount) - 1); + } + + + template + void DequeBase::DoInit(size_type n) + { + // This code is disabled because it doesn't currently work properly. + // We are trying to make it so that a deque can have a zero allocation + // initial empty state, but we (OK, I) am having a hard time making + // this elegant and efficient. + //if(n) + //{ + const size_type nNewPtrArraySize = (size_type)((n / kDequeSubarraySize) + 1); // Always have at least one, even if n is zero. + const size_type kMinPtrArraySize_ = kMinPtrArraySize; + + mnPtrArraySize = eastl::max_alt(kMinPtrArraySize_, (nNewPtrArraySize + 2)); + mpPtrArray = DoAllocatePtrArray(mnPtrArraySize); + + value_type** const pPtrArrayBegin = (mpPtrArray + ((mnPtrArraySize - nNewPtrArraySize) / 2)); // Try to place it in the middle. + value_type** const pPtrArrayEnd = pPtrArrayBegin + nNewPtrArraySize; + value_type** pPtrArrayCurrent = pPtrArrayBegin; + + #if EASTL_EXCEPTIONS_ENABLED + try + { + try + { + #endif + while(pPtrArrayCurrent < pPtrArrayEnd) + *pPtrArrayCurrent++ = DoAllocateSubarray(); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeSubarrays(pPtrArrayBegin, pPtrArrayCurrent); + throw; + } + } + catch(...) + { + DoFreePtrArray(mpPtrArray, mnPtrArraySize); + mpPtrArray = NULL; + mnPtrArraySize = 0; + throw; + } + #endif + + mItBegin.SetSubarray(pPtrArrayBegin); + mItBegin.mpCurrent = mItBegin.mpBegin; + + mItEnd.SetSubarray(pPtrArrayEnd - 1); + mItEnd.mpCurrent = mItEnd.mpBegin + (difference_type)(n % kDequeSubarraySize); + //} + //else // Else we do a zero-allocation initialization. + //{ + // mpPtrArray = NULL; + // mnPtrArraySize = 0; + // + // mItBegin.mpCurrentArrayPtr = NULL; + // mItBegin.mpBegin = NULL; + // mItBegin.mpEnd = NULL; // We intentionally create a situation whereby the subarray that has no capacity. + // mItBegin.mpCurrent = NULL; + // + // mItEnd = mItBegin; + //} + } + + + + /////////////////////////////////////////////////////////////////////// + // DequeIterator + /////////////////////////////////////////////////////////////////////// + + template + DequeIterator::DequeIterator() + : mpCurrent(NULL), mpBegin(NULL), mpEnd(NULL), mpCurrentArrayPtr(NULL) + { + // Empty + } + + + template + DequeIterator::DequeIterator(T** pCurrentArrayPtr, T* pCurrent) + : mpCurrent(pCurrent), mpBegin(*pCurrentArrayPtr), mpEnd(pCurrent + kDequeSubarraySize), mpCurrentArrayPtr(pCurrentArrayPtr) + { + // Empty + } + + + template + DequeIterator::DequeIterator(const iterator& x) + : mpCurrent(x.mpCurrent), mpBegin(x.mpBegin), mpEnd(x.mpEnd), mpCurrentArrayPtr(x.mpCurrentArrayPtr) + { + // Empty + } + + + template + DequeIterator::DequeIterator(const iterator& x, Increment) + : mpCurrent(x.mpCurrent), mpBegin(x.mpBegin), mpEnd(x.mpEnd), mpCurrentArrayPtr(x.mpCurrentArrayPtr) + { + operator++(); + } + + + template + DequeIterator::DequeIterator(const iterator& x, Decrement) + : mpCurrent(x.mpCurrent), mpBegin(x.mpBegin), mpEnd(x.mpEnd), mpCurrentArrayPtr(x.mpCurrentArrayPtr) + { + operator--(); + } + + + template + typename DequeIterator::pointer + DequeIterator::operator->() const + { + return mpCurrent; + } + + + template + typename DequeIterator::reference + DequeIterator::operator*() const + { + return *mpCurrent; + } + + + template + typename DequeIterator::this_type& + DequeIterator::operator++() + { + if(EASTL_UNLIKELY(++mpCurrent == mpEnd)) + { + mpBegin = *++mpCurrentArrayPtr; + mpEnd = mpBegin + kDequeSubarraySize; + mpCurrent = mpBegin; + } + return *this; + } + + + template + typename DequeIterator::this_type + DequeIterator::operator++(int) + { + const this_type temp(*this); + operator++(); + return temp; + } + + + template + typename DequeIterator::this_type& + DequeIterator::operator--() + { + if(EASTL_UNLIKELY(mpCurrent == mpBegin)) + { + mpBegin = *--mpCurrentArrayPtr; + mpEnd = mpBegin + kDequeSubarraySize; + mpCurrent = mpEnd; // fall through... + } + --mpCurrent; + return *this; + } + + + template + typename DequeIterator::this_type + DequeIterator::operator--(int) + { + const this_type temp(*this); + operator--(); + return temp; + } + + + template + typename DequeIterator::this_type& + DequeIterator::operator+=(difference_type n) + { + const difference_type subarrayPosition = (mpCurrent - mpBegin) + n; + + // Cast from signed to unsigned (size_t) in order to obviate the need to compare to < 0. + if((size_t)subarrayPosition < (size_t)kDequeSubarraySize) // If the new position is within the current subarray (i.e. >= 0 && < kSubArraySize)... + mpCurrent += n; + else + { + // This implementation is a branchless version which works by offsetting + // the math to always be in the positive range. Much of the values here + // reduce to constants and both the multiplication and division are of + // power of two sizes and so this calculation ends up compiling down to + // just one addition, one shift and one subtraction. This algorithm has + // a theoretical weakness in that on 32 bit systems it will fail if the + // value of n is >= (2^32 - 2^24) or 4,278,190,080 of if kDequeSubarraySize + // is >= 2^24 or 16,777,216. + EASTL_CT_ASSERT((kDequeSubarraySize & (kDequeSubarraySize - 1)) == 0); // Verify that it is a power of 2. + const difference_type subarrayIndex = (((16777216 + subarrayPosition) / (difference_type)kDequeSubarraySize)) - (16777216 / (difference_type)kDequeSubarraySize); + + SetSubarray(mpCurrentArrayPtr + subarrayIndex); + mpCurrent = mpBegin + (subarrayPosition - (subarrayIndex * (difference_type)kDequeSubarraySize)); + } + return *this; + } + + + template + typename DequeIterator::this_type& + DequeIterator::operator-=(difference_type n) + { + return (*this).operator+=(-n); + } + + + template + typename DequeIterator::this_type + DequeIterator::operator+(difference_type n) const + { + return this_type(*this).operator+=(n); + } + + + template + typename DequeIterator::this_type + DequeIterator::operator-(difference_type n) const + { + return this_type(*this).operator+=(-n); + } + + + template + typename DequeIterator::this_type + DequeIterator::copy(const iterator& first, const iterator& last, true_type) + { + // To do: Implement this as a loop which does memcpys between subarrays appropriately. + // Currently we only do memcpy if the entire operation occurs within a single subarray. + if((first.mpBegin == last.mpBegin) && (first.mpBegin == mpBegin)) // If all operations are within the same subarray, implement the operation as a memmove. + { + memmove(mpCurrent, first.mpCurrent, (size_t)((uintptr_t)last.mpCurrent - (uintptr_t)first.mpCurrent)); + return *this + (last.mpCurrent - first.mpCurrent); + } + return eastl::copy(eastl::make_move_iterator(first), eastl::make_move_iterator(last), eastl::make_move_iterator(*this)).base(); + } + + + template + typename DequeIterator::this_type + DequeIterator::copy(const iterator& first, const iterator& last, false_type) + { + return eastl::copy(eastl::make_move_iterator(first), eastl::make_move_iterator(last), eastl::make_move_iterator(*this)).base(); + } + + + template + void DequeIterator::copy_backward(const iterator& first, const iterator& last, true_type) + { + // To do: Implement this as a loop which does memmoves between subarrays appropriately. + // Currently we only do memcpy if the entire operation occurs within a single subarray. + if((first.mpBegin == last.mpBegin) && (first.mpBegin == mpBegin)) // If all operations are within the same subarray, implement the operation as a memcpy. + memmove(mpCurrent - (last.mpCurrent - first.mpCurrent), first.mpCurrent, (size_t)((uintptr_t)last.mpCurrent - (uintptr_t)first.mpCurrent)); + else + eastl::copy_backward(eastl::make_move_iterator(first), eastl::make_move_iterator(last), eastl::make_move_iterator(*this)); + } + + + template + void DequeIterator::copy_backward(const iterator& first, const iterator& last, false_type) + { + eastl::copy_backward(eastl::make_move_iterator(first), eastl::make_move_iterator(last), eastl::make_move_iterator(*this)).base(); + } + + + template + void DequeIterator::SetSubarray(T** pCurrentArrayPtr) + { + mpCurrentArrayPtr = pCurrentArrayPtr; + mpBegin = *pCurrentArrayPtr; + mpEnd = mpBegin + kDequeSubarraySize; + } + + + // 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 + inline bool operator==(const DequeIterator& a, + const DequeIterator& b) + { + return a.mpCurrent == b.mpCurrent; + } + + + template + inline bool operator!=(const DequeIterator& a, + const DequeIterator& b) + { + return a.mpCurrent != b.mpCurrent; + } + + + // 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 + inline bool operator!=(const DequeIterator& a, + const DequeIterator& b) + { + return a.mpCurrent != b.mpCurrent; + } + + + template + inline bool operator<(const DequeIterator& a, + const DequeIterator& b) + { + return (a.mpCurrentArrayPtr == b.mpCurrentArrayPtr) ? (a.mpCurrent < b.mpCurrent) : (a.mpCurrentArrayPtr < b.mpCurrentArrayPtr); + } + + + template + inline bool operator>(const DequeIterator& a, + const DequeIterator& b) + { + return (a.mpCurrentArrayPtr == b.mpCurrentArrayPtr) ? (a.mpCurrent > b.mpCurrent) : (a.mpCurrentArrayPtr > b.mpCurrentArrayPtr); + } + + + template + inline bool operator<=(const DequeIterator& a, + const DequeIterator& b) + { + return (a.mpCurrentArrayPtr == b.mpCurrentArrayPtr) ? (a.mpCurrent <= b.mpCurrent) : (a.mpCurrentArrayPtr <= b.mpCurrentArrayPtr); + } + + + template + inline bool operator>=(const DequeIterator& a, + const DequeIterator& b) + { + return (a.mpCurrentArrayPtr == b.mpCurrentArrayPtr) ? (a.mpCurrent >= b.mpCurrent) : (a.mpCurrentArrayPtr >= b.mpCurrentArrayPtr); + } + + + // Random access iterators must support operator + and operator -. + // You can only add an integer to an iterator, and you cannot add two iterators. + template + inline DequeIterator + operator+(ptrdiff_t n, const DequeIterator& x) + { + return x + n; // Implement (n + x) in terms of (x + n). + } + + + // You can only add an integer to an iterator, but you can subtract two iterators. + // The C++ defect report #179 mentioned above specifically refers to + // operator - and states that we support the subtraction of const and non-const iterators. + template + inline typename DequeIterator::difference_type + operator-(const DequeIterator& a, + const DequeIterator& b) + { + // This is a fairly clever algorithm that has been used in STL deque implementations since the original HP STL: + typedef typename DequeIterator::difference_type difference_type; + + return ((difference_type)kDequeSubarraySize * ((a.mpCurrentArrayPtr - b.mpCurrentArrayPtr) - 1)) + (a.mpCurrent - a.mpBegin) + (b.mpEnd - b.mpCurrent); + } + + + + + /////////////////////////////////////////////////////////////////////// + // deque + /////////////////////////////////////////////////////////////////////// + + template + inline deque::deque() + : base_type((size_type)0) + { + // Empty + } + + + template + inline deque::deque(const allocator_type& allocator) + : base_type((size_type)0, allocator) + { + // Empty + } + + + template + inline deque::deque(size_type n, const allocator_type& allocator) + : base_type(n, allocator) + { + DoFillInit(value_type()); + } + + + template + inline deque::deque(size_type n, const value_type& value, const allocator_type& allocator) + : base_type(n, allocator) + { + DoFillInit(value); + } + + + template + inline deque::deque(const this_type& x) + : base_type(x.size(), x.mAllocator) + { + eastl::uninitialized_copy(x.mItBegin, x.mItEnd, mItBegin); + } + + + template + inline deque::deque(this_type&& x) + : base_type((size_type)0, x.mAllocator) + { + swap(x); + } + + + template + inline deque::deque(this_type&& x, const allocator_type& allocator) + : base_type((size_type)0, allocator) + { + swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. + } + + + template + inline deque::deque(std::initializer_list ilist, const allocator_type& allocator) + : base_type(allocator) + { + DoInit(ilist.begin(), ilist.end(), false_type()); + } + + + template + template + inline deque::deque(InputIterator first, InputIterator last) + : base_type(EASTL_DEQUE_DEFAULT_ALLOCATOR) // Call the empty base constructor, which does nothing. We need to do all the work in our own DoInit. + { + DoInit(first, last, is_integral()); + } + + + template + inline deque::~deque() + { + // Call destructors. Parent class will free the memory. + for(iterator itCurrent(mItBegin); itCurrent != mItEnd; ++itCurrent) + itCurrent.mpCurrent->~value_type(); + } + + + template + typename deque::this_type& + deque::operator=(const this_type& x) + { + if(&x != this) // If not assigning to ourselves... + { + // If (EASTL_ALLOCATOR_COPY_ENABLED == 1) and the current contents are allocated by an + // allocator that's unequal to x's allocator, we need to reallocate our elements with + // our current allocator and reallocate it with x's allocator. If the allocators are + // equal then we can use a more optimal algorithm that doesn't reallocate our elements + // but instead can copy them in place. + + #if EASTL_ALLOCATOR_COPY_ENABLED + bool bSlowerPathwayRequired = (mAllocator != x.mAllocator); + #else + bool bSlowerPathwayRequired = false; + #endif + + if(bSlowerPathwayRequired) + { + // We can't currently use set_capacity(0) or shrink_to_fit, because they + // leave a remaining allocation with our old allocator. So we do a similar + // thing but set our allocator to x.mAllocator while doing so. + this_type temp(x.mAllocator); + DoSwap(temp); + // Now we have an empty container with an allocator equal to x.mAllocator, ready to assign from x. + } + + DoAssign(x.begin(), x.end(), eastl::false_type()); + } + + return *this; + } + + + template + inline typename deque::this_type& + deque::operator=(this_type&& x) + { + if(this != &x) + { + set_capacity(0); // To consider: Are we really required to clear here? x is going away soon and will clear itself in its dtor. + swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. + } + return *this; + } + + + template + inline typename deque::this_type& + deque::operator=(std::initializer_list ilist) + { + DoAssign(ilist.begin(), ilist.end(), false_type()); + return *this; + } + + + template + inline void deque::assign(size_type n, const value_type& value) + { + DoAssignValues(n, value); + } + + + template + inline void deque::assign(std::initializer_list ilist) + { + DoAssign(ilist.begin(), ilist.end(), false_type()); + } + + + // It turns out that the C++ std::deque specifies a two argument + // version of assign that takes (int size, int value). These are not + // iterators, so we need to do a template compiler trick to do the right thing. + template + template + inline void deque::assign(InputIterator first, InputIterator last) + { + DoAssign(first, last, is_integral()); + } + + + template + inline typename deque::iterator + deque::begin() EA_NOEXCEPT + { + return mItBegin; + } + + + template + inline typename deque::const_iterator + deque::begin() const EA_NOEXCEPT + { + return mItBegin; + } + + + template + inline typename deque::const_iterator + deque::cbegin() const EA_NOEXCEPT + { + return mItBegin; + } + + + template + inline typename deque::iterator + deque::end() EA_NOEXCEPT + { + return mItEnd; + } + + + template + typename deque::const_iterator + deque::end() const EA_NOEXCEPT + { + return mItEnd; + } + + + template + inline typename deque::const_iterator + deque::cend() const EA_NOEXCEPT + { + return mItEnd; + } + + + template + inline typename deque::reverse_iterator + deque::rbegin() EA_NOEXCEPT + { + return reverse_iterator(mItEnd); + } + + + template + inline typename deque::const_reverse_iterator + deque::rbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(mItEnd); + } + + + template + inline typename deque::const_reverse_iterator + deque::crbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(mItEnd); + } + + + template + inline typename deque::reverse_iterator + deque::rend() EA_NOEXCEPT + { + return reverse_iterator(mItBegin); + } + + + template + inline typename deque::const_reverse_iterator + deque::rend() const EA_NOEXCEPT + { + return const_reverse_iterator(mItBegin); + } + + + template + inline typename deque::const_reverse_iterator + deque::crend() const EA_NOEXCEPT + { + return const_reverse_iterator(mItBegin); + } + + + template + inline bool deque::empty() const EA_NOEXCEPT + { + return mItBegin.mpCurrent == mItEnd.mpCurrent; + } + + + template + typename deque::size_type + inline deque::size() const EA_NOEXCEPT + { + return (size_type)(mItEnd - mItBegin); + } + + + template + inline void deque::resize(size_type n, const value_type& value) + { + const size_type nSizeCurrent = size(); + + if(n > nSizeCurrent) // We expect that more often than not, resizes will be upsizes. + insert(mItEnd, n - nSizeCurrent, value); + else + erase(mItBegin + (difference_type)n, mItEnd); + } + + + template + inline void deque::resize(size_type n) + { + resize(n, value_type()); + } + + + template + inline void deque::shrink_to_fit() + { + this_type x(eastl::make_move_iterator(begin()), eastl::make_move_iterator(end())); + swap(x); + } + + + template + inline void deque::set_capacity(size_type n) + { + // Currently there isn't a way to remove all allocations from a deque, as it + // requires a single starting allocation for the subarrays. So we can't just + // free all memory without leaving it in a bad state. So the best means of + // implementing set_capacity() is to do what we do below. + + if(n == 0) + { + this_type temp(mAllocator); + DoSwap(temp); + } + else if(n < size()) + { + // We currently ignore the request to reduce capacity. To do: Implement this + // and do it in a way that doesn't result in temporarily ~doubling our memory usage. + // That might involve trimming unused subarrays from the front or back of + // the container. + resize(n); + } + } + + + template + typename deque::reference + deque::operator[](size_type n) + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(n >= (size_type)(mItEnd - mItBegin))) + EASTL_FAIL_MSG("deque::operator[] -- out of range"); + #elif EASTL_ASSERT_ENABLED + // We allow taking a reference to deque[0] + if (EASTL_UNLIKELY((n != 0) && n >= (size_type)(mItEnd - mItBegin))) + EASTL_FAIL_MSG("deque::operator[] -- out of range"); + #endif + + // See DequeIterator::operator+=() for an explanation of the code below. + iterator it(mItBegin); + + const difference_type subarrayPosition = (difference_type)((it.mpCurrent - it.mpBegin) + (difference_type)n); + const difference_type subarrayIndex = (((16777216 + subarrayPosition) / (difference_type)kDequeSubarraySize)) - (16777216 / (difference_type)kDequeSubarraySize); + + return *(*(it.mpCurrentArrayPtr + subarrayIndex) + (subarrayPosition - (subarrayIndex * (difference_type)kDequeSubarraySize))); + } + + + template + typename deque::const_reference + deque::operator[](size_type n) const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(n >= (size_type)(mItEnd - mItBegin))) + EASTL_FAIL_MSG("deque::operator[] -- out of range"); + #elif EASTL_ASSERT_ENABLED + // We allow the user to use a reference to deque[0] of an empty container. + if (EASTL_UNLIKELY((n != 0) && n >= (size_type)(mItEnd - mItBegin))) + EASTL_FAIL_MSG("deque::operator[] -- out of range"); + #endif + + // See DequeIterator::operator+=() for an explanation of the code below. + iterator it(mItBegin); + + const difference_type subarrayPosition = (it.mpCurrent - it.mpBegin) + (difference_type)n; + const difference_type subarrayIndex = (((16777216 + subarrayPosition) / (difference_type)kDequeSubarraySize)) - (16777216 / (difference_type)kDequeSubarraySize); + + return *(*(it.mpCurrentArrayPtr + subarrayIndex) + (subarrayPosition - (subarrayIndex * (difference_type)kDequeSubarraySize))); + } + + + template + typename deque::reference + deque::at(size_type n) + { + #if EASTL_EXCEPTIONS_ENABLED + if(n >= (size_type)(mItEnd - mItBegin)) + throw std::out_of_range("deque::at -- out of range"); + #elif EASTL_ASSERT_ENABLED + if(n >= (size_type)(mItEnd - mItBegin)) + EASTL_FAIL_MSG("deque::at -- out of range"); + #endif + return *(mItBegin.operator+((difference_type)n)); + } + + + template + typename deque::const_reference + deque::at(size_type n) const + { + #if EASTL_EXCEPTIONS_ENABLED + if(n >= (size_type)(mItEnd - mItBegin)) + throw std::out_of_range("deque::at -- out of range"); + #elif EASTL_ASSERT_ENABLED + if(n >= (size_type)(mItEnd - mItBegin)) + EASTL_FAIL_MSG("deque::at -- out of range"); + #endif + return *(mItBegin.operator+((difference_type)n)); + } + + + template + typename deque::reference + deque::front() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY((size_type)(mItEnd == mItBegin))) + EASTL_FAIL_MSG("deque::front -- empty deque"); + #else + // We allow the user to reference an empty container. + #endif + + return *mItBegin; + } + + + template + typename deque::const_reference + deque::front() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY((size_type)(mItEnd == mItBegin))) + EASTL_FAIL_MSG("deque::front -- empty deque"); + #else + // We allow the user to reference an empty container. + #endif + + return *mItBegin; + } + + + template + typename deque::reference + deque::back() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY((size_type)(mItEnd == mItBegin))) + EASTL_FAIL_MSG("deque::back -- empty deque"); + #else + // We allow the user to reference an empty container. + #endif + + return *iterator(mItEnd, typename iterator::Decrement()); + } + + + template + typename deque::const_reference + deque::back() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY((size_type)(mItEnd == mItBegin))) + EASTL_FAIL_MSG("deque::back -- empty deque"); + #else + // We allow the user to reference an empty container. + #endif + + return *iterator(mItEnd, typename iterator::Decrement()); + } + + + template + void deque::push_front(const value_type& value) + { + emplace_front(value); + } + + + template + void deque::push_front(value_type&& value) + { + emplace_front(eastl::move(value)); + } + + + template + typename deque::reference + deque::push_front() + { + emplace_front(value_type()); + return *mItBegin; // Same as return front(); + } + + + template + void deque::push_back(const value_type& value) + { + emplace_back(value); + } + + + template + void deque::push_back(value_type&& value) + { + emplace_back(eastl::move(value)); + } + + + template + typename deque::reference + deque::push_back() + { + emplace_back(value_type()); + return *iterator(mItEnd, typename iterator::Decrement()); // Same thing as return back(); + } + + + template + void deque::pop_front() + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY((size_type)(mItEnd == mItBegin))) + EASTL_FAIL_MSG("deque::pop_front -- empty deque"); + #endif + + if((mItBegin.mpCurrent + 1) != mItBegin.mpEnd) // If the operation is very simple... + (mItBegin.mpCurrent++)->~value_type(); + else + { + // This is executed only when we are popping the end (last) item off the front-most subarray. + // In this case we need to free the subarray and point mItBegin to the next subarray. + #ifdef EA_DEBUG + value_type** pp = mItBegin.mpCurrentArrayPtr; + #endif + + mItBegin.mpCurrent->~value_type(); // mpCurrent == mpEnd - 1 + DoFreeSubarray(mItBegin.mpBegin); + mItBegin.SetSubarray(mItBegin.mpCurrentArrayPtr + 1); + mItBegin.mpCurrent = mItBegin.mpBegin; + + #ifdef EA_DEBUG + *pp = NULL; + #endif + } + } + + + template + void deque::pop_back() + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY((size_type)(mItEnd == mItBegin))) + EASTL_FAIL_MSG("deque::pop_back -- empty deque"); + #endif + + if(mItEnd.mpCurrent != mItEnd.mpBegin) // If the operation is very simple... + (--mItEnd.mpCurrent)->~value_type(); + else + { + // This is executed only when we are popping the first item off the last subarray. + // In this case we need to free the subarray and point mItEnd to the previous subarray. + #ifdef EA_DEBUG + value_type** pp = mItEnd.mpCurrentArrayPtr; + #endif + + DoFreeSubarray(mItEnd.mpBegin); + mItEnd.SetSubarray(mItEnd.mpCurrentArrayPtr - 1); + mItEnd.mpCurrent = mItEnd.mpEnd - 1; // Recall that mItEnd points to one-past the last item in the container. + mItEnd.mpCurrent->~value_type(); // Thus we need to call the destructor on the item *before* that last item. + + #ifdef EA_DEBUG + *pp = NULL; + #endif + } + } + + + template + template + typename deque::iterator + deque::emplace(const_iterator position, Args&&... args) + { + if(EASTL_UNLIKELY(position.mpCurrent == mItEnd.mpCurrent)) // If we are doing the same thing as push_back... + { + emplace_back(eastl::forward(args)...); + return iterator(mItEnd, typename iterator::Decrement()); // Unfortunately, we need to make an iterator here, as the above push_back is an operation that can invalidate existing iterators. + } + else if(EASTL_UNLIKELY(position.mpCurrent == mItBegin.mpCurrent)) // If we are doing the same thing as push_front... + { + emplace_front(eastl::forward(args)...); + return mItBegin; + } + + iterator itPosition(position, typename iterator::FromConst()); + value_type valueSaved(eastl::forward(args)...); // We need to save this because value may come from within our container. It would be somewhat tedious to make a workaround that could avoid this. + const difference_type i(itPosition - mItBegin); + + #if EASTL_ASSERT_ENABLED + EASTL_ASSERT(!empty()); // The push_front and push_back calls below assume that we are non-empty. It turns out this is never called unless so. + + if(EASTL_UNLIKELY(!(validate_iterator(itPosition) & isf_valid))) + EASTL_FAIL_MSG("deque::emplace -- invalid iterator"); + #endif + + if(i < (difference_type)(size() / 2)) // Should we insert at the front or at the back? We divide the range in half. + { + emplace_front(eastl::move(*mItBegin)); // This operation potentially invalidates all existing iterators and so we need to assign them anew relative to mItBegin below. + + itPosition = mItBegin + i; + + const iterator newPosition (itPosition, typename iterator::Increment()); + iterator oldBegin (mItBegin, typename iterator::Increment()); + const iterator oldBeginPlus1(oldBegin, typename iterator::Increment()); + + oldBegin.copy(oldBeginPlus1, newPosition, eastl::has_trivial_relocate()); + } + else + { + emplace_back(eastl::move(*iterator(mItEnd, typename iterator::Decrement()))); + + itPosition = mItBegin + i; + + iterator oldBack (mItEnd, typename iterator::Decrement()); + const iterator oldBackMinus1(oldBack, typename iterator::Decrement()); + + oldBack.copy_backward(itPosition, oldBackMinus1, eastl::has_trivial_relocate()); + } + + *itPosition = eastl::move(valueSaved); + + return itPosition; + } + + template + template + void deque::emplace_front(Args&&... args) + { + if(mItBegin.mpCurrent != mItBegin.mpBegin) // If we have room in the first subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. + ::new((void*)--mItBegin.mpCurrent) value_type(eastl::forward(args)...); // Construct in place. If args is a single arg of type value_type&& then it this will be a move construction. + else + { + // To consider: Detect if value isn't coming from within this container and handle that efficiently. + value_type valueSaved(eastl::forward(args)...); // We need to make a temporary, because args may be a value_type that comes from within our container and the operations below may change the container. But we can use move instead of copy. + + if(mItBegin.mpCurrentArrayPtr == mpPtrArray) // If there are no more pointers in front of the current (first) one... + DoReallocPtrArray(1, kSideFront); + + mItBegin.mpCurrentArrayPtr[-1] = DoAllocateSubarray(); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + mItBegin.SetSubarray(mItBegin.mpCurrentArrayPtr - 1); + mItBegin.mpCurrent = mItBegin.mpEnd - 1; + ::new((void*)mItBegin.mpCurrent) value_type(eastl::move(valueSaved)); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + ++mItBegin; // The exception could only occur in the new operation above, after we have incremented mItBegin. So we need to undo it. + DoFreeSubarray(mItBegin.mpCurrentArrayPtr[-1]); + throw; + } + #endif + } + } + + template + template + void deque::emplace_back(Args&&... args) + { + if((mItEnd.mpCurrent + 1) != mItEnd.mpEnd) // If we have room in the last subarray... we hope that usually this 'new' pathway gets executed, as it is slightly faster. + ::new((void*)mItEnd.mpCurrent++) value_type(eastl::forward(args)...); // Construct in place. If args is a single arg of type value_type&& then it this will be a move construction. + else + { + // To consider: Detect if value isn't coming from within this container and handle that efficiently. + value_type valueSaved(eastl::forward(args)...); // We need to make a temporary, because args may be a value_type that comes from within our container and the operations below may change the container. But we can use move instead of copy. + if(((mItEnd.mpCurrentArrayPtr - mpPtrArray) + 1) >= (difference_type)mnPtrArraySize) // If there are no more pointers after the current (last) one. + DoReallocPtrArray(1, kSideBack); + + mItEnd.mpCurrentArrayPtr[1] = DoAllocateSubarray(); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new((void*)mItEnd.mpCurrent) value_type(eastl::move(valueSaved)); // We can move valueSaved into position. + mItEnd.SetSubarray(mItEnd.mpCurrentArrayPtr + 1); + mItEnd.mpCurrent = mItEnd.mpBegin; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + // No need to execute '--mItEnd', as the exception could only occur in the new operation above before we set mItEnd. + DoFreeSubarray(mItEnd.mpCurrentArrayPtr[1]); + throw; + } + #endif + } + } + + + template + typename deque::iterator + deque::insert(const_iterator position, const value_type& value) + { + return emplace(position, value); + } + + + template + typename deque::iterator + deque::insert(const_iterator position, value_type&& value) + { + return emplace(position, eastl::move(value)); + } + + + template + void deque::insert(const_iterator position, size_type n, const value_type& value) + { + DoInsertValues(position, n, value); + } + + + template + template + void deque::insert(const_iterator position, InputIterator first, InputIterator last) + { + DoInsert(position, first, last, is_integral()); // The C++ standard requires this sort of behaviour, as InputIterator might actually be Integer and 'first' is really 'count' and 'last' is really 'value'. + } + + + template + typename deque::iterator + deque::insert(const_iterator position, std::initializer_list ilist) + { + const difference_type i(position - mItBegin); + DoInsert(position, ilist.begin(), ilist.end(), false_type()); + return (mItBegin + i); + } + + + template + typename deque::iterator + deque::erase(const_iterator position) + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(!(validate_iterator(position) & isf_valid))) + EASTL_FAIL_MSG("deque::erase -- invalid iterator"); + + if(EASTL_UNLIKELY(position == end())) + EASTL_FAIL_MSG("deque::erase -- end() iterator is an invalid iterator for erase"); + #endif + + iterator itPosition(position, typename iterator::FromConst()); + iterator itNext(itPosition, typename iterator::Increment()); + const difference_type i(itPosition - mItBegin); + + if(i < (difference_type)(size() / 2)) // Should we move the front entries forward or the back entries backward? We divide the range in half. + { + itNext.copy_backward(mItBegin, itPosition, eastl::has_trivial_relocate()); + pop_front(); + } + else + { + itPosition.copy(itNext, mItEnd, eastl::has_trivial_relocate()); + pop_back(); + } + + return mItBegin + i; + } + + + template + typename deque::iterator + deque::erase(const_iterator first, const_iterator last) + { + iterator itFirst(first, typename iterator::FromConst()); + iterator itLast(last, typename iterator::FromConst()); + + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(!(validate_iterator(itFirst) & isf_valid))) + EASTL_FAIL_MSG("deque::erase -- invalid iterator"); + if(EASTL_UNLIKELY(!(validate_iterator(itLast) & isf_valid))) + EASTL_FAIL_MSG("deque::erase -- invalid iterator"); + #endif + + if((itFirst != mItBegin) || (itLast != mItEnd)) // If not erasing everything... (We expect that the user won't call erase(begin, end) because instead the user would just call clear.) + { + const difference_type n(itLast - itFirst); + const difference_type i(itFirst - mItBegin); + + if(i < (difference_type)((size() - n) / 2)) // Should we move the front entries forward or the back entries backward? We divide the range in half. + { + const iterator itNewBegin(mItBegin + n); + value_type** const pPtrArrayBegin = mItBegin.mpCurrentArrayPtr; + + itLast.copy_backward(mItBegin, itFirst, eastl::has_trivial_relocate()); + + for(; mItBegin != itNewBegin; ++mItBegin) // Question: If value_type is a POD type, will the compiler generate this loop at all? + mItBegin.mpCurrent->~value_type(); // If so, then we need to make a specialization for destructing PODs. + + DoFreeSubarrays(pPtrArrayBegin, itNewBegin.mpCurrentArrayPtr); + + // mItBegin = itNewBegin; <-- Not necessary, as the above loop makes it so already. + } + else // Else we will be moving back entries backward. + { + iterator itNewEnd(mItEnd - n); + value_type** const pPtrArrayEnd = itNewEnd.mpCurrentArrayPtr + 1; + + itFirst.copy(itLast, mItEnd, eastl::has_trivial_relocate()); + + for(iterator itTemp(itNewEnd); itTemp != mItEnd; ++itTemp) + itTemp.mpCurrent->~value_type(); + + DoFreeSubarrays(pPtrArrayEnd, mItEnd.mpCurrentArrayPtr + 1); + + mItEnd = itNewEnd; + } + + return mItBegin + i; + } + + clear(); + return mItEnd; + } + + + template + typename deque::reverse_iterator + deque::erase(reverse_iterator position) + { + return reverse_iterator(erase((++position).base())); + } + + + template + typename deque::reverse_iterator + deque::erase(reverse_iterator first, reverse_iterator last) + { + // Version which erases in order from first to last. + // difference_type i(first.base() - last.base()); + // while(i--) + // first = erase(first); + // return first; + + // Version which erases in order from last to first, but is slightly more efficient: + return reverse_iterator(erase(last.base(), first.base())); + } + + + template + void deque::clear() + { + // Destroy all values and all subarrays they belong to, except for the first one, + // as we need to reserve some space for a valid mItBegin/mItEnd. + if(mItBegin.mpCurrentArrayPtr != mItEnd.mpCurrentArrayPtr) // If there are multiple subarrays (more often than not, this will be so)... + { + for(value_type* p1 = mItBegin.mpCurrent; p1 < mItBegin.mpEnd; ++p1) + p1->~value_type(); + for(value_type* p2 = mItEnd.mpBegin; p2 < mItEnd.mpCurrent; ++p2) + p2->~value_type(); + DoFreeSubarray(mItEnd.mpBegin); // Leave mItBegin with a valid subarray. + } + else + { + for(value_type* p = mItBegin.mpCurrent; p < mItEnd.mpCurrent; ++p) + p->~value_type(); + // Don't free the one existing subarray, as we need it for mItBegin/mItEnd. + } + + for(value_type** pPtrArray = mItBegin.mpCurrentArrayPtr + 1; pPtrArray < mItEnd.mpCurrentArrayPtr; ++pPtrArray) + { + for(value_type* p = *pPtrArray, *pEnd = *pPtrArray + kDequeSubarraySize; p < pEnd; ++p) + p->~value_type(); + DoFreeSubarray(*pPtrArray); + } + + mItEnd = mItBegin; // mItBegin/mItEnd will not be dereferencable. + } + + + //template + //void deque::reset_lose_memory() + //{ + // // The reset_lose_memory 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. + // + // // Currently we are unable to get this reset_lose_memory operation to work correctly + // // as we haven't been able to find a good way to have a deque initialize + // // without allocating memory. We can lose the old memory, but DoInit + // // would necessarily do a ptrArray allocation. And this is not within + // // our definition of how reset_lose_memory works. + // base_type::DoInit(0); + // + //} + + + template + void deque::swap(deque& x) + { + #if defined(EASTL_DEQUE_LEGACY_SWAP_BEHAVIOUR_REQUIRES_COPY_CTOR) && EASTL_DEQUE_LEGACY_SWAP_BEHAVIOUR_REQUIRES_COPY_CTOR + if(mAllocator == x.mAllocator) // If allocators are equivalent... + DoSwap(x); + else // else swap the contents. + { + const this_type temp(*this); // Can't call eastl::swap because that would + *this = x; // itself call this member swap function. + x = temp; + } + #else + // NOTE(rparolin): The previous implementation required T to be copy-constructible in the fall-back case where + // allocators with unique instances copied elements. This was an unnecessary restriction and prevented the common + // usage of deque with non-copyable types (eg. eastl::deque or eastl::deque). + // + // The previous implementation violated the following requirements of deque::swap so the fall-back code has + // been removed. EASTL implicitly defines 'propagate_on_container_swap = false' therefore the fall-back case is + // undefined behaviour. We simply swap the contents and the allocator as that is the common expectation of + // users and does not put the container into an invalid state since it can not free its memory via its current + // allocator instance. + // + DoSwap(x); + #endif + } + + + template + template + void deque::DoInit(Integer n, Integer value, true_type) + { + base_type::DoInit(n); // Call the base uninitialized init function. + DoFillInit(value); + } + + + template + template + void deque::DoInit(InputIterator first, InputIterator last, false_type) + { + typedef typename eastl::iterator_traits::iterator_category IC; + DoInitFromIterator(first, last, IC()); + } + + + template + template + void deque::DoInitFromIterator(InputIterator first, InputIterator last, EASTL_ITC_NS::input_iterator_tag) + { + base_type::DoInit(0); // Call the base uninitialized init function, but don't actually allocate any values. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + // We have little choice but to turn through the source iterator and call + // push_back for each item. It can be slow because it will keep reallocating the + // container memory as we go. We are not allowed to use distance() on an InputIterator. + for(; first != last; ++first) // InputIterators by definition actually only allow you to iterate through them once. + { // Thus the standard *requires* that we do this (inefficient) implementation. + push_back(*first); // Luckily, InputIterators are in practice almost never used, so this code will likely never get executed. + } + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + clear(); + throw; + } + #endif + } + + + template + template + void deque::DoInitFromIterator(ForwardIterator first, ForwardIterator last, EASTL_ITC_NS::forward_iterator_tag) + { + typedef typename eastl::remove_const::type non_const_iterator_type; // If T is a const type (e.g. const int) then we need to initialize it as if it were non-const. + typedef typename eastl::remove_const::type non_const_value_type; + + const size_type n = (size_type)eastl::distance(first, last); + value_type** pPtrArrayCurrent; + + base_type::DoInit(n); // Call the base uninitialized init function. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(pPtrArrayCurrent = mItBegin.mpCurrentArrayPtr; pPtrArrayCurrent < mItEnd.mpCurrentArrayPtr; ++pPtrArrayCurrent) // Copy to the known-to-be-completely-used subarrays. + { + // We implment an algorithm here whereby we use uninitialized_copy() and advance() instead of just iterating from first to last and constructing as we go. The reason for this is that we can take advantage of POD data types and implement construction as memcpy operations. + ForwardIterator current(first); // To do: Implement a specialization of this algorithm for non-PODs which eliminates the need for 'current'. + + eastl::advance(current, kDequeSubarraySize); + eastl::uninitialized_copy((non_const_iterator_type)first, (non_const_iterator_type)current, (non_const_value_type*)*pPtrArrayCurrent); + first = current; + } + + eastl::uninitialized_copy((non_const_iterator_type)first, (non_const_iterator_type)last, (non_const_value_type*)mItEnd.mpBegin); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(iterator itCurrent(mItBegin), itEnd(pPtrArrayCurrent, *pPtrArrayCurrent); itCurrent != itEnd; ++itCurrent) + itCurrent.mpCurrent->~value_type(); + throw; + } + #endif + } + + + template + void deque::DoFillInit(const value_type& value) + { + value_type** pPtrArrayCurrent = mItBegin.mpCurrentArrayPtr; + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + while(pPtrArrayCurrent < mItEnd.mpCurrentArrayPtr) + { + eastl::uninitialized_fill(*pPtrArrayCurrent, *pPtrArrayCurrent + kDequeSubarraySize, value); + ++pPtrArrayCurrent; + } + eastl::uninitialized_fill(mItEnd.mpBegin, mItEnd.mpCurrent, value); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(iterator itCurrent(mItBegin), itEnd(pPtrArrayCurrent, *pPtrArrayCurrent); itCurrent != itEnd; ++itCurrent) + itCurrent.mpCurrent->~value_type(); + throw; + } + #endif + } + + + template + template + void deque::DoAssign(Integer n, Integer value, true_type) // false_type means this is the integer version instead of iterator version. + { + DoAssignValues(static_cast(n), static_cast(value)); + } + + + template + template + void deque::DoAssign(InputIterator first, InputIterator last, false_type) // false_type means this is the iterator version instead of integer version. + { + // Actually, the implementation below requires first/last to be a ForwardIterator and not just an InputIterator. + // But Paul Pedriana if you somehow need to work with an InputIterator and we can deal with it. + const size_type n = (size_type)eastl::distance(first, last); + const size_type nSize = size(); + + if(n > nSize) // If we are increasing the size... + { + InputIterator atEnd(first); + + eastl::advance(atEnd, (difference_type)nSize); + eastl::copy(first, atEnd, mItBegin); + insert(mItEnd, atEnd, last); + } + else // n is <= size. + { + iterator itEnd(eastl::copy(first, last, mItBegin)); + + if(n < nSize) // If we need to erase any trailing elements... + erase(itEnd, mItEnd); + } + } + + + template + void deque::DoAssignValues(size_type n, const value_type& value) + { + const size_type nSize = size(); + + if(n > nSize) // If we are increasing the size... + { + eastl::fill(mItBegin, mItEnd, value); + insert(mItEnd, n - nSize, value); + } + else + { + erase(mItBegin + (difference_type)n, mItEnd); + eastl::fill(mItBegin, mItEnd, value); + } + } + + + template + template + void deque::DoInsert(const const_iterator& position, Integer n, Integer value, true_type) + { + DoInsertValues(position, (size_type)n, (value_type)value); + } + + + template + template + void deque::DoInsert(const const_iterator& position, const InputIterator& first, const InputIterator& last, false_type) + { + typedef typename eastl::iterator_traits::iterator_category IC; + DoInsertFromIterator(position, first, last, IC()); + } + + + template + template + void deque::DoInsertFromIterator(const_iterator position, const InputIterator& first, const InputIterator& last, EASTL_ITC_NS::forward_iterator_tag) + { + const size_type n = (size_type)eastl::distance(first, last); + + // This implementation is nearly identical to DoInsertValues below. + // If you make a bug fix to one, you will likely want to fix the other. + if(position.mpCurrent == mItBegin.mpCurrent) // If inserting at the beginning or into an empty container... + { + iterator itNewBegin(DoReallocSubarray(n, kSideFront)); // itNewBegin to mItBegin refers to memory that isn't initialized yet; so it's not truly a valid iterator. Or at least not a dereferencable one. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + // We would like to use move here instead of copy when possible, which would be useful for + // when inserting from a std::initializer_list, for example. + // To do: solve this by having a template or runtime parameter which specifies move vs copy. + eastl::uninitialized_copy(first, last, itNewBegin); + mItBegin = itNewBegin; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeSubarrays(itNewBegin.mpCurrentArrayPtr, mItBegin.mpCurrentArrayPtr); + throw; + } + #endif + } + else if(EASTL_UNLIKELY(position.mpCurrent == mItEnd.mpCurrent)) // If inserting at the end (i.e. appending)... + { + const iterator itNewEnd(DoReallocSubarray(n, kSideBack)); // mItEnd to itNewEnd refers to memory that isn't initialized yet; so it's not truly a valid iterator. Or at least not a dereferencable one. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + // We would like to use move here instead of copy when possible, which would be useful for + // when inserting from a std::initializer_list, for example. + // To do: solve this by having a template or runtime parameter which specifies move vs copy. + eastl::uninitialized_copy(first, last, mItEnd); + mItEnd = itNewEnd; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeSubarrays(mItEnd.mpCurrentArrayPtr + 1, itNewEnd.mpCurrentArrayPtr + 1); + throw; + } + #endif + } + else + { + const difference_type nInsertionIndex = position - mItBegin; + const size_type nSize = size(); + + if(nInsertionIndex < (difference_type)(nSize / 2)) // If the insertion index is in the front half of the deque... grow the deque at the front. + { + const iterator itNewBegin(DoReallocSubarray(n, kSideFront)); // itNewBegin to mItBegin refers to memory that isn't initialized yet; so it's not truly a valid iterator. Or at least not a dereferencable one. + const iterator itOldBegin(mItBegin); + const iterator itPosition(mItBegin + nInsertionIndex); // We need to reset this value because the reallocation above can invalidate iterators. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + // We have a problem here: we would like to use move instead of copy, but it may be that the range to be inserted comes from + // this container and comes from the segment we need to move. So we can't use move operations unless we are careful to handle + // that situation. The newly inserted contents must be contents that were moved to and not moved from. To do: solve this. + if(nInsertionIndex >= (difference_type)n) // If the newly inserted items will be entirely within the old area... + { + iterator itUCopyEnd(mItBegin + (difference_type)n); + + eastl::uninitialized_copy(mItBegin, itUCopyEnd, itNewBegin); // This can throw. + itUCopyEnd = eastl::copy(itUCopyEnd, itPosition, itOldBegin); // Recycle 'itUCopyEnd' to mean something else. + eastl::copy(first, last, itUCopyEnd); + } + else // Else the newly inserted items are going within the newly allocated area at the front. + { + InputIterator mid(first); + + eastl::advance(mid, (difference_type)n - nInsertionIndex); + eastl::uninitialized_copy_copy(mItBegin, itPosition, first, mid, itNewBegin); // This can throw. + eastl::copy(mid, last, itOldBegin); + } + mItBegin = itNewBegin; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeSubarrays(itNewBegin.mpCurrentArrayPtr, mItBegin.mpCurrentArrayPtr); + throw; + } + #endif + } + else + { + const iterator itNewEnd(DoReallocSubarray(n, kSideBack)); + const iterator itOldEnd(mItEnd); + const difference_type nPushedCount = (difference_type)nSize - nInsertionIndex; + const iterator itPosition(mItEnd - nPushedCount); // We need to reset this value because the reallocation above can invalidate iterators. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + // We have a problem here: we would like to use move instead of copy, but it may be that the range to be inserted comes from + // this container and comes from the segment we need to move. So we can't use move operations unless we are careful to handle + // that situation. The newly inserted contents must be contents that were moved to and not moved from. To do: solve this. + if(nPushedCount > (difference_type)n) + { + const iterator itUCopyEnd(mItEnd - (difference_type)n); + + eastl::uninitialized_copy(itUCopyEnd, mItEnd, mItEnd); + eastl::copy_backward(itPosition, itUCopyEnd, itOldEnd); + eastl::copy(first, last, itPosition); + } + else + { + InputIterator mid(first); + + eastl::advance(mid, nPushedCount); + eastl::uninitialized_copy_copy(mid, last, itPosition, mItEnd, mItEnd); + eastl::copy(first, mid, itPosition); + } + mItEnd = itNewEnd; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeSubarrays(mItEnd.mpCurrentArrayPtr + 1, itNewEnd.mpCurrentArrayPtr + 1); + throw; + } + #endif + } + } + } + + + template + void deque::DoInsertValues(const_iterator position, size_type n, const value_type& value) + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(!(validate_iterator(position) & isf_valid))) + EASTL_FAIL_MSG("deque::insert -- invalid iterator"); + #endif + + // This implementation is nearly identical to DoInsertFromIterator above. + // If you make a bug fix to one, you will likely want to fix the other. + if(position.mpCurrent == mItBegin.mpCurrent) // If inserting at the beginning... + { + const iterator itNewBegin(DoReallocSubarray(n, kSideFront)); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + // Note that we don't make a temp copy of 'value' here. This is because in a + // deque, insertion at either the front or back doesn't cause a reallocation + // or move of data in the middle. That's a key feature of deques, in fact. + eastl::uninitialized_fill(itNewBegin, mItBegin, value); + mItBegin = itNewBegin; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeSubarrays(itNewBegin.mpCurrentArrayPtr, mItBegin.mpCurrentArrayPtr); + throw; + } + #endif + } + else if(EASTL_UNLIKELY(position.mpCurrent == mItEnd.mpCurrent)) // If inserting at the end (i.e. appending)... + { + const iterator itNewEnd(DoReallocSubarray(n, kSideBack)); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + // Note that we don't make a temp copy of 'value' here. This is because in a + // deque, insertion at either the front or back doesn't cause a reallocation + // or move of data in the middle. That's a key feature of deques, in fact. + eastl::uninitialized_fill(mItEnd, itNewEnd, value); + mItEnd = itNewEnd; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeSubarrays(mItEnd.mpCurrentArrayPtr + 1, itNewEnd.mpCurrentArrayPtr + 1); + throw; + } + #endif + } + else + { + // A key purpose of a deque is to implement insertions and removals more efficiently + // than with a vector. We are inserting into the middle of the deque here. A quick and + // dirty implementation of this would be to reallocate the subarrays and simply push + // all values in the middle upward like you would do with a vector. Instead we implement + // the minimum amount of reallocations needed but may need to do some value moving, + // as the subarray sizes need to remain constant and can have no holes in them. + const difference_type nInsertionIndex = position - mItBegin; + const size_type nSize = size(); + const value_type valueSaved(value); + + if(nInsertionIndex < (difference_type)(nSize / 2)) // If the insertion index is in the front half of the deque... grow the deque at the front. + { + const iterator itNewBegin(DoReallocSubarray(n, kSideFront)); + const iterator itOldBegin(mItBegin); + const iterator itPosition(mItBegin + nInsertionIndex); // We need to reset this value because the reallocation above can invalidate iterators. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + if(nInsertionIndex >= (difference_type)n) // If the newly inserted items will be entirely within the old area... + { + iterator itUCopyEnd(mItBegin + (difference_type)n); + + eastl::uninitialized_move_if_noexcept(mItBegin, itUCopyEnd, itNewBegin); // This can throw. + itUCopyEnd = eastl::move(itUCopyEnd, itPosition, itOldBegin); // Recycle 'itUCopyEnd' to mean something else. + eastl::fill(itUCopyEnd, itPosition, valueSaved); + } + else // Else the newly inserted items are going within the newly allocated area at the front. + { + eastl::uninitialized_move_fill(mItBegin, itPosition, itNewBegin, mItBegin, valueSaved); // This can throw. + eastl::fill(itOldBegin, itPosition, valueSaved); + } + mItBegin = itNewBegin; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeSubarrays(itNewBegin.mpCurrentArrayPtr, mItBegin.mpCurrentArrayPtr); + throw; + } + #endif + } + else // Else the insertion index is in the back half of the deque, so grow the deque at the back. + { + const iterator itNewEnd(DoReallocSubarray(n, kSideBack)); + const iterator itOldEnd(mItEnd); + const difference_type nPushedCount = (difference_type)nSize - nInsertionIndex; + const iterator itPosition(mItEnd - nPushedCount); // We need to reset this value because the reallocation above can invalidate iterators. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + if(nPushedCount > (difference_type)n) // If the newly inserted items will be entirely within the old area... + { + iterator itUCopyEnd(mItEnd - (difference_type)n); + + eastl::uninitialized_move_if_noexcept(itUCopyEnd, mItEnd, mItEnd); // This can throw. + itUCopyEnd = eastl::move_backward(itPosition, itUCopyEnd, itOldEnd); // Recycle 'itUCopyEnd' to mean something else. + eastl::fill(itPosition, itUCopyEnd, valueSaved); + } + else // Else the newly inserted items are going within the newly allocated area at the back. + { + eastl::uninitialized_fill_move(mItEnd, itPosition + (difference_type)n, valueSaved, itPosition, mItEnd); // This can throw. + eastl::fill(itPosition, itOldEnd, valueSaved); + } + mItEnd = itNewEnd; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeSubarrays(mItEnd.mpCurrentArrayPtr + 1, itNewEnd.mpCurrentArrayPtr + 1); + throw; + } + #endif + } + } + } + + + template + inline void deque::DoSwap(this_type& x) + { + eastl::swap(mpPtrArray, x.mpPtrArray); + eastl::swap(mnPtrArraySize, x.mnPtrArraySize); + eastl::swap(mItBegin, x.mItBegin); + eastl::swap(mItEnd, x.mItEnd); + eastl::swap(mAllocator, x.mAllocator); // We do this even if EASTL_ALLOCATOR_COPY_ENABLED is 0. + + } + + + template + inline bool deque::validate() const + { + // To do: More detailed validation. + // To do: Try to make the validation resistant to crashes if the data is invalid. + if((end() - begin()) < 0) + return false; + return true; + } + + + template + inline int deque::validate_iterator(const_iterator i) const + { + // To do: We don't currently track isf_current, will need to make it do so. + // To do: Fix the validation below, as it will not catch all invalid iterators. + if((i - begin()) < 0) + return isf_none; + + if((end() - i) < 0) + return isf_none; + + if(i == end()) + return (isf_valid | isf_current); + + return (isf_valid | isf_current | isf_can_dereference); + } + + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const deque& a, const deque& b) + { + return ((a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin())); + } + + template + inline bool operator!=(const deque& a, const deque& b) + { + return ((a.size() != b.size()) || !eastl::equal(a.begin(), a.end(), b.begin())); + } + + template + inline bool operator<(const deque& a, const deque& b) + { + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + template + inline bool operator>(const deque& a, const deque& b) + { + return b < a; + } + + template + inline bool operator<=(const deque& a, const deque& b) + { + return !(b < a); + } + + template + inline bool operator>=(const deque& a, const deque& b) + { + return !(a < b); + } + + template + inline void swap(deque& a, deque& b) + { + a.swap(b); + } + + /////////////////////////////////////////////////////////////////////// + // erase / erase_if + // + // https://en.cppreference.com/w/cpp/container/deque/erase2 + /////////////////////////////////////////////////////////////////////// + template + void erase(deque& c, const U& value) + { + // Erases all elements that compare equal to value from the container. + c.erase(eastl::remove(c.begin(), c.end(), value), c.end()); + } + + template + void erase_if(deque& c, Predicate predicate) + { + // Erases all elements that satisfy the predicate pred from the container. + c.erase(eastl::remove_if(c.begin(), c.end(), predicate), c.end()); + } + + +} // namespace eastl + + +EA_RESTORE_VC_WARNING(); +#if EASTL_EXCEPTIONS_ENABLED + EA_RESTORE_VC_WARNING(); +#endif + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/finally.h b/lib/EASTL/include/EASTL/finally.h new file mode 100644 index 000000000..b4ed5803b --- /dev/null +++ b/lib/EASTL/include/EASTL/finally.h @@ -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 +#include +#include + +namespace eastl +{ + /////////////////////////////////////////////////////////////////////////// + // finally + // + // finally is the type that calls the users callback on scope exit. + // + template + class finally + { + static_assert(!eastl::is_lvalue_reference_v, "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 + auto make_finally(F&& f) + { + return finally(eastl::forward(f)); + } +} + +#endif // EASTL_FINALLY_H diff --git a/lib/EASTL/include/EASTL/fixed_allocator.h b/lib/EASTL/include/EASTL/fixed_allocator.h new file mode 100644 index 000000000..488eae4a4 --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_allocator.h @@ -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 +#include +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS(); + +#include + +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 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(reinterpret_cast(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 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(reinterpret_cast(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 diff --git a/lib/EASTL/include/EASTL/fixed_function.h b/lib/EASTL/include/EASTL/fixed_function.h new file mode 100644 index 000000000..6aed768ab --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_function.h @@ -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 + +namespace eastl +{ + template + class fixed_function; + + namespace internal + { + template + struct is_fixed_function + : public eastl::false_type {}; + + template + struct is_fixed_function> + : public eastl::true_type {}; + + template + EA_CONSTEXPR bool is_fixed_function_v = is_fixed_function::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 + using EASTL_DISABLE_OVERLOAD_IF_FIXED_FUNCTION = + eastl::disable_if_t>>; + + + // fixed_function + // + template + class fixed_function : public internal::function_detail + { + using Base = internal::function_detail; + + 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 > + fixed_function(Functor functor) + : Base(eastl::move(functor)) + { + EASTL_INTERNAL_FIXED_FUNCTION_STATIC_ASSERT(Functor); + } + + template + fixed_function(const fixed_function& other) + : Base(other) + { + EASTL_INTERNAL_FIXED_FUNCTION_NEW_SIZE_STATIC_ASSERT(NEW_SIZE_IN_BYTES); + } + + template + fixed_function(fixed_function&& 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 + fixed_function& operator=(const fixed_function& other) + { + EASTL_INTERNAL_FIXED_FUNCTION_NEW_SIZE_STATIC_ASSERT(NEW_SIZE_IN_BYTES); + + Base::operator=(other); + return *this; + } + + template + fixed_function& operator=(fixed_function&& other) + { + EASTL_INTERNAL_FIXED_FUNCTION_NEW_SIZE_STATIC_ASSERT(NEW_SIZE_IN_BYTES); + + Base::operator=(eastl::move(other)); + return *this; + } + + template > + fixed_function& operator=(Functor&& functor) + { + EASTL_INTERNAL_FIXED_FUNCTION_STATIC_ASSERT(eastl::decay_t); + Base::operator=(eastl::forward(functor)); + return *this; + } + + template + fixed_function& operator=(eastl::reference_wrapper f) EA_NOEXCEPT + { + EASTL_INTERNAL_FIXED_FUNCTION_STATIC_ASSERT(eastl::reference_wrapper); + 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)...); + } + + #if EASTL_RTTI_ENABLED + const std::type_info& target_type() const EA_NOEXCEPT + { + return Base::target_type(); + } + + template + Functor* target() EA_NOEXCEPT + { + return Base::target(); + } + + template + const Functor* target() const EA_NOEXCEPT + { + return Base::target(); + } + #endif + }; + + template + bool operator==(const fixed_function& f, std::nullptr_t) EA_NOEXCEPT + { + return !f; + } + + template + bool operator==(std::nullptr_t, const fixed_function& f) EA_NOEXCEPT + { + return !f; + } + + template + bool operator!=(const fixed_function& f, std::nullptr_t) EA_NOEXCEPT + { + return !!f; + } + + template + bool operator!=(std::nullptr_t, const fixed_function& f) EA_NOEXCEPT + { + return !!f; + } + + template + void swap(fixed_function& lhs, fixed_function& rhs) + { + lhs.swap(rhs); + } + +} // namespace eastl + +#endif // EASTL_FIXED_FUNCTION_H diff --git a/lib/EASTL/include/EASTL/fixed_hash_map.h b/lib/EASTL/include/EASTL/fixed_hash_map.h new file mode 100644 index 000000000..b94ea541c --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_hash_map.h @@ -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 +#include + +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 Predicate = eastl::equal_to, bool bCacheHashCode = false, typename OverflowAllocator = EASTLAllocatorType> + class fixed_hash_map : public hash_map::node_type), + nodeCount, + EASTL_ALIGN_OF(eastl::pair), + 0, + bEnableOverflow, + OverflowAllocator>, + bCacheHashCode> + { + public: + typedef fixed_hashtable_allocator::node_type), nodeCount, EASTL_ALIGN_OF(eastl::pair), 0, + bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; + typedef hash_map base_type; + typedef fixed_hash_map 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 + 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 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 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 Predicate = eastl::equal_to, bool bCacheHashCode = false, typename OverflowAllocator = EASTLAllocatorType> + class fixed_hash_multimap : public hash_multimap::node_type), + nodeCount, + EASTL_ALIGN_OF(eastl::pair), + 0, + bEnableOverflow, + OverflowAllocator>, + bCacheHashCode> + { + public: + typedef fixed_hashtable_allocator::node_type), nodeCount, EASTL_ALIGN_OF(eastl::pair), 0, + bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; + typedef hash_multimap base_type; + typedef fixed_hash_multimap 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 + 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 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 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 + inline fixed_hash_map:: + 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 + inline fixed_hash_map:: + 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 + inline fixed_hash_map:: + 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 + template + fixed_hash_map:: + 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 + inline fixed_hash_map:: + 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 + inline fixed_hash_map:: + 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 + inline fixed_hash_map:: + 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 + inline fixed_hash_map:: + fixed_hash_map(std::initializer_list 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 + inline typename fixed_hash_map::this_type& + fixed_hash_map::operator=(const this_type& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_hash_map::this_type& + fixed_hash_map::operator=(this_type&& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_hash_map::this_type& + fixed_hash_map::operator=(std::initializer_list ilist) + { + base_type::clear(); + base_type::insert(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline void fixed_hash_map:: + swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_hash_map:: + 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 + inline typename fixed_hash_map::size_type + fixed_hash_map::max_size() const + { + return kMaxSize; + } + + + template + inline const typename fixed_hash_map::overflow_allocator_type& + fixed_hash_map::get_overflow_allocator() const EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline typename fixed_hash_map::overflow_allocator_type& + fixed_hash_map::get_overflow_allocator() EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline void fixed_hash_map:: + set_overflow_allocator(const overflow_allocator_type& allocator) + { + mAllocator.set_overflow_allocator(allocator); + } + + + template + inline void fixed_hash_map:: + 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 + inline void swap(fixed_hash_map& a, + fixed_hash_map& b) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(a, b); + } + + + + + /////////////////////////////////////////////////////////////////////// + // fixed_hash_multimap + /////////////////////////////////////////////////////////////////////// + + template + inline fixed_hash_multimap:: + 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 + inline fixed_hash_multimap:: + 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 + inline fixed_hash_multimap:: + 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 + template + fixed_hash_multimap:: + 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 + inline fixed_hash_multimap:: + 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 + inline fixed_hash_multimap:: + 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 + inline fixed_hash_multimap:: + 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 + inline fixed_hash_multimap:: + fixed_hash_multimap(std::initializer_list 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 + inline typename fixed_hash_multimap::this_type& + fixed_hash_multimap::operator=(const this_type& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_hash_multimap::this_type& + fixed_hash_multimap::operator=(this_type&& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_hash_multimap::this_type& + fixed_hash_multimap::operator=(std::initializer_list ilist) + { + base_type::clear(); + base_type::insert(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline void fixed_hash_multimap:: + swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_hash_multimap:: + 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 + inline typename fixed_hash_multimap::size_type + fixed_hash_multimap::max_size() const + { + return kMaxSize; + } + + + template + inline const typename fixed_hash_multimap::overflow_allocator_type& + fixed_hash_multimap::get_overflow_allocator() const EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline typename fixed_hash_multimap::overflow_allocator_type& + fixed_hash_multimap::get_overflow_allocator() EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline void fixed_hash_multimap::set_overflow_allocator(const overflow_allocator_type& allocator) + { + mAllocator.set_overflow_allocator(allocator); + } + + + template + inline void fixed_hash_multimap:: + 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 + inline void swap(fixed_hash_multimap& a, + fixed_hash_multimap& 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 + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/fixed_hash_set.h b/lib/EASTL/include/EASTL/fixed_hash_set.h new file mode 100644 index 000000000..fa2783adc --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_hash_set.h @@ -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 +#include + +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 Predicate = eastl::equal_to, bool bCacheHashCode = false, typename OverflowAllocator = EASTLAllocatorType> + class fixed_hash_set : public hash_set::node_type), + nodeCount, + EASTL_ALIGN_OF(typename hash_set::node_type), + 0, + bEnableOverflow, + OverflowAllocator>, + bCacheHashCode> + { + public: + typedef fixed_hashtable_allocator::node_type), nodeCount, + EASTL_ALIGN_OF(typename hash_set::node_type), + 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; + typedef fixed_hash_set this_type; + typedef hash_set 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 + 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 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 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 Predicate = eastl::equal_to, bool bCacheHashCode = false, typename OverflowAllocator = EASTLAllocatorType> + class fixed_hash_multiset : public hash_multiset::node_type), + nodeCount, + EASTL_ALIGN_OF(typename hash_multiset::node_type), + 0, + bEnableOverflow, + OverflowAllocator>, + bCacheHashCode> + { + public: + typedef fixed_hashtable_allocator::node_type), nodeCount, EASTL_ALIGN_OF(typename hash_multiset::node_type), 0, + bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; + typedef hash_multiset base_type; + typedef fixed_hash_multiset 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 + 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 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 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 + inline fixed_hash_set:: + 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 + inline fixed_hash_set:: + 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 + inline fixed_hash_set:: + 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 + template + fixed_hash_set:: + 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 + inline fixed_hash_set:: + 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 + inline fixed_hash_set::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 + inline fixed_hash_set::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 + inline fixed_hash_set:: + fixed_hash_set(std::initializer_list 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 fixed_hash_set::this_type& + fixed_hash_set::operator=(const this_type& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_hash_set::this_type& + fixed_hash_set::operator=(this_type&& x) + { + operator=(x); + return *this; + } + + + template + inline typename fixed_hash_set::this_type& + fixed_hash_set::operator=(std::initializer_list ilist) + { + base_type::clear(); + base_type::insert(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline void fixed_hash_set:: + 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 + void fixed_hash_set:: + reset_lose_memory() + { + base_type::reset_lose_memory(); + base_type::get_allocator().reset(mNodeBuffer); + } + + + template + inline typename fixed_hash_set::size_type + fixed_hash_set::max_size() const + { + return kMaxSize; + } + + + template + inline const typename fixed_hash_set::overflow_allocator_type& + fixed_hash_set::get_overflow_allocator() const EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline typename fixed_hash_set::overflow_allocator_type& + fixed_hash_set::get_overflow_allocator() EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline void fixed_hash_set:: + set_overflow_allocator(const overflow_allocator_type& allocator) + { + mAllocator.set_overflow_allocator(allocator); + } + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline void swap(fixed_hash_set& a, + fixed_hash_set& b) + { + a.swap(b); + } + + + + + /////////////////////////////////////////////////////////////////////// + // fixed_hash_multiset + /////////////////////////////////////////////////////////////////////// + + template + inline fixed_hash_multiset:: + 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 + inline fixed_hash_multiset:: + 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 + inline fixed_hash_multiset:: + 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 + template + inline fixed_hash_multiset:: + 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 + inline fixed_hash_multiset:: + 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 + inline fixed_hash_multiset::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 + inline fixed_hash_multiset::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 + inline fixed_hash_multiset:: + fixed_hash_multiset(std::initializer_list 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 + inline typename fixed_hash_multiset::this_type& + fixed_hash_multiset::operator=(const this_type& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_hash_multiset::this_type& + fixed_hash_multiset::operator=(this_type&& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_hash_multiset::this_type& + fixed_hash_multiset::operator=(std::initializer_list ilist) + { + base_type::clear(); + base_type::insert(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline void fixed_hash_multiset:: + swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_hash_multiset:: + reset_lose_memory() + { + base_type::reset_lose_memory(); + base_type::get_allocator().reset(mNodeBuffer); + } + + + template + inline typename fixed_hash_multiset::size_type + fixed_hash_multiset::max_size() const + { + return kMaxSize; + } + + + template + inline const typename fixed_hash_multiset::overflow_allocator_type& + fixed_hash_multiset::get_overflow_allocator() const EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline typename fixed_hash_multiset::overflow_allocator_type& + fixed_hash_multiset::get_overflow_allocator() EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline void fixed_hash_multiset:: + set_overflow_allocator(const overflow_allocator_type& allocator) + { + mAllocator.set_overflow_allocator(allocator); + } + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline void swap(fixed_hash_multiset& a, + fixed_hash_multiset& 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 + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/fixed_list.h b/lib/EASTL/include/EASTL/fixed_list.h new file mode 100644 index 000000000..e57c08bff --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_list.h @@ -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 +#include + +#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 + class fixed_list : public list::node_type), + nodeCount, EASTL_ALIGN_OF(typename list::node_type), 0, bEnableOverflow, OverflowAllocator> > + { + public: + typedef fixed_node_allocator::node_type), nodeCount, + EASTL_ALIGN_OF(typename list::node_type), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef OverflowAllocator overflow_allocator_type; + typedef list base_type; + typedef fixed_list 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 ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_LIST_DEFAULT_ALLOCATOR); + + template + fixed_list(InputIterator first, InputIterator last); + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list 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 + inline fixed_list::fixed_list() + : base_type(fixed_allocator_type(mBuffer)) + { + #if EASTL_NAME_ENABLED + internalAllocator().set_name(EASTL_FIXED_LIST_DEFAULT_NAME); + #endif + } + + + template + inline fixed_list::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 + inline fixed_list::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 + inline fixed_list::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 + inline fixed_list::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 + inline fixed_list::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 + inline fixed_list::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 + inline fixed_list::fixed_list(std::initializer_list ilist, const overflow_allocator_type& overflowAllocator) + : base_type(fixed_allocator_type(mBuffer, overflowAllocator)) + { + assign(ilist.begin(), ilist.end()); + } + + + template + template + fixed_list::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 + inline typename fixed_list::this_type& + fixed_list::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 + inline typename fixed_list::this_type& + fixed_list::operator=(this_type&& x) + { + return operator=(x); + } + + + template + inline typename fixed_list::this_type& + fixed_list::operator=(std::initializer_list ilist) + { + base_type::clear(); + base_type::assign(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline void fixed_list::swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_list::reset_lose_memory() + { + base_type::reset_lose_memory(); + get_allocator().reset(mBuffer); + } + + + template + inline typename fixed_list::size_type + fixed_list::max_size() const + { + return kMaxSize; + } + + + template + inline bool fixed_list::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 + inline bool fixed_list::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 + inline bool fixed_list::can_overflow() const + { + return bEnableOverflow; + } + + + template + inline const typename fixed_list::overflow_allocator_type& + fixed_list::get_overflow_allocator() const EA_NOEXCEPT + { + return internalAllocator().get_overflow_allocator(); + } + + + template + inline typename fixed_list::overflow_allocator_type& + fixed_list::get_overflow_allocator() EA_NOEXCEPT + { + return internalAllocator().get_overflow_allocator(); + } + + + template + inline void + fixed_list::set_overflow_allocator(const overflow_allocator_type& allocator) + { + internalAllocator().set_overflow_allocator(allocator); + } + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline void swap(fixed_list& a, + fixed_list& 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 + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/fixed_map.h b/lib/EASTL/include/EASTL/fixed_map.h new file mode 100644 index 000000000..c01db08fc --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_map.h @@ -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 +#include // 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 OverflowAllocator = EASTLAllocatorType> + class fixed_map : public map::node_type), + nodeCount, EASTL_ALIGN_OF(eastl::pair), 0, bEnableOverflow, OverflowAllocator> > + { + public: + typedef fixed_node_allocator::node_type), nodeCount, + EASTL_ALIGN_OF(eastl::pair), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; + typedef fixed_map this_type; + typedef map 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 ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_MAP_DEFAULT_ALLOCATOR); + + template + fixed_map(InputIterator first, InputIterator last); + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list 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 OverflowAllocator = EASTLAllocatorType> + class fixed_multimap : public multimap::node_type), + nodeCount, EASTL_ALIGN_OF(eastl::pair), 0, bEnableOverflow, OverflowAllocator> > + { + public: + typedef fixed_node_allocator::node_type), nodeCount, + EASTL_ALIGN_OF(eastl::pair), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; + typedef multimap base_type; + typedef fixed_multimap 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 ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_MULTIMAP_DEFAULT_ALLOCATOR); + + template + fixed_multimap(InputIterator first, InputIterator last); + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list 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 + inline fixed_map::fixed_map() + : base_type(fixed_allocator_type(mBuffer)) + { + #if EASTL_NAME_ENABLED + mAllocator.set_name(EASTL_FIXED_MAP_DEFAULT_NAME); + #endif + } + + + template + inline fixed_map::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 + inline fixed_map::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 + inline fixed_map::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 + inline fixed_map::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 + inline fixed_map::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 + fixed_map::fixed_map(std::initializer_list 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 + template + fixed_map::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 + inline typename fixed_map::this_type& + fixed_map::operator=(const this_type& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_map::this_type& + fixed_map::operator=(std::initializer_list ilist) + { + base_type::clear(); + insert(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline typename fixed_map::this_type& + fixed_map::operator=(this_type&& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline void fixed_map::swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_map::reset_lose_memory() + { + base_type::reset_lose_memory(); + base_type::get_allocator().reset(mBuffer); + } + + + template + inline typename fixed_map::size_type + fixed_map::max_size() const + { + return kMaxSize; + } + + + template + inline const typename fixed_map::overflow_allocator_type& + fixed_map::get_overflow_allocator() const EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline typename fixed_map::overflow_allocator_type& + fixed_map::get_overflow_allocator() EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline void + fixed_map::set_overflow_allocator(const overflow_allocator_type& allocator) + { + mAllocator.set_overflow_allocator(allocator); + } + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline void swap(fixed_map& a, + fixed_map& b) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(a, b); + } + + + + + /////////////////////////////////////////////////////////////////////// + // fixed_multimap + /////////////////////////////////////////////////////////////////////// + + template + inline fixed_multimap::fixed_multimap() + : base_type(fixed_allocator_type(mBuffer)) + { + #if EASTL_NAME_ENABLED + mAllocator.set_name(EASTL_FIXED_MULTIMAP_DEFAULT_NAME); + #endif + } + + + template + inline fixed_multimap::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 + inline fixed_multimap::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 + inline fixed_multimap::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 + inline fixed_multimap::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 + inline fixed_multimap::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 + fixed_multimap::fixed_multimap(std::initializer_list 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 + template + fixed_multimap:: + 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 + inline typename fixed_multimap::this_type& + fixed_multimap::operator=(const this_type& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_multimap::this_type& + fixed_multimap::operator=(std::initializer_list ilist) + { + base_type::clear(); + insert(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline typename fixed_multimap::this_type& + fixed_multimap::operator=(this_type&& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline void fixed_multimap::swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_multimap::reset_lose_memory() + { + base_type::reset_lose_memory(); + base_type::get_allocator().reset(mBuffer); + } + + + template + inline typename fixed_multimap::size_type + fixed_multimap::max_size() const + { + return kMaxSize; + } + + + template + inline const typename fixed_multimap::overflow_allocator_type& + fixed_multimap::get_overflow_allocator() const EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline typename fixed_multimap::overflow_allocator_type& + fixed_multimap::get_overflow_allocator() EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline void + fixed_multimap::set_overflow_allocator(const overflow_allocator_type& allocator) + { + mAllocator.set_overflow_allocator(allocator); + } + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline void swap(fixed_multimap& a, + fixed_multimap& 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 + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/fixed_set.h b/lib/EASTL/include/EASTL/fixed_set.h new file mode 100644 index 000000000..e5f002366 --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_set.h @@ -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 +#include + +#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 OverflowAllocator = EASTLAllocatorType> + class fixed_set : public set::node_type), + nodeCount, EASTL_ALIGN_OF(Key), 0, bEnableOverflow, OverflowAllocator> > + { + public: + typedef fixed_node_allocator::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 base_type; + typedef fixed_set 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 ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_SET_DEFAULT_ALLOCATOR); + + template + fixed_set(InputIterator first, InputIterator last); + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list 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 OverflowAllocator = EASTLAllocatorType> + class fixed_multiset : public multiset::node_type), + nodeCount, EASTL_ALIGN_OF(Key), 0, bEnableOverflow, OverflowAllocator> > + { + public: + typedef fixed_node_allocator::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 base_type; + typedef fixed_multiset 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 ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_MULTISET_DEFAULT_ALLOCATOR); + + template + fixed_multiset(InputIterator first, InputIterator last); + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list 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 + inline fixed_set::fixed_set() + : base_type(fixed_allocator_type(mBuffer)) + { + #if EASTL_NAME_ENABLED + mAllocator.set_name(EASTL_FIXED_SET_DEFAULT_NAME); + #endif + } + + + template + inline fixed_set::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 + inline fixed_set::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 + inline fixed_set::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 + inline fixed_set::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 + inline fixed_set::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 + fixed_set::fixed_set(std::initializer_list 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 + template + fixed_set::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 + inline typename fixed_set::this_type& + fixed_set::operator=(const this_type& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_set::this_type& + fixed_set::operator=(std::initializer_list ilist) + { + base_type::clear(); + insert(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline typename fixed_set::this_type& + fixed_set::operator=(this_type&& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline void fixed_set::swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_set::reset_lose_memory() + { + base_type::reset_lose_memory(); + base_type::get_allocator().reset(mBuffer); + } + + + template + inline typename fixed_set::size_type + fixed_set::max_size() const + { + return kMaxSize; + } + + + template + inline const typename fixed_set::overflow_allocator_type& + fixed_set::get_overflow_allocator() const EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline typename fixed_set::overflow_allocator_type& + fixed_set::get_overflow_allocator() EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline void fixed_set::set_overflow_allocator(const overflow_allocator_type& allocator) + { + mAllocator.set_overflow_allocator(allocator); + } + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline void swap(fixed_set& a, + fixed_set& b) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(a, b); + } + + + + /////////////////////////////////////////////////////////////////////// + // fixed_multiset + /////////////////////////////////////////////////////////////////////// + + template + inline fixed_multiset::fixed_multiset() + : base_type(fixed_allocator_type(mBuffer)) + { + #if EASTL_NAME_ENABLED + mAllocator.set_name(EASTL_FIXED_MULTISET_DEFAULT_NAME); + #endif + } + + + template + inline fixed_multiset::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 + inline fixed_multiset::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 + inline fixed_multiset::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 + inline fixed_multiset::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 + inline fixed_multiset::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 + fixed_multiset::fixed_multiset(std::initializer_list 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 + template + fixed_multiset::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 + inline typename fixed_multiset::this_type& + fixed_multiset::operator=(const this_type& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline typename fixed_multiset::this_type& + fixed_multiset::operator=(std::initializer_list ilist) + { + base_type::clear(); + insert(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline typename fixed_multiset::this_type& + fixed_multiset::operator=(this_type&& x) + { + base_type::operator=(x); + return *this; + } + + + template + inline void fixed_multiset::swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_multiset::reset_lose_memory() + { + base_type::reset_lose_memory(); + base_type::get_allocator().reset(mBuffer); + } + + + template + inline typename fixed_multiset::size_type + fixed_multiset::max_size() const + { + return kMaxSize; + } + + + template + inline const typename fixed_multiset::overflow_allocator_type& + fixed_multiset::get_overflow_allocator() const EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline typename fixed_multiset::overflow_allocator_type& + fixed_multiset::get_overflow_allocator() EA_NOEXCEPT + { + return mAllocator.get_overflow_allocator(); + } + + + template + inline void fixed_multiset::set_overflow_allocator(const overflow_allocator_type& allocator) + { + mAllocator.set_overflow_allocator(allocator); + } + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline void swap(fixed_multiset& a, + fixed_multiset& 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 + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/fixed_slist.h b/lib/EASTL/include/EASTL/fixed_slist.h new file mode 100644 index 000000000..abad7ad9d --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_slist.h @@ -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 +#include + +#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 + class fixed_slist : public slist::node_type), + nodeCount, EASTL_ALIGN_OF(typename slist::node_type), 0, bEnableOverflow, OverflowAllocator> > + { + public: + typedef fixed_node_allocator::node_type), nodeCount, + EASTL_ALIGN_OF(typename slist::node_type), 0, bEnableOverflow, OverflowAllocator> fixed_allocator_type; + typedef OverflowAllocator overflow_allocator_type; + typedef slist base_type; + typedef fixed_slist 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 ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_SLIST_DEFAULT_ALLOCATOR); + + template + fixed_slist(InputIterator first, InputIterator last); + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list 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 + inline fixed_slist::fixed_slist() + : base_type(fixed_allocator_type(mBuffer)) + { + #if EASTL_NAME_ENABLED + internalAllocator().set_name(EASTL_FIXED_SLIST_DEFAULT_NAME); + #endif + } + + + template + inline fixed_slist::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 + inline fixed_slist::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 + inline fixed_slist::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 + inline fixed_slist::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 + inline fixed_slist::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 + inline fixed_slist::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 + inline fixed_slist::fixed_slist(std::initializer_list 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 + template + fixed_slist::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 + inline typename fixed_slist::this_type& + fixed_slist::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 + inline typename fixed_slist::this_type& + fixed_slist::operator=(this_type&& x) + { + return operator=(x); + } + + + template + inline typename fixed_slist::this_type& + fixed_slist::operator=(std::initializer_list ilist) + { + base_type::clear(); + base_type::assign(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline void fixed_slist::swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_slist::reset_lose_memory() + { + base_type::reset_lose_memory(); + base_type::get_allocator().reset(mBuffer); + } + + + template + inline typename fixed_slist::size_type + fixed_slist::max_size() const + { + return kMaxSize; + } + + + template + inline bool fixed_slist::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 + inline bool fixed_slist::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 + inline bool fixed_slist::can_overflow() const + { + return bEnableOverflow; + } + + + template + inline const typename fixed_slist::overflow_allocator_type& + fixed_slist::get_overflow_allocator() const EA_NOEXCEPT + { + return internalAllocator().get_overflow_allocator(); + } + + + template + inline typename fixed_slist::overflow_allocator_type& + fixed_slist::get_overflow_allocator() EA_NOEXCEPT + { + return internalAllocator().get_overflow_allocator(); + } + + + template + inline void + fixed_slist::set_overflow_allocator(const overflow_allocator_type& allocator) + { + internalAllocator().set_overflow_allocator(allocator); + } + + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline void swap(fixed_slist& a, + fixed_slist& 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 + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/fixed_string.h b/lib/EASTL/include/EASTL/fixed_string.h new file mode 100644 index 000000000..f646302b9 --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_string.h @@ -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 +#include +#include + +#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 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 + class fixed_string : public basic_string > + { + public: + typedef fixed_vector_allocator fixed_allocator_type; + typedef typename fixed_allocator_type::overflow_allocator_type overflow_allocator_type; + typedef basic_string base_type; + typedef fixed_string 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 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 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 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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::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 + inline fixed_string::fixed_string(std::initializer_list 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 + inline fixed_string::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 + inline fixed_string::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 + inline typename fixed_string::this_type& + fixed_string::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 + inline typename fixed_string:: + this_type& fixed_string::operator=(const base_type& x) + { + if(static_cast(this) != &x) + { + clear(); + + #if EASTL_ALLOCATOR_COPY_ENABLED + get_allocator() = x.get_allocator(); + #endif + + append(x); + } + return *this; + } + + + template + inline typename fixed_string:: + this_type& fixed_string::operator=(const value_type* p) + { + if(internalLayout().HeapBeginPtr() != p) + { + clear(); + append(p); + } + return *this; + } + + + template + inline typename fixed_string:: + this_type& fixed_string::operator=(const value_type c) + { + clear(); + append((size_type)1, c); + return *this; + } + + + template + inline typename fixed_string:: + this_type& fixed_string::operator=(std::initializer_list ilist) + { + clear(); + append(ilist.begin(), ilist.end()); + return *this; + } + + + template + inline typename fixed_string:: + this_type& fixed_string::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(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 + inline void fixed_string::swap(this_type& x) + { + // Fixed containers use a special swap that can deal with excessively large buffers. + eastl::fixed_swap(*this, x); + } + + + template + inline void fixed_string::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 + inline void fixed_string::reset_lose_memory() + { + internalLayout().SetHeapBeginPtr(mArray); + internalLayout().SetHeapSize(0); + internalLayout().SetHeapCapacity(nodeCount - 1); + } + + + template + inline typename fixed_string:: + size_type fixed_string::max_size() const + { + return kMaxSize; + } + + + template + inline bool fixed_string::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 + inline bool fixed_string::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 + inline bool fixed_string::can_overflow() const + { + return bEnableOverflow; + } + + + template + inline typename fixed_string:: + this_type fixed_string::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 + inline typename fixed_string:: + this_type fixed_string::left(size_type n) const + { + const size_type nLength = size(); + if(n < nLength) + return fixed_string(internalLayout().HeapBeginPtr(), internalLayout().HeapBeginPtr() + n); + return *this; + } + + + template + inline typename fixed_string:: + this_type fixed_string::right(size_type n) const + { + const size_type nLength = size(); + if(n < nLength) + return fixed_string(internalLayout().HeapEndPtr() - n, internalLayout().HeapEndPtr()); + return *this; + } + + + template + inline const typename fixed_string:: + overflow_allocator_type& fixed_string::get_overflow_allocator() const EA_NOEXCEPT + { + return get_allocator().get_overflow_allocator(); + } + + + template + inline typename fixed_string:: + overflow_allocator_type& fixed_string::get_overflow_allocator() EA_NOEXCEPT + { + return get_allocator().get_overflow_allocator(); + } + + + template + inline void + fixed_string::set_overflow_allocator(const overflow_allocator_type& allocator) + { + get_allocator().set_overflow_allocator(allocator); + } + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + + // Operator + + template + fixed_string operator+(const fixed_string& a, + const fixed_string& 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 this_type; + + this_type result(const_cast(a).get_overflow_allocator()); + result.append(a); + result.append(b); + return result; + } + + + template + fixed_string operator+(const typename fixed_string::value_type* p, + const fixed_string& b) + { + typedef fixed_string this_type; + + const typename this_type::size_type n = (typename this_type::size_type)CharStrlen(p); + this_type result(const_cast(b).get_overflow_allocator()); + result.append(p, p + n); + result.append(b); + return result; + } + + + template + fixed_string operator+(typename fixed_string::value_type c, + const fixed_string& b) + { + typedef fixed_string this_type; + + this_type result(const_cast(b).get_overflow_allocator()); + result.push_back(c); + result.append(b); + return result; + } + + + template + fixed_string operator+(const fixed_string& a, + const typename fixed_string::value_type* p) + { + typedef fixed_string this_type; + + const typename this_type::size_type n = (typename this_type::size_type)CharStrlen(p); + this_type result(const_cast(a).get_overflow_allocator()); + result.append(a); + result.append(p, p + n); + return result; + } + + + template + fixed_string operator+(const fixed_string& a, + typename fixed_string::value_type c) + { + typedef fixed_string this_type; + + this_type result(const_cast(a).get_overflow_allocator()); + result.append(a); + result.push_back(c); + return result; + } + + + template + fixed_string operator+(fixed_string&& a, + fixed_string&& b) + { + a.append(b); // Using an rvalue by name results in it becoming an lvalue. + return eastl::move(a); + } + + template + fixed_string operator+(fixed_string&& a, + const fixed_string& b) + { + a.append(b); + return eastl::move(a); + } + + template + fixed_string operator+(const typename fixed_string::value_type* p, + fixed_string&& b) + { + b.insert(0, p); + return eastl::move(b); + } + + template + fixed_string operator+(fixed_string&& a, + const typename fixed_string::value_type* p) + { + a.append(p); + return eastl::move(a); + } + + template + fixed_string operator+(fixed_string&& a, + typename fixed_string::value_type c) + { + a.push_back(c); + return eastl::move(a); + } + + + // operator ==, !=, <, >, <=, >= come from the string implementations. + + template + inline void swap(fixed_string& a, + fixed_string& 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 diff --git a/lib/EASTL/include/EASTL/fixed_substring.h b/lib/EASTL/include/EASTL/fixed_substring.h new file mode 100644 index 000000000..033052f40 --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_substring.h @@ -0,0 +1,265 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_FIXED_SUBSTRING_H +#define EASTL_FIXED_SUBSTRING_H + + +#include + +#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 str("hello world"); + /// fixed_substring sub(str, 2, 5); // sub == "llo w" + /// + template + class fixed_substring : public basic_string + { + public: + typedef basic_string base_type; + typedef fixed_substring 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 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 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(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(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(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(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(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 diff --git a/lib/EASTL/include/EASTL/fixed_vector.h b/lib/EASTL/include/EASTL/fixed_vector.h new file mode 100644 index 000000000..1dc482bd0 --- /dev/null +++ b/lib/EASTL/include/EASTL/fixed_vector.h @@ -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 +#include + +#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 fixedVector); + /// + /// fixedVector.push_back(Widget()); + /// fixedVector.resize(200); + /// fixedVector.clear(); + /// + template ::type> + class fixed_vector : public vector > + { + public: + typedef fixed_vector_allocator fixed_allocator_type; + typedef OverflowAllocator overflow_allocator_type; + typedef vector base_type; + typedef fixed_vector 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 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 ilist, const overflow_allocator_type& overflowAllocator = EASTL_FIXED_VECTOR_DEFAULT_ALLOCATOR); + + template + fixed_vector(InputIterator first, InputIterator last); + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list 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 + inline fixed_vector::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 + inline fixed_vector::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 + inline fixed_vector::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 + inline fixed_vector::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 + inline fixed_vector::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(x.begin(), x.end(), false_type()); + } + + + template + inline fixed_vector::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, true>(eastl::make_move_iterator(x.begin()), eastl::make_move_iterator(x.end()), false_type()); + } + + + template + inline fixed_vector::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(x.begin(), x.end(), false_type()); + } + + + template + inline fixed_vector::fixed_vector(std::initializer_list ilist, const overflow_allocator_type& overflowAllocator) + : base_type(fixed_allocator_type(mBuffer.buffer, overflowAllocator)) + { + typedef typename std::initializer_list::iterator InputIterator; + typedef typename eastl::iterator_traits::iterator_category IC; + + mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0]; + internalCapacityPtr() = mpBegin + nodeCount; + base_type::template DoAssignFromIterator(ilist.begin(), ilist.end(), IC()); + } + + + template + template + fixed_vector::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(first, last, is_integral()); + } + + + template + inline typename fixed_vector::this_type& + fixed_vector::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(x.begin(), x.end(), false_type()); // Shorter route. + } + return *this; + } + + + template + inline typename fixed_vector::this_type& + fixed_vector::operator=(std::initializer_list ilist) + { + typedef typename std::initializer_list::iterator InputIterator; + typedef typename eastl::iterator_traits::iterator_category IC; + + clear(); + base_type::template DoAssignFromIterator(ilist.begin(), ilist.end(), IC()); + return *this; + } + + + template + inline typename fixed_vector::this_type& + fixed_vector::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, true>(eastl::make_move_iterator(x.begin()), eastl::make_move_iterator(x.end()), false_type()); // Shorter route. + } + return *this; + } + + + template + inline void fixed_vector::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 + inline void fixed_vector::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 + inline void fixed_vector::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 + inline void fixed_vector::reset_lose_memory() + { + mpBegin = mpEnd = (value_type*)&mBuffer.buffer[0]; + internalCapacityPtr() = mpBegin + nodeCount; + } + + + template + inline typename fixed_vector::size_type + fixed_vector::max_size() const + { + return kMaxSize; + } + + + template + inline bool fixed_vector::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 + inline bool fixed_vector::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 + inline bool fixed_vector::can_overflow() const + { + return bEnableOverflow; + } + + + template + inline void* fixed_vector::push_back_uninitialized() + { + return DoPushBackUninitialized(typename type_select::type()); + } + + + template + inline void* fixed_vector::DoPushBackUninitialized(true_type) + { + return base_type::push_back_uninitialized(); + } + + + template + inline void* fixed_vector::DoPushBackUninitialized(false_type) + { + EASTL_ASSERT(mpEnd < internalCapacityPtr()); + + return mpEnd++; + } + + + template + inline void fixed_vector::push_back(const value_type& value) + { + DoPushBack(typename type_select::type(), value); + } + + + template + inline void fixed_vector::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 + inline void fixed_vector::DoPushBack(false_type, const value_type& value) + { + EASTL_ASSERT(mpEnd < internalCapacityPtr()); + + ::new((void*)mpEnd++) value_type(value); + } + + + template + inline typename fixed_vector::reference fixed_vector::push_back() + { + return DoPushBack(typename type_select::type()); + } + + + template + inline typename fixed_vector::reference fixed_vector::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 + inline typename fixed_vector::reference fixed_vector::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 + inline void fixed_vector::push_back(value_type&& value) + { + DoPushBackMove(typename type_select::type(), eastl::move(value)); + } + + + template + inline void fixed_vector::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 + inline void fixed_vector::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 + inline const typename fixed_vector::overflow_allocator_type& + fixed_vector::get_overflow_allocator() const EA_NOEXCEPT + { + return get_allocator().get_overflow_allocator(); + } + + + template + inline typename fixed_vector::overflow_allocator_type& + fixed_vector::get_overflow_allocator() EA_NOEXCEPT + { + return get_allocator().get_overflow_allocator(); + } + + + template + inline void + fixed_vector::set_overflow_allocator(const overflow_allocator_type& allocator) + { + get_allocator().set_overflow_allocator(allocator); + } + + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + // operator ==, !=, <, >, <=, >= come from the vector implementations. + + template + inline void swap(fixed_vector& a, + fixed_vector& 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 + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/functional.h b/lib/EASTL/include/EASTL/functional.h new file mode 100644 index 000000000..6fa34893f --- /dev/null +++ b/lib/EASTL/include/EASTL/functional.h @@ -0,0 +1,1255 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_FUNCTIONAL_H +#define EASTL_FUNCTIONAL_H + + +#include +#include +#include +#include +#include +#include + + +#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 +{ + /////////////////////////////////////////////////////////////////////// + // Primary C++ functions + /////////////////////////////////////////////////////////////////////// + + template + struct plus : public binary_function + { + EA_CPP14_CONSTEXPR T operator()(const T& a, const T& b) const + { return a + b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/plus_void + template <> + struct plus + { + typedef int is_transparent; + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) + eastl::forward(b)) + { return eastl::forward(a) + eastl::forward(b); } + }; + + template + struct minus : public binary_function + { + EA_CPP14_CONSTEXPR T operator()(const T& a, const T& b) const + { return a - b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/minus_void + template <> + struct minus + { + typedef int is_transparent; + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) - eastl::forward(b)) + { return eastl::forward(a) - eastl::forward(b); } + }; + + template + struct multiplies : public binary_function + { + EA_CPP14_CONSTEXPR T operator()(const T& a, const T& b) const + { return a * b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/multiplies_void + template <> + struct multiplies + { + typedef int is_transparent; + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) * eastl::forward(b)) + { return eastl::forward(a) * eastl::forward(b); } + }; + + template + struct divides : public binary_function + { + EA_CPP14_CONSTEXPR T operator()(const T& a, const T& b) const + { return a / b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/divides_void + template <> + struct divides + { + typedef int is_transparent; + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) / eastl::forward(b)) + { return eastl::forward(a) / eastl::forward(b); } + }; + + template + struct modulus : public binary_function + { + EA_CPP14_CONSTEXPR T operator()(const T& a, const T& b) const + { return a % b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/modulus_void + template <> + struct modulus + { + typedef int is_transparent; + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) % eastl::forward(b)) + { return eastl::forward(a) % eastl::forward(b); } + }; + + template + struct negate : public unary_function + { + EA_CPP14_CONSTEXPR T operator()(const T& a) const + { return -a; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/negate_void + template <> + struct negate + { + typedef int is_transparent; + template + EA_CPP14_CONSTEXPR auto operator()(T&& t) const + -> decltype(-eastl::forward(t)) + { return -eastl::forward(t); } + }; + + template + struct equal_to : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const T& b) const + { return a == b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/equal_to_void + template <> + struct equal_to + { + typedef int is_transparent; + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) == eastl::forward(b)) + { return eastl::forward(a) == eastl::forward(b); } + }; + + template + bool validate_equal_to(const T& a, const T& b, Compare compare) + { + return compare(a, b) == compare(b, a); + } + + template + struct not_equal_to : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const T& b) const + { return a != b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/not_equal_to_void + template <> + struct not_equal_to + { + typedef int is_transparent; + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) != eastl::forward(b)) + { return eastl::forward(a) != eastl::forward(b); } + }; + + template + bool validate_not_equal_to(const T& a, const T& b, Compare compare) + { + return compare(a, b) == compare(b, a); // We want the not equal comparison results to be equal. + } + + /// str_equal_to + /// + /// Compares two 0-terminated string types. + /// The T types are expected to be iterators or act like iterators. + /// The expected behavior of str_less is the same as (strcmp(p1, p2) == 0). + /// + /// Example usage: + /// hash_set, str_equal_to > stringHashSet; + /// + /// Note: + /// You couldn't use str_equal_to like this: + /// bool result = equal("hi", "hi" + 2, "ho", str_equal_to()); + /// This is because equal tests an array of something, with each element by + /// the comparison function. But str_equal_to tests an array of something itself. + /// + /// To consider: Update this code to use existing word-based comparison optimizations, + /// such as that used in the EAStdC Strcmp function. + /// + template + struct str_equal_to : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(T a, T b) const + { + while(*a && (*a == *b)) + { + ++a; + ++b; + } + return (*a == *b); + } + }; + + template + struct greater : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const T& b) const + { return a > b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/greater_void + template <> + struct greater + { + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) > eastl::forward(b)) + { return eastl::forward(a) > eastl::forward(b); } + }; + + template + bool validate_greater(const T& a, const T& b, Compare compare) + { + return !compare(a, b) || !compare(b, a); // If (a > b), then !(b > a) + } + + + template + bool validate_less(const T& a, const T& b, Compare compare) + { + return !compare(a, b) || !compare(b, a); // If (a < b), then !(b < a) + } + + /// str_less + /// + /// Compares two 0-terminated string types. + /// The T types are expected to be iterators or act like iterators, + /// and that includes being a pointer to a C character array. + /// The expected behavior of str_less is the same as (strcmp(p1, p2) < 0). + /// This function is not Unicode-correct and it's not guaranteed to work + /// with all Unicode strings. + /// + /// Example usage: + /// set > stringSet; + /// + /// To consider: Update this code to use existing word-based comparison optimizations, + /// such as that used in the EAStdC Strcmp function. + /// + template + struct str_less : public binary_function + { + bool operator()(T a, T b) const + { + while(static_cast::type>::type>(*a) == + static_cast::type>::type>(*b)) + { + if(*a == 0) + return (*b != 0); + ++a; + ++b; + } + + char aValue = static_cast::type>(*a); + char bValue = static_cast::type>(*b); + + typename make_unsigned::type aValueU = static_cast::type>(aValue); + typename make_unsigned::type bValueU = static_cast::type>(bValue); + + return aValueU < bValueU; + + //return (static_cast::type>::type>(*a) < + // static_cast::type>::type>(*b)); + } + }; + + template + struct greater_equal : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const T& b) const + { return a >= b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/greater_equal_void + template <> + struct greater_equal + { + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) >= eastl::forward(b)) + { return eastl::forward(a) >= eastl::forward(b); } + }; + + template + bool validate_greater_equal(const T& a, const T& b, Compare compare) + { + return !compare(a, b) || !compare(b, a); // If (a >= b), then !(b >= a) + } + + template + struct less_equal : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const T& b) const + { return a <= b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/less_equal_void + template <> + struct less_equal + { + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) <= eastl::forward(b)) + { return eastl::forward(a) <= eastl::forward(b); } + }; + + template + bool validate_less_equal(const T& a, const T& b, Compare compare) + { + return !compare(a, b) || !compare(b, a); // If (a <= b), then !(b <= a) + } + + template + struct logical_and : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const T& b) const + { return a && b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/logical_and_void + template <> + struct logical_and + { + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) && eastl::forward(b)) + { return eastl::forward(a) && eastl::forward(b); } + }; + + template + struct logical_or : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const T& b) const + { return a || b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/logical_or_void + template <> + struct logical_or + { + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) || eastl::forward(b)) + { return eastl::forward(a) || eastl::forward(b); } + }; + + template + struct logical_not : public unary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a) const + { return !a; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/logical_not_void + template <> + struct logical_not + { + template + EA_CPP14_CONSTEXPR auto operator()(T&& t) const + -> decltype(!eastl::forward(t)) + { return !eastl::forward(t); } + }; + + + + /////////////////////////////////////////////////////////////////////// + // Dual type functions + /////////////////////////////////////////////////////////////////////// + + + template + struct equal_to_2 : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const U& b) const + { return a == b; } + + template , eastl::remove_const_t>>> + EA_CPP14_CONSTEXPR bool operator()(const U& b, const T& a) const + { return b == a; } + }; + + template + struct not_equal_to_2 : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const U& b) const + { return a != b; } + + template , eastl::remove_const_t>>> + EA_CPP14_CONSTEXPR bool operator()(const U& b, const T& a) const + { return b != a; } + }; + + + template + struct less_2 : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const U& b) const + { return a < b; } + + template , eastl::remove_const_t>>> + EA_CPP14_CONSTEXPR bool operator()(const U& b, const T& a) const + { return b < a; } + }; + + + /// unary_negate + /// + template + class unary_negate : public unary_function + { + protected: + Predicate mPredicate; + public: + explicit unary_negate(const Predicate& a) + : mPredicate(a) {} + EA_CPP14_CONSTEXPR bool operator()(const typename Predicate::argument_type& a) const + { return !mPredicate(a); } + }; + + template + inline EA_CPP14_CONSTEXPR unary_negate not1(const Predicate& predicate) + { return unary_negate(predicate); } + + + + /// binary_negate + /// + template + class binary_negate : public binary_function + { + protected: + Predicate mPredicate; + public: + explicit binary_negate(const Predicate& a) + : mPredicate(a) { } + EA_CPP14_CONSTEXPR bool operator()(const typename Predicate::first_argument_type& a, const typename Predicate::second_argument_type& b) const + { return !mPredicate(a, b); } + }; + + template + inline EA_CPP14_CONSTEXPR binary_negate not2(const Predicate& predicate) + { return binary_negate(predicate); } + + + + /// unary_compose + /// + template + struct unary_compose : public unary_function + { + protected: + Operation1 op1; + Operation2 op2; + + public: + unary_compose(const Operation1& x, const Operation2& y) + : op1(x), op2(y) {} + + typename Operation1::result_type operator()(const typename Operation2::argument_type& x) const + { return op1(op2(x)); } + + typename Operation1::result_type operator()(typename Operation2::argument_type& x) const + { return op1(op2(x)); } + }; + + template + inline unary_compose + compose1(const Operation1& op1, const Operation2& op2) + { + return unary_compose(op1,op2); + } + + + /// binary_compose + /// + template + class binary_compose : public unary_function + { + protected: + Operation1 op1; + Operation2 op2; + Operation3 op3; + + public: + // Support binary functors too. + typedef typename Operation2::argument_type first_argument_type; + typedef typename Operation3::argument_type second_argument_type; + + binary_compose(const Operation1& x, const Operation2& y, const Operation3& z) + : op1(x), op2(y), op3(z) { } + + typename Operation1::result_type operator()(const typename Operation2::argument_type& x) const + { return op1(op2(x),op3(x)); } + + typename Operation1::result_type operator()(typename Operation2::argument_type& x) const + { return op1(op2(x),op3(x)); } + + typename Operation1::result_type operator()(const typename Operation2::argument_type& x,const typename Operation3::argument_type& y) const + { return op1(op2(x),op3(y)); } + + typename Operation1::result_type operator()(typename Operation2::argument_type& x, typename Operation3::argument_type& y) const + { return op1(op2(x),op3(y)); } + }; + + + template + inline binary_compose + compose2(const Operation1& op1, const Operation2& op2, const Operation3& op3) + { + return binary_compose(op1, op2, op3); + } + + + + /////////////////////////////////////////////////////////////////////// + // pointer_to_unary_function + /////////////////////////////////////////////////////////////////////// + + /// pointer_to_unary_function + /// + /// This is an adapter template which converts a pointer to a standalone + /// function to a function object. This allows standalone functions to + /// work in many cases where the system requires a function object. + /// + /// Example usage: + /// ptrdiff_t Rand(ptrdiff_t n) { return rand() % n; } // Note: The C rand function is poor and slow. + /// pointer_to_unary_function randInstance(Rand); + /// random_shuffle(pArrayBegin, pArrayEnd, randInstance); + /// + template + class pointer_to_unary_function : public unary_function + { + protected: + Result (*mpFunction)(Arg); + + public: + pointer_to_unary_function() + { } + + explicit pointer_to_unary_function(Result (*pFunction)(Arg)) + : mpFunction(pFunction) { } + + Result operator()(Arg x) const + { return mpFunction(x); } + }; + + + /// ptr_fun + /// + /// This ptr_fun is simply shorthand for usage of pointer_to_unary_function. + /// + /// Example usage (actually, you don't need to use ptr_fun here, but it works anyway): + /// int factorial(int x) { return (x > 1) ? (x * factorial(x - 1)) : x; } + /// transform(pIntArrayBegin, pIntArrayEnd, pIntArrayBegin, ptr_fun(factorial)); + /// + template + inline pointer_to_unary_function + ptr_fun(Result (*pFunction)(Arg)) + { return pointer_to_unary_function(pFunction); } + + + + + + /////////////////////////////////////////////////////////////////////// + // pointer_to_binary_function + /////////////////////////////////////////////////////////////////////// + + /// pointer_to_binary_function + /// + /// This is an adapter template which converts a pointer to a standalone + /// function to a function object. This allows standalone functions to + /// work in many cases where the system requires a function object. + /// + template + class pointer_to_binary_function : public binary_function + { + protected: + Result (*mpFunction)(Arg1, Arg2); + + public: + pointer_to_binary_function() + { } + + explicit pointer_to_binary_function(Result (*pFunction)(Arg1, Arg2)) + : mpFunction(pFunction) {} + + Result operator()(Arg1 x, Arg2 y) const + { return mpFunction(x, y); } + }; + + + /// This ptr_fun is simply shorthand for usage of pointer_to_binary_function. + /// + /// Example usage (actually, you don't need to use ptr_fun here, but it works anyway): + /// int multiply(int x, int y) { return x * y; } + /// transform(pIntArray1Begin, pIntArray1End, pIntArray2Begin, pIntArray1Begin, ptr_fun(multiply)); + /// + template + inline pointer_to_binary_function + ptr_fun(Result (*pFunction)(Arg1, Arg2)) + { return pointer_to_binary_function(pFunction); } + + + + + + + /////////////////////////////////////////////////////////////////////// + // mem_fun + // mem_fun1 + // + // Note that mem_fun calls member functions via *pointers* to classes + // and not instances of classes. mem_fun_ref is for calling functions + // via instances of classes or references to classes. + // + // NOTE: + // mem_fun was deprecated in C++11 and removed in C++17, in favor + // of the more general mem_fn and bind. + // + /////////////////////////////////////////////////////////////////////// + + /// mem_fun_t + /// + /// Member function with no arguments. + /// + template + class mem_fun_t : public unary_function + { + public: + typedef Result (T::*MemberFunction)(); + + inline explicit mem_fun_t(MemberFunction pMemberFunction) + : mpMemberFunction(pMemberFunction) + { + // Empty + } + + inline Result operator()(T* pT) const + { + return (pT->*mpMemberFunction)(); + } + + protected: + MemberFunction mpMemberFunction; + }; + + + /// mem_fun1_t + /// + /// Member function with one argument. + /// + template + class mem_fun1_t : public binary_function + { + public: + typedef Result (T::*MemberFunction)(Argument); + + inline explicit mem_fun1_t(MemberFunction pMemberFunction) + : mpMemberFunction(pMemberFunction) + { + // Empty + } + + inline Result operator()(T* pT, Argument arg) const + { + return (pT->*mpMemberFunction)(arg); + } + + protected: + MemberFunction mpMemberFunction; + }; + + + /// const_mem_fun_t + /// + /// Const member function with no arguments. + /// Note that we inherit from unary_function + /// instead of what the C++ standard specifies: unary_function. + /// The C++ standard is in error and this has been recognized by the defect group. + /// + template + class const_mem_fun_t : public unary_function + { + public: + typedef Result (T::*MemberFunction)() const; + + inline explicit const_mem_fun_t(MemberFunction pMemberFunction) + : mpMemberFunction(pMemberFunction) + { + // Empty + } + + inline Result operator()(const T* pT) const + { + return (pT->*mpMemberFunction)(); + } + + protected: + MemberFunction mpMemberFunction; + }; + + + /// const_mem_fun1_t + /// + /// Const member function with one argument. + /// Note that we inherit from unary_function + /// instead of what the C++ standard specifies: unary_function. + /// The C++ standard is in error and this has been recognized by the defect group. + /// + template + class const_mem_fun1_t : public binary_function + { + public: + typedef Result (T::*MemberFunction)(Argument) const; + + inline explicit const_mem_fun1_t(MemberFunction pMemberFunction) + : mpMemberFunction(pMemberFunction) + { + // Empty + } + + inline Result operator()(const T* pT, Argument arg) const + { + return (pT->*mpMemberFunction)(arg); + } + + protected: + MemberFunction mpMemberFunction; + }; + + + /// mem_fun + /// + /// This is the high level interface to the mem_fun_t family. + /// + /// Example usage: + /// struct TestClass { void print() { puts("hello"); } } + /// TestClass* pTestClassArray[3] = { ... }; + /// for_each(pTestClassArray, pTestClassArray + 3, &TestClass::print); + /// + /// Note: using conventional inlining here to avoid issues on GCC/Linux + /// + template + inline mem_fun_t + mem_fun(Result (T::*MemberFunction)()) + { + return eastl::mem_fun_t(MemberFunction); + } + + template + inline mem_fun1_t + mem_fun(Result (T::*MemberFunction)(Argument)) + { + return eastl::mem_fun1_t(MemberFunction); + } + + template + inline const_mem_fun_t + mem_fun(Result (T::*MemberFunction)() const) + { + return eastl::const_mem_fun_t(MemberFunction); + } + + template + inline const_mem_fun1_t + mem_fun(Result (T::*MemberFunction)(Argument) const) + { + return eastl::const_mem_fun1_t(MemberFunction); + } + + + + + + /////////////////////////////////////////////////////////////////////// + // mem_fun_ref + // mem_fun1_ref + // + /////////////////////////////////////////////////////////////////////// + + /// mem_fun_ref_t + /// + template + class mem_fun_ref_t : public unary_function + { + public: + typedef Result (T::*MemberFunction)(); + + inline explicit mem_fun_ref_t(MemberFunction pMemberFunction) + : mpMemberFunction(pMemberFunction) + { + // Empty + } + + inline Result operator()(T& t) const + { + return (t.*mpMemberFunction)(); + } + + protected: + MemberFunction mpMemberFunction; + }; + + + /// mem_fun1_ref_t + /// + template + class mem_fun1_ref_t : public binary_function + { + public: + typedef Result (T::*MemberFunction)(Argument); + + inline explicit mem_fun1_ref_t(MemberFunction pMemberFunction) + : mpMemberFunction(pMemberFunction) + { + // Empty + } + + inline Result operator()(T& t, Argument arg) const + { + return (t.*mpMemberFunction)(arg); + } + + protected: + MemberFunction mpMemberFunction; + }; + + + /// const_mem_fun_ref_t + /// + template + class const_mem_fun_ref_t : public unary_function + { + public: + typedef Result (T::*MemberFunction)() const; + + inline explicit const_mem_fun_ref_t(MemberFunction pMemberFunction) + : mpMemberFunction(pMemberFunction) + { + // Empty + } + + inline Result operator()(const T& t) const + { + return (t.*mpMemberFunction)(); + } + + protected: + MemberFunction mpMemberFunction; + }; + + + /// const_mem_fun1_ref_t + /// + template + class const_mem_fun1_ref_t : public binary_function + { + public: + typedef Result (T::*MemberFunction)(Argument) const; + + inline explicit const_mem_fun1_ref_t(MemberFunction pMemberFunction) + : mpMemberFunction(pMemberFunction) + { + // Empty + } + + inline Result operator()(const T& t, Argument arg) const + { + return (t.*mpMemberFunction)(arg); + } + + protected: + MemberFunction mpMemberFunction; + }; + + + /// mem_fun_ref + /// Example usage: + /// struct TestClass { void print() { puts("hello"); } } + /// TestClass testClassArray[3]; + /// for_each(testClassArray, testClassArray + 3, &TestClass::print); + /// + /// Note: using conventional inlining here to avoid issues on GCC/Linux + /// + template + inline mem_fun_ref_t + mem_fun_ref(Result (T::*MemberFunction)()) + { + return eastl::mem_fun_ref_t(MemberFunction); + } + + template + inline mem_fun1_ref_t + mem_fun_ref(Result (T::*MemberFunction)(Argument)) + { + return eastl::mem_fun1_ref_t(MemberFunction); + } + + template + inline const_mem_fun_ref_t + mem_fun_ref(Result (T::*MemberFunction)() const) + { + return eastl::const_mem_fun_ref_t(MemberFunction); + } + + template + inline const_mem_fun1_ref_t + mem_fun_ref(Result (T::*MemberFunction)(Argument) const) + { + return eastl::const_mem_fun1_ref_t(MemberFunction); + } + + + // not_fn_ret + // not_fn_ret is a implementation specified return type of eastl::not_fn. + // The type name is not specified but it does have mandated functions that conforming implementations must support. + // + // http://en.cppreference.com/w/cpp/utility/functional/not_fn + // + template + struct not_fn_ret + { + explicit not_fn_ret(F&& f) : mDecayF(eastl::forward(f)) {} + not_fn_ret(not_fn_ret&& f) = default; + not_fn_ret(const not_fn_ret& f) = default; + + // overloads for lvalues + template + auto operator()(Args&&... args) & + -> decltype(!eastl::declval&, Args...>>()) + { return !eastl::invoke(mDecayF, eastl::forward(args)...); } + + template + auto operator()(Args&&... args) const & + -> decltype(!eastl::declval const&, Args...>>()) + { return !eastl::invoke(mDecayF, eastl::forward(args)...); } + + // overloads for rvalues + template + auto operator()(Args&&... args) && + -> decltype(!eastl::declval, Args...>>()) + { return !eastl::invoke(eastl::move(mDecayF), eastl::forward(args)...); } + + template + auto operator()(Args&&... args) const && + -> decltype(!eastl::declval const, Args...>>()) + { return !eastl::invoke(eastl::move(mDecayF), eastl::forward(args)...); } + + eastl::decay_t mDecayF; + }; + + /// not_fn + /// + /// Creates an implementation specified functor that returns the complement of the callable object it was passed. + /// not_fn is intended to replace the C++03-era negators eastl::not1 and eastl::not2. + /// + /// http://en.cppreference.com/w/cpp/utility/functional/not_fn + /// + /// Example usage: + /// + /// auto nf = eastl::not_fn([]{ return false; }); + /// assert(nf()); // return true + /// + template + inline not_fn_ret not_fn(F&& f) + { + return not_fn_ret(eastl::forward(f)); + } + + + /////////////////////////////////////////////////////////////////////// + // hash + /////////////////////////////////////////////////////////////////////// + namespace Internal + { + // utility to disable the generic template specialization that is + // used for enum types only. + template + struct EnableHashIf {}; + + template + struct EnableHashIf + { + size_t operator()(T p) const { return size_t(p); } + }; + } // namespace Internal + + + template struct hash; + + template + struct hash : Internal::EnableHashIf> {}; + + template struct hash // Note that we use the pointer as-is and don't divide by sizeof(T*). This is because the table is of a prime size and this division doesn't benefit distribution. + { size_t operator()(T* p) const { return size_t(uintptr_t(p)); } }; + + template <> struct hash + { size_t operator()(bool val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(char val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(signed char val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(unsigned char val) const { return static_cast(val); } }; + + #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE + template <> struct hash + { size_t operator()(char8_t val) const { return static_cast(val); } }; + #endif + + #if defined(EA_CHAR16_NATIVE) && EA_CHAR16_NATIVE + template <> struct hash + { size_t operator()(char16_t val) const { return static_cast(val); } }; + #endif + + #if defined(EA_CHAR32_NATIVE) && EA_CHAR32_NATIVE + template <> struct hash + { size_t operator()(char32_t val) const { return static_cast(val); } }; + #endif + + // If wchar_t is a native type instead of simply a define to an existing type... + #if !defined(EA_WCHAR_T_NON_NATIVE) + template <> struct hash + { size_t operator()(wchar_t val) const { return static_cast(val); } }; + #endif + + template <> struct hash + { size_t operator()(signed short val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(unsigned short val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(signed int val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(unsigned int val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(signed long val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(unsigned long val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(signed long long val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(unsigned long long val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(float val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(double val) const { return static_cast(val); } }; + + template <> struct hash + { size_t operator()(long double val) const { return static_cast(val); } }; + + #if defined(EA_HAVE_INT128) && EA_HAVE_INT128 + template <> struct hash + { size_t operator()(uint128_t val) const { return static_cast(val); } }; + #endif + + + /////////////////////////////////////////////////////////////////////////// + // string hashes + // + // Note that our string hashes here intentionally are slow for long strings. + // The reasoning for this is so: + // - The large majority of hashed strings are only a few bytes long. + // - The hash function is significantly more efficient if it can make this assumption. + // - The user is welcome to make a custom hash for those uncommon cases where + // long strings need to be hashed. Indeed, the user can probably make a + // special hash customized for such strings that's better than what we provide. + /////////////////////////////////////////////////////////////////////////// + + template <> struct hash + { + size_t operator()(const char* p) const + { + uint32_t c, result = 2166136261U; // FNV1 hash. Perhaps the best string hash. Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while((c = (uint8_t)*p++) != 0) // Using '!=' disables compiler warnings. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + template <> struct hash + { + size_t operator()(const char* p) const + { + uint32_t c, result = 2166136261U; // Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while((c = (uint8_t)*p++) != 0) // cast to unsigned 8 bit. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + +#if EA_CHAR8_UNIQUE + template <> struct hash + { + size_t operator()(const char8_t* p) const + { + uint32_t c, result = 2166136261U; // FNV1 hash. Perhaps the best string hash. Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while((c = (uint8_t)*p++) != 0) // Using '!=' disables compiler warnings. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + template <> struct hash + { + size_t operator()(const char8_t* p) const + { + uint32_t c, result = 2166136261U; // Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while((c = (uint8_t)*p++) != 0) // cast to unsigned 8 bit. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; +#endif + + + template <> struct hash + { + size_t operator()(const char16_t* p) const + { + uint32_t c, result = 2166136261U; // Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while((c = (uint16_t)*p++) != 0) // cast to unsigned 16 bit. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + template <> struct hash + { + size_t operator()(const char16_t* p) const + { + uint32_t c, result = 2166136261U; // Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while((c = (uint16_t)*p++) != 0) // cast to unsigned 16 bit. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + template <> struct hash + { + size_t operator()(const char32_t* p) const + { + uint32_t c, result = 2166136261U; // Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while((c = (uint32_t)*p++) != 0) // cast to unsigned 32 bit. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + template <> struct hash + { + size_t operator()(const char32_t* p) const + { + uint32_t c, result = 2166136261U; // Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while((c = (uint32_t)*p++) != 0) // cast to unsigned 32 bit. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + +#if defined(EA_WCHAR_UNIQUE) && EA_WCHAR_UNIQUE + template<> struct hash + { + size_t operator()(const wchar_t* p) const + { + uint32_t c, result = 2166136261U; // Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while ((c = (uint32_t)*p++) != 0) // cast to unsigned 32 bit. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + template<> struct hash + { + size_t operator()(const wchar_t* p) const + { + uint32_t c, result = 2166136261U; // Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while ((c = (uint32_t)*p++) != 0) // cast to unsigned 32 bit. + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; +#endif + + /// string_hash + /// + /// Defines a generic string hash for an arbitrary EASTL basic_string container. + /// + /// Example usage: + /// eastl::hash_set > hashSet; + /// + template + struct string_hash + { + typedef String string_type; + typedef typename String::value_type value_type; + typedef typename eastl::add_unsigned::type unsigned_value_type; + + size_t operator()(const string_type& s) const + { + const unsigned_value_type* p = (const unsigned_value_type*)s.c_str(); + uint32_t c, result = 2166136261U; // Intentionally uint32_t instead of size_t, so the behavior is the same regardless of size. + while((c = *p++) != 0) + result = (result * 16777619) ^ c; + return (size_t)result; + } + }; + + +} // namespace eastl + +#include + +#endif // Header include guard + + + + + + + diff --git a/lib/EASTL/include/EASTL/hash_map.h b/lib/EASTL/include/EASTL/hash_map.h new file mode 100644 index 000000000..c363597f2 --- /dev/null +++ b/lib/EASTL/include/EASTL/hash_map.h @@ -0,0 +1,580 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 +#include +#include +#include + +#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 hashMap; + /// i = hashMap.find_as("hello"); // Use default hash and compare. + /// + /// Example find_as usage (namespaces omitted for brevity): + /// hash_map hashMap; + /// i = hashMap.find_as("hello", hash(), equal_to_2()); + /// + template , typename Predicate = eastl::equal_to, + typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false> + class hash_map + : public hashtable, Allocator, eastl::use_first >, Predicate, + Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, true> + { + public: + typedef hashtable, Allocator, + eastl::use_first >, + Predicate, Hash, mod_range_hashing, default_ranged_hash, + prime_rehash_policy, bCacheHashCode, true, true> base_type; + typedef hash_map 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'. + 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. + /// + explicit hash_map(const allocator_type& allocator = EASTL_HASH_MAP_DEFAULT_ALLOCATOR) + : base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(), + Predicate(), eastl::use_first >(), 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 >(), 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 hm = { {3,"c"}, {4,"d"}, {5,"e"} }; ) + /// + hash_map(std::initializer_list 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 >(), 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 + 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 >(), allocator) + { + // Empty + } + + + this_type& operator=(const this_type& x) + { + return static_cast(base_type::operator=(x)); + } + + + this_type& operator=(std::initializer_list ilist) + { + return static_cast(base_type::operator=(ilist)); + } + + + this_type& operator=(this_type&& x) + { + return static_cast(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 key"); + #else + // assert false if asserts enabled + EASTL_ASSERT_MSG(false, "invalid hash_map 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 key"); + #else + // assert false if asserts enabled + EASTL_ASSERT_MSG(false, "invalid hash_map 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; + } + + + }; // hash_map + + /// hash_map erase_if + /// + /// https://en.cppreference.com/w/cpp/container/unordered_map/erase_if + template + void erase_if(eastl::hash_map& c, UserPredicate predicate) + { + // 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; + } + } + } + + + /// 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 Predicate = eastl::equal_to, + typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false> + class hash_multimap + : public hashtable, Allocator, eastl::use_first >, Predicate, + Hash, mod_range_hashing, default_ranged_hash, prime_rehash_policy, bCacheHashCode, true, false> + { + public: + typedef hashtable, Allocator, + eastl::use_first >, + Predicate, Hash, mod_range_hashing, default_ranged_hash, + prime_rehash_policy, bCacheHashCode, true, false> base_type; + typedef hash_multimap 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. + 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::try_emplace; + 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 >(), 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 >(), 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 hm = { {3,"c"}, {3,"C"}, {4,"d"} }; ) + /// + hash_multimap(std::initializer_list 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 >(), 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 + 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 >(), allocator) + { + // Empty + } + + + this_type& operator=(const this_type& x) + { + return static_cast(base_type::operator=(x)); + } + + + this_type& operator=(std::initializer_list ilist) + { + return static_cast(base_type::operator=(ilist)); + } + + + this_type& operator=(this_type&& x) + { + return static_cast(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 + void erase_if(eastl::hash_multimap& c, UserPredicate predicate) + { + // 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; + } + } + } + + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const hash_map& a, + const hash_map& b) + { + typedef typename hash_map::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; + } + + template + inline bool operator!=(const hash_map& a, + const hash_map& b) + { + return !(a == b); + } + + + template + inline bool operator==(const hash_multimap& a, + const hash_multimap& b) + { + typedef typename hash_multimap::const_iterator const_iterator; + typedef typename eastl::iterator_traits::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 aRange; + eastl::pair 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; + } + + template + inline bool operator!=(const hash_multimap& a, + const hash_multimap& b) + { + return !(a == b); + } + + +} // namespace eastl + + +#endif // Header include guard + + + + + + diff --git a/lib/EASTL/include/EASTL/hash_set.h b/lib/EASTL/include/EASTL/hash_set.h new file mode 100644 index 000000000..c075975de --- /dev/null +++ b/lib/EASTL/include/EASTL/hash_set.h @@ -0,0 +1,468 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 +#include +#include +#include + +#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 hashSet; + /// i = hashSet.find_as("hello"); // Use default hash and compare. + /// + /// Example find_as usage (namespaces omitted for brevity): + /// hash_set hashSet; + /// i = hashSet.find_as("hello", hash(), equal_to_2()); + /// + template , typename Predicate = eastl::equal_to, + typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false> + class hash_set + : public hashtable, Predicate, + Hash, mod_range_hashing, default_ranged_hash, + prime_rehash_policy, bCacheHashCode, false, true> + { + public: + typedef hashtable, Predicate, + Hash, mod_range_hashing, default_ranged_hash, + prime_rehash_policy, bCacheHashCode, false, true> base_type; + typedef hash_set 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. + /// + explicit hash_set(const allocator_type& allocator = EASTL_HASH_SET_DEFAULT_ALLOCATOR) + : base_type(0, Hash(), mod_range_hashing(), default_ranged_hash(), Predicate(), eastl::use_self(), 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(), 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 hs = { 3, 4, 5, }; ) + /// + hash_set(std::initializer_list 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(), 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 + 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(), allocator) + { + // Empty + } + + + this_type& operator=(const this_type& x) + { + return static_cast(base_type::operator=(x)); + } + + + this_type& operator=(std::initializer_list ilist) + { + return static_cast(base_type::operator=(ilist)); + } + + + this_type& operator=(this_type&& x) + { + return static_cast(base_type::operator=(eastl::move(x))); + } + + }; // hash_set + + /// hash_set erase_if + /// + /// https://en.cppreference.com/w/cpp/container/unordered_set/erase_if + template + void erase_if(eastl::hash_set& c, UserPredicate predicate) + { + // 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; + } + } + } + + + /// 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 Predicate = eastl::equal_to, + typename Allocator = EASTLAllocatorType, bool bCacheHashCode = false> + class hash_multiset + : public hashtable, Predicate, + Hash, mod_range_hashing, default_ranged_hash, + prime_rehash_policy, bCacheHashCode, false, false> + { + public: + typedef hashtable, Predicate, + Hash, mod_range_hashing, default_ranged_hash, + prime_rehash_policy, bCacheHashCode, false, false> base_type; + typedef hash_multiset 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(), 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(), 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 hs = { 3, 3, 4, }; ) + /// + hash_multiset(std::initializer_list 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(), 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 + 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(), allocator) + { + // Empty + } + + + this_type& operator=(const this_type& x) + { + return static_cast(base_type::operator=(x)); + } + + + this_type& operator=(std::initializer_list ilist) + { + return static_cast(base_type::operator=(ilist)); + } + + + this_type& operator=(this_type&& x) + { + return static_cast(base_type::operator=(eastl::move(x))); + } + + }; // hash_multiset + + /// hash_multiset erase_if + /// + /// https://en.cppreference.com/w/cpp/container/unordered_multiset/erase_if + template + void erase_if(eastl::hash_multiset& c, UserPredicate predicate) + { + // 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; + } + } + } + + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const hash_set& a, + const hash_set& b) + { + typedef typename hash_set::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; + } + + template + inline bool operator!=(const hash_set& a, + const hash_set& b) + { + return !(a == b); + } + + + template + inline bool operator==(const hash_multiset& a, + const hash_multiset& b) + { + typedef typename hash_multiset::const_iterator const_iterator; + typedef typename eastl::iterator_traits::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 aRange; + eastl::pair 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; + } + + template + inline bool operator!=(const hash_multiset& a, + const hash_multiset& b) + { + return !(a == b); + } + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/heap.h b/lib/EASTL/include/EASTL/heap.h new file mode 100644 index 000000000..a8e4260ea --- /dev/null +++ b/lib/EASTL/include/EASTL/heap.h @@ -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 +#include +#include + +#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 + 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()(*(first + parentPosition), value); + parentPosition = (position - 1) >> 1) + { + *(first + position) = eastl::forward(*(first + parentPosition)); // Swap the node with its parent. + position = parentPosition; + } + + *(first + position) = eastl::forward(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 + inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, const T& value) + { + typedef typename iterator_traits::value_type value_type; + promote_heap_impl(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 + inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, T&& value) + { + typedef typename iterator_traits::value_type value_type; + promote_heap_impl(first, topPosition, position, eastl::forward(value)); + } + + + template + 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(*(first + parentPosition)); // Swap the node with its parent. + position = parentPosition; + } + + *(first + position) = eastl::forward(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 + inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, const T& value, Compare compare) + { + typedef typename iterator_traits::value_type value_type; + promote_heap_impl(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 + inline void promote_heap(RandomAccessIterator first, Distance topPosition, Distance position, T&& value, Compare compare) + { + typedef typename iterator_traits::value_type value_type; + promote_heap_impl(first, topPosition, position, eastl::forward(value), compare); + } + + + + /////////////////////////////////////////////////////////////////////// + // adjust_heap (internal function) + /////////////////////////////////////////////////////////////////////// + + template + 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()(*(first + childPosition), *(first + (childPosition - 1)))) // Choose the larger of the two children. + --childPosition; + *(first + position) = eastl::forward(*(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(*(first + (childPosition - 1))); + position = childPosition - 1; + } + + eastl::promote_heap(first, topPosition, position, eastl::forward(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 + void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, const T& value) + { + typedef typename iterator_traits::value_type value_type; + adjust_heap_impl(first, topPosition, heapSize, position, eastl::forward(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 + void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T&& value) + { + typedef typename iterator_traits::value_type value_type; + adjust_heap_impl(first, topPosition, heapSize, position, eastl::forward(value)); + } + + + template + 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(*(first + childPosition)); // Swap positions with this child. + position = childPosition; + } + + if(childPosition == heapSize) // If we are at the bottom... + { + *(first + position) = eastl::forward(*(first + (childPosition - 1))); + position = childPosition - 1; + } + + eastl::promote_heap(first, topPosition, position, eastl::forward(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 + void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, const T& value, Compare compare) + { + typedef typename iterator_traits::value_type value_type; + adjust_heap_impl(first, topPosition, heapSize, position, eastl::forward(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 + void adjust_heap(RandomAccessIterator first, Distance topPosition, Distance heapSize, Distance position, T&& value, Compare compare) + { + typedef typename iterator_traits::value_type value_type; + adjust_heap_impl(first, topPosition, heapSize, position, eastl::forward(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 heap; + /// + /// heap.push_back(3); + /// push_heap(heap.begin(), heap.end()); // Places '3' appropriately. + /// + template + inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::value_type value_type; + + const value_type tempBottom(eastl::forward(*(last - 1))); + + eastl::promote_heap + (first, (difference_type)0, (difference_type)(last - first - 1), eastl::forward(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 + inline void push_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::value_type value_type; + + const value_type tempBottom(*(last - 1)); + + eastl::promote_heap + (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 heap; + /// + /// heap.push_back(2); + /// heap.push_back(3); + /// heap.push_back(1); + /// + /// 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 + inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::value_type value_type; + + value_type tempBottom(eastl::forward(*(last - 1))); + *(last - 1) = eastl::forward(*first); + eastl::adjust_heap + (first, (difference_type)0, (difference_type)(last - first - 1), 0, eastl::forward(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 + inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::value_type value_type; + + value_type tempBottom(eastl::forward(*(last - 1))); + *(last - 1) = eastl::forward(*first); + eastl::adjust_heap + (first, (difference_type)0, (difference_type)(last - first - 1), 0, eastl::forward(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 + 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::difference_type difference_type; + typedef typename eastl::iterator_traits::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(*(first + parentPosition))); + eastl::adjust_heap + (first, parentPosition, heapSize, parentPosition, eastl::forward(temp)); + } while(parentPosition != 0); + } + } + + + template + void make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare compare) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::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(*(first + parentPosition))); + eastl::adjust_heap + (first, parentPosition, heapSize, parentPosition, eastl::forward(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 + inline void sort_heap(RandomAccessIterator first, RandomAccessIterator last) + { + for(; (last - first) > 1; --last) // We simply use the heap to sort itself. + eastl::pop_heap(first, last); + } + + + /// sort_heap + /// + /// The Compare function must work equivalently to the compare function used + /// to make and maintain the heap. + /// + template + 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(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 + inline void remove_heap(RandomAccessIterator first, Distance heapSize, Distance position) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::value_type value_type; + + const value_type tempBottom(*(first + heapSize - 1)); + *(first + heapSize - 1) = *(first + position); + eastl::adjust_heap + (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 + inline void remove_heap(RandomAccessIterator first, Distance heapSize, Distance position, Compare compare) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::value_type value_type; + + const value_type tempBottom(*(first + heapSize - 1)); + *(first + heapSize - 1) = *(first + position); + eastl::adjust_heap + (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 + inline void change_heap(RandomAccessIterator first, Distance heapSize, Distance position) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::value_type value_type; + + eastl::remove_heap(first, heapSize, position); + + value_type tempBottom(*(first + heapSize - 1)); + + eastl::promote_heap + (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 + inline void change_heap(RandomAccessIterator first, Distance heapSize, Distance position, Compare compare) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::value_type value_type; + + eastl::remove_heap(first, heapSize, position, compare); + + value_type tempBottom(*(first + heapSize - 1)); + + eastl::promote_heap + (first, (difference_type)0, (difference_type)(heapSize - 1), tempBottom, compare); + } + + + + /////////////////////////////////////////////////////////////////////// + // is_heap_until + /////////////////////////////////////////////////////////////////////// + + /// is_heap_until + /// + template + 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 + 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 + 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 + 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 + // 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 + + + + diff --git a/lib/EASTL/include/EASTL/initializer_list.h b/lib/EASTL/include/EASTL/initializer_list.h new file mode 100644 index 000000000..028fb4f86 --- /dev/null +++ b/lib/EASTL/include/EASTL/initializer_list.h @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +// +// This file #includes 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 +#include + +#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 + 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 needs to be updated. + namespace std + { + // See the C++11 Standard, section 18.9. + template + 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 + const T* begin(std::initializer_list ilist) EA_NOEXCEPT + { + return ilist.begin(); + } + + template + const T* end(std::initializer_list ilist) EA_NOEXCEPT + { + return ilist.end(); + } + } + +#endif + + +#endif // Header include guard + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch.h new file mode 100644 index 000000000..4924a5911 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch.h @@ -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 */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_add_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_add_fetch.h new file mode 100644 index 000000000..65771f896 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_add_fetch.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 */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_and_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_and_fetch.h new file mode 100644 index 000000000..df7ba35df --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_and_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 */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_cmpxchg_strong.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_cmpxchg_strong.h new file mode 100644 index 000000000..1005dc33c --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_cmpxchg_strong.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 */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_cmpxchg_weak.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_cmpxchg_weak.h new file mode 100644 index 000000000..5ce263867 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_cmpxchg_weak.h @@ -0,0 +1,430 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_CMPXCHG_WEAK_H +#define EASTL_ATOMIC_INTERNAL_ARCH_CMPXCHG_WEAK_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128_AVAILABLE 0 +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_8_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_8_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_8_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_8(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_8_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_8(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_8_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) + + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_16_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_16_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_16_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_16(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_16_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_16(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_16_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired) + + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_32_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_32_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_32_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_32(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_32_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_32(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_32_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) + + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_64_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_64_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_64_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_64(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_64_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_64(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_64_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) + + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_128_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_128_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_128_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_128_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) + +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_128_AVAILABLE \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128_AVAILABLE +#define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_CMPXCHG_WEAK_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_compiler_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_compiler_barrier.h new file mode 100644 index 000000000..0652469ba --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_compiler_barrier.h @@ -0,0 +1,19 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_COMPILER_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_ARCH_COMPILER_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_ARCH_ATOMIC_COMPILER_BARRIER_AVAILABLE 0 + +#define EASTL_ARCH_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY_AVAILABLE 0 + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_COMPILER_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_cpu_pause.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_cpu_pause.h new file mode 100644 index 000000000..e8c2d1d76 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_cpu_pause.h @@ -0,0 +1,25 @@ +///////////////////////////////////////////////////////////////////////////////// +// copyright (c) electronic arts inc. all rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_CPU_PAUSE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_CPU_PAUSE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_PAUSE() +// +#if defined(EASTL_ARCH_ATOMIC_CPU_PAUSE) + #define EASTL_ARCH_ATOMIC_CPU_PAUSE_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CPU_PAUSE_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_CPU_PAUSE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_exchange.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_exchange.h new file mode 100644 index 000000000..76003188f --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_exchange.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_EXCHANGE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_EXCHANGE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_EXCHANGE_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_8) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_8) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_8) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_16) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_16) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_16) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_32) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_32) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_32) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_64) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_64) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_64) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_128) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_128) + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_128) + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_EXCHANGE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_add.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_add.h new file mode 100644 index 000000000..71907f709 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_add.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_FETCH_ADD_H +#define EASTL_ATOMIC_INTERNAL_ARCH_FETCH_ADD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_ADD_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_8) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_8) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_8) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_16) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_16) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_16) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_32) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_32) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_32) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_64) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_64) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_64) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_128) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_128) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_128) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_FETCH_ADD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_and.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_and.h new file mode 100644 index 000000000..f2b39a4c5 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_and.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_FETCH_AND_H +#define EASTL_ATOMIC_INTERNAL_ARCH_FETCH_AND_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_AND_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_8) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_8) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_8) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_16) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_16) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_16) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_32) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_32) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_32) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_64) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_64) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_64) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_128) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_128) + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_128) + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_FETCH_AND_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_or.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_or.h new file mode 100644 index 000000000..dd6dd0dbc --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_or.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_FETCH_OR_H +#define EASTL_ATOMIC_INTERNAL_ARCH_FETCH_OR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_OR_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_8) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_8) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_8) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_16) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_16) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_16) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_32) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_32) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_32) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_64) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_64) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_64) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_128) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_128) + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_128) + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_FETCH_OR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_sub.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_sub.h new file mode 100644 index 000000000..ea63db73b --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_sub.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_FETCH_SUB_H +#define EASTL_ATOMIC_INTERNAL_ARCH_FETCH_SUB_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_SUB_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_8) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_8) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_8) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_16) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_16) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_16) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_32) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_32) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_32) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_64) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_64) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_64) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_128) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_128) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_128) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_FETCH_SUB_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_xor.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_xor.h new file mode 100644 index 000000000..b41ad2d40 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_fetch_xor.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_FETCH_XOR_H +#define EASTL_ATOMIC_INTERNAL_ARCH_FETCH_XOR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_XOR_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_8) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_8) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_8) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_16) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_16) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_16) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_32) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_32) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_32) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_64) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_64) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_64) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_128) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_128) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_128) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_FETCH_XOR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_load.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_load.h new file mode 100644 index 000000000..eea7cf491 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_load.h @@ -0,0 +1,125 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_LOAD_H +#define EASTL_ATOMIC_INTERNAL_ARCH_LOAD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_LOAD_*_N(type, type ret, type * ptr) +// +#if defined(EASTL_ARCH_ATOMIC_LOAD_RELAXED_8) + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_LOAD_RELAXED_16) + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_LOAD_RELAXED_32) + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_READ_DEPENDS_32) + #define EASTL_ARCH_ATOMIC_LOAD_READ_DEPENDS_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_READ_DEPENDS_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_LOAD_RELAXED_64) + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_READ_DEPENDS_64) + #define EASTL_ARCH_ATOMIC_LOAD_READ_DEPENDS_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_READ_DEPENDS_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_LOAD_RELAXED_128) + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_LOAD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_memory_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_memory_barrier.h new file mode 100644 index 000000000..c6cc6bfcf --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_memory_barrier.h @@ -0,0 +1,47 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_MEMORY_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_ARCH_MEMORY_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_MB() +// +#if defined(EASTL_ARCH_ATOMIC_CPU_MB) + #define EASTL_ARCH_ATOMIC_CPU_MB_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CPU_MB_AVAILABLE 0 +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_WMB() +// +#if defined(EASTL_ARCH_ATOMIC_CPU_WMB) + #define EASTL_ARCH_ATOMIC_CPU_WMB_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CPU_WMB_AVAILABLE 0 +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_RMB() +// +#if defined(EASTL_ARCH_ATOMIC_CPU_RMB) + #define EASTL_ARCH_ATOMIC_CPU_RMB_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_CPU_RMB_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_MEMORY_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_or_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_or_fetch.h new file mode 100644 index 000000000..110326b45 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_or_fetch.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_OR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_ARCH_OR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_OR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_8) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_8) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_8) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_16) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_16) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_16) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_32) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_32) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_32) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_64) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_64) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_64) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_128) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_128) + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_128) + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_OR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_signal_fence.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_signal_fence.h new file mode 100644 index 000000000..65b64fc28 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_signal_fence.h @@ -0,0 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_SIGNAL_FENCE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_SIGNAL_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_ARCH_ATOMIC_SIGNAL_FENCE_RELAXED_AVAILABLE 0 +#define EASTL_ARCH_ATOMIC_SIGNAL_FENCE_ACQUIRE_AVAILABLE 0 +#define EASTL_ARCH_ATOMIC_SIGNAL_FENCE_RELEASE_AVAILABLE 0 +#define EASTL_ARCH_ATOMIC_SIGNAL_FENCE_ACQ_REL_AVAILABLE 0 +#define EASTL_ARCH_ATOMIC_SIGNAL_FENCE_SEQ_CST_AVAILABLE 0 + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_SIGNAL_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_store.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_store.h new file mode 100644 index 000000000..9a4112cb7 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_store.h @@ -0,0 +1,113 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_STORE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_STORE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_STORE_*_N(type, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_STORE_RELAXED_8) + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_RELEASE_8) + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_STORE_RELAXED_16) + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_RELEASE_16) + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_STORE_RELAXED_32) + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_RELEASE_32) + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_STORE_RELAXED_64) + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_RELEASE_64) + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_STORE_RELAXED_128) + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_RELEASE_128) + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_STORE_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_STORE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_sub_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_sub_fetch.h new file mode 100644 index 000000000..20241b14f --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_sub_fetch.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_SUB_FETCH_H +#define EASTL_ATOMIC_INTERNAL_ARCH_SUB_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_SUB_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_8) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_8) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_8) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_16) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_16) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_16) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_32) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_32) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_32) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_64) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_64) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_64) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_128) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_128) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_128) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_SUB_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_thread_fence.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_thread_fence.h new file mode 100644 index 000000000..676fbf191 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_thread_fence.h @@ -0,0 +1,49 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_THREAD_FENCE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_THREAD_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_THREAD_FENCE_*() +// +#if defined(EASTL_ARCH_ATOMIC_THREAD_FENCE_RELAXED) + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_RELAXED_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_RELAXED_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQUIRE) + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQUIRE_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQUIRE_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_THREAD_FENCE_RELEASE) + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_RELEASE_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_RELEASE_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQ_REL) + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQ_REL_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQ_REL_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_THREAD_FENCE_SEQ_CST) + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_SEQ_CST_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_SEQ_CST_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_THREAD_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arch_xor_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_xor_fetch.h new file mode 100644 index 000000000..63548c226 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arch_xor_fetch.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_XOR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_ARCH_XOR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_XOR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_8) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_8) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_8) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_8) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_8) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_16) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_16) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_16) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_16) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_16) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_32) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_32) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_32) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_32) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_32) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_64) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_64) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_64) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_64) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_64) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_128) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_128) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_128) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_128) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_128) + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_XOR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm.h new file mode 100644 index 000000000..cc2ce522a --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm.h @@ -0,0 +1,89 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_ARM_H +#define EASTL_ATOMIC_INTERNAL_ARCH_ARM_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +/** + * NOTE: We use this mapping + * + * ARMv7 Mapping 'trailing sync;': + * + * Load Relaxed : ldr + * Load Acquire : ldr; dmb ish + * Load Seq_Cst : ldr; dmb ish + * + * Store Relaxed : str + * Store Release : dmb ish; str + * Store Seq_Cst : dmb ish; str; dmb ish + * + * Relaxed Fence : + * Acquire Fence : dmb ish + * Release Fence : dmb ish + * Acq_Rel Fence : dmb ish + * Seq_Cst Fence : dmb ish + */ + +/** + * ARMv7 Mapping 'leading sync;'; + * + * Load Relaxed : ldr + * Load Acquire : ldr; dmb ish + * Load Seq_Cst : dmb ish; ldr; dmb ish + * + * Store Relaxed : str + * Store Release : dmb ish; str + * Store Seq_Cst : dmb ish: str + * + * Relaxed Fence : + * Acquire Fence : dmb ish + * Release Fence : dmb ish + * Acq_Rel Fence : dmb ish + * Seq_Cst Fence : dmb ish + */ + +/** + * NOTE: + * + * On ARM32/64, we use the 'trailing sync;' convention with the stricter load acquire that uses + * a dmb instead of a control dependency + isb to ensure the IRIW litmus test is satisfied + * as one reason. See EASTL/atomic.h for futher explanation and deep-dive. + * + * For ARMv8 we could move to use the new proper store release and load acquire, RCsc variant. + * All ARMv7 approaches work on ARMv8 and this code path is only used on msvc which isn't used + * heavily. Most of the ARM code will end up going thru clang or gcc since microsoft arm devices + * aren't that abundant. + */ + + +///////////////////////////////////////////////////////////////////////////////// + + +#if defined(EA_COMPILER_MSVC) + + #if EA_PLATFORM_PTR_SIZE == 8 + #define EASTL_ARCH_ATOMIC_HAS_128BIT + #endif + +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +#include "arch_arm_load.h" +#include "arch_arm_store.h" + +#include "arch_arm_memory_barrier.h" + +#include "arch_arm_thread_fence.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_ARM_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_load.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_load.h new file mode 100644 index 000000000..e3b79b84a --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_load.h @@ -0,0 +1,156 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_ARM_LOAD_H +#define EASTL_ATOMIC_INTERNAL_ARCH_ARM_LOAD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_LOAD_*_N(type, type ret, type * ptr) +// +#if defined(EA_COMPILER_MSVC) + + + /** + * NOTE: + * + * Even 8-byte aligned 64-bit memory accesses on ARM32 are not + * guaranteed to be atomic on all ARM32 cpus. Only guaranteed on + * cpus with the LPAE extension. We need to use a + * ldrexd instruction in order to ensure no shearing is observed + * for all ARM32 processors. + */ + #if defined(EA_PROCESSOR_ARM32) + + #define EASTL_ARCH_ATOMIC_ARM32_LDREXD(ret, ptr) \ + ret = __ldrexd((ptr)) + + #endif + + + #define EASTL_ARCH_ATOMIC_ARM_LOAD_N(integralType, bits, type, ret, ptr) \ + { \ + integralType retIntegral; \ + retIntegral = EA_PREPROCESSOR_JOIN(__iso_volatile_load, bits)(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr))); \ + \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, retIntegral); \ + } + + + #define EASTL_ARCH_ATOMIC_ARM_LOAD_8(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_N(__int8, 8, type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_ARM_LOAD_16(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_N(__int16, 16, type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_ARM_LOAD_32(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_N(__int32, 32, type, ret, ptr) + + + #if defined(EA_PROCESSOR_ARM32) + + + #define EASTL_ARCH_ATOMIC_LOAD_64(type, ret, ptr) \ + { \ + __int64 loadRet64; \ + EASTL_ARCH_ATOMIC_ARM32_LDREXD(loadRet64, EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(__int64, (ptr))); \ + \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, loadRet64); \ + } + + #else + + #define EASTL_ARCH_ATOMIC_ARM_LOAD_64(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_N(__int64, 64, type, ret, ptr) + + #endif + + + /** + * NOTE: + * + * The ARM documentation states the following: + * A 64-bit pair requires the address to be quadword aligned and is single-copy atomic for each doubleword at doubleword granularity + * + * Thus we must ensure the store succeeds inorder for the load to be observed as atomic. + * Thus we must use the full cmpxchg in order to do a proper atomic load. + */ + #define EASTL_ARCH_ATOMIC_ARM_LOAD_128(type, ret, ptr, MemoryOrder) \ + { \ + bool cmpxchgRetBool; \ + ret = *(ptr); \ + do \ + { \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_CMPXCHG_STRONG_, MemoryOrder), _128)(type, cmpxchgRetBool, \ + ptr, &(ret), ret); \ + } while (!cmpxchgRetBool); \ + } + + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_8(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_8(type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_16(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_16(type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_32(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_32(type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_64(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_64(type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_128(type, ret, ptr, RELAXED) + + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_8(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_8(type, ret, ptr); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_16(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_16(type, ret, ptr); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_32(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_32(type, ret, ptr); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_64(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_64(type, ret, ptr); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_128(type, ret, ptr, ACQUIRE) + + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_8(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_8(type, ret, ptr); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_16(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_16(type, ret, ptr); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_32(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_32(type, ret, ptr); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_64(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_64(type, ret, ptr); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_ARM_LOAD_128(type, ret, ptr, SEQ_CST) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_ARM_LOAD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_memory_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_memory_barrier.h new file mode 100644 index 000000000..44dc991dd --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_memory_barrier.h @@ -0,0 +1,97 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_ARM_MEMORY_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_ARCH_ARM_MEMORY_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#if defined(EA_COMPILER_MSVC) && !defined(EA_COMPILER_CLANG_CL) + + #if defined(EA_PROCESSOR_ARM32) + + #define EASTL_ARM_DMB_ISH _ARM_BARRIER_ISH + + #define EASTL_ARM_DMB_ISHST _ARM_BARRIER_ISHST + + #define EASTL_ARM_DMB_ISHLD _ARM_BARRIER_ISH + + #elif defined(EA_PROCESSOR_ARM64) + + #define EASTL_ARM_DMB_ISH _ARM64_BARRIER_ISH + + #define EASTL_ARM_DMB_ISHST _ARM64_BARRIER_ISHST + + #define EASTL_ARM_DMB_ISHLD _ARM64_BARRIER_ISHLD + + #endif + + + /** + * NOTE: + * + * While it makes no sense for a hardware memory barrier to not imply a compiler barrier. + * MSVC docs do not explicitly state that, so better to be safe than sorry chasing down + * hard to find bugs due to the compiler deciding to reorder things. + */ + + #define EASTL_ARCH_ATOMIC_ARM_EMIT_DMB(option) \ + EASTL_ATOMIC_COMPILER_BARRIER(); \ + __dmb(option); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + +#elif defined(EA_COMPILER_GNUC) || defined(__clang__) + + #define EASTL_ARM_DMB_ISH ish + + #define EASTL_ARM_DMB_ISHST ishst + + #if defined(EA_PROCESSOR_ARM32) + + #define EASTL_ARM_DMB_ISHLD ish + + #elif defined(EA_PROCESSOR_ARM64) + + #define EASTL_ARM_DMB_ISHLD ishld + + #endif + + + #define EASTL_ARCH_ATOMIC_ARM_EMIT_DMB(option) \ + __asm__ __volatile__ ("dmb " EA_STRINGIFY(option) ::: "memory") + + +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_MB() +// +#define EASTL_ARCH_ATOMIC_CPU_MB() \ + EASTL_ARCH_ATOMIC_ARM_EMIT_DMB(EASTL_ARM_DMB_ISH) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_WMB() +// +#define EASTL_ARCH_ATOMIC_CPU_WMB() \ + EASTL_ARCH_ATOMIC_ARM_EMIT_DMB(EASTL_ARM_DMB_ISHST) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_RMB() +// +#define EASTL_ARCH_ATOMIC_CPU_RMB() \ + EASTL_ARCH_ATOMIC_ARM_EMIT_DMB(EASTL_ARM_DMB_ISHLD) + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_ARM_MEMORY_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_store.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_store.h new file mode 100644 index 000000000..ab53b9d46 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_store.h @@ -0,0 +1,142 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_ARM_STORE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_ARM_STORE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_STORE_*_N(type, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) + + + #define EASTL_ARCH_ATOMIC_ARM_STORE_N(integralType, bits, type, ptr, val) \ + EA_PREPROCESSOR_JOIN(__iso_volatile_store, bits)(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), EASTL_ATOMIC_TYPE_PUN_CAST(integralType, (val))) + + + #define EASTL_ARCH_ATOMIC_ARM_STORE_8(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_N(__int8, 8, type, ptr, val) + + #define EASTL_ARCH_ATOMIC_ARM_STORE_16(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_N(__int16, 16, type, ptr, val) + + #define EASTL_ARCH_ATOMIC_ARM_STORE_32(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_N(__int32, 32, type, ptr, val) + + + #if defined(EA_PROCESSOR_ARM64) + + #define EASTL_ARCH_ATOMIC_ARM_STORE_64(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_N(__int64, 64, type, ptr, val) + + #endif + + + #define EASTL_ARCH_ATOMIC_ARM_STORE_128(type, ptr, val, MemoryOrder) \ + { \ + type exchange128; EA_UNUSED(exchange128); \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_EXCHANGE_, MemoryOrder), _128)(type, exchange128, ptr, val); \ + } + + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_8(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_8(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_16(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_16(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_32(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_32(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_128(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_128(type, ptr, val, RELAXED) + + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_8(type, ptr, val) \ + EASTL_ATOMIC_CPU_MB(); \ + EASTL_ARCH_ATOMIC_ARM_STORE_8(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_16(type, ptr, val) \ + EASTL_ATOMIC_CPU_MB(); \ + EASTL_ARCH_ATOMIC_ARM_STORE_16(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_32(type, ptr, val) \ + EASTL_ATOMIC_CPU_MB(); \ + EASTL_ARCH_ATOMIC_ARM_STORE_32(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_128(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_128(type, ptr, val, RELEASE) + + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_8(type, ptr, val) \ + EASTL_ATOMIC_CPU_MB(); \ + EASTL_ARCH_ATOMIC_ARM_STORE_8(type, ptr, val) ; \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_16(type, ptr, val) \ + EASTL_ATOMIC_CPU_MB(); \ + EASTL_ARCH_ATOMIC_ARM_STORE_16(type, ptr, val); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_32(type, ptr, val) \ + EASTL_ATOMIC_CPU_MB(); \ + EASTL_ARCH_ATOMIC_ARM_STORE_32(type, ptr, val); \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_128(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_128(type, ptr, val, SEQ_CST) + + + #if defined(EA_PROCESSOR_ARM32) + + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_64(type, ptr, val) \ + { \ + type retExchange64; EA_UNUSED(retExchange64); \ + EASTL_ATOMIC_EXCHANGE_RELAXED_64(type, retExchange64, ptr, val); \ + } + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_64(type, ptr, val) \ + { \ + type retExchange64; EA_UNUSED(retExchange64); \ + EASTL_ATOMIC_EXCHANGE_RELEASE_64(type, retExchange64, ptr, val); \ + } + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_64(type, ptr, val) \ + { \ + type retExchange64; EA_UNUSED(retExchange64); \ + EASTL_ATOMIC_EXCHANGE_SEQ_CST_64(type, retExchange64, ptr, val); \ + } + + + #elif defined(EA_PROCESSOR_ARM64) + + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_64(type, ptr, val) \ + EASTL_ARCH_ATOMIC_ARM_STORE_64(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_64(type, ptr, val) \ + EASTL_ATOMIC_CPU_MB(); \ + EASTL_ARCH_ATOMIC_ARM_STORE_64(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_64(type, ptr, val) \ + EASTL_ATOMIC_CPU_MB(); \ + EASTL_ARCH_ATOMIC_ARM_STORE_64(type, ptr, val); \ + EASTL_ATOMIC_CPU_MB() + + + #endif + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_ARM_STORE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_thread_fence.h b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_thread_fence.h new file mode 100644 index 000000000..391c64e06 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/arm/arch_arm_thread_fence.h @@ -0,0 +1,37 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_ARM_THREAD_FENCE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_ARM_THREAD_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_THREAD_FENCE_*() +// +#if defined(EA_COMPILER_MSVC) + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_RELAXED() + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQUIRE() \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_RELEASE() \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQ_REL() \ + EASTL_ATOMIC_CPU_MB() + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_SEQ_CST() \ + EASTL_ATOMIC_CPU_MB() + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_ARM_THREAD_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86.h new file mode 100644 index 000000000..142a51431 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86.h @@ -0,0 +1,158 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +/** + * x86 && x64 Mappings + * + * Load Relaxed : MOV + * Load Acquire : MOV; COMPILER_BARRIER; + * Load Seq_Cst : MOV; COMPILER_BARRIER; + * + * Store Relaxed : MOV + * Store Release : COMPILER_BARRIER; MOV; + * Store Seq_Cst : LOCK XCHG : MOV; MFENCE; + * + * Relaxed Fence : + * Acquire Fence : COMPILER_BARRIER + * Release Fence : COMPILER_BARRIER + * Acq_Rel Fence : COMPILER_BARRIER + * Seq_Cst FENCE : MFENCE + */ + + +///////////////////////////////////////////////////////////////////////////////// + +#if (defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64) + #define EASTL_ARCH_ATOMIC_HAS_128BIT +#elif defined(EA_COMPILER_MSVC) + #if EA_PLATFORM_PTR_SIZE == 8 + #define EASTL_ARCH_ATOMIC_HAS_128BIT + #endif +#endif + +///////////////////////////////////////////////////////////////////////////////// + + +/** + * NOTE: + * + * On 32-bit x86 CPUs Intel Pentium and newer, AMD K5 and newer + * and any i586 class of x86 CPUs support only 64-bit cmpxchg + * known as cmpxchg8b. + * + * On these class of cpus we can guarantee that 64-bit loads/stores are + * also atomic by using the SSE2 movq, SSE1 movlps, or x87 fild/fstp instructions. + * + * We support all other atomic operations + * on compilers that only provide this 64-bit cmpxchg instruction + * by wrapping them around the 64-bit cmpxchg8b instruction. + */ +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_NOP_PRE_COMPUTE_DESIRED(ret, observed, val) \ + static_assert(false, "EASTL_ARCH_ATOMIC_X86_NOP_PRE_COMPUTE_DESIRED() must be implmented!"); + + #define EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET(ret, prevObserved, val) + + + #define EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, MemoryOrder, PRE_COMPUTE_DESIRED, POST_COMPUTE_RET) \ + { \ + bool cmpxchgRet; \ + EASTL_ATOMIC_LOAD_RELAXED_64(type, ret, ptr); \ + do \ + { \ + type computedDesired; \ + PRE_COMPUTE_DESIRED(computedDesired, ret, (val)); \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_CMPXCHG_STRONG_, MemoryOrder), _64)(type, cmpxchgRet, ptr, &(ret), computedDesired); \ + } while (!cmpxchgRet); \ + POST_COMPUTE_RET(ret, ret, (val)); \ + } + + +#endif + + +/** + * NOTE: + * + * 64-bit x64 CPUs support only 128-bit cmpxchg known as cmpxchg16b. + * + * We support all other atomic operations by wrapping them around + * the 128-bit cmpxchg16b instruction. + * + * 128-bit loads are only atomic by using the cmpxchg16b instruction. + * SSE 128-bit loads are not guaranteed to be atomic even though some CPUs + * make them atomic such as AMD Ryzen or Intel SandyBridge. + */ +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_NOP_PRE_COMPUTE_DESIRED(ret, observed, val) \ + static_assert(false, "EASTL_ARCH_ATOMIC_X86_NOP_PRE_COMPUTE_DESIRED() must be implmented!"); + + #define EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET(ret, prevObserved, val) + + + #define EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, MemoryOrder, PRE_COMPUTE_DESIRED, POST_COMPUTE_RET) \ + { \ + bool cmpxchgRet; \ + /* This is intentionally a non-atomic 128-bit load which may observe shearing. */ \ + /* Either we do not observe *(ptr) but then the cmpxchg will fail and the observed */ \ + /* atomic load will be returned. Or the non-atomic load got lucky and the cmpxchg succeeds */ \ + /* because the observed value equals the value in *(ptr) thus we optimistically do a non-atomic load. */ \ + ret = *(ptr); \ + do \ + { \ + type computedDesired; \ + PRE_COMPUTE_DESIRED(computedDesired, ret, (val)); \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_CMPXCHG_STRONG_, MemoryOrder), _128)(type, cmpxchgRet, ptr, &(ret), computedDesired); \ + } while (!cmpxchgRet); \ + POST_COMPUTE_RET(ret, ret, (val)); \ + } + + +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +#include "arch_x86_fetch_add.h" +#include "arch_x86_fetch_sub.h" + +#include "arch_x86_fetch_and.h" +#include "arch_x86_fetch_xor.h" +#include "arch_x86_fetch_or.h" + +#include "arch_x86_add_fetch.h" +#include "arch_x86_sub_fetch.h" + +#include "arch_x86_and_fetch.h" +#include "arch_x86_xor_fetch.h" +#include "arch_x86_or_fetch.h" + +#include "arch_x86_exchange.h" + +#include "arch_x86_cmpxchg_weak.h" +#include "arch_x86_cmpxchg_strong.h" + +#include "arch_x86_memory_barrier.h" + +#include "arch_x86_thread_fence.h" + +#include "arch_x86_load.h" +#include "arch_x86_store.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_add_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_add_fetch.h new file mode 100644 index 000000000..7b77528e9 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_add_fetch.h @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_ADD_FETCH_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_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(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) + (val)) + + #define EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) + (val)) + + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) + (val)) + + #define EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) + (val)) + + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_ADD_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_ADD_FETCH_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_ADD_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_and_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_and_fetch.h new file mode 100644 index 000000000..058316362 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_and_fetch.h @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_AND_FETCH_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_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(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) & (val)) + + #define EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) & (val)) + + + #define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) & (val)) + + #define EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) & (val)) + + + #define EASTL_ARCH_ATOMIC_AND_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_AND_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_AND_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_AND_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_AND_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_AND_FETCH_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_AND_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_strong.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_strong.h new file mode 100644 index 000000000..1968e9abd --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_strong.h @@ -0,0 +1,69 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_CMPXCHG_STRONG_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_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(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) \ + { \ + /* Compare RDX:RAX with m128. If equal, set ZF and load RCX:RBX into m128. Else, clear ZF and load m128 into RDX:RAX. */ \ + __asm__ __volatile__ ("lock; cmpxchg16b %2\n" /* cmpxchg16b sets/clears ZF */ \ + "sete %3" /* If ZF == 1, set the return value to 1 */ \ + /* Output Operands */ \ + : "=a"((EASTL_ATOMIC_TYPE_CAST(uint64_t, (expected)))[0]), "=d"((EASTL_ATOMIC_TYPE_CAST(uint64_t, (expected)))[1]), \ + "+m"(*(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(__uint128_t, (ptr)))), \ + "=rm"((ret)) \ + /* Input Operands */ \ + : "b"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(desired)))[0]), "c"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(desired)))[1]), \ + "a"((EASTL_ATOMIC_TYPE_CAST(uint64_t, (expected)))[0]), "d"((EASTL_ATOMIC_TYPE_CAST(uint64_t, (expected)))[1]) \ + /* Clobbers */ \ + : "memory", "cc"); \ + } + + + #define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_ARCH_ATOMIC_X86_CMPXCHG_STRONG_128_IMPL(type, ret, ptr, expected, desired) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_CMPXCHG_STRONG_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_weak.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_weak.h new file mode 100644 index 000000000..61a126c1d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_cmpxchg_weak.h @@ -0,0 +1,52 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_CMPXCHG_WEAK_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_CMPXCHG_WEAK_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) + + #define EASTL_ARCH_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_CMPXCHG_WEAK_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_exchange.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_exchange.h new file mode 100644 index 000000000..624d2f551 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_exchange.h @@ -0,0 +1,91 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_EXCHANGE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_EXCHANGE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_EXCHANGE_*_N(type, type ret, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_EXCHANGE_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = (val) + + + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_EXCHANGE_128(type, ret, ptr, val, MemoryOrder) \ + { \ + bool cmpxchgRet; \ + /* This is intentionally a non-atomic 128-bit load which may observe shearing. */ \ + /* Either we do not observe *(ptr) but then the cmpxchg will fail and the observed */ \ + /* atomic load will be returned. Or the non-atomic load got lucky and the cmpxchg succeeds */ \ + /* because the observed value equals the value in *(ptr) thus we optimistically do a non-atomic load. */ \ + ret = *(ptr); \ + do \ + { \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_CMPXCHG_STRONG_, MemoryOrder), _128)(type, cmpxchgRet, ptr, &(ret), val); \ + } while (!cmpxchgRet); \ + } + + + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_128(type, ret, ptr, val, RELAXED) + + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_128(type, ret, ptr, val, ACQUIRE) + + #define EASTL_ARCH_ATOMIC_EXCHANGE_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_128(type, ret, ptr, val, RELEASE) + + #define EASTL_ARCH_ATOMIC_EXCHANGE_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_128(type, ret, ptr, val, ACQ_REL) + + #define EASTL_ARCH_ATOMIC_EXCHANGE_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_EXCHANGE_128(type, ret, ptr, val, SEQ_CST) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_EXCHANGE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_add.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_add.h new file mode 100644 index 000000000..e816af9b5 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_add.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_ADD_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_ADD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_ADD_*_N(type, type ret, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) + (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) + (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_ADD_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_ADD_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_ADD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_and.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_and.h new file mode 100644 index 000000000..ff27b1a28 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_and.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_AND_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_AND_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_AND_*_N(type, type ret, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) & (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) & (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_AND_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_AND_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_AND_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_AND_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_AND_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_or.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_or.h new file mode 100644 index 000000000..8627d3a25 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_or.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_OR_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_OR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_OR_*_N(type, type ret, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) | (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) | (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_OR_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_OR_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_OR_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_OR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_OR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_sub.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_sub.h new file mode 100644 index 000000000..14b43f906 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_sub.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_SUB_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_SUB_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_SUB_*_N(type, type ret, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) - (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) - (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_SUB_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_SUB_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_SUB_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_xor.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_xor.h new file mode 100644 index 000000000..666df8bfd --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_fetch_xor.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_XOR_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_XOR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_FETCH_XOR_*_N(type, type ret, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) ^ (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) ^ (val)) + + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_FETCH_XOR_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_FETCH_XOR_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_NOP_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_FETCH_XOR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_load.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_load.h new file mode 100644 index 000000000..644a2a172 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_load.h @@ -0,0 +1,164 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_LOAD_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_LOAD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_LOAD_*_N(type, type ret, type * ptr) +// + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + /** + * NOTE: + * + * Since the cmpxchg 128-bit inline assembly does a sete in the asm to set the return boolean, + * it doesn't get dead-store removed even though we don't care about the success of the + * cmpxchg since the compiler cannot reason about what is inside asm blocks. + * Thus this variant just does the minimum required to do an atomic load. + */ +#define EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, MemoryOrder) \ + { \ + EASTL_ATOMIC_FIXED_WIDTH_TYPE_128 expected = 0; \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, expected); \ + \ + /* Compare RDX:RAX with m128. If equal, set ZF and load RCX:RBX into m128. Else, clear ZF and load m128 into RDX:RAX. */ \ + __asm__ __volatile__ ("lock; cmpxchg16b %2" /* cmpxchg16b sets/clears ZF */ \ + /* Output Operands */ \ + : "=a"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[0]), "=d"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[1]), \ + "+m"(*(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(__uint128_t, (ptr)))) \ + /* Input Operands */ \ + : "b"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[0]), "c"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[1]), \ + "a"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[0]), "d"((EASTL_ATOMIC_TYPE_CAST(uint64_t, &(ret)))[1]) \ + /* Clobbers */ \ + : "memory", "cc"); \ + } + + +#define EASTL_ARCH_ATOMIC_LOAD_RELAXED_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, RELAXED) + +#define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, ACQUIRE) + +#define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, SEQ_CST) + +#elif defined(EA_COMPILER_MSVC) + + + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1920) // >= VS2019 + + #define EASTL_ARCH_ATOMIC_X86_LOAD_N(integralType, bits, type, ret, ptr) \ + { \ + integralType retIntegral; \ + retIntegral = EA_PREPROCESSOR_JOIN(__iso_volatile_load, bits)(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr))); \ + \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, retIntegral); \ + } + + #else + + #define EASTL_ARCH_ATOMIC_X86_LOAD_N(integralType, bits, type, ret, ptr) \ + { \ + integralType retIntegral; \ + retIntegral = (*(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)))); \ + \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, retIntegral); \ + } + + #endif + + + #define EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, MemoryOrder) \ + { \ + EASTL_ATOMIC_FIXED_WIDTH_TYPE_128 expected{0, 0}; \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, expected); \ + \ + bool cmpxchgRetBool; EA_UNUSED(cmpxchgRetBool); \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_CMPXCHG_STRONG_, MemoryOrder), _128)(type, cmpxchgRetBool, ptr, &(ret), ret); \ + } + + + #define EASTL_ARCH_ATOMIC_X86_LOAD_8(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_N(__int8, 8, type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_X86_LOAD_16(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_N(__int16, 16, type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_X86_LOAD_32(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_N(__int32, 32, type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_X86_LOAD_64(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_N(__int64, 64, type, ret, ptr) + + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_8(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_8(type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_16(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_16(type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_32(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_32(type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_64(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_64(type, ret, ptr) + + #define EASTL_ARCH_ATOMIC_LOAD_RELAXED_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, RELAXED) + + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_8(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_8(type, ret, ptr); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_16(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_16(type, ret, ptr); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_32(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_32(type, ret, ptr); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_64(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_64(type, ret, ptr); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_LOAD_ACQUIRE_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, ACQUIRE) + + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_8(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_8(type, ret, ptr); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_16(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_16(type, ret, ptr); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_32(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_32(type, ret, ptr); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_64(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_64(type, ret, ptr); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_LOAD_SEQ_CST_128(type, ret, ptr) \ + EASTL_ARCH_ATOMIC_X86_LOAD_128(type, ret, ptr, SEQ_CST) + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_LOAD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_memory_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_memory_barrier.h new file mode 100644 index 000000000..7bad141f6 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_memory_barrier.h @@ -0,0 +1,104 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_MEMORY_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_MEMORY_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_MB() +// +#if defined(EA_COMPILER_MSVC) + + /** + * NOTE: + * While it makes no sense for a hardware memory barrier to not imply a compiler barrier. + * MSVC docs do not explicitly state that, so better to be safe than sorry chasing down + * hard to find bugs due to the compiler deciding to reorder things. + */ + + #if 1 + + // 4459 : declaration of 'identifier' hides global declaration + // 4456 : declaration of 'identifier' hides previous local declaration + #define EASTL_ARCH_ATOMIC_CPU_MB() \ + { \ + EA_DISABLE_VC_WARNING(4459 4456); \ + volatile long _; \ + _InterlockedExchangeAdd(&_, 0); \ + EA_RESTORE_VC_WARNING(); \ + } + + #else + + #define EASTL_ARCH_ATOMIC_CPU_MB() \ + EASTL_ATOMIC_COMPILER_BARRIER(); \ + _mm_mfence(); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #endif + +#elif defined(__clang__) || defined(EA_COMPILER_GNUC) + + /** + * NOTE: + * + * mfence orders all loads/stores to/from all memory types. + * We only care about ordinary cacheable memory so lighter weight locked instruction + * is far faster than a mfence to get a full memory barrier. + * lock; addl against the top of the stack is good because: + * distinct for every thread so prevents false sharing + * that cacheline is most likely cache hot + * + * We intentionally do it below the stack pointer to avoid false RAW register dependencies, + * in cases where the compiler reads from the stack pointer after the lock; addl instruction + * + * Accounting for Red Zones or Cachelines doesn't provide extra benefit. + */ + + #if defined(EA_PROCESSOR_X86) + + #define EASTL_ARCH_ATOMIC_CPU_MB() \ + __asm__ __volatile__ ("lock; addl $0, -4(%%esp)" ::: "memory", "cc") + + #elif defined(EA_PROCESSOR_X86_64) + + #define EASTL_ARCH_ATOMIC_CPU_MB() \ + __asm__ __volatile__ ("lock; addl $0, -8(%%rsp)" ::: "memory", "cc") + + #else + + #define EASTL_ARCH_ATOMIC_CPU_MB() \ + __asm__ __volatile__ ("mfence" ::: "memory") + + #endif + + +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_WMB() +// +#define EASTL_ARCH_ATOMIC_CPU_WMB() \ + EASTL_ATOMIC_COMPILER_BARRIER() + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_CPU_RMB() +// +#define EASTL_ARCH_ATOMIC_CPU_RMB() \ + EASTL_ATOMIC_COMPILER_BARRIER() + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_MEMORY_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_or_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_or_fetch.h new file mode 100644 index 000000000..42f7d61fc --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_or_fetch.h @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_OR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_OR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_OR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) | (val)) + + #define EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) | (val)) + + + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) | (val)) + + #define EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) | (val)) + + + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_OR_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_OR_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_OR_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_OR_FETCH_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_OR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_store.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_store.h new file mode 100644 index 000000000..31655c3b0 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_store.h @@ -0,0 +1,171 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_STORE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_STORE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_STORE_*_N(type, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) + + + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1920) // >= VS2019 + + #define EASTL_ARCH_ATOMIC_X86_STORE_N(integralType, bits, type, ptr, val) \ + EA_PREPROCESSOR_JOIN(__iso_volatile_store, bits)(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), EASTL_ATOMIC_TYPE_PUN_CAST(integralType, (val))) + + #else + + #define EASTL_ARCH_ATOMIC_X86_STORE_N(integralType, bits, type, ptr, val) \ + { \ + integralType valIntegral = EASTL_ATOMIC_TYPE_PUN_CAST(integralType, (val)); \ + \ + (*(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)))) = valIntegral; \ + } + + #endif + + + #define EASTL_ARCH_ATOMIC_X86_STORE_128(type, ptr, val, MemoryOrder) \ + { \ + type exchange128; EA_UNUSED(exchange128); \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_EXCHANGE_, MemoryOrder), _128)(type, exchange128, ptr, val); \ + } + + + #define EASTL_ARCH_ATOMIC_X86_STORE_8(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_N(__int8, 8, type, ptr, val) + + #define EASTL_ARCH_ATOMIC_X86_STORE_16(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_N(__int16, 16, type, ptr, val) + + #define EASTL_ARCH_ATOMIC_X86_STORE_32(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_N(__int32, 32, type, ptr, val) + + #define EASTL_ARCH_ATOMIC_X86_STORE_64(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_N(__int64, 64, type, ptr, val) + + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_8(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_8(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_16(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_16(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_32(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_32(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_64(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_64(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_128(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_128(type, ptr, val, RELAXED) + + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_8(type, ptr, val) \ + EASTL_ATOMIC_COMPILER_BARRIER(); \ + EASTL_ARCH_ATOMIC_X86_STORE_8(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_16(type, ptr, val) \ + EASTL_ATOMIC_COMPILER_BARRIER(); \ + EASTL_ARCH_ATOMIC_X86_STORE_16(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_32(type, ptr, val) \ + EASTL_ATOMIC_COMPILER_BARRIER(); \ + EASTL_ARCH_ATOMIC_X86_STORE_32(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_64(type, ptr, val) \ + EASTL_ATOMIC_COMPILER_BARRIER(); \ + EASTL_ARCH_ATOMIC_X86_STORE_64(type, ptr, val) + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_128(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_128(type, ptr, val, RELEASE) + + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_8(type, ptr, val) \ + { \ + type exchange8; EA_UNUSED(exchange8); \ + EASTL_ATOMIC_EXCHANGE_SEQ_CST_8(type, exchange8, ptr, val); \ + } + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_16(type, ptr, val) \ + { \ + type exchange16; EA_UNUSED(exchange16); \ + EASTL_ATOMIC_EXCHANGE_SEQ_CST_16(type, exchange16, ptr, val); \ + } + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_32(type, ptr, val) \ + { \ + type exchange32; EA_UNUSED(exchange32); \ + EASTL_ATOMIC_EXCHANGE_SEQ_CST_32(type, exchange32, ptr, val); \ + } + + + /** + * NOTE: + * + * Since 64-bit exchange is wrapped around a cmpxchg8b on 32-bit x86, it is + * faster to just do a mov; mfence. + */ + #if defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_64(type, ptr, val) \ + EASTL_ATOMIC_COMPILER_BARRIER(); \ + EASTL_ARCH_ATOMIC_X86_STORE_64(type, ptr, val); \ + EASTL_ATOMIC_CPU_MB() + + + #elif defined(EA_PROCESSOR_X86_64) + + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_64(type, ptr, val) \ + { \ + type exchange64; EA_UNUSED(exchange64); \ + EASTL_ATOMIC_EXCHANGE_SEQ_CST_64(type, exchange64, ptr, val); \ + } + + + #endif + + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_128(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_128(type, ptr, val, SEQ_CST) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_STORE_128(type, ptr, val, MemoryOrder) \ + { \ + type exchange128; EA_UNUSED(exchange128); \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_EXCHANGE_, MemoryOrder), _128)(type, exchange128, ptr, val); \ + } + + + #define EASTL_ARCH_ATOMIC_STORE_RELAXED_128(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_128(type, ptr, val, RELAXED) + + #define EASTL_ARCH_ATOMIC_STORE_RELEASE_128(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_128(type, ptr, val, RELEASE) + + #define EASTL_ARCH_ATOMIC_STORE_SEQ_CST_128(type, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_STORE_128(type, ptr, val, SEQ_CST) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_STORE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_sub_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_sub_fetch.h new file mode 100644 index 000000000..a1d093298 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_sub_fetch.h @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_SUB_FETCH_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_SUB_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_SUB_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) - (val)) + + #define EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) - (val)) + + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) - (val)) + + #define EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) - (val)) + + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_SUB_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_SUB_FETCH_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_SUB_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_thread_fence.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_thread_fence.h new file mode 100644 index 000000000..183c7f3af --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_thread_fence.h @@ -0,0 +1,42 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_THREAD_FENCE_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_THREAD_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_THREAD_FENCE_*() +// +#if defined(EA_COMPILER_MSVC) + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_RELAXED() + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQUIRE() \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_RELEASE() \ + EASTL_ATOMIC_COMPILER_BARRIER() + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_ACQ_REL() \ + EASTL_ATOMIC_COMPILER_BARRIER() + +#endif + + +#if defined(EA_COMPILER_MSVC) || defined(__clang__) || defined(EA_COMPILER_GNUC) + + #define EASTL_ARCH_ATOMIC_THREAD_FENCE_SEQ_CST() \ + EASTL_ATOMIC_CPU_MB() + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_THREAD_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_xor_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_xor_fetch.h new file mode 100644 index 000000000..a5b62c3b1 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/arch/x86/arch_x86_xor_fetch.h @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ARCH_X86_XOR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_ARCH_X86_XOR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ARCH_ATOMIC_XOR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + + + #define EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) ^ (val)) + + #define EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) ^ (val)) + + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_64_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + +#endif + + +#if ((defined(__clang__) || defined(EA_COMPILER_GNUC)) && defined(EA_PROCESSOR_X86_64)) + + + #define EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED(ret, observed, val) \ + ret = ((observed) ^ (val)) + + #define EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET(ret, prevObserved, val) \ + ret = ((prevObserved) ^ (val)) + + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELAXED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQUIRE, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, RELEASE, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, ACQ_REL, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + #define EASTL_ARCH_ATOMIC_XOR_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ARCH_ATOMIC_X86_OP_128_IMPL(type, ret, ptr, val, SEQ_CST, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_PRE_COMPUTE_DESIRED, \ + EASTL_ARCH_ATOMIC_X86_XOR_FETCH_POST_COMPUTE_RET) + + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ARCH_X86_XOR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic.h b/lib/EASTL/include/EASTL/internal/atomic/atomic.h new file mode 100644 index 000000000..eb27d2d99 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic.h @@ -0,0 +1,252 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_H +#define EASTL_ATOMIC_INTERNAL_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#include +#include +#include +#include + +#include "atomic_macros.h" +#include "atomic_casts.h" + +#include "atomic_memory_order.h" +#include "atomic_asserts.h" + +#include "atomic_size_aligned.h" +#include "atomic_base_width.h" + +#include "atomic_integral.h" + +#include "atomic_pointer.h" + + +///////////////////////////////////////////////////////////////////////////////// + + +/** + * NOTE: + * + * All of the actual implementation is done via the ATOMIC_MACROS in the compiler or arch sub folders. + * The C++ code is merely boilerplate around these macros that actually implement the atomic operations. + * The C++ boilerplate is also hidden behind macros. + * This may seem more complicated but this is all meant to reduce copy-pasting and to ensure all operations + * all end up going down to one macro that does the actual implementation. + * The reduced code duplication makes it easier to verify the implementation and reason about it. + * Ensures we do not have to re-implement the same code for compilers that do not support generic builtins such as MSVC. + * Ensures for compilers that have separate intrinsics for different widths, that C++ boilerplate isn't copy-pasted leading to programmer errors. + * Ensures if we ever have to implement a new platform, only the low-level leaf macros have to be implemented, everything else will be generated for you. + */ + + +#include "atomic_push_compiler_options.h" + + +namespace eastl +{ + + +namespace internal +{ + + + template + struct is_atomic_lockfree_size + { + static EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR_OR_CONST bool value = false || + #if defined(EASTL_ATOMIC_HAS_8BIT) + sizeof(T) == 1 || + #endif + #if defined(EASTL_ATOMIC_HAS_16BIT) + sizeof(T) == 2 || + #endif + #if defined(EASTL_ATOMIC_HAS_32BIT) + sizeof(T) == 4 || + #endif + #if defined(EASTL_ATOMIC_HAS_64BIT) + sizeof(T) == 8 || + #endif + #if defined(EASTL_ATOMIC_HAS_128BIT) + sizeof(T) == 16 || + #endif + false; + }; + + + template + struct is_user_type_suitable_for_primary_template + { + static EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR_OR_CONST bool value = eastl::internal::is_atomic_lockfree_size::value; + }; + + + template + using select_atomic_inherit_0 = typename eastl::conditional || eastl::internal::is_user_type_suitable_for_primary_template::value, + eastl::internal::atomic_base_width, /* True */ + eastl::internal::atomic_invalid_type /* False */ + >::type; + + template + using select_atomic_inherit = select_atomic_inherit_0; + + +} // namespace internal + + +#define EASTL_ATOMIC_CLASS_IMPL(type, base, valueType, differenceType) \ + private: \ + \ + EASTL_ATOMIC_STATIC_ASSERT_TYPE(type); \ + \ + using Base = base; \ + \ + public: \ + \ + typedef valueType value_type; \ + typedef differenceType difference_type; \ + \ + public: \ + \ + static EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR_OR_CONST bool is_always_lock_free = eastl::internal::is_atomic_lockfree_size::value; \ + \ + public: /* deleted ctors && assignment operators */ \ + \ + atomic(const atomic&) EA_NOEXCEPT = delete; \ + \ + atomic& operator=(const atomic&) EA_NOEXCEPT = delete; \ + atomic& operator=(const atomic&) volatile EA_NOEXCEPT = delete; \ + \ + public: /* ctors */ \ + \ + EA_CONSTEXPR atomic(type desired) EA_NOEXCEPT \ + : Base{ desired } \ + { \ + } \ + \ + EA_CONSTEXPR atomic() EA_NOEXCEPT_IF(eastl::is_nothrow_default_constructible_v) = default; \ + \ + public: \ + \ + bool is_lock_free() const EA_NOEXCEPT \ + { \ + return eastl::internal::is_atomic_lockfree_size::value; \ + } \ + \ + bool is_lock_free() const volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(type); \ + return false; \ + } + + +#define EASTL_ATOMIC_USING_ATOMIC_BASE(type) \ + public: \ + \ + using Base::operator=; \ + using Base::store; \ + using Base::load; \ + using Base::exchange; \ + using Base::compare_exchange_weak; \ + using Base::compare_exchange_strong; \ + \ + public: \ + \ + operator type() const volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } \ + \ + operator type() const EA_NOEXCEPT \ + { \ + return load(eastl::memory_order_seq_cst); \ + } + + +#define EASTL_ATOMIC_USING_ATOMIC_INTEGRAL() \ + public: \ + \ + using Base::fetch_add; \ + using Base::add_fetch; \ + \ + using Base::fetch_sub; \ + using Base::sub_fetch; \ + \ + using Base::fetch_and; \ + using Base::and_fetch; \ + \ + using Base::fetch_or; \ + using Base::or_fetch; \ + \ + using Base::fetch_xor; \ + using Base::xor_fetch; \ + \ + using Base::operator++; \ + using Base::operator--; \ + using Base::operator+=; \ + using Base::operator-=; \ + using Base::operator&=; \ + using Base::operator|=; \ + using Base::operator^=; + + +#define EASTL_ATOMIC_USING_ATOMIC_POINTER() \ + public: \ + \ + using Base::fetch_add; \ + using Base::add_fetch; \ + using Base::fetch_sub; \ + using Base::sub_fetch; \ + \ + using Base::operator++; \ + using Base::operator--; \ + using Base::operator+=; \ + using Base::operator-=; + + +template +struct atomic : protected eastl::internal::select_atomic_inherit +{ + EASTL_ATOMIC_CLASS_IMPL(T, eastl::internal::select_atomic_inherit, T, T) + + EASTL_ATOMIC_USING_ATOMIC_BASE(T) +}; + + +template +struct atomic && !eastl::is_same_v>> : protected eastl::internal::atomic_integral_width +{ + EASTL_ATOMIC_CLASS_IMPL(T, eastl::internal::atomic_integral_width, T, T) + + EASTL_ATOMIC_USING_ATOMIC_BASE(T) + + EASTL_ATOMIC_USING_ATOMIC_INTEGRAL() +}; + + +template +struct atomic : protected eastl::internal::atomic_pointer_width +{ + EASTL_ATOMIC_CLASS_IMPL(T*, eastl::internal::atomic_pointer_width, T*, ptrdiff_t) + + EASTL_ATOMIC_USING_ATOMIC_BASE(T*) + + EASTL_ATOMIC_USING_ATOMIC_POINTER() +}; + + +} // namespace eastl + + +#include "atomic_pop_compiler_options.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_asserts.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_asserts.h new file mode 100644 index 000000000..9324a4797 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_asserts.h @@ -0,0 +1,75 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_STATIC_ASSERTS_H +#define EASTL_ATOMIC_INTERNAL_STATIC_ASSERTS_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(type) \ + static_assert(!eastl::is_same::value, "eastl::atomic : volatile eastl::atomic is not what you expect! Read the docs in EASTL/atomic.h! Use the memory orders to access the atomic object!"); + +#define EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(type) \ + static_assert(!eastl::is_same::value, "eastl::atomic : invalid memory order for the given operation!"); + +#define EASTL_ATOMIC_STATIC_ASSERT_TYPE(type) \ + /* User Provided T must not be cv qualified */ \ + static_assert(!eastl::is_const::value, "eastl::atomic : Template Typename T cannot be const!"); \ + static_assert(!eastl::is_volatile::value, "eastl::atomic : Template Typename T cannot be volatile! Use the memory orders to access the underlying type for the guarantees you need."); \ + /* T must satisfy StandardLayoutType */ \ + static_assert(eastl::is_standard_layout::value, "eastl::atomic : Must have standard layout!"); \ + /* T must be TriviallyCopyable but it does not have to be TriviallyConstructible */ \ + static_assert(eastl::is_trivially_copyable::value, "eastl::atomci : Template Typename T must be trivially copyable!"); \ + static_assert(eastl::is_copy_constructible::value, "eastl::atomic : Template Typename T must be copy constructible!"); \ + static_assert(eastl::is_move_constructible::value, "eastl::atomic : Template Typename T must be move constructible!"); \ + static_assert(eastl::is_copy_assignable::value, "eastl::atomic : Template Typename T must be copy assignable!"); \ + static_assert(eastl::is_move_assignable::value, "eastl::atomic : Template Typename T must be move assignable!"); \ + static_assert(eastl::is_trivially_destructible::value, "eastl::atomic : Must be trivially destructible!"); \ + static_assert(eastl::internal::is_atomic_lockfree_size::value, "eastl::atomic : Template Typename T must be a lockfree size!"); + +#define EASTL_ATOMIC_STATIC_ASSERT_TYPE_IS_OBJECT(type) \ + static_assert(eastl::is_object::value, "eastl::atomic : Template Typename T must be an object type!"); + +#define EASTL_ATOMIC_ASSERT_ALIGNED(alignment) \ + EASTL_ASSERT((alignment & (alignment - 1)) == 0); \ + EASTL_ASSERT((reinterpret_cast(this) & (alignment - 1)) == 0) + + +namespace eastl +{ + + +namespace internal +{ + + + template + struct atomic_invalid_type + { + /** + * class Test { int i; int j; int k; }; sizeof(Test) == 96 bits + * + * std::atomic allows non-primitive types to be used for the template type. + * This causes the api to degrade to locking for types that cannot fit into the lockfree size + * of the target platform such as std::atomic leading to performance traps. + * + * If this static_assert() fired, this means your template type T is larger than any atomic instruction + * supported on the given platform. + */ + + static_assert(!eastl::is_same::value, "eastl::atomic : invalid template type T!"); + }; + + +} // namespace internal + + +} // namespace eastl + + +#endif /* EASTL_ATOMIC_INTERNAL_STATIC_ASSERTS_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_base_width.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_base_width.h new file mode 100644 index 000000000..ca476182d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_base_width.h @@ -0,0 +1,346 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_BASE_WIDTH_H +#define EASTL_ATOMIC_INTERNAL_BASE_WIDTH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#include "atomic_push_compiler_options.h" + + +namespace eastl +{ + + +namespace internal +{ + + + template + struct atomic_base_width; + + /** + * NOTE: + * + * T does not have to be trivially default constructible but it still + * has to be a trivially copyable type for the primary atomic template. + * Thus we must type pun into whatever storage type of the given fixed width + * the platform designates. This ensures T does not have to be trivially constructible. + */ + +#define EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits) \ + EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_FIXED_WIDTH_TYPE_, bits) + + +#define EASTL_ATOMIC_STORE_FUNC_IMPL(op, bits) \ + EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits) fixedWidthDesired = EASTL_ATOMIC_TYPE_PUN_CAST(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), desired); \ + EA_PREPROCESSOR_JOIN(op, bits)(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), \ + EASTL_ATOMIC_TYPE_CAST(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), this->GetAtomicAddress()), \ + fixedWidthDesired) + + +#define EASTL_ATOMIC_LOAD_FUNC_IMPL(op, bits) \ + EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits) retVal; \ + EA_PREPROCESSOR_JOIN(op, bits)(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), \ + retVal, \ + EASTL_ATOMIC_TYPE_CAST(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), this->GetAtomicAddress())); \ + return EASTL_ATOMIC_TYPE_PUN_CAST(T, retVal); + + +#define EASTL_ATOMIC_EXCHANGE_FUNC_IMPL(op, bits) \ + EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits) retVal; \ + EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits) fixedWidthDesired = EASTL_ATOMIC_TYPE_PUN_CAST(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), desired); \ + EA_PREPROCESSOR_JOIN(op, bits)(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), \ + retVal, \ + EASTL_ATOMIC_TYPE_CAST(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), this->GetAtomicAddress()), \ + fixedWidthDesired); \ + return EASTL_ATOMIC_TYPE_PUN_CAST(T, retVal); + + +#define EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(op, bits) \ + bool retVal; \ + EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits) fixedWidthDesired = EASTL_ATOMIC_TYPE_PUN_CAST(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), desired); \ + EA_PREPROCESSOR_JOIN(op, bits)(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), \ + retVal, \ + EASTL_ATOMIC_TYPE_CAST(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), this->GetAtomicAddress()), \ + EASTL_ATOMIC_TYPE_CAST(EASTL_ATOMIC_BASE_FIXED_WIDTH_TYPE(bits), &expected), \ + fixedWidthDesired); \ + return retVal; + + +#define EASTL_ATOMIC_BASE_OP_JOIN(op, Order) \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_, op), Order) + + +#define EASTL_ATOMIC_BASE_CMPXCHG_FUNCS_IMPL(funcName, cmpxchgOp, bits) \ + using Base::funcName; \ + \ + bool funcName(T& expected, T desired) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _SEQ_CST_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _RELAXED_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_acquire_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _ACQUIRE_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_release_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _RELEASE_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_acq_rel_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _ACQ_REL_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_seq_cst_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _SEQ_CST_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_relaxed_s, \ + eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _RELAXED_RELAXED_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_acquire_s, \ + eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _ACQUIRE_RELAXED_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_acquire_s, \ + eastl::internal::memory_order_acquire_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _ACQUIRE_ACQUIRE_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_release_s, \ + eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _RELEASE_RELAXED_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_acq_rel_s, \ + eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _ACQ_REL_RELAXED_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_acq_rel_s, \ + eastl::internal::memory_order_acquire_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _ACQ_REL_ACQUIRE_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_seq_cst_s, \ + eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _SEQ_CST_RELAXED_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_seq_cst_s, \ + eastl::internal::memory_order_acquire_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _SEQ_CST_ACQUIRE_), bits); \ + } \ + \ + bool funcName(T& expected, T desired, \ + eastl::internal::memory_order_seq_cst_s, \ + eastl::internal::memory_order_seq_cst_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_CMPXCHG_FUNC_IMPL(EASTL_ATOMIC_BASE_OP_JOIN(cmpxchgOp, _SEQ_CST_SEQ_CST_), bits); \ + } + +#define EASTL_ATOMIC_BASE_CMPXCHG_WEAK_FUNCS_IMPL(bits) \ + EASTL_ATOMIC_BASE_CMPXCHG_FUNCS_IMPL(compare_exchange_weak, CMPXCHG_WEAK, bits) + +#define EASTL_ATOMIC_BASE_CMPXCHG_STRONG_FUNCS_IMPL(bits) \ + EASTL_ATOMIC_BASE_CMPXCHG_FUNCS_IMPL(compare_exchange_strong, CMPXCHG_STRONG, bits) + + +#define EASTL_ATOMIC_BASE_WIDTH_SPECIALIZE(bytes, bits) \ + template \ + struct atomic_base_width : public atomic_size_aligned \ + { \ + private: \ + \ + static_assert(EA_ALIGN_OF(atomic_size_aligned) == bytes, "eastl::atomic must be sizeof(T) aligned!"); \ + static_assert(EA_ALIGN_OF(atomic_size_aligned) == sizeof(T), "eastl::atomic must be sizeof(T) aligned!"); \ + using Base = atomic_size_aligned; \ + \ + public: /* ctors */ \ + \ + EA_CONSTEXPR atomic_base_width(T desired) EA_NOEXCEPT \ + : Base{ desired } \ + { \ + } \ + \ + EA_CONSTEXPR atomic_base_width() EA_NOEXCEPT_IF(eastl::is_nothrow_default_constructible_v) = default; \ + \ + atomic_base_width(const atomic_base_width&) EA_NOEXCEPT = delete; \ + \ + public: /* store */ \ + \ + using Base::store; \ + \ + void store(T desired) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STORE_FUNC_IMPL(EASTL_ATOMIC_STORE_SEQ_CST_, bits); \ + } \ + \ + void store(T desired, eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STORE_FUNC_IMPL(EASTL_ATOMIC_STORE_RELAXED_, bits); \ + } \ + \ + void store(T desired, eastl::internal::memory_order_release_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STORE_FUNC_IMPL(EASTL_ATOMIC_STORE_RELEASE_, bits); \ + } \ + \ + void store(T desired, eastl::internal::memory_order_seq_cst_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STORE_FUNC_IMPL(EASTL_ATOMIC_STORE_SEQ_CST_, bits); \ + } \ + \ + public: /* load */ \ + \ + using Base::load; \ + \ + T load() const EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_LOAD_FUNC_IMPL(EASTL_ATOMIC_LOAD_SEQ_CST_, bits); \ + } \ + \ + T load(eastl::internal::memory_order_relaxed_s) const EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_LOAD_FUNC_IMPL(EASTL_ATOMIC_LOAD_RELAXED_, bits); \ + } \ + \ + T load(eastl::internal::memory_order_acquire_s) const EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_LOAD_FUNC_IMPL(EASTL_ATOMIC_LOAD_ACQUIRE_, bits); \ + } \ + \ + T load(eastl::internal::memory_order_seq_cst_s) const EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_LOAD_FUNC_IMPL(EASTL_ATOMIC_LOAD_SEQ_CST_, bits); \ + } \ + \ + public: /* exchange */ \ + \ + using Base::exchange; \ + \ + T exchange(T desired) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_EXCHANGE_FUNC_IMPL(EASTL_ATOMIC_EXCHANGE_SEQ_CST_, bits); \ + } \ + \ + T exchange(T desired, eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_EXCHANGE_FUNC_IMPL(EASTL_ATOMIC_EXCHANGE_RELAXED_, bits); \ + } \ + \ + T exchange(T desired, eastl::internal::memory_order_acquire_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_EXCHANGE_FUNC_IMPL(EASTL_ATOMIC_EXCHANGE_ACQUIRE_, bits); \ + } \ + \ + T exchange(T desired, eastl::internal::memory_order_release_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_EXCHANGE_FUNC_IMPL(EASTL_ATOMIC_EXCHANGE_RELEASE_, bits); \ + } \ + \ + T exchange(T desired, eastl::internal::memory_order_acq_rel_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_EXCHANGE_FUNC_IMPL(EASTL_ATOMIC_EXCHANGE_ACQ_REL_, bits); \ + } \ + \ + T exchange(T desired, eastl::internal::memory_order_seq_cst_s) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_EXCHANGE_FUNC_IMPL(EASTL_ATOMIC_EXCHANGE_SEQ_CST_, bits); \ + } \ + \ + public: /* compare_exchange_weak */ \ + \ + EASTL_ATOMIC_BASE_CMPXCHG_WEAK_FUNCS_IMPL(bits) \ + \ + public: /* compare_exchange_strong */ \ + \ + EASTL_ATOMIC_BASE_CMPXCHG_STRONG_FUNCS_IMPL(bits) \ + \ + public: /* assignment operator */ \ + \ + using Base::operator=; \ + \ + T operator=(T desired) EA_NOEXCEPT \ + { \ + store(desired, eastl::memory_order_seq_cst); \ + return desired; \ + } \ + \ + atomic_base_width& operator=(const atomic_base_width&) EA_NOEXCEPT = delete; \ + atomic_base_width& operator=(const atomic_base_width&) volatile EA_NOEXCEPT = delete; \ + \ + }; + + +#if defined(EASTL_ATOMIC_HAS_8BIT) + EASTL_ATOMIC_BASE_WIDTH_SPECIALIZE(1, 8) +#endif + +#if defined(EASTL_ATOMIC_HAS_16BIT) + EASTL_ATOMIC_BASE_WIDTH_SPECIALIZE(2, 16) +#endif + +#if defined(EASTL_ATOMIC_HAS_32BIT) + EASTL_ATOMIC_BASE_WIDTH_SPECIALIZE(4, 32) +#endif + +#if defined(EASTL_ATOMIC_HAS_64BIT) + EASTL_ATOMIC_BASE_WIDTH_SPECIALIZE(8, 64) +#endif + +#if defined(EASTL_ATOMIC_HAS_128BIT) + EASTL_ATOMIC_BASE_WIDTH_SPECIALIZE(16, 128) +#endif + + +} // namespace internal + + +} // namespace eastl + + +#include "atomic_pop_compiler_options.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_BASE_WIDTH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_casts.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_casts.h new file mode 100644 index 000000000..54b9ed27f --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_casts.h @@ -0,0 +1,190 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_CASTS_H +#define EASTL_ATOMIC_INTERNAL_CASTS_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#include + + +#include + + +namespace eastl +{ + + +namespace internal +{ + + +template +EASTL_FORCE_INLINE volatile T* AtomicVolatileCast(T* ptr) EA_NOEXCEPT +{ + static_assert(!eastl::is_volatile::value, "eastl::atomic : pointer must not be volatile, the pointed to type must be volatile!"); + static_assert(eastl::is_volatile::value, "eastl::atomic : the pointed to type must be volatile!"); + + return reinterpret_cast(ptr); +} + + +/** + * NOTE: + * + * Some compiler intrinsics do not operate on pointer types thus + * doing atomic operations on pointers must be casted to the suitable + * sized unsigned integral type. + * + * Some compiler intrinsics aren't generics and thus structs must also + * be casted to the appropriate sized unsigned integral type. + * + * Atomic operations on an int* might have to be casted to a uint64_t on + * a platform with 8-byte pointers as an example. + * + * Also doing an atomic operation on a struct, we must ensure that we observe + * the whole struct as one atomic unit with no shearing between the members. + * A load of a struct with two uint32_t members must be one uint64_t load, + * not two separate uint32_t loads, thus casted to the suitable sized + * unsigned integral type. + */ +template +EASTL_FORCE_INLINE volatile Integral* AtomicVolatileIntegralCast(T* ptr) EA_NOEXCEPT +{ + static_assert(!eastl::is_volatile::value, "eastl::atomic : pointer must not be volatile, the pointed to type must be volatile!"); + static_assert(eastl::is_volatile::value, "eastl::atomic : the pointed to type must be volatile!"); + static_assert(eastl::is_integral::value, "eastl::atomic : Integral cast must cast to an Integral type!"); + static_assert(sizeof(Integral) == sizeof(T), "eastl::atomic : Integral and T must be same size for casting!"); + + return reinterpret_cast(ptr); +} + +template +EASTL_FORCE_INLINE Integral* AtomicIntegralCast(T* ptr) EA_NOEXCEPT +{ + static_assert(eastl::is_integral::value, "eastl::atomic : Integral cast must cast to an Integral type!"); + static_assert(sizeof(Integral) == sizeof(T), "eastl::atomic : Integral and T must be same size for casting!"); + + return reinterpret_cast(ptr); +} + + +/** + * NOTE: + * + * These casts are meant to be used with unions or structs of larger types that must be casted + * down to the smaller integral types. Like with 128-bit atomics and msvc intrinsics. + * + * struct Foo128 { __int64 array[2]; }; can be casted to a __int64* + * since a poiter to Foo128 is a pointer to the first member. + */ +template +EASTL_FORCE_INLINE volatile ToType* AtomicVolatileTypeCast(FromType* ptr) EA_NOEXCEPT +{ + static_assert(!eastl::is_volatile::value, "eastl::atomic : pointer must not be volatile, the pointed to type must be volatile!"); + static_assert(eastl::is_volatile::value, "eastl::atomic : the pointed to type must be volatile!"); + + return reinterpret_cast(ptr); +} + +template +EASTL_FORCE_INLINE ToType* AtomicTypeCast(FromType* ptr) EA_NOEXCEPT +{ + return reinterpret_cast(ptr); +} + + +/** + * NOTE: + * + * This is a compiler guaranteed safe type punning. + * This is useful when dealing with user defined structs. + * struct Test { uint32_t; unint32_t; }; + * + * Example: + * uint64_t atomicLoad = *((volatile uint64_t*)&Test); + * Test load = AtomicTypePunCast(atomicLoad); + * + * uint64_t comparand = AtomicTypePunCast(Test); + * cmpxchg(&Test, comparand, desired); + * + * This can be implemented in many different ways depending on the compiler such + * as thru a union, memcpy, reinterpret_cast(atomicLoad), etc. + */ +template , int> = 0> +EASTL_FORCE_INLINE Pun AtomicTypePunCast(const T& fromType) EA_NOEXCEPT +{ + static_assert(sizeof(Pun) == sizeof(T), "eastl::atomic : Pun and T must be the same size for type punning!"); + + /** + * aligned_storage ensures we can TypePun objects that aren't trivially default constructible + * but still trivially copyable. + */ + typename eastl::aligned_storage::type ret; + memcpy(eastl::addressof(ret), eastl::addressof(fromType), sizeof(Pun)); + return reinterpret_cast(ret); +} + +template , int> = 0> +EASTL_FORCE_INLINE Pun AtomicTypePunCast(const T& fromType) EA_NOEXCEPT +{ + return fromType; +} + + +template +EASTL_FORCE_INLINE T AtomicNegateOperand(T val) EA_NOEXCEPT +{ + static_assert(eastl::is_integral::value, "eastl::atomic : Integral Negation must be an Integral type!"); + static_assert(!eastl::is_volatile::value, "eastl::atomic : T must not be volatile!"); + + return static_cast(0U - static_cast>(val)); +} + +EASTL_FORCE_INLINE ptrdiff_t AtomicNegateOperand(ptrdiff_t val) EA_NOEXCEPT +{ + return -val; +} + + +} // namespace internal + + +} // namespace eastl + + +/** + * NOTE: + * + * These macros are meant to prevent inclusion hell. + * Also so that it fits with the style of the rest of the atomic macro implementation. + */ +#define EASTL_ATOMIC_VOLATILE_CAST(ptr) \ + eastl::internal::AtomicVolatileCast((ptr)) + +#define EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(IntegralType, ptr) \ + eastl::internal::AtomicVolatileIntegralCast((ptr)) + +#define EASTL_ATOMIC_INTEGRAL_CAST(IntegralType, ptr) \ + eastl::internal::AtomicIntegralCast((ptr)) + +#define EASTL_ATOMIC_VOLATILE_TYPE_CAST(ToType, ptr) \ + eastl::internal::AtomicVolatileTypeCast((ptr)) + +#define EASTL_ATOMIC_TYPE_CAST(ToType, ptr) \ + eastl::internal::AtomicTypeCast((ptr)) + +#define EASTL_ATOMIC_TYPE_PUN_CAST(PunType, fromType) \ + eastl::internal::AtomicTypePunCast((fromType)) + +#define EASTL_ATOMIC_NEGATE_OPERAND(val) \ + eastl::internal::AtomicNegateOperand((val)) + + +#endif /* EASTL_ATOMIC_INTERNAL_CASTS_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_flag.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_flag.h new file mode 100644 index 000000000..eed448aee --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_flag.h @@ -0,0 +1,170 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNA_ATOMIC_FLAG_H +#define EASTL_ATOMIC_INTERNA_ATOMIC_FLAG_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#include "atomic_push_compiler_options.h" + + +namespace eastl +{ + + +class atomic_flag +{ +public: /* ctors */ + + EA_CONSTEXPR atomic_flag(bool desired) EA_NOEXCEPT + : mFlag{ desired } + { + } + + EA_CONSTEXPR atomic_flag() EA_NOEXCEPT + : mFlag{ false } + { + } + +public: /* deleted ctors && assignment operators */ + + atomic_flag(const atomic_flag&) EA_NOEXCEPT = delete; + + atomic_flag& operator=(const atomic_flag&) EA_NOEXCEPT = delete; + atomic_flag& operator=(const atomic_flag&) volatile EA_NOEXCEPT = delete; + +public: /* clear */ + + template + void clear(Order /*order*/) volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(Order); + } + + template + void clear(Order /*order*/) EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(Order); + } + + void clear(eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT + { + mFlag.store(false, eastl::memory_order_relaxed); + } + + void clear(eastl::internal::memory_order_release_s) EA_NOEXCEPT + { + mFlag.store(false, eastl::memory_order_release); + } + + void clear(eastl::internal::memory_order_seq_cst_s) EA_NOEXCEPT + { + mFlag.store(false, eastl::memory_order_seq_cst); + } + + void clear() EA_NOEXCEPT + { + mFlag.store(false, eastl::memory_order_seq_cst); + } + +public: /* test_and_set */ + + template + bool test_and_set(Order /*order*/) volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(Order); + return false; + } + + template + bool test_and_set(Order /*order*/) EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(Order); + return false; + } + + bool test_and_set(eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT + { + return mFlag.exchange(true, eastl::memory_order_relaxed); + } + + bool test_and_set(eastl::internal::memory_order_acquire_s) EA_NOEXCEPT + { + return mFlag.exchange(true, eastl::memory_order_acquire); + } + + bool test_and_set(eastl::internal::memory_order_release_s) EA_NOEXCEPT + { + return mFlag.exchange(true, eastl::memory_order_release); + } + + bool test_and_set(eastl::internal::memory_order_acq_rel_s) EA_NOEXCEPT + { + return mFlag.exchange(true, eastl::memory_order_acq_rel); + } + + bool test_and_set(eastl::internal::memory_order_seq_cst_s) EA_NOEXCEPT + { + return mFlag.exchange(true, eastl::memory_order_seq_cst); + } + + bool test_and_set() EA_NOEXCEPT + { + return mFlag.exchange(true, eastl::memory_order_seq_cst); + } + +public: /* test */ + + template + bool test(Order /*order*/) const volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(Order); + return false; + } + + template + bool test(Order /*order*/) const EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(Order); + return false; + } + + bool test(eastl::internal::memory_order_relaxed_s) const EA_NOEXCEPT + { + return mFlag.load(eastl::memory_order_relaxed); + } + + bool test(eastl::internal::memory_order_acquire_s) const EA_NOEXCEPT + { + return mFlag.load(eastl::memory_order_acquire); + } + + bool test(eastl::internal::memory_order_seq_cst_s) const EA_NOEXCEPT + { + return mFlag.load(eastl::memory_order_seq_cst); + } + + bool test() const EA_NOEXCEPT + { + return mFlag.load(eastl::memory_order_seq_cst); + } + +private: + + eastl::atomic mFlag; +}; + + +} // namespace eastl + + +#include "atomic_pop_compiler_options.h" + + +#endif /* EASTL_ATOMIC_INTERNA_ATOMIC_FLAG_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_flag_standalone.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_flag_standalone.h new file mode 100644 index 000000000..b5284bed9 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_flag_standalone.h @@ -0,0 +1,69 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_FLAG_STANDALONE_H +#define EASTL_ATOMIC_INTERNAL_FLAG_STANDALONE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +namespace eastl +{ + + +//////////////////////////////////////////////////////////////////////////////// +// +// bool atomic_flag_test_and_set(eastl::atomic*) +// +EASTL_FORCE_INLINE bool atomic_flag_test_and_set(eastl::atomic_flag* atomicObj) EA_NOEXCEPT +{ + return atomicObj->test_and_set(); +} + +template +EASTL_FORCE_INLINE bool atomic_flag_test_and_set_explicit(eastl::atomic_flag* atomicObj, Order order) +{ + return atomicObj->test_and_set(order); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// bool atomic_flag_clear(eastl::atomic*) +// +EASTL_FORCE_INLINE void atomic_flag_clear(eastl::atomic_flag* atomicObj) +{ + atomicObj->clear(); +} + +template +EASTL_FORCE_INLINE void atomic_flag_clear_explicit(eastl::atomic_flag* atomicObj, Order order) +{ + atomicObj->clear(order); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// bool atomic_flag_test(eastl::atomic*) +// +EASTL_FORCE_INLINE bool atomic_flag_test(eastl::atomic_flag* atomicObj) +{ + return atomicObj->test(); +} + +template +EASTL_FORCE_INLINE bool atomic_flag_test_explicit(eastl::atomic_flag* atomicObj, Order order) +{ + return atomicObj->test(order); +} + + +} // namespace eastl + + +#endif /* EASTL_ATOMIC_INTERNAL_FLAG_STANDALONE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_integral.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_integral.h new file mode 100644 index 000000000..bcf7c1786 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_integral.h @@ -0,0 +1,343 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_INTEGRAL_H +#define EASTL_ATOMIC_INTERNAL_INTEGRAL_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#include "atomic_push_compiler_options.h" + + +namespace eastl +{ + + +namespace internal +{ + + +#define EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(funcName) \ + template \ + T funcName(T /*arg*/, Order /*order*/) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); \ + } \ + \ + template \ + T funcName(T /*arg*/, Order /*order*/) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } \ + \ + T funcName(T /*arg*/) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } + + +#define EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_INC_DEC_OPERATOR_IMPL(operatorOp) \ + T operator operatorOp() volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } \ + \ + T operator operatorOp(int) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } + + +#define EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(operatorOp) \ + T operator operatorOp(T /*arg*/) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } + + + template + struct atomic_integral_base : public atomic_base_width + { + private: + + using Base = atomic_base_width; + + public: /* ctors */ + + EA_CONSTEXPR atomic_integral_base(T desired) EA_NOEXCEPT + : Base{ desired } + { + } + + EA_CONSTEXPR atomic_integral_base() EA_NOEXCEPT = default; + + atomic_integral_base(const atomic_integral_base&) EA_NOEXCEPT = delete; + + public: /* assignment operator */ + + using Base::operator=; + + atomic_integral_base& operator=(const atomic_integral_base&) EA_NOEXCEPT = delete; + atomic_integral_base& operator=(const atomic_integral_base&) volatile EA_NOEXCEPT = delete; + + public: /* fetch_add */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(fetch_add) + + public: /* add_fetch */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(add_fetch) + + public: /* fetch_sub */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(fetch_sub) + + public: /* sub_fetch */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(sub_fetch) + + public: /* fetch_and */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(fetch_and) + + public: /* and_fetch */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(and_fetch) + + public: /* fetch_or */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(fetch_or) + + public: /* or_fetch */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(or_fetch) + + public: /* fetch_xor */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(fetch_xor) + + public: /* xor_fetch */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_FUNCS_IMPL(xor_fetch) + + public: /* operator++ && operator-- */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_INC_DEC_OPERATOR_IMPL(++) + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_INC_DEC_OPERATOR_IMPL(--) + + public: /* operator+= && operator-= */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(+=) + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(-=) + + public: /* operator&= */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(&=) + + public: /* operator|= */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(|=) + + public: /* operator^= */ + + EASTL_ATOMIC_INTEGRAL_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(^=) + + }; + + + template + struct atomic_integral_width; + +#define EASTL_ATOMIC_INTEGRAL_FUNC_IMPL(op, bits) \ + T retVal; \ + EA_PREPROCESSOR_JOIN(op, bits)(T, retVal, this->GetAtomicAddress(), arg); \ + return retVal; + +#define EASTL_ATOMIC_INTEGRAL_FETCH_IMPL(funcName, op, bits) \ + T funcName(T arg) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_INTEGRAL_FUNC_IMPL(op, bits); \ + } + +#define EASTL_ATOMIC_INTEGRAL_FETCH_ORDER_IMPL(funcName, orderType, op, bits) \ + T funcName(T arg, orderType) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_INTEGRAL_FUNC_IMPL(op, bits); \ + } + +#define EASTL_ATOMIC_INTEGRAL_FETCH_OP_JOIN(fetchOp, Order) \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_, fetchOp), Order) + +#define EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(funcName, fetchOp, bits) \ + using Base::funcName; \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_IMPL(funcName, EASTL_ATOMIC_INTEGRAL_FETCH_OP_JOIN(fetchOp, _SEQ_CST_), bits) \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_relaxed_s, \ + EASTL_ATOMIC_INTEGRAL_FETCH_OP_JOIN(fetchOp, _RELAXED_), bits) \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_acquire_s, \ + EASTL_ATOMIC_INTEGRAL_FETCH_OP_JOIN(fetchOp, _ACQUIRE_), bits) \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_release_s, \ + EASTL_ATOMIC_INTEGRAL_FETCH_OP_JOIN(fetchOp, _RELEASE_), bits) \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_acq_rel_s, \ + EASTL_ATOMIC_INTEGRAL_FETCH_OP_JOIN(fetchOp, _ACQ_REL_), bits) \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_seq_cst_s, \ + EASTL_ATOMIC_INTEGRAL_FETCH_OP_JOIN(fetchOp, _SEQ_CST_), bits) + +#define EASTL_ATOMIC_INTEGRAL_FETCH_INC_DEC_OPERATOR_IMPL(operatorOp, preFuncName, postFuncName) \ + using Base::operator operatorOp; \ + \ + T operator operatorOp() EA_NOEXCEPT \ + { \ + return preFuncName(1, eastl::memory_order_seq_cst); \ + } \ + \ + T operator operatorOp(int) EA_NOEXCEPT \ + { \ + return postFuncName(1, eastl::memory_order_seq_cst); \ + } + +#define EASTL_ATOMIC_INTEGRAL_FETCH_ASSIGNMENT_OPERATOR_IMPL(operatorOp, funcName) \ + using Base::operator operatorOp; \ + \ + T operator operatorOp(T arg) EA_NOEXCEPT \ + { \ + return funcName(arg, eastl::memory_order_seq_cst); \ + } + + +#define EASTL_ATOMIC_INTEGRAL_WIDTH_SPECIALIZE(bytes, bits) \ + template \ + struct atomic_integral_width : public atomic_integral_base \ + { \ + private: \ + \ + using Base = atomic_integral_base; \ + \ + public: /* ctors */ \ + \ + EA_CONSTEXPR atomic_integral_width(T desired) EA_NOEXCEPT \ + : Base{ desired } \ + { \ + } \ + \ + EA_CONSTEXPR atomic_integral_width() EA_NOEXCEPT = default; \ + \ + atomic_integral_width(const atomic_integral_width&) EA_NOEXCEPT = delete; \ + \ + public: /* assignment operator */ \ + \ + using Base::operator=; \ + \ + atomic_integral_width& operator=(const atomic_integral_width&) EA_NOEXCEPT = delete; \ + atomic_integral_width& operator=(const atomic_integral_width&) volatile EA_NOEXCEPT = delete; \ + \ + public: /* fetch_add */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(fetch_add, FETCH_ADD, bits) \ + \ + public: /* add_fetch */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(add_fetch, ADD_FETCH, bits) \ + \ + public: /* fetch_sub */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(fetch_sub, FETCH_SUB, bits) \ + \ + public: /* sub_fetch */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(sub_fetch, SUB_FETCH, bits) \ + \ + public: /* fetch_and */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(fetch_and, FETCH_AND, bits) \ + \ + public: /* and_fetch */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(and_fetch, AND_FETCH, bits) \ + \ + public: /* fetch_or */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(fetch_or, FETCH_OR, bits) \ + \ + public: /* or_fetch */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(or_fetch, OR_FETCH, bits) \ + \ + public: /* fetch_xor */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(fetch_xor, FETCH_XOR, bits) \ + \ + public: /* xor_fetch */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_FUNCS_IMPL(xor_fetch, XOR_FETCH, bits) \ + \ + public: /* operator++ && operator-- */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_INC_DEC_OPERATOR_IMPL(++, add_fetch, fetch_add) \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_INC_DEC_OPERATOR_IMPL(--, sub_fetch, fetch_sub) \ + \ + public: /* operator+= && operator-= */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ASSIGNMENT_OPERATOR_IMPL(+=, add_fetch) \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ASSIGNMENT_OPERATOR_IMPL(-=, sub_fetch) \ + \ + public: /* operator&= */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ASSIGNMENT_OPERATOR_IMPL(&=, and_fetch) \ + \ + public: /* operator|= */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ASSIGNMENT_OPERATOR_IMPL(|=, or_fetch) \ + \ + public: /* operator^= */ \ + \ + EASTL_ATOMIC_INTEGRAL_FETCH_ASSIGNMENT_OPERATOR_IMPL(^=, xor_fetch) \ + \ + }; + + +#if defined(EASTL_ATOMIC_HAS_8BIT) + EASTL_ATOMIC_INTEGRAL_WIDTH_SPECIALIZE(1, 8) +#endif + +#if defined(EASTL_ATOMIC_HAS_16BIT) + EASTL_ATOMIC_INTEGRAL_WIDTH_SPECIALIZE(2, 16) +#endif + +#if defined(EASTL_ATOMIC_HAS_32BIT) + EASTL_ATOMIC_INTEGRAL_WIDTH_SPECIALIZE(4, 32) +#endif + +#if defined(EASTL_ATOMIC_HAS_64BIT) + EASTL_ATOMIC_INTEGRAL_WIDTH_SPECIALIZE(8, 64) +#endif + +#if defined(EASTL_ATOMIC_HAS_128BIT) + EASTL_ATOMIC_INTEGRAL_WIDTH_SPECIALIZE(16, 128) +#endif + + +} // namespace internal + + +} // namespace eastl + + +#include "atomic_pop_compiler_options.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_INTEGRAL_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros.h new file mode 100644 index 000000000..756a4b4d1 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros.h @@ -0,0 +1,67 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_H +#define EASTL_ATOMIC_INTERNAL_MACROS_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// The reason for the implementation separating out into a compiler and architecture +// folder is as follows. +// +// The compiler directory is meant to implement atomics using the compiler provided +// intrinsics. This also implies that usually the same compiler instrinsic implementation +// can be used for any architecture the compiler supports. If a compiler provides intrinsics +// to support barriers or atomic operations, then that implementation should be in the +// compiler directory. +// +// The arch directory is meant to manually implement atomics for a specific architecture +// such as power or x86. There may be some compiler specific code in this directory because +// GCC inline assembly syntax may be different than another compiler as an example. +// +// The arch directory can also be used to implement some atomic operations ourselves +// if we deem the compiler provided implementation to be inefficient for the given +// architecture or we need to do some things manually for a given compiler. +// +// The atomic_macros directory implements the macros that the rest of the atomic +// library uses. These macros will expand to either the compiler or arch implemented +// macro. The arch implemented macro is given priority over the compiler implemented +// macro if both are implemented otherwise whichever is implemented is chosen or +// an error is emitted if none are implemented. +// +// The implementation being all macros has a couple nice side effects as well. +// +// 1. All the implementation ends up funneling into one low level macro implementation +// which makes it easy to verify correctness, reduce copy-paste errors and differences +// in various platform implementations. +// +// 2. Allows for the implementation to be implemented efficiently on compilers that do not +// directly implement the C++ memory model in their intrinsics such as msvc. +// +// 3. Allows for the implementation of atomics that may not be supported on the given platform, +// such as 128-bit atomics on 32-bit platforms since the macros will only ever be expanded +// on platforms that support said features. This makes implementing said features pretty easy +// since we do not have to worry about complicated feature detection in the low level implementations. +// +// The macro implementation may asume that all passed in types are trivially constructible thus it is +// free to create local variables of the passed in types as it may please. +// It may also assume that all passed in types are trivially copyable as well. +// It cannot assume any passed in type is any given type thus is a specific type if needed, it must do an +// EASTL_ATOMIC_TYPE_PUN_CAST() to the required type. +// + + +#include "compiler/compiler.h" +#include "arch/arch.h" + +#include "atomic_macros/atomic_macros.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros.h new file mode 100644 index 000000000..941ac51c5 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros.h @@ -0,0 +1,145 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_ATOMIC_MACROS_H +#define EASTL_ATOMIC_INTERNAL_ATOMIC_MACROS_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#include "atomic_macros_base.h" + +#include "atomic_macros_fetch_add.h" +#include "atomic_macros_fetch_sub.h" + +#include "atomic_macros_fetch_and.h" +#include "atomic_macros_fetch_xor.h" +#include "atomic_macros_fetch_or.h" + +#include "atomic_macros_add_fetch.h" +#include "atomic_macros_sub_fetch.h" + +#include "atomic_macros_and_fetch.h" +#include "atomic_macros_xor_fetch.h" +#include "atomic_macros_or_fetch.h" + +#include "atomic_macros_exchange.h" + +#include "atomic_macros_cmpxchg_weak.h" +#include "atomic_macros_cmpxchg_strong.h" + +#include "atomic_macros_load.h" +#include "atomic_macros_store.h" + +#include "atomic_macros_compiler_barrier.h" + +#include "atomic_macros_cpu_pause.h" + +#include "atomic_macros_memory_barrier.h" + +#include "atomic_macros_signal_fence.h" + +#include "atomic_macros_thread_fence.h" + + +///////////////////////////////////////////////////////////////////////////////// + + +#if defined(EASTL_COMPILER_ATOMIC_HAS_8BIT) || defined(EASTL_ARCH_ATOMIC_HAS_8BIT) + + #define EASTL_ATOMIC_HAS_8BIT + +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_HAS_16BIT) || defined(EASTL_ARCH_ATOMIC_HAS_16BIT) + + #define EASTL_ATOMIC_HAS_16BIT + +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_HAS_32BIT) || defined(EASTL_ARCH_ATOMIC_HAS_32BIT) + + #define EASTL_ATOMIC_HAS_32BIT + +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_HAS_64BIT) || defined(EASTL_ARCH_ATOMIC_HAS_64BIT) + + #define EASTL_ATOMIC_HAS_64BIT + +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_HAS_128BIT) || defined(EASTL_ARCH_ATOMIC_HAS_128BIT) + + #define EASTL_ATOMIC_HAS_128BIT + +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +#if defined(EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_8) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_8 EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_8 + +#elif defined(EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_8) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_8 EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_8 + +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_16) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_16 EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_16 + +#elif defined(EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_16) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_16 EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_16 + +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_32) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_32 EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_32 + +#elif defined(EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_32) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_32 EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_32 + +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_64) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_64 EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_64 + +#elif defined(EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_64) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_64 EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_64 + +#endif + + +#if defined(EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_128) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_128 EASTL_ARCH_ATOMIC_FIXED_WIDTH_TYPE_128 + +#elif defined(EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_128) + + #define EASTL_ATOMIC_FIXED_WIDTH_TYPE_128 EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_128 + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_ATOMIC_MACROS_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_add_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_add_fetch.h new file mode 100644 index 000000000..f551a07cb --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_add_fetch.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_ADD_FETCH_H +#define EASTL_ATOMIC_INTERNAL_MACROS_ADD_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_ADD_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_ADD_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_ADD_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_ADD_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_ADD_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_ADD_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_ADD_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_ADD_FETCH_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_ADD_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_and_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_and_fetch.h new file mode 100644 index 000000000..691272231 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_and_fetch.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_AND_FETCH_H +#define EASTL_ATOMIC_INTERNAL_MACROS_AND_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_AND_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_AND_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_AND_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_AND_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_AND_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_AND_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_AND_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_AND_FETCH_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_AND_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_base.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_base.h new file mode 100644 index 000000000..f03720d94 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_base.h @@ -0,0 +1,65 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_BASE_H +#define EASTL_ATOMIC_INTERNAL_MACROS_BASE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_ATOMIC_INTERNAL_COMPILER_AVAILABLE(op) \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_COMPILER_, op), _AVAILABLE) + +#define EASTL_ATOMIC_INTERNAL_ARCH_AVAILABLE(op) \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ARCH_, op), _AVAILABLE) + +#define EASTL_ATOMIC_INTERNAL_NOT_IMPLEMENTED_ERROR(...) \ + static_assert(false, "eastl::atomic atomic macro not implemented!") + + +/* Compiler && Arch Not Implemented */ +#define EASTL_ATOMIC_INTERNAL_OP_PATTERN_00(op) \ + EASTL_ATOMIC_INTERNAL_NOT_IMPLEMENTED_ERROR + +/* Arch Implemented */ +#define EASTL_ATOMIC_INTERNAL_OP_PATTERN_01(op) \ + EA_PREPROCESSOR_JOIN(EASTL_ARCH_, op) + +/* Compiler Implmented */ +#define EASTL_ATOMIC_INTERNAL_OP_PATTERN_10(op) \ + EA_PREPROCESSOR_JOIN(EASTL_COMPILER_, op) + +/* Compiler && Arch Implemented */ +#define EASTL_ATOMIC_INTERNAL_OP_PATTERN_11(op) \ + EA_PREPROCESSOR_JOIN(EASTL_ARCH_, op) + + +/* This macro creates the pattern macros above for the 2x2 True-False truth table */ +#define EASTL_ATOMIC_INTERNAL_OP_HELPER1(compiler, arch, op) \ + EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_INTERNAL_OP_PATTERN_, EA_PREPROCESSOR_JOIN(compiler, arch))(op) + + +///////////////////////////////////////////////////////////////////////////////// +// +// EASTL_ATOMIC_CHOOSE_OP_IMPL +// +// This macro chooses between the compiler or architecture implementation for a +// given atomic operation. +// +// USAGE: +// +// EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELAXED_8)(ret, ptr, val) +// +#define EASTL_ATOMIC_CHOOSE_OP_IMPL(op) \ + EASTL_ATOMIC_INTERNAL_OP_HELPER1( \ + EASTL_ATOMIC_INTERNAL_COMPILER_AVAILABLE(op), \ + EASTL_ATOMIC_INTERNAL_ARCH_AVAILABLE(op), \ + op \ + ) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_BASE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_cmpxchg_strong.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_cmpxchg_strong.h new file mode 100644 index 000000000..3cff4935b --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_cmpxchg_strong.h @@ -0,0 +1,245 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_CMPXCHG_STRONG_H +#define EASTL_ATOMIC_INTERNAL_MACROS_CMPXCHG_STRONG_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_CMPXCHG_STRONG_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128)(type, ret, ptr, expected, desired) + + +///////////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_CMPXCHG_STRONG_*(bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_8)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_16)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_32)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_64)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQUIRE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_RELEASE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_RELEASE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_ACQ_REL_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_ACQ_REL_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_STRONG_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_STRONG_SEQ_CST_128)(type, ret, ptr, expected, desired) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_CMPXCHG_STRONG_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_cmpxchg_weak.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_cmpxchg_weak.h new file mode 100644 index 000000000..60ea8b0ba --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_cmpxchg_weak.h @@ -0,0 +1,245 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_CMPXCHG_WEAK_H +#define EASTL_ATOMIC_INTERNAL_MACROS_CMPXCHG_WEAK_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_CMPXCHG_WEAK_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128)(type, ret, ptr, expected, desired) + + +///////////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_CMPXCHG_WEAK_*(bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_8)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_8)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_16)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_16)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_32)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_32)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_64)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_64)(type, ret, ptr, expected, desired) + + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELAXED_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQUIRE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_RELEASE_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_RELEASE_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_ACQ_REL_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_ACQ_REL_128)(type, ret, ptr, expected, desired) + +#define EASTL_ATOMIC_CMPXCHG_WEAK_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CMPXCHG_WEAK_SEQ_CST_128)(type, ret, ptr, expected, desired) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_CMPXCHG_WEAK_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_compiler_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_compiler_barrier.h new file mode 100644 index 000000000..96ea6d0b0 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_compiler_barrier.h @@ -0,0 +1,30 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_COMPILER_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_MACROS_COMPILER_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_COMPILER_BARRIER() +// +#define EASTL_ATOMIC_COMPILER_BARRIER() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_COMPILER_BARRIER)() + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY(const T&, type) +// +#define EASTL_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY(val, type) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY)(val, type) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_COMPILER_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_cpu_pause.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_cpu_pause.h new file mode 100644 index 000000000..e027b576d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_cpu_pause.h @@ -0,0 +1,22 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_CPU_PAUSE_H +#define EASTL_ATOMIC_INTERNAL_MACROS_CPU_PAUSE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_CPU_PAUSE() +// +#define EASTL_ATOMIC_CPU_PAUSE() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CPU_PAUSE)() + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_CPU_PAUSE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_exchange.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_exchange.h new file mode 100644 index 000000000..0681318f1 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_exchange.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_EXCHANGE_H +#define EASTL_ATOMIC_INTERNAL_MACROS_EXCHANGE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_EXCHANGE_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_EXCHANGE_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_EXCHANGE_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_EXCHANGE_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_EXCHANGE_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_EXCHANGE_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_EXCHANGE_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_EXCHANGE_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_EXCHANGE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_add.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_add.h new file mode 100644 index 000000000..701fdf374 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_add.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_FETCH_ADD_H +#define EASTL_ATOMIC_INTERNAL_MACROS_FETCH_ADD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_FETCH_ADD_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_FETCH_ADD_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_ADD_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_ADD_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_ADD_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_ADD_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_ADD_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_ADD_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_FETCH_ADD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_and.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_and.h new file mode 100644 index 000000000..831f1bfef --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_and.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_FETCH_AND_H +#define EASTL_ATOMIC_INTERNAL_MACROS_FETCH_AND_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_FETCH_AND_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_FETCH_AND_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_AND_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_AND_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_AND_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_AND_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_AND_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_AND_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_FETCH_AND_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_or.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_or.h new file mode 100644 index 000000000..b13229707 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_or.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_FETCH_OR_H +#define EASTL_ATOMIC_INTERNAL_MACROS_FETCH_OR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_FETCH_OR_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_FETCH_OR_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_OR_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_OR_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_OR_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_OR_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_OR_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_OR_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_FETCH_OR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_sub.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_sub.h new file mode 100644 index 000000000..009806432 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_sub.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_FETCH_SUB_H +#define EASTL_ATOMIC_INTERNAL_MACROS_FETCH_SUB_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_FETCH_SUB_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_FETCH_SUB_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_SUB_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_SUB_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_SUB_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_SUB_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_SUB_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_SUB_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_FETCH_SUB_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_xor.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_xor.h new file mode 100644 index 000000000..2887ea56b --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_fetch_xor.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_FETCH_XOR_H +#define EASTL_ATOMIC_INTERNAL_MACROS_FETCH_XOR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_FETCH_XOR_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_FETCH_XOR_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_XOR_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_XOR_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_XOR_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_FETCH_XOR_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_FETCH_XOR_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_FETCH_XOR_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_FETCH_XOR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_load.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_load.h new file mode 100644 index 000000000..76580593d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_load.h @@ -0,0 +1,75 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_LOAD_H +#define EASTL_ATOMIC_INTERNAL_MACROS_LOAD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_LOAD_*_N(type, type ret, type * ptr) +// +#define EASTL_ATOMIC_LOAD_RELAXED_8(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_RELAXED_8)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_ACQUIRE_8(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_ACQUIRE_8)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_SEQ_CST_8(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_SEQ_CST_8)(type, ret, ptr) + + +#define EASTL_ATOMIC_LOAD_RELAXED_16(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_RELAXED_16)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_ACQUIRE_16(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_ACQUIRE_16)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_SEQ_CST_16(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_SEQ_CST_16)(type, ret, ptr) + + +#define EASTL_ATOMIC_LOAD_RELAXED_32(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_RELAXED_32)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_ACQUIRE_32(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_ACQUIRE_32)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_SEQ_CST_32(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_SEQ_CST_32)(type, ret, ptr) + + +#define EASTL_ATOMIC_LOAD_RELAXED_64(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_RELAXED_64)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_ACQUIRE_64(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_ACQUIRE_64)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_SEQ_CST_64(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_SEQ_CST_64)(type, ret, ptr) + + +#define EASTL_ATOMIC_LOAD_RELAXED_128(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_RELAXED_128)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_ACQUIRE_128(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_ACQUIRE_128)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_SEQ_CST_128(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_SEQ_CST_128)(type, ret, ptr) + + +#define EASTL_ATOMIC_LOAD_READ_DEPENDS_32(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_READ_DEPENDS_32)(type, ret, ptr) + +#define EASTL_ATOMIC_LOAD_READ_DEPENDS_64(type, ret, ptr) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_LOAD_READ_DEPENDS_64)(type, ret, ptr) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_LOAD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_memory_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_memory_barrier.h new file mode 100644 index 000000000..14f7be92d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_memory_barrier.h @@ -0,0 +1,38 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_MEMORY_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_MACROS_MEMORY_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_CPU_MB() +// +#define EASTL_ATOMIC_CPU_MB() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CPU_MB)() + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_CPU_WMB() +// +#define EASTL_ATOMIC_CPU_WMB() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CPU_WMB)() + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_CPU_RMB() +// +#define EASTL_ATOMIC_CPU_RMB() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_CPU_RMB)() + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_MEMORY_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_or_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_or_fetch.h new file mode 100644 index 000000000..c9ebd6e33 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_or_fetch.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_OR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_MACROS_OR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_OR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_OR_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_OR_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_OR_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_OR_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_OR_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_OR_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_OR_FETCH_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_OR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_signal_fence.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_signal_fence.h new file mode 100644 index 000000000..dd16b1061 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_signal_fence.h @@ -0,0 +1,34 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_SIGNAL_FENCE_H +#define EASTL_ATOMIC_INTERNAL_MACROS_SIGNAL_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_SIGNAL_FENCE_*() +// +#define EASTL_ATOMIC_SIGNAL_FENCE_RELAXED() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SIGNAL_FENCE_RELAXED)() + +#define EASTL_ATOMIC_SIGNAL_FENCE_ACQUIRE() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SIGNAL_FENCE_ACQUIRE)() + +#define EASTL_ATOMIC_SIGNAL_FENCE_RELEASE() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SIGNAL_FENCE_RELEASE)() + +#define EASTL_ATOMIC_SIGNAL_FENCE_ACQ_REL() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SIGNAL_FENCE_ACQ_REL)() + +#define EASTL_ATOMIC_SIGNAL_FENCE_SEQ_CST() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SIGNAL_FENCE_SEQ_CST)() + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_SIGNAL_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_store.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_store.h new file mode 100644 index 000000000..64b662e1e --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_store.h @@ -0,0 +1,68 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_STORE_H +#define EASTL_ATOMIC_INTERNAL_MACROS_STORE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_STORE_*_N(type, type * ptr, type val) +// +#define EASTL_ATOMIC_STORE_RELAXED_8(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELAXED_8)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_RELEASE_8(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELEASE_8)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_SEQ_CST_8(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_SEQ_CST_8)(type, ptr, val) + + +#define EASTL_ATOMIC_STORE_RELAXED_16(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELAXED_16)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_RELEASE_16(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELEASE_16)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_SEQ_CST_16(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_SEQ_CST_16)(type, ptr, val) + + +#define EASTL_ATOMIC_STORE_RELAXED_32(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELAXED_32)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_RELEASE_32(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELEASE_32)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_SEQ_CST_32(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_SEQ_CST_32)(type, ptr, val) + + +#define EASTL_ATOMIC_STORE_RELAXED_64(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELAXED_64)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_RELEASE_64(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELEASE_64)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_SEQ_CST_64(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_SEQ_CST_64)(type, ptr, val) + + +#define EASTL_ATOMIC_STORE_RELAXED_128(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELAXED_128)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_RELEASE_128(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_RELEASE_128)(type, ptr, val) + +#define EASTL_ATOMIC_STORE_SEQ_CST_128(type, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_STORE_SEQ_CST_128)(type, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_STORE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_sub_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_sub_fetch.h new file mode 100644 index 000000000..330f38e96 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_sub_fetch.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_SUB_FETCH_H +#define EASTL_ATOMIC_INTERNAL_MACROS_SUB_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_SUB_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_SUB_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_SUB_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_SUB_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_SUB_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_SUB_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_SUB_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_SUB_FETCH_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_SUB_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_thread_fence.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_thread_fence.h new file mode 100644 index 000000000..26492c597 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_thread_fence.h @@ -0,0 +1,34 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_THREAD_FENCE_H +#define EASTL_ATOMIC_INTERNAL_MACROS_THREAD_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_THREAD_FENCE_*() +// +#define EASTL_ATOMIC_THREAD_FENCE_RELAXED() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_THREAD_FENCE_RELAXED)() + +#define EASTL_ATOMIC_THREAD_FENCE_ACQUIRE() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_THREAD_FENCE_ACQUIRE)() + +#define EASTL_ATOMIC_THREAD_FENCE_RELEASE() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_THREAD_FENCE_RELEASE)() + +#define EASTL_ATOMIC_THREAD_FENCE_ACQ_REL() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_THREAD_FENCE_ACQ_REL)() + +#define EASTL_ATOMIC_THREAD_FENCE_SEQ_CST() \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_THREAD_FENCE_SEQ_CST)() + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_THREAD_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_xor_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_xor_fetch.h new file mode 100644 index 000000000..422764704 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_macros/atomic_macros_xor_fetch.h @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MACROS_XOR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_MACROS_XOR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_ATOMIC_XOR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_ATOMIC_XOR_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELAXED_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQUIRE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELEASE_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQ_REL_8)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_SEQ_CST_8)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_XOR_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELAXED_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQUIRE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELEASE_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQ_REL_16)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_SEQ_CST_16)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_XOR_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELAXED_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQUIRE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELEASE_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQ_REL_32)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_SEQ_CST_32)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_XOR_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELAXED_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQUIRE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELEASE_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQ_REL_64)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_SEQ_CST_64)(type, ret, ptr, val) + + +#define EASTL_ATOMIC_XOR_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELAXED_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQUIRE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_RELEASE_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_ACQ_REL_128)(type, ret, ptr, val) + +#define EASTL_ATOMIC_XOR_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_ATOMIC_CHOOSE_OP_IMPL(ATOMIC_XOR_FETCH_SEQ_CST_128)(type, ret, ptr, val) + + +#endif /* EASTL_ATOMIC_INTERNAL_MACROS_XOR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_memory_order.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_memory_order.h new file mode 100644 index 000000000..1564d87dd --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_memory_order.h @@ -0,0 +1,44 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_MEMORY_ORDER_H +#define EASTL_ATOMIC_INTERNAL_MEMORY_ORDER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +namespace eastl +{ + + +namespace internal +{ + + +struct memory_order_relaxed_s {}; +struct memory_order_read_depends_s {}; +struct memory_order_acquire_s {}; +struct memory_order_release_s {}; +struct memory_order_acq_rel_s {}; +struct memory_order_seq_cst_s {}; + + +} // namespace internal + + +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_relaxed = internal::memory_order_relaxed_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_read_depends = internal::memory_order_read_depends_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_acquire = internal::memory_order_acquire_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_release = internal::memory_order_release_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_acq_rel = internal::memory_order_acq_rel_s{}; +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR auto memory_order_seq_cst = internal::memory_order_seq_cst_s{}; + + +} // namespace eastl + + +#endif /* EASTL_ATOMIC_INTERNAL_MEMORY_ORDER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_pointer.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_pointer.h new file mode 100644 index 000000000..c0b19e66e --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_pointer.h @@ -0,0 +1,281 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_POINTER_H +#define EASTL_ATOMIC_INTERNAL_POINTER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#include "atomic_push_compiler_options.h" + + +namespace eastl +{ + + +namespace internal +{ + + + template + struct atomic_pointer_base; + +#define EASTL_ATOMIC_POINTER_STATIC_ASSERT_FUNCS_IMPL(funcName) \ + template \ + T* funcName(ptrdiff_t /*arg*/, Order /*order*/) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); \ + } \ + \ + template \ + T* funcName(ptrdiff_t /*arg*/, Order /*order*/) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } \ + \ + T* funcName(ptrdiff_t /*arg*/) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } + +#define EASTL_ATOMIC_POINTER_STATIC_ASSERT_INC_DEC_OPERATOR_IMPL(operatorOp) \ + T* operator operatorOp() volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } \ + \ + T* operator operatorOp(int) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } + +#define EASTL_ATOMIC_POINTER_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(operatorOp) \ + T* operator operatorOp(ptrdiff_t /*arg*/) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + } + + + template + struct atomic_pointer_base : public atomic_base_width + { + private: + + using Base = atomic_base_width; + + public: /* ctors */ + + EA_CONSTEXPR atomic_pointer_base(T* desired) EA_NOEXCEPT + : Base{ desired } + { + } + + EA_CONSTEXPR atomic_pointer_base() EA_NOEXCEPT = default; + + atomic_pointer_base(const atomic_pointer_base&) EA_NOEXCEPT = delete; + + public: /* assignment operators */ + + using Base::operator=; + + atomic_pointer_base& operator=(const atomic_pointer_base&) EA_NOEXCEPT = delete; + atomic_pointer_base& operator=(const atomic_pointer_base&) volatile EA_NOEXCEPT = delete; + + public: /* fetch_add */ + + EASTL_ATOMIC_POINTER_STATIC_ASSERT_FUNCS_IMPL(fetch_add) + + public: /* add_fetch */ + + EASTL_ATOMIC_POINTER_STATIC_ASSERT_FUNCS_IMPL(add_fetch) + + public: /* fetch_sub */ + + EASTL_ATOMIC_POINTER_STATIC_ASSERT_FUNCS_IMPL(fetch_sub) + + public: /* sub_fetch */ + + EASTL_ATOMIC_POINTER_STATIC_ASSERT_FUNCS_IMPL(sub_fetch) + + public: /* operator++ && operator-- */ + + EASTL_ATOMIC_POINTER_STATIC_ASSERT_INC_DEC_OPERATOR_IMPL(++) + + EASTL_ATOMIC_POINTER_STATIC_ASSERT_INC_DEC_OPERATOR_IMPL(--) + + public: /* operator+= && operator-= */ + + EASTL_ATOMIC_POINTER_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(+=) + + EASTL_ATOMIC_POINTER_STATIC_ASSERT_ASSIGNMENT_OPERATOR_IMPL(-=) + + }; + + + template + struct atomic_pointer_width; + +#define EASTL_ATOMIC_POINTER_FUNC_IMPL(op, bits) \ + T* retVal; \ + { \ + ptr_integral_type retType; \ + ptr_integral_type addend = static_cast(arg) * static_cast(sizeof(T)); \ + \ + EA_PREPROCESSOR_JOIN(op, bits)(ptr_integral_type, retType, EASTL_ATOMIC_INTEGRAL_CAST(ptr_integral_type, this->GetAtomicAddress()), addend); \ + \ + retVal = reinterpret_cast(retType); \ + } \ + return retVal; + +#define EASTL_ATOMIC_POINTER_FETCH_IMPL(funcName, op, bits) \ + T* funcName(ptrdiff_t arg) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_TYPE_IS_OBJECT(T); \ + EASTL_ATOMIC_POINTER_FUNC_IMPL(op, bits); \ + } + +#define EASTL_ATOMIC_POINTER_FETCH_ORDER_IMPL(funcName, orderType, op, bits) \ + T* funcName(ptrdiff_t arg, orderType) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_TYPE_IS_OBJECT(T); \ + EASTL_ATOMIC_POINTER_FUNC_IMPL(op, bits); \ + } + +#define EASTL_ATOMIC_POINTER_FETCH_OP_JOIN(fetchOp, Order) \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_, fetchOp), Order) + +#define EASTL_ATOMIC_POINTER_FETCH_FUNCS_IMPL(funcName, fetchOp, bits) \ + using Base::funcName; \ + \ + EASTL_ATOMIC_POINTER_FETCH_IMPL(funcName, EASTL_ATOMIC_POINTER_FETCH_OP_JOIN(fetchOp, _SEQ_CST_), bits) \ + \ + EASTL_ATOMIC_POINTER_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_relaxed_s, \ + EASTL_ATOMIC_POINTER_FETCH_OP_JOIN(fetchOp, _RELAXED_), bits) \ + \ + EASTL_ATOMIC_POINTER_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_acquire_s, \ + EASTL_ATOMIC_POINTER_FETCH_OP_JOIN(fetchOp, _ACQUIRE_), bits) \ + \ + EASTL_ATOMIC_POINTER_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_release_s, \ + EASTL_ATOMIC_POINTER_FETCH_OP_JOIN(fetchOp, _RELEASE_), bits) \ + \ + EASTL_ATOMIC_POINTER_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_acq_rel_s, \ + EASTL_ATOMIC_POINTER_FETCH_OP_JOIN(fetchOp, _ACQ_REL_), bits) \ + \ + EASTL_ATOMIC_POINTER_FETCH_ORDER_IMPL(funcName, eastl::internal::memory_order_seq_cst_s, \ + EASTL_ATOMIC_POINTER_FETCH_OP_JOIN(fetchOp, _SEQ_CST_), bits) + +#define EASTL_ATOMIC_POINTER_FETCH_INC_DEC_OPERATOR_IMPL(operatorOp, preFuncName, postFuncName) \ + using Base::operator operatorOp; \ + \ + T* operator operatorOp() EA_NOEXCEPT \ + { \ + return preFuncName(1, eastl::memory_order_seq_cst); \ + } \ + \ + T* operator operatorOp(int) EA_NOEXCEPT \ + { \ + return postFuncName(1, eastl::memory_order_seq_cst); \ + } + +#define EASTL_ATOMIC_POINTER_FETCH_ASSIGNMENT_OPERATOR_IMPL(operatorOp, funcName) \ + using Base::operator operatorOp; \ + \ + T* operator operatorOp(ptrdiff_t arg) EA_NOEXCEPT \ + { \ + return funcName(arg, eastl::memory_order_seq_cst); \ + } + + +#define EASTL_ATOMIC_POINTER_WIDTH_SPECIALIZE(bytes, bits) \ + template \ + struct atomic_pointer_width : public atomic_pointer_base \ + { \ + private: \ + \ + using Base = atomic_pointer_base; \ + using u_ptr_integral_type = EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(uint, bits), _t); \ + using ptr_integral_type = EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(int, bits), _t); \ + \ + public: /* ctors */ \ + \ + EA_CONSTEXPR atomic_pointer_width(T* desired) EA_NOEXCEPT \ + : Base{ desired } \ + { \ + } \ + \ + EA_CONSTEXPR atomic_pointer_width() EA_NOEXCEPT = default; \ + \ + atomic_pointer_width(const atomic_pointer_width&) EA_NOEXCEPT = delete; \ + \ + public: /* assignment operators */ \ + \ + using Base::operator=; \ + \ + atomic_pointer_width& operator=(const atomic_pointer_width&) EA_NOEXCEPT = delete; \ + atomic_pointer_width& operator=(const atomic_pointer_width&) volatile EA_NOEXCEPT = delete; \ + \ + public: /* fetch_add */ \ + \ + EASTL_ATOMIC_POINTER_FETCH_FUNCS_IMPL(fetch_add, FETCH_ADD, bits) \ + \ + public: /* add_fetch */ \ + \ + EASTL_ATOMIC_POINTER_FETCH_FUNCS_IMPL(add_fetch, ADD_FETCH, bits) \ + \ + public: /* fetch_sub */ \ + \ + EASTL_ATOMIC_POINTER_FETCH_FUNCS_IMPL(fetch_sub, FETCH_SUB, bits) \ + \ + public: /* sub_fetch */ \ + \ + EASTL_ATOMIC_POINTER_FETCH_FUNCS_IMPL(sub_fetch, SUB_FETCH, bits) \ + \ + public: /* operator++ && operator-- */ \ + \ + EASTL_ATOMIC_POINTER_FETCH_INC_DEC_OPERATOR_IMPL(++, add_fetch, fetch_add) \ + \ + EASTL_ATOMIC_POINTER_FETCH_INC_DEC_OPERATOR_IMPL(--, sub_fetch, fetch_sub) \ + \ + public: /* operator+= && operator-= */ \ + \ + EASTL_ATOMIC_POINTER_FETCH_ASSIGNMENT_OPERATOR_IMPL(+=, add_fetch) \ + \ + EASTL_ATOMIC_POINTER_FETCH_ASSIGNMENT_OPERATOR_IMPL(-=, sub_fetch) \ + \ + public: \ + \ + using Base::load; \ + \ + T* load(eastl::internal::memory_order_read_depends_s) EA_NOEXCEPT \ + { \ + T* retPointer; \ + EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_LOAD_READ_DEPENDS_, bits)(T*, retPointer, this->GetAtomicAddress()); \ + return retPointer; \ + } \ + }; + + +#if defined(EASTL_ATOMIC_HAS_32BIT) && EA_PLATFORM_PTR_SIZE == 4 + EASTL_ATOMIC_POINTER_WIDTH_SPECIALIZE(4, 32) +#endif + +#if defined(EASTL_ATOMIC_HAS_64BIT) && EA_PLATFORM_PTR_SIZE == 8 + EASTL_ATOMIC_POINTER_WIDTH_SPECIALIZE(8, 64) +#endif + + +} // namespace internal + + +} // namespace eastl + + +#include "atomic_pop_compiler_options.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_POINTER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_pop_compiler_options.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_pop_compiler_options.h new file mode 100644 index 000000000..92f241a18 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_pop_compiler_options.h @@ -0,0 +1,11 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +/* NOTE: No Header Guard */ + + +EA_RESTORE_VC_WARNING(); + +EA_RESTORE_CLANG_WARNING(); diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_push_compiler_options.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_push_compiler_options.h new file mode 100644 index 000000000..c5a54715e --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_push_compiler_options.h @@ -0,0 +1,17 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +/* NOTE: No Header Guard */ + + +// 'class' : multiple assignment operators specified +EA_DISABLE_VC_WARNING(4522); + +// misaligned atomic operation may incur significant performance penalty +// The above warning is emitted in earlier versions of clang incorrectly. +// All eastl::atomic objects are size aligned. +// This is static and runtime asserted. +// Thus we disable this warning. +EA_DISABLE_CLANG_WARNING(-Watomic-alignment); diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_size_aligned.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_size_aligned.h new file mode 100644 index 000000000..f50337586 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_size_aligned.h @@ -0,0 +1,197 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_SIZE_ALIGNED_H +#define EASTL_ATOMIC_INTERNAL_SIZE_ALIGNED_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#include "atomic_push_compiler_options.h" + + +namespace eastl +{ + + +namespace internal +{ + + +#define EASTL_ATOMIC_SIZE_ALIGNED_STATIC_ASSERT_CMPXCHG_IMPL(funcName) \ + template \ + bool funcName(T& /*expected*/, T /*desired*/, \ + OrderSuccess /*orderSuccess*/, \ + OrderFailure /*orderFailure*/) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); \ + return false; \ + } \ + \ + template \ + bool funcName(T& /*expected*/, T /*desired*/, \ + OrderSuccess /*orderSuccess*/, \ + OrderFailure /*orderFailure*/) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + return false; \ + } \ + \ + template \ + bool funcName(T& /*expected*/, T /*desired*/, \ + Order /*order*/) EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); \ + return false; \ + } \ + \ + template \ + bool funcName(T& /*expected*/, T /*desired*/, \ + Order /*order*/) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + return false; \ + } \ + \ + bool funcName(T& /*expected*/, T /*desired*/) volatile EA_NOEXCEPT \ + { \ + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); \ + return false; \ + } + +#define EASTL_ATOMIC_SIZE_ALIGNED_STATIC_ASSERT_CMPXCHG_WEAK_IMPL() \ + EASTL_ATOMIC_SIZE_ALIGNED_STATIC_ASSERT_CMPXCHG_IMPL(compare_exchange_weak) + +#define EASTL_ATOMIC_SIZE_ALIGNED_STATIC_ASSERT_CMPXCHG_STRONG_IMPL() \ + EASTL_ATOMIC_SIZE_ALIGNED_STATIC_ASSERT_CMPXCHG_IMPL(compare_exchange_strong) + + + template + struct atomic_size_aligned + { + public: /* ctors */ + + EA_CONSTEXPR atomic_size_aligned(T desired) EA_NOEXCEPT + : mAtomic{ desired } + { + } + + EA_CONSTEXPR atomic_size_aligned() EA_NOEXCEPT_IF(eastl::is_nothrow_default_constructible_v) + : mAtomic{} /* Value-Initialize which will Zero-Initialize Trivial Constructible types */ + { + } + + atomic_size_aligned(const atomic_size_aligned&) EA_NOEXCEPT = delete; + + public: /* store */ + + template + void store(T /*desired*/, Order /*order*/) EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); + } + + template + void store(T /*desired*/, Order /*order*/) volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); + } + + void store(T /*desired*/) volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); + } + + public: /* load */ + + template + T load(Order /*order*/) const EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); + } + + template + T load(Order /*order*/) const volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); + } + + T load() const volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); + } + + public: /* exchange */ + + template + T exchange(T /*desired*/, Order /*order*/) EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(T); + } + + template + T exchange(T /*desired*/, Order /*order*/) volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); + } + + T exchange(T /*desired*/) volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); + } + + public: /* compare_exchange_weak */ + + EASTL_ATOMIC_SIZE_ALIGNED_STATIC_ASSERT_CMPXCHG_WEAK_IMPL() + + public: /* compare_exchange_strong */ + + EASTL_ATOMIC_SIZE_ALIGNED_STATIC_ASSERT_CMPXCHG_STRONG_IMPL() + + public: /* assignment operator */ + + T operator=(T /*desired*/) volatile EA_NOEXCEPT + { + EASTL_ATOMIC_STATIC_ASSERT_VOLATILE_MEM_FN(T); + } + + atomic_size_aligned& operator=(const atomic_size_aligned&) EA_NOEXCEPT = delete; + atomic_size_aligned& operator=(const atomic_size_aligned&) volatile EA_NOEXCEPT = delete; + + protected: /* Accessors */ + + T* GetAtomicAddress() const EA_NOEXCEPT + { + return eastl::addressof(mAtomic); + } + + private: + + /** + * Some compilers such as MSVC will align 64-bit values on 32-bit machines on + * 4-byte boundaries which can ruin the atomicity guarantees. + * + * Ensure everything is size aligned. + * + * mutable is needed in cases such as when loads are only guaranteed to be atomic + * using a compare exchange, such as for 128-bit atomics, so we need to be able + * to have write access to the variable as one example. + */ + EA_ALIGN(sizeof(T)) mutable T mAtomic; + }; + + +} // namespace internal + + +} // namespace eastl + + +#include "atomic_pop_compiler_options.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_SIZE_ALIGNED_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/atomic_standalone.h b/lib/EASTL/include/EASTL/internal/atomic/atomic_standalone.h new file mode 100644 index 000000000..011d5fb33 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/atomic_standalone.h @@ -0,0 +1,470 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_STANDALONE_H +#define EASTL_ATOMIC_INTERNAL_STANDALONE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +namespace eastl +{ + + +//////////////////////////////////////////////////////////////////////////////// +// +// bool atomic_compare_exchange_strong(eastl::atomic*, T* expected, T desired) +// +template +EASTL_FORCE_INLINE bool atomic_compare_exchange_strong(eastl::atomic* atomicObj, + typename eastl::atomic::value_type* expected, + typename eastl::atomic::value_type desired) EA_NOEXCEPT +{ + return atomicObj->compare_exchange_strong(*expected, desired); +} + +template +EASTL_FORCE_INLINE bool atomic_compare_exchange_strong_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::value_type* expected, + typename eastl::atomic::value_type desired, + OrderSuccess orderSuccess, OrderFailure orderFailure) EA_NOEXCEPT +{ + return atomicObj->compare_exchange_strong(*expected, desired, orderSuccess, orderFailure); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// bool atomic_compare_exchange_weak(eastl::atomic*, T* expected, T desired) +// +template +EASTL_FORCE_INLINE bool atomic_compare_exchange_weak(eastl::atomic* atomicObj, + typename eastl::atomic::value_type* expected, + typename eastl::atomic::value_type desired) EA_NOEXCEPT +{ + return atomicObj->compare_exchange_weak(*expected, desired); +} + +template +EASTL_FORCE_INLINE bool atomic_compare_exchange_weak_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::value_type* expected, + typename eastl::atomic::value_type desired, + OrderSuccess orderSuccess, OrderFailure orderFailure) EA_NOEXCEPT +{ + return atomicObj->compare_exchange_weak(*expected, desired, orderSuccess, orderFailure); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// T atomic_fetch_xor(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_xor(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg) EA_NOEXCEPT +{ + return atomicObj->fetch_xor(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_xor_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->fetch_xor(arg, order); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// T atomic_xor_fetch(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_xor_fetch(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg) EA_NOEXCEPT +{ + return atomicObj->xor_fetch(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_xor_fetch_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->xor_fetch(arg, order); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// T atomic_fetch_or(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_or(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg) EA_NOEXCEPT +{ + return atomicObj->fetch_or(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_or_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->fetch_or(arg, order); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// T atomic_or_fetch(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_or_fetch(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg) EA_NOEXCEPT +{ + return atomicObj->or_fetch(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_or_fetch_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->or_fetch(arg, order); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// T atomic_fetch_and(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_and(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg) EA_NOEXCEPT +{ + return atomicObj->fetch_and(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_and_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->fetch_and(arg, order); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// T atomic_and_fetch(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_and_fetch(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg) EA_NOEXCEPT +{ + return atomicObj->and_fetch(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_and_fetch_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::value_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->and_fetch(arg, order); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// T atomic_fetch_sub(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_sub(eastl::atomic* atomicObj, + typename eastl::atomic::difference_type arg) EA_NOEXCEPT +{ + return atomicObj->fetch_sub(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_sub_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::difference_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->fetch_sub(arg, order); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// T atomic_sub_fetch(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_sub_fetch(eastl::atomic* atomicObj, + typename eastl::atomic::difference_type arg) EA_NOEXCEPT +{ + return atomicObj->sub_fetch(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_sub_fetch_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::difference_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->sub_fetch(arg, order); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// T atomic_fetch_add(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_add(eastl::atomic* atomicObj, + typename eastl::atomic::difference_type arg) EA_NOEXCEPT +{ + return atomicObj->fetch_add(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_fetch_add_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::difference_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->fetch_add(arg, order); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// T atomic_add_fetch(eastl::atomic*, T arg) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_add_fetch(eastl::atomic* atomicObj, + typename eastl::atomic::difference_type arg) EA_NOEXCEPT +{ + return atomicObj->add_fetch(arg); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_add_fetch_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::difference_type arg, + Order order) EA_NOEXCEPT +{ + return atomicObj->add_fetch(arg, order); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// T atomic_exchange(eastl::atomic*, T desired) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_exchange(eastl::atomic* atomicObj, + typename eastl::atomic::value_type desired) EA_NOEXCEPT +{ + return atomicObj->exchange(desired); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_exchange_explicit(eastl::atomic* atomicObj, + typename eastl::atomic::value_type desired, + Order order) EA_NOEXCEPT +{ + return atomicObj->exchange(desired, order); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// T atomic_load(const eastl::atomic*) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_load(const eastl::atomic* atomicObj) EA_NOEXCEPT +{ + return atomicObj->load(); +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_load_explicit(const eastl::atomic* atomicObj, Order order) EA_NOEXCEPT +{ + return atomicObj->load(order); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// T atomic_load_cond(const eastl::atomic*) +// +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_load_cond(const eastl::atomic* atomicObj, Predicate pred) EA_NOEXCEPT +{ + for (;;) + { + typename eastl::atomic::value_type ret = atomicObj->load(); + + if (pred(ret)) + { + return ret; + } + + EASTL_ATOMIC_CPU_PAUSE(); + } +} + +template +EASTL_FORCE_INLINE typename eastl::atomic::value_type atomic_load_cond_explicit(const eastl::atomic* atomicObj, Predicate pred, Order order) EA_NOEXCEPT +{ + for (;;) + { + typename eastl::atomic::value_type ret = atomicObj->load(order); + + if (pred(ret)) + { + return ret; + } + + EASTL_ATOMIC_CPU_PAUSE(); + } +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// void atomic_store(eastl::atomic*, T) +// +template +EASTL_FORCE_INLINE void atomic_store(eastl::atomic* atomicObj, typename eastl::atomic::value_type desired) EA_NOEXCEPT +{ + atomicObj->store(desired); +} + +template +EASTL_FORCE_INLINE void atomic_store_explicit(eastl::atomic* atomicObj, typename eastl::atomic::value_type desired, Order order) EA_NOEXCEPT +{ + atomicObj->store(desired, order); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// void eastl::atomic_thread_fence(Order) +// +template +EASTL_FORCE_INLINE void atomic_thread_fence(Order) EA_NOEXCEPT +{ + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(Order); +} + +EASTL_FORCE_INLINE void atomic_thread_fence(eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_THREAD_FENCE_RELAXED(); +} + +EASTL_FORCE_INLINE void atomic_thread_fence(eastl::internal::memory_order_acquire_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_THREAD_FENCE_ACQUIRE(); +} + +EASTL_FORCE_INLINE void atomic_thread_fence(eastl::internal::memory_order_release_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_THREAD_FENCE_RELEASE(); +} + +EASTL_FORCE_INLINE void atomic_thread_fence(eastl::internal::memory_order_acq_rel_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_THREAD_FENCE_ACQ_REL(); +} + +EASTL_FORCE_INLINE void atomic_thread_fence(eastl::internal::memory_order_seq_cst_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_THREAD_FENCE_SEQ_CST(); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// void eastl::atomic_signal_fence(Order) +// +template +EASTL_FORCE_INLINE void atomic_signal_fence(Order) EA_NOEXCEPT +{ + EASTL_ATOMIC_STATIC_ASSERT_INVALID_MEMORY_ORDER(Order); +} + +EASTL_FORCE_INLINE void atomic_signal_fence(eastl::internal::memory_order_relaxed_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_SIGNAL_FENCE_RELAXED(); +} + +EASTL_FORCE_INLINE void atomic_signal_fence(eastl::internal::memory_order_acquire_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_SIGNAL_FENCE_ACQUIRE(); +} + +EASTL_FORCE_INLINE void atomic_signal_fence(eastl::internal::memory_order_release_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_SIGNAL_FENCE_RELEASE(); +} + +EASTL_FORCE_INLINE void atomic_signal_fence(eastl::internal::memory_order_acq_rel_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_SIGNAL_FENCE_ACQ_REL(); +} + +EASTL_FORCE_INLINE void atomic_signal_fence(eastl::internal::memory_order_seq_cst_s) EA_NOEXCEPT +{ + EASTL_ATOMIC_SIGNAL_FENCE_SEQ_CST(); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// void eastl::compiler_barrier() +// +EASTL_FORCE_INLINE void compiler_barrier() EA_NOEXCEPT +{ + EASTL_ATOMIC_COMPILER_BARRIER(); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// void eastl::compiler_barrier_data_dependency(const T&) +// +template +EASTL_FORCE_INLINE void compiler_barrier_data_dependency(const T& val) EA_NOEXCEPT +{ + EASTL_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY(val, T); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// void eastl::cpu_pause() +// +EASTL_FORCE_INLINE void cpu_pause() EA_NOEXCEPT +{ + EASTL_ATOMIC_CPU_PAUSE(); +} + + +///////////////////////////////////////////////////////////////////////////////// +// +// bool eastl::atomic_is_lock_free(eastl::atomic*) +// +template +EASTL_FORCE_INLINE bool atomic_is_lock_free(const eastl::atomic* atomicObj) EA_NOEXCEPT +{ + return atomicObj->is_lock_free(); +} + + +} // namespace eastl + + +#endif /* EASTL_ATOMIC_INTERNAL_STANDALONE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler.h new file mode 100644 index 000000000..fc128795d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler.h @@ -0,0 +1,120 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// Include the compiler specific implementations +// +#if defined(EA_COMPILER_GNUC) || defined(__clang__) + + #include "gcc/compiler_gcc.h" + +#elif defined(EA_COMPILER_MSVC) + + #include "msvc/compiler_msvc.h" + +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +namespace eastl +{ + + +namespace internal +{ + + +/** + * NOTE: + * + * This can be used by specific compiler implementations to implement a data dependency compiler barrier. + * Some compiler barriers do not take in input dependencies as is possible with the gcc asm syntax. + * Thus we need a way to create a false dependency on the input variable so the compiler does not dead-store + * remove it. + * A volatile function pointer ensures the compiler must always load the function pointer and call thru it + * since the compiler cannot reason about any side effects. Thus the compiler must always assume the + * input variable may be accessed and thus cannot be dead-stored. This technique works even in the presence + * of Link-Time Optimization. A compiler barrier with a data dependency is useful in these situations. + * + * void foo() + * { + * eastl::vector v; + * while (Benchmark.ContinueRunning()) + * { + * v.push_back(0); + * eastl::compiler_barrier(); OR eastl::compiler_barrier_data_dependency(v); + * } + * } + * + * We are trying to benchmark the push_back function of a vector. The vector v has only local scope. + * The compiler is well within its writes to remove all accesses to v even with the compiler barrier + * because there are no observable uses of the vector v. + * The compiler barrier data dependency ensures there is an input dependency on the variable so that + * it isn't removed. This is also useful when writing test code that the compiler may remove. + */ + +typedef void (*CompilerBarrierDataDependencyFuncPtr)(void*); + +extern EASTL_API volatile CompilerBarrierDataDependencyFuncPtr gCompilerBarrierDataDependencyFunc; + + +#define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY_FUNC(ptr) \ + eastl::internal::gCompilerBarrierDataDependencyFunc(ptr) + + +} // namespace internal + + +} // namespace eastl + + +///////////////////////////////////////////////////////////////////////////////// + + +#include "compiler_fetch_add.h" +#include "compiler_fetch_sub.h" + +#include "compiler_fetch_and.h" +#include "compiler_fetch_xor.h" +#include "compiler_fetch_or.h" + +#include "compiler_add_fetch.h" +#include "compiler_sub_fetch.h" + +#include "compiler_and_fetch.h" +#include "compiler_xor_fetch.h" +#include "compiler_or_fetch.h" + +#include "compiler_exchange.h" + +#include "compiler_cmpxchg_weak.h" +#include "compiler_cmpxchg_strong.h" + +#include "compiler_load.h" +#include "compiler_store.h" + +#include "compiler_barrier.h" + +#include "compiler_cpu_pause.h" + +#include "compiler_memory_barrier.h" + +#include "compiler_signal_fence.h" + +#include "compiler_thread_fence.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_add_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_add_fetch.h new file mode 100644 index 000000000..763921c45 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_add_fetch.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_ADD_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_ADD_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_ADD_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_ADD_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_and_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_and_fetch.h new file mode 100644 index 000000000..7b1e0a428 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_and_fetch.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_AND_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_AND_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_AND_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_AND_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_barrier.h new file mode 100644 index 000000000..550070e31 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_barrier.h @@ -0,0 +1,36 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_COMPILER_BARRIER() +// +#if defined(EASTL_COMPILER_ATOMIC_COMPILER_BARRIER) + #define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_AVAILABLE 0 +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY(const T&, type) +// +#if defined(EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY) + #define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_cmpxchg_strong.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_cmpxchg_strong.h new file mode 100644 index 000000000..2ee297112 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_cmpxchg_strong.h @@ -0,0 +1,430 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_CMPXCHG_STRONG_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_CMPXCHG_STRONG_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128_AVAILABLE 0 +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_CMPXCHG_STRONG_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_cmpxchg_weak.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_cmpxchg_weak.h new file mode 100644 index 000000000..9bc1a621b --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_cmpxchg_weak.h @@ -0,0 +1,430 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_CMPXCHG_WEAK_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_CMPXCHG_WEAK_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128_AVAILABLE 0 +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_8_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_16_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_32_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_64_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_128_AVAILABLE \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128_AVAILABLE +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_CMPXCHG_WEAK_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_cpu_pause.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_cpu_pause.h new file mode 100644 index 000000000..073b3fbb1 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_cpu_pause.h @@ -0,0 +1,32 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_CPU_PAUSE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_CPU_PAUSE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CPU_PAUSE() +// +#if defined(EASTL_COMPILER_ATOMIC_CPU_PAUSE) + + #define EASTL_COMPILER_ATOMIC_CPU_PAUSE_AVAILABLE 1 + +#else + + #define EASTL_COMPILER_ATOMIC_CPU_PAUSE() \ + ((void)0) + + #define EASTL_COMPILER_ATOMIC_CPU_PAUSE_AVAILABLE 1 + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_CPU_PAUSE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_exchange.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_exchange.h new file mode 100644 index 000000000..d82b199d7 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_exchange.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_EXCHANGE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_EXCHANGE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_EXCHANGE_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_EXCHANGE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_add.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_add.h new file mode 100644 index 000000000..e6c4238f0 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_add.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_ADD_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_ADD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_ADD_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_ADD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_and.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_and.h new file mode 100644 index 000000000..b0976fc73 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_and.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_AND_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_AND_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_AND_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_AND_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_or.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_or.h new file mode 100644 index 000000000..2e6cfdac6 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_or.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_OR_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_OR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_OR_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_OR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_sub.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_sub.h new file mode 100644 index 000000000..d7ed86cc1 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_sub.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_SUB_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_SUB_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_SUB_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_SUB_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_xor.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_xor.h new file mode 100644 index 000000000..10cf7d90d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_fetch_xor.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_XOR_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_XOR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_XOR_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_FETCH_XOR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_load.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_load.h new file mode 100644 index 000000000..734dbb80d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_load.h @@ -0,0 +1,139 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_LOAD_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_LOAD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_LOAD_*_N(type, type ret, type * ptr) +// +#if defined(EASTL_COMPILER_ATOMIC_LOAD_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_128_AVAILABLE 0 +#endif + + +/** + * NOTE: + * + * These are used for data-dependent reads thru a pointer. It is safe + * to assume that pointer-sized reads are atomic on any given platform. + * This implementation assumes the hardware doesn't reorder dependent + * loads unlike the DEC Alpha. + */ +#define EASTL_COMPILER_ATOMIC_LOAD_READ_DEPENDS_N(type, ret, ptr) \ + { \ + static_assert(eastl::is_pointer_v, "eastl::atomic : Read Depends Type must be a Pointer Type!"); \ + static_assert(eastl::is_pointer_v>, "eastl::atomic : Read Depends Ptr must be a Pointer to a Pointer!"); \ + \ + ret = (*EASTL_ATOMIC_VOLATILE_CAST(ptr)); \ + } + +#define EASTL_COMPILER_ATOMIC_LOAD_READ_DEPENDS_32(type, ret, ptr) \ + EASTL_COMPILER_ATOMIC_LOAD_READ_DEPENDS_N(type, ret, ptr) + +#define EASTL_COMPILER_ATOMIC_LOAD_READ_DEPENDS_64(type, ret, ptr) \ + EASTL_COMPILER_ATOMIC_LOAD_READ_DEPENDS_N(type, ret, ptr) + +#define EASTL_COMPILER_ATOMIC_LOAD_READ_DEPENDS_32_AVAILABLE 1 +#define EASTL_COMPILER_ATOMIC_LOAD_READ_DEPENDS_64_AVAILABLE 1 + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_LOAD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_memory_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_memory_barrier.h new file mode 100644 index 000000000..ac3923c6c --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_memory_barrier.h @@ -0,0 +1,47 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MEMORY_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MEMORY_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CPU_MB() +// +#if defined(EASTL_COMPILER_ATOMIC_CPU_MB) + #define EASTL_COMPILER_ATOMIC_CPU_MB_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CPU_MB_AVAILABLE 0 +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CPU_WMB() +// +#if defined(EASTL_COMPILER_ATOMIC_CPU_WMB) + #define EASTL_COMPILER_ATOMIC_CPU_WMB_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CPU_WMB_AVAILABLE 0 +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CPU_RMB() +// +#if defined(EASTL_COMPILER_ATOMIC_CPU_RMB) + #define EASTL_COMPILER_ATOMIC_CPU_RMB_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_CPU_RMB_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MEMORY_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_or_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_or_fetch.h new file mode 100644 index 000000000..a26a72c7f --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_or_fetch.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_OR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_OR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_OR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_OR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_signal_fence.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_signal_fence.h new file mode 100644 index 000000000..25b0b7413 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_signal_fence.h @@ -0,0 +1,49 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_SIGNAL_FENCE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_SIGNAL_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_*() +// +#if defined(EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELAXED) + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELAXED_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELAXED_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQUIRE) + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQUIRE_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQUIRE_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELEASE) + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELEASE_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELEASE_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQ_REL) + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQ_REL_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQ_REL_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_SEQ_CST) + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_SEQ_CST_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_SEQ_CST_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_SIGNAL_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_store.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_store.h new file mode 100644 index 000000000..1a553e2a7 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_store.h @@ -0,0 +1,113 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_STORE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_STORE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_STORE_*_N(type, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_STORE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_sub_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_sub_fetch.h new file mode 100644 index 000000000..4b7eea926 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_sub_fetch.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_SUB_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_SUB_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_SUB_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_SUB_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_thread_fence.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_thread_fence.h new file mode 100644 index 000000000..01d8f0f93 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_thread_fence.h @@ -0,0 +1,49 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_THREAD_FENCE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_THREAD_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_THREAD_FENCE_*() +// +#if defined(EASTL_COMPILER_ATOMIC_THREAD_FENCE_RELAXED) + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_RELAXED_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_RELAXED_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_THREAD_FENCE_ACQUIRE) + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_ACQUIRE_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_ACQUIRE_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_THREAD_FENCE_RELEASE) + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_RELEASE_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_RELEASE_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_THREAD_FENCE_ACQ_REL) + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_ACQ_REL_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_ACQ_REL_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_THREAD_FENCE_SEQ_CST) + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_SEQ_CST_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_THREAD_FENCE_SEQ_CST_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_THREAD_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_xor_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_xor_fetch.h new file mode 100644 index 000000000..05680bd15 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/compiler_xor_fetch.h @@ -0,0 +1,173 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_XOR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_XOR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_XOR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_8) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_8) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_8) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_8) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_8_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_8) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_8_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_8_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_16) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_16) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_16) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_16) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_16_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_16) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_16_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_16_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_32) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_32) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_32) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_32) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_32_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_32) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_32_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_32_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_64) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_64) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_64) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_64) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_64_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_64) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_64_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_64_AVAILABLE 0 +#endif + + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_128) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_128) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_128) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_128) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_128_AVAILABLE 0 +#endif + +#if defined(EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_128) + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_128_AVAILABLE 1 +#else + #define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_128_AVAILABLE 0 +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_XOR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc.h new file mode 100644 index 000000000..26a99c20e --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc.h @@ -0,0 +1,154 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +/** + * NOTE: + * + * gcc __atomic builtins may defer to function calls in libatomic.so for architectures that do not + * support atomic instructions of a given size. These functions will be implemented with pthread_mutex_t. + * It also requires the explicit linking against the compiler runtime libatomic.so. + * On architectures that do not support atomics, like armv6 the builtins may defer to kernel helpers + * or on classic uniprocessor systems just disable interrupts. + * + * We do not want to have to link against libatomic.so or fall into the trap of our atomics degrading + * into locks. We would rather have user-code explicitly use locking primitives if their code cannot + * be satisfied with atomic instructions on the given platform. + */ +static_assert(__atomic_always_lock_free(1, 0), "eastl::atomic where sizeof(T) == 1 must be lock-free!"); +static_assert(__atomic_always_lock_free(2, 0), "eastl::atomic where sizeof(T) == 2 must be lock-free!"); +static_assert(__atomic_always_lock_free(4, 0), "eastl::atomic where sizeof(T) == 4 must be lock-free!"); +#if EA_PLATFORM_PTR_SIZE == 8 + static_assert(__atomic_always_lock_free(8, 0), "eastl::atomic where sizeof(T) == 8 must be lock-free!"); +#endif + +/** + * NOTE: + * + * The following can fail on gcc/clang on 64-bit systems. + * Firstly, it depends on the -march setting on clang whether or not it calls out to libatomic for 128-bit operations. + * Second, gcc always calls out to libatomic for 128-bit atomics. It is unclear if it uses locks + * or tries to look at the cpuid and use cmpxchg16b if its available. + * gcc mailing lists argue that since load must be implemented with cmpxchg16b, then the __atomic bultin + * cannot be used in read-only memory which is why they always call out to libatomic. + * There is no way to tell gcc to not do that, unfortunately. + * We don't care about the read-only restriction because our eastl::atomic object is mutable + * and also msvc doesn't enforce this restriction thus to be fully platform agnostic we cannot either. + * + * Therefore, the follow static_assert is commented out for the time being, as it always fails on these compilers. + * We still guarantee 128-bit atomics are lock-free by handrolling the inline assembly ourselves. + * + * static_assert(__atomic_always_lock_free(16, 0), "eastl::atomic where sizeof(T) == 16 must be lock-free!"); + */ + +/** + * NOTE: + * + * Why do we do the cast to the unsigned fixed width types for every operation even though gcc/clang builtins are generics? + * Well gcc/clang correctly-incorrectly call out to libatomic and do locking on user types that may be potentially misaligned. + * struct UserType { uint8_t a,b; }; This given struct is 2 bytes in size but has only 1 byte alignment. + * gcc/clang cannot and doesn't know that we always guarantee every type T is size aligned within eastl::atomic. + * Therefore it always emits calls into libatomic and does locking for structs like these which we do not want. + * Therefore you'll notice we always cast each atomic ptr type to the equivalent unsigned fixed width type when doing the atomic operations. + * This ensures all user types are size aligned and thus are lock free. + */ + + +///////////////////////////////////////////////////////////////////////////////// + + +#define EASTL_COMPILER_ATOMIC_HAS_8BIT +#define EASTL_COMPILER_ATOMIC_HAS_16BIT +#define EASTL_COMPILER_ATOMIC_HAS_32BIT +#define EASTL_COMPILER_ATOMIC_HAS_64BIT + +#if EA_PLATFORM_PTR_SIZE == 8 + #define EASTL_COMPILER_ATOMIC_HAS_128BIT +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_8 uint8_t +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_16 uint16_t +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_32 uint32_t +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_64 uint64_t +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_128 __uint128_t + + +///////////////////////////////////////////////////////////////////////////////// + + +#define EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, fetchIntrinsic, type, ret, ptr, val, gccMemoryOrder) \ + { \ + integralType retIntegral; \ + integralType valIntegral = EASTL_ATOMIC_TYPE_PUN_CAST(integralType, (val)); \ + \ + retIntegral = fetchIntrinsic(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), valIntegral, gccMemoryOrder); \ + \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, retIntegral); \ + } + +#define EASTL_GCC_ATOMIC_CMPXCHG_INTRIN_N(integralType, type, ret, ptr, expected, desired, weak, successOrder, failOrder) \ + ret = __atomic_compare_exchange(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), \ + EASTL_ATOMIC_INTEGRAL_CAST(integralType, (expected)), \ + EASTL_ATOMIC_INTEGRAL_CAST(integralType, &(desired)), \ + weak, successOrder, failOrder) + +#define EASTL_GCC_ATOMIC_EXCHANGE_INTRIN_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + { \ + integralType retIntegral; \ + integralType valIntegral = EASTL_ATOMIC_TYPE_PUN_CAST(integralType, (val)); \ + \ + __atomic_exchange(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), \ + &valIntegral, &retIntegral, gccMemoryOrder); \ + \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, retIntegral); \ + } + + +///////////////////////////////////////////////////////////////////////////////// + + +#include "compiler_gcc_fetch_add.h" +#include "compiler_gcc_fetch_sub.h" + +#include "compiler_gcc_fetch_and.h" +#include "compiler_gcc_fetch_xor.h" +#include "compiler_gcc_fetch_or.h" + +#include "compiler_gcc_add_fetch.h" +#include "compiler_gcc_sub_fetch.h" + +#include "compiler_gcc_and_fetch.h" +#include "compiler_gcc_xor_fetch.h" +#include "compiler_gcc_or_fetch.h" + +#include "compiler_gcc_exchange.h" + +#include "compiler_gcc_cmpxchg_weak.h" +#include "compiler_gcc_cmpxchg_strong.h" + +#include "compiler_gcc_load.h" +#include "compiler_gcc_store.h" + +#include "compiler_gcc_barrier.h" + +#include "compiler_gcc_cpu_pause.h" + +#include "compiler_gcc_signal_fence.h" + +#include "compiler_gcc_thread_fence.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_add_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_add_fetch.h new file mode 100644 index 000000000..1d19196b1 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_add_fetch.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_ADD_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_ADD_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_ADD_FETCH_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_add_fetch, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_ADD_FETCH_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_ADD_FETCH_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_ADD_FETCH_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_ADD_FETCH_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_ADD_FETCH_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_ADD_FETCH_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_ADD_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_ADD_FETCH_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_ADD_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_and_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_and_fetch.h new file mode 100644 index 000000000..a35307f01 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_and_fetch.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_AND_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_AND_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_AND_FETCH_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_and_fetch, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_AND_FETCH_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_AND_FETCH_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_AND_FETCH_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_AND_FETCH_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_AND_FETCH_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_AND_FETCH_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_AND_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_AND_FETCH_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_AND_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_barrier.h new file mode 100644 index 000000000..64e8e541d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_barrier.h @@ -0,0 +1,30 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_COMPILER_BARRIER() +// +#define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER() \ + __asm__ __volatile__ ("" ::: "memory") + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY(const T&, type) +// +#define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY(val, type) \ + __asm__ __volatile__ ("" : /* Output Operands */ : "r"(&(val)) : "memory") + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_cmpxchg_strong.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_cmpxchg_strong.h new file mode 100644 index 000000000..3e47cf2ed --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_cmpxchg_strong.h @@ -0,0 +1,182 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_CMPXCHG_STRONG_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_CMPXCHG_STRONG_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_CMPXCHG_STRONG_N(integralType, type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_INTRIN_N(integralType, type, ret, ptr, expected, desired, false, successOrder, failOrder) + + +#define EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_N(uint8_t, type, ret, ptr, expected, desired, successOrder, failOrder) + +#define EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_N(uint16_t, type, ret, ptr, expected, desired, successOrder, failOrder) + +#define EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_N(uint32_t, type, ret, ptr, expected, desired, successOrder, failOrder) + +#define EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_N(uint64_t, type, ret, ptr, expected, desired, successOrder, failOrder) + +#define EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_N(__uint128_t, type, ret, ptr, expected, desired, successOrder, failOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_CMPXCHG_STRONG_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_cmpxchg_weak.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_cmpxchg_weak.h new file mode 100644 index 000000000..f55fe3a36 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_cmpxchg_weak.h @@ -0,0 +1,182 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_CMPXCHG_WEAK_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_CMPXCHG_WEAK_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_CMPXCHG_WEAK_N(integralType, type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_INTRIN_N(integralType, type, ret, ptr, expected, desired, true, successOrder, failOrder) + + +#define EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_N(uint8_t, type, ret, ptr, expected, desired, successOrder, failOrder) + +#define EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_N(uint16_t, type, ret, ptr, expected, desired, successOrder, failOrder) + +#define EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_N(uint32_t, type, ret, ptr, expected, desired, successOrder, failOrder) + +#define EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_N(uint64_t, type, ret, ptr, expected, desired, successOrder, failOrder) + +#define EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, successOrder, failOrder) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_N(__uint128_t, type, ret, ptr, expected, desired, successOrder, failOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, __ATOMIC_RELAXED, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, __ATOMIC_RELEASE, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_8(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16(type,ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_16(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_32(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_64(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_GCC_ATOMIC_CMPXCHG_WEAK_128(type, ret, ptr, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_CMPXCHG_WEAK_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_cpu_pause.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_cpu_pause.h new file mode 100644 index 000000000..9d4ac35eb --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_cpu_pause.h @@ -0,0 +1,31 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_CPU_PAUSE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_CPU_PAUSE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CPU_PAUSE() +// +#if defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64) + + #define EASTL_COMPILER_ATOMIC_CPU_PAUSE() \ + __asm__ __volatile__ ("pause") + +#elif defined(EA_PROCESSOR_ARM32) || defined(EA_PROCESSOR_ARM64) + + #define EASTL_COMPILER_ATOMIC_CPU_PAUSE() \ + __asm__ __volatile__ ("yield") + +#endif + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_CPU_PAUSE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_exchange.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_exchange.h new file mode 100644 index 000000000..a33255476 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_exchange.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_EXCHANGE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_EXCHANGE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_EXCHANGE_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_EXCHANGE_INTRIN_N(integralType, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_EXCHANGE_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_EXCHANGE_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_EXCHANGE_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_EXCHANGE_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_EXCHANGE_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_EXCHANGE_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_EXCHANGE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_add.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_add.h new file mode 100644 index 000000000..98abbb837 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_add.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_ADD_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_ADD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_FETCH_ADD_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_fetch_add, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_ADD_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_ADD_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_ADD_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_ADD_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_ADD_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_ADD_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_ADD_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_ADD_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_ADD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_and.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_and.h new file mode 100644 index 000000000..0dfb81dba --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_and.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_AND_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_AND_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_FETCH_AND_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_fetch_and, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_AND_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_AND_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_AND_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_AND_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_AND_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_AND_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_AND_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_AND_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_AND_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_or.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_or.h new file mode 100644 index 000000000..ba259b743 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_or.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_OR_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_OR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_FETCH_OR_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_fetch_or, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_OR_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_OR_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_OR_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_OR_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_OR_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_OR_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_OR_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_OR_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_OR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_sub.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_sub.h new file mode 100644 index 000000000..c8be225e7 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_sub.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_SUB_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_SUB_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_FETCH_SUB_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_fetch_sub, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_SUB_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_SUB_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_SUB_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_SUB_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_SUB_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_SUB_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_SUB_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_SUB_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_SUB_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_xor.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_xor.h new file mode 100644 index 000000000..4ec6d6769 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_fetch_xor.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_XOR_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_XOR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_FETCH_XOR_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_fetch_xor, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_XOR_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_XOR_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_XOR_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_XOR_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_FETCH_XOR_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_XOR_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_XOR_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_FETCH_XOR_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_FETCH_XOR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_load.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_load.h new file mode 100644 index 000000000..a4a3ebf1f --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_load.h @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_LOAD_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_LOAD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_LOAD_N(integralType, type, ret, ptr, gccMemoryOrder) \ + { \ + integralType retIntegral; \ + __atomic_load(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), &retIntegral, gccMemoryOrder); \ + \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, retIntegral); \ + } + +#define EASTL_GCC_ATOMIC_LOAD_8(type, ret, ptr, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_LOAD_N(uint8_t, type, ret, ptr, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_LOAD_16(type, ret, ptr, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_LOAD_N(uint16_t, type, ret, ptr, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_LOAD_32(type, ret, ptr, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_LOAD_N(uint32_t, type, ret, ptr, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_LOAD_64(type, ret, ptr, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_LOAD_N(uint64_t, type, ret, ptr, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_LOAD_128(type, ret, ptr, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_LOAD_N(__uint128_t, type, ret, ptr, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_LOAD_*_N(type, type ret, type * ptr) +// +#define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_8(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_8(type, ret, ptr, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_16(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_16(type, ret, ptr, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_32(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_32(type, ret, ptr, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_64(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_64(type, ret, ptr, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_LOAD_RELAXED_128(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_128(type, ret, ptr, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_8(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_8(type, ret, ptr, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_16(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_16(type, ret, ptr, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_32(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_32(type, ret, ptr, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_64(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_64(type, ret, ptr, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_LOAD_ACQUIRE_128(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_128(type, ret, ptr, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_8(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_8(type, ret, ptr, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_16(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_16(type, ret, ptr, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_32(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_32(type, ret, ptr, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_64(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_64(type, ret, ptr, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_LOAD_SEQ_CST_128(type, ret, ptr) \ + EASTL_GCC_ATOMIC_LOAD_128(type, ret, ptr, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_LOAD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_or_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_or_fetch.h new file mode 100644 index 000000000..9e4db3e16 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_or_fetch.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_OR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_OR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_OR_FETCH_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_or_fetch, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_OR_FETCH_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_OR_FETCH_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_OR_FETCH_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_OR_FETCH_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_OR_FETCH_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_OR_FETCH_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_OR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_OR_FETCH_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_OR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_signal_fence.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_signal_fence.h new file mode 100644 index 000000000..16dff14ff --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_signal_fence.h @@ -0,0 +1,38 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_SIGNAL_FENCE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_SIGNAL_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_SIGNAL_FENCE(gccMemoryOrder) \ + __atomic_signal_fence(gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_*() +// +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELAXED() \ + EASTL_GCC_ATOMIC_SIGNAL_FENCE(__ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQUIRE() \ + EASTL_GCC_ATOMIC_SIGNAL_FENCE(__ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELEASE() \ + EASTL_GCC_ATOMIC_SIGNAL_FENCE(__ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQ_REL() \ + EASTL_GCC_ATOMIC_SIGNAL_FENCE(__ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_SEQ_CST() \ + EASTL_GCC_ATOMIC_SIGNAL_FENCE(__ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_SIGNAL_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_store.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_store.h new file mode 100644 index 000000000..04a28ac48 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_store.h @@ -0,0 +1,89 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_STORE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_STORE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_STORE_N(integralType, ptr, val, gccMemoryOrder) \ + { \ + integralType valIntegral = EASTL_ATOMIC_TYPE_PUN_CAST(integralType, (val)); \ + __atomic_store(EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), &valIntegral, gccMemoryOrder); \ + } + + +#define EASTL_GCC_ATOMIC_STORE_8(ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_STORE_N(uint8_t, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_STORE_16(ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_STORE_N(uint16_t, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_STORE_32(ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_STORE_N(uint32_t, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_STORE_64(ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_STORE_N(uint64_t, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_STORE_128(ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_STORE_N(__uint128_t, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_STORE_*_N(type, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_STORE_RELAXED_8(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_8(ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_STORE_RELAXED_16(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_16(ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_STORE_RELAXED_32(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_32(ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_STORE_RELAXED_64(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_64(ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_STORE_RELAXED_128(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_128(ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_STORE_RELEASE_8(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_8(ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_STORE_RELEASE_16(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_16(ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_STORE_RELEASE_32(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_32(ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_STORE_RELEASE_64(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_64(ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_STORE_RELEASE_128(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_128(ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_8(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_8(ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_16(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_16(ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_32(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_32(ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_64(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_64(ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_STORE_SEQ_CST_128(type, ptr, val) \ + EASTL_GCC_ATOMIC_STORE_128(ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_STORE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_sub_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_sub_fetch.h new file mode 100644 index 000000000..62f8cd918 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_sub_fetch.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_SUB_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_SUB_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_SUB_FETCH_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_sub_fetch, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_SUB_FETCH_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_SUB_FETCH_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_SUB_FETCH_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_SUB_FETCH_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_SUB_FETCH_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_SUB_FETCH_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_SUB_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_SUB_FETCH_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_SUB_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_thread_fence.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_thread_fence.h new file mode 100644 index 000000000..0dd005e4c --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_thread_fence.h @@ -0,0 +1,38 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_THREAD_FENCE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_THREAD_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_THREAD_FENCE(gccMemoryOrder) \ + __atomic_thread_fence(gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_THREAD_FENCE_*() +// +#define EASTL_COMPILER_ATOMIC_THREAD_FENCE_RELAXED() \ + EASTL_GCC_ATOMIC_THREAD_FENCE(__ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_THREAD_FENCE_ACQUIRE() \ + EASTL_GCC_ATOMIC_THREAD_FENCE(__ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_THREAD_FENCE_RELEASE() \ + EASTL_GCC_ATOMIC_THREAD_FENCE(__ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_THREAD_FENCE_ACQ_REL() \ + EASTL_GCC_ATOMIC_THREAD_FENCE(__ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_THREAD_FENCE_SEQ_CST() \ + EASTL_GCC_ATOMIC_THREAD_FENCE(__ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_THREAD_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_xor_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_xor_fetch.h new file mode 100644 index 000000000..4827d79fc --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/gcc/compiler_gcc_xor_fetch.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_GCC_XOR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_GCC_XOR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_GCC_ATOMIC_XOR_FETCH_N(integralType, type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_FETCH_INTRIN_N(integralType, __atomic_xor_fetch, type, ret, ptr, val, gccMemoryOrder) + + +#define EASTL_GCC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_XOR_FETCH_N(uint8_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_XOR_FETCH_N(uint16_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_XOR_FETCH_N(uint32_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_XOR_FETCH_N(uint64_t, type, ret, ptr, val, gccMemoryOrder) + +#define EASTL_GCC_ATOMIC_XOR_FETCH_128(type, ret, ptr, val, gccMemoryOrder) \ + EASTL_GCC_ATOMIC_XOR_FETCH_N(__uint128_t, type, ret, ptr, val, gccMemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_XOR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, __ATOMIC_RELAXED) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_128(type, ret, ptr, val, __ATOMIC_RELAXED) + + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, __ATOMIC_RELEASE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_128(type, ret, ptr, val, __ATOMIC_RELEASE) + + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, __ATOMIC_ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_128(type, ret, ptr, val, __ATOMIC_ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, __ATOMIC_SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_GCC_ATOMIC_XOR_FETCH_128(type, ret, ptr, val, __ATOMIC_SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_GCC_XOR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc.h new file mode 100644 index 000000000..6df8c05f1 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc.h @@ -0,0 +1,260 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +EA_DISABLE_ALL_VC_WARNINGS(); +#include +#include +EA_RESTORE_ALL_VC_WARNINGS(); + + +///////////////////////////////////////////////////////////////////////////////// + + +#define EASTL_COMPILER_ATOMIC_HAS_8BIT +#define EASTL_COMPILER_ATOMIC_HAS_16BIT +#define EASTL_COMPILER_ATOMIC_HAS_32BIT +#define EASTL_COMPILER_ATOMIC_HAS_64BIT + +#if EA_PLATFORM_PTR_SIZE == 8 + #define EASTL_COMPILER_ATOMIC_HAS_128BIT +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_8 char +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_16 short +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_32 long +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_64 __int64 + +namespace eastl +{ + +namespace internal +{ + +struct FixedWidth128 +{ + __int64 value[2]; +}; + +} // namespace internal + +} // namespace eastl + +#define EASTL_COMPILER_ATOMIC_FIXED_WIDTH_TYPE_128 eastl::internal::FixedWidth128 + + +///////////////////////////////////////////////////////////////////////////////// + + +/** + * NOTE: + * + * Unfortunately MSVC Intrinsics depend on the architecture + * that we are compiling for. + * These are some indirection macros to make our lives easier and + * ensure the least possible amount of copy-paste to reduce programmer errors. + * + * All compiler implementations end up deferring to the below macros. + */ +#if defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64) + + + #define EASTL_MSVC_ATOMIC_FETCH_OP(ret, ptr, val, MemoryOrder, Intrinsic) \ + ret = Intrinsic(ptr, val) + + #define EASTL_MSVC_ATOMIC_EXCHANGE_OP(ret, ptr, val, MemoryOrder, Intrinsic) \ + ret = Intrinsic(ptr, val) + + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP(ret, ptr, comparand, exchange, MemoryOrder, Intrinsic) \ + ret = Intrinsic(ptr, exchange, comparand) + + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128_OP(ret, ptr, comparandResult, exchangeHigh, exchangeLow, MemoryOrder) \ + ret = _InterlockedCompareExchange128_np(ptr, exchangeHigh, exchangeLow, comparandResult) + + +#elif defined(EA_PROCESSOR_ARM32) || defined(EA_PROCESSOR_ARM64) + + + #define EASTL_MSVC_INTRINSIC_RELAXED(Intrinsic) \ + EA_PREPROCESSOR_JOIN(Intrinsic, _nf) + + #define EASTL_MSVC_INTRINSIC_ACQUIRE(Intrinsic) \ + EA_PREPROCESSOR_JOIN(Intrinsic, _acq) + + #define EASTL_MSVC_INTRINSIC_RELEASE(Intrinsic) \ + EA_PREPROCESSOR_JOIN(Intrinsic, _rel) + + #define EASTL_MSVC_INTRINSIC_ACQ_REL(Intrinsic) \ + Intrinsic + + #define EASTL_MSVC_INTRINSIC_SEQ_CST(Intrinsic) \ + Intrinsic + + + #define EASTL_MSVC_ATOMIC_FETCH_OP(ret, ptr, val, MemoryOrder, Intrinsic) \ + ret = EA_PREPROCESSOR_JOIN(EASTL_MSVC_INTRINSIC_, MemoryOrder)(Intrinsic)(ptr, val) + + #define EASTL_MSVC_ATOMIC_EXCHANGE_OP(ret, ptr, val, MemoryOrder, Intrinsic) \ + ret = EA_PREPROCESSOR_JOIN(EASTL_MSVC_INTRINSIC_, MemoryOrder)(Intrinsic)(ptr, val) + + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP(ret, ptr, comparand, exchange, MemoryOrder, Intrinsic) \ + ret = EA_PREPROCESSOR_JOIN(EASTL_MSVC_INTRINSIC_, MemoryOrder)(Intrinsic)(ptr, exchange, comparand) + + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128_OP(ret, ptr, comparandResult, exchangeHigh, exchangeLow, MemoryOrder) \ + ret = EA_PREPROCESSOR_JOIN(EASTL_MSVC_INTRINSIC_, MemoryOrder)(_InterlockedCompareExchange128)(ptr, exchangeHigh, exchangeLow, comparandResult) + + +#endif + + +///////////////////////////////////////////////////////////////////////////////// + + +#define EASTL_MSVC_NOP_POST_INTRIN_COMPUTE(ret, lhs, rhs) + +#define EASTL_MSVC_NOP_PRE_INTRIN_COMPUTE(ret, val) \ + ret = (val) + + +#define EASTL_MSVC_ATOMIC_FETCH_INTRIN_N(integralType, fetchIntrinsic, type, ret, ptr, val, MemoryOrder, PRE_INTRIN_COMPUTE, POST_INTRIN_COMPUTE) \ + { \ + integralType retIntegral; \ + type valCompute; \ + \ + PRE_INTRIN_COMPUTE(valCompute, (val)); \ + const integralType valIntegral = EASTL_ATOMIC_TYPE_PUN_CAST(integralType, valCompute); \ + \ + EASTL_MSVC_ATOMIC_FETCH_OP(retIntegral, EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), \ + valIntegral, MemoryOrder, fetchIntrinsic); \ + \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, retIntegral); \ + POST_INTRIN_COMPUTE(ret, ret, (val)); \ + } + +#define EASTL_MSVC_ATOMIC_EXCHANGE_INTRIN_N(integralType, exchangeIntrinsic, type, ret, ptr, val, MemoryOrder) \ + { \ + integralType retIntegral; \ + EASTL_MSVC_ATOMIC_EXCHANGE_OP(retIntegral, EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), \ + EASTL_ATOMIC_TYPE_PUN_CAST(integralType, (val)), MemoryOrder, \ + exchangeIntrinsic); \ + \ + ret = EASTL_ATOMIC_TYPE_PUN_CAST(type, retIntegral); \ + } + +#define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_N(integralType, cmpxchgStrongIntrinsic, type, ret, ptr, expected, desired, MemoryOrder) \ + { \ + integralType comparandIntegral = EASTL_ATOMIC_TYPE_PUN_CAST(integralType, *(expected)); \ + integralType oldIntegral; \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP(oldIntegral, EASTL_ATOMIC_VOLATILE_INTEGRAL_CAST(integralType, (ptr)), \ + comparandIntegral, EASTL_ATOMIC_TYPE_PUN_CAST(integralType, (desired)), \ + MemoryOrder, cmpxchgStrongIntrinsic); \ + \ + if (oldIntegral == comparandIntegral) \ + { \ + ret = true; \ + } \ + else \ + { \ + *(expected) = EASTL_ATOMIC_TYPE_PUN_CAST(type, oldIntegral); \ + ret = false; \ + } \ + } + +/** + * In my own opinion, I found the wording on Microsoft docs a little confusing. + * ExchangeHigh means the top 8 bytes so (ptr + 8). + * ExchangeLow means the low 8 butes so (ptr). + * Endianness does not matter since we are just loading data and comparing data. + * Thought of as memcpy() and memcmp() function calls whereby the layout of the + * data itself is irrelevant. + * Only after we type pun back to the original type, and load from memory does + * the layout of the data matter again. + */ +#define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_128(type, ret, ptr, expected, desired, MemoryOrder) \ + { \ + union TypePun \ + { \ + type templateType; \ + \ + struct exchange128 \ + { \ + __int64 value[2]; \ + }; \ + \ + struct exchange128 exchangePun; \ + }; \ + \ + union TypePun typePun = { (desired) }; \ + \ + unsigned char cmpxchgRetChar; \ + cmpxchgRetChar = EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128_OP(cmpxchgRetChar, EASTL_ATOMIC_VOLATILE_TYPE_CAST(__int64, (ptr)), \ + EASTL_ATOMIC_TYPE_CAST(__int64, (expected)), \ + typePun.exchangePun.value[1], typePun.exchangePun.value[0], \ + MemoryOrder); \ + \ + ret = static_cast(cmpxchgRetChar); \ + } + + +///////////////////////////////////////////////////////////////////////////////// + + +#define EASTL_MSVC_ATOMIC_FETCH_OP_N(integralType, fetchIntrinsic, type, ret, ptr, val, MemoryOrder, PRE_INTRIN_COMPUTE) \ + EASTL_MSVC_ATOMIC_FETCH_INTRIN_N(integralType, fetchIntrinsic, type, ret, ptr, val, MemoryOrder, PRE_INTRIN_COMPUTE, EASTL_MSVC_NOP_POST_INTRIN_COMPUTE) + +#define EASTL_MSVC_ATOMIC_OP_FETCH_N(integralType, fetchIntrinsic, type, ret, ptr, val, MemoryOrder, PRE_INTRIN_COMPUTE, POST_INTRIN_COMPUTE) \ + EASTL_MSVC_ATOMIC_FETCH_INTRIN_N(integralType, fetchIntrinsic, type, ret, ptr, val, MemoryOrder, PRE_INTRIN_COMPUTE, POST_INTRIN_COMPUTE) + +#define EASTL_MSVC_ATOMIC_EXCHANGE_OP_N(integralType, exchangeIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_EXCHANGE_INTRIN_N(integralType, exchangeIntrinsic, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP_N(integralType, cmpxchgStrongIntrinsic, type, ret, ptr, expected, desired, MemoryOrder) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_N(integralType, cmpxchgStrongIntrinsic, type, ret, ptr, expected, desired, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP_128(type, ret, ptr, expected, desired, MemoryOrder) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_128(type, ret, ptr, expected, desired, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// + + +#include "compiler_msvc_fetch_add.h" +#include "compiler_msvc_fetch_sub.h" + +#include "compiler_msvc_fetch_and.h" +#include "compiler_msvc_fetch_xor.h" +#include "compiler_msvc_fetch_or.h" + +#include "compiler_msvc_add_fetch.h" +#include "compiler_msvc_sub_fetch.h" + +#include "compiler_msvc_and_fetch.h" +#include "compiler_msvc_xor_fetch.h" +#include "compiler_msvc_or_fetch.h" + +#include "compiler_msvc_exchange.h" + +#include "compiler_msvc_cmpxchg_weak.h" +#include "compiler_msvc_cmpxchg_strong.h" + +#include "compiler_msvc_barrier.h" + +#include "compiler_msvc_cpu_pause.h" + +#include "compiler_msvc_signal_fence.h" + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_add_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_add_fetch.h new file mode 100644 index 000000000..12fc4b04a --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_add_fetch.h @@ -0,0 +1,104 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_ADD_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_ADD_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_MSVC_ADD_FETCH_POST_INTRIN_COMPUTE(ret, val, addend) \ + ret = (val) + (addend) + +#define EASTL_MSVC_ATOMIC_ADD_FETCH_N(integralType, addIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_OP_FETCH_N(integralType, addIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_NOP_PRE_INTRIN_COMPUTE, EASTL_MSVC_ADD_FETCH_POST_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_N(char, _InterlockedExchangeAdd8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_N(short, _InterlockedExchangeAdd16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_N(long, _InterlockedExchangeAdd, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_N(__int64, _InterlockedExchangeAdd64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_ADD_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_ADD_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_ADD_FETCH_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_ADD_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_and_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_and_fetch.h new file mode 100644 index 000000000..70ec577fd --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_and_fetch.h @@ -0,0 +1,121 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_AND_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_AND_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#if defined(EA_PROCESSOR_X86_64) + + #define EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_8 _InterlockedAnd8_np + #define EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_16 _InterlockedAnd16_np + #define EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_32 _InterlockedAnd_np + #define EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_64 _InterlockedAnd64_np + +#else + + #define EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_8 _InterlockedAnd8 + #define EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_16 _InterlockedAnd16 + #define EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_32 _InterlockedAnd + #define EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_64 _InterlockedAnd64 + +#endif + + +#define EASTL_MSVC_AND_FETCH_POST_INTRIN_COMPUTE(ret, val, andend) \ + ret = (val) & (andend) + +#define EASTL_MSVC_ATOMIC_AND_FETCH_N(integralType, andIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_OP_FETCH_N(integralType, andIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_NOP_PRE_INTRIN_COMPUTE, EASTL_MSVC_AND_FETCH_POST_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_AND_FETCH_N(char, EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_AND_FETCH_N(short, EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_AND_FETCH_N(long, EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_32, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_AND_FETCH_N(__int64, EASTL_MSVC_ATOMIC_AND_FETCH_INTRIN_64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_AND_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_AND_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_AND_FETCH_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_AND_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_barrier.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_barrier.h new file mode 100644 index 000000000..90b78a658 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_barrier.h @@ -0,0 +1,33 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_BARRIER_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_BARRIER_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_COMPILER_BARRIER() +// +#define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER() \ + EA_DISABLE_CLANG_WARNING(-Wdeprecated-declarations) \ + _ReadWriteBarrier() \ + EA_RESTORE_CLANG_WARNING() + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY(const T&, type) +// +#define EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY(val, type) \ + EASTL_COMPILER_ATOMIC_COMPILER_BARRIER_DATA_DEPENDENCY_FUNC(const_cast(eastl::addressof((val)))); \ + EASTL_ATOMIC_COMPILER_BARRIER() + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_BARRIER_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_strong.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_strong.h new file mode 100644 index 000000000..8217f2320 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_strong.h @@ -0,0 +1,194 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CMPXCHG_STRONG_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CMPXCHG_STRONG_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#if defined(EA_PROCESSOR_X86_64) + + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_8 _InterlockedCompareExchange8 + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_16 _InterlockedCompareExchange16_np + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_32 _InterlockedCompareExchange_np + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_64 _InterlockedCompareExchange64_np + +#else + + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_8 _InterlockedCompareExchange8 + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_16 _InterlockedCompareExchange16 + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_32 _InterlockedCompareExchange + #define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_64 _InterlockedCompareExchange64 + +#endif + + +#define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, MemoryOrder) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP_N(char, EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_8, type, ret, ptr, expected, desired, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, MemoryOrder) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP_N(short, EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_16, type, ret, ptr, expected, desired, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, MemoryOrder) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP_N(long, EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_32, type, ret, ptr, expected, desired, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, MemoryOrder) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP_N(__int64, EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_INTRIN_64, type, ret, ptr, expected, desired, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, MemoryOrder) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_OP_128(type, ret, ptr, expected, desired, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, RELAXED) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, RELEASE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, RELEASE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, RELEASE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, RELEASE) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, SEQ_CST) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, SEQ_CST) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_8(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_16(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_32(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_64(type, ret, ptr, expected, desired, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_MSVC_ATOMIC_CMPXCHG_STRONG_128(type, ret, ptr, expected, desired, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CMPXCHG_STRONG_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_weak.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_weak.h new file mode 100644 index 000000000..8f4147ac0 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cmpxchg_weak.h @@ -0,0 +1,162 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CMPXCHG_WEAK_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CMPXCHG_WEAK_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_*_*_N(type, bool ret, type * ptr, type * expected, type desired) +// +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELAXED_RELAXED_128(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_RELAXED_128(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQUIRE_ACQUIRE_128(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_RELEASE_RELAXED_128(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_RELAXED_128(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_ACQ_REL_ACQUIRE_128(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_RELAXED_128(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_ACQUIRE_128(type, ret, ptr, expected, desired) + + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_8(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_16(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_32(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_64(type, ret, ptr, expected, desired) + +#define EASTL_COMPILER_ATOMIC_CMPXCHG_WEAK_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) \ + EASTL_COMPILER_ATOMIC_CMPXCHG_STRONG_SEQ_CST_SEQ_CST_128(type, ret, ptr, expected, desired) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CMPXCHG_WEAK_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cpu_pause.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cpu_pause.h new file mode 100644 index 000000000..720701ab8 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_cpu_pause.h @@ -0,0 +1,27 @@ +///////////////////////////////////////////////////////////////////////////////// +// copyright (c) electronic arts inc. all rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CPU_PAUSE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CPU_PAUSE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_CPU_PAUSE() +// +// NOTE: +// Rather obscure macro in Windows.h that expands to pause or rep; nop on +// compatible x86 cpus or the arm yield on compatible arm processors. +// This is nicer than switching on platform specific intrinsics. +// +#define EASTL_COMPILER_ATOMIC_CPU_PAUSE() \ + YieldProcessor() + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_CPU_PAUSE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_exchange.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_exchange.h new file mode 100644 index 000000000..323f1fae3 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_exchange.h @@ -0,0 +1,125 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_EXCHANGE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_EXCHANGE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_MSVC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_EXCHANGE_OP_N(char, _InterlockedExchange8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_EXCHANGE_OP_N(short, _InterlockedExchange16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_EXCHANGE_OP_N(long, _InterlockedExchange, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_EXCHANGE_OP_N(__int64, _InterlockedExchange64, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, MemoryOrder) \ + { \ + bool cmpxchgRet; \ + /* This is intentionally a non-atomic 128-bit load which may observe shearing. */ \ + /* Either we do not observe *(ptr) but then the cmpxchg will fail and the observed */ \ + /* atomic load will be returned. Or the non-atomic load got lucky and the cmpxchg succeeds */ \ + /* because the observed value equals the value in *(ptr) thus we optimistically do a non-atomic load. */ \ + ret = *(ptr); \ + do \ + { \ + EA_PREPROCESSOR_JOIN(EA_PREPROCESSOR_JOIN(EASTL_ATOMIC_CMPXCHG_STRONG_, MemoryOrder), _128)(type, cmpxchgRet, ptr, &(ret), val); \ + } while (!cmpxchgRet); \ + } + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_EXCHANGE_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELAXED_128(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQUIRE_128(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_RELEASE_128(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_ACQ_REL_128(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_64(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_EXCHANGE_SEQ_CST_128(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_EXCHANGE_128(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_EXCHANGE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_add.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_add.h new file mode 100644 index 000000000..a951740e9 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_add.h @@ -0,0 +1,101 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_ADD_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_ADD_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_MSVC_ATOMIC_FETCH_ADD_N(integralType, addIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_OP_N(integralType, addIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_NOP_PRE_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_N(char, _InterlockedExchangeAdd8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_N(short, _InterlockedExchangeAdd16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_N(long, _InterlockedExchangeAdd, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_N(__int64, _InterlockedExchangeAdd64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_ADD_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_ADD_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_ADD_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_ADD_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_and.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_and.h new file mode 100644 index 000000000..96f789424 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_and.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_AND_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_AND_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#if defined(EA_PROCESSOR_X86_64) + + #define EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_8 _InterlockedAnd8_np + #define EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_16 _InterlockedAnd16_np + #define EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_32 _InterlockedAnd_np + #define EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_64 _InterlockedAnd64_np + +#else + + #define EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_8 _InterlockedAnd8 + #define EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_16 _InterlockedAnd16 + #define EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_32 _InterlockedAnd + #define EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_64 _InterlockedAnd64 + +#endif + + +#define EASTL_MSVC_ATOMIC_FETCH_AND_N(integralType, andIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_OP_N(integralType, andIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_NOP_PRE_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_AND_N(char, EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_AND_N(short, EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_AND_N(long, EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_32, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_AND_N(__int64, EASTL_MSVC_ATOMIC_FETCH_AND_INTRIN_64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_AND_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_AND_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_AND_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_AND_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_or.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_or.h new file mode 100644 index 000000000..2792fc3de --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_or.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_OR_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_OR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#if defined(EA_PROCESSOR_X86_64) + + #define EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_8 _InterlockedOr8_np + #define EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_16 _InterlockedOr16_np + #define EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_32 _InterlockedOr_np + #define EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_64 _InterlockedOr64_np + +#else + + #define EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_8 _InterlockedOr8 + #define EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_16 _InterlockedOr16 + #define EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_32 _InterlockedOr + #define EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_64 _InterlockedOr64 + +#endif + + +#define EASTL_MSVC_ATOMIC_FETCH_OR_N(integralType, orIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_OP_N(integralType, orIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_NOP_PRE_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_OR_N(char, EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_OR_N(short, EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_OR_N(long, EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_32, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_OR_N(long long, EASTL_MSVC_ATOMIC_FETCH_OR_INTRIN_64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_OR_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_OR_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_OR_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_OR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_sub.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_sub.h new file mode 100644 index 000000000..6d5d9e3aa --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_sub.h @@ -0,0 +1,104 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_SUB_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_SUB_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_MSVC_FETCH_SUB_PRE_INTRIN_COMPUTE(ret, val) \ + ret = EASTL_ATOMIC_NEGATE_OPERAND((val)) + +#define EASTL_MSVC_ATOMIC_FETCH_SUB_N(integralType, subIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_OP_N(integralType, subIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_FETCH_SUB_PRE_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_N(char, _InterlockedExchangeAdd8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_N(short, _InterlockedExchangeAdd16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_N(long, _InterlockedExchangeAdd, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_N(__int64, _InterlockedExchangeAdd64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_SUB_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_SUB_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_SUB_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_SUB_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_xor.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_xor.h new file mode 100644 index 000000000..371153e93 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_fetch_xor.h @@ -0,0 +1,118 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_XOR_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_XOR_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#if defined(EA_PROCESSOR_X86_64) + + #define EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_8 _InterlockedXor8_np + #define EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_16 _InterlockedXor16_np + #define EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_32 _InterlockedXor_np + #define EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_64 _InterlockedXor64_np + +#else + + #define EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_8 _InterlockedXor8 + #define EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_16 _InterlockedXor16 + #define EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_32 _InterlockedXor + #define EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_64 _InterlockedXor64 + +#endif + + +#define EASTL_MSVC_ATOMIC_FETCH_XOR_N(integralType, xorIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_OP_N(integralType, xorIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_NOP_PRE_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_N(char, EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_N(short, EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_N(long, EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_32, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_N(__int64, EASTL_MSVC_ATOMIC_FETCH_XOR_INTRIN_64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_FETCH_XOR_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_FETCH_XOR_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_FETCH_XOR_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_FETCH_XOR_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_or_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_or_fetch.h new file mode 100644 index 000000000..c5b5fac34 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_or_fetch.h @@ -0,0 +1,121 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_OR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_OR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#if defined(EA_PROCESSOR_X86_64) + + #define EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_8 _InterlockedOr8_np + #define EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_16 _InterlockedOr16_np + #define EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_32 _InterlockedOr_np + #define EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_64 _InterlockedOr64_np + +#else + + #define EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_8 _InterlockedOr8 + #define EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_16 _InterlockedOr16 + #define EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_32 _InterlockedOr + #define EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_64 _InterlockedOr64 + +#endif + + +#define EASTL_MSVC_OR_FETCH_POST_INTRIN_COMPUTE(ret, val, orend) \ + ret = (val) | (orend) + +#define EASTL_MSVC_ATOMIC_OR_FETCH_N(integralType, orIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_OP_FETCH_N(integralType, orIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_NOP_PRE_INTRIN_COMPUTE, EASTL_MSVC_OR_FETCH_POST_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_OR_FETCH_N(char, EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_OR_FETCH_N(short, EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_OR_FETCH_N(long, EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_32, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_OR_FETCH_N(__int64, EASTL_MSVC_ATOMIC_OR_FETCH_INTRIN_64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_OR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_OR_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_OR_FETCH_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_OR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_signal_fence.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_signal_fence.h new file mode 100644 index 000000000..f35f57722 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_signal_fence.h @@ -0,0 +1,34 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_SIGNAL_FENCE_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_SIGNAL_FENCE_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_*() +// +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELAXED() \ + EASTL_ATOMIC_COMPILER_BARRIER() + +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQUIRE() \ + EASTL_ATOMIC_COMPILER_BARRIER() + +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_RELEASE() \ + EASTL_ATOMIC_COMPILER_BARRIER() + +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_ACQ_REL() \ + EASTL_ATOMIC_COMPILER_BARRIER() + +#define EASTL_COMPILER_ATOMIC_SIGNAL_FENCE_SEQ_CST() \ + EASTL_ATOMIC_COMPILER_BARRIER() + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_SIGNAL_FENCE_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_sub_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_sub_fetch.h new file mode 100644 index 000000000..6fb61e294 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_sub_fetch.h @@ -0,0 +1,107 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_SUB_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_SUB_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#define EASTL_MSVC_SUB_FETCH_PRE_INTRIN_COMPUTE(ret, val) \ + ret = EASTL_ATOMIC_NEGATE_OPERAND((val)) + +#define EASTL_MSVC_SUB_FETCH_POST_INTRIN_COMPUTE(ret, val, subend) \ + ret = (val) - (subend) + +#define EASTL_MSVC_ATOMIC_SUB_FETCH_N(integralType, subIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_OP_FETCH_N(integralType, subIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_SUB_FETCH_PRE_INTRIN_COMPUTE, EASTL_MSVC_SUB_FETCH_POST_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_N(char, _InterlockedExchangeAdd8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_N(short, _InterlockedExchangeAdd16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_N(long, _InterlockedExchangeAdd, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_N(__int64, _InterlockedExchangeAdd64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_SUB_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_SUB_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_SUB_FETCH_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_SUB_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_xor_fetch.h b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_xor_fetch.h new file mode 100644 index 000000000..44ffff90f --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/atomic/compiler/msvc/compiler_msvc_xor_fetch.h @@ -0,0 +1,121 @@ +///////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_XOR_FETCH_H +#define EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_XOR_FETCH_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +#if defined(EA_PROCESSOR_X86_64) + + #define EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_8 _InterlockedXor8_np + #define EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_16 _InterlockedXor16_np + #define EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_32 _InterlockedXor_np + #define EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_64 _InterlockedXor64_np + +#else + + #define EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_8 _InterlockedXor8 + #define EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_16 _InterlockedXor16 + #define EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_32 _InterlockedXor + #define EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_64 _InterlockedXor64 + +#endif + + +#define EASTL_MSVC_XOR_FETCH_POST_INTRIN_COMPUTE(ret, val, xorend) \ + ret = (val) ^ (xorend) + +#define EASTL_MSVC_ATOMIC_XOR_FETCH_N(integralType, xorIntrinsic, type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_OP_FETCH_N(integralType, xorIntrinsic, type, ret, ptr, val, MemoryOrder, \ + EASTL_MSVC_NOP_PRE_INTRIN_COMPUTE, EASTL_MSVC_XOR_FETCH_POST_INTRIN_COMPUTE) + + +#define EASTL_MSVC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_N(char, EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_8, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_N(short, EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_16, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_N(long, EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_32, type, ret, ptr, val, MemoryOrder) + +#define EASTL_MSVC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, MemoryOrder) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_N(__int64, EASTL_MSVC_ATOMIC_XOR_FETCH_INTRIN_64, type, ret, ptr, val, MemoryOrder) + + +///////////////////////////////////////////////////////////////////////////////// +// +// void EASTL_COMPILER_ATOMIC_XOR_FETCH_*_N(type, type ret, type * ptr, type val) +// +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, RELAXED) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELAXED_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, RELAXED) + + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, ACQUIRE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQUIRE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, ACQUIRE) + + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, RELEASE) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_RELEASE_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, RELEASE) + + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, ACQ_REL) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_ACQ_REL_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, ACQ_REL) + + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_8(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_8(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_16(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_16(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_32(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_32(type, ret, ptr, val, SEQ_CST) + +#define EASTL_COMPILER_ATOMIC_XOR_FETCH_SEQ_CST_64(type, ret, ptr, val) \ + EASTL_MSVC_ATOMIC_XOR_FETCH_64(type, ret, ptr, val, SEQ_CST) + + +#endif /* EASTL_ATOMIC_INTERNAL_COMPILER_MSVC_XOR_FETCH_H */ diff --git a/lib/EASTL/include/EASTL/internal/char_traits.h b/lib/EASTL/include/EASTL/internal/char_traits.h new file mode 100644 index 000000000..62fe79b9e --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/char_traits.h @@ -0,0 +1,464 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This file implements similar functionality to char_traits which is part of +// the C++ standard STL library specification. This is intended for internal +// EASTL use only. Functionality can be accessed through the eastl::string or +// eastl::string_view types. +// +// http://en.cppreference.com/w/cpp/string/char_traits +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_CHAR_TRAITS_H +#define EASTL_CHAR_TRAITS_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include // toupper, etc. +#include // memset, etc. +EA_RESTORE_ALL_VC_WARNINGS() + +namespace eastl +{ + /////////////////////////////////////////////////////////////////////////////// + /// DecodePart + /// + /// These implement UTF8/UCS2/UCS4 encoding/decoding. + /// + EASTL_API bool DecodePart(const char*& pSrc, const char* pSrcEnd, char*& pDest, char* pDestEnd); + EASTL_API bool DecodePart(const char*& pSrc, const char* pSrcEnd, char16_t*& pDest, char16_t* pDestEnd); + EASTL_API bool DecodePart(const char*& pSrc, const char* pSrcEnd, char32_t*& pDest, char32_t* pDestEnd); + + EASTL_API bool DecodePart(const char16_t*& pSrc, const char16_t* pSrcEnd, char*& pDest, char* pDestEnd); + EASTL_API bool DecodePart(const char16_t*& pSrc, const char16_t* pSrcEnd, char16_t*& pDest, char16_t* pDestEnd); + EASTL_API bool DecodePart(const char16_t*& pSrc, const char16_t* pSrcEnd, char32_t*& pDest, char32_t* pDestEnd); + + EASTL_API bool DecodePart(const char32_t*& pSrc, const char32_t* pSrcEnd, char*& pDest, char* pDestEnd); + EASTL_API bool DecodePart(const char32_t*& pSrc, const char32_t* pSrcEnd, char16_t*& pDest, char16_t* pDestEnd); + EASTL_API bool DecodePart(const char32_t*& pSrc, const char32_t* pSrcEnd, char32_t*& pDest, char32_t* pDestEnd); + + EASTL_API bool DecodePart(const int*& pSrc, const int* pSrcEnd, char*& pDest, char* pDestEnd); + EASTL_API bool DecodePart(const int*& pSrc, const int* pSrcEnd, char16_t*& pDest, char16_t* pDestEnd); + EASTL_API bool DecodePart(const int*& pSrc, const int* pSrcEnd, char32_t*& pDest, char32_t* pDestEnd); + + #if EA_CHAR8_UNIQUE + bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd); + + bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, char*& pDest, char* pDestEnd); + bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, char16_t*& pDest, char16_t* pDestEnd); + bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, char32_t*& pDest, char32_t* pDestEnd); + + bool DecodePart(const char*& pSrc, const char* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd); + bool DecodePart(const char16_t*& pSrc, const char16_t* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd); + bool DecodePart(const char32_t*& pSrc, const char32_t* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd); + #endif + + #if EA_WCHAR_UNIQUE + bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd); + + bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, char*& pDest, char* pDestEnd); + bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, char16_t*& pDest, char16_t* pDestEnd); + bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, char32_t*& pDest, char32_t* pDestEnd); + + bool DecodePart(const char*& pSrc, const char* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd); + bool DecodePart(const char16_t*& pSrc, const char16_t* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd); + bool DecodePart(const char32_t*& pSrc, const char32_t* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd); + #endif + + #if EA_CHAR8_UNIQUE && EA_WCHAR_UNIQUE + bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd); + bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd); + #endif + + + #if EA_WCHAR_UNIQUE + inline bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd) + { + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + } + + inline bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, char*& pDest, char* pDestEnd) + { + #if (EA_WCHAR_SIZE == 2) + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), pDest, pDestEnd); + #elif (EA_WCHAR_SIZE == 4) + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), pDest, pDestEnd); + #endif + } + + inline bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, char16_t*& pDest, char16_t* pDestEnd) + { + #if (EA_WCHAR_SIZE == 2) + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), pDest, pDestEnd); + #elif (EA_WCHAR_SIZE == 4) + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), pDest, pDestEnd); + #endif + } + + inline bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, char32_t*& pDest, char32_t* pDestEnd) + { + #if (EA_WCHAR_SIZE == 2) + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), pDest, pDestEnd); + #elif (EA_WCHAR_SIZE == 4) + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), pDest, pDestEnd); + #endif + } + + inline bool DecodePart(const char*& pSrc, const char* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd) + { + #if (EA_WCHAR_SIZE == 2) + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #elif (EA_WCHAR_SIZE == 4) + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #endif + } + + inline bool DecodePart(const char16_t*& pSrc, const char16_t* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd) + { + #if (EA_WCHAR_SIZE == 2) + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #elif (EA_WCHAR_SIZE == 4) + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #endif + } + + inline bool DecodePart(const char32_t*& pSrc, const char32_t* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd) + { + #if (EA_WCHAR_SIZE == 2) + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #elif (EA_WCHAR_SIZE == 4) + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #endif + } + #endif + + #if EA_CHAR8_UNIQUE + inline bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd) + { + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + } + + inline bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, char*& pDest, char* pDestEnd) + { + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), pDest, pDestEnd); + } + + inline bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, char16_t*& pDest, char16_t* pDestEnd) + { + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), pDest, pDestEnd); + } + + inline bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, char32_t*& pDest, char32_t* pDestEnd) + { + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), pDest, pDestEnd); + } + + inline bool DecodePart(const char*& pSrc, const char* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd) + { + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + } + + inline bool DecodePart(const char16_t*& pSrc, const char16_t* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd) + { + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + } + + inline bool DecodePart(const char32_t*& pSrc, const char32_t* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd) + { + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + } + #endif + + #if EA_CHAR8_UNIQUE && EA_WCHAR_UNIQUE + inline bool DecodePart(const char8_t*& pSrc, const char8_t* pSrcEnd, wchar_t*& pDest, wchar_t* pDestEnd) + { + #if (EA_WCHAR_SIZE == 2) + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #elif (EA_WCHAR_SIZE == 4) + return DecodePart(pSrc, pSrcEnd, reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #endif + } + + inline bool DecodePart(const wchar_t*& pSrc, const wchar_t* pSrcEnd, char8_t*& pDest, char8_t* pDestEnd) + { + #if (EA_WCHAR_SIZE == 2) + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #elif (EA_WCHAR_SIZE == 4) + return DecodePart(reinterpret_cast(pSrc), reinterpret_cast(pSrcEnd), reinterpret_cast(pDest), reinterpret_cast(pDestEnd)); + #endif + } + #endif + + /////////////////////////////////////////////////////////////////////////////// + // 'char traits' functionality + // + inline char CharToLower(char c) + { return (char)tolower((uint8_t)c); } + + template + inline T CharToLower(T c) + { if((unsigned)c <= 0xff) return (T)tolower((uint8_t)c); return c; } + + + inline char CharToUpper(char c) + { return (char)toupper((uint8_t)c); } + + template + inline T CharToUpper(T c) + { if((unsigned)c <= 0xff) return (T)toupper((uint8_t)c); return c; } + + + template + int Compare(const T* p1, const T* p2, size_t n) + { + for(; n > 0; ++p1, ++p2, --n) + { + if(*p1 != *p2) + return (static_cast::type>(*p1) < + static_cast::type>(*p2)) ? -1 : 1; + } + return 0; + } + + inline int Compare(const char* p1, const char* p2, size_t n) + { + return memcmp(p1, p2, n); + } + + + template + inline int CompareI(const T* p1, const T* p2, size_t n) + { + for(; n > 0; ++p1, ++p2, --n) + { + const T c1 = CharToLower(*p1); + const T c2 = CharToLower(*p2); + + if(c1 != c2) + return (static_cast::type>(c1) < + static_cast::type>(c2)) ? -1 : 1; + } + return 0; + } + + + template + inline const T* Find(const T* p, T c, size_t n) + { + for(; n > 0; --n, ++p) + { + if(*p == c) + return p; + } + + return NULL; + } + + inline const char* Find(const char* p, char c, size_t n) + { + return (const char*)memchr(p, c, n); + } + + + template + inline EA_CPP14_CONSTEXPR size_t CharStrlen(const T* p) + { + const auto* pCurrent = p; + while(*pCurrent) + ++pCurrent; + return (size_t)(pCurrent - p); + } + + + template + inline T* CharStringUninitializedCopy(const T* pSource, const T* pSourceEnd, T* pDestination) + { + memmove(pDestination, pSource, (size_t)(pSourceEnd - pSource) * sizeof(T)); + return pDestination + (pSourceEnd - pSource); + } + + + template + const T* CharTypeStringFindEnd(const T* pBegin, const T* pEnd, T c) + { + const T* pTemp = pEnd; + while(--pTemp >= pBegin) + { + if(*pTemp == c) + return pTemp; + } + + return pEnd; + } + + + template + const T* CharTypeStringRSearch(const T* p1Begin, const T* p1End, + const T* p2Begin, const T* p2End) + { + // Test for zero length strings, in which case we have a match or a failure, + // but the return value is the same either way. + if((p1Begin == p1End) || (p2Begin == p2End)) + return p1Begin; + + // Test for a pattern of length 1. + if((p2Begin + 1) == p2End) + return CharTypeStringFindEnd(p1Begin, p1End, *p2Begin); + + // Test for search string length being longer than string length. + if((p2End - p2Begin) > (p1End - p1Begin)) + return p1End; + + // General case. + const T* pSearchEnd = (p1End - (p2End - p2Begin) + 1); + const T* pCurrent1; + const T* pCurrent2; + + while(pSearchEnd != p1Begin) + { + // Search for the last occurrence of *p2Begin. + pCurrent1 = CharTypeStringFindEnd(p1Begin, pSearchEnd, *p2Begin); + if(pCurrent1 == pSearchEnd) // If the first char of p2 wasn't found, + return p1End; // then we immediately have failure. + + // In this case, *pTemp == *p2Begin. So compare the rest. + pCurrent2 = p2Begin; + while(*pCurrent1++ == *pCurrent2++) + { + if(pCurrent2 == p2End) + return (pCurrent1 - (p2End - p2Begin)); + } + + // A smarter algorithm might know to subtract more than just one, + // but in most cases it won't make much difference anyway. + --pSearchEnd; + } + + return p1End; + } + + + template + inline const T* CharTypeStringFindFirstOf(const T* p1Begin, const T* p1End, const T* p2Begin, const T* p2End) + { + for (; p1Begin != p1End; ++p1Begin) + { + for (const T* pTemp = p2Begin; pTemp != p2End; ++pTemp) + { + if (*p1Begin == *pTemp) + return p1Begin; + } + } + return p1End; + } + + + template + inline const T* CharTypeStringRFindFirstNotOf(const T* p1RBegin, const T* p1REnd, const T* p2Begin, const T* p2End) + { + for (; p1RBegin != p1REnd; --p1RBegin) + { + const T* pTemp; + for (pTemp = p2Begin; pTemp != p2End; ++pTemp) + { + if (*(p1RBegin - 1) == *pTemp) + break; + } + if (pTemp == p2End) + return p1RBegin; + } + return p1REnd; + } + + + template + inline const T* CharTypeStringFindFirstNotOf(const T* p1Begin, const T* p1End, const T* p2Begin, const T* p2End) + { + for (; p1Begin != p1End; ++p1Begin) + { + const T* pTemp; + for (pTemp = p2Begin; pTemp != p2End; ++pTemp) + { + if (*p1Begin == *pTemp) + break; + } + if (pTemp == p2End) + return p1Begin; + } + return p1End; + } + + + template + inline const T* CharTypeStringRFindFirstOf(const T* p1RBegin, const T* p1REnd, const T* p2Begin, const T* p2End) + { + for (; p1RBegin != p1REnd; --p1RBegin) + { + for (const T* pTemp = p2Begin; pTemp != p2End; ++pTemp) + { + if (*(p1RBegin - 1) == *pTemp) + return p1RBegin; + } + } + return p1REnd; + } + + + template + inline const T* CharTypeStringRFind(const T* pRBegin, const T* pREnd, const T c) + { + while (pRBegin > pREnd) + { + if (*(pRBegin - 1) == c) + return pRBegin; + --pRBegin; + } + return pREnd; + } + + + inline char* CharStringUninitializedFillN(char* pDestination, size_t n, const char c) + { + if(n) // Some compilers (e.g. GCC 4.3+) generate a warning (which can't be disabled) if you call memset with a size of 0. + memset(pDestination, (uint8_t)c, (size_t)n); + return pDestination + n; + } + + template + inline T* CharStringUninitializedFillN(T* pDestination, size_t n, const T c) + { + T * pDest = pDestination; + const T* const pEnd = pDestination + n; + while(pDest < pEnd) + *pDest++ = c; + return pDestination + n; + } + + + inline char* CharTypeAssignN(char* pDestination, size_t n, char c) + { + if(n) // Some compilers (e.g. GCC 4.3+) generate a warning (which can't be disabled) if you call memset with a size of 0. + return (char*)memset(pDestination, c, (size_t)n); + return pDestination; + } + + template + inline T* CharTypeAssignN(T* pDestination, size_t n, T c) + { + T* pDest = pDestination; + const T* const pEnd = pDestination + n; + while(pDest < pEnd) + *pDest++ = c; + return pDestination; + } +} // namespace eastl + +#endif // EASTL_CHAR_TRAITS_H diff --git a/lib/EASTL/include/EASTL/internal/config.h b/lib/EASTL/include/EASTL/internal/config.h new file mode 100644 index 000000000..8dc142025 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/config.h @@ -0,0 +1,1889 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_CONFIG_H +#define EASTL_INTERNAL_CONFIG_H + + +/////////////////////////////////////////////////////////////////////////////// +// ReadMe +// +// This is the EASTL configuration file. All configurable parameters of EASTL +// are controlled through this file. However, all the settings here can be +// manually overridden by the user. There are three ways for a user to override +// the settings in this file: +// +// - Simply edit this file. +// - Define EASTL_USER_CONFIG_HEADER. +// - Predefine individual defines (e.g. EASTL_ASSERT). +// +/////////////////////////////////////////////////////////////////////////////// + + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_USER_CONFIG_HEADER +// +// This allows the user to define a header file to be #included before the +// EASTL config.h contents are compiled. A primary use of this is to override +// the contents of this config.h file. Note that all the settings below in +// this file are user-overridable. +// +// Example usage: +// #define EASTL_USER_CONFIG_HEADER "MyConfigOverrides.h" +// #include +// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef EASTL_USER_CONFIG_HEADER + #include EASTL_USER_CONFIG_HEADER +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_EABASE_DISABLED +// +// The user can disable EABase usage and manually supply the configuration +// via defining EASTL_EABASE_DISABLED and defining the appropriate entities +// globally or via the above EASTL_USER_CONFIG_HEADER. +// +// Example usage: +// #define EASTL_EABASE_DISABLED +// #include +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_EABASE_DISABLED + #include +#endif +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_VERSION +// +// We more or less follow the conventional EA packaging approach to versioning +// here. A primary distinction here is that minor versions are defined as two +// digit entities (e.g. .03") instead of minimal digit entities ".3"). The logic +// here is that the value is a counter and not a floating point fraction. +// Note that the major version doesn't have leading zeros. +// +// Example version strings: +// "0.91.00" // Major version 0, minor version 91, patch version 0. +// "1.00.00" // Major version 1, minor and patch version 0. +// "3.10.02" // Major version 3, minor version 10, patch version 02. +// "12.03.01" // Major version 12, minor version 03, patch version +// +// Example usage: +// printf("EASTL version: %s", EASTL_VERSION); +// printf("EASTL version: %d.%d.%d", EASTL_VERSION_N / 10000 % 100, EASTL_VERSION_N / 100 % 100, EASTL_VERSION_N % 100); +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_VERSION + #define EASTL_VERSION "3.18.00" + #define EASTL_VERSION_N 31800 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EA_COMPILER_NO_STANDARD_CPP_LIBRARY +// +// Defined as 1 or undefined. +// Implements support for the definition of EA_COMPILER_NO_STANDARD_CPP_LIBRARY for the case +// of using EABase versions prior to the addition of its EA_COMPILER_NO_STANDARD_CPP_LIBRARY support. +// +#if !defined(EA_COMPILER_NO_STANDARD_CPP_LIBRARY) + #if defined(EA_PLATFORM_ANDROID) + // Disabled because EA's eaconfig/android_config/android_sdk packages currently + // don't support linking STL libraries. Perhaps we can figure out what linker arguments + // are needed for an app so we can manually specify them and then re-enable this code. + // + //#include + // + //#if (__ANDROID_API__ < 9) // Earlier versions of Android provide no std C++ STL implementation. + #define EA_COMPILER_NO_STANDARD_CPP_LIBRARY 1 + //#endif + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EA_NOEXCEPT +// +// Defined as a macro. Provided here for backward compatibility with older +// EABase versions prior to 2.00.40 that don't yet define it themselves. +// +#if !defined(EA_NOEXCEPT) + #define EA_NOEXCEPT + #define EA_NOEXCEPT_IF(predicate) + #define EA_NOEXCEPT_EXPR(expression) false +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EA_CPP14_CONSTEXPR +// +// Defined as constexpr when a C++14 compiler is present. Defines it as nothing +// when using a C++11 compiler. +// C++14 relaxes the specification for constexpr such that it allows more +// kinds of expressions. Since a C++11 compiler doesn't allow this, we need +// to make a unique define for C++14 constexpr. This macro should be used only +// when you are using it with code that specfically requires C++14 constexpr +// functionality beyond the regular C++11 constexpr functionality. +// http://en.wikipedia.org/wiki/C%2B%2B14#Relaxed_constexpr_restrictions +// +#if !defined(EA_CPP14_CONSTEXPR) + #if defined(EA_COMPILER_CPP14_ENABLED) + #define EA_CPP14_CONSTEXPR constexpr + #else + #define EA_CPP14_CONSTEXPR // not supported + #define EA_NO_CPP14_CONSTEXPR + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL namespace +// +// We define this so that users that #include this config file can reference +// these namespaces without seeing any other files that happen to use them. +/////////////////////////////////////////////////////////////////////////////// + +/// EA Standard Template Library +namespace eastl +{ + // Intentionally empty. +} + + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_DEBUG +// +// Defined as an integer >= 0. Default is 1 for debug builds and 0 for +// release builds. This define is also a master switch for the default value +// of some other settings. +// +// Example usage: +// #if EASTL_DEBUG +// ... +// #endif +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_DEBUG + #if defined(EA_DEBUG) || defined(_DEBUG) + #define EASTL_DEBUG 1 + #else + #define EASTL_DEBUG 0 + #endif +#endif + +// Developer debug. Helps EASTL developers assert EASTL is coded correctly. +// Normally disabled for users since it validates internal things and not user things. +#ifndef EASTL_DEV_DEBUG + #define EASTL_DEV_DEBUG 0 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_DEBUGPARAMS_LEVEL +// +// EASTL_DEBUGPARAMS_LEVEL controls what debug information is passed through to +// the allocator by default. +// This value may be defined by the user ... if not it will default to 1 for +// EA_DEBUG builds, otherwise 0. +// +// 0 - no debug information is passed through to allocator calls. +// 1 - 'name' is passed through to allocator calls. +// 2 - 'name', __FILE__, and __LINE__ are passed through to allocator calls. +// +// This parameter mirrors the equivalent parameter in the CoreAllocator package. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_DEBUGPARAMS_LEVEL + #if EASTL_DEBUG + #define EASTL_DEBUGPARAMS_LEVEL 2 + #else + #define EASTL_DEBUGPARAMS_LEVEL 0 + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_DLL +// +// Defined as 0 or 1. The default is dependent on the definition of EA_DLL. +// If EA_DLL is defined, then EASTL_DLL is 1, else EASTL_DLL is 0. +// EA_DLL is a define that controls DLL builds within the EAConfig build system. +// EASTL_DLL controls whether EASTL is built and used as a DLL. +// Normally you wouldn't do such a thing, but there are use cases for such +// a thing, particularly in the case of embedding C++ into C# applications. +// +#ifndef EASTL_DLL + #if defined(EA_DLL) + #define EASTL_DLL 1 + #else + #define EASTL_DLL 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_IF_NOT_DLL +// +// Utility to include expressions only for static builds. +// +#ifndef EASTL_IF_NOT_DLL + #if EASTL_DLL + #define EASTL_IF_NOT_DLL(x) + #else + #define EASTL_IF_NOT_DLL(x) x + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_API +// +// This is used to label functions as DLL exports under Microsoft platforms. +// If EA_DLL is defined, then the user is building EASTL as a DLL and EASTL's +// non-templated functions will be exported. EASTL template functions are not +// labelled as EASTL_API (and are thus not exported in a DLL build). This is +// because it's not possible (or at least unsafe) to implement inline templated +// functions in a DLL. +// +// Example usage of EASTL_API: +// EASTL_API int someVariable = 10; // Export someVariable in a DLL build. +// +// struct EASTL_API SomeClass{ // Export SomeClass and its member functions in a DLL build. +// EASTL_LOCAL void PrivateMethod(); // Not exported. +// }; +// +// EASTL_API void SomeFunction(); // Export SomeFunction in a DLL build. +// +// +#if defined(EA_DLL) && !defined(EASTL_DLL) + #define EASTL_DLL 1 +#endif + +#ifndef EASTL_API // If the build file hasn't already defined this to be dllexport... + #if EASTL_DLL + #if defined(_MSC_VER) + #define EASTL_API __declspec(dllimport) + #define EASTL_LOCAL + #elif defined(__CYGWIN__) + #define EASTL_API __attribute__((dllimport)) + #define EASTL_LOCAL + #elif (defined(__GNUC__) && (__GNUC__ >= 4)) + #define EASTL_API __attribute__ ((visibility("default"))) + #define EASTL_LOCAL __attribute__ ((visibility("hidden"))) + #else + #define EASTL_API + #define EASTL_LOCAL + #endif + #else + #define EASTL_API + #define EASTL_LOCAL + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_EASTDC_API +// +// This is used for importing EAStdC functions into EASTL, possibly via a DLL import. +// +#ifndef EASTL_EASTDC_API + #if EASTL_DLL + #if defined(_MSC_VER) + #define EASTL_EASTDC_API __declspec(dllimport) + #define EASTL_EASTDC_LOCAL + #elif defined(__CYGWIN__) + #define EASTL_EASTDC_API __attribute__((dllimport)) + #define EASTL_EASTDC_LOCAL + #elif (defined(__GNUC__) && (__GNUC__ >= 4)) + #define EASTL_EASTDC_API __attribute__ ((visibility("default"))) + #define EASTL_EASTDC_LOCAL __attribute__ ((visibility("hidden"))) + #else + #define EASTL_EASTDC_API + #define EASTL_EASTDC_LOCAL + #endif + #else + #define EASTL_EASTDC_API + #define EASTL_EASTDC_LOCAL + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_EASTDC_VSNPRINTF +// +// Defined as 0 or 1. By default it is 1. +// +// When enabled EASTL uses EAStdC's Vsnprintf function directly instead of +// having the user provide a global Vsnprintf8/16/32 function. The benefit +// of this is that it will allow EASTL to just link to EAStdC's Vsnprintf +// without the user doing anything. The downside is that any users who aren't +// already using EAStdC will either need to now depend on EAStdC or globally +// define this property to be 0 and simply provide functions that have the same +// names. See the usage of EASTL_EASTDC_VSNPRINTF in string.h for more info. +// +#if !defined(EASTL_EASTDC_VSNPRINTF) + #define EASTL_EASTDC_VSNPRINTF 1 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_NAME_ENABLED / EASTL_NAME / EASTL_NAME_VAL +// +// Used to wrap debug string names. In a release build, the definition +// goes away. These are present to avoid release build compiler warnings +// and to make code simpler. +// +// Example usage of EASTL_NAME: +// // pName will defined away in a release build and thus prevent compiler warnings. +// void allocator::set_name(const char* EASTL_NAME(pName)) +// { +// #if EASTL_NAME_ENABLED +// mpName = pName; +// #endif +// } +// +// Example usage of EASTL_NAME_VAL: +// // "xxx" is defined to NULL in a release build. +// vector::vector(const allocator_type& allocator = allocator_type(EASTL_NAME_VAL("xxx"))); +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_NAME_ENABLED + #define EASTL_NAME_ENABLED EASTL_DEBUG +#endif + +#ifndef EASTL_NAME + #if EASTL_NAME_ENABLED + #define EASTL_NAME(x) x + #define EASTL_NAME_VAL(x) x + #else + #define EASTL_NAME(x) + #define EASTL_NAME_VAL(x) ((const char*)NULL) + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_DEFAULT_NAME_PREFIX +// +// Defined as a string literal. Defaults to "EASTL". +// This define is used as the default name for EASTL where such a thing is +// referenced in EASTL. For example, if the user doesn't specify an allocator +// name for their deque, it is named "EASTL deque". However, you can override +// this to say "SuperBaseball deque" by changing EASTL_DEFAULT_NAME_PREFIX. +// +// Example usage (which is simply taken from how deque.h uses this define): +// #ifndef EASTL_DEQUE_DEFAULT_NAME +// #define EASTL_DEQUE_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " deque" +// #endif +// +#ifndef EASTL_DEFAULT_NAME_PREFIX + #define EASTL_DEFAULT_NAME_PREFIX "EASTL" +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_ASSERT_ENABLED +// +// Defined as 0 or non-zero. Default is same as EASTL_DEBUG. +// If EASTL_ASSERT_ENABLED is non-zero, then asserts will be executed via +// the assertion mechanism. +// +// Example usage: +// #if EASTL_ASSERT_ENABLED +// EASTL_ASSERT(v.size() > 17); +// #endif +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_ASSERT_ENABLED + #define EASTL_ASSERT_ENABLED EASTL_DEBUG +#endif + +// Developer assert. Helps EASTL developers assert EASTL is coded correctly. +// Normally disabled for users since it validates internal things and not user things. +#ifndef EASTL_DEV_ASSERT_ENABLED + #define EASTL_DEV_ASSERT_ENABLED EASTL_DEV_DEBUG +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_EMPTY_REFERENCE_ASSERT_ENABLED +// +// Defined as 0 or non-zero. Default is same as EASTL_ASSERT_ENABLED. +// This is like EASTL_ASSERT_ENABLED, except it is for empty container +// references. Sometime people like to be able to take a reference to +// the front of the container, but not use it if the container is empty. +// In practice it's often easier and more efficient to do this than to write +// extra code to check if the container is empty. +// +// NOTE: If this is enabled, EASTL_ASSERT_ENABLED must also be enabled +// +// Example usage: +// template +// inline typename vector::reference +// vector::front() +// { +// #if EASTL_ASSERT_ENABLED +// EASTL_ASSERT(mpEnd > mpBegin); +// #endif +// +// return *mpBegin; +// } +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + #define EASTL_EMPTY_REFERENCE_ASSERT_ENABLED EASTL_ASSERT_ENABLED +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// SetAssertionFailureFunction +// +// Allows the user to set a custom assertion failure mechanism. +// +// Example usage: +// void Assert(const char* pExpression, void* pContext); +// SetAssertionFailureFunction(Assert, this); +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_ASSERTION_FAILURE_DEFINED + #define EASTL_ASSERTION_FAILURE_DEFINED + + namespace eastl + { + typedef void (*EASTL_AssertionFailureFunction)(const char* pExpression, void* pContext); + EASTL_API void SetAssertionFailureFunction(EASTL_AssertionFailureFunction pFunction, void* pContext); + + // These are the internal default functions that implement asserts. + EASTL_API void AssertionFailure(const char* pExpression); + EASTL_API void AssertionFailureFunctionDefault(const char* pExpression, void* pContext); + } +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_ASSERT +// +// Assertion macro. Can be overridden by user with a different value. +// +// Example usage: +// EASTL_ASSERT(intVector.size() < 100); +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_ASSERT + #if EASTL_ASSERT_ENABLED + #define EASTL_ASSERT(expression) \ + EA_DISABLE_VC_WARNING(4127) \ + do { \ + EA_ANALYSIS_ASSUME(expression); \ + (void)((expression) || (eastl::AssertionFailure(#expression), 0)); \ + } while (0) \ + EA_RESTORE_VC_WARNING() + #else + #define EASTL_ASSERT(expression) + #endif +#endif + +// Developer assert. Helps EASTL developers assert EASTL is coded correctly. +// Normally disabled for users since it validates internal things and not user things. +#ifndef EASTL_DEV_ASSERT + #if EASTL_DEV_ASSERT_ENABLED + #define EASTL_DEV_ASSERT(expression) \ + EA_DISABLE_VC_WARNING(4127) \ + do { \ + EA_ANALYSIS_ASSUME(expression); \ + (void)((expression) || (eastl::AssertionFailure(#expression), 0)); \ + } while(0) \ + EA_RESTORE_VC_WARNING() + #else + #define EASTL_DEV_ASSERT(expression) + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_ASSERT_MSG +// +// Example usage: +// EASTL_ASSERT_MSG(false, "detected error condition!"); +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef EASTL_ASSERT_MSG + #if EASTL_ASSERT_ENABLED + #define EASTL_ASSERT_MSG(expression, message) \ + EA_DISABLE_VC_WARNING(4127) \ + do { \ + EA_ANALYSIS_ASSUME(expression); \ + (void)((expression) || (eastl::AssertionFailure(message), 0)); \ + } while (0) \ + EA_RESTORE_VC_WARNING() + #else + #define EASTL_ASSERT_MSG(expression, message) + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_FAIL_MSG +// +// Failure macro. Can be overridden by user with a different value. +// +// Example usage: +// EASTL_FAIL("detected error condition!"); +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_FAIL_MSG + #if EASTL_ASSERT_ENABLED + #define EASTL_FAIL_MSG(message) (eastl::AssertionFailure(message)) + #else + #define EASTL_FAIL_MSG(message) + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_CT_ASSERT / EASTL_CT_ASSERT_NAMED +// +// EASTL_CT_ASSERT is a macro for compile time assertion checks, useful for +// validating *constant* expressions. The advantage over using EASTL_ASSERT +// is that errors are caught at compile time instead of runtime. +// +// Example usage: +// EASTL_CT_ASSERT(sizeof(uint32_t) == 4); +// +/////////////////////////////////////////////////////////////////////////////// + +#define EASTL_CT_ASSERT(expression) static_assert(expression, #expression) + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_CT_ASSERT_MSG +// +// EASTL_CT_ASSERT_MSG is a macro for compile time assertion checks, useful for +// validating *constant* expressions. The advantage over using EASTL_ASSERT +// is that errors are caught at compile time instead of runtime. +// The message must be a string literal. +// +// Example usage: +// EASTL_CT_ASSERT_MSG(sizeof(uint32_t) == 4, "The size of uint32_t must be 4."); +// +/////////////////////////////////////////////////////////////////////////////// + +#define EASTL_CT_ASSERT_MSG(expression, message) static_assert(expression, message) + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_DEBUG_BREAK / EASTL_DEBUG_BREAK_OVERRIDE +// +// This function causes an app to immediately stop under the debugger. +// It is implemented as a macro in order to allow stopping at the site +// of the call. +// +// EASTL_DEBUG_BREAK_OVERRIDE allows one to define EASTL_DEBUG_BREAK directly. +// This is useful in cases where you desire to disable EASTL_DEBUG_BREAK +// but do not wish to (or cannot) define a custom void function() to replace +// EASTL_DEBUG_BREAK callsites. +// +// Example usage: +// EASTL_DEBUG_BREAK(); +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_DEBUG_BREAK_OVERRIDE + #ifndef EASTL_DEBUG_BREAK + #if defined(_MSC_VER) && (_MSC_VER >= 1300) + #define EASTL_DEBUG_BREAK() __debugbreak() // This is a compiler intrinsic which will map to appropriate inlined asm for the platform. + #elif (defined(EA_PROCESSOR_ARM) && !defined(EA_PROCESSOR_ARM64)) && defined(__APPLE__) + #define EASTL_DEBUG_BREAK() asm("trap") + #elif defined(EA_PROCESSOR_ARM64) && defined(__APPLE__) + #include + #include + #define EASTL_DEBUG_BREAK() kill( getpid(), SIGINT ) + #elif defined(EA_PROCESSOR_ARM64) && defined(__GNUC__) + #define EASTL_DEBUG_BREAK() asm("brk 10") + #elif defined(EA_PROCESSOR_ARM) && defined(__GNUC__) + #define EASTL_DEBUG_BREAK() asm("BKPT 10") // The 10 is arbitrary. It's just a unique id. + #elif defined(EA_PROCESSOR_ARM) && defined(__ARMCC_VERSION) + #define EASTL_DEBUG_BREAK() __breakpoint(10) + #elif defined(EA_PROCESSOR_POWERPC) // Generic PowerPC. + #define EASTL_DEBUG_BREAK() asm(".long 0") // This triggers an exception by executing opcode 0x00000000. + #elif (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) && defined(EA_ASM_STYLE_INTEL) + #define EASTL_DEBUG_BREAK() { __asm int 3 } + #elif (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) && (defined(EA_ASM_STYLE_ATT) || defined(__GNUC__)) + #define EASTL_DEBUG_BREAK() asm("int3") + #else + void EASTL_DEBUG_BREAK(); // User must define this externally. + #endif + #else + void EASTL_DEBUG_BREAK(); // User must define this externally. + #endif +#else + #ifndef EASTL_DEBUG_BREAK + #if EASTL_DEBUG_BREAK_OVERRIDE == 1 + // define an empty callable to satisfy the call site. + #define EASTL_DEBUG_BREAK ([]{}) + #else + #define EASTL_DEBUG_BREAK EASTL_DEBUG_BREAK_OVERRIDE + #endif + #else + #error EASTL_DEBUG_BREAK is already defined yet you would like to override it. Please ensure no other headers are already defining EASTL_DEBUG_BREAK before this header (config.h) is included + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_ALLOCATOR_COPY_ENABLED +// +// Defined as 0 or 1. Default is 0 (disabled) until some future date. +// If enabled (1) then container operator= copies the allocator from the +// source container. It ideally should be set to enabled but for backwards +// compatibility with older versions of EASTL it is currently set to 0. +// Regardless of whether this value is 0 or 1, this container copy constructs +// or copy assigns allocators. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_ALLOCATOR_COPY_ENABLED + #define EASTL_ALLOCATOR_COPY_ENABLED 0 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_FIXED_SIZE_TRACKING_ENABLED +// +// Defined as an integer >= 0. Default is same as EASTL_DEBUG. +// If EASTL_FIXED_SIZE_TRACKING_ENABLED is enabled, then fixed +// containers in debug builds track the max count of objects +// that have been in the container. This allows for the tuning +// of fixed container sizes to their minimum required size. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_FIXED_SIZE_TRACKING_ENABLED + #define EASTL_FIXED_SIZE_TRACKING_ENABLED EASTL_DEBUG +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_RTTI_ENABLED +// +// Defined as 0 or 1. Default is 1 if RTTI is supported by the compiler. +// This define exists so that we can use some dynamic_cast operations in the +// code without warning. dynamic_cast is only used if the specifically refers +// to it; EASTL won't do dynamic_cast behind your back. +// +// Example usage: +// #if EASTL_RTTI_ENABLED +// pChildClass = dynamic_cast(pParentClass); +// #endif +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_RTTI_ENABLED + // The VC++ default Standard Library (Dinkumware) disables major parts of RTTI + // (e.g. type_info) if exceptions are disabled, even if RTTI itself is enabled. + // _HAS_EXCEPTIONS is defined by Dinkumware to 0 or 1 (disabled or enabled). + #if defined(EA_COMPILER_NO_RTTI) || (defined(_MSC_VER) && defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && !(defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS)) + #define EASTL_RTTI_ENABLED 0 + #else + #define EASTL_RTTI_ENABLED 1 + #endif +#endif + + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_EXCEPTIONS_ENABLED +// +// Defined as 0 or 1. Default is to follow what the compiler settings are. +// The user can predefine EASTL_EXCEPTIONS_ENABLED to 0 or 1; however, if the +// compiler is set to disable exceptions then EASTL_EXCEPTIONS_ENABLED is +// forced to a value of 0 regardless of the user predefine. +// +// Note that we do not enable EASTL exceptions by default if the compiler +// has exceptions enabled. To enable EASTL_EXCEPTIONS_ENABLED you need to +// manually set it to 1. +// +/////////////////////////////////////////////////////////////////////////////// + +#if !defined(EASTL_EXCEPTIONS_ENABLED) || ((EASTL_EXCEPTIONS_ENABLED == 1) && defined(EA_COMPILER_NO_EXCEPTIONS)) + #define EASTL_EXCEPTIONS_ENABLED 0 +#endif + + + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_STRING_OPT_XXXX +// +// Enables some options / optimizations options that cause the string class +// to behave slightly different from the C++ standard basic_string. These are +// options whereby you can improve performance by avoiding operations that +// in practice may never occur for you. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_STRING_OPT_EXPLICIT_CTORS + // Defined as 0 or 1. Default is 0. + // Defines if we should implement explicity in constructors where the C++ + // standard string does not. The advantage of enabling explicit constructors + // is that you can do this: string s = "hello"; in addition to string s("hello"); + // The disadvantage of enabling explicity constructors is that there can be + // silent conversions done which impede performance if the user isn't paying + // attention. + // C++ standard string ctors are not explicit. + #define EASTL_STRING_OPT_EXPLICIT_CTORS 0 +#endif + +#ifndef EASTL_STRING_OPT_LENGTH_ERRORS + // Defined as 0 or 1. Default is equal to EASTL_EXCEPTIONS_ENABLED. + // Defines if we check for string values going beyond kMaxSize + // (a very large value) and throw exections if so. + // C++ standard strings are expected to do such checks. + #define EASTL_STRING_OPT_LENGTH_ERRORS EASTL_EXCEPTIONS_ENABLED +#endif + +#ifndef EASTL_STRING_OPT_RANGE_ERRORS + // Defined as 0 or 1. Default is equal to EASTL_EXCEPTIONS_ENABLED. + // Defines if we check for out-of-bounds references to string + // positions and throw exceptions if so. Well-behaved code shouldn't + // refence out-of-bounds positions and so shouldn't need these checks. + // C++ standard strings are expected to do such range checks. + #define EASTL_STRING_OPT_RANGE_ERRORS EASTL_EXCEPTIONS_ENABLED +#endif + +#ifndef EASTL_STRING_OPT_ARGUMENT_ERRORS + // Defined as 0 or 1. Default is 0. + // Defines if we check for NULL ptr arguments passed to string + // functions by the user and throw exceptions if so. Well-behaved code + // shouldn't pass bad arguments and so shouldn't need these checks. + // Also, some users believe that strings should check for NULL pointers + // in all their arguments and do no-ops if so. This is very debatable. + // C++ standard strings are not required to check for such argument errors. + #define EASTL_STRING_OPT_ARGUMENT_ERRORS 0 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_BITSET_SIZE_T +// +// Defined as 0 or 1. Default is 1. +// Controls whether bitset uses size_t or eastl_size_t. +// +#ifndef EASTL_BITSET_SIZE_T + #define EASTL_BITSET_SIZE_T 1 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_INT128_SUPPORTED +// +// Defined as 0 or 1. +// +#ifndef EASTL_INT128_SUPPORTED + #if defined(__SIZEOF_INT128__) || (defined(EA_COMPILER_INTMAX_SIZE) && (EA_COMPILER_INTMAX_SIZE >= 16)) + #define EASTL_INT128_SUPPORTED 1 + #else + #define EASTL_INT128_SUPPORTED 0 + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_DEFAULT_ALLOCATOR_ALIGNED_ALLOCATIONS_SUPPORTED +// +// Defined as 0 or 1. +// Tells if you can use the default EASTL allocator to do aligned allocations, +// which for most uses tells if you can store aligned objects in containers +// that use default allocators. It turns out that when built as a DLL for +// some platforms, EASTL doesn't have a way to do aligned allocations, as it +// doesn't have a heap that supports it. There is a way to work around this +// with dynamically defined allocators, but that's currently a to-do. +// +#ifndef EASTL_DEFAULT_ALLOCATOR_ALIGNED_ALLOCATIONS_SUPPORTED + #if EASTL_DLL + #define EASTL_DEFAULT_ALLOCATOR_ALIGNED_ALLOCATIONS_SUPPORTED 0 + #else + #define EASTL_DEFAULT_ALLOCATOR_ALIGNED_ALLOCATIONS_SUPPORTED 1 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_INT128_DEFINED +// +// Defined as 0 or 1. +// Specifies whether eastl_int128_t/eastl_uint128_t have been typedef'd yet. +// +#ifndef EASTL_INT128_DEFINED + #if EASTL_INT128_SUPPORTED + #define EASTL_INT128_DEFINED 1 + + #if defined(__SIZEOF_INT128__) || defined(EA_COMPILER_GNUC) || defined(__clang__) + typedef __int128_t eastl_int128_t; + typedef __uint128_t eastl_uint128_t; + #else + typedef int128_t eastl_int128_t; // The EAStdC package defines an EA::StdC::int128_t and uint128_t type, + typedef uint128_t eastl_uint128_t; // though they are currently within the EA::StdC namespace. + #endif + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_BITSET_WORD_TYPE_DEFAULT / EASTL_BITSET_WORD_SIZE_DEFAULT +// +// Defined as an integral power of two type, usually uint32_t or uint64_t. +// Specifies the word type that bitset should use internally to implement +// storage. By default this is the platform register word size, but there +// may be reasons to use a different value. +// +// Defines the integral data type used by bitset by default. +// You can override this default on a bitset-by-bitset case by supplying a +// custom bitset WordType template parameter. +// +// The C++ standard specifies that the std::bitset word type be unsigned long, +// but that isn't necessarily the most efficient data type for the given platform. +// We can follow the standard and be potentially less efficient or we can do what +// is more efficient but less like the C++ std::bitset. +// +#if !defined(EASTL_BITSET_WORD_TYPE_DEFAULT) + #if defined(EASTL_BITSET_WORD_SIZE) // EASTL_BITSET_WORD_SIZE is deprecated, but we temporarily support the ability for the user to specify it. Use EASTL_BITSET_WORD_TYPE_DEFAULT instead. + #if (EASTL_BITSET_WORD_SIZE == 4) + #define EASTL_BITSET_WORD_TYPE_DEFAULT uint32_t + #define EASTL_BITSET_WORD_SIZE_DEFAULT 4 + #else + #define EASTL_BITSET_WORD_TYPE_DEFAULT uint64_t + #define EASTL_BITSET_WORD_SIZE_DEFAULT 8 + #endif + #elif (EA_PLATFORM_WORD_SIZE == 16) // EA_PLATFORM_WORD_SIZE is defined in EABase. + #define EASTL_BITSET_WORD_TYPE_DEFAULT uint128_t + #define EASTL_BITSET_WORD_SIZE_DEFAULT 16 + #elif (EA_PLATFORM_WORD_SIZE == 8) + #define EASTL_BITSET_WORD_TYPE_DEFAULT uint64_t + #define EASTL_BITSET_WORD_SIZE_DEFAULT 8 + #elif (EA_PLATFORM_WORD_SIZE == 4) + #define EASTL_BITSET_WORD_TYPE_DEFAULT uint32_t + #define EASTL_BITSET_WORD_SIZE_DEFAULT 4 + #else + #define EASTL_BITSET_WORD_TYPE_DEFAULT uint16_t + #define EASTL_BITSET_WORD_SIZE_DEFAULT 2 + #endif +#endif + + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_LIST_SIZE_CACHE +// +// Defined as 0 or 1. Default is 1. Changed from 0 in version 1.16.01. +// If defined as 1, the list and slist containers (and possibly any additional +// containers as well) keep a member mSize (or similar) variable which allows +// the size() member function to execute in constant time (a.k.a. O(1)). +// There are debates on both sides as to whether it is better to have this +// cached value or not, as having it entails some cost (memory and code). +// To consider: Make list size caching an optional template parameter. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_LIST_SIZE_CACHE + #define EASTL_LIST_SIZE_CACHE 1 +#endif + +#ifndef EASTL_SLIST_SIZE_CACHE + #define EASTL_SLIST_SIZE_CACHE 1 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_MAX_STACK_USAGE +// +// Defined as an integer greater than zero. Default is 4000. +// There are some places in EASTL where temporary objects are put on the +// stack. A common example of this is in the implementation of container +// swap functions whereby a temporary copy of the container is made. +// There is a problem, however, if the size of the item created on the stack +// is very large. This can happen with fixed-size containers, for example. +// The EASTL_MAX_STACK_USAGE define specifies the maximum amount of memory +// (in bytes) that the given platform/compiler will safely allow on the stack. +// Platforms such as Windows will generally allow larger values than embedded +// systems or console machines, but it is usually a good idea to stick with +// a max usage value that is portable across all platforms, lest the user be +// surprised when something breaks as it is ported to another platform. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_MAX_STACK_USAGE + #define EASTL_MAX_STACK_USAGE 4000 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_VA_COPY_ENABLED +// +// Defined as 0 or 1. Default is 1 for compilers that need it, 0 for others. +// Some compilers on some platforms implement va_list whereby its contents +// are destroyed upon usage, even if passed by value to another function. +// With these compilers you can use va_copy to save and restore a va_list. +// Known compiler/platforms that destroy va_list contents upon usage include: +// CodeWarrior on PowerPC +// GCC on x86-64 +// However, va_copy is part of the C99 standard and not part of earlier C and +// C++ standards. So not all compilers support it. VC++ doesn't support va_copy, +// but it turns out that VC++ doesn't usually need it on the platforms it supports, +// and va_copy can usually be implemented via memcpy(va_list, va_list) with VC++. +// +// Example usage: +// void Function(va_list arguments) +// { +// #if EASTL_VA_COPY_ENABLED +// va_list argumentsCopy; +// va_copy(argumentsCopy, arguments); +// #endif +// +// #if EASTL_VA_COPY_ENABLED +// va_end(argumentsCopy); +// #endif +// } +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_VA_COPY_ENABLED + #if ((defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__)) && (!defined(__i386__) || defined(__x86_64__)) && !defined(__ppc__) && !defined(__PPC__) && !defined(__PPC64__) + #define EASTL_VA_COPY_ENABLED 1 + #else + #define EASTL_VA_COPY_ENABLED 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_OPERATOR_EQUALS_OTHER_ENABLED +// +// Defined as 0 or 1. Default is 0 until such day that it's deemed safe. +// When enabled, enables operator= for other char types, e.g. for code +// like this: +// eastl::string8 s8; +// eastl::string16 s16; +// s8 = s16; +// This option is considered experimental, and may exist as such for an +// indefinite amount of time. +// +#if !defined(EASTL_OPERATOR_EQUALS_OTHER_ENABLED) + #define EASTL_OPERATOR_EQUALS_OTHER_ENABLED 0 +#endif +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_LIST_PROXY_ENABLED +// +#if !defined(EASTL_LIST_PROXY_ENABLED) + // GCC with -fstrict-aliasing has bugs (or undocumented functionality in their + // __may_alias__ implementation. The compiler gets confused about function signatures. + // VC8 (1400) doesn't need the proxy because it has built-in smart debugging capabilities. + #if defined(EASTL_DEBUG) && !defined(__GNUC__) && (!defined(_MSC_VER) || (_MSC_VER < 1400)) + #define EASTL_LIST_PROXY_ENABLED 1 + #define EASTL_LIST_PROXY_MAY_ALIAS EASTL_MAY_ALIAS + #else + #define EASTL_LIST_PROXY_ENABLED 0 + #define EASTL_LIST_PROXY_MAY_ALIAS + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_STD_ITERATOR_CATEGORY_ENABLED +// +// Defined as 0 or 1. Default is 0. +// If defined as non-zero, EASTL iterator categories (iterator.h's input_iterator_tag, +// forward_iterator_tag, etc.) are defined to be those from std C++ in the std +// namespace. The reason for wanting to enable such a feature is that it allows +// EASTL containers and algorithms to work with std STL containes and algorithms. +// The default value was changed from 1 to 0 in EASL 1.13.03, January 11, 2012. +// The reason for the change was that almost nobody was taking advantage of it and +// it was slowing down compile times for some compilers quite a bit due to them +// having a lot of headers behind . +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_STD_ITERATOR_CATEGORY_ENABLED + #define EASTL_STD_ITERATOR_CATEGORY_ENABLED 0 +#endif + +#if EASTL_STD_ITERATOR_CATEGORY_ENABLED + #define EASTL_ITC_NS std +#else + #define EASTL_ITC_NS eastl +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_VALIDATION_ENABLED +// +// Defined as an integer >= 0. Default is to be equal to EASTL_DEBUG. +// If nonzero, then a certain amount of automatic runtime validation is done. +// Runtime validation is not considered the same thing as asserting that user +// input values are valid. Validation refers to internal consistency checking +// of the validity of containers and their iterators. Validation checking is +// something that often involves significantly more than basic assertion +// checking, and it may sometimes be desirable to disable it. +// This macro would generally be used internally by EASTL. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_VALIDATION_ENABLED + #define EASTL_VALIDATION_ENABLED EASTL_DEBUG +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_VALIDATE_COMPARE +// +// Defined as EASTL_ASSERT or defined away. Default is EASTL_ASSERT if EASTL_VALIDATION_ENABLED is enabled. +// This is used to validate user-supplied comparison functions, particularly for sorting purposes. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_VALIDATE_COMPARE_ENABLED + #define EASTL_VALIDATE_COMPARE_ENABLED EASTL_VALIDATION_ENABLED +#endif + +#if EASTL_VALIDATE_COMPARE_ENABLED + #define EASTL_VALIDATE_COMPARE EASTL_ASSERT +#else + #define EASTL_VALIDATE_COMPARE(expression) +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_VALIDATE_INTRUSIVE_LIST +// +// Defined as an integral value >= 0. Controls the amount of automatic validation +// done by intrusive_list. A value of 0 means no automatic validation is done. +// As of this writing, EASTL_VALIDATE_INTRUSIVE_LIST defaults to 0, as it makes +// the intrusive_list_node become a non-POD, which may be an issue for some code. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_VALIDATE_INTRUSIVE_LIST + #define EASTL_VALIDATE_INTRUSIVE_LIST 0 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_FORCE_INLINE +// +// Defined as a "force inline" expression or defined away. +// You generally don't need to use forced inlining with the Microsoft and +// Metrowerks compilers, but you may need it with the GCC compiler (any version). +// +// Example usage: +// template +// EASTL_FORCE_INLINE typename vector::size_type +// vector::size() const +// { return mpEnd - mpBegin; } +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_FORCE_INLINE + #define EASTL_FORCE_INLINE EA_FORCE_INLINE +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_MAY_ALIAS +// +// Defined as a macro that wraps the GCC may_alias attribute. This attribute +// has no significance for VC++ because VC++ doesn't support the concept of +// strict aliasing. Users should avoid writing code that breaks strict +// aliasing rules; EASTL_MAY_ALIAS is for cases with no alternative. +// +// Example usage: +// uint32_t value EASTL_MAY_ALIAS; +// +// Example usage: +// typedef uint32_t EASTL_MAY_ALIAS value_type; +// value_type value; +// +#if defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 303) && !defined(EA_COMPILER_RVCT) + #define EASTL_MAY_ALIAS __attribute__((__may_alias__)) +#else + #define EASTL_MAY_ALIAS +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_LIKELY / EASTL_UNLIKELY +// +// Defined as a macro which gives a hint to the compiler for branch +// prediction. GCC gives you the ability to manually give a hint to +// the compiler about the result of a comparison, though it's often +// best to compile shipping code with profiling feedback under both +// GCC (-fprofile-arcs) and VC++ (/LTCG:PGO, etc.). However, there +// are times when you feel very sure that a boolean expression will +// usually evaluate to either true or false and can help the compiler +// by using an explicity directive... +// +// Example usage: +// if(EASTL_LIKELY(a == 0)) // Tell the compiler that a will usually equal 0. +// { ... } +// +// Example usage: +// if(EASTL_UNLIKELY(a == 0)) // Tell the compiler that a will usually not equal 0. +// { ... } +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_LIKELY + #if defined(__GNUC__) && (__GNUC__ >= 3) + #define EASTL_LIKELY(x) __builtin_expect(!!(x), true) + #define EASTL_UNLIKELY(x) __builtin_expect(!!(x), false) + #else + #define EASTL_LIKELY(x) (x) + #define EASTL_UNLIKELY(x) (x) + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_STD_TYPE_TRAITS_AVAILABLE +// +// Defined as 0 or 1; default is based on auto-detection. +// Specifies whether Standard C++11 support exists. +// Sometimes the auto-detection below fails to work properly and the +// user needs to override it. Does not define whether the compiler provides +// built-in compiler type trait support (e.g. __is_abstract()), as some +// compilers will EASTL_STD_TYPE_TRAITS_AVAILABLE = 0, but have built +// in type trait support. +// +#ifndef EASTL_STD_TYPE_TRAITS_AVAILABLE + /* Disabled because we don't currently need it. + #if defined(_MSC_VER) && (_MSC_VER >= 1500) // VS2008 or later + #pragma warning(push, 0) + #include + #pragma warning(pop) + #if ((defined(_HAS_TR1) && _HAS_TR1) || _MSC_VER >= 1700) // VS2012 (1700) and later has built-in type traits support. + #define EASTL_STD_TYPE_TRAITS_AVAILABLE 1 + #include + #else + #define EASTL_STD_TYPE_TRAITS_AVAILABLE 0 + #endif + + #elif defined(EA_COMPILER_CLANG) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003) && !defined(__GCCXML__)) && !defined(EA_COMPILER_NO_STANDARD_CPP_LIBRARY) + #include // This will define __GLIBCXX__ if using GNU's libstdc++ and _LIBCPP_VERSION if using clang's libc++. + + #if defined(EA_COMPILER_CLANG) && !defined(EA_PLATFORM_APPLE) // As of v3.0.0, Apple's clang doesn't support type traits. + // http://clang.llvm.org/docs/LanguageExtensions.html#checking_type_traits + // Clang has some built-in compiler trait support. This support doesn't currently + // directly cover all our type_traits, though the C++ Standard Library that's used + // with clang could fill that in. + #define EASTL_STD_TYPE_TRAITS_AVAILABLE 1 + #endif + + #if !defined(EASTL_STD_TYPE_TRAITS_AVAILABLE) + #if defined(_LIBCPP_VERSION) // This is defined by clang's libc++. + #include + + #elif defined(__GLIBCXX__) && (__GLIBCXX__ >= 20090124) // It's not clear if this is the oldest version that has type traits; probably it isn't. + #define EASTL_STD_TYPE_TRAITS_AVAILABLE 1 + + #if defined(__GXX_EXPERIMENTAL_CXX0X__) // To do: Update this test to include conforming C++11 implementations. + #include + #else + #include + #endif + #else + #define EASTL_STD_TYPE_TRAITS_AVAILABLE 0 + #endif + #endif + + #elif defined(__MSL_CPP__) && (__MSL_CPP__ >= 0x8000) // CodeWarrior compiler. + #define EASTL_STD_TYPE_TRAITS_AVAILABLE 0 + // To do: Implement support for this (via modifying the EASTL type + // traits headers, as CodeWarrior provides this. + #else + #define EASTL_STD_TYPE_TRAITS_AVAILABLE 0 + #endif + */ +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE +// +// Defined as 0 or 1; default is based on auto-detection. +// Specifies whether the compiler provides built-in compiler type trait support +// (e.g. __is_abstract()). Does not specify any details about which traits +// are available or what their standards-compliance is. Nevertheless this is a +// useful macro identifier for our type traits implementation. +// +#ifndef EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE + #if defined(_MSC_VER) && (_MSC_VER >= 1500) && !defined(EA_COMPILER_CLANG_CL) // VS2008 or later + #pragma warning(push, 0) + #include + #pragma warning(pop) + #if ((defined(_HAS_TR1) && _HAS_TR1) || _MSC_VER >= 1700) // VS2012 (1700) and later has built-in type traits support. + #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 1 + #else + #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 0 + #endif + #elif defined(__clang__) && defined(__APPLE__) && defined(_CXXCONFIG) // Apple clang but with GCC's libstdc++. + #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 0 + #elif defined(__clang__) + #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 1 + #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003) && !defined(__GCCXML__) + #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 1 + #elif defined(__MSL_CPP__) && (__MSL_CPP__ >= 0x8000) // CodeWarrior compiler. + #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 1 + #else + #define EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE 0 + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_RESET_ENABLED +// +// Defined as 0 or 1; default is 1 for the time being. +// The reset_lose_memory function works the same as reset, as described below. +// +// Specifies whether the container reset functionality is enabled. If enabled +// then ::reset forgets its memory, otherwise it acts as the clear +// function. The reset function is potentially dangerous, as it (by design) +// causes containers to not free their memory. +// This option has no applicability to the bitset::reset function, as bitset +// isn't really a container. Also it has no applicability to the smart pointer +// wrappers (e.g. intrusive_ptr). +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_RESET_ENABLED + #define EASTL_RESET_ENABLED 0 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_MINMAX_ENABLED +// +// Defined as 0 or 1; default is 1. +// Specifies whether the min and max algorithms are available. +// It may be useful to disable the min and max algorithms because sometimes +// #defines for min and max exist which would collide with EASTL min and max. +// Note that there are already alternative versions of min and max in EASTL +// with the min_alt and max_alt functions. You can use these without colliding +// with min/max macros that may exist. +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef EASTL_MINMAX_ENABLED + #define EASTL_MINMAX_ENABLED 1 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_NOMINMAX +// +// Defined as 0 or 1; default is 1. +// MSVC++ has #defines for min/max which collide with the min/max algorithm +// declarations. If EASTL_NOMINMAX is defined as 1, then we undefine min and +// max if they are #defined by an external library. This allows our min and +// max definitions in algorithm.h to work as expected. An alternative to +// the enabling of EASTL_NOMINMAX is to #define NOMINMAX in your project +// settings if you are compiling for Windows. +// Note that this does not control the availability of the EASTL min and max +// algorithms; the EASTL_MINMAX_ENABLED configuration parameter does that. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_NOMINMAX + #define EASTL_NOMINMAX 1 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_STD_CPP_ONLY +// +// Defined as 0 or 1; default is 0. +// Disables the use of compiler language extensions. We use compiler language +// extensions only in the case that they provide some benefit that can't be +// had any other practical way. But sometimes the compiler is set to disable +// language extensions or sometimes one compiler's preprocesor is used to generate +// code for another compiler, and so it's necessary to disable language extension usage. +// +// Example usage: +// #if defined(_MSC_VER) && !EASTL_STD_CPP_ONLY +// enum : size_type { npos = container_type::npos }; // Microsoft extension which results in significantly smaller debug symbols. +// #else +// static const size_type npos = container_type::npos; +// #endif +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_STD_CPP_ONLY + #define EASTL_STD_CPP_ONLY 0 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_NO_RVALUE_REFERENCES +// +// Defined as 0 or 1. +// This is the same as EABase EA_COMPILER_NO_RVALUE_REFERENCES except that it +// follows the convention of being always defined, as 0 or 1. +/////////////////////////////////////////////////////////////////////////////// +#if !defined(EASTL_NO_RVALUE_REFERENCES) + #if defined(EA_COMPILER_NO_RVALUE_REFERENCES) + #define EASTL_NO_RVALUE_REFERENCES 1 + #else + #define EASTL_NO_RVALUE_REFERENCES 0 + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_MOVE_SEMANTICS_ENABLED +// +// Defined as 0 or 1. +// If enabled then C++11-like functionality with rvalue references and move +// operations is enabled. +/////////////////////////////////////////////////////////////////////////////// +#if !defined(EASTL_MOVE_SEMANTICS_ENABLED) + #if EASTL_NO_RVALUE_REFERENCES // If the compiler doesn't support rvalue references or EASTL is configured to disable them... + #define EASTL_MOVE_SEMANTICS_ENABLED 0 + #else + #define EASTL_MOVE_SEMANTICS_ENABLED 1 + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_VARIADIC_TEMPLATES_ENABLED +// +// Defined as 0 or 1. +// If enabled then C++11-like functionality with variadic templates is enabled. +/////////////////////////////////////////////////////////////////////////////// +#if !defined(EASTL_VARIADIC_TEMPLATES_ENABLED) + #if defined(EA_COMPILER_NO_VARIADIC_TEMPLATES) // If the compiler doesn't support variadic templates + #define EASTL_VARIADIC_TEMPLATES_ENABLED 0 + #else + #define EASTL_VARIADIC_TEMPLATES_ENABLED 1 + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_VARIABLE_TEMPLATES_ENABLED +// +// Defined as 0 or 1. +// If enabled then C++11-like functionality with variable templates is enabled. +/////////////////////////////////////////////////////////////////////////////// +#if !defined(EASTL_VARIABLE_TEMPLATES_ENABLED) + #if((EABASE_VERSION_N < 20605) || defined(EA_COMPILER_NO_VARIABLE_TEMPLATES)) + #define EASTL_VARIABLE_TEMPLATES_ENABLED 0 + #else + #define EASTL_VARIABLE_TEMPLATES_ENABLED 1 + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_INLINE_VARIABLE_ENABLED +// +// Defined as 0 or 1. +// If enabled then C++17-like functionality with inline variable is enabled. +/////////////////////////////////////////////////////////////////////////////// +#if !defined(EASTL_INLINE_VARIABLE_ENABLED) + #if((EABASE_VERSION_N < 20707) || defined(EA_COMPILER_NO_INLINE_VARIABLES)) + #define EASTL_INLINE_VARIABLE_ENABLED 0 + #else + #define EASTL_INLINE_VARIABLE_ENABLED 1 + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_CPP17_INLINE_VARIABLE +// +// Used to prefix a variable as inline when C++17 inline variables are available +// Usage: EASTL_CPP17_INLINE_VARIABLE constexpr bool type_trait_v = type_trait::value +/////////////////////////////////////////////////////////////////////////////// +#if !defined(EASTL_CPP17_INLINE_VARIABLE) + #if EASTL_INLINE_VARIABLE_ENABLED + #define EASTL_CPP17_INLINE_VARIABLE inline + #else + #define EASTL_CPP17_INLINE_VARIABLE + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_HAVE_CPP11_TYPE_TRAITS +// +// Defined as 0 or 1. +// This is the same as EABase EA_HAVE_CPP11_TYPE_TRAITS except that it +// follows the convention of being always defined, as 0 or 1. Note that this +// identifies if the Standard Library has C++11 type traits and not if EASTL +// has its equivalents to C++11 type traits. +/////////////////////////////////////////////////////////////////////////////// +#if !defined(EASTL_HAVE_CPP11_TYPE_TRAITS) + // To do: Change this to use the EABase implementation once we have a few months of testing + // of this and we are sure it works right. Do this at some point after ~January 2014. + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ + #define EASTL_HAVE_CPP11_TYPE_TRAITS 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007) // Prior versions of libstdc++ have incomplete support for C++11 type traits. + #define EASTL_HAVE_CPP11_TYPE_TRAITS 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EASTL_HAVE_CPP11_TYPE_TRAITS 1 + #else + #define EASTL_HAVE_CPP11_TYPE_TRAITS 0 + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS undef +// +// We need revise this macro to be undefined in some cases, in case the user +// isn't using an updated EABase. +/////////////////////////////////////////////////////////////////////////////// +#if defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403) // It may in fact be supported by 4.01 or 4.02 but we don't have compilers to test with. + #if defined(EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) + #undef EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_NO_RANGE_BASED_FOR_LOOP +// +// Defined as 0 or 1. +// This is the same as EABase EA_COMPILER_NO_RANGE_BASED_FOR_LOOP except that it +// follows the convention of being always defined, as 0 or 1. +/////////////////////////////////////////////////////////////////////////////// +#if !defined(EASTL_NO_RANGE_BASED_FOR_LOOP) + #if defined(EA_COMPILER_NO_RANGE_BASED_FOR_LOOP) + #define EASTL_NO_RANGE_BASED_FOR_LOOP 1 + #else + #define EASTL_NO_RANGE_BASED_FOR_LOOP 0 + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_ALIGN_OF +// +// Determines the alignment of a type. +// +// Example usage: +// size_t alignment = EASTL_ALIGN_OF(int); +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef EASTL_ALIGN_OF + #define EASTL_ALIGN_OF alignof +#endif + + + + +/////////////////////////////////////////////////////////////////////////////// +// eastl_size_t +// +// Defined as an unsigned integer type, usually either size_t or uint32_t. +// Defaults to size_t to match std STL unless the user specifies to use +// uint32_t explicitly via the EASTL_SIZE_T_32BIT define +// +// Example usage: +// eastl_size_t n = intVector.size(); +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_SIZE_T_32BIT // Defines whether EASTL_SIZE_T uses uint32_t/int32_t as opposed to size_t/ssize_t. + #define EASTL_SIZE_T_32BIT 0 // This makes a difference on 64 bit platforms because they use a 64 bit size_t. +#endif // By default we do the same thing as std STL and use size_t. + +#ifndef EASTL_SIZE_T + #if (EASTL_SIZE_T_32BIT == 0) || (EA_PLATFORM_WORD_SIZE == 4) + #include + #define EASTL_SIZE_T size_t + #define EASTL_SSIZE_T intptr_t + #else + #define EASTL_SIZE_T uint32_t + #define EASTL_SSIZE_T int32_t + #endif +#endif + +typedef EASTL_SIZE_T eastl_size_t; // Same concept as std::size_t. +typedef EASTL_SSIZE_T eastl_ssize_t; // Signed version of eastl_size_t. Concept is similar to Posix's ssize_t. + + + + +/////////////////////////////////////////////////////////////////////////////// +// AddRef / Release +// +// AddRef and Release are used for "intrusive" reference counting. By the term +// "intrusive", we mean that the reference count is maintained by the object +// and not by the user of the object. Given that an object implements referencing +// counting, the user of the object needs to be able to increment and decrement +// that reference count. We do that via the venerable AddRef and Release functions +// which the object must supply. These defines here allow us to specify the name +// of the functions. They could just as well be defined to addref and delref or +// IncRef and DecRef. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTLAddRef + #define EASTLAddRef AddRef +#endif + +#ifndef EASTLRelease + #define EASTLRelease Release +#endif + + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_ALLOCATOR_EXPLICIT_ENABLED +// +// Defined as 0 or 1. Default is 0 for now but ideally would be changed to +// 1 some day. It's 0 because setting it to 1 breaks some existing code. +// This option enables the allocator ctor to be explicit, which avoids +// some undesirable silent conversions, especially with the string class. +// +// Example usage: +// class allocator +// { +// public: +// EASTL_ALLOCATOR_EXPLICIT allocator(const char* pName); +// }; +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_ALLOCATOR_EXPLICIT_ENABLED + #define EASTL_ALLOCATOR_EXPLICIT_ENABLED 0 +#endif + +#if EASTL_ALLOCATOR_EXPLICIT_ENABLED + #define EASTL_ALLOCATOR_EXPLICIT explicit +#else + #define EASTL_ALLOCATOR_EXPLICIT +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_ALLOCATOR_MIN_ALIGNMENT +// +// Defined as an integral power-of-2 that's >= 1. +// Identifies the minimum alignment that EASTL should assume its allocators +// use. There is code within EASTL that decides whether to do a Malloc or +// MallocAligned call and it's typically better if it can use the Malloc call. +// But this requires knowing what the minimum possible alignment is. +#if !defined(EASTL_ALLOCATOR_MIN_ALIGNMENT) + #define EASTL_ALLOCATOR_MIN_ALIGNMENT EA_PLATFORM_MIN_MALLOC_ALIGNMENT +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_SYSTEM_ALLOCATOR_MIN_ALIGNMENT +// +// Identifies the minimum alignment that EASTL should assume system allocations +// from malloc and new will have. +#if !defined(EASTL_SYSTEM_ALLOCATOR_MIN_ALIGNMENT) + #if defined(EA_PLATFORM_MICROSOFT) || defined(EA_PLATFORM_APPLE) + #define EASTL_SYSTEM_ALLOCATOR_MIN_ALIGNMENT 16 + #else + #define EASTL_SYSTEM_ALLOCATOR_MIN_ALIGNMENT (EA_PLATFORM_PTR_SIZE * 2) + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL allocator +// +// The EASTL allocator system allows you to redefine how memory is allocated +// via some defines that are set up here. In the container code, memory is +// allocated via macros which expand to whatever the user has them set to +// expand to. Given that there are multiple allocator systems available, +// this system allows you to configure it to use whatever system you want, +// provided your system meets the requirements of this library. +// The requirements are: +// +// - Must be constructable via a const char* (name) parameter. +// Some uses of allocators won't require this, however. +// - Allocate a block of memory of size n and debug name string. +// - Allocate a block of memory of size n, debug name string, +// alignment a, and offset o. +// - Free memory allocated via either of the allocation functions above. +// - Provide a default allocator instance which can be used if the user +// doesn't provide a specific one. +// +/////////////////////////////////////////////////////////////////////////////// + +// namespace eastl +// { +// class allocator +// { +// 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(); // This is used for anonymous allocations. +// } + +#ifndef EASTLAlloc // To consider: Instead of calling through pAllocator, just go directly to operator new, since that's what allocator does. + #define EASTLAlloc(allocator, n) (allocator).allocate(n); +#endif + +#ifndef EASTLAllocFlags // To consider: Instead of calling through pAllocator, just go directly to operator new, since that's what allocator does. + #define EASTLAllocFlags(allocator, n, flags) (allocator).allocate(n, flags); +#endif + +#ifndef EASTLAllocAligned + #define EASTLAllocAligned(allocator, n, alignment, offset) (allocator).allocate((n), (alignment), (offset)) +#endif + +#ifndef EASTLAllocAlignedFlags + #define EASTLAllocAlignedFlags(allocator, n, alignment, offset, flags) (allocator).allocate((n), (alignment), (offset), (flags)) +#endif + +#ifndef EASTLFree + #define EASTLFree(allocator, p, size) (allocator).deallocate((void*)(p), (size)) // Important to cast to void* as p may be non-const. +#endif + +#ifndef EASTLAllocatorType + #define EASTLAllocatorType eastl::allocator +#endif + +#ifndef EASTLDummyAllocatorType + #define EASTLDummyAllocatorType eastl::dummy_allocator +#endif + +#ifndef EASTLAllocatorDefault + // EASTLAllocatorDefault returns the default allocator instance. This is not a global + // allocator which implements all container allocations but is the allocator that is + // used when EASTL needs to allocate memory internally. There are very few cases where + // EASTL allocates memory internally, and in each of these it is for a sensible reason + // that is documented to behave as such. + #define EASTLAllocatorDefault eastl::GetDefaultAllocator +#endif + + +/// EASTL_ALLOCATOR_DEFAULT_NAME +/// +/// Defines a default allocator name in the absence of a user-provided name. +/// +#ifndef EASTL_ALLOCATOR_DEFAULT_NAME + #define EASTL_ALLOCATOR_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX // Unless the user overrides something, this is "EASTL". +#endif + +/// EASTL_USE_FORWARD_WORKAROUND +/// +/// This is to workaround a compiler bug that we found in VS2013. Update 1 did not fix it. +/// This should be fixed in a future release of VS2013 http://accentuable4.rssing.com/browser.php?indx=3511740&item=15696 +/// +#ifndef EASTL_USE_FORWARD_WORKAROUND + #if defined(_MSC_FULL_VER) && _MSC_FULL_VER == 180021005 || (defined(__EDG_VERSION__) && (__EDG_VERSION__ < 405))// VS2013 initial release + #define EASTL_USE_FORWARD_WORKAROUND 1 + #else + #define EASTL_USE_FORWARD_WORKAROUND 0 + #endif +#endif + + +/// EASTL_TUPLE_ENABLED +/// EASTL tuple implementation depends on variadic template support +#if EASTL_VARIADIC_TEMPLATES_ENABLED && !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + #define EASTL_TUPLE_ENABLED 1 +#else + #define EASTL_TUPLE_ENABLED 0 +#endif + + +/// EASTL_FUNCTION_ENABLED +/// +#ifndef EASTL_FUNCTION_ENABLED + #define EASTL_FUNCTION_ENABLED 1 +#endif + + +/// EASTL_USER_LITERALS_ENABLED +#ifndef EASTL_USER_LITERALS_ENABLED + #if defined(EA_COMPILER_CPP14_ENABLED) + #define EASTL_USER_LITERALS_ENABLED 1 + + // Disabling the Clang/GCC/MSVC warning about using user defined literals without a leading '_' as they are + // reserved for standard libary usage. + EA_DISABLE_CLANG_WARNING(-Wuser-defined-literals) + EA_DISABLE_CLANG_WARNING(-Wreserved-user-defined-literal) + EA_DISABLE_GCC_WARNING(-Wliteral-suffix) + #ifdef _MSC_VER + #pragma warning(disable: 4455) // disable warning C4455: literal suffix identifiers that do not start with an underscore are reserved + #endif + + #else + #define EASTL_USER_LITERALS_ENABLED 0 + #endif +#endif + + +/// EASTL_INLINE_NAMESPACES_ENABLED +#ifndef EASTL_INLINE_NAMESPACES_ENABLED + #if defined(EA_COMPILER_CPP14_ENABLED) + #define EASTL_INLINE_NAMESPACES_ENABLED 1 + #else + #define EASTL_INLINE_NAMESPACES_ENABLED 0 + #endif +#endif + + +/// EASTL_CORE_ALLOCATOR_ENABLED +#ifndef EASTL_CORE_ALLOCATOR_ENABLED + #define EASTL_CORE_ALLOCATOR_ENABLED 0 +#endif + +/// EASTL_OPENSOURCE +/// This is enabled when EASTL is building built in an "open source" mode. Which is a mode that eliminates code +/// dependencies on other technologies that have not been released publically. +/// EASTL_OPENSOURCE = 0, is the default. +/// EASTL_OPENSOURCE = 1, utilizes technologies that not publically available. +/// +#ifndef EASTL_OPENSOURCE + #define EASTL_OPENSOURCE 0 +#endif + + +/// EASTL_OPTIONAL_ENABLED +#if defined(EA_COMPILER_MSVC_2012) + #define EASTL_OPTIONAL_ENABLED 0 +#elif defined(EA_COMPILER_MSVC_2013) + #define EASTL_OPTIONAL_ENABLED 0 +#elif defined(EA_COMPILER_MSVC_2015) + #define EASTL_OPTIONAL_ENABLED 1 +#elif EASTL_VARIADIC_TEMPLATES_ENABLED && !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) && !defined(EA_COMPILER_NO_DEFAULTED_FUNCTIONS) && defined(EA_COMPILER_CPP11_ENABLED) + #define EASTL_OPTIONAL_ENABLED 1 +#else + #define EASTL_OPTIONAL_ENABLED 0 +#endif + + +/// EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE +#if defined(__clang__) + #if !__is_identifier(__has_unique_object_representations) + #define EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE 1 + #else + #define EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE 0 + #endif +#elif defined(_MSC_VER) && (_MSC_VER >= 1913) // VS2017+ + #define EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE 1 +#else + #define EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE 0 +#endif + + +/// EASTL_ENABLE_PAIR_FIRST_ELEMENT_CONSTRUCTOR +/// This feature define allows users to toggle the problematic eastl::pair implicit +/// single element constructor. +#ifndef EASTL_ENABLE_PAIR_FIRST_ELEMENT_CONSTRUCTOR + #define EASTL_ENABLE_PAIR_FIRST_ELEMENT_CONSTRUCTOR 0 +#endif + +/// EASTL_SYSTEM_BIG_ENDIAN_STATEMENT +/// EASTL_SYSTEM_LITTLE_ENDIAN_STATEMENT +/// These macros allow you to write endian specific macros as statements. +/// This allows endian specific code to be macro expanded from within other macros +/// +#if defined(EA_SYSTEM_BIG_ENDIAN) + #define EASTL_SYSTEM_BIG_ENDIAN_STATEMENT(...) __VA_ARGS__ +#else + #define EASTL_SYSTEM_BIG_ENDIAN_STATEMENT(...) +#endif + +#if defined(EA_SYSTEM_LITTLE_ENDIAN) + #define EASTL_SYSTEM_LITTLE_ENDIAN_STATEMENT(...) __VA_ARGS__ +#else + #define EASTL_SYSTEM_LITTLE_ENDIAN_STATEMENT(...) +#endif + +/// EASTL_CONSTEXPR_BIT_CAST_SUPPORTED +/// eastl::bit_cast, in order to be implemented as constexpr, requires explicit compiler support. +/// This macro defines whether it's possible for bit_cast to be constexpr. +/// +#if (defined(EA_COMPILER_MSVC) && defined(EA_COMPILER_MSVC_VERSION_14_26) && EA_COMPILER_VERSION >= EA_COMPILER_MSVC_VERSION_14_26) \ + || EA_COMPILER_HAS_BUILTIN(__builtin_bit_cast) + #define EASTL_CONSTEXPR_BIT_CAST_SUPPORTED 1 +#else + #define EASTL_CONSTEXPR_BIT_CAST_SUPPORTED 0 +#endif + + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/internal/copy_help.h b/lib/EASTL/include/EASTL/internal/copy_help.h new file mode 100644 index 000000000..67b5d8769 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/copy_help.h @@ -0,0 +1,200 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_COPY_HELP_H +#define EASTL_INTERNAL_COPY_HELP_H + +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include +#include // memcpy, memcmp, memmove + + +namespace eastl +{ + /// move / move_n / move_backward + /// copy / copy_n / copy_backward + /// + /// We want to optimize move, move_n, move_backward, copy, copy_backward, copy_n to do memmove operations + /// when possible. + /// + /// We could possibly use memcpy, though it has stricter overlap requirements than the move and copy + /// algorithms and would require a runtime if/else to choose it over memmove. In particular, memcpy + /// allows no range overlap at all, whereas move/copy allow output end overlap and move_backward/copy_backward + /// allow output begin overlap. Despite this it might be useful to use memcpy for any platforms where + /// memcpy is significantly faster than memmove, and since in most cases the copy/move operation in fact + /// doesn't target overlapping memory and so memcpy would be usable. + /// + /// We can use memmove/memcpy if the following hold true: + /// InputIterator and OutputIterator are of the same type. + /// InputIterator and OutputIterator are of type contiguous_iterator_tag or simply are pointers (the two are virtually synonymous). + /// is_trivially_copyable::value is true. i.e. the constructor T(const T& t) (or T(T&& t) if present) can be replaced by memmove(this, &t, sizeof(T)) + /// + /// copy normally differs from move, but there is a case where copy is the same as move: when copy is + /// used with a move_iterator. We handle that case here by detecting that copy is being done with a + /// move_iterator and redirect it to move (which can take advantage of memmove/memcpy). + /// + /// The generic_iterator class is typically used for wrapping raw memory pointers so they can act like + /// formal iterators. Since pointers provide an opportunity for memmove/memcpy operations, we can + /// detect a generic iterator and use it's wrapped type as a pointer if it happens to be one. + + // Implementation moving copying both trivial and non-trivial data via a lesser iterator than random-access. + template + struct move_and_copy_helper + { + template + static OutputIterator move_or_copy(InputIterator first, InputIterator last, OutputIterator result) + { + for(; first != last; ++result, ++first) + *result = *first; + return result; + } + }; + + // Specialization for copying non-trivial data via a random-access iterator. It's theoretically faster because the compiler can see the count when its a compile-time const. + // This specialization converts the random access InputIterator last-first to an integral type. There's simple way for us to take advantage of a random access output iterator, + // as the range is specified by the input instead of the output, and distance(first, last) for a non-random-access iterator is potentially slow. + template <> + struct move_and_copy_helper + { + template + static OutputIterator move_or_copy(InputIterator first, InputIterator last, OutputIterator result) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + + for(difference_type n = (last - first); n > 0; --n, ++first, ++result) + *result = *first; + + return result; + } + }; + + // Specialization for moving non-trivial data via a lesser iterator than random-access. + template + struct move_and_copy_helper + { + template + static OutputIterator move_or_copy(InputIterator first, InputIterator last, OutputIterator result) + { + for(; first != last; ++result, ++first) + *result = eastl::move(*first); + return result; + } + }; + + // Specialization for moving non-trivial data via a random-access iterator. It's theoretically faster because the compiler can see the count when its a compile-time const. + template <> + struct move_and_copy_helper + { + template + static OutputIterator move_or_copy(InputIterator first, InputIterator last, OutputIterator result) + { + typedef typename eastl::iterator_traits::difference_type difference_type; + + for(difference_type n = (last - first); n > 0; --n, ++first, ++result) + *result = eastl::move(*first); + + return result; + } + }; + + // Specialization for when we can use memmove/memcpy. See the notes above for what conditions allow this. + template + struct move_and_copy_helper + { + template + static T* move_or_copy(const T* first, const T* last, T* result) + { + if (EASTL_UNLIKELY(first == last)) + return result; + + // We could use memcpy here if there's no range overlap, but memcpy is rarely much faster than memmove. + return (T*)memmove(result, first, (size_t)((uintptr_t)last - (uintptr_t)first)) + (last - first); + } + }; + + + + template + inline OutputIterator move_and_copy_chooser(InputIterator first, InputIterator last, OutputIterator result) + { + typedef typename eastl::iterator_traits::iterator_category IIC; + typedef typename eastl::iterator_traits::iterator_category OIC; + typedef typename eastl::iterator_traits::value_type value_type_input; + typedef typename eastl::iterator_traits::value_type value_type_output; + + const bool canBeMemmoved = eastl::is_trivially_copyable::value && + eastl::is_same::value && + (eastl::is_pointer::value || eastl::is_same::value) && + (eastl::is_pointer::value || eastl::is_same::value); + + return eastl::move_and_copy_helper::move_or_copy(first, last, result); // Need to chose based on the input iterator tag and not the output iterator tag, because containers accept input ranges of iterator types different than self. + } + + + // We have a second layer of unwrap_iterator calls because the original iterator might be something like move_iterator > (i.e. doubly-wrapped). + template + inline OutputIterator move_and_copy_unwrapper(InputIterator first, InputIterator last, OutputIterator result) + { + return OutputIterator(eastl::move_and_copy_chooser(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), eastl::unwrap_iterator(result))); // Have to convert to OutputIterator because result.base() could be a T* + } + + + /// move + /// + /// After this operation the elements in the moved-from range will still contain valid values of the + /// appropriate type, but not necessarily the same values as before the move. + /// Returns the end of the result range. + /// Note: When moving between containers, the dest range must be valid; this function doesn't resize containers. + /// Note: if result is within [first, last), move_backward must be used instead of move. + /// + /// Example usage: + /// eastl::move(myArray.begin(), myArray.end(), myDestArray.begin()); + /// + /// Reference implementation: + /// template + /// OutputIterator move(InputIterator first, InputIterator last, OutputIterator result) + /// { + /// while(first != last) + /// *result++ = eastl::move(*first++); + /// return result; + /// } + + template + inline OutputIterator move(InputIterator first, InputIterator last, OutputIterator result) + { + return eastl::move_and_copy_unwrapper(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), result); + } + + + /// copy + /// + /// Effects: Copies elements in the range [first, last) into the range [result, result + (last - first)) + /// starting from first and proceeding to last. For each nonnegative integer n < (last - first), + /// performs *(result + n) = *(first + n). + /// + /// Returns: result + (last - first). That is, returns the end of the result. Note that this + /// is different from how memmove/memcpy work, as they return the beginning of the result. + /// + /// Requires: result shall not be in the range [first, last). But the end of the result range + /// may in fact be within the input rante. + /// + /// Complexity: Exactly 'last - first' assignments. + /// + template + inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) + { + const bool isMove = eastl::is_move_iterator::value; EA_UNUSED(isMove); + + return eastl::move_and_copy_unwrapper(eastl::unwrap_iterator(first), eastl::unwrap_iterator(last), result); + } +} // namespace eastl + +#endif // EASTL_INTERNAL_COPY_HELP_H diff --git a/lib/EASTL/include/EASTL/internal/enable_shared.h b/lib/EASTL/include/EASTL/internal/enable_shared.h new file mode 100644 index 000000000..ac5f0729c --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/enable_shared.h @@ -0,0 +1,83 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_ENABLE_SHARED_H +#define EASTL_INTERNAL_ENABLE_SHARED_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +namespace eastl +{ + + /// enable_shared_from_this + /// + /// This is a helper mixin class that allows you to make any class + /// export a shared_ptr instance that is associated with the class + /// instance. Any class that inherits from this class gets two functions: + /// shared_ptr shared_from_this(); + /// shared_ptr shared_from_this() const; + /// If you call shared_from_this, you get back a shared_ptr that + /// refers to the class. A second call to shared_from_this returns + /// another shared_ptr that is shared with the first one. + /// + /// The trick that happens which is not so obvious here (and which is + /// not mentioned at all in the Boost documentation of their version + /// of this) is that the shared_ptr constructor detects that the + /// class has an enable_shared_from_this mixin and sets up this system + /// automatically for the user. This is done with template tricks. + /// + /// For some additional explanation, see the Boost documentation for + /// their description of their version of enable_shared_from_this. + /// + template + class enable_shared_from_this + { + public: + shared_ptr shared_from_this() + { return shared_ptr(mWeakPtr); } + + shared_ptr shared_from_this() const + { return shared_ptr(mWeakPtr); } + + weak_ptr weak_from_this() + { return mWeakPtr; } + + weak_ptr weak_from_this() const + { return mWeakPtr; } + + public: // This is public because the alternative fails on some compilers that we need to support. + mutable weak_ptr mWeakPtr; + + protected: + template friend class shared_ptr; + + EA_CONSTEXPR enable_shared_from_this() EA_NOEXCEPT + { } + + enable_shared_from_this(const enable_shared_from_this&) EA_NOEXCEPT + { } + + enable_shared_from_this& operator=(const enable_shared_from_this&) EA_NOEXCEPT + { return *this; } + + ~enable_shared_from_this() + { } + + }; // enable_shared_from_this + +} // namespace eastl + + +#endif // Header include guard + + + + + + diff --git a/lib/EASTL/include/EASTL/internal/fill_help.h b/lib/EASTL/include/EASTL/internal/fill_help.h new file mode 100644 index 000000000..07e3b62dd --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/fill_help.h @@ -0,0 +1,484 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_FILL_HELP_H +#define EASTL_INTERNAL_FILL_HELP_H + + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include + +#if defined(EA_COMPILER_MICROSOFT) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) +#include +#endif + +namespace eastl +{ + // fill + // + // We implement some fill helper functions in order to allow us to optimize it + // where possible. + // + template + struct fill_imp + { + template + static void do_fill(ForwardIterator first, ForwardIterator last, const T& value) + { + // The C++ standard doesn't specify whether we need to create a temporary + // or not, but all std STL implementations are written like what we have here. + for(; first != last; ++first) + *first = value; + } + }; + + template <> + struct fill_imp + { + template + static void do_fill(ForwardIterator first, ForwardIterator last, const T& value) + { + typedef typename eastl::iterator_traits::value_type value_type; + // We create a temp and fill from that because value might alias to the + // destination range and so the compiler would be forced into generating + // less efficient code. + for(const T temp = value; first != last; ++first) + { + EA_UNUSED(temp); + *first = static_cast(temp); + } + } + }; + + /// fill + /// + /// fill is like memset in that it assigns a single value repeatedly to a + /// destination range. It allows for any type of iterator (not just an array) + /// and the source value can be any type, not just a byte. + /// Note that the source value (which is a reference) can come from within + /// the destination range. + /// + /// Effects: Assigns value through all the iterators in the range [first, last). + /// + /// Complexity: Exactly 'last - first' assignments. + /// + /// Note: The C++ standard doesn't specify anything about the value parameter + /// coming from within the first-last range. All std STL implementations act + /// as if the standard specifies that value must not come from within this range. + /// + template + inline void fill(ForwardIterator first, ForwardIterator last, const T& value) + { + eastl::fill_imp< is_scalar::value >::do_fill(first, last, value); + + // Possibly better implementation, as it will deal with small PODs as well as scalars: + // bEasyCopy is true if the type has a trivial constructor (e.g. is a POD) and if + // it is small. Thus any built-in type or any small user-defined struct will qualify. + //const bool bEasyCopy = eastl::type_and::value, + // eastl::integral_constant::value; + //eastl::fill_imp::do_fill(first, last, value); + + } + + #if (defined(EA_COMPILER_GNUC) || defined(__clang__)) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + #if defined(EA_PROCESSOR_X86_64) + template + inline void fill(uint64_t* first, uint64_t* last, Value c) + { + uintptr_t count = (uintptr_t)(last - first); + uint64_t value = (uint64_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosq\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + } + + + template + inline void fill(int64_t* first, int64_t* last, Value c) + { + uintptr_t count = (uintptr_t)(last - first); + int64_t value = (int64_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosq\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + } + #endif + + template + inline void fill(uint32_t* first, uint32_t* last, Value c) + { + uintptr_t count = (uintptr_t)(last - first); + uint32_t value = (uint32_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosl\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + } + + + template + inline void fill(int32_t* first, int32_t* last, Value c) + { + uintptr_t count = (uintptr_t)(last - first); + int32_t value = (int32_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosl\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + } + + + template + inline void fill(uint16_t* first, uint16_t* last, Value c) + { + uintptr_t count = (uintptr_t)(last - first); + uint16_t value = (uint16_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosw\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + } + + + template + inline void fill(int16_t* first, int16_t* last, Value c) + { + uintptr_t count = (uintptr_t)(last - first); + int16_t value = (int16_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosw\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + } + + #elif defined(EA_COMPILER_MICROSOFT) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + #if defined(EA_PROCESSOR_X86_64) + template + inline void fill(uint64_t* first, uint64_t* last, Value c) + { + __stosq(first, (uint64_t)c, (size_t)(last - first)); + } + + template + inline void fill(int64_t* first, int64_t* last, Value c) + { + __stosq((uint64_t*)first, (uint64_t)c, (size_t)(last - first)); + } + #endif + + template + inline void fill(uint32_t* first, uint32_t* last, Value c) + { + __stosd((unsigned long*)first, (unsigned long)c, (size_t)(last - first)); + } + + template + inline void fill(int32_t* first, int32_t* last, Value c) + { + __stosd((unsigned long*)first, (unsigned long)c, (size_t)(last - first)); + } + + template + inline void fill(uint16_t* first, uint16_t* last, Value c) + { + __stosw(first, (uint16_t)c, (size_t)(last - first)); + } + + template + inline void fill(int16_t* first, int16_t* last, Value c) + { + __stosw((uint16_t*)first, (uint16_t)c, (size_t)(last - first)); + } + #endif + + + inline void fill(char* first, char* last, const char& c) // It's debateable whether we should use 'char& c' or 'char c' here. + { + memset(first, (unsigned char)c, (size_t)(last - first)); + } + + inline void fill(char* first, char* last, const int c) // This is used for cases like 'fill(first, last, 0)'. + { + memset(first, (unsigned char)c, (size_t)(last - first)); + } + + inline void fill(unsigned char* first, unsigned char* last, const unsigned char& c) + { + memset(first, (unsigned char)c, (size_t)(last - first)); + } + + inline void fill(unsigned char* first, unsigned char* last, const int c) + { + memset(first, (unsigned char)c, (size_t)(last - first)); + } + + inline void fill(signed char* first, signed char* last, const signed char& c) + { + memset(first, (unsigned char)c, (size_t)(last - first)); + } + + inline void fill(signed char* first, signed char* last, const int c) + { + memset(first, (unsigned char)c, (size_t)(last - first)); + } + + #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__ICL) // ICL = Intel compiler + inline void fill(bool* first, bool* last, const bool& b) + { + memset(first, (char)b, (size_t)(last - first)); + } + #endif + + + + + // fill_n + // + // We implement some fill helper functions in order to allow us to optimize it + // where possible. + // + template + struct fill_n_imp + { + template + static OutputIterator do_fill(OutputIterator first, Size n, const T& value) + { + for(; n-- > 0; ++first) + *first = value; + return first; + } + }; + + template <> + struct fill_n_imp + { + template + static OutputIterator do_fill(OutputIterator first, Size n, const T& value) + { + typedef typename eastl::iterator_traits::value_type value_type; + + // We create a temp and fill from that because value might alias to + // the destination range and so the compiler would be forced into + // generating less efficient code. + for(const T temp = value; n-- > 0; ++first) + *first = static_cast(temp); + return first; + } + }; + + /// fill_n + /// + /// The fill_n function is very much like memset in that a copies a source value + /// n times into a destination range. The source value may come from within + /// the destination range. + /// + /// Effects: Assigns value through all the iterators in the range [first, first + n). + /// + /// Complexity: Exactly n assignments. + /// + template + OutputIterator fill_n(OutputIterator first, Size n, const T& value) + { + return eastl::fill_n_imp::value>::do_fill(first, n, value); + } + + template + inline char* fill_n(char* first, Size n, const char& c) + { + return (char*)memset(first, (char)c, (size_t)n) + n; + } + + template + inline unsigned char* fill_n(unsigned char* first, Size n, const unsigned char& c) + { + return (unsigned char*)memset(first, (unsigned char)c, (size_t)n) + n; + } + + template + inline signed char* fill_n(signed char* first, Size n, const signed char& c) + { + return (signed char*)memset(first, (signed char)c, n) + (size_t)n; + } + + #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__ICL) // ICL = Intel compiler + template + inline bool* fill_n(bool* first, Size n, const bool& b) + { + return (bool*)memset(first, (char)b, n) + (size_t)n; + } + #endif + + #if (defined(EA_COMPILER_GNUC) || defined(__clang__)) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + #if defined(EA_PROCESSOR_X86_64) + template + inline uint64_t* fill_n(uint64_t* first, Size n, Value c) + { + uintptr_t count = (uintptr_t)(n); + uint64_t value = (uint64_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosq\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + return first; // first is updated by the code above. + } + + + template + inline int64_t* fill_n(int64_t* first, Size n, Value c) + { + uintptr_t count = (uintptr_t)(n); + int64_t value = (int64_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosq\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + return first; // first is updated by the code above. + } + #endif + + template + inline uint32_t* fill_n(uint32_t* first, Size n, Value c) + { + uintptr_t count = (uintptr_t)(n); + uint32_t value = (uint32_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosl\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + return first; // first is updated by the code above. + } + + + template + inline int32_t* fill_n(int32_t* first, Size n, Value c) + { + uintptr_t count = (uintptr_t)(n); + int32_t value = (int32_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosl\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + return first; // first is updated by the code above. + } + + + template + inline uint16_t* fill_n(uint16_t* first, Size n, Value c) + { + uintptr_t count = (uintptr_t)(n); + uint16_t value = (uint16_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosw\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + return first; // first is updated by the code above. + } + + + template + inline int16_t* fill_n(int16_t* first, Size n, Value c) + { + uintptr_t count = (uintptr_t)(n); + int16_t value = (int16_t)(c); + + __asm__ __volatile__ ("cld\n\t" + "rep stosw\n\t" + : "+c" (count), "+D" (first), "=m" (first) + : "a" (value) + : "cc" ); + return first; // first is updated by the code above. + } + + #elif defined(EA_COMPILER_MICROSOFT) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + #if defined(EA_PROCESSOR_X86_64) + template + inline uint64_t* fill_n(uint64_t* first, Size n, Value c) + { + __stosq(first, (uint64_t)c, (size_t)n); + return first + n; + } + + template + inline int64_t* fill_n(int64_t* first, Size n, Value c) + { + __stosq((uint64_t*)first, (uint64_t)c, (size_t)n); + return first + n; + } + #endif + + template + inline uint32_t* fill_n(uint32_t* first, Size n, Value c) + { + __stosd((unsigned long*)first, (unsigned long)c, (size_t)n); + return first + n; + } + + template + inline int32_t* fill_n(int32_t* first, Size n, Value c) + { + __stosd((unsigned long*)first, (unsigned long)c, (size_t)n); + return first + n; + } + + template + inline uint16_t* fill_n(uint16_t* first, Size n, Value c) + { + __stosw(first, (uint16_t)c, (size_t)n); + return first + n; + } + + template + inline int16_t* fill_n(int16_t* first, Size n, Value c) + { + __stosw((uint16_t*)first, (uint16_t)c, (size_t)n); + return first + n; + } + #endif + +} // namespace eastl + +#endif // Header include guard + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/internal/fixed_pool.h b/lib/EASTL/include/EASTL/internal/fixed_pool.h new file mode 100644 index 000000000..4d7103548 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/fixed_pool.h @@ -0,0 +1,1630 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This file implements the following +// aligned_buffer +// fixed_pool_base +// fixed_pool +// fixed_pool_with_overflow +// fixed_hashtable_allocator +// fixed_vector_allocator +// fixed_swap +// +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_FIXED_POOL_H +#define EASTL_INTERNAL_FIXED_POOL_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include +#include +#include +#include + + +EA_DISABLE_ALL_VC_WARNINGS(); +#include +EA_RESTORE_ALL_VC_WARNINGS(); + +// 4275 - non dll-interface class used as base for DLL-interface classkey 'identifier' +EA_DISABLE_VC_WARNING(4275); + + +namespace eastl +{ + + /// EASTL_FIXED_POOL_DEFAULT_NAME + /// + /// Defines a default allocator name in the absence of a user-provided name. + /// + #ifndef EASTL_FIXED_POOL_DEFAULT_NAME + #define EASTL_FIXED_POOL_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " fixed_pool" // Unless the user overrides something, this is "EASTL fixed_pool". + #endif + + + + /////////////////////////////////////////////////////////////////////////// + // aligned_buffer + /////////////////////////////////////////////////////////////////////////// + + /// aligned_buffer + /// + /// This is useful for creating a buffer of the same size and alignment + /// of a given struct or class. This is useful for creating memory pools + /// that support both size and alignment requirements of stored objects + /// but without wasting space in over-allocating. + /// + /// Note that we implement this via struct specializations, as some + /// compilers such as VC++ do not support specification of alignments + /// in any way other than via an integral constant. + /// + /// Example usage: + /// struct Widget{ }; // This class has a given size and alignment. + /// + /// Declare a char buffer of equal size and alignment to Widget. + /// aligned_buffer mWidgetBuffer; + /// + /// Declare an array this time. + /// aligned_buffer mWidgetArray[15]; + /// + typedef char EASTL_MAY_ALIAS aligned_buffer_char; + + template + struct aligned_buffer { aligned_buffer_char buffer[size]; }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(2) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(2); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(4) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(4); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(8) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(8); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(16) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(16); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(32) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(32); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(64) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(64); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(128) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(128); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(256) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(256); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(512) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(512); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(1024) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(1024); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(2048) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(2048); }; + + template + struct aligned_buffer { EA_PREFIX_ALIGN(4096) aligned_buffer_char buffer[size] EA_POSTFIX_ALIGN(4096); }; + + + + + /////////////////////////////////////////////////////////////////////////// + // fixed_pool_base + /////////////////////////////////////////////////////////////////////////// + + /// fixed_pool_base + /// + /// This is a base class for the implementation of fixed-size pools. + /// In particular, the fixed_pool and fixed_pool_with_overflow classes + /// are based on fixed_pool_base. + /// + struct fixed_pool_base + { + public: + /// fixed_pool_base + /// + fixed_pool_base(void* pMemory = NULL) + : mpHead((Link*)pMemory) + , mpNext((Link*)pMemory) + , mpCapacity((Link*)pMemory) + , mnNodeSize(0) // This is normally set in the init function. + { + #if EASTL_FIXED_SIZE_TRACKING_ENABLED + mnCurrentSize = 0; + mnPeakSize = 0; + #endif + } + + + /// fixed_pool_base + /// + // Disabled because the default is sufficient. While it normally makes no sense to deep copy + // this data, our usage of this class is such that this is OK and wanted. + // + // fixed_pool_base(const fixed_pool_base& x) + // { + // } + + + /// operator= + /// + fixed_pool_base& operator=(const fixed_pool_base&) + { + // By design we do nothing. We don't attempt to deep-copy member data. + return *this; + } + + + /// init + /// + /// Initializes a fixed_pool with a given set of parameters. + /// You cannot call this function twice else the resulting + /// behaviour will be undefined. You can only call this function + /// after constructing the fixed_pool with the default constructor. + /// + EASTL_API void init(void* pMemory, size_t memorySize, size_t nodeSize, + size_t alignment, size_t alignmentOffset = 0); + + + /// peak_size + /// + /// Returns the maximum number of outstanding allocations there have been + /// at any one time. This represents a high water mark for the allocation count. + /// + size_t peak_size() const + { + #if EASTL_FIXED_SIZE_TRACKING_ENABLED + return mnPeakSize; + #else + return 0; + #endif + } + + + /// can_allocate + /// + /// Returns true if there are any free links. + /// + bool can_allocate() const + { + return (mpHead != NULL) || (mpNext != mpCapacity); + } + + public: + /// Link + /// Implements a singly-linked list. + struct Link + { + Link* mpNext; + }; + + Link* mpHead; + Link* mpNext; + Link* mpCapacity; + size_t mnNodeSize; + + #if EASTL_FIXED_SIZE_TRACKING_ENABLED + uint32_t mnCurrentSize; /// Current number of allocated nodes. + uint32_t mnPeakSize; /// Max number of allocated nodes at any one time. + #endif + + }; // fixed_pool_base + + + + + + /////////////////////////////////////////////////////////////////////////// + // fixed_pool + /////////////////////////////////////////////////////////////////////////// + + /// fixed_pool + /// + /// Implements a simple fixed pool allocator for use by fixed-size containers. + /// This is not a generic eastl allocator which can be plugged into an arbitrary + /// eastl container, as it simplifies some functions are arguments for the + /// purpose of efficiency. + /// + class EASTL_API fixed_pool : public fixed_pool_base + { + public: + /// fixed_pool + /// + /// Default constructor. User usually will want to call init() after + /// constructing via this constructor. The pMemory argument is for the + /// purposes of temporarily storing a pointer to the buffer to be used. + /// Even though init may have a pMemory argument, this arg is useful + /// for temporary storage, as per copy construction. + /// + fixed_pool(void* pMemory = NULL) + : fixed_pool_base(pMemory) + { + } + + + /// fixed_pool + /// + /// Constructs a fixed_pool with a given set of parameters. + /// + fixed_pool(void* pMemory, size_t memorySize, size_t nodeSize, + size_t alignment, size_t alignmentOffset = 0) + { + init(pMemory, memorySize, nodeSize, alignment, alignmentOffset); + } + + + /// fixed_pool + /// + // Disabled because the default is sufficient. While it normally makes no sense to deep copy + // this data, our usage of this class is such that this is OK and wanted. + // + // fixed_pool(const fixed_pool& x) + // { + // } + + + /// operator= + /// + fixed_pool& operator=(const fixed_pool&) + { + // By design we do nothing. We don't attempt to deep-copy member data. + return *this; + } + + + /// allocate + /// + /// Allocates a new object of the size specified upon class initialization. + /// Returns NULL if there is no more memory. + /// + void* allocate() + { + 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(reinterpret_cast(mpNext) + mnNodeSize); + + #if EASTL_FIXED_SIZE_TRACKING_ENABLED + if(++mnCurrentSize > mnPeakSize) + mnPeakSize = mnCurrentSize; + #endif + + return pLink; + } + + return NULL; + } + } + + void* allocate(size_t /*alignment*/, size_t /*offset*/) + { + return allocate(); + } + + /// 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) + { + #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_pool + + + + + + /////////////////////////////////////////////////////////////////////////// + // fixed_pool_with_overflow + /////////////////////////////////////////////////////////////////////////// + + /// fixed_pool_with_overflow + /// + template + class fixed_pool_with_overflow : public fixed_pool_base + { + public: + typedef OverflowAllocator overflow_allocator_type; + + + fixed_pool_with_overflow(void* pMemory = NULL) + : fixed_pool_base(pMemory), + mOverflowAllocator(EASTL_FIXED_POOL_DEFAULT_NAME) + { + // Leave mpPoolBegin, mpPoolEnd uninitialized. + } + + + fixed_pool_with_overflow(void* pMemory, const overflow_allocator_type& allocator) + : fixed_pool_base(pMemory), + mOverflowAllocator(allocator) + { + // Leave mpPoolBegin, mpPoolEnd uninitialized. + } + + + fixed_pool_with_overflow(void* pMemory, size_t memorySize, size_t nodeSize, + size_t alignment, size_t alignmentOffset = 0) + : mOverflowAllocator(EASTL_FIXED_POOL_DEFAULT_NAME) + { + fixed_pool_base::init(pMemory, memorySize, nodeSize, alignment, alignmentOffset); + + mpPoolBegin = pMemory; + } + + + fixed_pool_with_overflow(void* pMemory, size_t memorySize, size_t nodeSize, + size_t alignment, size_t alignmentOffset, + const overflow_allocator_type& allocator) + : mOverflowAllocator(allocator) + { + fixed_pool_base::init(pMemory, memorySize, nodeSize, alignment, alignmentOffset); + + mpPoolBegin = pMemory; + } + + + // Disabled because the default is sufficient. While it normally makes no sense to deep copy + // this data, our usage of this class is such that this is OK and wanted. + // + //fixed_pool_with_overflow(const fixed_pool_with_overflow& x) + //{ + // ... + //} + + + fixed_pool_with_overflow& operator=(const fixed_pool_with_overflow& x) + { + #if EASTL_ALLOCATOR_COPY_ENABLED + mOverflowAllocator = x.mOverflowAllocator; + #else + (void)x; + #endif + + return *this; + } + + + 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; + } + + + void* allocate() + { + void* p = NULL; + Link* pLink = mpHead; + + if(pLink) + { + // Unlink from chain + p = pLink; + mpHead = pLink->mpNext; + } + else + { + // If there's no free node in the free list, just + // allocate another from the reserved memory area + + if(mpNext != mpCapacity) + { + p = pLink = mpNext; + mpNext = reinterpret_cast(reinterpret_cast(mpNext) + mnNodeSize); + } + else + p = mOverflowAllocator.allocate(mnNodeSize); + } + + #if EASTL_FIXED_SIZE_TRACKING_ENABLED + if(p && (++mnCurrentSize > mnPeakSize)) + mnPeakSize = mnCurrentSize; + #endif + + return p; + } + + + void* allocate(size_t alignment, size_t alignmentOffset) + { + void* p = NULL; + Link* pLink = mpHead; + + if (pLink) + { + // Unlink from chain + p = pLink; + mpHead = pLink->mpNext; + } + else + { + // If there's no free node in the free list, just + // allocate another from the reserved memory area + + if (mpNext != mpCapacity) + { + p = pLink = mpNext; + mpNext = reinterpret_cast(reinterpret_cast(mpNext)+mnNodeSize); + } + else + { + p = allocate_memory(mOverflowAllocator, mnNodeSize, alignment, alignmentOffset); + EASTL_ASSERT_MSG(p != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + } + + } + + #if EASTL_FIXED_SIZE_TRACKING_ENABLED + if (p && (++mnCurrentSize > mnPeakSize)) + mnPeakSize = mnCurrentSize; + #endif + + return p; + } + + void deallocate(void* p) + { + #if EASTL_FIXED_SIZE_TRACKING_ENABLED + --mnCurrentSize; + #endif + + if((p >= mpPoolBegin) && (p < mpCapacity)) + { + ((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); + } + + + const overflow_allocator_type& get_overflow_allocator() const + { + return mOverflowAllocator; + } + + + overflow_allocator_type& get_overflow_allocator() + { + return mOverflowAllocator; + } + + + void set_overflow_allocator(const overflow_allocator_type& overflowAllocator) + { + mOverflowAllocator = overflowAllocator; + } + public: + OverflowAllocator mOverflowAllocator; + void* mpPoolBegin; // Ideally we wouldn't need this member variable. he problem is that the information about the pool buffer and object size is stored in the owning container 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_pool_with_overflow + + + + + + /////////////////////////////////////////////////////////////////////////// + // fixed_node_allocator + /////////////////////////////////////////////////////////////////////////// + + /// fixed_node_allocator + /// + /// Note: This class was previously named fixed_node_pool, but was changed because this name + /// was inconsistent with the other allocators here which ended with _allocator. + /// + /// Implements a fixed_pool with a given node count, alignment, and alignment offset. + /// fixed_node_allocator is like fixed_pool except it is templated on the node type instead + /// of being a generic allocator. All it does is pass allocations through to + /// the fixed_pool base. This functionality is separate from fixed_pool because there + /// are other uses for fixed_pool. + /// + /// We template on kNodeSize instead of node_type because the former allows for the + /// two different node_types of the same size to use the same template implementation. + /// + /// Template parameters: + /// nodeSize The size of the object to allocate. + /// nodeCount The number of objects the pool contains. + /// nodeAlignment The alignment of the objects to allocate. + /// nodeAlignmentOffset The alignment offset of the objects to allocate. + /// 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 + class fixed_node_allocator + { + public: + typedef typename type_select, fixed_pool>::type pool_type; + typedef fixed_node_allocator this_type; + typedef OverflowAllocator overflow_allocator_type; + + enum + { + kNodeSize = nodeSize, + kNodeCount = nodeCount, + kNodesSize = nodeCount * nodeSize, // Note that the kBufferSize calculation assumes that the compiler sets sizeof(T) to be a multiple alignof(T), and so sizeof(T) is always >= alignof(T). + kBufferSize = kNodesSize + ((nodeAlignment > 1) ? nodeSize-1 : 0) + nodeAlignmentOffset, + kNodeAlignment = nodeAlignment, + kNodeAlignmentOffset = nodeAlignmentOffset + }; + + public: + pool_type mPool; + + public: + //fixed_node_allocator(const char* pName) + //{ + // mPool.set_name(pName); + //} + + + fixed_node_allocator(void* pNodeBuffer) + : mPool(pNodeBuffer, kNodesSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset) + { + } + + + fixed_node_allocator(void* pNodeBuffer, const overflow_allocator_type& allocator) + : mPool(pNodeBuffer, kNodesSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset, allocator) + { + } + + + /// fixed_node_allocator + /// + /// Note that we are copying x.mpHead to our own fixed_pool. This at first may seem + /// broken, as fixed pools cannot take over ownership of other fixed pools' memory. + /// However, we declare that this copy ctor can only ever be safely called when + /// the user has intentionally pre-seeded the source with the destination pointer. + /// This is somewhat playing with fire, but it allows us to get around chicken-and-egg + /// problems with containers being their own allocators, without incurring any memory + /// costs or extra code costs. There's another reason for this: we very strongly want + /// to avoid full copying of instances of fixed_pool around, especially via the stack. + /// Larger pools won't even be able to fit on many machine's stacks. So this solution + /// is also a mechanism to prevent that situation from existing and being used. + /// Perhaps some day we'll find a more elegant yet costless way around this. + /// + fixed_node_allocator(const this_type& x) + : mPool(x.mPool.mpNext, kNodesSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset, x.mPool.mOverflowAllocator) + { + } + + + this_type& operator=(const this_type& x) + { + mPool = x.mPool; + return *this; + } + + + void* allocate(size_t n, int /*flags*/ = 0) + { + (void)n; + EASTL_ASSERT(n == kNodeSize); + return mPool.allocate(); + } + + + void* allocate(size_t n, size_t alignment, size_t offset, int /*flags*/ = 0) + { + (void)n; + EASTL_ASSERT(n == kNodeSize); + return mPool.allocate(alignment, offset); + } + + + void deallocate(void* p, size_t) + { + mPool.deallocate(p); + } + + + /// can_allocate + /// + /// Returns true if there are any free links. + /// + bool can_allocate() const + { + return mPool.can_allocate(); + } + + + /// reset + /// + /// This function unilaterally resets the fixed pool back to a newly initialized + /// state. This is useful for using in tandem with container reset functionality. + /// + void reset(void* pNodeBuffer) + { + mPool.init(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset); + } + + + const char* get_name() const + { + return mPool.get_name(); + } + + + void set_name(const char* pName) + { + mPool.set_name(pName); + } + + + const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT + { + return mPool.mOverflowAllocator; + } + + + overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT + { + return mPool.mOverflowAllocator; + } + + + void set_overflow_allocator(const overflow_allocator_type& allocator) + { + mPool.mOverflowAllocator = allocator; + } + + + void copy_overflow_allocator(const this_type& x) // This function exists so we can write generic code that works for allocators that do and don't have overflow allocators. + { + mPool.mOverflowAllocator = x.mPool.mOverflowAllocator; + } + + }; // fixed_node_allocator + + + // This is a near copy of the code above, with the only difference being + // the 'false' bEnableOverflow template parameter, the pool_type and this_type typedefs, + // and the get_overflow_allocator / set_overflow_allocator functions. + template + class fixed_node_allocator + { + public: + typedef fixed_pool pool_type; + typedef fixed_node_allocator this_type; + typedef OverflowAllocator overflow_allocator_type; + + enum + { + kNodeSize = nodeSize, + kNodeCount = nodeCount, + kNodesSize = nodeCount * nodeSize, // Note that the kBufferSize calculation assumes that the compiler sets sizeof(T) to be a multiple alignof(T), and so sizeof(T) is always >= alignof(T). + kBufferSize = kNodesSize + ((nodeAlignment > 1) ? nodeSize-1 : 0) + nodeAlignmentOffset, + kNodeAlignment = nodeAlignment, + kNodeAlignmentOffset = nodeAlignmentOffset + }; + + public: + pool_type mPool; + + public: + fixed_node_allocator(void* pNodeBuffer) + : mPool(pNodeBuffer, kNodesSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset) + { + } + + + fixed_node_allocator(void* pNodeBuffer, const overflow_allocator_type& /*allocator*/) // allocator is unused because bEnableOverflow is false in this specialization. + : mPool(pNodeBuffer, kNodesSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset) + { + } + + + /// fixed_node_allocator + /// + /// Note that we are copying x.mpHead to our own fixed_pool. This at first may seem + /// broken, as fixed pools cannot take over ownership of other fixed pools' memory. + /// However, we declare that this copy ctor can only ever be safely called when + /// the user has intentionally pre-seeded the source with the destination pointer. + /// This is somewhat playing with fire, but it allows us to get around chicken-and-egg + /// problems with containers being their own allocators, without incurring any memory + /// costs or extra code costs. There's another reason for this: we very strongly want + /// to avoid full copying of instances of fixed_pool around, especially via the stack. + /// Larger pools won't even be able to fit on many machine's stacks. So this solution + /// is also a mechanism to prevent that situation from existing and being used. + /// Perhaps some day we'll find a more elegant yet costless way around this. + /// + fixed_node_allocator(const this_type& x) // No need to copy the overflow allocator, because bEnableOverflow is false in this specialization. + : mPool(x.mPool.mpNext, kNodesSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset) + { + } + + + this_type& operator=(const this_type& x) + { + mPool = x.mPool; + return *this; + } + + + void* allocate(size_t n, int /*flags*/ = 0) + { + (void)n; + EASTL_ASSERT(n == kNodeSize); + return mPool.allocate(); + } + + + void* allocate(size_t n, size_t alignment, size_t offset, int /*flags*/ = 0) + { + (void)n; + EASTL_ASSERT(n == kNodeSize); + return mPool.allocate(alignment, offset); + } + + + void deallocate(void* p, size_t) + { + mPool.deallocate(p); + } + + + bool can_allocate() const + { + return mPool.can_allocate(); + } + + + void reset(void* pNodeBuffer) + { + mPool.init(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset); + } + + + const char* get_name() const + { + return mPool.get_name(); + } + + + void set_name(const char* pName) + { + mPool.set_name(pName); + } + + + const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT + { + EASTL_ASSERT(false); + overflow_allocator_type* pNULL = NULL; + return *pNULL; // This is not pretty, but it should never execute. This is here only to allow this to compile. + } + + + overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT + { + EASTL_ASSERT(false); + overflow_allocator_type* pNULL = NULL; + return *pNULL; // This is not pretty, but it should never execute. This is here only to allow this to compile. + } + + + void set_overflow_allocator(const overflow_allocator_type& /*allocator*/) + { + // We don't have an overflow allocator. + EASTL_ASSERT(false); + } + + + void copy_overflow_allocator(const this_type&) // This function exists so we can write generic code that works for allocators that do and don't have overflow allocators. + { + // We don't have an overflow allocator. + } + + }; // fixed_node_allocator + + + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const fixed_node_allocator& a, + const fixed_node_allocator& b) + { + return (&a == &b); // They are only equal if they are the same object. + } + + + template + inline bool operator!=(const fixed_node_allocator& a, + const fixed_node_allocator& b) + { + return (&a != &b); // They are only equal if they are the same object. + } + + + + + + + /////////////////////////////////////////////////////////////////////////// + // fixed_hashtable_allocator + /////////////////////////////////////////////////////////////////////////// + + /// fixed_hashtable_allocator + /// + /// Provides a base class for fixed hashtable allocations. + /// To consider: Have this inherit from fixed_node_allocator. + /// + /// Template parameters: + /// bucketCount The fixed number of hashtable buckets to provide. + /// nodeCount The number of objects the pool contains. + /// nodeAlignment The alignment of the objects to allocate. + /// nodeAlignmentOffset The alignment offset of the objects to allocate. + /// 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 + class fixed_hashtable_allocator + { + public: + typedef typename type_select, fixed_pool>::type pool_type; + typedef fixed_hashtable_allocator this_type; + typedef OverflowAllocator overflow_allocator_type; + + enum + { + kBucketCount = bucketCount + 1, // '+1' because the hash table needs a null terminating bucket. + kBucketsSize = bucketCount * sizeof(void*), + kNodeSize = nodeSize, + kNodeCount = nodeCount, + kNodesSize = nodeCount * nodeSize, // Note that the kBufferSize calculation assumes that the compiler sets sizeof(T) to be a multiple alignof(T), and so sizeof(T) is always >= alignof(T). + kBufferSize = kNodesSize + ((nodeAlignment > 1) ? nodeSize-1 : 0) + nodeAlignmentOffset, // Don't need to include kBucketsSize in this calculation, as fixed_hash_xxx containers have a separate buffer for buckets. + kNodeAlignment = nodeAlignment, + kNodeAlignmentOffset = nodeAlignmentOffset, + kAllocFlagBuckets = 0x00400000 // Flag to allocator which indicates that we are allocating buckets and not nodes. + }; + + protected: + pool_type mPool; + void* mpBucketBuffer; + + public: + // Disabled because it causes compile conflicts. + //fixed_hashtable_allocator(const char* pName) + //{ + // mPool.set_name(pName); + //} + + fixed_hashtable_allocator(void* pNodeBuffer) + : mPool(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset), + mpBucketBuffer(NULL) + { + // EASTL_ASSERT(false); // As it stands now, this is not supposed to be called. + } + + + fixed_hashtable_allocator(void* pNodeBuffer, const overflow_allocator_type& allocator) + : mPool(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset, allocator), + mpBucketBuffer(NULL) + { + // EASTL_ASSERT(false); // As it stands now, this is not supposed to be called. + } + + + fixed_hashtable_allocator(void* pNodeBuffer, void* pBucketBuffer) + : mPool(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset), + mpBucketBuffer(pBucketBuffer) + { + } + + + fixed_hashtable_allocator(void* pNodeBuffer, void* pBucketBuffer, const overflow_allocator_type& allocator) + : mPool(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset, allocator), + mpBucketBuffer(pBucketBuffer) + { + } + + + /// fixed_hashtable_allocator + /// + /// Note that we are copying x.mpHead and mpBucketBuffer to our own fixed_pool. + /// See the discussion above in fixed_node_allocator for important information about this. + /// + fixed_hashtable_allocator(const this_type& x) + : mPool(x.mPool.mpHead, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset, x.mPool.mOverflowAllocator), + mpBucketBuffer(x.mpBucketBuffer) + { + } + + + fixed_hashtable_allocator& operator=(const fixed_hashtable_allocator& x) + { + mPool = x.mPool; + return *this; + } + + + void* allocate(size_t n, int flags = 0) + { + // We expect that the caller uses kAllocFlagBuckets when it wants us to allocate buckets instead of nodes. + EASTL_CT_ASSERT(kAllocFlagBuckets == 0x00400000); // Currently we expect this to be so, because the hashtable has a copy of this enum. + + if((flags & kAllocFlagBuckets) == 0) // If we are allocating nodes and (probably) not buckets... + { + EASTL_ASSERT(n == kNodeSize); EA_UNUSED(n); + return mPool.allocate(); + } + + // If bucket size no longer fits within local buffer... + if ((flags & kAllocFlagBuckets) == kAllocFlagBuckets && (n > kBucketsSize)) + return get_overflow_allocator().allocate(n); + + EASTL_ASSERT(n <= kBucketsSize); + return mpBucketBuffer; + } + + + void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0) + { + // We expect that the caller uses kAllocFlagBuckets when it wants us to allocate buckets instead of nodes. + if ((flags & kAllocFlagBuckets) == 0) // If we are allocating nodes and (probably) not buckets... + { + EASTL_ASSERT(n == kNodeSize); EA_UNUSED(n); + return mPool.allocate(alignment, offset); + } + + // If bucket size no longer fits within local buffer... + if ((flags & kAllocFlagBuckets) == kAllocFlagBuckets && (n > kBucketsSize)) + return get_overflow_allocator().allocate(n, alignment, offset); + + EASTL_ASSERT(n <= kBucketsSize); + return mpBucketBuffer; + } + + + void deallocate(void* p, size_t) + { + if(p != mpBucketBuffer) // If we are freeing a node and not buckets... + mPool.deallocate(p); + } + + + bool can_allocate() const + { + return mPool.can_allocate(); + } + + + void reset(void* pNodeBuffer) + { + // No need to modify mpBucketBuffer, as that is constant. + mPool.init(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset); + } + + + const char* get_name() const + { + return mPool.get_name(); + } + + + void set_name(const char* pName) + { + mPool.set_name(pName); + } + + + const overflow_allocator_type& get_overflow_allocator() const + { + return mPool.mOverflowAllocator; + } + + + overflow_allocator_type& get_overflow_allocator() + { + return mPool.mOverflowAllocator; + } + + + void set_overflow_allocator(const overflow_allocator_type& allocator) + { + mPool.mOverflowAllocator = allocator; + } + + + void copy_overflow_allocator(const this_type& x) // This function exists so we can write generic code that works for allocators that do and don't have overflow allocators. + { + mPool.mOverflowAllocator = x.mPool.mOverflowAllocator; + } + + }; // fixed_hashtable_allocator + + + // This is a near copy of the code above, with the only difference being + // the 'false' bEnableOverflow template parameter, the pool_type and this_type typedefs, + // and the get_overflow_allocator / set_overflow_allocator functions. + template + class fixed_hashtable_allocator + { + public: + typedef fixed_pool pool_type; + typedef fixed_hashtable_allocator this_type; + typedef OverflowAllocator overflow_allocator_type; + + enum + { + kBucketCount = bucketCount + 1, // '+1' because the hash table needs a null terminating bucket. + kBucketsSize = bucketCount * sizeof(void*), + kNodeSize = nodeSize, + kNodeCount = nodeCount, + kNodesSize = nodeCount * nodeSize, // Note that the kBufferSize calculation assumes that the compiler sets sizeof(T) to be a multiple alignof(T), and so sizeof(T) is always >= alignof(T). + kBufferSize = kNodesSize + ((nodeAlignment > 1) ? nodeSize-1 : 0) + nodeAlignmentOffset, // Don't need to include kBucketsSize in this calculation, as fixed_hash_xxx containers have a separate buffer for buckets. + kNodeAlignment = nodeAlignment, + kNodeAlignmentOffset = nodeAlignmentOffset, + kAllocFlagBuckets = 0x00400000 // Flag to allocator which indicates that we are allocating buckets and not nodes. + }; + + protected: + pool_type mPool; + void* mpBucketBuffer; + + public: + // Disabled because it causes compile conflicts. + //fixed_hashtable_allocator(const char* pName) + //{ + // mPool.set_name(pName); + //} + + fixed_hashtable_allocator(void* pNodeBuffer) + : mPool(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset), + mpBucketBuffer(NULL) + { + // EASTL_ASSERT(false); // As it stands now, this is not supposed to be called. + } + + fixed_hashtable_allocator(void* pNodeBuffer, const overflow_allocator_type& /*allocator*/) // allocator is unused because bEnableOverflow is false in this specialization. + : mPool(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset), + mpBucketBuffer(NULL) + { + // EASTL_ASSERT(false); // As it stands now, this is not supposed to be called. + } + + + fixed_hashtable_allocator(void* pNodeBuffer, void* pBucketBuffer) + : mPool(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset), + mpBucketBuffer(pBucketBuffer) + { + } + + + fixed_hashtable_allocator(void* pNodeBuffer, void* pBucketBuffer, const overflow_allocator_type& /*allocator*/) // allocator is unused because bEnableOverflow is false in this specialization. + : mPool(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset), + mpBucketBuffer(pBucketBuffer) + { + } + + + /// fixed_hashtable_allocator + /// + /// Note that we are copying x.mpHead and mpBucketBuffer to our own fixed_pool. + /// See the discussion above in fixed_node_allocator for important information about this. + /// + fixed_hashtable_allocator(const this_type& x) // No need to copy the overflow allocator, because bEnableOverflow is false in this specialization. + : mPool(x.mPool.mpHead, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset), + mpBucketBuffer(x.mpBucketBuffer) + { + } + + + fixed_hashtable_allocator& operator=(const fixed_hashtable_allocator& x) + { + mPool = x.mPool; + return *this; + } + + + void* allocate(size_t n, int flags = 0) + { + // We expect that the caller uses kAllocFlagBuckets when it wants us to allocate buckets instead of nodes. + EASTL_CT_ASSERT(kAllocFlagBuckets == 0x00400000); // Currently we expect this to be so, because the hashtable has a copy of this enum. + if((flags & kAllocFlagBuckets) == 0) // If we are allocating nodes and (probably) not buckets... + { + EASTL_ASSERT(n == kNodeSize); (void)n; // Make unused var warning go away. + return mPool.allocate(); + } + + // Don't allow hashtable buckets to overflow in this case. + EASTL_ASSERT(n <= kBucketsSize); + return mpBucketBuffer; + } + + + void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0) + { + // We expect that the caller uses kAllocFlagBuckets when it wants us to allocate buckets instead of nodes. + if((flags & kAllocFlagBuckets) == 0) // If we are allocating nodes and (probably) not buckets... + { + EASTL_ASSERT(n == kNodeSize); (void)n; // Make unused var warning go away. + return mPool.allocate(alignment, offset); + } + + // Don't allow hashtable buckets to overflow in this case. + EASTL_ASSERT(n <= kBucketsSize); + return mpBucketBuffer; + } + + + void deallocate(void* p, size_t) + { + if(p != mpBucketBuffer) // If we are freeing a node and not buckets... + mPool.deallocate(p); + } + + + bool can_allocate() const + { + return mPool.can_allocate(); + } + + + void reset(void* pNodeBuffer) + { + // No need to modify mpBucketBuffer, as that is constant. + mPool.init(pNodeBuffer, kBufferSize, kNodeSize, kNodeAlignment, kNodeAlignmentOffset); + } + + + const char* get_name() const + { + return mPool.get_name(); + } + + + void set_name(const char* pName) + { + mPool.set_name(pName); + } + + + const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT + { + EASTL_ASSERT(false); + overflow_allocator_type* pNULL = NULL; + return *pNULL; // This is not pretty, but it should never execute. This is here only to allow this to compile. + } + + + overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT + { + EASTL_ASSERT(false); + overflow_allocator_type* pNULL = NULL; + return *pNULL; // This is not pretty, but it should never execute. This is here only to allow this to compile. + } + + void set_overflow_allocator(const overflow_allocator_type& /*allocator*/) + { + // We don't have an overflow allocator. + EASTL_ASSERT(false); + } + + void copy_overflow_allocator(const this_type&) // This function exists so we can write generic code that works for allocators that do and don't have overflow allocators. + { + // We don't have an overflow allocator. + } + + }; // fixed_hashtable_allocator + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const fixed_hashtable_allocator& a, + const fixed_hashtable_allocator& b) + { + return (&a == &b); // They are only equal if they are the same object. + } + + + template + inline bool operator!=(const fixed_hashtable_allocator& a, + const fixed_hashtable_allocator& b) + { + return (&a != &b); // They are only equal if they are the same object. + } + + + + + + + /////////////////////////////////////////////////////////////////////////// + // fixed_vector_allocator + /////////////////////////////////////////////////////////////////////////// + + /// fixed_vector_allocator + /// + /// Template parameters: + /// nodeSize The size of individual objects. + /// nodeCount The number of objects the pool contains. + /// nodeAlignment The alignment of the objects to allocate. + /// nodeAlignmentOffset The alignment offset of the objects to allocate. + /// 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 + class fixed_vector_allocator + { + public: + typedef fixed_vector_allocator this_type; + typedef OverflowAllocator overflow_allocator_type; + + enum + { + kNodeSize = nodeSize, + kNodeCount = nodeCount, + kNodesSize = nodeCount * nodeSize, // Note that the kBufferSize calculation assumes that the compiler sets sizeof(T) to be a multiple alignof(T), and so sizeof(T) is always >= alignof(T). + kBufferSize = kNodesSize + ((nodeAlignment > 1) ? nodeSize-1 : 0) + nodeAlignmentOffset, + kNodeAlignment = nodeAlignment, + kNodeAlignmentOffset = nodeAlignmentOffset + }; + + public: + overflow_allocator_type mOverflowAllocator; + void* mpPoolBegin; // To consider: Find some way to make this data unnecessary, without increasing template proliferation. + + public: + // Disabled because it causes compile conflicts. + //fixed_vector_allocator(const char* pName = NULL) + //{ + // mOverflowAllocator.set_name(pName); + //} + + fixed_vector_allocator(void* pNodeBuffer = nullptr) + : mpPoolBegin(pNodeBuffer) + { + } + + fixed_vector_allocator(void* pNodeBuffer, const overflow_allocator_type& allocator) + : mOverflowAllocator(allocator), mpPoolBegin(pNodeBuffer) + { + } + + fixed_vector_allocator(const fixed_vector_allocator& x) + { + mpPoolBegin = x.mpPoolBegin; + mOverflowAllocator = x.mOverflowAllocator; + } + + fixed_vector_allocator& operator=(const fixed_vector_allocator& x) + { + // We leave our mpPoolBegin variable alone. + + #if EASTL_ALLOCATOR_COPY_ENABLED + mOverflowAllocator = x.mOverflowAllocator; + #else + (void)x; + #endif + + return *this; + } + + void* allocate(size_t n, int flags = 0) + { + return mOverflowAllocator.allocate(n, flags); + } + + void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0) + { + return mOverflowAllocator.allocate(n, alignment, offset, flags); + } + + void deallocate(void* p, size_t n) + { + if(p != mpPoolBegin) + mOverflowAllocator.deallocate(p, n); // Can't do this to our own allocation. + } + + const char* get_name() const + { + return mOverflowAllocator.get_name(); + } + + void set_name(const char* pName) + { + mOverflowAllocator.set_name(pName); + } + + const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT + { + return mOverflowAllocator; + } + + overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT + { + return mOverflowAllocator; + } + + void set_overflow_allocator(const overflow_allocator_type& allocator) + { + mOverflowAllocator = allocator; + } + + void copy_overflow_allocator(const this_type& x) // This function exists so we can write generic code that works for allocators that do and don't have overflow allocators. + { + mOverflowAllocator = x.mOverflowAllocator; + } + + }; // fixed_vector_allocator + + + template + class fixed_vector_allocator + { + public: + typedef fixed_vector_allocator this_type; + typedef OverflowAllocator overflow_allocator_type; + + enum + { + kNodeSize = nodeSize, + kNodeCount = nodeCount, + kNodesSize = nodeCount * nodeSize, // Note that the kBufferSize calculation assumes that the compiler sets sizeof(T) to be a multiple alignof(T), and so sizeof(T) is always >= alignof(T). + kBufferSize = kNodesSize + ((nodeAlignment > 1) ? nodeSize-1 : 0) + nodeAlignmentOffset, + kNodeAlignment = nodeAlignment, + kNodeAlignmentOffset = nodeAlignmentOffset + }; + + // Disabled because it causes compile conflicts. + //fixed_vector_allocator(const char* = NULL) // This char* parameter is present so that this class can be like the other version. + //{ + //} + + fixed_vector_allocator() + { + } + + fixed_vector_allocator(void* /*pNodeBuffer*/) + { + } + + fixed_vector_allocator(void* /*pNodeBuffer*/, const overflow_allocator_type& /*allocator*/) // allocator is unused because bEnableOverflow is false in this specialization. + { + } + + /// fixed_vector_allocator + /// + // Disabled because there is nothing to do. No member data. And the default for this is sufficient. + // fixed_vector_allocator(const fixed_vector_allocator&) + // { + // } + + // Disabled because there is nothing to do. No member data. + //fixed_vector_allocator& operator=(const fixed_vector_allocator& x) + //{ + // return *this; + //} + + void* allocate(size_t /*n*/, int /*flags*/ = 0) + { + EASTL_ASSERT(false); // A fixed_vector should not reallocate, else the user has exhausted its space. + return NULL; + } + + void* allocate(size_t /*n*/, size_t /*alignment*/, size_t /*offset*/, int /*flags*/ = 0) + { + EASTL_ASSERT(false); + return NULL; + } + + void deallocate(void* /*p*/, size_t /*n*/) + { + } + + const char* get_name() const + { + return EASTL_FIXED_POOL_DEFAULT_NAME; + } + + void set_name(const char* /*pName*/) + { + } + + const overflow_allocator_type& get_overflow_allocator() const EA_NOEXCEPT + { + EASTL_ASSERT(false); + overflow_allocator_type* pNULL = NULL; + return *pNULL; // This is not pretty, but it should never execute. This is here only to allow this to compile. + } + + overflow_allocator_type& get_overflow_allocator() EA_NOEXCEPT + { + EASTL_ASSERT(false); + overflow_allocator_type* pNULL = NULL; + return *pNULL; // This is not pretty, but it should never execute. This is here only to allow this to compile. + } + + void set_overflow_allocator(const overflow_allocator_type& /*allocator*/) + { + // We don't have an overflow allocator. + EASTL_ASSERT(false); + } + + void copy_overflow_allocator(const this_type&) // This function exists so we can write generic code that works for allocators that do and don't have overflow allocators. + { + // We don't have an overflow allocator. + } + + }; // fixed_vector_allocator + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const fixed_vector_allocator& a, + const fixed_vector_allocator& b) + { + return (&a == &b); // They are only equal if they are the same object. + } + + + template + inline bool operator!=(const fixed_vector_allocator& a, + const fixed_vector_allocator& b) + { + return (&a != &b); // They are only equal if they are the same object. + } + + + + + + /////////////////////////////////////////////////////////////////////////// + // fixed_swap + /////////////////////////////////////////////////////////////////////////// + + /// fixed_swap + /// + /// This function implements a swap suitable for fixed containers. + /// This is an issue because the size of fixed containers can be very + /// large, due to their having the container buffer within themselves. + /// Note that we are referring to sizeof(container) and not the total + /// sum of memory allocated by the container from the heap. + /// + /// + /// This implementation switches at compile time whether or not the + /// temporary is allocated on the stack or the heap as some compilers + /// will allocate the (large) stack frame regardless of which code + /// path is picked. + template + class fixed_swap_impl + { + public: + static void swap(Container& a, Container& b); + }; + + + template + class fixed_swap_impl + { + public: + static void swap(Container& a, Container& b) + { + Container temp(EASTL_MOVE(a)); // Can't use global swap because that could + a = EASTL_MOVE(b); // itself call this swap function in return. + b = EASTL_MOVE(temp); + } + }; + + + template + class fixed_swap_impl + { + public: + static void swap(Container& a, Container& b) + { + EASTLAllocatorType allocator(*EASTLAllocatorDefault(), EASTL_TEMP_DEFAULT_NAME); + void* const pMemory = allocator.allocate(sizeof(a)); + + if(pMemory) + { + Container* pTemp = ::new(pMemory) Container(EASTL_MOVE(a)); + a = EASTL_MOVE(b); + b = EASTL_MOVE(*pTemp); + + pTemp->~Container(); + allocator.deallocate(pMemory, sizeof(a)); + } + } + }; + + + template + void fixed_swap(Container& a, Container& b) + { + return fixed_swap_impl= EASTL_MAX_STACK_USAGE>::swap(a, b); + } + + + +} // namespace eastl + + +EA_RESTORE_VC_WARNING(); + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/internal/function.h b/lib/EASTL/include/EASTL/internal/function.h new file mode 100644 index 000000000..785969d2c --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/function.h @@ -0,0 +1,163 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_FUNCTION_H +#define EASTL_FUNCTION_H + +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include + +namespace eastl +{ + + /// EASTL_FUNCTION_DEFAULT_CAPTURE_SSO_SIZE + /// + /// Defines the size of the SSO buffer which is used to hold the specified capture state of the callable. + /// + #ifndef EASTL_FUNCTION_DEFAULT_CAPTURE_SSO_SIZE + #define EASTL_FUNCTION_DEFAULT_CAPTURE_SSO_SIZE (2 * sizeof(void*)) + #endif + + static_assert(EASTL_FUNCTION_DEFAULT_CAPTURE_SSO_SIZE >= sizeof(void*), "functor storage must be able to hold at least a pointer!"); + + template + class function; + + template + class function : public internal::function_detail + { + private: + using Base = internal::function_detail; + public: + using typename Base::result_type; + + function() EA_NOEXCEPT = default; + function(std::nullptr_t p) EA_NOEXCEPT + : Base(p) + { + } + + function(const function& other) + : Base(other) + { + } + + function(function&& other) + : Base(eastl::move(other)) + { + } + + template + function(Functor functor) + : Base(eastl::move(functor)) + { + } + + ~function() EA_NOEXCEPT = default; + + function& operator=(const function& other) + { + Base::operator=(other); + return *this; + } + + function& operator=(function&& other) + { + Base::operator=(eastl::move(other)); + return *this; + } + + function& operator=(std::nullptr_t p) EA_NOEXCEPT + { + Base::operator=(p); + return *this; + } + + template + function& operator=(Functor&& functor) + { + Base::operator=(eastl::forward(functor)); + return *this; + } + + template + function& operator=(eastl::reference_wrapper f) EA_NOEXCEPT + { + Base::operator=(f); + return *this; + } + + void swap(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)...); + } + + #if EASTL_RTTI_ENABLED + const std::type_info& target_type() const EA_NOEXCEPT + { + return Base::target_type(); + } + + template + Functor* target() EA_NOEXCEPT + { + return Base::target(); + } + + template + const Functor* target() const EA_NOEXCEPT + { + return Base::target(); + } + #endif // EASTL_RTTI_ENABLED + }; + + template + bool operator==(const function& f, std::nullptr_t) EA_NOEXCEPT + { + return !f; + } + + template + bool operator==(std::nullptr_t, const function& f) EA_NOEXCEPT + { + return !f; + } + + template + bool operator!=(const function& f, std::nullptr_t) EA_NOEXCEPT + { + return !!f; + } + + template + bool operator!=(std::nullptr_t, const function& f) EA_NOEXCEPT + { + return !!f; + } + + template + void swap(function& lhs, function& rhs) + { + lhs.swap(rhs); + } + +} // namespace eastl + +#endif // EASTL_FUNCTION_H diff --git a/lib/EASTL/include/EASTL/internal/function_detail.h b/lib/EASTL/include/EASTL/internal/function_detail.h new file mode 100644 index 000000000..3ee36677d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/function_detail.h @@ -0,0 +1,673 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_FUNCTION_DETAIL_H +#define EASTL_FUNCTION_DETAIL_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#if EASTL_RTTI_ENABLED + #include +#endif + +#if EASTL_EXCEPTIONS_ENABLED + EA_DISABLE_ALL_VC_WARNINGS() + #include + #include + EA_RESTORE_ALL_VC_WARNINGS() +#endif + +namespace eastl +{ + #if EASTL_EXCEPTIONS_ENABLED + class bad_function_call : public std::exception + { + public: + bad_function_call() EA_NOEXCEPT = default; + + const char* what() const EA_NOEXCEPT EA_OVERRIDE + { + return "bad function_detail call"; + } + }; + #endif + + namespace internal + { + class unused_class {}; + + union functor_storage_alignment + { + void (*unused_func_ptr)(void); + void (unused_class::*unused_func_mem_ptr)(void); + void* unused_ptr; + }; + + template + struct functor_storage + { + static_assert(SIZE_IN_BYTES >= 0, "local buffer storage cannot have a negative size!"); + template + Ret& GetStorageTypeRef() const + { + return *reinterpret_cast(const_cast(&storage[0])); + } + + union + { + functor_storage_alignment align; + char storage[SIZE_IN_BYTES]; + }; + }; + + template <> + struct functor_storage<0> + { + template + Ret& GetStorageTypeRef() const + { + return *reinterpret_cast(const_cast(&storage[0])); + } + + union + { + functor_storage_alignment align; + char storage[sizeof(functor_storage_alignment)]; + }; + }; + + template + struct is_functor_inplace_allocatable + { + static EA_CONSTEXPR bool value = + sizeof(Functor) <= sizeof(functor_storage) && + (eastl::alignment_of_v> % eastl::alignment_of_v) == 0; + }; + + + /// function_base_detail + /// + template + class function_base_detail + { + public: + using FunctorStorageType = functor_storage; + FunctorStorageType mStorage; + + enum ManagerOperations : int + { + MGROPS_DESTRUCT_FUNCTOR = 0, + MGROPS_COPY_FUNCTOR = 1, + MGROPS_MOVE_FUNCTOR = 2, + #if EASTL_RTTI_ENABLED + MGROPS_GET_TYPE_INFO = 3, + MGROPS_GET_FUNC_PTR = 4, + #endif + }; + + // Functor can be allocated inplace + template + class function_manager_base + { + public: + + static Functor* GetFunctorPtr(const FunctorStorageType& storage) EA_NOEXCEPT + { + return &(storage.template GetStorageTypeRef()); + } + + template + static void CreateFunctor(FunctorStorageType& storage, T&& functor) + { + ::new (GetFunctorPtr(storage)) Functor(eastl::forward(functor)); + } + + static void DestructFunctor(FunctorStorageType& storage) + { + GetFunctorPtr(storage)->~Functor(); + } + + static void CopyFunctor(FunctorStorageType& to, const FunctorStorageType& from) + { + ::new (GetFunctorPtr(to)) Functor(*GetFunctorPtr(from)); + } + + static void MoveFunctor(FunctorStorageType& to, FunctorStorageType& from) EA_NOEXCEPT + { + ::new (GetFunctorPtr(to)) Functor(eastl::move(*GetFunctorPtr(from))); + } + + static void* Manager(void* to, void* from, typename function_base_detail::ManagerOperations ops) EA_NOEXCEPT + { + switch (ops) + { + case MGROPS_DESTRUCT_FUNCTOR: + { + DestructFunctor(*static_cast(to)); + } + break; + case MGROPS_COPY_FUNCTOR: + { + CopyFunctor(*static_cast(to), + *static_cast(from)); + } + break; + case MGROPS_MOVE_FUNCTOR: + { + MoveFunctor(*static_cast(to), *static_cast(from)); + DestructFunctor(*static_cast(from)); + } + break; + default: + break; + } + return nullptr; + } + }; + + // Functor is allocated on the heap + template + class function_manager_base::value>::type> + { + public: + static Functor* GetFunctorPtr(const FunctorStorageType& storage) EA_NOEXCEPT + { + return storage.template GetStorageTypeRef(); + } + + static Functor*& GetFunctorPtrRef(const FunctorStorageType& storage) EA_NOEXCEPT + { + return storage.template GetStorageTypeRef(); + } + + template + static void CreateFunctor(FunctorStorageType& storage, T&& functor) + { + auto& allocator = *EASTLAllocatorDefault(); + Functor* func = static_cast(allocator.allocate(sizeof(Functor), alignof(Functor), 0)); + + #if EASTL_EXCEPTIONS_ENABLED + if (!func) + { + throw std::bad_alloc(); + } + #else + EASTL_ASSERT_MSG(func != nullptr, "Allocation failed!"); + #endif + + ::new (static_cast(func)) Functor(eastl::forward(functor)); + GetFunctorPtrRef(storage) = func; + } + + static void DestructFunctor(FunctorStorageType& storage) + { + Functor* func = GetFunctorPtr(storage); + if (func) + { + auto& allocator = *EASTLAllocatorDefault(); + func->~Functor(); + allocator.deallocate(static_cast(func), sizeof(Functor)); + } + } + + static void CopyFunctor(FunctorStorageType& to, const FunctorStorageType& from) + { + auto& allocator = *EASTLAllocatorDefault(); + Functor* func = static_cast(allocator.allocate(sizeof(Functor), alignof(Functor), 0)); + #if EASTL_EXCEPTIONS_ENABLED + if (!func) + { + throw std::bad_alloc(); + } + #else + EASTL_ASSERT_MSG(func != nullptr, "Allocation failed!"); + #endif + ::new (static_cast(func)) Functor(*GetFunctorPtr(from)); + GetFunctorPtrRef(to) = func; + } + + static void MoveFunctor(FunctorStorageType& to, FunctorStorageType& from) EA_NOEXCEPT + { + Functor* func = GetFunctorPtr(from); + GetFunctorPtrRef(to) = func; + GetFunctorPtrRef(from) = nullptr; + } + + static void* Manager(void* to, void* from, typename function_base_detail::ManagerOperations ops) EA_NOEXCEPT + { + switch (ops) + { + case MGROPS_DESTRUCT_FUNCTOR: + { + DestructFunctor(*static_cast(to)); + } + break; + case MGROPS_COPY_FUNCTOR: + { + CopyFunctor(*static_cast(to), + *static_cast(from)); + } + break; + case MGROPS_MOVE_FUNCTOR: + { + MoveFunctor(*static_cast(to), *static_cast(from)); + // Moved ptr, no need to destruct ourselves + } + break; + default: + break; + } + return nullptr; + } + }; + + template + class function_manager final : public function_manager_base + { + public: + using Base = function_manager_base; + + #if EASTL_RTTI_ENABLED + static void* GetTypeInfo() EA_NOEXCEPT + { + return reinterpret_cast(const_cast(&typeid(Functor))); + } + + static void* Manager(void* to, void* from, typename function_base_detail::ManagerOperations ops) EA_NOEXCEPT + { + switch (ops) + { + case MGROPS_GET_TYPE_INFO: + { + return GetTypeInfo(); + } + break; + case MGROPS_GET_FUNC_PTR: + { + return static_cast(Base::GetFunctorPtr(*static_cast(to))); + } + break; + default: + { + return Base::Manager(to, from, ops); + } + break; + } + } + #endif // EASTL_RTTI_ENABLED + + /** + * NOTE: + * + * The order of arguments here is vital to the call optimization. Let's dig into why and look at some asm. + * We have two invoker signatures to consider: + * R Invoker(const FunctorStorageType& functor, Args... args) + * R Invoker(Args... args, const FunctorStorageType& functor) + * + * Assume we are using the Windows x64 Calling Convention where the first 4 arguments are passed into + * RCX, RDX, R8, R9. This optimization works for any Calling Convention, we are just using Windows x64 for + * this example. + * + * Given the following member function: void TestMemberFunc(int a, int b) + * RCX == this + * RDX == a + * R8 == b + * + * All three arguments to the function including the hidden this pointer, which in C++ is always the first argument + * are passed into the first three registers. + * The function call chain for eastl::function<>() is as follows: + * operator ()(this, Args... args) -> Invoker(Args... args, this->mStorage) -> StoredFunction(Args... arg) + * + * Let's look at what is happening at the asm level with the different Invoker function signatures and why. + * + * You will notice that operator ()() and Invoker() have the arguments reversed. operator ()() just directly calls + * to Invoker(), it is a tail call, so we force inline the call operator to ensure we directly call to the Invoker(). + * Most compilers always inline it anyways by default; have been instances where it doesn't even though the asm ends + * up being cheaper. + * call -> call -> call versus call -> call + * + * eastl::function = FunctionPointer + * + * Assume we have the above eastl::function object that holds a pointer to a function as the internal callable. + * + * Invoker(this->mStorage, Args... args) is called with the follow arguments in registers: + * RCX = this | RDX = a | R8 = b + * + * Inside Invoker() we use RCX to deference into the eastl::function object and get the function pointer to call. + * This function to call has signature Func(int, int) and thus requires its arguments in registers RCX and RDX. + * The compiler must shift all the arguments towards the left. The full asm looks something as follows. + * + * Calling Invoker: Inside Invoker: + * + * mov rcx, this mov rax, [rcx] + * mov rdx, a mov rcx, rdx + * mov r8, b mov rdx, r8 + * call [rcx + offset to Invoker] jmp [rax] + * + * Notice how the compiler shifts all the arguments before calling the callable and also we only use the this pointer + * to access the internal storage inside the eastl::function object. + * + * Invoker(Args... args, this->mStorage) is called with the following arguments in registers: + * RCX = a | RDX = b | R8 = this + * + * You can see we no longer have to shift the arguments down when going to call the internal stored callable. + * + * Calling Invoker: Inside Invoker: + * + * mov rcx, a mov rax, [r8] + * mov rdx, b jmp [rax] + * mov r8, this + * call [r8 + offset to Invoker] + * + * The generated asm does a straight tail jmp to the loaded function pointer. The arguments are already in the correct + * registers. + * + * For Functors or Lambdas with no captures, this gives us another free register to use to pass arguments since the this + * is at the end, it can be passed onto the stack if we run out of registers. Since the callable has no captures; inside + * the Invoker(), we won't ever need to touch this thus we can just call the operator ()() or let the compiler inline it. + * + * For a callable with captures there is no perf hit since the callable in the common case is inlined and the pointer to the callable + * buffer is passed in a register which the compiler can use to access the captures. + * + * For eastl::function that a holds a pointer to member function. The this pointers is implicitly + * the first argument in the argument list, const T&, and the member function pointer will be called on that object. + * This prevents any argument shifting since the this for the member function pointer is already in RCX. + * + * This is why having this at the end of the argument list is important for generating efficient Invoker() thunks. + */ + static R Invoker(Args... args, const FunctorStorageType& functor) + { + return eastl::invoke(*Base::GetFunctorPtr(functor), eastl::forward(args)...); + } + }; + + function_base_detail() EA_NOEXCEPT = default; + ~function_base_detail() EA_NOEXCEPT = default; + }; + + #define EASTL_INTERNAL_FUNCTION_VALID_FUNCTION_ARGS(FUNCTOR, RET, ARGS, BASE, MYSELF) \ + typename eastl::enable_if_t && \ + !eastl::is_base_of_v> && \ + !eastl::is_same_v, MYSELF>> + + #define EASTL_INTERNAL_FUNCTION_DETAIL_VALID_FUNCTION_ARGS(FUNCTOR, RET, ARGS, MYSELF) \ + EASTL_INTERNAL_FUNCTION_VALID_FUNCTION_ARGS(FUNCTOR, RET, ARGS, MYSELF, MYSELF) + + + /// function_detail + /// + template + class function_detail; + + template + class function_detail : public function_base_detail + { + public: + using result_type = R; + + protected: + using Base = function_base_detail; + using FunctorStorageType = typename function_base_detail::FunctorStorageType; + using Base::mStorage; + + public: + function_detail() EA_NOEXCEPT = default; + function_detail(std::nullptr_t) EA_NOEXCEPT {} + + function_detail(const function_detail& other) + { + if (this != &other) + { + Copy(other); + } + } + + function_detail(function_detail&& other) + { + if (this != &other) + { + Move(eastl::move(other)); + } + } + + template + function_detail(Functor functor) + { + CreateForwardFunctor(eastl::move(functor)); + } + + ~function_detail() EA_NOEXCEPT + { + Destroy(); + } + + function_detail& operator=(const function_detail& other) + { + if (this != &other) + { + Destroy(); + Copy(other); + } + + return *this; + } + + function_detail& operator=(function_detail&& other) + { + if(this != &other) + { + Destroy(); + Move(eastl::move(other)); + } + + return *this; + } + + function_detail& operator=(std::nullptr_t) EA_NOEXCEPT + { + Destroy(); + mMgrFuncPtr = nullptr; + mInvokeFuncPtr = &DefaultInvoker; + + return *this; + } + + template + function_detail& operator=(Functor&& functor) + { + Destroy(); + CreateForwardFunctor(eastl::forward(functor)); + return *this; + } + + template + function_detail& operator=(eastl::reference_wrapper f) EA_NOEXCEPT + { + Destroy(); + CreateForwardFunctor(f); + return *this; + } + + void swap(function_detail& other) EA_NOEXCEPT + { + if(this == &other) + return; + + FunctorStorageType tempStorage; + if (other.HaveManager()) + { + (void)(*other.mMgrFuncPtr)(static_cast(&tempStorage), static_cast(&other.mStorage), + Base::ManagerOperations::MGROPS_MOVE_FUNCTOR); + } + + if (HaveManager()) + { + (void)(*mMgrFuncPtr)(static_cast(&other.mStorage), static_cast(&mStorage), + Base::ManagerOperations::MGROPS_MOVE_FUNCTOR); + } + + if (other.HaveManager()) + { + (void)(*other.mMgrFuncPtr)(static_cast(&mStorage), static_cast(&tempStorage), + Base::ManagerOperations::MGROPS_MOVE_FUNCTOR); + } + + eastl::swap(mMgrFuncPtr, other.mMgrFuncPtr); + eastl::swap(mInvokeFuncPtr, other.mInvokeFuncPtr); + } + + explicit operator bool() const EA_NOEXCEPT + { + return HaveManager(); + } + + EASTL_FORCE_INLINE R operator ()(Args... args) const + { + return (*mInvokeFuncPtr)(eastl::forward(args)..., this->mStorage); + } + + #if EASTL_RTTI_ENABLED + const std::type_info& target_type() const EA_NOEXCEPT + { + if (HaveManager()) + { + void* ret = (*mMgrFuncPtr)(nullptr, nullptr, Base::ManagerOperations::MGROPS_GET_TYPE_INFO); + return *(static_cast(ret)); + } + return typeid(void); + } + + template + Functor* target() EA_NOEXCEPT + { + if (HaveManager() && target_type() == typeid(Functor)) + { + void* ret = (*mMgrFuncPtr)(static_cast(&mStorage), nullptr, + Base::ManagerOperations::MGROPS_GET_FUNC_PTR); + return ret ? static_cast(ret) : nullptr; + } + return nullptr; + } + + template + const Functor* target() const EA_NOEXCEPT + { + if (HaveManager() && target_type() == typeid(Functor)) + { + void* ret = (*mMgrFuncPtr)(static_cast(&mStorage), nullptr, + Base::ManagerOperations::MGROPS_GET_FUNC_PTR); + return ret ? static_cast(ret) : nullptr; + } + return nullptr; + } + #endif // EASTL_RTTI_ENABLED + + private: + bool HaveManager() const EA_NOEXCEPT + { + return (mMgrFuncPtr != nullptr); + } + + void Destroy() EA_NOEXCEPT + { + if (HaveManager()) + { + (void)(*mMgrFuncPtr)(static_cast(&mStorage), nullptr, + Base::ManagerOperations::MGROPS_DESTRUCT_FUNCTOR); + } + } + + void Copy(const function_detail& other) + { + if (other.HaveManager()) + { + (void)(*other.mMgrFuncPtr)(static_cast(&mStorage), + const_cast(static_cast(&other.mStorage)), + Base::ManagerOperations::MGROPS_COPY_FUNCTOR); + } + + mMgrFuncPtr = other.mMgrFuncPtr; + mInvokeFuncPtr = other.mInvokeFuncPtr; + } + + void Move(function_detail&& other) + { + if (other.HaveManager()) + { + (void)(*other.mMgrFuncPtr)(static_cast(&mStorage), static_cast(&other.mStorage), + Base::ManagerOperations::MGROPS_MOVE_FUNCTOR); + } + + mMgrFuncPtr = other.mMgrFuncPtr; + mInvokeFuncPtr = other.mInvokeFuncPtr; + other.mMgrFuncPtr = nullptr; + other.mInvokeFuncPtr = &DefaultInvoker; + } + + template + void CreateForwardFunctor(Functor&& functor) + { + using DecayedFunctorType = typename eastl::decay::type; + using FunctionManagerType = typename Base::template function_manager; + + if (internal::is_null(functor)) + { + mMgrFuncPtr = nullptr; + mInvokeFuncPtr = &DefaultInvoker; + } + else + { + mMgrFuncPtr = &FunctionManagerType::Manager; + mInvokeFuncPtr = &FunctionManagerType::Invoker; + FunctionManagerType::CreateFunctor(mStorage, eastl::forward(functor)); + } + } + + private: + typedef void* (*ManagerFuncPtr)(void*, void*, typename Base::ManagerOperations); + typedef R (*InvokeFuncPtr)(Args..., const FunctorStorageType&); + + EA_DISABLE_GCC_WARNING(-Wreturn-type); + EA_DISABLE_CLANG_WARNING(-Wreturn-type); + EA_DISABLE_VC_WARNING(4716); // 'function' must return a value + // We cannot assume that R is default constructible. + // This function is called only when the function object CANNOT be called because it is empty, + // it will always throw or assert so we never use the return value anyways and neither should the caller. + static R DefaultInvoker(Args... /*args*/, const FunctorStorageType& /*functor*/) + { + #if EASTL_EXCEPTIONS_ENABLED + throw eastl::bad_function_call(); + #else + EASTL_ASSERT_MSG(false, "function_detail call on an empty function_detail"); + #endif + }; + EA_RESTORE_VC_WARNING(); + EA_RESTORE_CLANG_WARNING(); + EA_RESTORE_GCC_WARNING(); + + + ManagerFuncPtr mMgrFuncPtr = nullptr; + InvokeFuncPtr mInvokeFuncPtr = &DefaultInvoker; + }; + + } // namespace internal + +} // namespace eastl + +#endif // EASTL_FUNCTION_DETAIL_H diff --git a/lib/EASTL/include/EASTL/internal/function_help.h b/lib/EASTL/include/EASTL/internal/function_help.h new file mode 100644 index 000000000..04481d376 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/function_help.h @@ -0,0 +1,51 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_INTERNAL_FUNCTION_HELP_H +#define EASTL_INTERNAL_FUNCTION_HELP_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include + +namespace eastl +{ + namespace internal + { + + ////////////////////////////////////////////////////////////////////// + // is_null + // + template + bool is_null(const T&) + { + return false; + } + + template + bool is_null(Result (*const& function_pointer)(Arguments...)) + { + return function_pointer == nullptr; + } + + template + bool is_null(Result (Class::*const& function_pointer)(Arguments...)) + { + return function_pointer == nullptr; + } + + template + bool is_null(Result (Class::*const& function_pointer)(Arguments...) const) + { + return function_pointer == nullptr; + } + + } // namespace internal +} // namespace eastl + +#endif // Header include guard + diff --git a/lib/EASTL/include/EASTL/internal/functional_base.h b/lib/EASTL/include/EASTL/internal/functional_base.h new file mode 100644 index 000000000..ef27800bb --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/functional_base.h @@ -0,0 +1,419 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_FUNCTIONAL_BASE_H +#define EASTL_INTERNAL_FUNCTIONAL_BASE_H + +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include +#include + + +namespace eastl +{ + // foward declaration for swap + template + inline void swap(T& a, T& b) + EA_NOEXCEPT_IF(eastl::is_nothrow_move_constructible::value && eastl::is_nothrow_move_assignable::value); + + + /// invoke + /// + /// invoke is a generalized function-call operator which works on function pointers, member function + /// pointers, callable objects and member pointers. + /// + /// For (member/non-member) function pointers and callable objects, it returns the result of calling + /// the function/object with the specified arguments. For member data pointers, it simply returns + /// the member. + /// + /// Note that there are also reference_wrapper specializations of invoke, which need to be defined + /// later since reference_wrapper uses invoke in its implementation. Those are defined immediately + /// after the definition of reference_wrapper. + /// + /// http://en.cppreference.com/w/cpp/utility/functional/invoke + /// + template + EA_CONSTEXPR auto invoke_impl(R C::*func, T&& obj, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR((eastl::forward(obj).*func)(eastl::forward(args)...))) + -> typename enable_if>::value, + decltype((eastl::forward(obj).*func)(eastl::forward(args)...))>::type + { + return (eastl::forward(obj).*func)(eastl::forward(args)...); + } + + template + EA_CONSTEXPR auto invoke_impl(F&& func, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(eastl::forward(func)(eastl::forward(args)...))) + -> decltype(eastl::forward(func)(eastl::forward(args)...)) + { + return eastl::forward(func)(eastl::forward(args)...); + } + + + template + EA_CONSTEXPR auto invoke_impl(R C::*func, T&& obj, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(((*eastl::forward(obj)).*func)(eastl::forward(args)...))) + -> decltype(((*eastl::forward(obj)).*func)(eastl::forward(args)...)) + { + return ((*eastl::forward(obj)).*func)(eastl::forward(args)...); + } + + template + EA_CONSTEXPR auto invoke_impl(M C::*member, T&& obj) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(eastl::forward(obj).*member)) + -> typename enable_if< + is_base_of>::value, + decltype(eastl::forward(obj).*member) + >::type + { + return eastl::forward(obj).*member; + } + + template + EA_CONSTEXPR auto invoke_impl(M C::*member, T&& obj) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR((*eastl::forward(obj)).*member)) + -> decltype((*eastl::forward(obj)).*member) + { + return (*eastl::forward(obj)).*member; + } + + template + EA_CONSTEXPR decltype(auto) invoke(F&& func, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(invoke_impl(eastl::forward(func), eastl::forward(args)...))) + { + return invoke_impl(eastl::forward(func), eastl::forward(args)...); + } + + template + struct invoke_result_impl { + }; + + template + struct invoke_result_impl(), eastl::declval()...))>, Args...> + { + typedef decltype(invoke_impl(eastl::declval(), eastl::declval()...)) type; + }; + + template + struct invoke_result : public invoke_result_impl {}; + + #if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + using invoke_result_t = typename invoke_result::type; + #endif + + template + struct is_invocable_impl : public eastl::false_type {}; + + template + struct is_invocable_impl::type>, Args...> : public eastl::true_type {}; + + template + struct is_invocable : public is_invocable_impl {}; + + template + struct is_invocable_r_impl : public eastl::false_type {}; + + template + struct is_invocable_r_impl::type>, Args...> + : public is_convertible::type, R> {}; + + template + struct is_invocable_r : public is_invocable_r_impl {}; + + template + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_invocable_v = is_invocable::value; + + template + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_invocable_r_v = is_invocable_r::value; + + template + struct is_nothrow_invocable_impl : public eastl::false_type {}; + + template + struct is_nothrow_invocable_impl::type>, Args...> + : public eastl::bool_constant(), eastl::declval()...))> {}; + + template + struct is_nothrow_invocable : public is_nothrow_invocable_impl {}; + + template + struct is_nothrow_invocable_r_impl : public eastl::false_type {}; + + template + struct is_nothrow_invocable_r_impl::type>, Args...> + { + static EA_CONSTEXPR_OR_CONST bool value = eastl::is_convertible::type, R>::value + && eastl::is_nothrow_invocable::value; + }; + + template + struct is_nothrow_invocable_r : public is_nothrow_invocable_r_impl {}; + + template + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_no_throw_invocable_v = is_nothrow_invocable::value; + + template + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_nothrow_invocable_r_v = is_nothrow_invocable_r::value; + + /// allocator_arg_t + /// + /// allocator_arg_t is an empty class type used to disambiguate the overloads of + /// constructors and member functions of allocator-aware objects, including tuple, + /// function, promise, and packaged_task. + /// http://en.cppreference.com/w/cpp/memory/allocator_arg_t + /// + struct allocator_arg_t + {}; + + + /// allocator_arg + /// + /// allocator_arg is a constant of type allocator_arg_t used to disambiguate, at call site, + /// the overloads of the constructors and member functions of allocator-aware objects, + /// such as tuple, function, promise, and packaged_task. + /// http://en.cppreference.com/w/cpp/memory/allocator_arg + /// + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR allocator_arg_t allocator_arg = allocator_arg_t(); + + + template + struct unary_function + { + typedef Argument argument_type; + typedef Result result_type; + }; + + + template + struct binary_function + { + typedef Argument1 first_argument_type; + typedef Argument2 second_argument_type; + typedef Result result_type; + }; + + + /// less + template + struct less : public binary_function + { + EA_CPP14_CONSTEXPR bool operator()(const T& a, const T& b) const + { return a < b; } + }; + + // http://en.cppreference.com/w/cpp/utility/functional/less_void + template <> + struct less + { + template + EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const + -> decltype(eastl::forward(a) < eastl::forward(b)) + { return eastl::forward(a) < eastl::forward(b); } + }; + + + /// reference_wrapper + template + class reference_wrapper + { + public: + typedef T type; + + reference_wrapper(T&) EA_NOEXCEPT; + reference_wrapper(T&&) = delete; + reference_wrapper(const reference_wrapper& x) EA_NOEXCEPT; + + reference_wrapper& operator=(const reference_wrapper& x) EA_NOEXCEPT; + + operator T& () const EA_NOEXCEPT; + T& get() const EA_NOEXCEPT; + + template + typename eastl::result_of::type operator() (ArgTypes&&...) const; + + private: + T* val; + }; + + template + reference_wrapper::reference_wrapper(T &v) EA_NOEXCEPT + : val(eastl::addressof(v)) + {} + + template + reference_wrapper::reference_wrapper(const reference_wrapper& other) EA_NOEXCEPT + : val(other.val) + {} + + template + reference_wrapper& reference_wrapper::operator=(const reference_wrapper& other) EA_NOEXCEPT + { + val = other.val; + return *this; + } + + template + reference_wrapper::operator T&() const EA_NOEXCEPT + { + return *val; + } + + template + T& reference_wrapper::get() const EA_NOEXCEPT + { + return *val; + } + + template + template + typename eastl::result_of::type reference_wrapper::operator() (ArgTypes&&... args) const + { + return eastl::invoke(*val, eastl::forward(args)...); + } + + // reference_wrapper-specific utilties + template + reference_wrapper ref(T& t) EA_NOEXCEPT + { + return eastl::reference_wrapper(t); + } + + template + void ref(const T&&) = delete; + + template + reference_wrapper ref(reference_wrappert) EA_NOEXCEPT + { + return eastl::ref(t.get()); + } + + template + reference_wrapper cref(const T& t) EA_NOEXCEPT + { + return eastl::reference_wrapper(t); + } + + template + void cref(const T&&) = delete; + + template + reference_wrapper cref(reference_wrapper t) EA_NOEXCEPT + { + return eastl::cref(t.get()); + } + + + // reference_wrapper-specific type traits + template + struct is_reference_wrapper_helper + : public eastl::false_type {}; + + template + struct is_reference_wrapper_helper > + : public eastl::true_type {}; + + template + struct is_reference_wrapper + : public eastl::is_reference_wrapper_helper::type> {}; + + + // Helper which adds a reference to a type when given a reference_wrapper of that type. + template + struct remove_reference_wrapper + { typedef T type; }; + + template + struct remove_reference_wrapper< eastl::reference_wrapper > + { typedef T& type; }; + + template + struct remove_reference_wrapper< const eastl::reference_wrapper > + { typedef T& type; }; + + // reference_wrapper specializations of invoke + // These have to come after reference_wrapper is defined, but reference_wrapper needs to have a + // definition of invoke, so these specializations need to come after everything else has been defined. + template + EA_CONSTEXPR auto invoke_impl(R C::*func, T&& obj, Args&&... args) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR((obj.get().*func)(eastl::forward(args)...))) + -> typename enable_if>::value, + decltype((obj.get().*func)(eastl::forward(args)...))>::type + { + return (obj.get().*func)(eastl::forward(args)...); + } + + template + EA_CONSTEXPR auto invoke_impl(M C::*member, T&& obj) EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(obj.get().*member)) + -> typename enable_if>::value, + decltype(obj.get().*member)>::type + { + return obj.get().*member; + } + + + /////////////////////////////////////////////////////////////////////// + // bind + /////////////////////////////////////////////////////////////////////// + + /// bind1st + /// + template + class binder1st : public unary_function + { + protected: + typename Operation::first_argument_type value; + Operation op; + + public: + binder1st(const Operation& x, const typename Operation::first_argument_type& y) + : value(y), op(x) { } + + typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const + { return op(value, x); } + + typename Operation::result_type operator()(typename Operation::second_argument_type& x) const + { return op(value, x); } + }; + + + template + inline binder1st bind1st(const Operation& op, const T& x) + { + typedef typename Operation::first_argument_type value; + return binder1st(op, value(x)); + } + + + /// bind2nd + /// + template + class binder2nd : public unary_function + { + protected: + Operation op; + typename Operation::second_argument_type value; + + public: + binder2nd(const Operation& x, const typename Operation::second_argument_type& y) + : op(x), value(y) { } + + typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const + { return op(x, value); } + + typename Operation::result_type operator()(typename Operation::first_argument_type& x) const + { return op(x, value); } + }; + + + template + inline binder2nd bind2nd(const Operation& op, const T& x) + { + typedef typename Operation::second_argument_type value; + return binder2nd(op, value(x)); + } + +} // namespace eastl + +#endif // EASTL_INTERNAL_FUNCTIONAL_BASE_H diff --git a/lib/EASTL/include/EASTL/internal/generic_iterator.h b/lib/EASTL/include/EASTL/internal/generic_iterator.h new file mode 100644 index 000000000..b32998a8c --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/generic_iterator.h @@ -0,0 +1,208 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Implements a generic iterator from a given iteratable type, such as a pointer. +// We cannot put this file into our own iterator.h file because we need to +// still be able to use this file when we have our iterator.h disabled. +// +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_GENERIC_ITERATOR_H +#define EASTL_INTERNAL_GENERIC_ITERATOR_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include +#include + +// There is no warning number 'number'. +// Member template functions cannot be used for copy-assignment or copy-construction. +EA_DISABLE_VC_WARNING(4619 4217); + + +namespace eastl +{ + + /// generic_iterator + /// + /// Converts something which can be iterated into a formal iterator. + /// While this class' primary purpose is to allow the conversion of + /// a pointer to an iterator, you can convert anything else to an + /// iterator by defining an iterator_traits<> specialization for that + /// object type. See EASTL iterator.h for this. + /// + /// Example usage: + /// typedef generic_iterator IntArrayIterator; + /// typedef generic_iterator IntArrayIteratorOther; + /// + template + class generic_iterator + { + protected: + Iterator mIterator; + + public: + typedef typename eastl::iterator_traits::iterator_category iterator_category; + typedef typename eastl::iterator_traits::value_type value_type; + typedef typename eastl::iterator_traits::difference_type difference_type; + typedef typename eastl::iterator_traits::reference reference; + typedef typename eastl::iterator_traits::pointer pointer; + typedef Iterator iterator_type; + typedef iterator_type wrapped_iterator_type; // This is not in the C++ Standard; it's used by use to identify it as a wrapping iterator type. + typedef Container container_type; + typedef generic_iterator this_type; + + generic_iterator() + : mIterator(iterator_type()) { } + + explicit generic_iterator(const iterator_type& x) + : mIterator(x) { } + + this_type& operator=(const iterator_type& x) + { mIterator = x; return *this; } + + template + generic_iterator(const generic_iterator& x) + : mIterator(x.base()) { } + + reference operator*() const + { return *mIterator; } + + pointer operator->() const + { return mIterator; } + + this_type& operator++() + { ++mIterator; return *this; } + + this_type operator++(int) + { return this_type(mIterator++); } + + this_type& operator--() + { --mIterator; return *this; } + + this_type operator--(int) + { return this_type(mIterator--); } + + reference operator[](const difference_type& n) const + { return mIterator[n]; } + + this_type& operator+=(const difference_type& n) + { mIterator += n; return *this; } + + this_type operator+(const difference_type& n) const + { return this_type(mIterator + n); } + + this_type& operator-=(const difference_type& n) + { mIterator -= n; return *this; } + + this_type operator-(const difference_type& n) const + { return this_type(mIterator - n); } + + const iterator_type& base() const + { return mIterator; } + + }; // class generic_iterator + + + template + inline bool operator==(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() == rhs.base(); } + + template + inline bool operator==(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() == rhs.base(); } + + template + inline bool operator!=(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() != rhs.base(); } + + template + inline bool operator!=(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() != rhs.base(); } + + template + inline bool operator<(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() < rhs.base(); } + + template + inline bool operator<(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() < rhs.base(); } + + template + inline bool operator>(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() > rhs.base(); } + + template + inline bool operator>(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() > rhs.base(); } + + template + inline bool operator<=(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() <= rhs.base(); } + + template + inline bool operator<=(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() <= rhs.base(); } + + template + inline bool operator>=(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() >= rhs.base(); } + + template + inline bool operator>=(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() >= rhs.base(); } + + template + inline typename generic_iterator::difference_type + operator-(const generic_iterator& lhs, const generic_iterator& rhs) + { return lhs.base() - rhs.base(); } + + template + inline generic_iterator + operator+(typename generic_iterator::difference_type n, const generic_iterator& x) + { return generic_iterator(x.base() + n); } + + + + /// is_generic_iterator + /// + /// Tells if an iterator is one of these generic_iterators. This is useful if you want to + /// write code that uses miscellaneous iterators but wants to tell if they are generic_iterators. + /// A primary reason to do so is that you can get at the pointer within the generic_iterator. + /// + template + struct is_generic_iterator : public false_type { }; + + template + struct is_generic_iterator > : public true_type { }; + + + /// unwrap_generic_iterator + /// + /// Returns Iterator::get_base() if it's a generic_iterator, else returns Iterator as-is. + /// + /// Example usage: + /// vector intVector; + /// eastl::generic_iterator::iterator> genericIterator(intVector.begin()); + /// vector::iterator it = unwrap_generic_iterator(genericIterator); + /// + template + inline typename eastl::is_iterator_wrapper_helper::value>::iterator_type unwrap_generic_iterator(Iterator it) + { return eastl::is_iterator_wrapper_helper::value>::get_base(it); } + + +} // namespace eastl + + +EA_RESTORE_VC_WARNING(); + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/internal/hashtable.h b/lib/EASTL/include/EASTL/internal/hashtable.h new file mode 100644 index 000000000..12a93a25f --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/hashtable.h @@ -0,0 +1,3222 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This file implements a hashtable, much like the C++11 unordered_set/unordered_map. +// proposed classes. +// The primary distinctions between this hashtable and C++11 unordered containers are: +// - hashtable is savvy to an environment that doesn't have exception handling, +// as is sometimes the case with console or embedded environments. +// - hashtable is slightly more space-efficient than a conventional std hashtable +// implementation on platforms with 64 bit size_t. This is +// because std STL uses size_t (64 bits) in data structures whereby 32 bits +// of data would be fine. +// - hashtable can contain objects with alignment requirements. TR1 hash tables +// cannot do so without a bit of tedious non-portable effort. +// - hashtable supports debug memory naming natively. +// - hashtable provides a find function that lets you specify a type that is +// different from the hash table key type. This is particularly useful for +// the storing of string objects but finding them by char pointers. +// - hashtable provides a lower level insert function which lets the caller +// specify the hash code and optionally the node instance. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_HASHTABLE_H +#define EASTL_INTERNAL_HASHTABLE_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() + #include + #include +EA_RESTORE_ALL_VC_WARNINGS() + +// 4512 - 'class' : assignment operator could not be generated. +// 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc +// 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. +EA_DISABLE_VC_WARNING(4512 4530 4571); + + +namespace eastl +{ + + /// EASTL_HASHTABLE_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_HASHTABLE_DEFAULT_NAME + #define EASTL_HASHTABLE_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " hashtable" // Unless the user overrides something, this is "EASTL hashtable". + #endif + + + /// EASTL_HASHTABLE_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_HASHTABLE_DEFAULT_ALLOCATOR + #define EASTL_HASHTABLE_DEFAULT_ALLOCATOR allocator_type(EASTL_HASHTABLE_DEFAULT_NAME) + #endif + + + /// kHashtableAllocFlagBuckets + /// Flag to allocator which indicates that we are allocating buckets and not nodes. + enum { kHashtableAllocFlagBuckets = 0x00400000 }; + + + /// gpEmptyBucketArray + /// + /// A shared representation of an empty hash table. This is present so that + /// a new empty hashtable allocates no memory. It has two entries, one for + /// the first lone empty (NULL) bucket, and one for the non-NULL trailing sentinel. + /// + extern EASTL_API void* gpEmptyBucketArray[2]; + + + /// EASTL_MACRO_SWAP + /// + /// Use EASTL_MACRO_SWAP because GCC (at least v4.6-4.8) has a bug where it fails to compile eastl::swap(mpBucketArray, x.mpBucketArray). + /// + #define EASTL_MACRO_SWAP(Type, a, b) \ + { Type temp = a; a = b; b = temp; } + + + /// hash_node + /// + /// A hash_node stores an element in a hash table, much like a + /// linked list node stores an element in a linked list. + /// A hash_node additionally can, via template parameter, + /// store a hash code in the node to speed up hash calculations + /// and comparisons in some cases. + /// + template + struct hash_node; + + EA_DISABLE_VC_WARNING(4625 4626) // "copy constructor / assignment operator could not be generated because a base class copy constructor is inaccessible or deleted" + #ifdef EA_COMPILER_MSVC_2015 + EA_DISABLE_VC_WARNING(5026) // disable warning: "move constructor was implicitly defined as deleted" + #endif + template + struct hash_node + { + hash_node() = default; + hash_node(const hash_node&) = default; + hash_node(hash_node&&) = default; + + Value mValue; + hash_node* mpNext; + eastl_size_t mnHashCode; // See config.h for the definition of eastl_size_t, which defaults to size_t. + } EASTL_MAY_ALIAS; + + template + struct hash_node + { + hash_node() = default; + hash_node(const hash_node&) = default; + hash_node(hash_node&&) = default; + + Value mValue; + hash_node* mpNext; + } EASTL_MAY_ALIAS; + + #ifdef EA_COMPILER_MSVC_2015 + EA_RESTORE_VC_WARNING() + #endif + EA_RESTORE_VC_WARNING() + + + // has_hashcode_member + // + // Custom type-trait that checks for the existence of a class data member 'mnHashCode'. + // + // In order to explicitly instantiate the hashtable without error we need to SFINAE away the functions that will + // fail to compile based on if the 'hash_node' contains a 'mnHashCode' member dictated by the hashtable template + // parameters. The hashtable support this level of configuration to allow users to choose which between the space vs. + // time optimization. + // + namespace Internal + { + template + struct has_hashcode_member + { + private: + template static eastl::no_type test(...); + template static eastl::yes_type test(decltype(U::mnHashCode)* = 0); + public: + static const bool value = sizeof(test(0)) == sizeof(eastl::yes_type); + }; + } + + static_assert(Internal::has_hashcode_member>::value, "contains a mnHashCode member"); + static_assert(!Internal::has_hashcode_member>::value, "doesn't contain a mnHashCode member"); + + // convenience macros to increase the readability of the code paths that must SFINAE on if the 'hash_node' + // contains the cached hashed value or not. + #define ENABLE_IF_HAS_HASHCODE(T, RT) typename eastl::enable_if::value, RT>::type* + #define ENABLE_IF_HASHCODE_EASTLSIZET(T, RT) typename eastl::enable_if::value, RT>::type + #define ENABLE_IF_TRUETYPE(T) typename eastl::enable_if::type* + #define DISABLE_IF_TRUETYPE(T) typename eastl::enable_if::type* + + + /// node_iterator_base + /// + /// Node iterators iterate nodes within a given bucket. + /// + /// We define a base class here because it is shared by both const and + /// non-const iterators. + /// + template + struct node_iterator_base + { + typedef hash_node node_type; + + node_type* mpNode; + + node_iterator_base(node_type* pNode) + : mpNode(pNode) { } + + void increment() + { mpNode = mpNode->mpNext; } + }; + + + + /// node_iterator + /// + /// Node iterators iterate nodes within a given bucket. + /// + /// The bConst parameter defines if the iterator is a const_iterator + /// or an iterator. + /// + template + struct node_iterator : public node_iterator_base + { + public: + typedef node_iterator_base base_type; + typedef node_iterator this_type; + typedef typename base_type::node_type node_type; + typedef Value value_type; + typedef typename type_select::type pointer; + typedef typename type_select::type reference; + typedef ptrdiff_t difference_type; + typedef EASTL_ITC_NS::forward_iterator_tag iterator_category; + + public: + explicit node_iterator(node_type* pNode = NULL) + : base_type(pNode) { } + + node_iterator(const node_iterator& x) + : base_type(x.mpNode) { } + + reference operator*() const + { return base_type::mpNode->mValue; } + + pointer operator->() const + { return &(base_type::mpNode->mValue); } + + node_iterator& operator++() + { base_type::increment(); return *this; } + + node_iterator operator++(int) + { node_iterator temp(*this); base_type::increment(); return temp; } + + }; // node_iterator + + + + /// hashtable_iterator_base + /// + /// A hashtable_iterator iterates the entire hash table and not just + /// nodes within a single bucket. Users in general will use a hash + /// table iterator much more often, as it is much like other container + /// iterators (e.g. vector::iterator). + /// + /// We define a base class here because it is shared by both const and + /// non-const iterators. + /// + template + struct hashtable_iterator_base + { + public: + typedef hashtable_iterator_base this_type; + typedef hash_node node_type; + + protected: + template + friend class hashtable; + + template + friend struct hashtable_iterator; + + template + friend bool operator==(const hashtable_iterator_base&, const hashtable_iterator_base&); + + template + friend bool operator!=(const hashtable_iterator_base&, const hashtable_iterator_base&); + + node_type* mpNode; // Current node within current bucket. + node_type** mpBucket; // Current bucket. + + public: + hashtable_iterator_base(node_type* pNode, node_type** pBucket) + : mpNode(pNode), mpBucket(pBucket) { } + + void increment_bucket() + { + ++mpBucket; + while(*mpBucket == NULL) // We store an extra bucket with some non-NULL value at the end + ++mpBucket; // of the bucket array so that finding the end of the bucket + mpNode = *mpBucket; // array is quick and simple. + } + + void increment() + { + mpNode = mpNode->mpNext; + + while(mpNode == NULL) + mpNode = *++mpBucket; + } + + }; // hashtable_iterator_base + + + + + /// hashtable_iterator + /// + /// A hashtable_iterator iterates the entire hash table and not just + /// nodes within a single bucket. Users in general will use a hash + /// table iterator much more often, as it is much like other container + /// iterators (e.g. vector::iterator). + /// + /// The bConst parameter defines if the iterator is a const_iterator + /// or an iterator. + /// + template + struct hashtable_iterator : public hashtable_iterator_base + { + public: + typedef hashtable_iterator_base base_type; + typedef hashtable_iterator this_type; + typedef hashtable_iterator this_type_non_const; + typedef typename base_type::node_type node_type; + typedef Value value_type; + typedef typename type_select::type pointer; + typedef typename type_select::type reference; + typedef ptrdiff_t difference_type; + typedef EASTL_ITC_NS::forward_iterator_tag iterator_category; + + public: + hashtable_iterator(node_type* pNode = NULL, node_type** pBucket = NULL) + : base_type(pNode, pBucket) { } + + hashtable_iterator(node_type** pBucket) + : base_type(*pBucket, pBucket) { } + + hashtable_iterator(const this_type_non_const& x) + : base_type(x.mpNode, x.mpBucket) { } + + reference operator*() const + { return base_type::mpNode->mValue; } + + pointer operator->() const + { return &(base_type::mpNode->mValue); } + + hashtable_iterator& operator++() + { base_type::increment(); return *this; } + + hashtable_iterator operator++(int) + { hashtable_iterator temp(*this); base_type::increment(); return temp; } + + const node_type* get_node() const + { return base_type::mpNode; } + + }; // hashtable_iterator + + + + + /// ht_distance + /// + /// This function returns the same thing as distance() for + /// forward iterators but returns zero for input iterators. + /// The reason why is that input iterators can only be read + /// once, and calling distance() on an input iterator destroys + /// the ability to read it. This ht_distance is used only for + /// optimization and so the code will merely work better with + /// forward iterators that input iterators. + /// + template + inline typename eastl::iterator_traits::difference_type + distance_fw_impl(Iterator /*first*/, Iterator /*last*/, EASTL_ITC_NS::input_iterator_tag) + { + return 0; + } + + template + inline typename eastl::iterator_traits::difference_type + distance_fw_impl(Iterator first, Iterator last, EASTL_ITC_NS::forward_iterator_tag) + { return eastl::distance(first, last); } + + template + inline typename eastl::iterator_traits::difference_type + ht_distance(Iterator first, Iterator last) + { + typedef typename eastl::iterator_traits::iterator_category IC; + return distance_fw_impl(first, last, IC()); + } + + + + + /// mod_range_hashing + /// + /// Implements the algorithm for conversion of a number in the range of + /// [0, SIZE_T_MAX] to the range of [0, BucketCount). + /// + struct mod_range_hashing + { + uint32_t operator()(size_t r, uint32_t n) const + { return r % n; } + }; + + + /// default_ranged_hash + /// + /// Default ranged hash function H. In principle it should be a + /// function object composed from objects of type H1 and H2 such that + /// h(k, n) = h2(h1(k), n), but that would mean making extra copies of + /// h1 and h2. So instead we'll just use a tag to tell class template + /// hashtable to do that composition. + /// + struct default_ranged_hash{ }; + + + /// prime_rehash_policy + /// + /// Default value for rehash policy. Bucket size is (usually) the + /// smallest prime that keeps the load factor small enough. + /// + struct EASTL_API prime_rehash_policy + { + public: + uint32_t mfMaxLoadFactor; + uint32_t mfGrowthFactor; + mutable uint32_t mnNextResize; + + public: + prime_rehash_policy(uint32_t fMaxLoadFactor = 1) + : mfMaxLoadFactor(fMaxLoadFactor), mfGrowthFactor(2), mnNextResize(0) { } + + float GetMaxLoadFactor() const + { return mfMaxLoadFactor; } + + /// Return a bucket count no greater than nBucketCountHint, + /// Don't update member variables while at it. + static uint32_t GetPrevBucketCountOnly(uint32_t nBucketCountHint); + + /// Return a bucket count no greater than nBucketCountHint. + /// This function has a side effect of updating mnNextResize. + uint32_t GetPrevBucketCount(uint32_t nBucketCountHint) const; + + /// Return a bucket count no smaller than nBucketCountHint. + /// This function has a side effect of updating mnNextResize. + uint32_t GetNextBucketCount(uint32_t nBucketCountHint) const; + + /// Return a bucket count appropriate for nElementCount elements. + /// This function has a side effect of updating mnNextResize. + uint32_t GetBucketCount(uint32_t nElementCount) const; + + /// nBucketCount is current bucket count, nElementCount is current element count, + /// and nElementAdd is number of elements to be inserted. Do we need + /// to increase bucket count? If so, return pair(true, n), where + /// n is the new bucket count. If not, return pair(false, 0). + eastl::pair + GetRehashRequired(uint32_t nBucketCount, uint32_t nElementCount, uint32_t nElementAdd) const; + }; + + + + + + /////////////////////////////////////////////////////////////////////// + // Base classes for hashtable. We define these base classes because + // in some cases we want to do different things depending on the + // value of a policy class. In some cases the policy class affects + // which member functions and nested typedefs are defined; we handle that + // by specializing base class templates. Several of the base class templates + // need to access other members of class template hashtable, so we use + // the "curiously recurring template pattern" (parent class is templated + // on type of child class) for them. + /////////////////////////////////////////////////////////////////////// + + + /// rehash_base + /// + /// Give hashtable the get_max_load_factor functions if the rehash + /// policy is prime_rehash_policy. + /// + template + struct rehash_base { }; + + template + struct rehash_base + { + // Returns the max load factor, which is the load factor beyond + // which we rebuild the container with a new bucket count. + float get_max_load_factor() const + { + const Hashtable* const pThis = static_cast(this); + return pThis->rehash_policy().GetMaxLoadFactor(); + } + + // If you want to make the hashtable never rehash (resize), + // set the max load factor to be a very high number (e.g. 100000.f). + void set_max_load_factor(float fMaxLoadFactor) + { + Hashtable* const pThis = static_cast(this); + pThis->rehash_policy(prime_rehash_policy(fMaxLoadFactor)); + } + }; + + + + + /// hash_code_base + /// + /// Encapsulates two policy issues that aren't quite orthogonal. + /// (1) The difference between using a ranged hash function and using + /// the combination of a hash function and a range-hashing function. + /// In the former case we don't have such things as hash codes, so + /// we have a dummy type as placeholder. + /// (2) Whether or not we cache hash codes. Caching hash codes is + /// meaningless if we have a ranged hash function. This is because + /// a ranged hash function converts an object directly to its + /// bucket index without ostensibly using a hash code. + /// We also put the key extraction and equality comparison function + /// objects here, for convenience. + /// + template + struct hash_code_base; + + + /// hash_code_base + /// + /// Specialization: ranged hash function, no caching hash codes. + /// H1 and H2 are provided but ignored. We define a dummy hash code type. + /// + template + struct hash_code_base + { + protected: + ExtractKey mExtractKey; // To do: Make this member go away entirely, as it never has any data. + Equal mEqual; // To do: Make this instance use zero space when it is zero size. + H mRangedHash; // To do: Make this instance use zero space when it is zero size + + public: + H1 hash_function() const + { return H1(); } + + Equal equal_function() const // Deprecated. Use key_eq() instead, as key_eq is what the new C++ standard + { return mEqual; } // has specified in its hashtable (unordered_*) proposal. + + const Equal& key_eq() const + { return mEqual; } + + Equal& key_eq() + { return mEqual; } + + protected: + typedef void* hash_code_t; + typedef uint32_t bucket_index_t; + + hash_code_base(const ExtractKey& extractKey, const Equal& eq, const H1&, const H2&, const H& h) + : mExtractKey(extractKey), mEqual(eq), mRangedHash(h) { } + + hash_code_t get_hash_code(const Key& key) const + { + EA_UNUSED(key); + return NULL; + } + + bucket_index_t bucket_index(hash_code_t, uint32_t) const + { return (bucket_index_t)0; } + + bucket_index_t bucket_index(const Key& key, hash_code_t, uint32_t nBucketCount) const + { return (bucket_index_t)mRangedHash(key, nBucketCount); } + + bucket_index_t bucket_index(const hash_node* pNode, uint32_t nBucketCount) const + { return (bucket_index_t)mRangedHash(mExtractKey(pNode->mValue), nBucketCount); } + + bool compare(const Key& key, hash_code_t, hash_node* pNode) const + { return mEqual(key, mExtractKey(pNode->mValue)); } + + void copy_code(hash_node*, const hash_node*) const + { } // Nothing to do. + + void set_code(hash_node* pDest, hash_code_t c) const + { + EA_UNUSED(pDest); + EA_UNUSED(c); + } + + void base_swap(hash_code_base& x) + { + eastl::swap(mExtractKey, x.mExtractKey); + eastl::swap(mEqual, x.mEqual); + eastl::swap(mRangedHash, x.mRangedHash); + } + + }; // hash_code_base + + + + // No specialization for ranged hash function while caching hash codes. + // That combination is meaningless, and trying to do it is an error. + + + /// hash_code_base + /// + /// Specialization: ranged hash function, cache hash codes. + /// This combination is meaningless, so we provide only a declaration + /// and no definition. + /// + template + struct hash_code_base; + + + + /// hash_code_base + /// + /// Specialization: hash function and range-hashing function, + /// no caching of hash codes. H is provided but ignored. + /// Provides typedef and accessor required by TR1. + /// + template + struct hash_code_base + { + protected: + ExtractKey mExtractKey; + Equal mEqual; + H1 m_h1; + H2 m_h2; + + public: + typedef H1 hasher; + + H1 hash_function() const + { return m_h1; } + + Equal equal_function() const // Deprecated. Use key_eq() instead, as key_eq is what the new C++ standard + { return mEqual; } // has specified in its hashtable (unordered_*) proposal. + + const Equal& key_eq() const + { return mEqual; } + + Equal& key_eq() + { return mEqual; } + + protected: + typedef size_t hash_code_t; + typedef uint32_t bucket_index_t; + typedef hash_node node_type; + + hash_code_base(const ExtractKey& ex, const Equal& eq, const H1& h1, const H2& h2, const default_ranged_hash&) + : mExtractKey(ex), mEqual(eq), m_h1(h1), m_h2(h2) { } + + hash_code_t get_hash_code(const Key& key) const + { return (hash_code_t)m_h1(key); } + + bucket_index_t bucket_index(hash_code_t c, uint32_t nBucketCount) const + { return (bucket_index_t)m_h2(c, nBucketCount); } + + bucket_index_t bucket_index(const Key&, hash_code_t c, uint32_t nBucketCount) const + { return (bucket_index_t)m_h2(c, nBucketCount); } + + bucket_index_t bucket_index(const node_type* pNode, uint32_t nBucketCount) const + { return (bucket_index_t)m_h2((hash_code_t)m_h1(mExtractKey(pNode->mValue)), nBucketCount); } + + bool compare(const Key& key, hash_code_t, node_type* pNode) const + { return mEqual(key, mExtractKey(pNode->mValue)); } + + void copy_code(node_type*, const node_type*) const + { } // Nothing to do. + + void set_code(node_type*, hash_code_t) const + { } // Nothing to do. + + void base_swap(hash_code_base& x) + { + eastl::swap(mExtractKey, x.mExtractKey); + eastl::swap(mEqual, x.mEqual); + eastl::swap(m_h1, x.m_h1); + eastl::swap(m_h2, x.m_h2); + } + + }; // hash_code_base + + + + /// hash_code_base + /// + /// Specialization: hash function and range-hashing function, + /// caching hash codes. H is provided but ignored. + /// Provides typedef and accessor required by TR1. + /// + template + struct hash_code_base + { + protected: + ExtractKey mExtractKey; + Equal mEqual; + H1 m_h1; + H2 m_h2; + + public: + typedef H1 hasher; + + H1 hash_function() const + { return m_h1; } + + Equal equal_function() const // Deprecated. Use key_eq() instead, as key_eq is what the new C++ standard + { return mEqual; } // has specified in its hashtable (unordered_*) proposal. + + const Equal& key_eq() const + { return mEqual; } + + Equal& key_eq() + { return mEqual; } + + protected: + typedef uint32_t hash_code_t; + typedef uint32_t bucket_index_t; + typedef hash_node node_type; + + hash_code_base(const ExtractKey& ex, const Equal& eq, const H1& h1, const H2& h2, const default_ranged_hash&) + : mExtractKey(ex), mEqual(eq), m_h1(h1), m_h2(h2) { } + + hash_code_t get_hash_code(const Key& key) const + { return (hash_code_t)m_h1(key); } + + bucket_index_t bucket_index(hash_code_t c, uint32_t nBucketCount) const + { return (bucket_index_t)m_h2(c, nBucketCount); } + + bucket_index_t bucket_index(const Key&, hash_code_t c, uint32_t nBucketCount) const + { return (bucket_index_t)m_h2(c, nBucketCount); } + + bucket_index_t bucket_index(const node_type* pNode, uint32_t nBucketCount) const + { return (bucket_index_t)m_h2((uint32_t)pNode->mnHashCode, nBucketCount); } + + bool compare(const Key& key, hash_code_t c, node_type* pNode) const + { return (pNode->mnHashCode == c) && mEqual(key, mExtractKey(pNode->mValue)); } + + void copy_code(node_type* pDest, const node_type* pSource) const + { pDest->mnHashCode = pSource->mnHashCode; } + + void set_code(node_type* pDest, hash_code_t c) const + { pDest->mnHashCode = c; } + + void base_swap(hash_code_base& x) + { + eastl::swap(mExtractKey, x.mExtractKey); + eastl::swap(mEqual, x.mEqual); + eastl::swap(m_h1, x.m_h1); + eastl::swap(m_h2, x.m_h2); + } + + }; // hash_code_base + + + + + + /////////////////////////////////////////////////////////////////////////// + /// hashtable + /// + /// Key and Value: arbitrary CopyConstructible types. + /// + /// ExtractKey: function object that takes a object of type Value + /// and returns a value of type Key. + /// + /// Equal: function object that takes two objects of type k and returns + /// a bool-like value that is true if the two objects are considered equal. + /// + /// H1: a hash function. A unary function object with argument type + /// Key and result type size_t. Return values should be distributed + /// over the entire range [0, numeric_limits::max()]. + /// + /// H2: a range-hashing function (in the terminology of Tavori and + /// Dreizin). This is a function which takes the output of H1 and + /// converts it to the range of [0, n]. Usually it merely takes the + /// output of H1 and mods it to n. + /// + /// H: a ranged hash function (Tavori and Dreizin). This is merely + /// a class that combines the functionality of H1 and H2 together, + /// possibly in some way that is somehow improved over H1 and H2 + /// It is a binary function whose argument types are Key and size_t + /// and whose result type is uint32_t. Given arguments k and n, the + /// return value is in the range [0, n). Default: h(k, n) = h2(h1(k), n). + /// If H is anything other than the default, H1 and H2 are ignored, + /// as H is thus overriding H1 and H2. + /// + /// RehashPolicy: Policy class with three members, all of which govern + /// the bucket count. nBucket(n) returns a bucket count no smaller + /// than n. GetBucketCount(n) returns a bucket count appropriate + /// for an element count of n. GetRehashRequired(nBucketCount, nElementCount, nElementAdd) + /// determines whether, if the current bucket count is nBucket and the + /// current element count is nElementCount, we need to increase the bucket + /// count. If so, returns pair(true, n), where n is the new + /// bucket count. If not, returns pair(false, ). + /// + /// Currently it is hard-wired that the number of buckets never + /// shrinks. Should we allow RehashPolicy to change that? + /// + /// bCacheHashCode: true if we store the value of the hash + /// function along with the value. This is a time-space tradeoff. + /// Storing it may improve lookup speed by reducing the number of + /// times we need to call the Equal function. + /// + /// bMutableIterators: true if hashtable::iterator is a mutable + /// iterator, false if iterator and const_iterator are both const + /// iterators. This is true for hash_map and hash_multimap, + /// false for hash_set and hash_multiset. + /// + /// bUniqueKeys: true if the return value of hashtable::count(k) + /// is always at most one, false if it may be an arbitrary number. + /// This is true for hash_set and hash_map and is false for + /// hash_multiset and hash_multimap. + /// + /////////////////////////////////////////////////////////////////////// + /// Note: + /// 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. + /// + /// 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. See the find_as function + /// for more documentation on this. + /// + /// find_by_hash + /// In the interest of supporting fast operations wherever possible, + /// we provide a find_by_hash function which finds a node using its + /// hash code. This is useful for cases where the node's hash is + /// already known, allowing us to avoid a redundant hash operation + /// in the normal find path. + /// + template + class hashtable + : public rehash_base >, + public hash_code_base + { + public: + typedef Key key_type; + typedef Value value_type; + typedef typename ExtractKey::result_type mapped_type; + typedef hash_code_base hash_code_base_type; + typedef typename hash_code_base_type::hash_code_t hash_code_t; + typedef Allocator allocator_type; + typedef Equal key_equal; + typedef ptrdiff_t difference_type; + typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t. + typedef value_type& reference; + typedef const value_type& const_reference; + typedef node_iterator local_iterator; + typedef node_iterator const_local_iterator; + typedef hashtable_iterator iterator; + typedef hashtable_iterator const_iterator; + typedef hash_node node_type; + typedef typename type_select, iterator>::type insert_return_type; + typedef hashtable this_type; + typedef RehashPolicy rehash_policy_type; + typedef ExtractKey extract_key_type; + typedef H1 h1_type; + typedef H2 h2_type; + typedef H h_type; + typedef integral_constant has_unique_keys_type; + + using hash_code_base_type::key_eq; + using hash_code_base_type::hash_function; + using hash_code_base_type::mExtractKey; + using hash_code_base_type::get_hash_code; + using hash_code_base_type::bucket_index; + using hash_code_base_type::compare; + using hash_code_base_type::set_code; + using hash_code_base_type::copy_code; + + static const bool kCacheHashCode = bCacheHashCode; + + enum + { + // This enumeration is deprecated in favor of eastl::kHashtableAllocFlagBuckets. + kAllocFlagBuckets = eastl::kHashtableAllocFlagBuckets // Flag to allocator which indicates that we are allocating buckets and not nodes. + }; + + protected: + node_type** mpBucketArray; + size_type mnBucketCount; + size_type mnElementCount; + RehashPolicy mRehashPolicy; // To do: Use base class optimization to make this go away. + allocator_type mAllocator; // To do: Use base class optimization to make this go away. + + public: + hashtable(size_type nBucketCount, const H1&, const H2&, const H&, const Equal&, const ExtractKey&, + const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR); + + template + hashtable(FowardIterator first, FowardIterator last, size_type nBucketCount, + const H1&, const H2&, const H&, const Equal&, const ExtractKey&, + const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR); + + hashtable(const hashtable& x); + + // initializer_list ctor support is implemented in subclasses (e.g. hash_set). + // hashtable(initializer_list, size_type nBucketCount, const H1&, const H2&, const H&, + // const Equal&, const ExtractKey&, const allocator_type& allocator = EASTL_HASHTABLE_DEFAULT_ALLOCATOR); + + hashtable(this_type&& x); + hashtable(this_type&& x, const allocator_type& allocator); + ~hashtable(); + + const allocator_type& get_allocator() const EA_NOEXCEPT; + allocator_type& get_allocator() EA_NOEXCEPT; + void set_allocator(const allocator_type& allocator); + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list ilist); + this_type& operator=(this_type&& x); + + void swap(this_type& x); + + iterator begin() EA_NOEXCEPT + { + iterator i(mpBucketArray); + if(!i.mpNode) + i.increment_bucket(); + return i; + } + + const_iterator begin() const EA_NOEXCEPT + { + const_iterator i(mpBucketArray); + if(!i.mpNode) + i.increment_bucket(); + return i; + } + + const_iterator cbegin() const EA_NOEXCEPT + { return begin(); } + + iterator end() EA_NOEXCEPT + { return iterator(mpBucketArray + mnBucketCount); } + + const_iterator end() const EA_NOEXCEPT + { return const_iterator(mpBucketArray + mnBucketCount); } + + const_iterator cend() const EA_NOEXCEPT + { return const_iterator(mpBucketArray + mnBucketCount); } + + // Returns an iterator to the first item in bucket n. + local_iterator begin(size_type n) EA_NOEXCEPT + { return local_iterator(mpBucketArray[n]); } + + const_local_iterator begin(size_type n) const EA_NOEXCEPT + { return const_local_iterator(mpBucketArray[n]); } + + const_local_iterator cbegin(size_type n) const EA_NOEXCEPT + { return const_local_iterator(mpBucketArray[n]); } + + // Returns an iterator to the last item in a bucket returned by begin(n). + local_iterator end(size_type) EA_NOEXCEPT + { return local_iterator(NULL); } + + const_local_iterator end(size_type) const EA_NOEXCEPT + { return const_local_iterator(NULL); } + + const_local_iterator cend(size_type) const EA_NOEXCEPT + { return const_local_iterator(NULL); } + + bool empty() const EA_NOEXCEPT + { return mnElementCount == 0; } + + size_type size() const EA_NOEXCEPT + { return mnElementCount; } + + size_type bucket_count() const EA_NOEXCEPT + { return mnBucketCount; } + + size_type bucket_size(size_type n) const EA_NOEXCEPT + { return (size_type)eastl::distance(begin(n), end(n)); } + + //size_type bucket(const key_type& k) const EA_NOEXCEPT + // { return bucket_index(k, (hash code here), (uint32_t)mnBucketCount); } + + // Returns the ratio of element count to bucket count. A return value of 1 means + // there's an optimal 1 bucket for each element. + float load_factor() const EA_NOEXCEPT + { return (float)mnElementCount / (float)mnBucketCount; } + + // Inherited from the base class. + // Returns the max load factor, which is the load factor beyond + // which we rebuild the container with a new bucket count. + // get_max_load_factor comes from rehash_base. + // float get_max_load_factor() const; + + // Inherited from the base class. + // If you want to make the hashtable never rehash (resize), + // set the max load factor to be a very high number (e.g. 100000.f). + // set_max_load_factor comes from rehash_base. + // void set_max_load_factor(float fMaxLoadFactor); + + /// Generalization of get_max_load_factor. This is an extension that's + /// not present in C++ hash tables (unordered containers). + const rehash_policy_type& rehash_policy() const EA_NOEXCEPT + { return mRehashPolicy; } + + /// Generalization of set_max_load_factor. This is an extension that's + /// not present in C++ hash tables (unordered containers). + void rehash_policy(const rehash_policy_type& rehashPolicy); + + template + insert_return_type emplace(Args&&... args); + + template + iterator emplace_hint(const_iterator position, Args&&... args); + + template insert_return_type try_emplace(const key_type& k, Args&&... args); + template insert_return_type try_emplace(key_type&& k, Args&&... args); + template iterator try_emplace(const_iterator position, const key_type& k, Args&&... args); + template iterator try_emplace(const_iterator position, key_type&& k, Args&&... args); + + insert_return_type insert(const value_type& value); + insert_return_type insert(value_type&& otherValue); + iterator insert(const_iterator hint, const value_type& value); + iterator insert(const_iterator hint, value_type&& value); + void insert(std::initializer_list ilist); + template void insert(InputIterator first, InputIterator last); + //insert_return_type insert(node_type&& nh); + //iterator insert(const_iterator hint, node_type&& nh); + + // This overload attempts to mitigate the overhead associated with mismatched cv-quality elements of + // the hashtable pair. It can avoid copy overhead because it will perfect forward the user provided pair types + // until it can constructed in-place in the allocated hashtable node. + // + // Ideally we would remove this overload as it deprecated and removed in C++17 but it currently causes + // performance regressions for hashtables with complex keys (keys that allocate resources). + template , key_type> && + #endif + !eastl::is_literal_type_v

&& + eastl::is_constructible_v>> + insert_return_type insert(P&& otherValue); + + // Non-standard extension + template // See comments below for the const value_type& equivalent to this function. + insert_return_type insert(hash_code_t c, node_type* pNodeNew, P&& otherValue); + + // We provide a version of insert which lets the caller directly specify the hash value and + // a potential node to insert if needed. This allows for less thread contention in the case + // of a thread-shared hash table that's accessed during a mutex lock, because the hash calculation + // and node creation is done outside of the lock. If pNodeNew is supplied by the user (i.e. non-NULL) + // then it must be freeable via the hash table's allocator. If the return value is true then this function + // took over ownership of pNodeNew, else pNodeNew is still owned by the caller to free or to pass + // to another call to insert. pNodeNew need not be assigned the value by the caller, as the insert + // function will assign value to pNodeNew upon insertion into the hash table. pNodeNew may be + // created by the user with the allocate_uninitialized_node function, and freed by the free_uninitialized_node function. + insert_return_type insert(hash_code_t c, node_type* pNodeNew, const value_type& value); + + template eastl::pair insert_or_assign(const key_type& k, M&& obj); + template eastl::pair insert_or_assign(key_type&& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); + + // Used to allocate and free memory used by insert(const value_type& value, hash_code_t c, node_type* pNodeNew). + node_type* allocate_uninitialized_node(); + void free_uninitialized_node(node_type* pNode); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + size_type erase(const key_type& k); + + void clear(); + void clear(bool clearBuckets); // If clearBuckets is true, we free the bucket memory and set the bucket count back to the newly constructed count. + void reset_lose_memory() EA_NOEXCEPT; // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. + void rehash(size_type nBucketCount); + void reserve(size_type nElementCount); + + iterator find(const key_type& key); + const_iterator find(const key_type& key) const; + + /// Implements a find whereby the user supplies a comparison of a different type + /// than the hashtable value_type. A useful case of this is one whereby you have + /// a container of string objects but want to do searches via passing in char pointers. + /// The problem is that without this kind of find, you need to do the expensive operation + /// of converting the char pointer to a string so it can be used as the argument to the + /// find function. + /// + /// Example usage (namespaces omitted for brevity): + /// hash_set hashSet; + /// hashSet.find_as("hello"); // Use default hash and compare. + /// + /// Example usage (note that the predicate uses string as first type and char* as second): + /// hash_set hashSet; + /// hashSet.find_as("hello", hash(), equal_to_2()); + /// + template + iterator find_as(const U& u, UHash uhash, BinaryPredicate predicate); + + template + const_iterator find_as(const U& u, UHash uhash, BinaryPredicate predicate) const; + + template + iterator find_as(const U& u); + + template + const_iterator find_as(const U& u) const; + + // Note: find_by_hash and find_range_by_hash both perform a search based on a hash value. + // It is important to note that multiple hash values may map to the same hash bucket, so + // it would be incorrect to assume all items returned match the hash value that + // was searched for. + + /// Implements a find whereby the user supplies the node's hash code. + /// It returns an iterator to the first element that matches the given hash. However, there may be multiple elements that match the given hash. + + template + ENABLE_IF_HASHCODE_EASTLSIZET(HashCodeT, iterator) find_by_hash(HashCodeT c) + { + EASTL_CT_ASSERT_MSG(bCacheHashCode, + "find_by_hash(hash_code_t c) is designed to avoid recomputing hashes, " + "so it requires cached hash codes. Consider setting template parameter " + "bCacheHashCode to true or using find_by_hash(const key_type& k, hash_code_t c) instead."); + + const size_type n = (size_type)bucket_index(c, (uint32_t)mnBucketCount); + + node_type* const pNode = DoFindNode(mpBucketArray[n], c); + + return pNode ? iterator(pNode, mpBucketArray + n) : + iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end() + } + + template + ENABLE_IF_HASHCODE_EASTLSIZET(HashCodeT, const_iterator) find_by_hash(HashCodeT c) const + { + EASTL_CT_ASSERT_MSG(bCacheHashCode, + "find_by_hash(hash_code_t c) is designed to avoid recomputing hashes, " + "so it requires cached hash codes. Consider setting template parameter " + "bCacheHashCode to true or using find_by_hash(const key_type& k, hash_code_t c) instead."); + + const size_type n = (size_type)bucket_index(c, (uint32_t)mnBucketCount); + + node_type* const pNode = DoFindNode(mpBucketArray[n], c); + + return pNode ? + const_iterator(pNode, mpBucketArray + n) : + const_iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end() + } + + iterator find_by_hash(const key_type& k, hash_code_t c) + { + const size_type n = (size_type)bucket_index(c, (uint32_t)mnBucketCount); + + node_type* const pNode = DoFindNode(mpBucketArray[n], k, c); + return pNode ? iterator(pNode, mpBucketArray + n) : iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end() + } + + const_iterator find_by_hash(const key_type& k, hash_code_t c) const + { + const size_type n = (size_type)bucket_index(c, (uint32_t)mnBucketCount); + + node_type* const pNode = DoFindNode(mpBucketArray[n], k, c); + return pNode ? const_iterator(pNode, mpBucketArray + n) : const_iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end() + } + + // Returns a pair that allows iterating over all nodes in a hash bucket + // first in the pair returned holds the iterator for the beginning of the bucket, + // second in the pair returned holds the iterator for the end of the bucket, + // If no bucket is found, both values in the pair are set to end(). + // + // See also the note above. + eastl::pair find_range_by_hash(hash_code_t c); + eastl::pair find_range_by_hash(hash_code_t c) const; + + size_type count(const key_type& k) const EA_NOEXCEPT; + + eastl::pair equal_range(const key_type& k); + eastl::pair equal_range(const key_type& k) const; + + bool validate() const; + int validate_iterator(const_iterator i) const; + + protected: + // We must remove one of the 'DoGetResultIterator' overloads from the overload-set (via SFINAE) because both can + // not compile successfully at the same time. The 'bUniqueKeys' template parameter chooses at compile-time the + // type of 'insert_return_type' between a pair and a raw iterator. We must pick between the two + // overloads that unpacks the iterator from the pair or simply passes the provided iterator to the caller based + // on the class template parameter. + template + iterator DoGetResultIterator(BoolConstantT, + const insert_return_type& irt, + ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr) const EA_NOEXCEPT + { + return irt.first; + } + + template + iterator DoGetResultIterator(BoolConstantT, + const insert_return_type& irt, + DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr) const EA_NOEXCEPT + { + return irt; + } + + node_type* DoAllocateNodeFromKey(const key_type& key); + node_type* DoAllocateNodeFromKey(key_type&& key); + void DoFreeNode(node_type* pNode); + void DoFreeNodes(node_type** pBucketArray, size_type); + + node_type** DoAllocateBuckets(size_type n); + void DoFreeBuckets(node_type** pBucketArray, size_type n); + + template + eastl::pair DoInsertValue(BoolConstantT, Args&&... args); + + template + iterator DoInsertValue(BoolConstantT, Args&&... args); + + + template + eastl::pair DoInsertValueExtra(BoolConstantT, + const key_type& k, + hash_code_t c, + node_type* pNodeNew, + value_type&& value, + ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr); + + template + eastl::pair DoInsertValue(BoolConstantT, + value_type&& value, + ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr); + + template + iterator DoInsertValueExtra(BoolConstantT, + const key_type& k, + hash_code_t c, + node_type* pNodeNew, + value_type&& value, + DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); + + template + iterator DoInsertValue(BoolConstantT, value_type&& value, DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); + + + template + eastl::pair DoInsertValueExtra(BoolConstantT, + const key_type& k, + hash_code_t c, + node_type* pNodeNew, + const value_type& value, + ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr); + + template + eastl::pair DoInsertValue(BoolConstantT, + const value_type& value, + ENABLE_IF_TRUETYPE(BoolConstantT) = nullptr); + + template + iterator DoInsertValueExtra(BoolConstantT, + const key_type& k, + hash_code_t c, + node_type* pNodeNew, + const value_type& value, + DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); + + template + iterator DoInsertValue(BoolConstantT, const value_type& value, DISABLE_IF_TRUETYPE(BoolConstantT) = nullptr); + + template + node_type* DoAllocateNode(Args&&... args); + node_type* DoAllocateNode(value_type&& value); + node_type* DoAllocateNode(const value_type& value); + + // DoInsertKey is supposed to get hash_code_t c = get_hash_code(key). + // it is done in case application has it's own hashset/hashmap-like containter, where hash code is for some reason known prior the insert + // this allows to save some performance, especially with heavy hash functions + eastl::pair DoInsertKey(true_type, const key_type& key, hash_code_t c); + iterator DoInsertKey(false_type, const key_type& key, hash_code_t c); + eastl::pair DoInsertKey(true_type, key_type&& key, hash_code_t c); + iterator DoInsertKey(false_type, key_type&& key, hash_code_t c); + + // We keep DoInsertKey overload without third parameter, for compatibility with older revisions of EASTL (3.12.07 and earlier) + // It used to call get_hash_code as a first call inside the DoInsertKey. + eastl::pair DoInsertKey(true_type, const key_type& key) { return DoInsertKey(true_type(), key, get_hash_code(key)); } + iterator DoInsertKey(false_type, const key_type& key) { return DoInsertKey(false_type(), key, get_hash_code(key)); } + eastl::pair DoInsertKey(true_type, key_type&& key) { return DoInsertKey(true_type(), eastl::move(key), get_hash_code(key)); } + iterator DoInsertKey(false_type, key_type&& key) { return DoInsertKey(false_type(), eastl::move(key), get_hash_code(key)); } + + void DoRehash(size_type nBucketCount); + node_type* DoFindNode(node_type* pNode, const key_type& k, hash_code_t c) const; + + template + ENABLE_IF_HAS_HASHCODE(T, node_type) DoFindNode(T* pNode, hash_code_t c) const + { + for (; pNode; pNode = pNode->mpNext) + { + if (pNode->mnHashCode == c) + return pNode; + } + return NULL; + } + + template + node_type* DoFindNodeT(node_type* pNode, const U& u, BinaryPredicate predicate) const; + + }; // class hashtable + + + + + + /////////////////////////////////////////////////////////////////////// + // node_iterator_base + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const node_iterator_base& a, const node_iterator_base& b) + { return a.mpNode == b.mpNode; } + + template + inline bool operator!=(const node_iterator_base& a, const node_iterator_base& b) + { return a.mpNode != b.mpNode; } + + + + + /////////////////////////////////////////////////////////////////////// + // hashtable_iterator_base + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const hashtable_iterator_base& a, const hashtable_iterator_base& b) + { return a.mpNode == b.mpNode; } + + template + inline bool operator!=(const hashtable_iterator_base& a, const hashtable_iterator_base& b) + { return a.mpNode != b.mpNode; } + + + + + /////////////////////////////////////////////////////////////////////// + // hashtable + /////////////////////////////////////////////////////////////////////// + + template + hashtable + ::hashtable(size_type nBucketCount, const H1& h1, const H2& h2, const H& h, + const Eq& eq, const EK& ek, const allocator_type& allocator) + : rehash_base(), + hash_code_base(ek, eq, h1, h2, h), + mnBucketCount(0), + mnElementCount(0), + mRehashPolicy(), + mAllocator(allocator) + { + if(nBucketCount < 2) // If we are starting in an initially empty state, with no memory allocation done. + reset_lose_memory(); + else // Else we are creating a potentially non-empty hashtable... + { + EASTL_ASSERT(nBucketCount < 10000000); + mnBucketCount = (size_type)mRehashPolicy.GetNextBucketCount((uint32_t)nBucketCount); + mpBucketArray = DoAllocateBuckets(mnBucketCount); // mnBucketCount will always be at least 2. + } + } + + + + template + template + hashtable::hashtable(FowardIterator first, FowardIterator last, size_type nBucketCount, + const H1& h1, const H2& h2, const H& h, + const Eq& eq, const EK& ek, const allocator_type& allocator) + : rehash_base(), + hash_code_base(ek, eq, h1, h2, h), + //mnBucketCount(0), // This gets re-assigned below. + mnElementCount(0), + mRehashPolicy(), + mAllocator(allocator) + { + if(nBucketCount < 2) + { + const size_type nElementCount = (size_type)eastl::ht_distance(first, last); + mnBucketCount = (size_type)mRehashPolicy.GetBucketCount((uint32_t)nElementCount); + } + else + { + EASTL_ASSERT(nBucketCount < 10000000); + mnBucketCount = nBucketCount; + } + + mpBucketArray = DoAllocateBuckets(mnBucketCount); // mnBucketCount will always be at least 2. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(; first != last; ++first) + insert(*first); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + clear(); + DoFreeBuckets(mpBucketArray, mnBucketCount); + throw; + } + #endif + } + + + + template + hashtable::hashtable(const this_type& x) + : rehash_base(x), + hash_code_base(x), + mnBucketCount(x.mnBucketCount), + mnElementCount(x.mnElementCount), + mRehashPolicy(x.mRehashPolicy), + mAllocator(x.mAllocator) + { + if(mnElementCount) // If there is anything to copy... + { + mpBucketArray = DoAllocateBuckets(mnBucketCount); // mnBucketCount will be at least 2. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(size_type i = 0; i < x.mnBucketCount; ++i) + { + node_type* pNodeSource = x.mpBucketArray[i]; + node_type** ppNodeDest = mpBucketArray + i; + + while(pNodeSource) + { + *ppNodeDest = DoAllocateNode(pNodeSource->mValue); + copy_code(*ppNodeDest, pNodeSource); + ppNodeDest = &(*ppNodeDest)->mpNext; + pNodeSource = pNodeSource->mpNext; + } + } + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + clear(); + DoFreeBuckets(mpBucketArray, mnBucketCount); + throw; + } + #endif + } + else + { + // In this case, instead of allocate memory and copy nothing from x, + // we reset ourselves to a zero allocation state. + reset_lose_memory(); + } + } + + + template + hashtable::hashtable(this_type&& x) + : rehash_base(x), + hash_code_base(x), + mnBucketCount(0), + mnElementCount(0), + mRehashPolicy(x.mRehashPolicy), + mAllocator(x.mAllocator) + { + reset_lose_memory(); // We do this here the same as we do it in the default ctor because it puts the container in a proper initial empty state. This code would be cleaner if we could rely on being able to use C++11 delegating constructors and just call the default ctor here. + swap(x); + } + + + template + hashtable::hashtable(this_type&& x, const allocator_type& allocator) + : rehash_base(x), + hash_code_base(x), + mnBucketCount(0), + mnElementCount(0), + mRehashPolicy(x.mRehashPolicy), + mAllocator(allocator) + { + reset_lose_memory(); // We do this here the same as we do it in the default ctor because it puts the container in a proper initial empty state. This code would be cleaner if we could rely on being able to use C++11 delegating constructors and just call the default ctor here. + swap(x); // swap will directly or indirectly handle the possibility that mAllocator != x.mAllocator. + } + + + template + inline const typename hashtable::allocator_type& + hashtable::get_allocator() const EA_NOEXCEPT + { + return mAllocator; + } + + + + template + inline typename hashtable::allocator_type& + hashtable::get_allocator() EA_NOEXCEPT + { + return mAllocator; + } + + + + template + inline void hashtable::set_allocator(const allocator_type& allocator) + { + mAllocator = allocator; + } + + + + template + inline typename hashtable::this_type& + hashtable::operator=(const this_type& x) + { + if(this != &x) + { + clear(); + + #if EASTL_ALLOCATOR_COPY_ENABLED + mAllocator = x.mAllocator; + #endif + + insert(x.begin(), x.end()); + } + return *this; + } + + + template + inline typename hashtable::this_type& + hashtable::operator=(this_type&& x) + { + if(this != &x) + { + clear(); // To consider: Are we really required to clear here? x is going away soon and will clear itself in its dtor. + swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. + } + return *this; + } + + + template + inline typename hashtable::this_type& + hashtable::operator=(std::initializer_list ilist) + { + // The simplest means of doing this is to clear and insert. There probably isn't a generic + // solution that's any more efficient without having prior knowledge of the ilist contents. + clear(); + insert(ilist.begin(), ilist.end()); + return *this; + } + + + + template + inline hashtable::~hashtable() + { + clear(); + DoFreeBuckets(mpBucketArray, mnBucketCount); + } + + + template + typename hashtable::node_type* + hashtable::DoAllocateNodeFromKey(const key_type& key) + { + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); + EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new(eastl::addressof(pNode->mValue)) value_type(pair_first_construct, key); + pNode->mpNext = NULL; + return pNode; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + EASTLFree(mAllocator, pNode, sizeof(node_type)); + throw; + } + #endif + } + + + template + typename hashtable::node_type* + hashtable::DoAllocateNodeFromKey(key_type&& key) + { + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); + EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new(eastl::addressof(pNode->mValue)) value_type(pair_first_construct, eastl::move(key)); + pNode->mpNext = NULL; + return pNode; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + EASTLFree(mAllocator, pNode, sizeof(node_type)); + throw; + } + #endif + } + + + template + inline void hashtable::DoFreeNode(node_type* pNode) + { + pNode->~node_type(); + EASTLFree(mAllocator, pNode, sizeof(node_type)); + } + + + + template + void hashtable::DoFreeNodes(node_type** pNodeArray, size_type n) + { + for(size_type i = 0; i < n; ++i) + { + node_type* pNode = pNodeArray[i]; + while(pNode) + { + node_type* const pTempNode = pNode; + pNode = pNode->mpNext; + DoFreeNode(pTempNode); + } + pNodeArray[i] = NULL; + } + } + + + + template + typename hashtable::node_type** + hashtable::DoAllocateBuckets(size_type n) + { + // We allocate one extra bucket to hold a sentinel, an arbitrary + // non-null pointer. Iterator increment relies on this. + EASTL_ASSERT(n > 1); // We reserve an mnBucketCount of 1 for the shared gpEmptyBucketArray. + EASTL_CT_ASSERT(kHashtableAllocFlagBuckets == 0x00400000); // Currently we expect this to be so, because the allocator has a copy of this enum. + node_type** const pBucketArray = (node_type**)EASTLAllocAlignedFlags(mAllocator, (n + 1) * sizeof(node_type*), EASTL_ALIGN_OF(node_type*), 0, kHashtableAllocFlagBuckets); + //eastl::fill(pBucketArray, pBucketArray + n, (node_type*)NULL); + memset(pBucketArray, 0, n * sizeof(node_type*)); + pBucketArray[n] = reinterpret_cast((uintptr_t)~0); + return pBucketArray; + } + + + + template + inline void hashtable::DoFreeBuckets(node_type** pBucketArray, size_type n) + { + // If n <= 1, then pBucketArray is from the shared gpEmptyBucketArray. We don't test + // for pBucketArray == &gpEmptyBucketArray because one library have a different gpEmptyBucketArray + // than another but pass a hashtable to another. So we go by the size. + if(n > 1) + EASTLFree(mAllocator, pBucketArray, (n + 1) * sizeof(node_type*)); // '+1' because DoAllocateBuckets allocates nBucketCount + 1 buckets in order to have a NULL sentinel at the end. + } + + + template + void hashtable::swap(this_type& x) + { + hash_code_base::base_swap(x); // hash_code_base has multiple implementations, so we let them handle the swap. + eastl::swap(mRehashPolicy, x.mRehashPolicy); + EASTL_MACRO_SWAP(node_type**, mpBucketArray, x.mpBucketArray); + eastl::swap(mnBucketCount, x.mnBucketCount); + eastl::swap(mnElementCount, x.mnElementCount); + + if (mAllocator != x.mAllocator) // If allocators are not equivalent... + { + eastl::swap(mAllocator, x.mAllocator); + } + } + + + template + inline void hashtable::rehash_policy(const rehash_policy_type& rehashPolicy) + { + mRehashPolicy = rehashPolicy; + + const size_type nBuckets = rehashPolicy.GetBucketCount((uint32_t)mnElementCount); + + if(nBuckets > mnBucketCount) + DoRehash(nBuckets); + } + + + + template + inline typename hashtable::iterator + hashtable::find(const key_type& k) + { + const hash_code_t c = get_hash_code(k); + const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + + node_type* const pNode = DoFindNode(mpBucketArray[n], k, c); + return pNode ? iterator(pNode, mpBucketArray + n) : iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end() + } + + + + template + inline typename hashtable::const_iterator + hashtable::find(const key_type& k) const + { + const hash_code_t c = get_hash_code(k); + const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + + node_type* const pNode = DoFindNode(mpBucketArray[n], k, c); + return pNode ? const_iterator(pNode, mpBucketArray + n) : const_iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end() + } + + + + template + template + inline typename hashtable::iterator + hashtable::find_as(const U& other, UHash uhash, BinaryPredicate predicate) + { + const hash_code_t c = (hash_code_t)uhash(other); + const size_type n = (size_type)(c % mnBucketCount); // This assumes we are using the mod range policy. + + node_type* const pNode = DoFindNodeT(mpBucketArray[n], other, predicate); + return pNode ? iterator(pNode, mpBucketArray + n) : iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end() + } + + + + template + template + inline typename hashtable::const_iterator + hashtable::find_as(const U& other, UHash uhash, BinaryPredicate predicate) const + { + const hash_code_t c = (hash_code_t)uhash(other); + const size_type n = (size_type)(c % mnBucketCount); // This assumes we are using the mod range policy. + + node_type* const pNode = DoFindNodeT(mpBucketArray[n], other, predicate); + return pNode ? const_iterator(pNode, mpBucketArray + n) : const_iterator(mpBucketArray + mnBucketCount); // iterator(mpBucketArray + mnBucketCount) == end() + } + + + /// hashtable_find + /// + /// Helper function that defaults to using hash and equal_to_2. + /// This makes it so that by default you don't need to provide these. + /// Note that the default hash functions may not be what you want, though. + /// + /// Example usage. Instead of this: + /// hash_set hashSet; + /// hashSet.find("hello", hash(), equal_to_2()); + /// + /// You can use this: + /// hash_set hashSet; + /// hashtable_find(hashSet, "hello"); + /// + template + inline typename H::iterator hashtable_find(H& hashTable, U u) + { return hashTable.find_as(u, eastl::hash(), eastl::equal_to_2()); } + + template + inline typename H::const_iterator hashtable_find(const H& hashTable, U u) + { return hashTable.find_as(u, eastl::hash(), eastl::equal_to_2()); } + + + + template + template + inline typename hashtable::iterator + hashtable::find_as(const U& other) + { return eastl::hashtable_find(*this, other); } + // VC++ doesn't appear to like the following, though it seems correct to me. + // So we implement the workaround above until we can straighten this out. + //{ return find_as(other, eastl::hash(), eastl::equal_to_2()); } + + + template + template + inline typename hashtable::const_iterator + hashtable::find_as(const U& other) const + { return eastl::hashtable_find(*this, other); } + // VC++ doesn't appear to like the following, though it seems correct to me. + // So we implement the workaround above until we can straighten this out. + //{ return find_as(other, eastl::hash(), eastl::equal_to_2()); } + + + + template + eastl::pair::const_iterator, + typename hashtable::const_iterator> + hashtable::find_range_by_hash(hash_code_t c) const + { + const size_type start = (size_type)bucket_index(c, (uint32_t)mnBucketCount); + node_type* const pNodeStart = mpBucketArray[start]; + + if (pNodeStart) + { + eastl::pair pair(const_iterator(pNodeStart, mpBucketArray + start), + const_iterator(pNodeStart, mpBucketArray + start)); + pair.second.increment_bucket(); + return pair; + } + + return eastl::pair(const_iterator(mpBucketArray + mnBucketCount), + const_iterator(mpBucketArray + mnBucketCount)); + } + + + + template + eastl::pair::iterator, + typename hashtable::iterator> + hashtable::find_range_by_hash(hash_code_t c) + { + const size_type start = (size_type)bucket_index(c, (uint32_t)mnBucketCount); + node_type* const pNodeStart = mpBucketArray[start]; + + if (pNodeStart) + { + eastl::pair pair(iterator(pNodeStart, mpBucketArray + start), + iterator(pNodeStart, mpBucketArray + start)); + pair.second.increment_bucket(); + return pair; + + } + + return eastl::pair(iterator(mpBucketArray + mnBucketCount), + iterator(mpBucketArray + mnBucketCount)); + } + + + + template + typename hashtable::size_type + hashtable::count(const key_type& k) const EA_NOEXCEPT + { + const hash_code_t c = get_hash_code(k); + const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + size_type result = 0; + + // To do: Make a specialization for bU (unique keys) == true and take + // advantage of the fact that the count will always be zero or one in that case. + for(node_type* pNode = mpBucketArray[n]; pNode; pNode = pNode->mpNext) + { + if(compare(k, c, pNode)) + ++result; + } + return result; + } + + + + template + eastl::pair::iterator, + typename hashtable::iterator> + hashtable::equal_range(const key_type& k) + { + const hash_code_t c = get_hash_code(k); + const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + node_type** head = mpBucketArray + n; + node_type* pNode = DoFindNode(*head, k, c); + + if(pNode) + { + node_type* p1 = pNode->mpNext; + + for(; p1; p1 = p1->mpNext) + { + if(!compare(k, c, p1)) + break; + } + + iterator first(pNode, head); + iterator last(p1, head); + + if(!p1) + last.increment_bucket(); + + return eastl::pair(first, last); + } + + return eastl::pair(iterator(mpBucketArray + mnBucketCount), // iterator(mpBucketArray + mnBucketCount) == end() + iterator(mpBucketArray + mnBucketCount)); + } + + + + + template + eastl::pair::const_iterator, + typename hashtable::const_iterator> + hashtable::equal_range(const key_type& k) const + { + const hash_code_t c = get_hash_code(k); + const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + node_type** head = mpBucketArray + n; + node_type* pNode = DoFindNode(*head, k, c); + + if(pNode) + { + node_type* p1 = pNode->mpNext; + + for(; p1; p1 = p1->mpNext) + { + if(!compare(k, c, p1)) + break; + } + + const_iterator first(pNode, head); + const_iterator last(p1, head); + + if(!p1) + last.increment_bucket(); + + return eastl::pair(first, last); + } + + return eastl::pair(const_iterator(mpBucketArray + mnBucketCount), // iterator(mpBucketArray + mnBucketCount) == end() + const_iterator(mpBucketArray + mnBucketCount)); + } + + + + template + inline typename hashtable::node_type* + hashtable::DoFindNode(node_type* pNode, const key_type& k, hash_code_t c) const + { + for(; pNode; pNode = pNode->mpNext) + { + if(compare(k, c, pNode)) + return pNode; + } + return NULL; + } + + + + template + template + inline typename hashtable::node_type* + hashtable::DoFindNodeT(node_type* pNode, const U& other, BinaryPredicate predicate) const + { + for(; pNode; pNode = pNode->mpNext) + { + if(predicate(mExtractKey(pNode->mValue), other)) // Intentionally compare with key as first arg and other as second arg. + return pNode; + } + return NULL; + } + + + + template + template + eastl::pair::iterator, bool> + hashtable::DoInsertValue(BoolConstantT, Args&&... args) // true_type means bUniqueKeys is true. + { + // Adds the value to the hash table if not already present. + // If already present then the existing value is returned via an iterator/bool pair. + + // We have a chicken-and-egg problem here. In order to know if and where to insert the value, we need to get the + // hashtable key for the value. But we don't explicitly have a value argument, we have a templated Args&&... argument. + // We need the value_type in order to proceed, but that entails getting an instance of a value_type from the args. + // And it may turn out that the value is already present in the hashtable and we need to cancel the insertion, + // despite having obtained a value_type to put into the hashtable. We have mitigated this problem somewhat by providing + // specializations of the insert function for const value_type& and value_type&&, and so the only time this function + // should get called is when args refers to arguments to construct a value_type. + + node_type* const pNodeNew = DoAllocateNode(eastl::forward(args)...); + const key_type& k = mExtractKey(pNodeNew->mValue); + const hash_code_t c = get_hash_code(k); + size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + node_type* const pNode = DoFindNode(mpBucketArray[n], k, c); + + if(pNode == NULL) // If value is not present... add it. + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + if(bRehash.first) + { + n = (size_type)bucket_index(k, c, (uint32_t)bRehash.second); + DoRehash(bRehash.second); + } + + EASTL_ASSERT((uintptr_t)mpBucketArray != (uintptr_t)&gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + ++mnElementCount; + + return eastl::pair(iterator(pNodeNew, mpBucketArray + n), true); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeNode(pNodeNew); + throw; + } + #endif + } + else + { + // To do: We have an inefficiency to deal with here. We allocated a node above but we are freeing it here because + // it turned out it wasn't needed. But we needed to create the node in order to get the hashtable key for + // the node. One possible resolution is to create specializations: DoInsertValue(true_type, value_type&&) and + // DoInsertValue(true_type, const value_type&) which don't need to create a node up front in order to get the + // hashtable key. Probably most users would end up using these pathways instead of this Args... pathway. + // While we should considering handling this to-do item, a lot of the performance limitations of maps and sets + // in practice is with finding elements rather than adding (potentially redundant) new elements. + DoFreeNode(pNodeNew); + } + + return eastl::pair(iterator(pNode, mpBucketArray + n), false); + } + + + template + template + typename hashtable::iterator + hashtable::DoInsertValue(BoolConstantT, Args&&... args) // false_type means bUniqueKeys is false. + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + if(bRehash.first) + DoRehash(bRehash.second); + + node_type* pNodeNew = DoAllocateNode(eastl::forward(args)...); + const key_type& k = mExtractKey(pNodeNew->mValue); + const hash_code_t c = get_hash_code(k); + const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + // To consider: Possibly make this insertion not make equal elements contiguous. + // As it stands now, we insert equal values contiguously in the hashtable. + // The benefit is that equal_range can work in a sensible manner and that + // erase(value) can more quickly find equal values. The downside is that + // this insertion operation taking some extra time. How important is it to + // us that equal_range span all equal items? + node_type* const pNodePrev = DoFindNode(mpBucketArray[n], k, c); + + if(pNodePrev == NULL) + { + EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + } + else + { + pNodeNew->mpNext = pNodePrev->mpNext; + pNodePrev->mpNext = pNodeNew; + } + + ++mnElementCount; + + return iterator(pNodeNew, mpBucketArray + n); + } + + + template + template + typename hashtable::node_type* + hashtable::DoAllocateNode(Args&&... args) + { + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); + EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new(eastl::addressof(pNode->mValue)) value_type(eastl::forward(args)...); + pNode->mpNext = NULL; + return pNode; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + EASTLFree(mAllocator, pNode, sizeof(node_type)); + throw; + } + #endif + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // Note: The following insertion-related functions are nearly copies of the above three functions, + // but are for value_type&& and const value_type& arguments. It's useful for us to have the functions + // below, even when using a fully compliant C++11 compiler that supports the above functions. + // The reason is because the specializations below are slightly more efficient because they can delay + // the creation of a node until it's known that it will be needed. + //////////////////////////////////////////////////////////////////////////////////////////////////// + + template + template + eastl::pair::iterator, bool> + hashtable::DoInsertValueExtra(BoolConstantT, const key_type& k, + hash_code_t c, node_type* pNodeNew, value_type&& value, ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. + { + // Adds the value to the hash table if not already present. + // If already present then the existing value is returned via an iterator/bool pair. + size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + node_type* const pNode = DoFindNode(mpBucketArray[n], k, c); + + if(pNode == NULL) // If value is not present... add it. + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + // Allocate the new node before doing the rehash so that we don't + // do a rehash if the allocation throws. + #if EASTL_EXCEPTIONS_ENABLED + bool nodeAllocated; // If exceptions are enabled then we we need to track if we allocated the node so we can free it in the catch block. + #endif + + if(pNodeNew) + { + ::new(eastl::addressof(pNodeNew->mValue)) value_type(eastl::move(value)); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. + #if EASTL_EXCEPTIONS_ENABLED + nodeAllocated = false; + #endif + } + else + { + pNodeNew = DoAllocateNode(eastl::move(value)); + #if EASTL_EXCEPTIONS_ENABLED + nodeAllocated = true; + #endif + } + + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + if(bRehash.first) + { + n = (size_type)bucket_index(k, c, (uint32_t)bRehash.second); + DoRehash(bRehash.second); + } + + EASTL_ASSERT((uintptr_t)mpBucketArray != (uintptr_t)&gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + ++mnElementCount; + + return eastl::pair(iterator(pNodeNew, mpBucketArray + n), true); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + if(nodeAllocated) // If we allocated the node within this function, free it. Else let the caller retain ownership of it. + DoFreeNode(pNodeNew); + throw; + } + #endif + } + // Else the value is already present, so don't add a new node. And don't free pNodeNew. + + return eastl::pair(iterator(pNode, mpBucketArray + n), false); + } + + + template + template + eastl::pair::iterator, bool> + hashtable::DoInsertValue(BoolConstantT, value_type&& value, ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. + { + const key_type& k = mExtractKey(value); + const hash_code_t c = get_hash_code(k); + + return DoInsertValueExtra(true_type(), k, c, NULL, eastl::move(value)); + } + + + template + template + typename hashtable::iterator + hashtable::DoInsertValueExtra(BoolConstantT, const key_type& k, hash_code_t c, node_type* pNodeNew, value_type&& value, + DISABLE_IF_TRUETYPE(BoolConstantT)) // false_type means bUniqueKeys is false. + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + if(bRehash.first) + DoRehash(bRehash.second); // Note: We don't need to wrap this call with try/catch because there's nothing we would need to do in the catch. + + const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + + if(pNodeNew) + ::new(eastl::addressof(pNodeNew->mValue)) value_type(eastl::move(value)); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. + else + pNodeNew = DoAllocateNode(eastl::move(value)); + + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + // To consider: Possibly make this insertion not make equal elements contiguous. + // As it stands now, we insert equal values contiguously in the hashtable. + // The benefit is that equal_range can work in a sensible manner and that + // erase(value) can more quickly find equal values. The downside is that + // this insertion operation taking some extra time. How important is it to + // us that equal_range span all equal items? + node_type* const pNodePrev = DoFindNode(mpBucketArray[n], k, c); + + if(pNodePrev == NULL) + { + EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + } + else + { + pNodeNew->mpNext = pNodePrev->mpNext; + pNodePrev->mpNext = pNodeNew; + } + + ++mnElementCount; + + return iterator(pNodeNew, mpBucketArray + n); + } + + + template + template + typename hashtable::iterator + hashtable::DoInsertValue(BoolConstantT, value_type&& value, DISABLE_IF_TRUETYPE(BoolConstantT)) // false_type means bUniqueKeys is false. + { + const key_type& k = mExtractKey(value); + const hash_code_t c = get_hash_code(k); + + return DoInsertValueExtra(false_type(), k, c, NULL, eastl::move(value)); + } + + + template + typename hashtable::node_type* + hashtable::DoAllocateNode(value_type&& value) + { + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); + EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new(eastl::addressof(pNode->mValue)) value_type(eastl::move(value)); + pNode->mpNext = NULL; + return pNode; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + EASTLFree(mAllocator, pNode, sizeof(node_type)); + throw; + } + #endif + } + + + template + template + eastl::pair::iterator, bool> + hashtable::DoInsertValueExtra(BoolConstantT, const key_type& k, hash_code_t c, node_type* pNodeNew, const value_type& value, + ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. + { + // Adds the value to the hash table if not already present. + // If already present then the existing value is returned via an iterator/bool pair. + size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + node_type* const pNode = DoFindNode(mpBucketArray[n], k, c); + + if(pNode == NULL) // If value is not present... add it. + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + // Allocate the new node before doing the rehash so that we don't + // do a rehash if the allocation throws. + #if EASTL_EXCEPTIONS_ENABLED + bool nodeAllocated; // If exceptions are enabled then we we need to track if we allocated the node so we can free it in the catch block. + #endif + + if(pNodeNew) + { + ::new(eastl::addressof(pNodeNew->mValue)) value_type(value); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. + #if EASTL_EXCEPTIONS_ENABLED + nodeAllocated = false; + #endif + } + else + { + pNodeNew = DoAllocateNode(value); + #if EASTL_EXCEPTIONS_ENABLED + nodeAllocated = true; + #endif + } + + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + if(bRehash.first) + { + n = (size_type)bucket_index(k, c, (uint32_t)bRehash.second); + DoRehash(bRehash.second); + } + + EASTL_ASSERT((uintptr_t)mpBucketArray != (uintptr_t)&gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + ++mnElementCount; + + return eastl::pair(iterator(pNodeNew, mpBucketArray + n), true); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + if(nodeAllocated) // If we allocated the node within this function, free it. Else let the caller retain ownership of it. + DoFreeNode(pNodeNew); + throw; + } + #endif + } + // Else the value is already present, so don't add a new node. And don't free pNodeNew. + + return eastl::pair(iterator(pNode, mpBucketArray + n), false); + } + + + template + template + eastl::pair::iterator, bool> + hashtable::DoInsertValue(BoolConstantT, const value_type& value, ENABLE_IF_TRUETYPE(BoolConstantT)) // true_type means bUniqueKeys is true. + { + const key_type& k = mExtractKey(value); + const hash_code_t c = get_hash_code(k); + + return DoInsertValueExtra(true_type(), k, c, NULL, value); + } + + + template + template + typename hashtable::iterator + hashtable::DoInsertValueExtra(BoolConstantT, const key_type& k, hash_code_t c, node_type* pNodeNew, const value_type& value, + DISABLE_IF_TRUETYPE(BoolConstantT)) // false_type means bUniqueKeys is false. + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + if(bRehash.first) + DoRehash(bRehash.second); // Note: We don't need to wrap this call with try/catch because there's nothing we would need to do in the catch. + + const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + + if(pNodeNew) + ::new(eastl::addressof(pNodeNew->mValue)) value_type(value); // It's expected that pNodeNew was allocated with allocate_uninitialized_node. + else + pNodeNew = DoAllocateNode(value); + + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + // To consider: Possibly make this insertion not make equal elements contiguous. + // As it stands now, we insert equal values contiguously in the hashtable. + // The benefit is that equal_range can work in a sensible manner and that + // erase(value) can more quickly find equal values. The downside is that + // this insertion operation taking some extra time. How important is it to + // us that equal_range span all equal items? + node_type* const pNodePrev = DoFindNode(mpBucketArray[n], k, c); + + if(pNodePrev == NULL) + { + EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + } + else + { + pNodeNew->mpNext = pNodePrev->mpNext; + pNodePrev->mpNext = pNodeNew; + } + + ++mnElementCount; + + return iterator(pNodeNew, mpBucketArray + n); + } + + + template + template + typename hashtable::iterator + hashtable::DoInsertValue(BoolConstantT, const value_type& value, DISABLE_IF_TRUETYPE(BoolConstantT)) // false_type means bUniqueKeys is false. + { + const key_type& k = mExtractKey(value); + const hash_code_t c = get_hash_code(k); + + return DoInsertValueExtra(false_type(), k, c, NULL, value); + } + + + template + typename hashtable::node_type* + hashtable::DoAllocateNode(const value_type& value) + { + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); + EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new(eastl::addressof(pNode->mValue)) value_type(value); + pNode->mpNext = NULL; + return pNode; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + EASTLFree(mAllocator, pNode, sizeof(node_type)); + throw; + } + #endif + } + + + template + typename hashtable::node_type* + hashtable::allocate_uninitialized_node() + { + // We don't wrap this in try/catch because users of this function are expected to do that themselves as needed. + node_type* const pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); + EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + // Leave pNode->mValue uninitialized. + pNode->mpNext = NULL; + return pNode; + } + + + template + void hashtable::free_uninitialized_node(node_type* pNode) + { + // pNode->mValue is expected to be uninitialized. + EASTLFree(mAllocator, pNode, sizeof(node_type)); + } + + + template + eastl::pair::iterator, bool> + hashtable::DoInsertKey(true_type, const key_type& key, const hash_code_t c) // true_type means bUniqueKeys is true. + { + size_type n = (size_type)bucket_index(key, c, (uint32_t)mnBucketCount); + node_type* const pNode = DoFindNode(mpBucketArray[n], key, c); + + if(pNode == NULL) + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + // Allocate the new node before doing the rehash so that we don't + // do a rehash if the allocation throws. + node_type* const pNodeNew = DoAllocateNodeFromKey(key); + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + if(bRehash.first) + { + n = (size_type)bucket_index(key, c, (uint32_t)bRehash.second); + DoRehash(bRehash.second); + } + + EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + ++mnElementCount; + + return eastl::pair(iterator(pNodeNew, mpBucketArray + n), true); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeNode(pNodeNew); + throw; + } + #endif + } + + return eastl::pair(iterator(pNode, mpBucketArray + n), false); + } + + + + template + typename hashtable::iterator + hashtable::DoInsertKey(false_type, const key_type& key, const hash_code_t c) // false_type means bUniqueKeys is false. + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + if(bRehash.first) + DoRehash(bRehash.second); + + const size_type n = (size_type)bucket_index(key, c, (uint32_t)mnBucketCount); + + node_type* const pNodeNew = DoAllocateNodeFromKey(key); + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + // To consider: Possibly make this insertion not make equal elements contiguous. + // As it stands now, we insert equal values contiguously in the hashtable. + // The benefit is that equal_range can work in a sensible manner and that + // erase(value) can more quickly find equal values. The downside is that + // this insertion operation taking some extra time. How important is it to + // us that equal_range span all equal items? + node_type* const pNodePrev = DoFindNode(mpBucketArray[n], key, c); + + if(pNodePrev == NULL) + { + EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + } + else + { + pNodeNew->mpNext = pNodePrev->mpNext; + pNodePrev->mpNext = pNodeNew; + } + + ++mnElementCount; + + return iterator(pNodeNew, mpBucketArray + n); + } + + + template + eastl::pair::iterator, bool> + hashtable::DoInsertKey(true_type, key_type&& key, const hash_code_t c) // true_type means bUniqueKeys is true. + { + size_type n = (size_type)bucket_index(key, c, (uint32_t)mnBucketCount); + node_type* const pNode = DoFindNode(mpBucketArray[n], key, c); + + if(pNode == NULL) + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + // Allocate the new node before doing the rehash so that we don't + // do a rehash if the allocation throws. + node_type* const pNodeNew = DoAllocateNodeFromKey(eastl::move(key)); + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + if(bRehash.first) + { + n = (size_type)bucket_index(key, c, (uint32_t)bRehash.second); + DoRehash(bRehash.second); + } + + EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + ++mnElementCount; + + return eastl::pair(iterator(pNodeNew, mpBucketArray + n), true); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeNode(pNodeNew); + throw; + } + #endif + } + + return eastl::pair(iterator(pNode, mpBucketArray + n), false); + } + + + template + typename hashtable::iterator + hashtable::DoInsertKey(false_type, key_type&& key, const hash_code_t c) // false_type means bUniqueKeys is false. + { + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, (uint32_t)1); + + if(bRehash.first) + DoRehash(bRehash.second); + + const size_type n = (size_type)bucket_index(key, c, (uint32_t)mnBucketCount); + + node_type* const pNodeNew = DoAllocateNodeFromKey(eastl::move(key)); + set_code(pNodeNew, c); // This is a no-op for most hashtables. + + // To consider: Possibly make this insertion not make equal elements contiguous. + // As it stands now, we insert equal values contiguously in the hashtable. + // The benefit is that equal_range can work in a sensible manner and that + // erase(value) can more quickly find equal values. The downside is that + // this insertion operation taking some extra time. How important is it to + // us that equal_range span all equal items? + node_type* const pNodePrev = DoFindNode(mpBucketArray[n], key, c); + + if(pNodePrev == NULL) + { + EASTL_ASSERT((void**)mpBucketArray != &gpEmptyBucketArray[0]); + pNodeNew->mpNext = mpBucketArray[n]; + mpBucketArray[n] = pNodeNew; + } + else + { + pNodeNew->mpNext = pNodePrev->mpNext; + pNodePrev->mpNext = pNodeNew; + } + + ++mnElementCount; + + return iterator(pNodeNew, mpBucketArray + n); + } + + + template + template + typename hashtable::insert_return_type + hashtable::emplace(Args&&... args) + { + return DoInsertValue(has_unique_keys_type(), eastl::forward(args)...); // Need to use forward instead of move because Args&& is a "universal reference" instead of an rvalue reference. + } + + template + template + typename hashtable::iterator + hashtable::emplace_hint(const_iterator, Args&&... args) + { + // We currently ignore the iterator argument as a hint. + insert_return_type result = DoInsertValue(has_unique_keys_type(), eastl::forward(args)...); + return DoGetResultIterator(has_unique_keys_type(), result); + } + + template + template + // inline eastl::pair::iterator, bool> + inline typename hashtable::insert_return_type + hashtable::try_emplace(const key_type& key, Args&&... args) + { + return DoInsertValue(has_unique_keys_type(), piecewise_construct, eastl::forward_as_tuple(key), + eastl::forward_as_tuple(eastl::forward(args)...)); + } + + template + template + // inline eastl::pair::iterator, bool> + inline typename hashtable::insert_return_type + hashtable::try_emplace(key_type&& key, Args&&... args) + { + return DoInsertValue(has_unique_keys_type(), piecewise_construct, eastl::forward_as_tuple(eastl::move(key)), + eastl::forward_as_tuple(eastl::forward(args)...)); + } + + template + template + inline typename hashtable::iterator + hashtable::try_emplace(const_iterator, const key_type& key, Args&&... args) + { + insert_return_type result = DoInsertValue( + has_unique_keys_type(), + value_type(piecewise_construct, eastl::forward_as_tuple(key), eastl::forward_as_tuple(eastl::forward(args)...))); + + return DoGetResultIterator(has_unique_keys_type(), result); + } + + template + template + inline typename hashtable::iterator + hashtable::try_emplace(const_iterator, key_type&& key, Args&&... args) + { + insert_return_type result = + DoInsertValue(has_unique_keys_type(), value_type(piecewise_construct, eastl::forward_as_tuple(eastl::move(key)), + eastl::forward_as_tuple(eastl::forward(args)...))); + + return DoGetResultIterator(has_unique_keys_type(), result); + } + + template + typename hashtable::insert_return_type + hashtable::insert(value_type&& otherValue) + { + return DoInsertValue(has_unique_keys_type(), eastl::move(otherValue)); + } + + + template + template + typename hashtable::insert_return_type + hashtable::insert(hash_code_t c, node_type* pNodeNew, P&& otherValue) + { + // pNodeNew->mValue is expected to be uninitialized. + value_type value(eastl::forward

(otherValue)); // Need to use forward instead of move because P&& is a "universal reference" instead of an rvalue reference. + const key_type& k = mExtractKey(value); + return DoInsertValueExtra(has_unique_keys_type(), k, c, pNodeNew, eastl::move(value)); + } + + + template + typename hashtable::iterator + hashtable::insert(const_iterator, value_type&& value) + { + // We currently ignore the iterator argument as a hint. + insert_return_type result = DoInsertValue(has_unique_keys_type(), value_type(eastl::move(value))); + return DoGetResultIterator(has_unique_keys_type(), result); + } + + + template + typename hashtable::insert_return_type + hashtable::insert(const value_type& value) + { + return DoInsertValue(has_unique_keys_type(), value); + } + + + template + typename hashtable::insert_return_type + hashtable::insert(hash_code_t c, node_type* pNodeNew, const value_type& value) + { + // pNodeNew->mValue is expected to be uninitialized. + const key_type& k = mExtractKey(value); + return DoInsertValueExtra(has_unique_keys_type(), k, c, pNodeNew, value); + } + + + template + template + typename hashtable::insert_return_type + hashtable::insert(P&& otherValue) + { + return emplace(eastl::forward

(otherValue)); + } + + + template + typename hashtable::iterator + hashtable::insert(const_iterator, const value_type& value) + { + // We ignore the first argument (hint iterator). It's not likely to be useful for hashtable containers. + insert_return_type result = DoInsertValue(has_unique_keys_type(), value); + return DoGetResultIterator(has_unique_keys_type(), result); + } + + + template + void hashtable::insert(std::initializer_list ilist) + { + insert(ilist.begin(), ilist.end()); + } + + + template + template + void + hashtable::insert(InputIterator first, InputIterator last) + { + const uint32_t nElementAdd = (uint32_t)eastl::ht_distance(first, last); + const eastl::pair bRehash = mRehashPolicy.GetRehashRequired((uint32_t)mnBucketCount, (uint32_t)mnElementCount, nElementAdd); + + if(bRehash.first) + DoRehash(bRehash.second); + + for(; first != last; ++first) + DoInsertValue(has_unique_keys_type(), *first); + } + + + template + template + eastl::pair::iterator, bool> + hashtable::insert_or_assign(const key_type& k, M&& obj) + { + auto iter = find(k); + if(iter == end()) + { + return insert(value_type(piecewise_construct, eastl::forward_as_tuple(k), eastl::forward_as_tuple(eastl::forward(obj)))); + } + else + { + iter->second = eastl::forward(obj); + return {iter, false}; + } + } + + template + template + eastl::pair::iterator, bool> + hashtable::insert_or_assign(key_type&& k, M&& obj) + { + auto iter = find(k); + if(iter == end()) + { + return insert(value_type(piecewise_construct, eastl::forward_as_tuple(eastl::move(k)), eastl::forward_as_tuple(eastl::forward(obj)))); + } + else + { + iter->second = eastl::forward(obj); + return {iter, false}; + } + } + + template + template + typename hashtable::iterator + hashtable::insert_or_assign(const_iterator, const key_type& k, M&& obj) + { + return insert_or_assign(k, eastl::forward(obj)).first; // we ignore the iterator hint + } + + template + template + typename hashtable::iterator + hashtable::insert_or_assign(const_iterator, key_type&& k, M&& obj) + { + return insert_or_assign(eastl::move(k), eastl::forward(obj)).first; // we ignore the iterator hint + } + + + template + typename hashtable::iterator + hashtable::erase(const_iterator i) + { + iterator iNext(i.mpNode, i.mpBucket); // Convert from const_iterator to iterator while constructing. + ++iNext; + + node_type* pNode = i.mpNode; + node_type* pNodeCurrent = *i.mpBucket; + + if(pNodeCurrent == pNode) + *i.mpBucket = pNodeCurrent->mpNext; + else + { + // We have a singly-linked list, so we have no choice but to + // walk down it till we find the node before the node at 'i'. + node_type* pNodeNext = pNodeCurrent->mpNext; + + while(pNodeNext != pNode) + { + pNodeCurrent = pNodeNext; + pNodeNext = pNodeCurrent->mpNext; + } + + pNodeCurrent->mpNext = pNodeNext->mpNext; + } + + DoFreeNode(pNode); + --mnElementCount; + + return iNext; + } + + + + template + inline typename hashtable::iterator + hashtable::erase(const_iterator first, const_iterator last) + { + while(first != last) + first = erase(first); + return iterator(first.mpNode, first.mpBucket); + } + + + + template + typename hashtable::size_type + hashtable::erase(const key_type& k) + { + // To do: Reimplement this function to do a single loop and not try to be + // smart about element contiguity. The mechanism here is only a benefit if the + // buckets are heavily overloaded; otherwise this mechanism may be slightly slower. + + const hash_code_t c = get_hash_code(k); + const size_type n = (size_type)bucket_index(k, c, (uint32_t)mnBucketCount); + const size_type nElementCountSaved = mnElementCount; + + node_type** pBucketArray = mpBucketArray + n; + + while(*pBucketArray && !compare(k, c, *pBucketArray)) + pBucketArray = &(*pBucketArray)->mpNext; + + while(*pBucketArray && compare(k, c, *pBucketArray)) + { + node_type* const pNode = *pBucketArray; + *pBucketArray = pNode->mpNext; + DoFreeNode(pNode); + --mnElementCount; + } + + return nElementCountSaved - mnElementCount; + } + + + + template + inline void hashtable::clear() + { + DoFreeNodes(mpBucketArray, mnBucketCount); + mnElementCount = 0; + } + + + + template + inline void hashtable::clear(bool clearBuckets) + { + DoFreeNodes(mpBucketArray, mnBucketCount); + if(clearBuckets) + { + DoFreeBuckets(mpBucketArray, mnBucketCount); + reset_lose_memory(); + } + mnElementCount = 0; + } + + + + template + inline void hashtable::reset_lose_memory() EA_NOEXCEPT + { + // 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. + mnBucketCount = 1; + + #ifdef _MSC_VER + mpBucketArray = (node_type**)&gpEmptyBucketArray[0]; + #else + void* p = &gpEmptyBucketArray[0]; + memcpy(&mpBucketArray, &p, sizeof(mpBucketArray)); // Other compilers implement strict aliasing and casting is thus unsafe. + #endif + + mnElementCount = 0; + mRehashPolicy.mnNextResize = 0; + } + + + template + inline void hashtable::reserve(size_type nElementCount) + { + rehash(mRehashPolicy.GetBucketCount(uint32_t(nElementCount))); + } + + + + template + inline void hashtable::rehash(size_type nBucketCount) + { + // Note that we unilaterally use the passed in bucket count; we do not attempt migrate it + // up to the next prime number. We leave it at the user's discretion to do such a thing. + DoRehash(nBucketCount); + } + + + + template + void hashtable::DoRehash(size_type nNewBucketCount) + { + node_type** const pBucketArray = DoAllocateBuckets(nNewBucketCount); // nNewBucketCount should always be >= 2. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + node_type* pNode; + + for(size_type i = 0; i < mnBucketCount; ++i) + { + while((pNode = mpBucketArray[i]) != NULL) // Using '!=' disables compiler warnings. + { + const size_type nNewBucketIndex = (size_type)bucket_index(pNode, (uint32_t)nNewBucketCount); + + mpBucketArray[i] = pNode->mpNext; + pNode->mpNext = pBucketArray[nNewBucketIndex]; + pBucketArray[nNewBucketIndex] = pNode; + } + } + + DoFreeBuckets(mpBucketArray, mnBucketCount); + mnBucketCount = nNewBucketCount; + mpBucketArray = pBucketArray; + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + // A failure here means that a hash function threw an exception. + // We can't restore the previous state without calling the hash + // function again, so the only sensible recovery is to delete everything. + DoFreeNodes(pBucketArray, nNewBucketCount); + DoFreeBuckets(pBucketArray, nNewBucketCount); + DoFreeNodes(mpBucketArray, mnBucketCount); + mnElementCount = 0; + throw; + } + #endif + } + + + template + inline bool hashtable::validate() const + { + // Verify our empty bucket array is unmodified. + if(gpEmptyBucketArray[0] != NULL) + return false; + + if(gpEmptyBucketArray[1] != (void*)uintptr_t(~0)) + return false; + + // Verify that we have at least one bucket. Calculations can + // trigger division by zero exceptions otherwise. + if(mnBucketCount == 0) + return false; + + // Verify that gpEmptyBucketArray is used correctly. + // gpEmptyBucketArray is only used when initially empty. + if((void**)mpBucketArray == &gpEmptyBucketArray[0]) + { + if(mnElementCount) // gpEmptyBucketArray is used only for empty hash tables. + return false; + + if(mnBucketCount != 1) // gpEmptyBucketArray is used exactly an only for mnBucketCount == 1. + return false; + } + else + { + if(mnBucketCount < 2) // Small bucket counts *must* use gpEmptyBucketArray. + return false; + } + + // Verify that the element count matches mnElementCount. + size_type nElementCount = 0; + + for(const_iterator temp = begin(), tempEnd = end(); temp != tempEnd; ++temp) + ++nElementCount; + + if(nElementCount != mnElementCount) + return false; + + // To do: Verify that individual elements are in the expected buckets. + + return true; + } + + + template + int hashtable::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 + /////////////////////////////////////////////////////////////////////// + + // operator==, != have been moved to the specific container subclasses (e.g. hash_map). + + // The following comparison operators are deprecated and will likely be removed in a + // future version of this package. + // + // Comparing hash tables for less-ness is an odd thing to do. We provide it for + // completeness, though the user is advised to be wary of how they use this. + // + template + inline bool operator<(const hashtable& a, + const hashtable& b) + { + // This requires hash table elements to support operator<. Since the hash table + // doesn't compare elements via less (it does so via equals), we must use the + // globally defined operator less for the elements. + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + + template + inline bool operator>(const hashtable& a, + const hashtable& b) + { + return b < a; + } + + + template + inline bool operator<=(const hashtable& a, + const hashtable& b) + { + return !(b < a); + } + + + template + inline bool operator>=(const hashtable& a, + const hashtable& b) + { + return !(a < b); + } + + + template + inline void swap(const hashtable& a, + const hashtable& b) + { + a.swap(b); + } + + +} // namespace eastl + + +EA_RESTORE_VC_WARNING(); + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/internal/in_place_t.h b/lib/EASTL/include/EASTL/internal/in_place_t.h new file mode 100644 index 000000000..79acd1842 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/in_place_t.h @@ -0,0 +1,82 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_IN_PLACE_T_H +#define EASTL_INTERNAL_IN_PLACE_T_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +namespace eastl +{ + namespace Internal + { + struct in_place_tag {}; + template struct in_place_type_tag {}; + template struct in_place_index_tag {}; + } + + /////////////////////////////////////////////////////////////////////////////// + /// in_place_tag + /// + /// http://en.cppreference.com/w/cpp/utility/in_place_tag + /// + struct in_place_tag + { + in_place_tag() = delete; + + private: + explicit in_place_tag(Internal::in_place_tag) {} + friend inline in_place_tag Internal_ConstructInPlaceTag(); + }; + + // internal factory function for in_place_tag + inline in_place_tag Internal_ConstructInPlaceTag() { return in_place_tag(Internal::in_place_tag{}); } + + + /////////////////////////////////////////////////////////////////////////////// + /// in_place_t / in_place_type_t / in_place_index_t + /// + /// used to disambiguate overloads that take arguments (possibly a parameter + /// pack) for in-place construction of some value. + /// + /// http://en.cppreference.com/w/cpp/utility/optional/in_place_t + /// + using in_place_t = in_place_tag(&)(Internal::in_place_tag); + + template + using in_place_type_t = in_place_tag(&)(Internal::in_place_type_tag); + + template + using in_place_index_t = in_place_tag(&)(Internal::in_place_index_tag); + + + /////////////////////////////////////////////////////////////////////////////// + /// in_place / in_place / in_place + /// + /// http://en.cppreference.com/w/cpp/utility/in_place + /// + inline in_place_tag in_place(Internal::in_place_tag) { return Internal_ConstructInPlaceTag(); } + + template + inline in_place_tag in_place(Internal::in_place_type_tag) { return Internal_ConstructInPlaceTag(); } + + template + inline in_place_tag in_place(Internal::in_place_index_tag) { return Internal_ConstructInPlaceTag(); } + + +} // namespace eastl + + +#endif // Header include guard + + + + + + diff --git a/lib/EASTL/include/EASTL/internal/integer_sequence.h b/lib/EASTL/include/EASTL/internal/integer_sequence.h new file mode 100644 index 000000000..2a5539dd0 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/integer_sequence.h @@ -0,0 +1,104 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_INTEGER_SEQUENCE_H +#define EASTL_INTEGER_SEQUENCE_H + +#include +#include +#include + +namespace eastl +{ + +#if EASTL_VARIADIC_TEMPLATES_ENABLED && !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + +// integer_sequence +template +class integer_sequence +{ +public: + typedef T value_type; + static_assert(is_integral::value, "eastl::integer_sequence can only be instantiated with an integral type"); + static EA_CONSTEXPR size_t size() EA_NOEXCEPT { return sizeof...(Ints); } +}; + +template +struct make_index_sequence_impl; + +template +struct make_index_sequence_impl> +{ + typedef typename make_index_sequence_impl>::type type; +}; + +template +struct make_index_sequence_impl<0, integer_sequence> +{ + typedef integer_sequence type; +}; + +template +using index_sequence = integer_sequence; + +template +using make_index_sequence = typename make_index_sequence_impl>::type; + +template +struct integer_sequence_convert_impl; + +template +struct integer_sequence_convert_impl> +{ + typedef integer_sequence type; +}; + +template +struct make_integer_sequence_impl +{ + typedef typename integer_sequence_convert_impl>::type type; +}; + +template +using make_integer_sequence = typename make_integer_sequence_impl::type; + +// Helper alias template that converts any type parameter pack into an index sequence of the same length +template +using index_sequence_for = make_index_sequence; + +namespace internal +{ + +template +struct integer_sequence_size_helper; + +template +struct integer_sequence_size_helper> : public integral_constant +{ +}; + +template +struct integer_sequence_size : public integer_sequence_size_helper> +{ +}; + +template +struct index_sequence_size : public integer_sequence_size_helper> +{ +}; + +template +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR size_t integer_sequence_size_v = integer_sequence_size::value; + +template +EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR size_t index_sequence_size_v = index_sequence_size::value; + + +} // namespace internal + +#endif // EASTL_VARIADIC_TEMPLATES_ENABLED + +} // namespace eastl + +#endif // EASTL_INTEGER_SEQUENCE_H diff --git a/lib/EASTL/include/EASTL/internal/intrusive_hashtable.h b/lib/EASTL/include/EASTL/internal/intrusive_hashtable.h new file mode 100644 index 000000000..dccca5b1d --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/intrusive_hashtable.h @@ -0,0 +1,989 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This file implements an intrusive hash table, which is a hash table whereby +// the container nodes are the hash table objects themselves. This has benefits +// primarily in terms of memory management. There are some minor limitations +// that result from this. +// +/////////////////////////////////////////////////////////////////////////////// + + + +#ifndef EASTL_INTERNAL_INTRUSIVE_HASHTABLE_H +#define EASTL_INTERNAL_INTRUSIVE_HASHTABLE_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS(); +#include +#include +#include +EA_RESTORE_ALL_VC_WARNINGS(); + + +namespace eastl +{ + + /// intrusive_hash_node + /// + /// A hash_node stores an element in a hash table, much like a + /// linked list node stores an element in a linked list. + /// An intrusive_hash_node additionally can, via template parameter, + /// store a hash code in the node to speed up hash calculations + /// and comparisons in some cases. + /// + /// To consider: Make a version of intrusive_hash_node which is + /// templated on the container type. This would allow for the + /// mpNext pointer to be the container itself and thus allow + /// for easier debugging. + /// + /// Example usage: + /// struct Widget : public intrusive_hash_node{ ... }; + /// + /// struct Dagget : public intrusive_hash_node_key{ ... }; + /// + struct intrusive_hash_node + { + intrusive_hash_node* mpNext; + }; + + + template + struct intrusive_hash_node_key : public intrusive_hash_node + { + typedef Key key_type; + Key mKey; + }; + + + + /// intrusive_node_iterator + /// + /// Node iterators iterate nodes within a given bucket. + /// + /// The bConst parameter defines if the iterator is a const_iterator + /// or an iterator. + /// + template + struct intrusive_node_iterator + { + public: + typedef intrusive_node_iterator this_type; + typedef Value value_type; + typedef Value node_type; + typedef ptrdiff_t difference_type; + typedef typename type_select::type pointer; + typedef typename type_select::type reference; + typedef EASTL_ITC_NS::forward_iterator_tag iterator_category; + + public: + node_type* mpNode; + + public: + intrusive_node_iterator() + : mpNode(NULL) { } + + explicit intrusive_node_iterator(value_type* pNode) + : mpNode(pNode) { } + + intrusive_node_iterator(const intrusive_node_iterator& x) + : mpNode(x.mpNode) { } + + reference operator*() const + { return *mpNode; } + + pointer operator->() const + { return mpNode; } + + this_type& operator++() + { mpNode = static_cast(mpNode->mpNext); return *this; } + + this_type operator++(int) + { this_type temp(*this); mpNode = static_cast(mpNode->mpNext); return temp; } + + }; // intrusive_node_iterator + + + + + /// intrusive_hashtable_iterator_base + /// + /// An intrusive_hashtable_iterator_base iterates the entire hash table and + /// not just nodes within a single bucket. Users in general will use a hash + /// table iterator much more often, as it is much like other container + /// iterators (e.g. vector::iterator). + /// + /// We define a base class here because it is shared by both const and + /// non-const iterators. + /// + template + struct intrusive_hashtable_iterator_base + { + public: + typedef Value value_type; + + protected: + template + friend class intrusive_hashtable; + + template + friend struct intrusive_hashtable_iterator; + + template + friend bool operator==(const intrusive_hashtable_iterator_base&, const intrusive_hashtable_iterator_base&); + + template + friend bool operator!=(const intrusive_hashtable_iterator_base&, const intrusive_hashtable_iterator_base&); + + value_type* mpNode; // Current node within current bucket. + value_type** mpBucket; // Current bucket. + + public: + intrusive_hashtable_iterator_base(value_type* pNode, value_type** pBucket) + : mpNode(pNode), mpBucket(pBucket) { } + + void increment_bucket() + { + ++mpBucket; + while(*mpBucket == NULL) // We store an extra bucket with some non-NULL value at the end + ++mpBucket; // of the bucket array so that finding the end of the bucket + mpNode = *mpBucket; // array is quick and simple. + } + + void increment() + { + mpNode = static_cast(mpNode->mpNext); + + while(mpNode == NULL) + mpNode = *++mpBucket; + } + + }; // intrusive_hashtable_iterator_base + + + + + /// intrusive_hashtable_iterator + /// + /// An intrusive_hashtable_iterator iterates the entire hash table and not + /// just nodes within a single bucket. Users in general will use a hash + /// table iterator much more often, as it is much like other container + /// iterators (e.g. vector::iterator). + /// + /// The bConst parameter defines if the iterator is a const_iterator + /// or an iterator. + /// + template + struct intrusive_hashtable_iterator : public intrusive_hashtable_iterator_base + { + public: + typedef intrusive_hashtable_iterator_base base_type; + typedef intrusive_hashtable_iterator this_type; + typedef intrusive_hashtable_iterator this_type_non_const; + typedef typename base_type::value_type value_type; + typedef typename type_select::type pointer; + typedef typename type_select::type reference; + typedef ptrdiff_t difference_type; + typedef EASTL_ITC_NS::forward_iterator_tag iterator_category; + + public: + intrusive_hashtable_iterator() + : base_type(NULL, NULL) { } + + explicit intrusive_hashtable_iterator(value_type* pNode, value_type** pBucket) + : base_type(pNode, pBucket) { } + + explicit intrusive_hashtable_iterator(value_type** pBucket) + : base_type(*pBucket, pBucket) { } + + intrusive_hashtable_iterator(const this_type_non_const& x) + : base_type(x.mpNode, x.mpBucket) { } + + reference operator*() const + { return *base_type::mpNode; } + + pointer operator->() const + { return base_type::mpNode; } + + this_type& operator++() + { base_type::increment(); return *this; } + + this_type operator++(int) + { this_type temp(*this); base_type::increment(); return temp; } + + }; // intrusive_hashtable_iterator + + + + /// use_intrusive_key + /// + /// operator()(x) returns x.mKey. Used in maps, as opposed to sets. + /// This is a template policy implementation; it is an alternative to + /// the use_self template implementation, which is used for sets. + /// + template + struct use_intrusive_key // : public unary_function // Perhaps we want to make it a subclass of unary_function. + { + typedef Key result_type; + + const result_type& operator()(const Node& x) const + { return x.mKey; } + }; + + + + /////////////////////////////////////////////////////////////////////////// + /// intrusive_hashtable + /// + template + class intrusive_hashtable + { + public: + typedef intrusive_hashtable this_type; + typedef Key key_type; + typedef Value value_type; + typedef Value mapped_type; + typedef Value node_type; + typedef uint32_t hash_code_t; + typedef Equal key_equal; + typedef ptrdiff_t difference_type; + typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t. + typedef value_type& reference; + typedef const value_type& const_reference; + typedef intrusive_node_iterator local_iterator; + typedef intrusive_node_iterator const_local_iterator; + typedef intrusive_hashtable_iterator iterator; + typedef intrusive_hashtable_iterator const_iterator; + typedef typename type_select, iterator>::type insert_return_type; + typedef typename type_select, + eastl::use_intrusive_key >::type extract_key; + + enum + { + kBucketCount = bucketCount + }; + + protected: + node_type* mBucketArray[kBucketCount + 1]; // '+1' because we have an end bucket which is non-NULL so iterators always stop on it. + size_type mnElementCount; + Hash mHash; // To do: Use base class optimization to make this go away when it is of zero size. + Equal mEqual; // To do: Use base class optimization to make this go away when it is of zero size. + + public: + intrusive_hashtable(const Hash&, const Equal&); + + void swap(this_type& x); + + iterator begin() EA_NOEXCEPT + { + iterator i(mBucketArray); + if(!i.mpNode) + i.increment_bucket(); + return i; + } + + const_iterator begin() const EA_NOEXCEPT + { + const_iterator i(const_cast(mBucketArray)); + if(!i.mpNode) + i.increment_bucket(); + return i; + } + + const_iterator cbegin() const EA_NOEXCEPT + { + return begin(); + } + + iterator end() EA_NOEXCEPT + { return iterator(mBucketArray + kBucketCount); } + + const_iterator end() const EA_NOEXCEPT + { return const_iterator(const_cast(mBucketArray) + kBucketCount); } + + const_iterator cend() const EA_NOEXCEPT + { return const_iterator(const_cast(mBucketArray) + kBucketCount); } + + local_iterator begin(size_type n) EA_NOEXCEPT + { return local_iterator(mBucketArray[n]); } + + const_local_iterator begin(size_type n) const EA_NOEXCEPT + { return const_local_iterator(mBucketArray[n]); } + + const_local_iterator cbegin(size_type n) const EA_NOEXCEPT + { return const_local_iterator(mBucketArray[n]); } + + local_iterator end(size_type) EA_NOEXCEPT + { return local_iterator(NULL); } + + const_local_iterator end(size_type) const EA_NOEXCEPT + { return const_local_iterator(NULL); } + + const_local_iterator cend(size_type) const EA_NOEXCEPT + { return const_local_iterator(NULL); } + + size_type size() const EA_NOEXCEPT + { return mnElementCount; } + + bool empty() const EA_NOEXCEPT + { return mnElementCount == 0; } + + size_type bucket_count() const EA_NOEXCEPT // This function is unnecessary, as the user can directly reference + { return kBucketCount; } // intrusive_hashtable::kBucketCount as a constant. + + size_type bucket_size(size_type n) const EA_NOEXCEPT + { return (size_type)eastl::distance(begin(n), end(n)); } + + size_type bucket(const key_type& k) const EA_NOEXCEPT + { return (size_type)(mHash(k) % kBucketCount); } + + public: + float load_factor() const EA_NOEXCEPT + { return (float)mnElementCount / (float)kBucketCount; } + + public: + insert_return_type insert(value_type& value) + { return DoInsertValue(value, integral_constant()); } + + insert_return_type insert(const_iterator, value_type& value) + { return insert(value); } // To consider: We might be able to use the iterator argument to specify a specific insertion location. + + template + void insert(InputIterator first, InputIterator last); + + public: + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + size_type erase(const key_type& k); + iterator remove(value_type& value); // Removes by value instead of by iterator. This is an O(1) operation, due to this hashtable being 'intrusive'. + + void clear(); + + public: + iterator find(const key_type& k); + const_iterator find(const key_type& k) const; + + /// Implements a find whereby the user supplies a comparison of a different type + /// than the hashtable value_type. A useful case of this is one whereby you have + /// a container of string objects but want to do searches via passing in char pointers. + /// The problem is that without this kind of find, you need to do the expensive operation + /// of converting the char pointer to a string so it can be used as the argument to the + /// find function. + /// + /// Example usage: + /// hash_set hashSet; + /// hashSet.find_as("hello"); // Use default hash and compare. + /// + /// Example usage (namespaces omitted for brevity): + /// hash_set hashSet; + /// hashSet.find_as("hello", hash(), equal_to_2()); + /// + template + iterator find_as(const U& u, UHash uhash, BinaryPredicate predicate); + + template + const_iterator find_as(const U& u, UHash uhash, BinaryPredicate predicate) const; + + template + iterator find_as(const U& u); + + template + const_iterator find_as(const U& u) const; + + size_type count(const key_type& k) const; + + // The use for equal_range in a hash_table seems somewhat questionable. + // The primary reason for its existence is to replicate the interface of set/map. + eastl::pair equal_range(const key_type& k); + eastl::pair equal_range(const key_type& k) const; + + public: + bool validate() const; + int validate_iterator(const_iterator i) const; + + public: + Hash hash_function() const + { return mHash; } + + Equal equal_function() const // Deprecated. Use key_eq() instead, as key_eq is what the new C++ standard + { return mEqual; } // has specified in its hashtable (unordered_*) proposal. + + const key_equal& key_eq() const + { return mEqual; } + + key_equal& key_eq() + { return mEqual; } + + protected: + eastl::pair DoInsertValue(value_type&, true_type); // true_type means bUniqueKeys is true. + iterator DoInsertValue(value_type&, false_type); // false_type means bUniqueKeys is false. + + node_type* DoFindNode(node_type* pNode, const key_type& k) const; + + template + node_type* DoFindNode(node_type* pNode, const U& u, BinaryPredicate predicate) const; + + }; // class intrusive_hashtable + + + + + + /////////////////////////////////////////////////////////////////////// + // node_iterator_base + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const intrusive_node_iterator& a, + const intrusive_node_iterator& b) + { return a.mpNode == b.mpNode; } + + template + inline bool operator!=(const intrusive_node_iterator& a, + const intrusive_node_iterator& b) + { return a.mpNode != b.mpNode; } + + + + + /////////////////////////////////////////////////////////////////////// + // hashtable_iterator_base + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const intrusive_hashtable_iterator_base& a, + const intrusive_hashtable_iterator_base& b) + { return a.mpNode == b.mpNode; } + + + template + inline bool operator!=(const intrusive_hashtable_iterator_base& a, + const intrusive_hashtable_iterator_base& b) + { return a.mpNode != b.mpNode; } + + + + + /////////////////////////////////////////////////////////////////////// + // intrusive_hashtable + /////////////////////////////////////////////////////////////////////// + + template + inline intrusive_hashtable::intrusive_hashtable(const H& h, const Eq& eq) + : mnElementCount(0), + mHash(h), + mEqual(eq) + { + memset(mBucketArray, 0, kBucketCount * sizeof(mBucketArray[0])); + mBucketArray[kBucketCount] = reinterpret_cast((uintptr_t)~0); + } + + + template + void intrusive_hashtable::swap(this_type& x) + { + for(size_t i = 0; i < kBucketCount; i++) + eastl::swap(mBucketArray[i], x.mBucketArray[i]); + + eastl::swap(mnElementCount, x.mnElementCount); + eastl::swap(mHash, x.mHash); + eastl::swap(mEqual, x.mEqual); + } + + + template + inline typename intrusive_hashtable::iterator + intrusive_hashtable::find(const key_type& k) + { + const size_type n = (size_type)(mHash(k) % kBucketCount); + node_type* const pNode = DoFindNode(mBucketArray[n], k); + return pNode ? iterator(pNode, mBucketArray + n) : iterator(mBucketArray + kBucketCount); + } + + + template + inline typename intrusive_hashtable::const_iterator + intrusive_hashtable::find(const key_type& k) const + { + const size_type n = (size_type)(mHash(k) % kBucketCount); + node_type* const pNode = DoFindNode(mBucketArray[n], k); + return pNode ? const_iterator(pNode, const_cast(mBucketArray) + n) : const_iterator(const_cast(mBucketArray) + kBucketCount); + } + + + template + template + inline typename intrusive_hashtable::iterator + intrusive_hashtable::find_as(const U& other, UHash uhash, BinaryPredicate predicate) + { + const size_type n = (size_type)(uhash(other) % kBucketCount); + node_type* const pNode = DoFindNode(mBucketArray[n], other, predicate); + return pNode ? iterator(pNode, mBucketArray + n) : iterator(mBucketArray + kBucketCount); + } + + + template + template + inline typename intrusive_hashtable::const_iterator + intrusive_hashtable::find_as(const U& other, UHash uhash, BinaryPredicate predicate) const + { + const size_type n = (size_type)(uhash(other) % kBucketCount); + node_type* const pNode = DoFindNode(mBucketArray[n], other, predicate); + return pNode ? const_iterator(pNode, const_cast(mBucketArray) + n) : const_iterator(const_cast(mBucketArray) + kBucketCount); + } + + + /// intrusive_hashtable_find + /// + /// Helper function that defaults to using hash and equal_to_2. + /// This makes it so that by default you don't need to provide these. + /// Note that the default hash functions may not be what you want, though. + /// + /// Example usage. Instead of this: + /// hash_set hashSet; + /// hashSet.find("hello", hash(), equal_to_2()); + /// + /// You can use this: + /// hash_set hashSet; + /// hashtable_find(hashSet, "hello"); + /// + template + inline typename H::iterator intrusive_hashtable_find(H& hashTable, const U& u) + { return hashTable.find_as(u, eastl::hash(), eastl::equal_to_2()); } + + template + inline typename H::const_iterator intrusive_hashtable_find(const H& hashTable, const U& u) + { return hashTable.find_as(u, eastl::hash(), eastl::equal_to_2()); } + + + + template + template + inline typename intrusive_hashtable::iterator + intrusive_hashtable::find_as(const U& other) + { return eastl::intrusive_hashtable_find(*this, other); } + // VC++ doesn't appear to like the following, though it seems correct to me. + // So we implement the workaround above until we can straighten this out. + //{ return find_as(other, eastl::hash(), eastl::equal_to_2()); } + + + template + template + inline typename intrusive_hashtable::const_iterator + intrusive_hashtable::find_as(const U& other) const + { return eastl::intrusive_hashtable_find(*this, other); } + // VC++ doesn't appear to like the following, though it seems correct to me. + // So we implement the workaround above until we can straighten this out. + //{ return find_as(other, eastl::hash(), eastl::equal_to_2()); } + + + template + typename intrusive_hashtable::size_type + intrusive_hashtable::count(const key_type& k) const + { + const size_type n = (size_type)(mHash(k) % kBucketCount); + size_type result = 0; + extract_key extractKey; // extract_key is empty and thus this ctor is a no-op. + + // To do: Make a specialization for bU (unique keys) == true and take + // advantage of the fact that the count will always be zero or one in that case. + for(node_type* pNode = mBucketArray[n]; pNode; pNode = static_cast(pNode->mpNext)) + { + if(mEqual(k, extractKey(*pNode))) + ++result; + } + return result; + } + + + template + eastl::pair::iterator, + typename intrusive_hashtable::iterator> + intrusive_hashtable::equal_range(const key_type& k) + { + const size_type n = (size_type)(mHash(k) % kBucketCount); + node_type** head = mBucketArray + n; + node_type* pNode = DoFindNode(*head, k); + extract_key extractKey; // extract_key is empty and thus this ctor is a no-op. + + if(pNode) + { + node_type* p1 = static_cast(pNode->mpNext); + + for(; p1; p1 = static_cast(p1->mpNext)) + { + if(!mEqual(k, extractKey(*p1))) + break; + } + + iterator first(pNode, head); + iterator last(p1, head); + + if(!p1) + last.increment_bucket(); + + return eastl::pair(first, last); + } + + return eastl::pair(iterator(mBucketArray + kBucketCount), + iterator(mBucketArray + kBucketCount)); + } + + + + + template + eastl::pair::const_iterator, + typename intrusive_hashtable::const_iterator> + intrusive_hashtable::equal_range(const key_type& k) const + { + const size_type n = (size_type)(mHash(k) % kBucketCount); + node_type** head = const_cast(mBucketArray + n); + node_type* pNode = DoFindNode(*head, k); + extract_key extractKey; // extract_key is empty and thus this ctor is a no-op. + + if(pNode) + { + node_type* p1 = static_cast(pNode->mpNext); + + for(; p1; p1 = static_cast(p1->mpNext)) + { + if(!mEqual(k, extractKey(*p1))) + break; + } + + const_iterator first(pNode, head); + const_iterator last(p1, head); + + if(!p1) + last.increment_bucket(); + + return eastl::pair(first, last); + } + + return eastl::pair(const_iterator(const_cast(mBucketArray) + kBucketCount), + const_iterator(const_cast(mBucketArray) + kBucketCount)); + } + + + template + inline typename intrusive_hashtable::node_type* + intrusive_hashtable::DoFindNode(node_type* pNode, const key_type& k) const + { + extract_key extractKey; // extract_key is empty and thus this ctor is a no-op. + + for(; pNode; pNode = static_cast(pNode->mpNext)) + { + if(mEqual(k, extractKey(*pNode))) + return pNode; + } + return NULL; + } + + + template + template + inline typename intrusive_hashtable::node_type* + intrusive_hashtable::DoFindNode(node_type* pNode, const U& other, BinaryPredicate predicate) const + { + extract_key extractKey; // extract_key is empty and thus this ctor is a no-op. + + for(; pNode; pNode = static_cast(pNode->mpNext)) + { + if(predicate(extractKey(*pNode), other)) // Intentionally compare with key as first arg and other as second arg. + return pNode; + } + return NULL; + } + + + template + eastl::pair::iterator, bool> + intrusive_hashtable::DoInsertValue(value_type& value, true_type) // true_type means bUniqueKeys is true. + { + // For sets (as opposed to maps), one could argue that all insertions are successful, + // as all elements are unique. However, the equal function might not think so. + extract_key extractKey; // extract_key is empty and thus this ctor is a no-op. + const size_type n = (size_type)(mHash(extractKey(value)) % kBucketCount); + node_type* const pNode = DoFindNode(mBucketArray[n], extractKey(value)); + + if(pNode == NULL) + { + value.mpNext = mBucketArray[n]; + mBucketArray[n] = &value; + ++mnElementCount; + + return eastl::pair(iterator(&value, mBucketArray + n), true); + } + + return eastl::pair(iterator(pNode, mBucketArray + n), false); + } + + + template + typename intrusive_hashtable::iterator + intrusive_hashtable::DoInsertValue(value_type& value, false_type) // false_type means bUniqueKeys is false. + { + extract_key extractKey; // extract_key is empty and thus this ctor is a no-op. + const size_type n = (size_type)(mHash(extractKey(value)) % kBucketCount); + node_type* const pNodePrev = DoFindNode(mBucketArray[n], extractKey(value)); + + if(pNodePrev == NULL) + { + value.mpNext = mBucketArray[n]; + mBucketArray[n] = &value; + } + else + { + value.mpNext = pNodePrev->mpNext; + pNodePrev->mpNext = &value; + } + + ++mnElementCount; + + return iterator(&value, mBucketArray + n); + } + + + + template + template + inline void intrusive_hashtable::insert(InputIterator first, InputIterator last) + { + for(; first != last; ++first) + insert(*first); + } + + + template + typename intrusive_hashtable::iterator + intrusive_hashtable::erase(const_iterator i) + { + iterator iNext(i.mpNode, i.mpBucket); + ++iNext; + + node_type* pNode = i.mpNode; + node_type* pNodeCurrent = *i.mpBucket; + + if(pNodeCurrent == pNode) + *i.mpBucket = static_cast(pNodeCurrent->mpNext); + else + { + // We have a singly-linked list, so we have no choice but to + // walk down it till we find the node before the node at 'i'. + node_type* pNodeNext = static_cast(pNodeCurrent->mpNext); + + while(pNodeNext != pNode) + { + pNodeCurrent = pNodeNext; + pNodeNext = static_cast(pNodeCurrent->mpNext); + } + + pNodeCurrent->mpNext = static_cast(pNodeNext->mpNext); + } + + // To consider: In debug builds set the node mpNext to NULL. + --mnElementCount; + + return iNext; + } + + + template + inline typename intrusive_hashtable::iterator + intrusive_hashtable::erase(const_iterator first, const_iterator last) + { + while(first != last) + first = erase(first); + return iterator(first.mpNode, first.mpBucket); + } + + + template + typename intrusive_hashtable::size_type + intrusive_hashtable::erase(const key_type& k) + { + const size_type n = (size_type)(mHash(k) % kBucketCount); + const size_type nElementCountSaved = mnElementCount; + node_type*& pNodeBase = mBucketArray[n]; + extract_key extractKey; // extract_key is empty and thus this ctor is a no-op. + + // Note by Paul Pedriana: + // We have two loops here, and I'm not finding any easy way to having just one + // loop without changing the requirements of the hashtable node definition. + // It's a problem of taking an address of a variable and converting it to the + // address of another type without knowing what that type is. Perhaps I'm a + // little overly tired, so if there is a simple solution I am probably missing it. + + while(pNodeBase && mEqual(k, extractKey(*pNodeBase))) + { + pNodeBase = static_cast(pNodeBase->mpNext); + --mnElementCount; + } + + node_type* pNodePrev = pNodeBase; + + if(pNodePrev) + { + node_type* pNodeCur; + + while((pNodeCur = static_cast(pNodePrev->mpNext)) != NULL) + { + if(mEqual(k, extractKey(*pNodeCur))) + { + pNodePrev->mpNext = static_cast(pNodeCur->mpNext); + --mnElementCount; // To consider: In debug builds set the node mpNext to NULL. + } + else + pNodePrev = static_cast(pNodePrev->mpNext); + } + } + + return nElementCountSaved - mnElementCount; + } + + + template + inline typename intrusive_hashtable::iterator + intrusive_hashtable::remove(value_type& value) + { + extract_key extractKey; // extract_key is empty and thus this ctor is a no-op. + const size_type n = (size_type)(mHash(extractKey(value)) % kBucketCount); + + return erase(iterator(&value, &mBucketArray[n])); + } + + + template + inline void intrusive_hashtable::clear() + { + // To consider: In debug builds set the node mpNext to NULL. + memset(mBucketArray, 0, kBucketCount * sizeof(mBucketArray[0])); + mnElementCount = 0; + } + + + template + inline bool intrusive_hashtable::validate() const + { + // Verify that the element count matches mnElementCount. + size_type nElementCount = 0; + + for(const_iterator temp = begin(), tempEnd = end(); temp != tempEnd; ++temp) + ++nElementCount; + + if(nElementCount != mnElementCount) + return false; + + // To do: Verify that individual elements are in the expected buckets. + + return true; + } + + + template + int intrusive_hashtable::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 + inline bool operator==(const intrusive_hashtable& a, + const intrusive_hashtable& b) + { + return (a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin()); + } + + + template + inline bool operator!=(const intrusive_hashtable& a, + const intrusive_hashtable& b) + { + return !(a == b); + } + + + // Comparing hash tables for less-ness is an odd thing to do. We provide it for + // completeness, though the user is advised to be wary of how they use this. + template + inline bool operator<(const intrusive_hashtable& a, + const intrusive_hashtable& b) + { + // This requires hash table elements to support operator<. Since the hash table + // doesn't compare elements via less (it does so via equals), we must use the + // globally defined operator less for the elements. + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + + template + inline bool operator>(const intrusive_hashtable& a, + const intrusive_hashtable& b) + { + return b < a; + } + + + template + inline bool operator<=(const intrusive_hashtable& a, + const intrusive_hashtable& b) + { + return !(b < a); + } + + + template + inline bool operator>=(const intrusive_hashtable& a, + const intrusive_hashtable& b) + { + return !(a < b); + } + + + template + inline void swap(const intrusive_hashtable& a, + const intrusive_hashtable& b) + { + a.swap(b); + } + + +} // namespace eastl + + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/internal/mem_fn.h b/lib/EASTL/include/EASTL/internal/mem_fn.h new file mode 100644 index 000000000..1d3e7b3fd --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/mem_fn.h @@ -0,0 +1,304 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_MEM_FN_H +#define EASTL_INTERNAL_MEM_FN_H + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) +#pragma once +#endif + +//////////////////////////////////////////////////////////////////////////////// +// The code in this file is a modification of the libcxx implementation. We copy +// the license information here as required. +// +// We implement only enough of mem_fn to implement eastl::function. +//////////////////////////////////////////////////////////////////////////////// + +//===------------------------ functional ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +namespace eastl +{ + // + // apply_cv + // + template ::type>::value, + bool = is_volatile::type>::value> + struct apply_cv { typedef U type; }; + + template struct apply_cv { typedef const U type; }; + template struct apply_cv { typedef volatile U type; }; + template struct apply_cv { typedef const volatile U type; }; + template struct apply_cv { typedef U& type; }; + template struct apply_cv { typedef const U& type; }; + template struct apply_cv { typedef volatile U& type; }; + template struct apply_cv { typedef const volatile U& type; }; + + + + // + // has_result_type + // + template + struct has_result_type + { + private: + template + static eastl::no_type test(...); + + template + static eastl::yes_type test(typename U::result_type* = 0); + + public: + static const bool value = sizeof(test(0)) == sizeof(eastl::yes_type); + }; + + + + // + // derives_from_unary_function + // derives_from_binary_function + // + template + struct derives_from_unary_function + { + private: + static eastl::no_type test(...); + + template + static unary_function test(const volatile unary_function*); + + public: + static const bool value = !is_same::value; + typedef decltype(test((T*)0)) type; + }; + + template + struct derives_from_binary_function + { + private: + static eastl::no_type test(...); + template + static binary_function test(const volatile binary_function*); + + public: + static const bool value = !is_same::value; + typedef decltype(test((T*)0)) type; + }; + + + + // + // maybe_derives_from_unary_function + // maybe_derives_from_binary_function + // + template ::value> + struct maybe_derive_from_unary_function // bool is true + : public derives_from_unary_function::type { }; + + template + struct maybe_derive_from_unary_function { }; + + template ::value> + struct maybe_derive_from_binary_function // bool is true + : public derives_from_binary_function::type { }; + + template + struct maybe_derive_from_binary_function { }; + + + + // + // weak_result_type_imp + // + template ::value> + struct weak_result_type_imp // bool is true + : public maybe_derive_from_unary_function, + public maybe_derive_from_binary_function + { + typedef typename T::result_type result_type; + }; + + template + struct weak_result_type_imp : public maybe_derive_from_unary_function, + public maybe_derive_from_binary_function { }; + + + + // + // weak_result_type + // + template + struct weak_result_type : public weak_result_type_imp { }; + + // 0 argument case + template struct weak_result_type { typedef R result_type; }; + template struct weak_result_type { typedef R result_type; }; + template struct weak_result_type { typedef R result_type; }; + + // 1 argument case + template struct weak_result_type : public unary_function { }; + template struct weak_result_type : public unary_function { }; + template struct weak_result_type : public unary_function { }; + template struct weak_result_type : public unary_function { }; + template struct weak_result_type : public unary_function { }; + template struct weak_result_type : public unary_function { }; + template struct weak_result_type : public unary_function { }; + + // 2 argument case + template struct weak_result_type : public binary_function { }; + template struct weak_result_type : public binary_function { }; + template struct weak_result_type : public binary_function { }; + template struct weak_result_type : public binary_function { }; + template struct weak_result_type : public binary_function { }; + template struct weak_result_type : public binary_function { }; + template struct weak_result_type : public binary_function { }; + + // 3 or more arguments +#if EASTL_VARIADIC_TEMPLATES_ENABLED + template struct weak_result_type { typedef R result_type; }; + template struct weak_result_type { typedef R result_type; }; + template struct weak_result_type { typedef R result_type; }; + template struct weak_result_type { typedef R result_type; }; + template struct weak_result_type { typedef R result_type; }; + template struct weak_result_type { typedef R result_type; }; + template struct weak_result_type { typedef R result_type; }; +#endif + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // mem_fn_impl + // + template + class mem_fn_impl +#if defined(_MSC_VER) && (_MSC_VER >= 1900) // VS2015 or later + // Due to a (seemingly random) internal compiler error on VS2013 we disable eastl::unary_function and + // binary_function support for eastl::mem_fn as its not widely (if at all) used. If you require this support + // on VS2013 or below please contact us. + : public weak_result_type +#endif + { + public: + typedef T type; + + private: + type func; + + public: + EASTL_FORCE_INLINE mem_fn_impl(type _func) : func(_func) {} + +#if EASTL_VARIADIC_TEMPLATES_ENABLED + template + typename invoke_result::type operator()(ArgTypes&&... args) const + { + return invoke(func, eastl::forward(args)...); + } +#else + typename invoke_result::type operator()() const { return invoke_impl(func); } + + template + typename invoke_result0::type operator()(A0& a0) const + { + return invoke(func, a0); + } + + template + typename invoke_result1::type operator()(A0& a0, A1& a1) const + { + return invoke(func, a0, a1); + } + + template + typename invoke_result2::type operator()(A0& a0, A1& a1, A2& a2) const + { + return invoke(func, a0, a1, a2); + } +#endif + }; // mem_fn_impl + + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // mem_fn -> mem_fn_impl adapters + // + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R T::*pm) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)()) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0)) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0, A1)) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0, A1, A2)) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)() const) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0) const) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0, A1) const) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0, A1, A2) const) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)() volatile) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0) volatile) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0, A1) volatile) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0, A1, A2) volatile) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)() const volatile) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0) const volatile) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0, A1) const volatile) + { return mem_fn_impl(pm); } + + template + EASTL_FORCE_INLINE mem_fn_impl mem_fn(R (T::*pm)(A0, A1, A2) const volatile) + { return mem_fn_impl(pm); } + +} // namespace eastl + +#endif // EASTL_INTERNAL_MEM_FN_H diff --git a/lib/EASTL/include/EASTL/internal/memory_base.h b/lib/EASTL/include/EASTL/internal/memory_base.h new file mode 100644 index 000000000..b1c3490b1 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/memory_base.h @@ -0,0 +1,37 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_INTERNAL_MEMORY_BASE_H +#define EASTL_INTERNAL_MEMORY_BASE_H + +#include + +#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 + + +//////////////////////////////////////////////////////////////////////////////////////////// +// This file contains basic functionality found in the standard library 'memory' header that +// have limited or no dependencies. This allows us to utilize these utilize these functions +// in other EASTL code while avoid circular dependencies. +//////////////////////////////////////////////////////////////////////////////////////////// + +namespace eastl +{ + /// addressof + /// + /// From the C++11 Standard, section 20.6.12.1 + /// Returns the actual address of the object or function referenced by r, even in the presence of an overloaded operator&. + /// + template + T* addressof(T& value) EA_NOEXCEPT + { + return reinterpret_cast(&const_cast(reinterpret_cast(value))); + } + +} // namespace eastl + +#endif // EASTL_INTERNAL_MEMORY_BASE_H + diff --git a/lib/EASTL/include/EASTL/internal/move_help.h b/lib/EASTL/include/EASTL/internal/move_help.h new file mode 100644 index 000000000..97990df68 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/move_help.h @@ -0,0 +1,162 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_MOVE_HELP_H +#define EASTL_INTERNAL_MOVE_HELP_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include + + +// C++11's rvalue references aren't supported by earlier versions of C++. +// It turns out that in a number of cases under earlier C++ versions we can +// write code that uses rvalues similar to lvalues. We have macros below for +// such cases. For example, eastl::move (same as std::move) can be treated +// as a no-op under C++03, though with the consequence that move functionality +// isn't taken advantage of. + + +/// EASTL_MOVE +/// Acts like eastl::move when possible. Same as C++11 std::move. +/// +/// EASTL_MOVE_INLINE +/// Acts like eastl::move but is implemented inline instead of a function call. +/// This allows code to be faster in debug builds in particular. +/// Depends on C++ compiler decltype support or a similar extension. +/// +/// EASTL_FORWARD +/// Acts like eastl::forward when possible. Same as C++11 std::forward. +/// +/// EASTL_FORWARD_INLINE +/// Acts like eastl::forward but is implemented inline instead of a function call. +/// This allows code to be faster in debug builds in particular. +/// +#define EASTL_MOVE(x) eastl::move(x) +#if !defined(EA_COMPILER_NO_DECLTYPE) + #define EASTL_MOVE_INLINE(x) static_cast::type&&>(x) +#elif defined(__GNUC__) + #define EASTL_MOVE_INLINE(x) static_cast::type&&>(x) +#else + #define EASTL_MOVE_INLINE(x) eastl::move(x) +#endif + +#define EASTL_FORWARD(T, x) eastl::forward(x) +#define EASTL_FORWARD_INLINE(T, x) eastl::forward(x) // Need to investigate how to properly make a macro for this. (eastl::is_reference::value ? static_cast(static_cast(x)) : static_cast(x)) + + + + +/// EASTL_MOVE_RANGE +/// Acts like the eastl::move algorithm when possible. Same as C++11 std::move. +/// Note to be confused with the single argument move: (typename remove_reference::type&& move(T&& x)) +/// http://en.cppreference.com/w/cpp/algorithm/move +/// http://en.cppreference.com/w/cpp/algorithm/move_backward +/// +#define EASTL_MOVE_RANGE(first, last, result) eastl::move(first, last, result) +#define EASTL_MOVE_BACKWARD_RANGE(first, last, resultEnd) eastl::move_backward(first, last, resultEnd) + + +namespace eastl +{ + // forward + // + // forwards the argument to another function exactly as it was passed to the calling function. + // Not to be confused with move, this is specifically for echoing templated argument types + // to another function. move is specifically about making a type be an rvalue reference (i.e. movable) type. + // + // Example usage: + // template + // void WrapperFunction(T&& arg) + // { foo(eastl::forward(arg)); } + // + // template + // void WrapperFunction(Args&&... args) + // { foo(eastl::forward(args)...); } + // + // See the C++ Standard, section 20.2.3 + // http://en.cppreference.com/w/cpp/utility/forward + // + template + EA_CPP14_CONSTEXPR T&& forward(typename eastl::remove_reference::type& x) EA_NOEXCEPT + { + return static_cast(x); + } + + + template + EA_CPP14_CONSTEXPR T&& forward(typename eastl::remove_reference::type&& x) EA_NOEXCEPT + { + static_assert(!is_lvalue_reference::value, "forward T isn't lvalue reference"); + return static_cast(x); + } + + + // move + // + // move obtains an rvalue reference to its argument and converts it to an xvalue. + // Returns, by definition: static_cast::type&&>(t). + // The primary use of this is to pass a move'd type to a function which takes T&&, + // and thus select that function instead of (e.g.) a function which takes T or T&. + // See the C++ Standard, section 20.2.3 + // http://en.cppreference.com/w/cpp/utility/move + // + template + EA_CPP14_CONSTEXPR typename eastl::remove_reference::type&& + move(T&& x) EA_NOEXCEPT + { + return static_cast::type&&>(x); + } + + + // move_if_noexcept + // + // Returns T&& if move-constructing T throws no exceptions. Instead returns const T& if + // move-constructing T throws exceptions or has no accessible copy constructor. + // The purpose of this is to use automatically use copy construction instead of move + // construction when the move may possible throw an exception. + // See the C++ Standard, section 20.2.3 + // http://en.cppreference.com/w/cpp/utility/move_if_noexcept + // + #if EASTL_EXCEPTIONS_ENABLED + template + EA_CPP14_CONSTEXPR typename eastl::conditional::value && + eastl::is_copy_constructible::value, const T&, T&&>::type + move_if_noexcept(T& x) EA_NOEXCEPT + { + return eastl::move(x); + } + #else + template + EA_CPP14_CONSTEXPR T&& + move_if_noexcept(T& x) EA_NOEXCEPT + { + return eastl::move(x); + } + #endif + +} // namespace eastl + +#endif // Header include guard + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/internal/pair_fwd_decls.h b/lib/EASTL/include/EASTL/internal/pair_fwd_decls.h new file mode 100644 index 000000000..a716482dc --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/pair_fwd_decls.h @@ -0,0 +1,16 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_PAIR_FWD_DECLS_H +#define EASTL_PAIR_FWD_DECLS_H + +#include + +namespace eastl +{ + template + struct pair; +} + +#endif // EASTL_PAIR_FWD_DECLS_H diff --git a/lib/EASTL/include/EASTL/internal/piecewise_construct_t.h b/lib/EASTL/include/EASTL/internal/piecewise_construct_t.h new file mode 100644 index 000000000..d853f0ea4 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/piecewise_construct_t.h @@ -0,0 +1,46 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_PIECEWISE_CONSTRUCT_T_H +#define EASTL_INTERNAL_PIECEWISE_CONSTRUCT_T_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +namespace eastl +{ + /////////////////////////////////////////////////////////////////////////////// + /// piecewise_construct_t + /// + /// http://en.cppreference.com/w/cpp/utility/piecewise_construct_t + /// + struct piecewise_construct_t + { + explicit piecewise_construct_t() = default; + }; + + + /////////////////////////////////////////////////////////////////////////////// + /// piecewise_construct + /// + /// A tag type used to disambiguate between function overloads that take two tuple arguments. + /// + /// http://en.cppreference.com/w/cpp/utility/piecewise_construct + /// + EA_CONSTEXPR piecewise_construct_t piecewise_construct = eastl::piecewise_construct_t(); + +} // namespace eastl + + +#endif // Header include guard + + + + + + diff --git a/lib/EASTL/include/EASTL/internal/red_black_tree.h b/lib/EASTL/include/EASTL/internal/red_black_tree.h new file mode 100644 index 000000000..111fbb429 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/red_black_tree.h @@ -0,0 +1,2408 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_RED_BLACK_TREE_H +#define EASTL_RED_BLACK_TREE_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + + +// 4512 - 'class' : assignment operator could not be generated +// 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc +// 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. +EA_DISABLE_VC_WARNING(4512 4530 4571); + + +namespace eastl +{ + + /// EASTL_RBTREE_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_RBTREE_DEFAULT_NAME + #define EASTL_RBTREE_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " rbtree" // Unless the user overrides something, this is "EASTL rbtree". + #endif + + + /// EASTL_RBTREE_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_RBTREE_DEFAULT_ALLOCATOR + #define EASTL_RBTREE_DEFAULT_ALLOCATOR allocator_type(EASTL_RBTREE_DEFAULT_NAME) + #endif + + + /// EASTL_RBTREE_LEGACY_SWAP_BEHAVIOUR_REQUIRES_COPY_CTOR + /// + #ifndef EASTL_RBTREE_LEGACY_SWAP_BEHAVIOUR_REQUIRES_COPY_CTOR + #define EASTL_RBTREE_LEGACY_SWAP_BEHAVIOUR_REQUIRES_COPY_CTOR 0 + #endif + + + /// RBTreeColor + /// + enum RBTreeColor + { + kRBTreeColorRed, + kRBTreeColorBlack + }; + + + + /// RBTreeColor + /// + enum RBTreeSide + { + kRBTreeSideLeft, + kRBTreeSideRight + }; + + + + /// rbtree_node_base + /// + /// We define a rbtree_node_base separately from rbtree_node (below), because it + /// allows us to have non-templated operations, and it makes it so that the + /// rbtree 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 rbtree harder, given that the node pointers are of type + /// rbtree_node_base and not rbtree_node. + /// + struct rbtree_node_base + { + typedef rbtree_node_base this_type; + + public: + this_type* mpNodeRight; // Declared first because it is used most often. + this_type* mpNodeLeft; + this_type* mpNodeParent; + char mColor; // We only need one bit here, would be nice if we could stuff that bit somewhere else. + }; + + + /// rbtree_node + /// + template + struct rbtree_node : public rbtree_node_base + { + Value mValue; // For set and multiset, this is the user's value, for map and multimap, this is a pair of key/value. + + // This type is never constructed, so to avoid a MSVC warning we "delete" the copy constructor. + // + // Potentially we could provide a constructor that would satisfy the compiler and change the code to use this constructor + // instead of constructing mValue in place within an unconstructed rbtree_node. + #if defined(_MSC_VER) + rbtree_node(const rbtree_node&) = delete; + #endif + }; + + + + + // rbtree_node_base functions + // + // These are the fundamental functions that we use to maintain the + // tree. The bulk of the work of the tree maintenance is done in + // these functions. + // + EASTL_API rbtree_node_base* RBTreeIncrement (const rbtree_node_base* pNode); + EASTL_API rbtree_node_base* RBTreeDecrement (const rbtree_node_base* pNode); + EASTL_API rbtree_node_base* RBTreeGetMinChild (const rbtree_node_base* pNode); + EASTL_API rbtree_node_base* RBTreeGetMaxChild (const rbtree_node_base* pNode); + EASTL_API size_t RBTreeGetBlackCount(const rbtree_node_base* pNodeTop, + const rbtree_node_base* pNodeBottom); + EASTL_API void RBTreeInsert ( rbtree_node_base* pNode, + rbtree_node_base* pNodeParent, + rbtree_node_base* pNodeAnchor, + RBTreeSide insertionSide); + EASTL_API void RBTreeErase ( rbtree_node_base* pNode, + rbtree_node_base* pNodeAnchor); + + + + + + + + /// rbtree_iterator + /// + template + struct rbtree_iterator + { + typedef rbtree_iterator this_type; + typedef rbtree_iterator iterator; + typedef rbtree_iterator 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 rbtree_node_base base_node_type; + typedef rbtree_node node_type; + typedef Pointer pointer; + typedef Reference reference; + typedef EASTL_ITC_NS::bidirectional_iterator_tag iterator_category; + + public: + node_type* mpNode; + + public: + rbtree_iterator(); + explicit rbtree_iterator(const node_type* pNode); + rbtree_iterator(const iterator& x); + rbtree_iterator& operator=(const iterator& x); + + reference operator*() const; + pointer operator->() const; + + rbtree_iterator& operator++(); + rbtree_iterator operator++(int); + + rbtree_iterator& operator--(); + rbtree_iterator operator--(int); + + }; // rbtree_iterator + + + /////////////////////////////////////////////////////////////////////////////// + // rb_base_compare_ebo + // + // Utilizes the "empty base-class optimization" to reduce the size of the rbtree + // when its Compare template argument is an empty class. + /////////////////////////////////////////////////////////////////////////////// + + template ::value> + struct rb_base_compare_ebo + { + protected: + rb_base_compare_ebo() : mCompare() {} + rb_base_compare_ebo(const Compare& compare) : mCompare(compare) {} + + Compare& get_compare() { return mCompare; } + const Compare& get_compare() const { return mCompare; } + + template + bool compare(const T& lhs, const T& rhs) + { + return mCompare(lhs, rhs); + } + + template + bool compare(const T& lhs, const T& rhs) const + { + return mCompare(lhs, rhs); + } + + private: + Compare mCompare; + }; + + template + struct rb_base_compare_ebo : private Compare + { + protected: + rb_base_compare_ebo() {} + rb_base_compare_ebo(const Compare& compare) : Compare(compare) {} + + Compare& get_compare() { return *this; } + const Compare& get_compare() const { return *this; } + + template + bool compare(const T& lhs, const T& rhs) + { + return Compare::operator()(lhs, rhs); + } + + template + bool compare(const T& lhs, const T& rhs) const + { + return Compare::operator()(lhs, rhs); + } + }; + + + + /////////////////////////////////////////////////////////////////////////////// + // rb_base + // + // This class allows us to use a generic rbtree as the basis of map, multimap, + // set, and multiset transparently. The vital template parameters for this are + // the ExtractKey and the bUniqueKeys parameters. + // + // If the rbtree has a value type of the form pair (i.e. it is a map or + // multimap and not a set or multiset) and a key extraction policy that returns + // the first part of the pair, the rbtree gets a mapped_type typedef. + // If it satisfies those criteria and also has unique keys, then it also gets an + // operator[] (which only map and set have and multimap and multiset don't have). + // + /////////////////////////////////////////////////////////////////////////////// + + + + /// rb_base + /// This specialization is used for 'set'. In this case, Key and Value + /// will be the same as each other and ExtractKey will be eastl::use_self. + /// + template + struct rb_base : public rb_base_compare_ebo + { + typedef ExtractKey extract_key; + + protected: + using rb_base_compare_ebo::compare; + using rb_base_compare_ebo::get_compare; + + public: + rb_base() {} + rb_base(const Compare& compare) : rb_base_compare_ebo(compare) {} + }; + + + /// rb_base + /// This class is used for 'multiset'. + /// In this case, Key and Value will be the same as each + /// other and ExtractKey will be eastl::use_self. + /// + template + struct rb_base : public rb_base_compare_ebo + { + typedef ExtractKey extract_key; + + protected: + using rb_base_compare_ebo::compare; + using rb_base_compare_ebo::get_compare; + + public: + rb_base() {} + rb_base(const Compare& compare) : rb_base_compare_ebo(compare) {} + }; + + + /// rb_base + /// This specialization is used for 'map'. + /// + template + struct rb_base, true, RBTree> : public rb_base_compare_ebo + { + typedef eastl::use_first extract_key; + + using rb_base_compare_ebo::compare; + using rb_base_compare_ebo::get_compare; + + public: + rb_base() {} + rb_base(const Compare& compare) : rb_base_compare_ebo(compare) {} + }; + + + /// rb_base + /// This specialization is used for 'multimap'. + /// + template + struct rb_base, false, RBTree> : public rb_base_compare_ebo + { + typedef eastl::use_first extract_key; + + using rb_base_compare_ebo::compare; + using rb_base_compare_ebo::get_compare; + + public: + rb_base() {} + rb_base(const Compare& compare) : rb_base_compare_ebo(compare) {} + }; + + + /// rbtree + /// + /// rbtree is the red-black tree basis for the map, multimap, set, and multiset + /// containers. Just about all the work of those containers is done here, and + /// they are merely a shell which sets template policies that govern the code + /// generation for this rbtree. + /// + /// This rbtree implementation is pretty much the same as all other modern + /// rbtree implementations, as the topic is well known and researched. We may + /// choose to implement a "relaxed balancing" option at some point in the + /// future if it is deemed worthwhile. Most rbtree implementations don't do this. + /// + /// The primary rbtree member variable is mAnchor, which is a node_type and + /// acts as the end node. However, like any other node, it has mpNodeLeft, + /// mpNodeRight, and mpNodeParent members. We do the conventional trick of + /// assigning begin() (left-most rbtree node) to mpNodeLeft, assigning + /// 'end() - 1' (a.k.a. rbegin()) to mpNodeRight, and assigning the tree root + /// node to mpNodeParent. + /// + /// Compare (functor): This is a comparison class which defaults to 'less'. + /// It is a common STL thing which takes two arguments and returns true if + /// the first is less than the second. + /// + /// ExtractKey (functor): This is a class which gets the key from a stored + /// node. With map and set, the node is a pair, whereas with set and multiset + /// the node is just the value. ExtractKey will be either eastl::use_first (map and multimap) + /// or eastl::use_self (set and multiset). + /// + /// bMutableIterators (bool): true if rbtree::iterator is a mutable + /// iterator, false if iterator and const_iterator are both const iterators. + /// It will be true for map and multimap and false for set and multiset. + /// + /// bUniqueKeys (bool): true if the keys are to be unique, and false if there + /// can be multiple instances of a given key. It will be true for set and map + /// and false for multiset and multimap. + /// + /// To consider: Add an option for relaxed tree balancing. This could result + /// in performance improvements but would require a more complicated implementation. + /// + /////////////////////////////////////////////////////////////////////// + /// 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. + /// + template + class rbtree + : public rb_base > + { + public: + typedef ptrdiff_t difference_type; + typedef eastl_size_t size_type; // See config.h for the definition of eastl_size_t, which defaults to size_t. + typedef Key key_type; + typedef Value value_type; + typedef rbtree_node node_type; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef value_type* pointer; + typedef const value_type* const_pointer; + + typedef typename type_select, + rbtree_iterator >::type iterator; + typedef rbtree_iterator const_iterator; + typedef eastl::reverse_iterator reverse_iterator; + typedef eastl::reverse_iterator const_reverse_iterator; + + typedef Allocator allocator_type; + typedef Compare key_compare; + typedef typename type_select, iterator>::type insert_return_type; // map/set::insert return a pair, multimap/multiset::iterator return an iterator. + typedef rbtree this_type; + typedef rb_base base_type; + typedef integral_constant has_unique_keys_type; + typedef typename base_type::extract_key extract_key; + + protected: + using base_type::compare; + using base_type::get_compare; + + public: + rbtree_node_base mAnchor; /// This node acts as end() and its mpLeft points to begin(), and mpRight points to rbegin() (the last node on the right). + size_type mnSize; /// Stores the count of nodes in the tree (not counting the anchor node). + allocator_type mAllocator; // To do: Use base class optimization to make this go away. + + public: + // ctor/dtor + rbtree(); + rbtree(const allocator_type& allocator); + rbtree(const Compare& compare, const allocator_type& allocator = EASTL_RBTREE_DEFAULT_ALLOCATOR); + rbtree(const this_type& x); + rbtree(this_type&& x); + rbtree(this_type&& x, const allocator_type& allocator); + + template + rbtree(InputIterator first, InputIterator last, const Compare& compare, const allocator_type& allocator = EASTL_RBTREE_DEFAULT_ALLOCATOR); + + ~rbtree(); + + public: + // properties + const allocator_type& get_allocator() const EA_NOEXCEPT; + allocator_type& get_allocator() EA_NOEXCEPT; + void set_allocator(const allocator_type& allocator); + + const key_compare& key_comp() const { return get_compare(); } + key_compare& key_comp() { return get_compare(); } + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list 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: + bool empty() const EA_NOEXCEPT; + size_type size() const EA_NOEXCEPT; + + template + insert_return_type emplace(Args&&... args); + + template + iterator emplace_hint(const_iterator position, Args&&... args); + + template eastl::pair try_emplace(const key_type& k, Args&&... args); + template eastl::pair try_emplace(key_type&& k, Args&&... args); + template iterator try_emplace(const_iterator position, const key_type& k, Args&&... args); + template iterator try_emplace(const_iterator position, key_type&& k, Args&&... args); + + // Standard conversion overload to avoid the overhead of mismatched 'pair' types. + template ::value>::type> + insert_return_type insert(P&& otherValue); + + // Currently limited to value_type instead of P because it collides with insert(InputIterator, InputIterator). + // To allow this to work with templated P we need to implement a compile-time specialization for the + // case that P&& is const_iterator and have that specialization handle insert(InputIterator, InputIterator) + // instead of insert(InputIterator, InputIterator). Curiously, neither libstdc++ nor libc++ + // implement this function either, which suggests they ran into the same problem I did here + // and haven't yet resolved it (at least as of March 2014, GCC 4.8.1). + iterator insert(const_iterator hint, value_type&& value); + + /// map::insert and set::insert return a pair, while multimap::insert and + /// multiset::insert return an iterator. + insert_return_type insert(const value_type& value); + + // C++ standard: inserts value if and only if there is no element with + // key equivalent to the key of t in containers with unique keys; always + // inserts value in containers with equivalent keys. Always returns the + // iterator pointing to the element with key equivalent to the key of value. + // iterator position is a hint pointing to where the insert should start + // to search. However, there is a potential defect/improvement report on this behaviour: + // LWG issue #233 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1780.html) + // We follow the same approach as SGI STL/STLPort and use the position as + // a forced insertion position for the value when possible. + iterator insert(const_iterator position, const value_type& value); + + void insert(std::initializer_list ilist); + + template + void insert(InputIterator first, InputIterator last); + + // TODO(rparolin): + // insert_return_type insert(node_type&& nh); + // iterator insert(const_iterator hint, node_type&& nh); + + template pair insert_or_assign(const key_type& k, M&& obj); + template pair insert_or_assign(key_type&& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + reverse_iterator erase(const_reverse_iterator position); + reverse_iterator erase(const_reverse_iterator first, const_reverse_iterator last); + + // For some reason, multiple STL versions make a specialization + // for erasing an array of key_types. I'm pretty sure we don't + // need this, but just to be safe we will follow suit. + // The implementation is trivial. Returns void because the values + // could well be randomly distributed throughout the tree and thus + // a return value would be nearly meaningless. + void erase(const key_type* first, const key_type* last); + + void clear(); + void reset_lose_memory(); // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. + + iterator find(const key_type& key); + const_iterator find(const key_type& key) const; + + /// Implements a find whereby the user supplies a comparison of a different type + /// than the tree's value_type. A useful case of this is one whereby you have + /// a container of string objects but want to do searches via passing in char pointers. + /// The problem is that without this kind of find, you need to do the expensive operation + /// of converting the char pointer to a string so it can be used as the argument to the + /// find function. + /// + /// Example usage (note that the compare uses string as first type and char* as second): + /// set strings; + /// strings.find_as("hello", less_2()); + /// + template iterator find_as(const U& u, Compare2 compare2); + template const_iterator find_as(const U& u, Compare2 compare2) const; + + iterator lower_bound(const key_type& key); + const_iterator lower_bound(const key_type& key) const; + + iterator upper_bound(const key_type& key); + const_iterator upper_bound(const key_type& key) const; + + bool validate() const; + int validate_iterator(const_iterator i) const; + + protected: + node_type* DoAllocateNode(); + void DoFreeNode(node_type* pNode); + + node_type* DoCreateNodeFromKey(const key_type& key); + + template + node_type* DoCreateNode(Args&&... args); + node_type* DoCreateNode(const value_type& value); + node_type* DoCreateNode(value_type&& value); + node_type* DoCreateNode(const node_type* pNodeSource, node_type* pNodeParent); + + node_type* DoCopySubtree(const node_type* pNodeSource, node_type* pNodeDest); + void DoNukeSubtree(node_type* pNode); + + template + eastl::pair DoInsertValue(true_type, Args&&... args); + + template + iterator DoInsertValue(false_type, Args&&... args); + + eastl::pair DoInsertValue(true_type, value_type&& value); + iterator DoInsertValue(false_type, value_type&& value); + + template + iterator DoInsertValueImpl(node_type* pNodeParent, bool bForceToLeft, const key_type& key, Args&&... args); + iterator DoInsertValueImpl(node_type* pNodeParent, bool bForceToLeft, const key_type& key, node_type* pNodeNew); + + eastl::pair DoInsertKey(true_type, const key_type& key); + iterator DoInsertKey(false_type, const key_type& key); + + template + iterator DoInsertValueHint(true_type, const_iterator position, Args&&... args); + + template + iterator DoInsertValueHint(false_type, const_iterator position, Args&&... args); + + iterator DoInsertValueHint(true_type, const_iterator position, value_type&& value); + iterator DoInsertValueHint(false_type, const_iterator position, value_type&& value); + + iterator DoInsertKey(true_type, const_iterator position, const key_type& key); // By design we return iterator and not a pair. + iterator DoInsertKey(false_type, const_iterator position, const key_type& key); + iterator DoInsertKeyImpl(node_type* pNodeParent, bool bForceToLeft, const key_type& key); + + node_type* DoGetKeyInsertionPositionUniqueKeys(bool& canInsert, const key_type& key); + node_type* DoGetKeyInsertionPositionNonuniqueKeys(const key_type& key); + + node_type* DoGetKeyInsertionPositionUniqueKeysHint(const_iterator position, bool& bForceToLeft, const key_type& key); + node_type* DoGetKeyInsertionPositionNonuniqueKeysHint(const_iterator position, bool& bForceToLeft, const key_type& key); + + }; // rbtree + + + + + + /////////////////////////////////////////////////////////////////////// + // rbtree_node_base functions + /////////////////////////////////////////////////////////////////////// + + EASTL_API inline rbtree_node_base* RBTreeGetMinChild(const rbtree_node_base* pNodeBase) + { + while(pNodeBase->mpNodeLeft) + pNodeBase = pNodeBase->mpNodeLeft; + return const_cast(pNodeBase); + } + + EASTL_API inline rbtree_node_base* RBTreeGetMaxChild(const rbtree_node_base* pNodeBase) + { + while(pNodeBase->mpNodeRight) + pNodeBase = pNodeBase->mpNodeRight; + return const_cast(pNodeBase); + } + + // The rest of the functions are non-trivial and are found in + // the corresponding .cpp file to this file. + + + + /////////////////////////////////////////////////////////////////////// + // rbtree_iterator functions + /////////////////////////////////////////////////////////////////////// + + template + rbtree_iterator::rbtree_iterator() + : mpNode(NULL) { } + + + template + rbtree_iterator::rbtree_iterator(const node_type* pNode) + : mpNode(static_cast(const_cast(pNode))) { } + + + template + rbtree_iterator::rbtree_iterator(const iterator& x) + : mpNode(x.mpNode) { } + + template + typename rbtree_iterator::this_type& + rbtree_iterator::operator=(const iterator& x) + { + mpNode = x.mpNode; + return *this; + } + + template + typename rbtree_iterator::reference + rbtree_iterator::operator*() const + { return mpNode->mValue; } + + + template + typename rbtree_iterator::pointer + rbtree_iterator::operator->() const + { return &mpNode->mValue; } + + + template + typename rbtree_iterator::this_type& + rbtree_iterator::operator++() + { + mpNode = static_cast(RBTreeIncrement(mpNode)); + return *this; + } + + + template + typename rbtree_iterator::this_type + rbtree_iterator::operator++(int) + { + this_type temp(*this); + mpNode = static_cast(RBTreeIncrement(mpNode)); + return temp; + } + + + template + typename rbtree_iterator::this_type& + rbtree_iterator::operator--() + { + mpNode = static_cast(RBTreeDecrement(mpNode)); + return *this; + } + + + template + typename rbtree_iterator::this_type + rbtree_iterator::operator--(int) + { + this_type temp(*this); + mpNode = static_cast(RBTreeDecrement(mpNode)); + 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 + inline bool operator==(const rbtree_iterator& a, + const rbtree_iterator& b) + { + return a.mpNode == b.mpNode; + } + + + template + inline bool operator!=(const rbtree_iterator& a, + const rbtree_iterator& 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 + inline bool operator!=(const rbtree_iterator& a, + const rbtree_iterator& b) + { + return a.mpNode != b.mpNode; + } + + + + + /////////////////////////////////////////////////////////////////////// + // rbtree functions + /////////////////////////////////////////////////////////////////////// + + template + inline rbtree::rbtree() + : mAnchor(), + mnSize(0), + mAllocator(EASTL_RBTREE_DEFAULT_NAME) + { + reset_lose_memory(); + } + + + template + inline rbtree::rbtree(const allocator_type& allocator) + : mAnchor(), + mnSize(0), + mAllocator(allocator) + { + reset_lose_memory(); + } + + + template + inline rbtree::rbtree(const C& compare, const allocator_type& allocator) + : base_type(compare), + mAnchor(), + mnSize(0), + mAllocator(allocator) + { + reset_lose_memory(); + } + + + template + inline rbtree::rbtree(const this_type& x) + : base_type(x.get_compare()), + mAnchor(), + mnSize(0), + mAllocator(x.mAllocator) + { + reset_lose_memory(); + + if(x.mAnchor.mpNodeParent) // mAnchor.mpNodeParent is the rb_tree root node. + { + mAnchor.mpNodeParent = DoCopySubtree((const node_type*)x.mAnchor.mpNodeParent, (node_type*)&mAnchor); + mAnchor.mpNodeRight = RBTreeGetMaxChild(mAnchor.mpNodeParent); + mAnchor.mpNodeLeft = RBTreeGetMinChild(mAnchor.mpNodeParent); + mnSize = x.mnSize; + } + } + + + template + inline rbtree::rbtree(this_type&& x) + : base_type(x.get_compare()), + mAnchor(), + mnSize(0), + mAllocator(x.mAllocator) + { + reset_lose_memory(); + swap(x); + } + + template + inline rbtree::rbtree(this_type&& x, const allocator_type& allocator) + : base_type(x.get_compare()), + mAnchor(), + mnSize(0), + mAllocator(allocator) + { + reset_lose_memory(); + swap(x); // swap will directly or indirectly handle the possibility that mAllocator != x.mAllocator. + } + + + template + template + inline rbtree::rbtree(InputIterator first, InputIterator last, const C& compare, const allocator_type& allocator) + : base_type(compare), + mAnchor(), + mnSize(0), + mAllocator(allocator) + { + reset_lose_memory(); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(; first != last; ++first) + insert(*first); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + clear(); + throw; + } + #endif + } + + + template + inline rbtree::~rbtree() + { + // Erase the entire tree. DoNukeSubtree is not a + // conventional erase function, as it does no rebalancing. + DoNukeSubtree((node_type*)mAnchor.mpNodeParent); + } + + + template + inline const typename rbtree::allocator_type& + rbtree::get_allocator() const EA_NOEXCEPT + { + return mAllocator; + } + + + template + inline typename rbtree::allocator_type& + rbtree::get_allocator() EA_NOEXCEPT + { + return mAllocator; + } + + + template + inline void rbtree::set_allocator(const allocator_type& allocator) + { + mAllocator = allocator; + } + + + template + inline typename rbtree::size_type + rbtree::size() const EA_NOEXCEPT + { return mnSize; } + + + template + inline bool rbtree::empty() const EA_NOEXCEPT + { return (mnSize == 0); } + + + template + inline typename rbtree::iterator + rbtree::begin() EA_NOEXCEPT + { return iterator(static_cast(mAnchor.mpNodeLeft)); } + + + template + inline typename rbtree::const_iterator + rbtree::begin() const EA_NOEXCEPT + { return const_iterator(static_cast(const_cast(mAnchor.mpNodeLeft))); } + + + template + inline typename rbtree::const_iterator + rbtree::cbegin() const EA_NOEXCEPT + { return const_iterator(static_cast(const_cast(mAnchor.mpNodeLeft))); } + + + template + inline typename rbtree::iterator + rbtree::end() EA_NOEXCEPT + { return iterator(static_cast(&mAnchor)); } + + + template + inline typename rbtree::const_iterator + rbtree::end() const EA_NOEXCEPT + { return const_iterator(static_cast(const_cast(&mAnchor))); } + + + template + inline typename rbtree::const_iterator + rbtree::cend() const EA_NOEXCEPT + { return const_iterator(static_cast(const_cast(&mAnchor))); } + + + template + inline typename rbtree::reverse_iterator + rbtree::rbegin() EA_NOEXCEPT + { return reverse_iterator(end()); } + + + template + inline typename rbtree::const_reverse_iterator + rbtree::rbegin() const EA_NOEXCEPT + { return const_reverse_iterator(end()); } + + + template + inline typename rbtree::const_reverse_iterator + rbtree::crbegin() const EA_NOEXCEPT + { return const_reverse_iterator(end()); } + + + template + inline typename rbtree::reverse_iterator + rbtree::rend() EA_NOEXCEPT + { return reverse_iterator(begin()); } + + + template + inline typename rbtree::const_reverse_iterator + rbtree::rend() const EA_NOEXCEPT + { return const_reverse_iterator(begin()); } + + + template + inline typename rbtree::const_reverse_iterator + rbtree::crend() const EA_NOEXCEPT + { return const_reverse_iterator(begin()); } + + + template + inline typename rbtree::this_type& + rbtree::operator=(const this_type& x) + { + if(this != &x) + { + clear(); + + #if EASTL_ALLOCATOR_COPY_ENABLED + mAllocator = x.mAllocator; + #endif + + get_compare() = x.get_compare(); + + if(x.mAnchor.mpNodeParent) // mAnchor.mpNodeParent is the rb_tree root node. + { + mAnchor.mpNodeParent = DoCopySubtree((const node_type*)x.mAnchor.mpNodeParent, (node_type*)&mAnchor); + mAnchor.mpNodeRight = RBTreeGetMaxChild(mAnchor.mpNodeParent); + mAnchor.mpNodeLeft = RBTreeGetMinChild(mAnchor.mpNodeParent); + mnSize = x.mnSize; + } + } + return *this; + } + + template + inline typename rbtree::this_type& + rbtree::operator=(this_type&& x) + { + if(this != &x) + { + clear(); // To consider: Are we really required to clear here? x is going away soon and will clear itself in its dtor. + swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. + } + return *this; + } + + template + inline typename rbtree::this_type& + rbtree::operator=(std::initializer_list ilist) + { + // The simplest means of doing this is to clear and insert. There probably isn't a generic + // solution that's any more efficient without having prior knowledge of the ilist contents. + clear(); + + for(typename std::initializer_list::iterator it = ilist.begin(), itEnd = ilist.end(); it != itEnd; ++it) + DoInsertValue(has_unique_keys_type(), eastl::move(*it)); + + return *this; + } + + + template + void rbtree::swap(this_type& x) + { + #if EASTL_RBTREE_LEGACY_SWAP_BEHAVIOUR_REQUIRES_COPY_CTOR + if(mAllocator == x.mAllocator) // If allocators are equivalent... + #endif + { + // Most of our members can be exchaged by a basic swap: + // We leave mAllocator as-is. + eastl::swap(mnSize, x.mnSize); + eastl::swap(get_compare(), x.get_compare()); + #if !EASTL_RBTREE_LEGACY_SWAP_BEHAVIOUR_REQUIRES_COPY_CTOR + eastl::swap(mAllocator, x.mAllocator); + #endif + + + // However, because our anchor node is a part of our class instance and not + // dynamically allocated, we can't do a swap of it but must do a more elaborate + // procedure. This is the downside to having the mAnchor be like this, but + // otherwise we consider it a good idea to avoid allocating memory for a + // nominal container instance. + + // We optimize for the expected most common case: both pointers being non-null. + if(mAnchor.mpNodeParent && x.mAnchor.mpNodeParent) // If both pointers are non-null... + { + eastl::swap(mAnchor.mpNodeRight, x.mAnchor.mpNodeRight); + eastl::swap(mAnchor.mpNodeLeft, x.mAnchor.mpNodeLeft); + eastl::swap(mAnchor.mpNodeParent, x.mAnchor.mpNodeParent); + + // We need to fix up the anchors to point to themselves (we can't just swap them). + mAnchor.mpNodeParent->mpNodeParent = &mAnchor; + x.mAnchor.mpNodeParent->mpNodeParent = &x.mAnchor; + } + else if(mAnchor.mpNodeParent) + { + x.mAnchor.mpNodeRight = mAnchor.mpNodeRight; + x.mAnchor.mpNodeLeft = mAnchor.mpNodeLeft; + x.mAnchor.mpNodeParent = mAnchor.mpNodeParent; + x.mAnchor.mpNodeParent->mpNodeParent = &x.mAnchor; + + // We need to fix up our anchor to point it itself (we can't have it swap with x). + mAnchor.mpNodeRight = &mAnchor; + mAnchor.mpNodeLeft = &mAnchor; + mAnchor.mpNodeParent = NULL; + } + else if(x.mAnchor.mpNodeParent) + { + mAnchor.mpNodeRight = x.mAnchor.mpNodeRight; + mAnchor.mpNodeLeft = x.mAnchor.mpNodeLeft; + mAnchor.mpNodeParent = x.mAnchor.mpNodeParent; + mAnchor.mpNodeParent->mpNodeParent = &mAnchor; + + // We need to fix up x's anchor to point it itself (we can't have it swap with us). + x.mAnchor.mpNodeRight = &x.mAnchor; + x.mAnchor.mpNodeLeft = &x.mAnchor; + x.mAnchor.mpNodeParent = NULL; + } // Else both are NULL and there is nothing to do. + } + #if EASTL_RBTREE_LEGACY_SWAP_BEHAVIOUR_REQUIRES_COPY_CTOR + else + { + const this_type temp(*this); // Can't call eastl::swap because that would + *this = x; // itself call this member swap function. + x = temp; + } + #endif + } + + + template + template + inline typename rbtree::insert_return_type // map/set::insert return a pair, multimap/multiset::iterator return an iterator. + rbtree::emplace(Args&&... args) + { + return DoInsertValue(has_unique_keys_type(), eastl::forward(args)...); + } + + template + template + typename rbtree::iterator + rbtree::emplace_hint(const_iterator position, Args&&... args) + { + return DoInsertValueHint(has_unique_keys_type(), position, eastl::forward(args)...); + } + + template + template + inline eastl::pair::iterator, bool> + rbtree::try_emplace(const key_type& key, Args&&... args) + { + return DoInsertValue(has_unique_keys_type(), piecewise_construct, eastl::forward_as_tuple(key), eastl::forward_as_tuple(eastl::forward(args)...)); + } + + template + template + inline eastl::pair::iterator, bool> + rbtree::try_emplace(key_type&& key, Args&&... args) + { + return DoInsertValue(has_unique_keys_type(), piecewise_construct, eastl::forward_as_tuple(eastl::move(key)), eastl::forward_as_tuple(eastl::forward(args)...)); + } + + template + template + inline typename rbtree::iterator + rbtree::try_emplace(const_iterator position, const key_type& key, Args&&... args) + { + return DoInsertValueHint( + has_unique_keys_type(), position, + piecewise_construct, eastl::forward_as_tuple(key), eastl::forward_as_tuple(eastl::forward(args)...)); + } + + template + template + inline typename rbtree::iterator + rbtree::try_emplace(const_iterator position, key_type&& key, Args&&... args) + { + return DoInsertValueHint( + has_unique_keys_type(), position, + piecewise_construct, eastl::forward_as_tuple(eastl::move(key)), eastl::forward_as_tuple(eastl::forward(args)...)); + } + + + template + template + inline typename rbtree::insert_return_type // map/set::insert return a pair, multimap/multiset::iterator return an iterator. + rbtree::insert(P&& otherValue) + { + // Need to use forward instead of move because P&& is a "universal reference" instead of an rvalue reference. + return emplace(eastl::forward

(otherValue)); + } + + + template + inline typename rbtree::iterator + rbtree::insert(const_iterator position, value_type&& value) + { + return DoInsertValueHint(has_unique_keys_type(), position, eastl::move(value)); + } + + + template + inline typename rbtree::insert_return_type // map/set::insert return a pair, multimap/multiset::iterator return an iterator. + rbtree::insert(const value_type& value) + { + return DoInsertValue(has_unique_keys_type(), value); + } + + + template + typename rbtree::iterator + rbtree::insert(const_iterator position, const value_type& value) + { + return DoInsertValueHint(has_unique_keys_type(), position, value); + } + + + template + template + eastl::pair::iterator, bool> + rbtree::insert_or_assign(const key_type& k, M&& obj) + { + auto iter = find(k); + + if(iter == end()) + { + return insert(value_type(piecewise_construct, eastl::forward_as_tuple(k), eastl::forward_as_tuple(eastl::forward(obj)))); + } + else + { + iter->second = eastl::forward(obj); + return {iter, false}; + } + } + + template + template + eastl::pair::iterator, bool> + rbtree::insert_or_assign(key_type&& k, M&& obj) + { + auto iter = find(k); + + if(iter == end()) + { + return insert(value_type(piecewise_construct, eastl::forward_as_tuple(eastl::move(k)), eastl::forward_as_tuple(eastl::forward(obj)))); + } + else + { + iter->second = eastl::forward(obj); + return {iter, false}; + } + } + + template + template + typename rbtree::iterator + rbtree::insert_or_assign(const_iterator hint, const key_type& k, M&& obj) + { + auto iter = find(k); + + if(iter == end()) + { + return insert(hint, value_type(piecewise_construct, eastl::forward_as_tuple(k), eastl::forward_as_tuple(eastl::forward(obj)))); + } + else + { + iter->second = eastl::forward(obj); + return iter; + } + } + + template + template + typename rbtree::iterator + rbtree::insert_or_assign(const_iterator hint, key_type&& k, M&& obj) + { + auto iter = find(k); + + if(iter == end()) + { + return insert(hint, value_type(piecewise_construct, eastl::forward_as_tuple(eastl::move(k)), eastl::forward_as_tuple(eastl::forward(obj)))); + } + else + { + iter->second = eastl::forward(obj); + return iter; + } + } + + template + typename rbtree::node_type* + rbtree::DoGetKeyInsertionPositionUniqueKeys(bool& canInsert, const key_type& key) + { + // This code is essentially a slightly modified copy of the the rbtree::insert + // function whereby this version takes a key and not a full value_type. + extract_key extractKey; + + node_type* pCurrent = (node_type*)mAnchor.mpNodeParent; // Start with the root node. + node_type* pLowerBound = (node_type*)&mAnchor; // Set it to the container end for now. + node_type* pParent; // This will be where we insert the new node. + + bool bValueLessThanNode = true; // If the tree is empty, this will result in an insertion at the front. + + // Find insertion position of the value. This will either be a position which + // already contains the value, a position which is greater than the value or + // end(), which we treat like a position which is greater than the value. + while(EASTL_LIKELY(pCurrent)) // Do a walk down the tree. + { + bValueLessThanNode = compare(key, extractKey(pCurrent->mValue)); + pLowerBound = pCurrent; + + if(bValueLessThanNode) + { + EASTL_VALIDATE_COMPARE(!compare(extractKey(pCurrent->mValue), key)); // Validate that the compare function is sane. + pCurrent = (node_type*)pCurrent->mpNodeLeft; + } + else + pCurrent = (node_type*)pCurrent->mpNodeRight; + } + + pParent = pLowerBound; // pLowerBound is actually upper bound right now (i.e. it is > value instead of <=), but we will make it the lower bound below. + + if(bValueLessThanNode) // If we ended up on the left side of the last parent node... + { + if(EASTL_LIKELY(pLowerBound != (node_type*)mAnchor.mpNodeLeft)) // If the tree was empty or if we otherwise need to insert at the very front of the tree... + { + // At this point, pLowerBound points to a node which is > than value. + // Move it back by one, so that it points to a node which is <= value. + pLowerBound = (node_type*)RBTreeDecrement(pLowerBound); + } + else + { + canInsert = true; + return pLowerBound; + } + } + + // Since here we require values to be unique, we will do nothing if the value already exists. + if(compare(extractKey(pLowerBound->mValue), key)) // If the node is < the value (i.e. if value is >= the node)... + { + EASTL_VALIDATE_COMPARE(!compare(key, extractKey(pLowerBound->mValue))); // Validate that the compare function is sane. + canInsert = true; + return pParent; + } + + // The item already exists (as found by the compare directly above), so return false. + canInsert = false; + return pLowerBound; + } + + + template + typename rbtree::node_type* + rbtree::DoGetKeyInsertionPositionNonuniqueKeys(const key_type& key) + { + // This is the pathway for insertion of non-unique keys (multimap and multiset, but not map and set). + node_type* pCurrent = (node_type*)mAnchor.mpNodeParent; // Start with the root node. + node_type* pRangeEnd = (node_type*)&mAnchor; // Set it to the container end for now. + extract_key extractKey; + + while(pCurrent) + { + pRangeEnd = pCurrent; + + if(compare(key, extractKey(pCurrent->mValue))) + { + EASTL_VALIDATE_COMPARE(!compare(extractKey(pCurrent->mValue), key)); // Validate that the compare function is sane. + pCurrent = (node_type*)pCurrent->mpNodeLeft; + } + else + pCurrent = (node_type*)pCurrent->mpNodeRight; + } + + return pRangeEnd; + } + + + template + eastl::pair::iterator, bool> + rbtree::DoInsertValue(true_type, value_type&& value) + { + extract_key extractKey; + key_type key(extractKey(value)); + bool canInsert; + node_type* pPosition = DoGetKeyInsertionPositionUniqueKeys(canInsert, key); + + if(canInsert) + { + const iterator itResult(DoInsertValueImpl(pPosition, false, key, eastl::move(value))); + return pair(itResult, true); + } + + return pair(iterator(pPosition), false); + } + + + template + typename rbtree::iterator + rbtree::DoInsertValue(false_type, value_type&& value) + { + extract_key extractKey; + key_type key(extractKey(value)); + node_type* pPosition = DoGetKeyInsertionPositionNonuniqueKeys(key); + + return DoInsertValueImpl(pPosition, false, key, eastl::move(value)); + } + + + template + template + eastl::pair::iterator, bool> + rbtree::DoInsertValue(true_type, Args&&... args) // true_type means keys are unique. + { + // This is the pathway for insertion of unique keys (map and set, but not multimap and multiset). + // Note that we return a pair and not an iterator. This is because the C++ standard for map + // and set is to return a pair and not just an iterator. + + node_type* pNodeNew = DoCreateNode(eastl::forward(args)...); // Note that pNodeNew->mpLeft, mpRight, mpParent, will be uninitialized. + const key_type& key = extract_key{}(pNodeNew->mValue); + + bool canInsert; + node_type* pPosition = DoGetKeyInsertionPositionUniqueKeys(canInsert, key); + + if(canInsert) + { + iterator itResult(DoInsertValueImpl(pPosition, false, key, pNodeNew)); + return pair(itResult, true); + } + + DoFreeNode(pNodeNew); + return pair(iterator(pPosition), false); + } + + + template + template + typename rbtree::iterator + rbtree::DoInsertValue(false_type, Args&&... args) // false_type means keys are not unique. + { + // We have a problem here if sizeof(value_type) is too big for the stack. We may want to consider having a specialization for large value_types. + // To do: Change this so that we call DoCreateNode(eastl::forward(args)...) here and use the value from the resulting pNode to get the + // key, and make DoInsertValueImpl take that node as an argument. That way there is no value created on the stack. + + node_type* const pNodeNew = DoCreateNode(eastl::forward(args)...); // Note that pNodeNew->mpLeft, mpRight, mpParent, will be uninitialized. + const key_type& key = extract_key{}(pNodeNew->mValue); + + node_type* pPosition = DoGetKeyInsertionPositionNonuniqueKeys(key); + + return DoInsertValueImpl(pPosition, false, key, pNodeNew); + } + + + template + template + typename rbtree::iterator + rbtree::DoInsertValueImpl(node_type* pNodeParent, bool bForceToLeft, const key_type& key, Args&&... args) + { + node_type* const pNodeNew = DoCreateNode(eastl::forward(args)...); // Note that pNodeNew->mpLeft, mpRight, mpParent, will be uninitialized. + + return DoInsertValueImpl(pNodeParent, bForceToLeft, key, pNodeNew); + } + + + template + typename rbtree::iterator + rbtree::DoInsertValueImpl(node_type* pNodeParent, bool bForceToLeft, const key_type& key, node_type* pNodeNew) + { + EASTL_ASSERT_MSG(pNodeNew != nullptr, "node to insert to the rbtree must not be null"); + + RBTreeSide side; + extract_key extractKey; + + // The reason we may want to have bForceToLeft == true is that pNodeParent->mValue and value may be equal. + // In that case it doesn't matter what side we insert on, except that the C++ LWG #233 improvement report + // suggests that we should use the insert hint position to force an ordering. So that's what we do. + if(bForceToLeft || (pNodeParent == &mAnchor) || compare(key, extractKey(pNodeParent->mValue))) + side = kRBTreeSideLeft; + else + side = kRBTreeSideRight; + + RBTreeInsert(pNodeNew, pNodeParent, &mAnchor, side); + mnSize++; + + return iterator(pNodeNew); + } + + + template + eastl::pair::iterator, bool> + rbtree::DoInsertKey(true_type, const key_type& key) // true_type means keys are unique. + { + // This is the pathway for insertion of unique keys (map and set, but not multimap and multiset). + // Note that we return a pair and not an iterator. This is because the C++ standard for map + // and set is to return a pair and not just an iterator. + bool canInsert; + node_type* pPosition = DoGetKeyInsertionPositionUniqueKeys(canInsert, key); + + if(canInsert) + { + const iterator itResult(DoInsertKeyImpl(pPosition, false, key)); + return pair(itResult, true); + } + + return pair(iterator(pPosition), false); + } + + + template + typename rbtree::iterator + rbtree::DoInsertKey(false_type, const key_type& key) // false_type means keys are not unique. + { + node_type* pPosition = DoGetKeyInsertionPositionNonuniqueKeys(key); + + return DoInsertKeyImpl(pPosition, false, key); + } + + + + template + typename rbtree::node_type* + rbtree::DoGetKeyInsertionPositionUniqueKeysHint(const_iterator position, bool& bForceToLeft, const key_type& key) + { + extract_key extractKey; + + if((position.mpNode != mAnchor.mpNodeRight) && (position.mpNode != &mAnchor)) // If the user specified a specific insertion position... + { + iterator itNext(position.mpNode); + ++itNext; + + // To consider: Change this so that 'position' specifies the position after + // where the insertion goes and not the position before where the insertion goes. + // Doing so would make this more in line with user expectations and with LWG #233. + const bool bPositionLessThanValue = compare(extractKey(position.mpNode->mValue), key); + + if(bPositionLessThanValue) // If (value > *position)... + { + EASTL_VALIDATE_COMPARE(!compare(key, extractKey(position.mpNode->mValue))); // Validate that the compare function is sane. + + const bool bValueLessThanNext = compare(key, extractKey(itNext.mpNode->mValue)); + + if(bValueLessThanNext) // If value < *itNext... + { + EASTL_VALIDATE_COMPARE(!compare(extractKey(itNext.mpNode->mValue), key)); // Validate that the compare function is sane. + + if(position.mpNode->mpNodeRight) + { + bForceToLeft = true; // Specifically insert in front of (to the left of) itNext (and thus after 'position'). + return itNext.mpNode; + } + + bForceToLeft = false; + return position.mpNode; + } + } + + bForceToLeft = false; + return NULL; // The above specified hint was not useful, then we do a regular insertion. + } + + if(mnSize && compare(extractKey(((node_type*)mAnchor.mpNodeRight)->mValue), key)) + { + EASTL_VALIDATE_COMPARE(!compare(key, extractKey(((node_type*)mAnchor.mpNodeRight)->mValue))); // Validate that the compare function is sane. + bForceToLeft = false; + return (node_type*)mAnchor.mpNodeRight; + } + + bForceToLeft = false; + return NULL; // The caller can do a default insert. + } + + + template + typename rbtree::node_type* + rbtree::DoGetKeyInsertionPositionNonuniqueKeysHint(const_iterator position, bool& bForceToLeft, const key_type& key) + { + extract_key extractKey; + + if((position.mpNode != mAnchor.mpNodeRight) && (position.mpNode != &mAnchor)) // If the user specified a specific insertion position... + { + iterator itNext(position.mpNode); + ++itNext; + + // To consider: Change this so that 'position' specifies the position after + // where the insertion goes and not the position before where the insertion goes. + // Doing so would make this more in line with user expectations and with LWG #233. + if(!compare(key, extractKey(position.mpNode->mValue)) && // If value >= *position && + !compare(extractKey(itNext.mpNode->mValue), key)) // if value <= *itNext... + { + if(position.mpNode->mpNodeRight) // If there are any nodes to the right... [this expression will always be true as long as we aren't at the end()] + { + bForceToLeft = true; // Specifically insert in front of (to the left of) itNext (and thus after 'position'). + return itNext.mpNode; + } + + bForceToLeft = false; + return position.mpNode; + } + + bForceToLeft = false; + return NULL; // The above specified hint was not useful, then we do a regular insertion. + } + + // This pathway shouldn't be commonly executed, as the user shouldn't be calling + // this hinted version of insert if the user isn't providing a useful hint. + if(mnSize && !compare(key, extractKey(((node_type*)mAnchor.mpNodeRight)->mValue))) // If we are non-empty and the value is >= the last node... + { + bForceToLeft =false; + return (node_type*)mAnchor.mpNodeRight; + } + + bForceToLeft = false; + return NULL; + } + + template + template + typename rbtree::iterator + rbtree::DoInsertValueHint(true_type, const_iterator position, Args&&... args) // true_type means keys are unique. + { + // This is the pathway for insertion of unique keys (map and set, but not multimap and multiset). + // + // We follow the same approach as SGI STL/STLPort and use the position as + // a forced insertion position for the value when possible. + + node_type* pNodeNew = DoCreateNode(eastl::forward(args)...); // Note that pNodeNew->mpLeft, mpRight, mpParent, will be uninitialized. + const key_type& key(extract_key{}(pNodeNew->mValue)); + + bool bForceToLeft; + node_type* pPosition = DoGetKeyInsertionPositionUniqueKeysHint(position, bForceToLeft, key); + + if (!pPosition) + { + bool canInsert; + pPosition = DoGetKeyInsertionPositionUniqueKeys(canInsert, key); + + if (!canInsert) + { + DoFreeNode(pNodeNew); + return iterator(pPosition); + } + + bForceToLeft = false; + } + + return DoInsertValueImpl(pPosition, bForceToLeft, key, pNodeNew); + } + + + template + template + typename rbtree::iterator + rbtree::DoInsertValueHint(false_type, const_iterator position, Args&&... args) // false_type means keys are not unique. + { + // This is the pathway for insertion of non-unique keys (multimap and multiset, but not map and set). + // + // We follow the same approach as SGI STL/STLPort and use the position as + // a forced insertion position for the value when possible. + + node_type* pNodeNew = DoCreateNode(eastl::forward(args)...); // Note that pNodeNew->mpLeft, mpRight, mpParent, will be uninitialized. + const key_type& key(extract_key{}(pNodeNew->mValue)); + + bool bForceToLeft; + node_type* pPosition = DoGetKeyInsertionPositionNonuniqueKeysHint(position, bForceToLeft, key); + + if (!pPosition) + { + pPosition = DoGetKeyInsertionPositionNonuniqueKeys(key); + bForceToLeft = false; + } + + return DoInsertValueImpl(pPosition, bForceToLeft, key, pNodeNew); + } + + + template + typename rbtree::iterator + rbtree::DoInsertValueHint(true_type, const_iterator position, value_type&& value) // true_type means keys are unique. + { + // This is the pathway for insertion of unique keys (map and set, but not multimap and multiset). + // + // We follow the same approach as SGI STL/STLPort and use the position as + // a forced insertion position for the value when possible. + + extract_key extractKey; + key_type key(extractKey(value)); + bool bForceToLeft; + node_type* pPosition = DoGetKeyInsertionPositionUniqueKeysHint(position, bForceToLeft, key); + + if(pPosition) + return DoInsertValueImpl(pPosition, bForceToLeft, key, eastl::move(value)); + else + return DoInsertValue(has_unique_keys_type(), eastl::move(value)).first; + } + + + template + typename rbtree::iterator + rbtree::DoInsertValueHint(false_type, const_iterator position, value_type&& value) // false_type means keys are not unique. + { + // This is the pathway for insertion of non-unique keys (multimap and multiset, but not map and set). + // + // We follow the same approach as SGI STL/STLPort and use the position as + // a forced insertion position for the value when possible. + extract_key extractKey; + key_type key(extractKey(value)); + bool bForceToLeft; + node_type* pPosition = DoGetKeyInsertionPositionNonuniqueKeysHint(position, bForceToLeft, key); + + if(pPosition) + return DoInsertValueImpl(pPosition, bForceToLeft, key, eastl::move(value)); + else + return DoInsertValue(has_unique_keys_type(), eastl::move(value)); + } + + + template + typename rbtree::iterator + rbtree::DoInsertKey(true_type, const_iterator position, const key_type& key) // true_type means keys are unique. + { + bool bForceToLeft; + node_type* pPosition = DoGetKeyInsertionPositionUniqueKeysHint(position, bForceToLeft, key); + + if(pPosition) + return DoInsertKeyImpl(pPosition, bForceToLeft, key); + else + return DoInsertKey(has_unique_keys_type(), key).first; + } + + + template + typename rbtree::iterator + rbtree::DoInsertKey(false_type, const_iterator position, const key_type& key) // false_type means keys are not unique. + { + // This is the pathway for insertion of non-unique keys (multimap and multiset, but not map and set). + // + // We follow the same approach as SGI STL/STLPort and use the position as + // a forced insertion position for the value when possible. + bool bForceToLeft; + node_type* pPosition = DoGetKeyInsertionPositionNonuniqueKeysHint(position, bForceToLeft, key); + + if(pPosition) + return DoInsertKeyImpl(pPosition, bForceToLeft, key); + else + return DoInsertKey(has_unique_keys_type(), key); // We are empty or we are inserting at the end. + } + + + template + typename rbtree::iterator + rbtree::DoInsertKeyImpl(node_type* pNodeParent, bool bForceToLeft, const key_type& key) + { + RBTreeSide side; + extract_key extractKey; + + // The reason we may want to have bForceToLeft == true is that pNodeParent->mValue and value may be equal. + // In that case it doesn't matter what side we insert on, except that the C++ LWG #233 improvement report + // suggests that we should use the insert hint position to force an ordering. So that's what we do. + if(bForceToLeft || (pNodeParent == &mAnchor) || compare(key, extractKey(pNodeParent->mValue))) + side = kRBTreeSideLeft; + else + side = kRBTreeSideRight; + + node_type* const pNodeNew = DoCreateNodeFromKey(key); // Note that pNodeNew->mpLeft, mpRight, mpParent, will be uninitialized. + RBTreeInsert(pNodeNew, pNodeParent, &mAnchor, side); + mnSize++; + + return iterator(pNodeNew); + } + + + template + void rbtree::insert(std::initializer_list ilist) + { + for(typename std::initializer_list::iterator it = ilist.begin(), itEnd = ilist.end(); it != itEnd; ++it) + DoInsertValue(has_unique_keys_type(), eastl::move(*it)); + } + + + template + template + void rbtree::insert(InputIterator first, InputIterator last) + { + for( ; first != last; ++first) + DoInsertValue(has_unique_keys_type(), *first); // Or maybe we should call 'insert(end(), *first)' instead. If the first-last range was sorted then this might make some sense. + } + + + template + inline void rbtree::clear() + { + // Erase the entire tree. DoNukeSubtree is not a + // conventional erase function, as it does no rebalancing. + DoNukeSubtree((node_type*)mAnchor.mpNodeParent); + reset_lose_memory(); + } + + + template + inline void rbtree::reset_lose_memory() + { + // The reset_lose_memory 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. + mAnchor.mpNodeRight = &mAnchor; + mAnchor.mpNodeLeft = &mAnchor; + mAnchor.mpNodeParent = NULL; + mAnchor.mColor = kRBTreeColorRed; + mnSize = 0; + } + + + template + inline typename rbtree::iterator + rbtree::erase(const_iterator position) + { + const iterator iErase(position.mpNode); + --mnSize; // Interleave this between the two references to itNext. We expect no exceptions to occur during the code below. + ++position; + RBTreeErase(iErase.mpNode, &mAnchor); + DoFreeNode(iErase.mpNode); + return iterator(position.mpNode); + } + + + template + typename rbtree::iterator + rbtree::erase(const_iterator first, const_iterator last) + { + // We expect that if the user means to clear the container, they will call clear. + if(EASTL_LIKELY((first.mpNode != mAnchor.mpNodeLeft) || (last.mpNode != &mAnchor))) // If (first != begin or last != end) ... + { + // Basic implementation: + while(first != last) + first = erase(first); + return iterator(first.mpNode); + + // Inlined implementation: + //size_type n = 0; + //while(first != last) + //{ + // const iterator itErase(first); + // ++n; + // ++first; + // RBTreeErase(itErase.mpNode, &mAnchor); + // DoFreeNode(itErase.mpNode); + //} + //mnSize -= n; + //return first; + } + + clear(); + return iterator((node_type*)&mAnchor); // Same as: return end(); + } + + + template + inline typename rbtree::reverse_iterator + rbtree::erase(const_reverse_iterator position) + { + return reverse_iterator(erase((++position).base())); + } + + + template + typename rbtree::reverse_iterator + rbtree::erase(const_reverse_iterator first, const_reverse_iterator last) + { + // Version which erases in order from first to last. + // difference_type i(first.base() - last.base()); + // while(i--) + // first = erase(first); + // return first; + + // Version which erases in order from last to first, but is slightly more efficient: + return reverse_iterator(erase((++last).base(), (++first).base())); + } + + + template + inline void rbtree::erase(const key_type* first, const key_type* last) + { + // We have no choice but to run a loop like this, as the first/last range could + // have values that are discontiguously located in the tree. And some may not + // even be in the tree. + while(first != last) + erase(*first++); + } + + + template + typename rbtree::iterator + rbtree::find(const key_type& key) + { + // To consider: Implement this instead via calling lower_bound and + // inspecting the result. The following is an implementation of this: + // const iterator it(lower_bound(key)); + // return ((it.mpNode == &mAnchor) || compare(key, extractKey(it.mpNode->mValue))) ? iterator(&mAnchor) : it; + // We don't currently implement the above because in practice people tend to call + // find a lot with trees, but very uncommonly call lower_bound. + extract_key extractKey; + + node_type* pCurrent = (node_type*)mAnchor.mpNodeParent; // Start with the root node. + node_type* pRangeEnd = (node_type*)&mAnchor; // Set it to the container end for now. + + while(EASTL_LIKELY(pCurrent)) // Do a walk down the tree. + { + if(EASTL_LIKELY(!compare(extractKey(pCurrent->mValue), key))) // If pCurrent is >= key... + { + pRangeEnd = pCurrent; + pCurrent = (node_type*)pCurrent->mpNodeLeft; + } + else + { + EASTL_VALIDATE_COMPARE(!compare(key, extractKey(pCurrent->mValue))); // Validate that the compare function is sane. + pCurrent = (node_type*)pCurrent->mpNodeRight; + } + } + + if(EASTL_LIKELY((pRangeEnd != &mAnchor) && !compare(key, extractKey(pRangeEnd->mValue)))) + return iterator(pRangeEnd); + return iterator((node_type*)&mAnchor); + } + + + template + inline typename rbtree::const_iterator + rbtree::find(const key_type& key) const + { + typedef rbtree rbtree_type; + return const_iterator(const_cast(this)->find(key)); + } + + + template + template + typename rbtree::iterator + rbtree::find_as(const U& u, Compare2 compare2) + { + extract_key extractKey; + + node_type* pCurrent = (node_type*)mAnchor.mpNodeParent; // Start with the root node. + node_type* pRangeEnd = (node_type*)&mAnchor; // Set it to the container end for now. + + while(EASTL_LIKELY(pCurrent)) // Do a walk down the tree. + { + if(EASTL_LIKELY(!compare2(extractKey(pCurrent->mValue), u))) // If pCurrent is >= u... + { + pRangeEnd = pCurrent; + pCurrent = (node_type*)pCurrent->mpNodeLeft; + } + else + { + EASTL_VALIDATE_COMPARE(!compare2(u, extractKey(pCurrent->mValue))); // Validate that the compare function is sane. + pCurrent = (node_type*)pCurrent->mpNodeRight; + } + } + + if(EASTL_LIKELY((pRangeEnd != &mAnchor) && !compare2(u, extractKey(pRangeEnd->mValue)))) + return iterator(pRangeEnd); + return iterator((node_type*)&mAnchor); + } + + + template + template + inline typename rbtree::const_iterator + rbtree::find_as(const U& u, Compare2 compare2) const + { + typedef rbtree rbtree_type; + return const_iterator(const_cast(this)->find_as(u, compare2)); + } + + + template + typename rbtree::iterator + rbtree::lower_bound(const key_type& key) + { + extract_key extractKey; + + node_type* pCurrent = (node_type*)mAnchor.mpNodeParent; // Start with the root node. + node_type* pRangeEnd = (node_type*)&mAnchor; // Set it to the container end for now. + + while(EASTL_LIKELY(pCurrent)) // Do a walk down the tree. + { + if(EASTL_LIKELY(!compare(extractKey(pCurrent->mValue), key))) // If pCurrent is >= key... + { + pRangeEnd = pCurrent; + pCurrent = (node_type*)pCurrent->mpNodeLeft; + } + else + { + EASTL_VALIDATE_COMPARE(!compare(key, extractKey(pCurrent->mValue))); // Validate that the compare function is sane. + pCurrent = (node_type*)pCurrent->mpNodeRight; + } + } + + return iterator(pRangeEnd); + } + + + template + inline typename rbtree::const_iterator + rbtree::lower_bound(const key_type& key) const + { + typedef rbtree rbtree_type; + return const_iterator(const_cast(this)->lower_bound(key)); + } + + + template + typename rbtree::iterator + rbtree::upper_bound(const key_type& key) + { + extract_key extractKey; + + node_type* pCurrent = (node_type*)mAnchor.mpNodeParent; // Start with the root node. + node_type* pRangeEnd = (node_type*)&mAnchor; // Set it to the container end for now. + + while(EASTL_LIKELY(pCurrent)) // Do a walk down the tree. + { + if(EASTL_LIKELY(compare(key, extractKey(pCurrent->mValue)))) // If key is < pCurrent... + { + EASTL_VALIDATE_COMPARE(!compare(extractKey(pCurrent->mValue), key)); // Validate that the compare function is sane. + pRangeEnd = pCurrent; + pCurrent = (node_type*)pCurrent->mpNodeLeft; + } + else + pCurrent = (node_type*)pCurrent->mpNodeRight; + } + + return iterator(pRangeEnd); + } + + + template + inline typename rbtree::const_iterator + rbtree::upper_bound(const key_type& key) const + { + typedef rbtree rbtree_type; + return const_iterator(const_cast(this)->upper_bound(key)); + } + + + // To do: Move this validate function entirely to a template-less implementation. + template + bool rbtree::validate() const + { + // Red-black trees have the following canonical properties which we validate here: + // 1 Every node is either red or black. + // 2 Every leaf (NULL) is black by defintion. Any number of black nodes may appear in a sequence. + // 3 If a node is red, then both its children are black. Thus, on any path from + // the root to a leaf, red nodes must not be adjacent. + // 4 Every simple path from a node to a descendant leaf contains the same number of black nodes. + // 5 The mnSize member of the tree must equal the number of nodes in the tree. + // 6 The tree is sorted as per a conventional binary tree. + // 7 The comparison function is sane; it obeys strict weak ordering. If compare(a,b) is true, then compare(b,a) must be false. Both cannot be true. + + extract_key extractKey; + + if(mnSize) + { + // Verify basic integrity. + //if(!mAnchor.mpNodeParent || (mAnchor.mpNodeLeft == mAnchor.mpNodeRight)) + // return false; // Fix this for case of empty tree. + + if(mAnchor.mpNodeLeft != RBTreeGetMinChild(mAnchor.mpNodeParent)) + return false; + + if(mAnchor.mpNodeRight != RBTreeGetMaxChild(mAnchor.mpNodeParent)) + return false; + + const size_t nBlackCount = RBTreeGetBlackCount(mAnchor.mpNodeParent, mAnchor.mpNodeLeft); + size_type nIteratedSize = 0; + + for(const_iterator it = begin(); it != end(); ++it, ++nIteratedSize) + { + const node_type* const pNode = (const node_type*)it.mpNode; + const node_type* const pNodeRight = (const node_type*)pNode->mpNodeRight; + const node_type* const pNodeLeft = (const node_type*)pNode->mpNodeLeft; + + // Verify #7 above. + if(pNodeRight && compare(extractKey(pNodeRight->mValue), extractKey(pNode->mValue)) && compare(extractKey(pNode->mValue), extractKey(pNodeRight->mValue))) // Validate that the compare function is sane. + return false; + + // Verify #7 above. + if(pNodeLeft && compare(extractKey(pNodeLeft->mValue), extractKey(pNode->mValue)) && compare(extractKey(pNode->mValue), extractKey(pNodeLeft->mValue))) // Validate that the compare function is sane. + return false; + + // Verify item #1 above. + if((pNode->mColor != kRBTreeColorRed) && (pNode->mColor != kRBTreeColorBlack)) + return false; + + // Verify item #3 above. + if(pNode->mColor == kRBTreeColorRed) + { + if((pNodeRight && (pNodeRight->mColor == kRBTreeColorRed)) || + (pNodeLeft && (pNodeLeft->mColor == kRBTreeColorRed))) + return false; + } + + // Verify item #6 above. + if(pNodeRight && compare(extractKey(pNodeRight->mValue), extractKey(pNode->mValue))) + return false; + + if(pNodeLeft && compare(extractKey(pNode->mValue), extractKey(pNodeLeft->mValue))) + return false; + + if(!pNodeRight && !pNodeLeft) // If we are at a bottom node of the tree... + { + // Verify item #4 above. + if(RBTreeGetBlackCount(mAnchor.mpNodeParent, pNode) != nBlackCount) + return false; + } + } + + // Verify item #5 above. + if(nIteratedSize != mnSize) + return false; + + return true; + } + else + { + if((mAnchor.mpNodeLeft != &mAnchor) || (mAnchor.mpNodeRight != &mAnchor)) + return false; + } + + return true; + } + + + template + inline int rbtree::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; + } + + + template + inline typename rbtree::node_type* + rbtree::DoAllocateNode() + { + auto* pNode = (node_type*)allocate_memory(mAllocator, sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); + EASTL_ASSERT_MSG(pNode != nullptr, "the behaviour of eastl::allocators that return nullptr is not defined."); + + return pNode; + } + + + template + inline void rbtree::DoFreeNode(node_type* pNode) + { + pNode->~node_type(); + EASTLFree(mAllocator, pNode, sizeof(node_type)); + } + + + template + typename rbtree::node_type* + rbtree::DoCreateNodeFromKey(const key_type& key) + { + // Note that this function intentionally leaves the node pointers uninitialized. + // The caller would otherwise just turn right around and modify them, so there's + // no point in us initializing them to anything (except in a debug build). + node_type* const pNode = DoAllocateNode(); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new (eastl::addressof(pNode->mValue)) value_type(pair_first_construct, key); + + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeNode(pNode); + throw; + } + #endif + + #if EASTL_DEBUG + pNode->mpNodeRight = NULL; + pNode->mpNodeLeft = NULL; + pNode->mpNodeParent = NULL; + pNode->mColor = kRBTreeColorBlack; + #endif + + return pNode; + } + + + template + typename rbtree::node_type* + rbtree::DoCreateNode(const value_type& value) + { + // Note that this function intentionally leaves the node pointers uninitialized. + // The caller would otherwise just turn right around and modify them, so there's + // no point in us initializing them to anything (except in a debug build). + node_type* const pNode = DoAllocateNode(); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new(eastl::addressof(pNode->mValue)) value_type(value); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeNode(pNode); + throw; + } + #endif + + #if EASTL_DEBUG + pNode->mpNodeRight = NULL; + pNode->mpNodeLeft = NULL; + pNode->mpNodeParent = NULL; + pNode->mColor = kRBTreeColorBlack; + #endif + + return pNode; + } + + + template + typename rbtree::node_type* + rbtree::DoCreateNode(value_type&& value) + { + // Note that this function intentionally leaves the node pointers uninitialized. + // The caller would otherwise just turn right around and modify them, so there's + // no point in us initializing them to anything (except in a debug build). + node_type* const pNode = DoAllocateNode(); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new(eastl::addressof(pNode->mValue)) value_type(eastl::move(value)); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeNode(pNode); + throw; + } + #endif + + #if EASTL_DEBUG + pNode->mpNodeRight = NULL; + pNode->mpNodeLeft = NULL; + pNode->mpNodeParent = NULL; + pNode->mColor = kRBTreeColorBlack; + #endif + + return pNode; + } + + + template + template + typename rbtree::node_type* + rbtree::DoCreateNode(Args&&... args) + { + // Note that this function intentionally leaves the node pointers uninitialized. + // The caller would otherwise just turn right around and modify them, so there's + // no point in us initializing them to anything (except in a debug build). + node_type* const pNode = DoAllocateNode(); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + ::new(eastl::addressof(pNode->mValue)) value_type(eastl::forward(args)...); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoFreeNode(pNode); + throw; + } + #endif + + #if EASTL_DEBUG + pNode->mpNodeRight = NULL; + pNode->mpNodeLeft = NULL; + pNode->mpNodeParent = NULL; + pNode->mColor = kRBTreeColorBlack; + #endif + + return pNode; + } + + + template + typename rbtree::node_type* + rbtree::DoCreateNode(const node_type* pNodeSource, node_type* pNodeParent) + { + node_type* const pNode = DoCreateNode(pNodeSource->mValue); + + pNode->mpNodeRight = NULL; + pNode->mpNodeLeft = NULL; + pNode->mpNodeParent = pNodeParent; + pNode->mColor = pNodeSource->mColor; + + return pNode; + } + + + template + typename rbtree::node_type* + rbtree::DoCopySubtree(const node_type* pNodeSource, node_type* pNodeDest) + { + node_type* const pNewNodeRoot = DoCreateNode(pNodeSource, pNodeDest); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + // Copy the right side of the tree recursively. + if(pNodeSource->mpNodeRight) + pNewNodeRoot->mpNodeRight = DoCopySubtree((const node_type*)pNodeSource->mpNodeRight, pNewNodeRoot); + + node_type* pNewNodeLeft; + + for(pNodeSource = (node_type*)pNodeSource->mpNodeLeft, pNodeDest = pNewNodeRoot; + pNodeSource; + pNodeSource = (node_type*)pNodeSource->mpNodeLeft, pNodeDest = pNewNodeLeft) + { + pNewNodeLeft = DoCreateNode(pNodeSource, pNodeDest); + + pNodeDest->mpNodeLeft = pNewNodeLeft; + + // Copy the right side of the tree recursively. + if(pNodeSource->mpNodeRight) + pNewNodeLeft->mpNodeRight = DoCopySubtree((const node_type*)pNodeSource->mpNodeRight, pNewNodeLeft); + } + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + DoNukeSubtree(pNewNodeRoot); + throw; + } + #endif + + return pNewNodeRoot; + } + + + template + void rbtree::DoNukeSubtree(node_type* pNode) + { + while(pNode) // Recursively traverse the tree and destroy items as we go. + { + DoNukeSubtree((node_type*)pNode->mpNodeRight); + + node_type* const pNodeLeft = (node_type*)pNode->mpNodeLeft; + DoFreeNode(pNode); + pNode = pNodeLeft; + } + } + + + + /////////////////////////////////////////////////////////////////////// + // global operators + /////////////////////////////////////////////////////////////////////// + + template + inline bool operator==(const rbtree& a, const rbtree& b) + { + return (a.size() == b.size()) && eastl::equal(a.begin(), a.end(), b.begin()); + } + + + // Note that in operator< we do comparisons based on the tree value_type with operator<() of the + // value_type instead of the tree's Compare function. For set/multiset, the value_type is T, while + // for map/multimap the value_type is a pair. operator< for pair can be seen by looking + // utility.h, but it basically is uses the operator< for pair.first and pair.second. The C++ standard + // appears to require this behaviour, whether intentionally or not. If anything, a good reason to do + // this is for consistency. A map and a vector that contain the same items should compare the same. + template + inline bool operator<(const rbtree& a, const rbtree& b) + { + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + + template + inline bool operator!=(const rbtree& a, const rbtree& b) + { + return !(a == b); + } + + + template + inline bool operator>(const rbtree& a, const rbtree& b) + { + return b < a; + } + + + template + inline bool operator<=(const rbtree& a, const rbtree& b) + { + return !(b < a); + } + + + template + inline bool operator>=(const rbtree& a, const rbtree& b) + { + return !(a < b); + } + + + template + inline void swap(rbtree& a, rbtree& b) + { + a.swap(b); + } + + +} // namespace eastl + + +EA_RESTORE_VC_WARNING(); + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/internal/smart_ptr.h b/lib/EASTL/include/EASTL/internal/smart_ptr.h new file mode 100644 index 000000000..8a37950fa --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/smart_ptr.h @@ -0,0 +1,267 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_SMART_PTR_H +#define EASTL_INTERNAL_SMART_PTR_H + + +#include +#include +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +namespace eastl +{ + + namespace Internal + { + // Tells if the Deleter type has a typedef for pointer to T. If so then return it, + // else return T*. The large majority of the time the pointer type will be T*. + // The C++11 Standard requires that scoped_ptr let the deleter define the pointer type. + // + // Example usage: + // typedef typename unique_pointer_type::type pointer + // + template + class unique_pointer_type + { + template + static typename U::pointer test(typename U::pointer*); + + template + static T* test(...); + + public: + typedef decltype(test::type>(0)) type; + }; + + + /////////////////////////////////////////////////////////////////////// + // is_array_cv_convertible + // + // Tells if the array pointer P1 is cv-convertible to array pointer P2. + // The two types have two be equivalent pointer types and be convertible + // when you consider const/volatile properties of them. + // + // Example usage: + // is_array_cv_convertible::value => false + // is_array_cv_convertible::value => false + // is_array_cv_convertible::value => false + // is_array_cv_convertible::value => false + // is_array_cv_convertible::value => false + // is_array_cv_convertible::value => true + // is_array_cv_convertible::value => true + // is_array_cv_convertible::value => true + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_array_cv_convertible_CONFORMANCE 1 + + template ::element_type>, + eastl::remove_cv_t::element_type>>> + struct is_array_cv_convertible_impl + : public eastl::is_convertible {}; // Return true if P1 is convertible to P2. + + template + struct is_array_cv_convertible_impl + : public eastl::false_type {}; // P1's underlying type is not the same as P2's, so it can't be converted, even if P2 refers to a subclass of P1. Parent == Child, but Parent[] != Child[] + + template && !eastl::is_pointer_v> + struct is_array_cv_convertible + : public is_array_cv_convertible_impl {}; + + template + struct is_array_cv_convertible + : public eastl::false_type {}; // P1 is scalar not a pointer, so it can't be converted to a pointer. + + + /////////////////////////////////////////////////////////////////////// + // is_derived + // + // Given two (possibly identical) types Base and Derived, is_base_of::value == true + // if and only if Base is a direct or indirect base class of Derived. This is like is_base_of + // but returns false if Derived is the same as Base. So is_derived is true only if Derived is actually a subclass + // of Base and not Base itself. + // + // is_derived may only be applied to complete types. + // + // Example usage: + // is_derived::value => false + // is_derived::value => false + // is_derived::value => true + // is_derived::value => false + /////////////////////////////////////////////////////////////////////// + + #if EASTL_TYPE_TRAIT_is_base_of_CONFORMANCE + #define EASTL_TYPE_TRAIT_is_derived_CONFORMANCE 1 + + template + struct is_derived : public eastl::integral_constant::value && !eastl::is_same::type, typename eastl::remove_cv::type>::value> {}; + #else + #define EASTL_TYPE_TRAIT_is_derived_CONFORMANCE 0 + + template // This returns true if Derived is unrelated to Base. That's a wrong answer, but is better for us than returning false for compilers that don't support is_base_of. + struct is_derived : public eastl::integral_constant::type, typename eastl::remove_cv::type>::value> {}; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_safe_array_conversion + // + // Say you have two array types: T* t and U* u. You want to assign the u to t but only if + // that's a safe thing to do. As shown in the logic below, the array conversion + // is safe if U* and T* are convertible, if U is an array, and if either U or T is not + // a pointer or U is not derived from T. + // + // Note: Usage of this class could be replaced with is_array_cv_convertible usage. + // To do: Do this replacement and test it. + // + /////////////////////////////////////////////////////////////////////// + + template + struct is_safe_array_conversion : public eastl::integral_constant::value && + eastl::is_array::value && + (!eastl::is_pointer::value || !is_pointer::value || !Internal::is_derived::type>::value)> {}; + + } // namespace Internal + + + + + + + + /// default_delete + /// + /// C++11 smart pointer default delete function class. + /// + /// Provides a default way to delete an object. This default is simply to call delete on the + /// object pointer. You can provide an alternative to this class or you can override this on + /// a class-by-class basis like the following: + /// template <> + /// struct smart_ptr_deleter + /// { + /// void operator()(MyClass* p) const + /// { SomeCustomFunction(p); } + /// }; + /// + template + struct default_delete + { + #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION <= 4006) // GCC prior to 4.7 has a bug with noexcept here. + EA_CONSTEXPR default_delete() = default; + #else + EA_CONSTEXPR default_delete() EA_NOEXCEPT = default; + #endif + + template // Enable if T* can be constructed with U* (i.e. U* is convertible to T*). + default_delete(const default_delete&, typename eastl::enable_if::value>::type* = 0) EA_NOEXCEPT {} + + void operator()(T* p) const EA_NOEXCEPT + { + static_assert(eastl::internal::is_complete_type_v, "Attempting to call the destructor of an incomplete type"); + delete p; + } + }; + + + template + struct default_delete // Specialization for arrays. + { + #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION <= 4006) // GCC prior to 4.7 has a bug with noexcept here. + EA_CONSTEXPR default_delete() = default; + #else + EA_CONSTEXPR default_delete() EA_NOEXCEPT = default; + #endif + + template // This ctor is enabled if T is equal to or a base of U, and if U is less or equal const/volatile-qualified than T. + default_delete(const default_delete&, typename eastl::enable_if::value>::type* = 0) EA_NOEXCEPT {} + + void operator()(T* p) const EA_NOEXCEPT + { delete[] p; } + }; + + + + + /// smart_ptr_deleter + /// + /// Deprecated in favor of the C++11 name: default_delete + /// + template + struct smart_ptr_deleter + { + typedef T value_type; + + void operator()(const value_type* p) const // We use a const argument type in order to be most flexible with what types we accept. + { delete const_cast(p); } + }; + + template <> + struct smart_ptr_deleter + { + typedef void value_type; + + void operator()(const void* p) const + { delete[] (char*)p; } // We don't seem to have much choice but to cast to a scalar type. + }; + + template <> + struct smart_ptr_deleter + { + typedef void value_type; + + void operator()(const void* p) const + { delete[] (char*)p; } // We don't seem to have much choice but to cast to a scalar type. + }; + + + + /// smart_array_deleter + /// + /// Deprecated in favor of the C++11 name: default_delete + /// + template + struct smart_array_deleter + { + typedef T value_type; + + void operator()(const value_type* p) const // We use a const argument type in order to be most flexible with what types we accept. + { delete[] const_cast(p); } + }; + + template <> + struct smart_array_deleter + { + typedef void value_type; + + void operator()(const void* p) const + { delete[] (char*)p; } // We don't seem to have much choice but to cast to a scalar type. + }; + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/internal/thread_support.h b/lib/EASTL/include/EASTL/internal/thread_support.h new file mode 100644 index 000000000..60272a986 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/thread_support.h @@ -0,0 +1,246 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_THREAD_SUPPORT_H +#define EASTL_INTERNAL_THREAD_SUPPORT_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif +#include + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// NOTE(rparolin): We need a fallback mutex implementation because the Microsoft implementation +// of std::mutex can not be included in managed-cpp code. +// +// fatal error C1189: is not supported when compiling with /clr or /clr:pure +///////////////////////////////////////////////////////////////////////////////////////////////////// +#if !defined(EASTL_CPP11_MUTEX_ENABLED) + #if defined(EA_HAVE_CPP11_MUTEX) && !defined(EA_COMPILER_MANAGED_CPP) + #define EASTL_CPP11_MUTEX_ENABLED 1 + #else + #define EASTL_CPP11_MUTEX_ENABLED 0 + #endif +#endif + +#if EASTL_CPP11_MUTEX_ENABLED + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() +#endif + +#if defined(EA_PLATFORM_MICROSOFT) + // Cannot include Windows headers in our headers, as they kill builds with their #defines. +#elif defined(EA_PLATFORM_POSIX) + #include +#endif + +// copy constructor could not be generated because a base class copy constructor is inaccessible or deleted. +// assignment operator could not be generated because a base class assignment operator is inaccessible or deleted. +// non dll-interface class used as base for DLL-interface classkey 'identifier'. +EA_DISABLE_VC_WARNING(4625 4626 4275); + + +#if defined(EA_PLATFORM_MICROSOFT) + #if defined(EA_PROCESSOR_POWERPC) + extern "C" long __stdcall _InterlockedIncrement(long volatile* Addend); + #pragma intrinsic (_InterlockedIncrement) + + extern "C" long __stdcall _InterlockedDecrement(long volatile* Addend); + #pragma intrinsic (_InterlockedDecrement) + + extern "C" long __stdcall _InterlockedCompareExchange(long volatile* Dest, long Exchange, long Comp); + #pragma intrinsic (_InterlockedCompareExchange) + #else + extern "C" long _InterlockedIncrement(long volatile* Addend); + #pragma intrinsic (_InterlockedIncrement) + + extern "C" long _InterlockedDecrement(long volatile* Addend); + #pragma intrinsic (_InterlockedDecrement) + + extern "C" long _InterlockedCompareExchange(long volatile* Dest, long Exchange, long Comp); + #pragma intrinsic (_InterlockedCompareExchange) + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EASTL_THREAD_SUPPORT_AVAILABLE +// +// Defined as 0 or 1, based on existing support. +// Identifies if thread support (e.g. atomics, mutexes) is available for use. +// The large majority of EASTL doesn't use thread support, but a few parts +// of it (e.g. shared_ptr) do. +/////////////////////////////////////////////////////////////////////////////// + +#if !defined(EASTL_THREAD_SUPPORT_AVAILABLE) + #if defined(__clang__) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003)) + #define EASTL_THREAD_SUPPORT_AVAILABLE 1 + #elif defined(EA_COMPILER_MSVC) + #define EASTL_THREAD_SUPPORT_AVAILABLE 1 + #else + #define EASTL_THREAD_SUPPORT_AVAILABLE 0 + #endif +#endif + + +namespace eastl +{ + namespace Internal + { + /// atomic_increment + /// Returns the new value. + inline int32_t atomic_increment(int32_t* p32) EA_NOEXCEPT + { + #if defined(__clang__) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003)) + return __sync_add_and_fetch(p32, 1); + #elif defined(EA_COMPILER_MSVC) + static_assert(sizeof(long) == sizeof(int32_t), "unexpected size"); + return _InterlockedIncrement((volatile long*)p32); + #elif defined(EA_COMPILER_GNUC) + int32_t result; + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "=r" (result), "=m" (*p32) + : "0" (1), "m" (*p32) + : "memory" + ); + return result + 1; + #else + EASTL_FAIL_MSG("EASTL thread safety is not implemented yet. See EAThread for how to do this for the given platform."); + return ++*p32; + #endif + } + + /// atomic_decrement + /// Returns the new value. + inline int32_t atomic_decrement(int32_t* p32) EA_NOEXCEPT + { + #if defined(__clang__) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003)) + return __sync_add_and_fetch(p32, -1); + #elif defined(EA_COMPILER_MSVC) + return _InterlockedDecrement((volatile long*)p32); // volatile long cast is OK because int32_t == long on Microsoft platforms. + #elif defined(EA_COMPILER_GNUC) + int32_t result; + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "=r" (result), "=m" (*p32) + : "0" (-1), "m" (*p32) + : "memory" + ); + return result - 1; + #else + EASTL_FAIL_MSG("EASTL thread safety is not implemented yet. See EAThread for how to do this for the given platform."); + return --*p32; + #endif + } + + + /// atomic_compare_and_swap + /// Safely sets the value to a new value if the original value is equal to + /// a condition value. Returns true if the condition was met and the + /// assignment occurred. The comparison and value setting are done as + /// an atomic operation and thus another thread cannot intervene between + /// the two as would be the case with simple C code. + inline bool atomic_compare_and_swap(int32_t* p32, int32_t newValue, int32_t condition) + { + #if defined(__clang__) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003)) + return __sync_bool_compare_and_swap(p32, condition, newValue); + #elif defined(EA_COMPILER_MSVC) + return ((int32_t)_InterlockedCompareExchange((volatile long*)p32, (long)newValue, (long)condition) == condition); + #elif defined(EA_COMPILER_GNUC) + // GCC Inline ASM Constraints + // r <--> Any general purpose register + // a <--> The a register. + // 1 <--> The constraint '1' for operand 2 says that it must occupy the same location as operand 1. + // =a <--> output registers + // =r <--> output registers + + int32_t result; + __asm__ __volatile__( + "lock; cmpxchgl %3, (%1) \n" // Test *p32 against EAX, if same, then *p32 = newValue + : "=a" (result), "=r" (p32) // outputs + : "a" (condition), "r" (newValue), "1" (p32) // inputs + : "memory" // clobbered + ); + return result == condition; + #else + EASTL_FAIL_MSG("EASTL thread safety is not implemented yet. See EAThread for how to do this for the given platform."); + if(*p32 == condition) + { + *p32 = newValue; + return true; + } + return false; + #endif + } + + + // mutex + #if EASTL_CPP11_MUTEX_ENABLED + using std::mutex; + #else + class EASTL_API mutex + { + public: + mutex(); + ~mutex(); + + void lock(); + void unlock(); + + protected: + #if defined(EA_PLATFORM_MICROSOFT) + #if defined(_WIN64) + uint64_t mMutexBuffer[40 / sizeof(uint64_t)]; // CRITICAL_SECTION is 40 bytes on Win64. + #elif defined(_WIN32) + uint32_t mMutexBuffer[24 / sizeof(uint32_t)]; // CRITICAL_SECTION is 24 bytes on Win32. + #endif + #elif defined(EA_PLATFORM_POSIX) + pthread_mutex_t mMutex; + #endif + }; + #endif + + + // auto_mutex + class EASTL_API auto_mutex + { + public: + EA_FORCE_INLINE auto_mutex(mutex& mutex) : pMutex(&mutex) + { pMutex->lock(); } + + EA_FORCE_INLINE ~auto_mutex() + { pMutex->unlock(); } + + protected: + mutex* pMutex; + + auto_mutex(const auto_mutex&) = delete; + void operator=(const auto_mutex&) = delete; + }; + + + // shared_ptr_auto_mutex + class EASTL_API shared_ptr_auto_mutex : public auto_mutex + { + public: + shared_ptr_auto_mutex(const void* pSharedPtr); + + shared_ptr_auto_mutex(const shared_ptr_auto_mutex&) = delete; + void operator=(shared_ptr_auto_mutex&&) = delete; + }; + + + } // namespace Internal + +} // namespace eastl + + +EA_RESTORE_VC_WARNING(); + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/internal/tuple_fwd_decls.h b/lib/EASTL/include/EASTL/internal/tuple_fwd_decls.h new file mode 100644 index 000000000..a2c773cde --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/tuple_fwd_decls.h @@ -0,0 +1,56 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_TUPLE_FWD_DECLS_H +#define EASTL_TUPLE_FWD_DECLS_H + +#include + +#if EASTL_TUPLE_ENABLED + +namespace eastl +{ + template + class tuple; + + template + class tuple_size; + + template + class tuple_element; + + template + using tuple_element_t = typename tuple_element::type; + + // const typename for tuple_element_t, for when tuple or TupleImpl cannot itself be const + template + using const_tuple_element_t = typename conditional< + is_lvalue_reference>::value, + add_lvalue_reference_t>>, + const tuple_element_t + >::type; + + // get + template + tuple_element_t>& get(tuple& t); + + template + const_tuple_element_t>& get(const tuple& t); + + template + tuple_element_t>&& get(tuple&& t); + + template + T& get(tuple& t); + + template + const T& get(const tuple& t); + + template + T&& get(tuple&& t); +} + +#endif // EASTL_VARIADIC_TEMPLATES_ENABLED + +#endif // EASTL_TUPLE_FWD_DECLS_H diff --git a/lib/EASTL/include/EASTL/internal/type_compound.h b/lib/EASTL/include/EASTL/internal/type_compound.h new file mode 100644 index 000000000..1f8525097 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/type_compound.h @@ -0,0 +1,749 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_TYPE_COMPOUND_H +#define EASTL_INTERNAL_TYPE_COMPOUND_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + + +// Until we revise the code below to handle EDG warnings, we don't have much choice but to disable them. +#if defined(__EDG_VERSION__) + #pragma diag_suppress=1931 // operand of sizeof is not a type, variable, or dereferenced pointer expression +#endif + + +namespace eastl +{ + + /////////////////////////////////////////////////////////////////////// + // extent + // + // extent::value is an integral type representing the number of + // elements in the Ith dimension of array type T. + // + // For a given array type T[N], extent::value == N. + // For a given multi-dimensional array type T[M][N], extent::value == N. + // For a given multi-dimensional array type T[M][N], extent::value == M. + // For a given array type T and a given dimension I where I >= rank::value, extent::value == 0. + // For a given array type of unknown extent T[], extent::value == 0. + // For a given non-array type T and an arbitrary dimension I, extent::value == 0. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_extent_CONFORMANCE 1 // extent is conforming. + + template + struct extent_help : public eastl::integral_constant {}; + + template + struct extent_help : public eastl::integral_constant {}; + + template + struct extent_help : public eastl::extent_help { }; + + template + struct extent_help : public eastl::extent_help {}; + + template // extent uses unsigned instead of size_t. + struct extent : public eastl::extent_help { }; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR auto extent_v = extent::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_array + // + // is_array::value == true if and only if T is an array type, + // including unbounded array types. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_array_CONFORMANCE 1 // is_array is conforming; doesn't make mistakes. + + template + struct is_array : public eastl::false_type {}; + + template + struct is_array : public eastl::true_type {}; + + template + struct is_array : public eastl::true_type {}; + + #if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + EA_CONSTEXPR bool is_array_v = is_array::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_array_of_known_bounds + // + // Not part of the C++11 Standard. + // is_array_of_known_bounds::value is true if T is an array and is + // of known bounds. is_array_of_unknown_bounds::value == true, + // while is_array_of_unknown_bounds::value = false. + // + /////////////////////////////////////////////////////////////////////// + + template + struct is_array_of_known_bounds + : public eastl::integral_constant::value != 0> {}; + + + /////////////////////////////////////////////////////////////////////// + // is_array_of_unknown_bounds + // + // Not part of the C++11 Standard. + // is_array_of_unknown_bounds::value is true if T is an array but is + // of unknown bounds. is_array_of_unknown_bounds::value == false, + // while is_array_of_unknown_bounds::value = true. + // + /////////////////////////////////////////////////////////////////////// + + template + struct is_array_of_unknown_bounds + : public eastl::integral_constant::value && (eastl::extent::value == 0)> {}; + + + /////////////////////////////////////////////////////////////////////// + // is_member_function_pointer + // + // is_member_function_pointer::value == true if and only if T is a + // pointer to member function type. + // + /////////////////////////////////////////////////////////////////////// + // We detect member functions with 0 to N arguments. We can extend this + // for additional arguments if necessary. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_member_function_pointer_CONFORMANCE 1 // is_member_function_pointer is conforming; doesn't make mistakes. + + // To do: Revise this to support C++11 variadic templates when possible. + // To do: We can probably also use remove_cv to simply the multitude of types below. + + template struct is_mem_fun_pointer_value : public false_type{}; + + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + template struct is_mem_fun_pointer_value : public true_type{}; + + template + struct is_member_function_pointer : public integral_constant::value>{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_member_function_pointer_v = is_member_function_pointer::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_member_pointer + // + // is_member_pointer::value == true if and only if: + // is_member_object_pointer::value == true, or + // is_member_function_pointer::value == true + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_member_pointer_CONFORMANCE 1 // is_member_pointer is conforming; doesn't make mistakes. + + template + struct is_member_pointer + : public eastl::integral_constant::value>{}; + + template + struct is_member_pointer + : public eastl::true_type{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_member_pointer_v = is_member_pointer::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_member_object_pointer + // + // is_member_object_pointer::value == true if and only if T is a + // pointer to data member type. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_member_object_pointer_CONFORMANCE 1 // is_member_object_pointer is conforming; doesn't make mistakes. + + template + struct is_member_object_pointer : public eastl::integral_constant::value && + !eastl::is_member_function_pointer::value + > {}; + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_member_object_pointer_v = is_member_object_pointer::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_pointer + // + // is_pointer::value == true if and only if T is a pointer type. + // This category includes function pointer types, but not pointer to + // member types. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_pointer_CONFORMANCE 1 // is_pointer is conforming; doesn't make mistakes. + + template struct is_pointer_helper : public false_type{}; + + template struct is_pointer_helper : public true_type{}; + template struct is_pointer_helper : public true_type{}; + template struct is_pointer_helper : public true_type{}; + template struct is_pointer_helper : public true_type{}; + + template + struct is_pointer_value : public type_and::value, type_not::value>::value> {}; + + template + struct is_pointer : public integral_constant::value>{}; + + #if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + EA_CONSTEXPR bool is_pointer_v = is_pointer::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_convertible + // + // Given two (possible identical) types From and To, is_convertible::value == true + // if and only if an lvalue of type From can be implicitly converted to type To, + // or is_void::value == true + // + // An instance of the type predicate holds true if the expression To to = from;, where from is an object of type From, is well-formed. + // + // is_convertible may only be applied to complete types. + // Type To may not be an abstract type. + // If the conversion is ambiguous, the program is ill-formed. + // If either or both of From and To are class types, and the conversion would invoke + // non-public member functions of either From or To (such as a private constructor of To, + // or a private conversion operator of From), the program is ill-formed. + // + // Note that without compiler help, both is_convertible and is_base + // can produce compiler errors if the conversion is ambiguous. + // Example: + // struct A {}; + // struct B : A {}; + // struct C : A {}; + // struct D : B, C {}; + // is_convertible::value; // Generates compiler error. + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_convertible_to))) + #define EASTL_TYPE_TRAIT_is_convertible_CONFORMANCE 1 // is_convertible is conforming. + + // Problem: VC++ reports that int is convertible to short, yet if you construct a short from an int then VC++ generates a warning: + // warning C4242: 'initializing' : conversion from 'int' to 'short', possible loss of data. We can deal with this by making + // is_convertible be false for conversions that could result in loss of data. Or we could make another trait called is_lossless_convertible + // and use that appropriately in our code. Or we could put the onus on the user to work around such warnings. + template + struct is_convertible : public integral_constant{}; + + #else + #define EASTL_TYPE_TRAIT_is_convertible_CONFORMANCE 1 + + template::value || eastl::is_function::value || eastl::is_array::value > + struct is_convertible_helper // Anything is convertible to void. Nothing is convertible to a function or an array. + { static const bool value = eastl::is_void::value; }; + + template + class is_convertible_helper + { + template + static void ToFunction(To1); // We try to call this function with an instance of From. It is valid if From can be converted to To. + + template + static eastl::no_type is(...); + + template + static decltype(ToFunction(eastl::declval()), eastl::yes_type()) is(int); + + public: + static const bool value = sizeof(is(0)) == 1; + }; + + template + struct is_convertible + : public integral_constant::value> {}; + + #endif + + #if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + EA_CONSTEXPR bool is_convertible_v = is_convertible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_convertible + // + // https://en.cppreference.com/w/cpp/types/is_convertible + // + // template + // struct is_explicitly_convertible + // : public is_constructible {}; + /////////////////////////////////////////////////////////////////////// + // TODO(rparolin): implement type-trait + + + + /////////////////////////////////////////////////////////////////////// + // is_explicitly_convertible + // + // This sometime-seen extension trait is the same as is_constructible + // and so we don't define it. + // + // template + // struct is_explicitly_convertible + // : public is_constructible {}; + /////////////////////////////////////////////////////////////////////// + + + + /////////////////////////////////////////////////////////////////////// + // is_union + // + // is_union::value == true if and only if T is a union type. + // + // There is no way to tell if a type is a union without compiler help. + // As of this writing, only Metrowerks v8+ supports such functionality + // via 'msl::is_union::value'. The user can force something to be + // evaluated as a union via EASTL_DECLARE_UNION. + /////////////////////////////////////////////////////////////////////// + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_union))) + #define EASTL_TYPE_TRAIT_is_union_CONFORMANCE 1 // is_union is conforming. + + template + struct is_union : public integral_constant{}; + #else + #define EASTL_TYPE_TRAIT_is_union_CONFORMANCE 0 // is_union is not fully conforming. + + template struct is_union : public false_type{}; + #endif + + #define EASTL_DECLARE_UNION(T) namespace eastl{ template <> struct is_union : public true_type{}; template <> struct is_union : public true_type{}; } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_union_v = is_union::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_class + // + // is_class::value == true if and only if T is a class or struct + // type (and not a union type). + // + // Without specific compiler help, it is not possible to + // distinguish between unions and classes. As a result, is_class + // will erroneously evaluate to true for union types. + /////////////////////////////////////////////////////////////////////// + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_class))) + #define EASTL_TYPE_TRAIT_is_class_CONFORMANCE 1 // is_class is conforming. + + template + struct is_class : public integral_constant{}; + #elif defined(__EDG__) + #define EASTL_TYPE_TRAIT_is_class_CONFORMANCE EASTL_TYPE_TRAIT_is_union_CONFORMANCE + + typedef char yes_array_type[1]; + typedef char no_array_type[2]; + template static yes_array_type& is_class_helper(void (U::*)()); + template static no_array_type& is_class_helper(...); + + template + struct is_class : public integral_constant(0)) == sizeof(yes_array_type) && !is_union::value + >{}; + #elif !defined(__GNUC__) || (((__GNUC__ * 100) + __GNUC_MINOR__) >= 304) // Not GCC or GCC 3.4+ + #define EASTL_TYPE_TRAIT_is_class_CONFORMANCE EASTL_TYPE_TRAIT_is_union_CONFORMANCE + + template static yes_type is_class_helper(void (U::*)()); + template static no_type is_class_helper(...); + + template + struct is_class : public integral_constant(0)) == sizeof(yes_type) && !is_union::value + >{}; + #else + #define EASTL_TYPE_TRAIT_is_class_CONFORMANCE 0 // is_class is not fully conforming. + + // GCC 2.x version, due to GCC being broken. + template + struct is_class : public false_type{}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_class_v = is_class::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_polymorphic + // + // is_polymorphic::value == true if and only if T is a class or struct + // that declares or inherits a virtual function. is_polymorphic may only + // be applied to complete types. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_polymorphic))) + #define EASTL_TYPE_TRAIT_is_polymorphic_CONFORMANCE 1 // is_polymorphic is conforming. + + template + struct is_polymorphic : public integral_constant{}; + #else + #define EASTL_TYPE_TRAIT_is_polymorphic_CONFORMANCE 1 // is_polymorphic is conforming. + + template + struct is_polymorphic_imp1 + { + typedef typename remove_cv::type t; + + struct helper_1 : public t + { + helper_1(); + ~helper_1() throw(); + char pad[64]; + }; + + struct helper_2 : public t + { + helper_2(); + virtual ~helper_2() throw(); + #ifndef _MSC_VER + virtual void foo(); + #endif + char pad[64]; + }; + + static const bool value = (sizeof(helper_1) == sizeof(helper_2)); + }; + + template + struct is_polymorphic_imp2{ static const bool value = false; }; + + template + struct is_polymorphic_selector{ template struct rebind{ typedef is_polymorphic_imp2 type; }; }; + + template <> + struct is_polymorphic_selector{ template struct rebind{ typedef is_polymorphic_imp1 type; }; }; + + template + struct is_polymorphic_value{ + typedef is_polymorphic_selector::value> selector; + typedef typename selector::template rebind binder; + typedef typename binder::type imp_type; + static const bool value = imp_type::value; + }; + + template + struct is_polymorphic : public integral_constant::value>{}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_polymorphic_v = is_polymorphic::value; + #endif + + + + + /////////////////////////////////////////////////////////////////////// + // is_object + // + // is_object::value == true if and only if: + // is_reference::value == false, and + // is_function::value == false, and + // is_void::value == false + // + // The C++ standard, section 3.9p9, states: "An object type is a + // (possibly cv-qualified) type that is not a function type, not a + // reference type, and not incomplete (except for an incompletely + // defined object type). + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_object_CONFORMANCE (EASTL_TYPE_TRAIT_is_reference_CONFORMANCE && EASTL_TYPE_TRAIT_is_void_CONFORMANCE && EASTL_TYPE_TRAIT_is_function_CONFORMANCE) + + template + struct is_object : public integral_constant::value && !is_void::value && !is_function::value + >{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_object_v = is_object::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_scalar + // + // is_scalar::value == true if and only if: + // is_arithmetic::value == true, or + // is_enum::value == true, or + // is_pointer::value == true, or + // is_member_pointer::value == true, or + // is_null_pointer::value == true + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_scalar_CONFORMANCE 1 // is_scalar is conforming. + + template + struct is_scalar : public integral_constant::value || is_enum::value || is_pointer::value || + is_member_pointer::value || + is_null_pointer::value> {}; + + template struct is_scalar : public true_type {}; + template struct is_scalar : public true_type {}; + template struct is_scalar : public true_type {}; + template struct is_scalar : public true_type {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_scalar_v = is_scalar::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_compound + // + // Compound means anything but fundamental. See C++ standard, section 3.9.2. + // + // is_compound::value == true if and only if: + // is_fundamental::value == false + // + // Thus, is_compound::value == true if and only if: + // is_floating_point::value == false, and + // is_integral::value == false, and + // is_void::value == false + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_compound_CONFORMANCE EASTL_TYPE_TRAIT_is_fundamental_CONFORMANCE + + template + struct is_compound : public integral_constant::value>{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_compound_v = is_compound::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // decay + // + // Converts the type T to its decayed equivalent. That means doing + // lvalue to rvalue, array to pointer, function to pointer conversions, + // and removal of const and volatile. + // This is the type conversion silently applied by the compiler to + // all function arguments when passed by value. + + #define EASTL_TYPE_TRAIT_decay_CONFORMANCE 1 // decay is conforming. + + template + struct decay + { + typedef typename eastl::remove_reference::type U; + + typedef typename eastl::conditional< + eastl::is_array::value, + typename eastl::remove_extent::type*, + typename eastl::conditional< + eastl::is_function::value, + typename eastl::add_pointer::type, + typename eastl::remove_cv::type + >::type + >::type type; + }; + + + // decay_t is the C++14 using typedef for typename decay::type, though + // it requires only C++11 compiler functionality to implement. + // We provide a backwards-compatible means to access it through a macro for pre-C++11 compilers. + #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + #define EASTL_DECAY_T(T) typename decay::type + #else + template + using decay_t = typename decay::type; + #define EASTL_DECAY_T(T) decay_t + #endif + + + /////////////////////////////////////////////////////////////////////// + // common_type + // + // Determines the common type among all types T..., that is the type all T... + // can be implicitly converted to. + // + // It is intended that this be specialized by the user for cases where it + // is useful to do so. Example specialization: + // template + // struct common_type{ typedef MyBaseClassB type; }; + // + // The member typedef type shall be defined as set out in 20.9.7.6,p3. All types in + // the parameter pack T shall be complete or (possibly cv) void. A program may + // specialize this trait if at least one template parameter in the specialization + // is a user-defined type. Note: Such specializations are needed when only + // explicit conversions are desired among the template arguments. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_common_type_CONFORMANCE 1 // common_type is conforming. + + template + struct common_type; + + template + struct common_type + { typedef decay_t type; }; // Question: Should we use T or decay_t here? The C++11 Standard specifically (20.9.7.6,p3) specifies that it be without decay, but libc++ uses decay. + + template + struct common_type + { + typedef decay_t() : declval())> type; // The type of a tertiary expression is set by the compiler to be the common type of the two result types. + }; + + template + struct common_type + { typedef typename common_type::type, V...>::type type; }; + + + // common_type_t is the C++14 using typedef for typename common_type::type. + // We provide a backwards-compatible means to access it through a macro for pre-C++11 compilers. + #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + #define EASTL_COMMON_TYPE_T(...) typename common_type<__VA_ARGS__>::type + #else + template + using common_type_t = typename common_type::type; + #define EASTL_COMMON_TYPE_T(...) common_type_t<__VA_ARGS__> + #endif + + /////////////////////////////////////////////////////////////////////// + // is_final + /////////////////////////////////////////////////////////////////////// + #if EA_COMPILER_HAS_FEATURE(is_final) + template + struct is_final : public integral_constant {}; + #else + // no compiler support so we always return false + template + struct is_final : public false_type {}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_final_v = is_final::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_aggregate + // + // https://en.cppreference.com/w/cpp/language/aggregate_initialization + // + // An aggregate is one of the following types: + // * array type + // * class type (typically, struct or union), that has + // * no private or protected non-static data members + // * no user-provided constructors (explicitly defaulted or deleted constructors are allowed) + // * no user-provided, inherited, or explicit constructors + // * (explicitly defaulted or deleted constructors are allowed) + // * no virtual, private, or protected (since C++17) base classes + // * no virtual member functions + // * no default member initializers + // + /////////////////////////////////////////////////////////////////////// + #if EA_COMPILER_HAS_FEATURE(is_aggregate) || defined(_MSC_VER) && (_MSC_VER >= 1916) // VS2017 15.9+ + #define EASTL_TYPE_TRAIT_is_aggregate_CONFORMANCE 1 + + template + struct is_aggregate : public integral_constant {}; + #else + #define EASTL_TYPE_TRAIT_is_aggregate_CONFORMANCE 0 + + // no compiler support so we always return false + template + struct is_aggregate : public false_type {}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_aggregate_v = is_aggregate::value; + #endif +} // namespace eastl + + +#endif // Header include guard + + + + diff --git a/lib/EASTL/include/EASTL/internal/type_fundamental.h b/lib/EASTL/include/EASTL/internal/type_fundamental.h new file mode 100644 index 000000000..5ff9259eb --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/type_fundamental.h @@ -0,0 +1,342 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_TYPE_FUNDAMENTAL_H +#define EASTL_INTERNAL_TYPE_FUNDAMENTAL_H + + +#include +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +namespace eastl +{ + + + /////////////////////////////////////////////////////////////////////// + // is_void + // + // is_void::value == true if and only if T is one of the following types: + // [const][volatile] void + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_void_CONFORMANCE 1 // is_void is conforming. + + template struct is_void : public false_type{}; + + template <> struct is_void : public true_type{}; + template <> struct is_void : public true_type{}; + template <> struct is_void : public true_type{}; + template <> struct is_void : public true_type{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_void_v = is_void::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // has_void_arg + // + // utility which identifies if any of the given template arguments is void. + // + // TODO(rparolin): refactor with fold expressions when C++17 compilers are widely available. + /////////////////////////////////////////////////////////////////////// + + template + struct has_void_arg; + + template <> + struct has_void_arg<> + : public eastl::false_type {}; + + template + struct has_void_arg + { static const bool value = (eastl::is_void::value || eastl::has_void_arg::value); }; + + + /////////////////////////////////////////////////////////////////////// + // is_null_pointer + // + // C++14 type trait. Refers only to nullptr_t and not NULL (0). + // eastl::is_null_pointer::value == true + // eastl::is_null_pointer::value == true + // eastl::is_null_pointer::value == false + // eastl::is_null_pointer::value == [cannot compile] + // + /////////////////////////////////////////////////////////////////////// + + #if defined(EA_COMPILER_CPP11_ENABLED) && !defined(EA_COMPILER_NO_DECLTYPE) && !defined(_MSC_VER) // VC++'s handling of decltype(nullptr) is broken. + #define EASTL_TYPE_TRAIT_is_null_pointer_CONFORMANCE 1 + + template + struct is_null_pointer : public eastl::is_same::type, decltype(nullptr)> {}; // A C++11 compiler defines nullptr, but you need a C++11 standard library to declare std::nullptr_t. So it's safer to compare against decltype(nullptr) than to use std::nullptr_t, because we may have a C++11 compiler but C++98 library (happens with Apple frequently). + #else + #define EASTL_TYPE_TRAIT_is_null_pointer_CONFORMANCE 1 + + template + struct is_null_pointer : public eastl::is_same::type, std::nullptr_t> {}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_null_pointer_v = is_null_pointer::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_integral + // + // is_integral::value == true if and only if T is one of the following types: + // [const] [volatile] bool + // [const] [volatile] char + // [const] [volatile] signed char + // [const] [volatile] unsigned char + // [const] [volatile] wchar_t + // [const] [volatile] short + // [const] [volatile] int + // [const] [volatile] long + // [const] [volatile] long long + // [const] [volatile] unsigned short + // [const] [volatile] unsigned int + // [const] [volatile] unsigned long + // [const] [volatile] unsigned long long + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_integral_CONFORMANCE 1 // is_integral is conforming. + + template struct is_integral_helper : public false_type{}; + + template <> struct is_integral_helper : public true_type{}; + template <> struct is_integral_helper : public true_type{}; + template <> struct is_integral_helper : public true_type{}; + template <> struct is_integral_helper : public true_type{}; + template <> struct is_integral_helper : public true_type{}; + + template <> struct is_integral_helper : public true_type{}; + template <> struct is_integral_helper : public true_type{}; + template <> struct is_integral_helper : public true_type{}; + template <> struct is_integral_helper : public true_type{}; + template <> struct is_integral_helper : public true_type{}; + + template <> struct is_integral_helper : public true_type{}; + template <> struct is_integral_helper : public true_type{}; + #if defined(EA_CHAR16_NATIVE) && EA_CHAR16_NATIVE + template <> struct is_integral_helper : public true_type{}; + #endif + #if defined(EA_CHAR32_NATIVE) && EA_CHAR32_NATIVE + template <> struct is_integral_helper : public true_type{}; + #endif + #ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type which is already handled above... + template <> struct is_integral_helper : public true_type{}; + #endif + #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + template <> struct is_integral_helper<__int128_t> : public true_type{}; + template <> struct is_integral_helper<__uint128_t> : public true_type{}; + #endif + + template + struct is_integral : public eastl::is_integral_helper::type>{}; + + #define EASTL_DECLARE_INTEGRAL(T) \ + namespace eastl{ \ + template <> struct is_integral : public true_type{}; \ + template <> struct is_integral : public true_type{}; \ + template <> struct is_integral : public true_type{}; \ + template <> struct is_integral : public true_type{}; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_integral_v = is_integral::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_floating_point + // + // is_floating_point::value == true if and only if T is one of the following types: + // [const] [volatile] float + // [const] [volatile] double + // [const] [volatile] long double + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_floating_point_CONFORMANCE 1 // is_floating_point is conforming. + + template struct is_floating_point_helper : public false_type{}; + + template <> struct is_floating_point_helper : public true_type{}; + template <> struct is_floating_point_helper : public true_type{}; + template <> struct is_floating_point_helper : public true_type{}; + + template + struct is_floating_point : public eastl::is_floating_point_helper::type>{}; + + #define EASTL_DECLARE_FLOATING_POINT(T) \ + namespace eastl{ \ + template <> struct is_floating_point : public true_type{}; \ + template <> struct is_floating_point : public true_type{}; \ + template <> struct is_floating_point : public true_type{}; \ + template <> struct is_floating_point : public true_type{}; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_floating_point_v = is_floating_point::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_arithmetic + // + // is_arithmetic::value == true if and only if: + // is_floating_point::value == true, or + // is_integral::value == true + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_arithmetic_CONFORMANCE 1 // is_arithmetic is conforming. + + template + struct is_arithmetic + : public integral_constant::value || is_floating_point::value> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_arithmetic_v = is_arithmetic::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_fundamental + // + // is_fundamental::value == true if and only if: + // is_floating_point::value == true, or + // is_integral::value == true, or + // is_void::value == true + // is_null_pointer::value == true + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_fundamental_CONFORMANCE 1 // is_fundamental is conforming. + + template + struct is_fundamental + : public bool_constant || is_integral_v || is_floating_point_v || is_null_pointer_v> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_fundamental_v = is_fundamental::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_hat_type + // + // is_hat_type::value == true if and only if: + // underlying type is a C++/CX '^' type such as: Foo^ + // meaning the type is heap allocated and ref-counted + /////////////////////////////////////////////////////////////////////// + + template struct is_hat_type_helper : public false_type {}; + + #if (EABASE_VERSION_N > 20607 && defined(EA_COMPILER_WINRTCX_ENABLED)) || defined(__cplusplus_winrt) + template struct is_hat_type_helper : public true_type{}; + #endif + + template + struct is_hat_type : public eastl::is_hat_type_helper {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_hat_type_v = is_hat_type::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_enum + // + // is_enum::value == true if and only if T is an enumeration type. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_enum))) + #define EASTL_TYPE_TRAIT_is_enum_CONFORMANCE 1 // is_enum is conforming. + + template + struct is_enum : public integral_constant{}; + #else + #define EASTL_TYPE_TRAIT_is_enum_CONFORMANCE 1 // is_enum is conforming. + + struct int_convertible{ int_convertible(int); }; + + template + struct is_enum_helper { template struct nest : public is_convertible{}; }; + + template <> + struct is_enum_helper { template struct nest : public false_type {}; }; + + template + struct is_enum_helper2 + { + typedef type_or::value, is_reference::value, is_class::value> selector; + typedef is_enum_helper helper_t; + typedef typename add_reference::type ref_t; + typedef typename helper_t::template nest result; + }; + + template + struct is_enum : public integral_constant::result::value>{}; + + template <> struct is_enum : public false_type {}; + template <> struct is_enum : public false_type {}; + template <> struct is_enum : public false_type {}; + template <> struct is_enum : public false_type {}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_enum_v = is_enum::value; + #endif + + #define EASTL_DECLARE_ENUM(T) namespace eastl{ template <> struct is_enum : public true_type{}; template <> struct is_enum : public true_type{}; } + + + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/internal/type_pod.h b/lib/EASTL/include/EASTL/internal/type_pod.h new file mode 100644 index 000000000..998e95744 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/type_pod.h @@ -0,0 +1,1945 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_TYPE_POD_H +#define EASTL_INTERNAL_TYPE_POD_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include + +namespace eastl +{ + /////////////////////////////////////////////////////////////////////// + // is_empty + // + // is_empty::value == true if and only if T is an empty class or struct. + // is_empty may only be applied to complete types. + // + // is_empty cannot be used with union types until is_union can be made to work. + /////////////////////////////////////////////////////////////////////// + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_empty))) + #define EASTL_TYPE_TRAIT_is_empty_CONFORMANCE 1 // is_empty is conforming. + + template + struct is_empty : public integral_constant{}; + #else + #define EASTL_TYPE_TRAIT_is_empty_CONFORMANCE 1 // is_empty is fully conforming. + + template + struct is_empty_helper_t1 : public T { char m[64]; }; + struct is_empty_helper_t2 { char m[64]; }; + + // The inheritance in empty_helper_t1 will not work with non-class types + template + struct is_empty_helper : public eastl::false_type{}; + + template + struct is_empty_helper : public eastl::integral_constant) == sizeof(is_empty_helper_t2) + >{}; + + template + struct is_empty_helper2 + { + typedef typename eastl::remove_cv::type _T; + typedef eastl::is_empty_helper<_T, eastl::is_class<_T>::value> type; + }; + + template + struct is_empty : public eastl::is_empty_helper2::type {}; + #endif + + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_empty_v = is_empty::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_pod + // + // is_pod::value == true if and only if, for a given type T: + // - is_scalar::value == true, or + // - T is a class or struct that has no user-defined copy assignment + // operator or destructor, and T has no non-static data members M for + // which is_pod::value == false, and no members of reference type, or + // - T is the type of an array of objects E for which is_pod::value == true + // + // is_pod may only be applied to complete types. + // + // Without some help from the compiler or user, is_pod will not report + // that a struct or class is a POD, but will correctly report that + // built-in types such as int are PODs. The user can help the compiler + // by using the EASTL_DECLARE_POD macro on a class. + /////////////////////////////////////////////////////////////////////// + + #if defined(EA_COMPILER_MSVC) + #define EASTL_TYPE_TRAIT_is_pod_CONFORMANCE 1 // is_pod is conforming. Actually as of VS2008 it is apparently not fully conforming, as it flags the following as a non-pod: struct Pod{ Pod(){} }; + + EA_DISABLE_VC_WARNING(4647) + template // We check for has_trivial_constructor only because the VC++ is_pod does. Is it due to some compiler bug? + struct is_pod : public eastl::integral_constant::value) || eastl::is_void::value || eastl::is_scalar::value>{}; + EA_RESTORE_VC_WARNING() + + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_pod))) + #define EASTL_TYPE_TRAIT_is_pod_CONFORMANCE 1 // is_pod is conforming. + + template + struct is_pod : public eastl::integral_constant::value || eastl::is_scalar::value>{}; + #else + #define EASTL_TYPE_TRAIT_is_pod_CONFORMANCE 0 // is_pod is not conforming. Can return false negatives. + + template // There's not much we can do here without some compiler extension. + struct is_pod : public eastl::integral_constant::value || eastl::is_scalar::type>::value>{}; + #endif + + template + struct is_pod : public is_pod{}; + + template + struct is_POD : public is_pod{}; // Backwards compatibility. + + #define EASTL_DECLARE_IS_POD(T, isPod) \ + namespace eastl { \ + template <> struct is_pod : public eastl::integral_constant { }; \ + template <> struct is_pod : public eastl::integral_constant { }; \ + template <> struct is_pod : public eastl::integral_constant { }; \ + template <> struct is_pod : public eastl::integral_constant { }; \ + } + + // Old style macro, for bacwards compatibility: + #define EASTL_DECLARE_POD(T) namespace eastl{ template <> struct is_pod : public true_type{}; template <> struct is_pod : public true_type{}; } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_pod_v = is_pod::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_standard_layout + // + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(EA_COMPILER_MSVC) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006)) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_standard_layout))) + #define EASTL_TYPE_TRAIT_is_standard_layout_CONFORMANCE 1 // is_standard_layout is conforming. + + template + struct is_standard_layout : public eastl::integral_constant::value || eastl::is_scalar::value>{}; + #else + #define EASTL_TYPE_TRAIT_is_standard_layout_CONFORMANCE 0 // is_standard_layout is not conforming. Can return false negatives. + + template // There's not much we can do here without some compiler extension. + struct is_standard_layout : public eastl::integral_constant::value || is_scalar::value>{}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_standard_layout_v = is_standard_layout::value; + #endif + + #define EASTL_DECLARE_IS_STANDARD_LAYOUT(T, isStandardLayout) \ + namespace eastl { \ + template <> struct is_standard_layout : public eastl::integral_constant { }; \ + template <> struct is_standard_layout : public eastl::integral_constant { }; \ + template <> struct is_standard_layout : public eastl::integral_constant { }; \ + template <> struct is_standard_layout : public eastl::integral_constant { }; \ + } + + // Old style macro, for bacwards compatibility: + #define EASTL_DECLARE_STANDARD_LAYOUT(T) namespace eastl{ template <> struct is_standard_layout : public true_type{}; template <> struct is_standard_layout : public true_type{}; } + + + + /////////////////////////////////////////////////////////////////////// + // has_trivial_constructor + // + // has_trivial_constructor::value == true if and only if T is a class + // or struct that has a trivial constructor. A constructor is trivial if + // - it is implicitly defined by the compiler, and + // - is_polymorphic::value == false, and + // - T has no virtual base classes, and + // - for every direct base class of T, has_trivial_constructor::value == true, + // where B is the type of the base class, and + // - for every nonstatic data member of T that has class type or array + // of class type, has_trivial_constructor::value == true, + // where M is the type of the data member + // + // has_trivial_constructor may only be applied to complete types. + // + // Without from the compiler or user, has_trivial_constructor will not + // report that a class or struct has a trivial constructor. + // The user can use EASTL_DECLARE_TRIVIAL_CONSTRUCTOR to help the compiler. + // + // A default constructor for a class X is a constructor of class X that + // can be called without an argument. + /////////////////////////////////////////////////////////////////////// + + #if defined(_MSC_VER) && (_MSC_VER >= 1600) && !defined(EA_COMPILER_CLANG_CL) // VS2010+ + #define EASTL_TYPE_TRAIT_has_trivial_constructor_CONFORMANCE 1 // has_trivial_constructor is conforming. + + template + struct has_trivial_constructor : public eastl::integral_constant::value) && !eastl::is_hat_type::value>{}; + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) + #define EASTL_TYPE_TRAIT_has_trivial_constructor_CONFORMANCE 1 // has_trivial_constructor is conforming. + + template + struct has_trivial_constructor : public eastl::integral_constant::value>{}; + #else + #define EASTL_TYPE_TRAIT_has_trivial_constructor_CONFORMANCE 0 // has_trivial_constructor is not fully conforming. Can return false negatives. + + // With current compilers, this is all we can do. + template + struct has_trivial_constructor : public eastl::is_pod {}; + #endif + + #define EASTL_DECLARE_HAS_TRIVIAL_CONSTRUCTOR(T, hasTrivialConstructor) \ + namespace eastl { \ + template <> struct has_trivial_constructor : public eastl::integral_constant { }; \ + } + + // Old style macro, for bacwards compatibility: + #define EASTL_DECLARE_TRIVIAL_CONSTRUCTOR(T) namespace eastl{ template <> struct has_trivial_constructor : public true_type{}; template <> struct has_trivial_constructor : public true_type{}; } + + + + + /////////////////////////////////////////////////////////////////////// + // has_trivial_copy + // + // has_trivial_copy::value == true if and only if T is a class or + // struct that has a trivial copy constructor. A copy constructor is + // trivial if + // - it is implicitly defined by the compiler, and + // - is_polymorphic::value == false, and + // - T has no virtual base classes, and + // - for every direct base class of T, has_trivial_copy::value == true, + // where B is the type of the base class, and + // - for every nonstatic data member of T that has class type or array + // of class type, has_trivial_copy::value == true, where M is the + // type of the data member + // + // has_trivial_copy may only be applied to complete types. + // + // Another way of looking at this is: + // A copy constructor for class X is trivial if it is implicitly + // declared and if all the following are true: + // - Class X has no virtual functions (10.3) and no virtual base classes (10.1). + // - Each direct base class of X has a trivial copy constructor. + // - For all the nonstatic data members of X that are of class type + // (or array thereof), each such class type has a trivial copy constructor; + // otherwise the copy constructor is nontrivial. + // + // Without help from the compiler or user, has_trivial_copy will not report + // that a class or struct has a trivial copy constructor. The user can + // use EASTL_DECLARE_TRIVIAL_COPY to help the compiler. + /////////////////////////////////////////////////////////////////////// + + #if defined(_MSC_VER) && !defined(EA_COMPILER_CLANG_CL) + #define EASTL_TYPE_TRAIT_has_trivial_copy_CONFORMANCE 1 // has_trivial_copy is conforming. + + template + struct has_trivial_copy : public eastl::integral_constant::value) && !eastl::is_volatile::value && !eastl::is_hat_type::value>{}; + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + #define EASTL_TYPE_TRAIT_has_trivial_copy_CONFORMANCE 1 // has_trivial_copy is conforming. + + template + struct has_trivial_copy : public eastl::integral_constant::value) && (!eastl::is_volatile::value && !eastl::is_reference::value)>{}; + #else + #define EASTL_TYPE_TRAIT_has_trivial_copy_CONFORMANCE 0 // has_trivial_copy is not fully conforming. Can return false negatives. + + template + struct has_trivial_copy : public eastl::integral_constant::value && !eastl::is_volatile::value>{}; + #endif + + #define EASTL_DECLARE_HAS_TRIVIAL_COPY(T, hasTrivialCopy) \ + namespace eastl { \ + template <> struct has_trivial_copy : public eastl::integral_constant { }; \ + } + + // Old style macro, for bacwards compatibility: + #define EASTL_DECLARE_TRIVIAL_COPY(T) namespace eastl{ template <> struct has_trivial_copy : public true_type{}; template <> struct has_trivial_copy : public true_type{}; } + + + + + /////////////////////////////////////////////////////////////////////// + // has_trivial_assign + // + // has_trivial_assign::value == true if and only if T is a class or + // struct that has a trivial copy assignment operator. A copy assignment + // operator is trivial if: + // - it is implicitly defined by the compiler, and + // - is_polymorphic::value == false, and + // - T has no virtual base classes, and + // - for every direct base class of T, has_trivial_assign::value == true, + // where B is the type of the base class, and + // - for every nonstatic data member of T that has class type or array + // of class type, has_trivial_assign::value == true, where M is + // the type of the data member. + // + // has_trivial_assign may only be applied to complete types. + // + // Without from the compiler or user, has_trivial_assign will not + // report that a class or struct has trivial assignment. The user + // can use EASTL_DECLARE_TRIVIAL_ASSIGN to help the compiler. + /////////////////////////////////////////////////////////////////////// + + #if defined(_MSC_VER) && (_MSC_VER >= 1600) && !defined(EA_COMPILER_CLANG_CL) + #define EASTL_TYPE_TRAIT_has_trivial_assign_CONFORMANCE 1 // has_trivial_assign is conforming. + + template + struct has_trivial_assign : public integral_constant::value) && !eastl::is_const::value && !eastl::is_volatile::value && !eastl::is_hat_type::value>{}; + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) + #define EASTL_TYPE_TRAIT_has_trivial_assign_CONFORMANCE 1 // has_trivial_assign is conforming. + + template + struct has_trivial_assign : public integral_constant::value) && !eastl::is_const::value && !eastl::is_volatile::value>{}; + #else + #define EASTL_TYPE_TRAIT_has_trivial_assign_CONFORMANCE 0 // is_pod is not fully conforming. Can return false negatives. + + template + struct has_trivial_assign : public integral_constant::value && !is_const::value && !is_volatile::value + >{}; + #endif + + #define EASTL_DECLARE_HAS_TRIVIAL_ASSIGN(T, hasTrivialAssign) \ + namespace eastl { \ + template <> struct has_trivial_assign : public eastl::integral_constant { }; \ + } + + // Old style macro, for bacwards compatibility: + #define EASTL_DECLARE_TRIVIAL_ASSIGN(T) namespace eastl{ template <> struct has_trivial_assign : public true_type{}; template <> struct has_trivial_assign : public true_type{}; } + + + + + /////////////////////////////////////////////////////////////////////// + // has_trivial_destructor + // + // has_trivial_destructor::value == true if and only if T is a class + // or struct that has a trivial destructor. A destructor is trivial if + // - it is implicitly defined by the compiler, and + // - for every direct base class of T, has_trivial_destructor::value == true, + // where B is the type of the base class, and + // - for every nonstatic data member of T that has class type or + // array of class type, has_trivial_destructor::value == true, + // where M is the type of the data member + // + // has_trivial_destructor may only be applied to complete types. + // + // Without from the compiler or user, has_trivial_destructor will not + // report that a class or struct has a trivial destructor. + // The user can use EASTL_DECLARE_TRIVIAL_DESTRUCTOR to help the compiler. + /////////////////////////////////////////////////////////////////////// + + #if defined(_MSC_VER) && (_MSC_VER >= 1600) && !defined(EA_COMPILER_CLANG_CL) + #define EASTL_TYPE_TRAIT_has_trivial_destructor_CONFORMANCE 1 // has_trivial_destructor is conforming. + + template + struct has_trivial_destructor : public eastl::integral_constant::value) && !eastl::is_hat_type::value>{}; + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) + #define EASTL_TYPE_TRAIT_has_trivial_destructor_CONFORMANCE 1 // has_trivial_destructor is conforming. + + template + struct has_trivial_destructor : public eastl::integral_constant::value>{}; + #else + #define EASTL_TYPE_TRAIT_has_trivial_destructor_CONFORMANCE 0 // is_pod is not fully conforming. Can return false negatives. + + // With current compilers, this is all we can do. + template + struct has_trivial_destructor : public eastl::is_pod{}; + #endif + + #define EASTL_DECLARE_HAS_TRIVIAL_DESTRUCTOR(T, hasTrivialDestructor) \ + namespace eastl { \ + template <> struct has_trivial_destructor : public eastl::integral_constant { }; \ + } + + // Old style macro, for bacwards compatibility: + #define EASTL_DECLARE_TRIVIAL_DESTRUCTOR(T) namespace eastl{ template <> struct has_trivial_destructor : public true_type{}; template <> struct has_trivial_destructor : public true_type{}; } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool has_trivial_destructor_v = has_trivial_destructor::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // has_trivial_relocate + // + // This is an EA extension to the type traits standard. + // This trait is deprecated under conforming C++11 compilers, as C++11 + // move functionality supercedes this functionality and we want to + // migrate away from it in the future. + // + // A trivially relocatable object is one that can be safely memmove'd + // to uninitialized memory. construction, assignment, and destruction + // properties are not addressed by this trait. A type that has the + // is_fundamental trait would always have the has_trivial_relocate trait. + // A type that has the has_trivial_constructor, has_trivial_copy or + // has_trivial_assign traits would usally have the has_trivial_relocate + // trait, but this is not strictly guaranteed. + // + // The user can use EASTL_DECLARE_TRIVIAL_RELOCATE to help the compiler. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_has_trivial_relocate_CONFORMANCE 0 // is_pod is not fully conforming. Can return false negatives. + + template + struct has_trivial_relocate : public eastl::bool_constant && !eastl::is_volatile_v> {}; + + #define EASTL_DECLARE_TRIVIAL_RELOCATE(T) namespace eastl{ template <> struct has_trivial_relocate : public true_type{}; template <> struct has_trivial_relocate : public true_type{}; } + + + + + /////////////////////////////////////////////////////////////////////// + // has_nothrow_constructor + // + // has_nothrow_constructor::value == true if and only if T is a + // class or struct whose default constructor has an empty throw specification. + // + // has_nothrow_constructor may only be applied to complete types. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + #define EASTL_TYPE_TRAIT_has_nothrow_constructor_CONFORMANCE 1 + + template + struct has_nothrow_constructor + : public eastl::integral_constant{}; + + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && defined(_MSC_VER) + // Microsoft's implementation of __has_nothrow_constructor is crippled and returns true only if T is a class that has an explicit constructor. + // "Returns true if the default constructor has an empty exception specification." + #define EASTL_TYPE_TRAIT_has_nothrow_constructor_CONFORMANCE 0 + + template // This is mistakenly returning true for an unbounded array of scalar type. + struct has_nothrow_constructor : public eastl::integral_constant::type>::value || eastl::is_reference::value>{}; + + #else + #define EASTL_TYPE_TRAIT_has_nothrow_constructor_CONFORMANCE 0 // has_nothrow_constructor is not fully conforming. Can return false negatives. + + template + struct has_nothrow_constructor // To do: Improve this to include other types that can work. + { static const bool value = eastl::is_scalar::type>::value || eastl::is_reference::value; }; + #endif + + #define EASTL_DECLARE_HAS_NOTHROW_CONSTRUCTOR(T, hasNothrowConstructor) \ + namespace eastl { \ + template <> struct has_nothrow_constructor : public eastl::integral_constant { }; \ + } + + + + /////////////////////////////////////////////////////////////////////// + // has_nothrow_copy + // + // has_nothrow_copy::value == true if and only if T is a class or + // struct whose copy constructor has an empty throw specification. + // + // has_nothrow_copy may only be applied to complete types. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + #define EASTL_TYPE_TRAIT_has_nothrow_copy_CONFORMANCE 1 + + template + struct has_nothrow_copy : public eastl::integral_constant{}; + + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && defined(_MSC_VER) + // Microsoft's implementation of __has_nothrow_copy is crippled and returns true only if T is a class that has a copy constructor. + // "Returns true if the copy constructor has an empty exception specification." + #define EASTL_TYPE_TRAIT_has_nothrow_copy_CONFORMANCE 0 + + template + struct has_nothrow_copy : public eastl::integral_constant::type>::value || eastl::is_reference::value>{}; + + #else + #define EASTL_TYPE_TRAIT_has_nothrow_copy_CONFORMANCE 0 // has_nothrow_copy is not fully conforming. Can return false negatives. + + template + struct has_nothrow_copy // To do: Improve this to include other types that can work. + { static const bool value = eastl::is_scalar::type>::value || eastl::is_reference::value; }; + #endif + + #define EASTL_DECLARE_HAS_NOTHROW_COPY(T, hasNothrowCopy) \ + namespace eastl { \ + template <> struct has_nothrow_copy : public eastl::integral_constant { }; \ + } + + + + /////////////////////////////////////////////////////////////////////// + // has_nothrow_assign + // + // has_nothrow_assign::value == true if and only if T is a class or + // struct whose copy assignment operator has an empty throw specification. + // + // has_nothrow_assign may only be applied to complete types. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + #define EASTL_TYPE_TRAIT_has_nothrow_assign_CONFORMANCE 1 + + template + struct has_nothrow_assign : public eastl::integral_constant{}; + + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && defined(_MSC_VER) + // Microsoft's implementation of __has_nothrow_assign is crippled and returns true only if T is a class that has an assignment operator. + // "Returns true if a copy assignment operator has an empty exception specification." + #define EASTL_TYPE_TRAIT_has_nothrow_assign_CONFORMANCE 0 + + template // This is mistakenly returning true for an unbounded array of scalar type. + struct has_nothrow_assign : public eastl::integral_constant::type>::value || eastl::is_reference::value>{}; + #else + #define EASTL_TYPE_TRAIT_has_nothrow_assign_CONFORMANCE 0 // has_nothrow_assign is not fully conforming. Can return false negatives. + + template + struct has_nothrow_assign // To do: Improve this to include other types that can work. + { static const bool value = eastl::is_scalar::type>::value || eastl::is_reference::value; } ; + #endif + + #define EASTL_DECLARE_HAS_NOTHROW_ASSIGN(T, hasNothrowAssign) \ + namespace eastl { \ + template <> struct has_nothrow_assign : public eastl::integral_constant { }; \ + } + + + + /////////////////////////////////////////////////////////////////////// + // has_virtual_destructor + // + // has_virtual_destructor::value == true if and only if T is a class + // or struct with a virtual destructor. + // + // has_virtual_destructor may only be applied to complete types. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) + #define EASTL_TYPE_TRAIT_has_virtual_destructor_CONFORMANCE 1 + + template + struct has_virtual_destructor : public eastl::integral_constant{}; + #else + #define EASTL_TYPE_TRAIT_has_virtual_destructor_CONFORMANCE 0 // has_virtual_destructor is not fully conforming. Can return false negatives. + + template + struct has_virtual_destructor : public eastl::false_type{}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool has_virtual_destructor_v = has_virtual_destructor::value; + #endif + + #define EASTL_DECLARE_HAS_VIRTUAL_DESTRUCTOR(T, hasVirtualDestructor) \ + namespace eastl { \ + template <> struct has_virtual_destructor : public eastl::integral_constant { }; \ + template <> struct has_virtual_destructor : public eastl::integral_constant { }; \ + template <> struct has_virtual_destructor : public eastl::integral_constant { }; \ + template <> struct has_virtual_destructor : public eastl::integral_constant { }; \ + } + + + /////////////////////////////////////////////////////////////////////// + // is_literal_type + // + // See the C++11 Standard, section 2.9,p10. + // A type is a literal type if it is: + // - a scalar type; or + // - a reference type referring to a literal type; or + // - an array of literal type; or + // - a class type (Clause 9) that has all of the following properties: + // - it has a trivial destructor, + // - every constructor call and full-expression in the brace-or-equal-initializer s for non-static data members (if any) is a constant expression (5.19), + // - it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and + // - all of its non-static data members and base classes are of literal types. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_literal)) + #define EASTL_TYPE_TRAIT_is_literal_type_CONFORMANCE 1 + + template + struct is_literal_type : public eastl::integral_constant{}; + + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006)) || (defined(_MSC_VER) && (_MSC_VER >= 1700))) // VS2012+ + #if defined(EA_COMPILER_GNUC) && (!defined(EA_COMPILER_CPP11_ENABLED) || (EA_COMPILER_VERSION < 4007)) + #define EASTL_TYPE_TRAIT_is_literal_type_CONFORMANCE 0 // It seems that in this case GCC supports the compiler intrinsic but reports it as false when it's true. + #else + #define EASTL_TYPE_TRAIT_is_literal_type_CONFORMANCE 1 + #endif + + template + struct is_literal_type : public eastl::integral_constant{}; + + #else + #define EASTL_TYPE_TRAIT_is_literal_type_CONFORMANCE 0 + + // It's not clear if this trait can be fully implemented without explicit compiler support. + // For now we assume that it can't be but implement something that gets it right at least + // some of the time. Recall that partial positives and false negatives are OK (though not ideal), + // while false positives are not OK for us to generate. + + template // This is not a complete implementation and will be true for only some literal types (the basic ones). + struct is_literal_type : public eastl::integral_constant::type>::type>::value>{}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_literal_type_v = is_literal_type::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_abstract + // + // is_abstract::value == true if and only if T is a class or struct + // that has at least one pure virtual function. is_abstract may only + // be applied to complete types. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_abstract))) + #define EASTL_TYPE_TRAIT_is_abstract_CONFORMANCE 1 // is_abstract is conforming. + + template + struct is_abstract : public integral_constant{}; + #else + #define EASTL_TYPE_TRAIT_is_abstract_CONFORMANCE 0 + + template::value> + class is_abstract_helper + { + template + static eastl::yes_type test(...); + + template + static eastl::no_type test(T1(*)[1]); // The following: 'typedef SomeAbstractClass (*SomeFunctionType)[1];' is invalid (can't have an array of abstract types) and thus doesn't choose this path. + + public: + static const bool value = (sizeof(test(NULL)) == sizeof(eastl::yes_type)); + }; + + template + struct is_abstract_helper + { static const bool value = false; }; + + template + struct is_abstract + : public integral_constant::value> { }; + + #endif + + #define EASTL_DECLARE_IS_ABSTRACT(T, isAbstract) \ + namespace eastl { \ + template <> struct is_abstract : public eastl::integral_constant { }; \ + template <> struct is_abstract : public eastl::integral_constant { }; \ + template <> struct is_abstract : public eastl::integral_constant { }; \ + template <> struct is_abstract : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_abstract_v = is_abstract::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_trivially_copyable + // + // T is a trivially copyable type (3.9) T shall be a complete type, + // (possibly cv-qualified) void, or an array of unknown bound. + // + // 3.9,p3: For any trivially copyable type T, if two pointers to T + // point to distinct T objects obj1 and obj2, where neither obj1 nor + // obj2 is a base-class subobject, if the underlying bytes making + // up obj1 are copied into obj2, obj2 shall subsequently hold the + // same value as obj1. In other words, you can memcpy/memmove it. + /////////////////////////////////////////////////////////////////////// + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(_MSC_VER) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 5003)) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_trivially_copyable))) + #define EASTL_TYPE_TRAIT_is_trivially_copyable_CONFORMANCE 1 + + // https://connect.microsoft.com/VisualStudio/feedback/details/808827/c-std-is-trivially-copyable-produces-wrong-result-for-arrays + // + // From Microsoft: + // We're working on fixing this. When overhauling in VC 2013, I incorrectly believed that is_trivially_copyable was a synonym + // for is_trivially_copy_constructible. I've asked the compiler team to provide a compiler hook with 100% accurate answers. (Currently, the + // compiler hook has incorrect answers for volatile scalars, volatile data members, and various scenarios for defaulted/deleted/private + // special member functions - I wrote an exhaustive test case to exercise the complicated Standardese.) When the compiler hook is fixed, + // I'll change to invoke it. + // + // Microsoft broken VS2013 STL implementation: + // template + // struct is_trivially_copyable + // : is_trivially_copy_constructible<_Ty>::type + // { // determine whether _Ty has a trivial copy constructor + // }; + // + + template + struct is_trivially_copyable { static const bool value = __is_trivially_copyable(T); }; + + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_MSVC) || defined(EA_COMPILER_GNUC)) + #define EASTL_TYPE_TRAIT_is_trivially_copyable_CONFORMANCE 1 + + // Micrsoft (prior to VS2012) and GCC have __has_trivial_copy, but it may not be identical with the goals of this type trait. + template + struct is_trivially_copyable : public integral_constant::type>::value) && (!eastl::is_void::value && !eastl::is_volatile::value && !eastl::is_reference::value)>{}; + #else + #define EASTL_TYPE_TRAIT_is_trivially_copyable_CONFORMANCE 0 // Generates false negatives. + + template + struct is_trivially_copyable { static const bool value = eastl::is_scalar::type>::value; }; + #endif + + #define EASTL_DECLARE_IS_TRIVIALLY_COPYABLE(T, isTriviallyCopyable) \ + namespace eastl { \ + template <> struct is_trivially_copyable : public eastl::integral_constant { }; \ + template <> struct is_trivially_copyable : public eastl::integral_constant { }; \ + template <> struct is_trivially_copyable : public eastl::integral_constant { }; \ + template <> struct is_trivially_copyable : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_copyable_v = is_trivially_copyable::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_constructible + // + // See the C++11 Standard, section 20.9.4.3,p6. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_constructible_CONFORMANCE 1 + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_constructible))) + template + struct is_constructible : public bool_constant<__is_constructible(T, Args...) > {}; + #else + // We implement a copy of move here has move_internal. We are currently stuck doing this because our move + // implementation is in and currently #includes us, and so we have a header + // chicken-and-egg problem. To do: Resolve this, probably by putting eastl::move somewhere else. + template + inline typename eastl::remove_reference::type&& move_internal(T&& x) EA_NOEXCEPT + { return ((typename eastl::remove_reference::type&&)x); } + + template + typename first_type_select()...)))>::type is(T&&, Args&& ...); + + template + struct can_construct_scalar_helper + { + static eastl::true_type can(T); + static eastl::false_type can(...); + }; + + template + eastl::false_type is(argument_sink, Args&& ...); + + // Except for scalars and references (handled below), check for constructibility via decltype. + template + struct is_constructible_helper_2 // argument_sink will catch all T that is not constructible from the Args and denote false_type + : public eastl::identity(), eastl::declval()...))>::type {}; + + template + struct is_constructible_helper_2 + : public eastl::is_scalar {}; + + template // We handle the case of multiple arguments below (by disallowing them). + struct is_constructible_helper_2 + : public eastl::identity::can(eastl::declval()))>::type {}; + + // Scalars and references can be constructed only with 0 or 1 argument. e.g the following is an invalid expression: int(17, 23) + template + struct is_constructible_helper_2 + : public eastl::false_type {}; + + template + struct is_constructible_helper_1 + : public is_constructible_helper_2::value || eastl::is_reference::value, T, Args...> {}; + + // Unilaterally dismiss void, abstract, unknown bound arrays, and function types as not constructible. + template + struct is_constructible_helper_1 + : public false_type {}; + + // is_constructible + template + struct is_constructible + : public is_constructible_helper_1<(eastl::is_abstract::type>::value || + eastl::is_array_of_unknown_bounds::value || + eastl::is_function::type>::value || + eastl::has_void_arg::value), + T, Args...> {}; + + // Array types are constructible if constructed with no arguments and if their element type is default-constructible + template + struct is_constructible_helper_2 + : public eastl::is_constructible::type> {}; + + // Arrays with arguments are not constructible. e.g. the following is an invalid expression: int[3](37, 34, 12) + template + struct is_constructible_helper_2 + : public eastl::false_type {}; + + #endif + + + // You need to manually declare const/volatile variants individually if you want them. + #define EASTL_DECLARE_IS_CONSTRUCTIBLE(T, U, isConstructible) \ + namespace eastl { \ + template <> struct is_constructible : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_constructible_v = is_constructible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_trivially_constructible + // + // is_constructible::value is true and the variable definition + // for is_constructible, as defined below, is known to call no operation + // that is not trivial (3.9, 12). T and all types in the parameter pack + // Args shall be complete types, (possibly cv-qualified) void, or arrays + // of unknown bound. + // + // Note: + // C++11's is_trivially_constructible sounds the same as the pre-standard + // has_trivial_constructor type trait (which we also support here). However, + // the definition of has_trivial_constructor has never been formally standardized + // and so we can't just blindly equate the two to each other. Since we are + // moving forward with C++11 and deprecating the old type traits, we leave + // the old ones as-is, though we defer to them in cases where we don't seem + // to have a good alternative. + // + /////////////////////////////////////////////////////////////////////// + + #if defined(EA_COMPILER_NO_VARIADIC_TEMPLATES) + + #define EASTL_TYPE_TRAIT_is_trivially_constructible_CONFORMANCE 0 + + // In this version we allow only zero or one argument (Arg). We can add more arguments + // by creating a number of extra specializations. It's probably not possible to + // simplify the implementation with recursive templates because ctor argument + // presence is specific. + // + // To consider: we can fold the two implementations below by making a macro that's defined + // has __is_trivially_constructible(T) or eastl::has_trivial_copy::value, depending on + // whether the __is_trivially_constructible compiler intrinsic is available. + + // If the compiler has this trait built-in (which ideally all compilers would have since it's necessary for full conformance) use it. + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_trivially_constructible)) + + template + struct is_trivially_constructible + : public eastl::false_type {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant {}; + + #else + + template + struct is_trivially_constructible + : public eastl::false_type {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_constructor::type>::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + #endif + + #else + + // If the compiler has this trait built-in (which ideally all compilers would have since it's necessary for full conformance) use it. + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_trivially_constructible)) + #define EASTL_TYPE_TRAIT_is_trivially_constructible_CONFORMANCE 1 + + // We have a problem with clang here as of clang 3.4: __is_trivially_constructible(int[]) is false, yet I believe it should be true. + // Until it gets resolved, what we do is check for is_constructible along with __is_trivially_constructible(). + template + struct is_trivially_constructible + : public eastl::integral_constant::value && __is_trivially_constructible(T, Args...)> {}; + + #else + + #define EASTL_TYPE_TRAIT_is_trivially_constructible_CONFORMANCE 0 // This is 0 but in fact it will work for most real-world cases due to the has_trivial_constructor specialization below. + + template + struct is_trivially_constructible + : public eastl::false_type {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_constructor::type>::value> {}; + + // It's questionable whether we can use has_trivial_copy here, as it could theoretically create a false-positive. + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + template + struct is_trivially_constructible + : public eastl::integral_constant::value && eastl::has_trivial_copy::value> {}; + + #endif + + #endif + + + #define EASTL_DECLARE_IS_TRIVIALLY_CONSTRUCTIBLE(T, isTriviallyConstructible) \ + namespace eastl { \ + template <> struct is_trivially_constructible : public eastl::integral_constant { }; \ + template <> struct is_trivially_constructible : public eastl::integral_constant { }; \ + template <> struct is_trivially_constructible : public eastl::integral_constant { }; \ + template <> struct is_trivially_constructible : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_constructible_v = is_trivially_constructible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_trivially_default_constructible + // + // is_trivially_constructible::value is true. + // This is thus identical to is_trivially_constructible. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_trivially_default_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_trivially_constructible_CONFORMANCE + + template + struct is_trivially_default_constructible + : public eastl::is_trivially_constructible {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_default_constructible_v = is_trivially_default_constructible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_trivial + // + // is_trivial::value == true if T is a scalar type, a trivially copyable + // class with a trivial default constructor, or array of such type/class, + // possibly cv-qualified), provides the member constant value equal true. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_trivial_CONFORMANCE ((EASTL_TYPE_TRAIT_is_trivially_default_constructible_CONFORMANCE && EASTL_TYPE_TRAIT_is_trivially_copyable_CONFORMANCE) ? 1 : 0) + + #if defined(_MSC_VER) && _MSC_VER == 1800 + template + struct is_trivial_helper + : public eastl::integral_constant::value && eastl::is_trivially_default_constructible::value>{}; + + template + struct is_trivial_helper + : public false_type{}; + + template + struct is_trivial + : public is_trivial_helper<(EA_ALIGN_OF(T) > EA_PLATFORM_MIN_MALLOC_ALIGNMENT), T>::type{}; + #else + // All other compilers seem to be able to handle aligned types passed as value + template + struct is_trivial + : public eastl::integral_constant::value && eastl::is_trivially_default_constructible::value> {}; + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivial_v = is_trivial::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_constructible + // + // is_constructible::value is true and the variable definition + // for is_constructible, as defined below, is known not to throw any + // exceptions (5.3.7). T and all types in the parameter pack Args shall + // be complete types, (possibly cv-qualified) void, or arrays of unknown bound. + // + /////////////////////////////////////////////////////////////////////// + #if defined(EA_COMPILER_NO_NOEXCEPT) + + #define EASTL_TYPE_TRAIT_is_nothrow_constructible_CONFORMANCE 0 + + template + struct is_nothrow_constructible + : public eastl::false_type {}; + + template + struct is_nothrow_constructible + : public eastl::integral_constant::value> {}; + + template + struct is_nothrow_constructible + : public eastl::integral_constant::value> {}; + + template + struct is_nothrow_constructible + : public eastl::integral_constant::value> {}; + + template + struct is_nothrow_constructible + : public eastl::integral_constant::value> {}; + + template + struct is_nothrow_constructible + : public eastl::integral_constant::value> {}; + + #else + #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION < 4008) + #define EASTL_TYPE_TRAIT_is_nothrow_constructible_CONFORMANCE 0 // GCC up to v4.7's noexcept is broken and fails to generate true for the case of compiler-generated constructors. + #else + #define EASTL_TYPE_TRAIT_is_nothrow_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_constructible_CONFORMANCE + #endif + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // *_noexcept_wrapper implements a workaround for VS2015 preview. A standards conforming noexcept operator allows variadic template expansion. + // There appears to be an issue with VS2015 preview that prevents variadic template expansion into a noexcept operator that is passed directly + // to a template parameter. + // + // The fix hoists the noexcept expression into a separate struct and caches the result of the expression. This result is then passed to integral_constant. + // + // Example code from Clang libc++ + // template + // struct __libcpp_is_nothrow_constructible<[>is constructible*/true, /*is reference<]false, _Tp, _Args...> + // : public integral_constant()...))> { }; + // + + template + struct is_nothrow_constructible_helper_noexcept_wrapper + { static const bool value = noexcept(T(eastl::declval()...)); }; + + template + struct is_nothrow_constructible_helper; + + template + struct is_nothrow_constructible_helper + : public eastl::integral_constant::value> {}; + + template + struct is_nothrow_constructible_helper + : public eastl::integral_constant()))> {}; + + template + struct is_nothrow_constructible_helper + : public eastl::integral_constant {}; + + template + struct is_nothrow_constructible_helper + : public eastl::false_type {}; + + template + struct is_nothrow_constructible + : public eastl::is_nothrow_constructible_helper::value, T, Args...> {}; + + template + struct is_nothrow_constructible + : public eastl::is_nothrow_constructible_helper::value, T> {}; + #endif + + #define EASTL_DECLARE_IS_NOTHROW_CONSTRUCTIBLE(T, isNothrowConstructible) \ + namespace eastl{ \ + template <> struct is_nothrow_constructible : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_nothrow_constructible_v = is_nothrow_constructible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_default_constructible + // + // is_constructible::value is true. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_default_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_constructible_CONFORMANCE + + template + struct is_default_constructible + : public eastl::is_constructible {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_default_constructible_v = is_default_constructible::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_default_constructible + /////////////////////////////////////////////////////////////////////// + // TODO(rparolin): implement type-trait + + + + /////////////////////////////////////////////////////////////////////// + // is_copy_constructible + // + // is_constructible::value is true. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_copy_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_constructible_CONFORMANCE + + template + struct is_copy_constructible + : public eastl::is_constructible::type>::type> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_copy_constructible_v = is_copy_constructible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_trivially_copy_constructible + // + // is_trivially_constructible::value is true. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_trivially_copy_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_trivially_constructible_CONFORMANCE + + template + struct is_trivially_copy_constructible + : public eastl::is_trivially_constructible::type>::type> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_copy_constructible_v = is_trivially_copy_constructible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_copy_constructible + // + // is_nothrow_-constructible::value is true. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_nothrow_copy_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_nothrow_constructible_CONFORMANCE + + template + struct is_nothrow_copy_constructible + : public is_nothrow_constructible::type>::type> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_nothrow_copy_constructible_v = is_nothrow_copy_constructible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_move_constructible + // + // is_constructible::value is true. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_move_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_constructible_CONFORMANCE + + template + struct is_move_constructible + : public eastl::is_constructible::type> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_move_constructible_v = is_move_constructible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_trivially_move_constructible + // + // is_trivially_constructible::value is true. + // T shall be a complete type, (possibly cv-qualified) void, or an + // array of unknown bound. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_trivially_move_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_trivially_constructible_CONFORMANCE + + template + struct is_trivially_move_constructible + : public eastl::is_trivially_constructible::type> {}; + + #define EASTL_DECLARE_IS_TRIVIALLY_MOVE_CONSTRUCTIBLE(T, isTrivallyMoveConstructible) \ + namespace eastl{ \ + template <> struct is_trivially_move_constructible : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_move_constructible_v = is_trivially_move_constructible::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_assignable + // + // The expression declval() = declval() is well-formed when treated as an unevaluated operand. + // Access checking is performed as if in a context unrelated to T and U. Only the validity of + // the immediate context of the assignment expression is considered. The compilation of the expression + // can result in side effects such as the instantiation of class template specializations and function + // template specializations, the generation of implicitly-defined functions, and so on. Such side + // effects are not in the "immediate context" and can result in the program being ill-formed. + // + // Note: + // This type trait has a misleading and counter-intuitive name. It does not indicate whether an instance + // of U can be assigned to an instance of T (e.g. t = u). Instead it indicates whether the assignment can be + // done after adding rvalue references to both, as in add_rvalue_reference::type = add_rvalue_reference::type. + // A counterintuitive result of this is that is_assignable::value == false. The is_copy_assignable + // trait indicates if a type can be assigned to its own type, though there isn't a standard C++ way to tell + // if an arbitrary type is assignable to another type. + // http://stackoverflow.com/questions/19920213/why-is-stdis-assignable-counter-intuitive + // + // Note: + // A true is_assignable value doesn't guarantee that the expression is compile-able, the compiler checks + // only that the assignment matches before compilation. In particular, if you have templated operator= + // for a class, the compiler will always say is_assignable is true, regardless of what's being tested + // on the right hand side of the expression. It may actually turn out during compilation that the + // templated operator= fails to compile because in practice it doesn't accept every possible type for + // the right hand side of the expression. + // + // Expected results: + // is_assignable::value == false + // is_assignable::value == true + // is_assignable::value == false + // is_assignable::value == false + // is_assignable::value == false + // is_assignable::value == false + // is_assignable::value == false + // is_assignable::value == false + // is_assignable::value == false + // is_assignable::value == false + // is_assignable::value == false + // is_assignable::value == true + // is_assignable::value == false + // + // Note: + // Our implementation here yields different results than does the std::is_assignable from Dinkumware-based Standard + // Libraries, but yields similar results to the std::is_assignable from GCC's libstdc++ and clang's libc++. It may + // possibly be that the Dinkumware results are intentionally different for some practical purpose or because they + // represent the spirit or the Standard but not the letter of the Standard. + // + /////////////////////////////////////////////////////////////////////// + #define EASTL_TYPE_TRAIT_is_assignable_CONFORMANCE 1 + + template + struct is_assignable_helper + { + template + static eastl::no_type is(...); + + template + static decltype(eastl::declval() = eastl::declval(), eastl::yes_type()) is(int); + + static const bool value = (sizeof(is(0)) == sizeof(eastl::yes_type)); + }; + + template + struct is_assignable : + public eastl::integral_constant::value> {}; + + // The main purpose of this function is to help the non-conforming case above. + // Note: We don't handle const/volatile variations here, as we expect the user to + // manually specify any such variations via this macro. + // Example usage: + // EASTL_DECLARE_IS_ASSIGNABLE(int, int, false) + // + #define EASTL_DECLARE_IS_ASSIGNABLE(T, U, isAssignable) \ + namespace eastl { \ + template <> struct is_assignable : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_assignable_v = is_assignable::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_lvalue_assignable + // + // This is an EASTL extension function which is like is_assignable but + // works for arbitrary assignments and not just rvalue assignments. + // This function provides an intuitive assignability test, as opposed + // to is_assignable. + // + // Note: is_lvalue_assignable === is_copy_assignable + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_lvalue_assignable_CONFORMANCE EASTL_TYPE_TRAIT_is_assignable_CONFORMANCE + + template + struct is_lvalue_assignable + : public eastl::is_assignable::type, + typename eastl::add_lvalue_reference::type>::type> {}; + + #define EASTL_DECLARE_IS_LVALUE_ASSIGNABLE(T, U, isLvalueAssignable) \ + namespace eastl { \ + template <> struct is_lvalue_assignable : public eastl::integral_constant { }; \ + } + + + + /////////////////////////////////////////////////////////////////////// + // is_trivially_assignable + // + // is_assignable::value is true and the assignment, as defined by + // is_assignable, is known to call no operation that is not trivial (3.9, 12). + // T and U shall be complete types, (possibly cv-qualified) void, or + // arrays of unknown bound + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(__clang__) && EA_COMPILER_HAS_FEATURE(is_trivially_assignable)) + #define EASTL_TYPE_TRAIT_is_trivially_assignable_CONFORMANCE 1 + + template + struct is_trivially_assignable + : eastl::integral_constant {}; + + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) && (_MSC_VER >= 1800)) + #define EASTL_TYPE_TRAIT_is_trivially_assignable_CONFORMANCE EASTL_TYPE_TRAIT_is_assignable_CONFORMANCE + + // This code path is attempting to work around the issue with VS2013 __is_trivially_assignable compiler intrinsic documented in the link + // below. todo: Re-evaluate in VS2014. + // + // https://connect.microsoft.com/VisualStudio/feedback/details/806233/std-is-trivially-copyable-const-int-n-and-std-is-trivially-copyable-int-n-incorrect + + template + struct is_trivially_assignable_helper; + + template + struct is_trivially_assignable_helper : eastl::integral_constant{}; + + template + struct is_trivially_assignable_helper : false_type{}; + + template + struct is_trivially_assignable + : eastl::integral_constant::value, T, U >::value> {}; + + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(EA_COMPILER_MSVC) || defined(EA_COMPILER_GNUC)) + #define EASTL_TYPE_TRAIT_is_trivially_assignable_CONFORMANCE EASTL_TYPE_TRAIT_is_assignable_CONFORMANCE + + // Micrsoft (up till at least VS2012) and GCC have __has_trivial_assign, but it may not be identical with the goals of this type trait. + // The Microsoft type trait headers suggest that a future version of VS will have a __is_trivially_assignable intrinsic, but we + // need to come up with something in the meantime. To do: Re-evalulate this for VS2013+ when it becomes available. + template + struct is_trivially_assignable + : eastl::integral_constant::value && + (eastl::is_pod::type>::value || __has_trivial_assign(typename eastl::remove_reference::type))> {}; + #else + + #define EASTL_TYPE_TRAIT_is_trivially_assignable_CONFORMANCE 0 // Generates false negatives. + + template + struct is_trivially_assignable + : public eastl::false_type {}; + + template + struct is_trivially_assignable + : public eastl::integral_constant::value> {}; + + template + struct is_trivially_assignable + : public eastl::integral_constant::value> {}; + + template + struct is_trivially_assignable + : public eastl::integral_constant::value> {}; + + template + struct is_trivially_assignable + : public eastl::integral_constant::value> {}; + + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_assignable_v = is_trivially_assignable::value; + #endif + + // The main purpose of this function is to help the non-conforming case above. + // Note: We don't handle const/volatile variations here, as we expect the user to + // manually specify any such variations via this macro. + // Example usage: + // EASTL_DECLARE_IS_TRIVIALLY_ASSIGNABLE(int, int, false) + // + #define EASTL_DECLARE_IS_TRIVIALLY_ASSIGNABLE(T, U, isTriviallyAssignable) \ + namespace eastl { \ + template <> struct is_trivially_assignable : public eastl::integral_constant { }; \ + } + + + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_assignable + // + // is_assignable::value is true and the assignment is known + // not to throw any exceptions (5.3.7). T and U shall be complete + // types, (possibly cv-qualified) void, or arrays of unknown bound. + // + /////////////////////////////////////////////////////////////////////// + + #if defined(_MSC_VER) && (_MSC_VER >= 1800) // VS2013+ + #define EASTL_TYPE_TRAIT_is_nothrow_assignable_CONFORMANCE 1 + + template + struct is_nothrow_assignable + : eastl::integral_constant {}; + + #elif defined(EA_COMPILER_NO_NOEXCEPT) || defined(__EDG_VERSION__) // EDG mis-compiles the conforming code below and so must be placed here. + #define EASTL_TYPE_TRAIT_is_nothrow_assignable_CONFORMANCE 0 + + template + struct is_nothrow_assignable + : public false_type {}; + + // Note that the following are crippled in that they support only assignment of T types to other T types. + template + struct is_nothrow_assignable + : public eastl::integral_constant::value> {}; + + template + struct is_nothrow_assignable + : public eastl::integral_constant::value> {}; + + template + struct is_nothrow_assignable + : public eastl::integral_constant::value> {}; + + #else + #define EASTL_TYPE_TRAIT_is_nothrow_assignable_CONFORMANCE 1 + + template + struct is_nothrow_assignable_helper; + + template + struct is_nothrow_assignable_helper + : public false_type {}; + + template + struct is_nothrow_assignable_helper // Set to true if the assignment (same as is_assignable) cannot generate an exception. + : public eastl::integral_constant() = eastl::declval()) > + { + }; + + template + struct is_nothrow_assignable + : public eastl::is_nothrow_assignable_helper::value, T, U> + { + }; + #endif + + #define EASTL_DECLARE_IS_NOTHROW_ASSIGNABLE(T, isNothrowAssignable) \ + namespace eastl{ \ + template <> struct is_nothrow_assignable : public eastl::integral_constant { }; \ + template <> struct is_nothrow_assignable : public eastl::integral_constant { }; \ + template <> struct is_nothrow_assignable : public eastl::integral_constant { }; \ + template <> struct is_nothrow_assignable : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_nothrow_assignable_v = is_nothrow_assignable::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_copy_assignable + // + // is_assignable::value is true. T shall be a complete type, + // (possibly cv -qualified) void, or an array of unknown bound. + // + // This (and not is_assignable) is the type trait you use to tell if you + // can do an arbitrary assignment. is_assignable tells if you can do an + // assignment specifically to an rvalue and not in general. + // http://stackoverflow.com/a/19921030/725009 + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_copy_assignable_CONFORMANCE EASTL_TYPE_TRAIT_is_assignable_CONFORMANCE + + template + struct is_copy_assignable + : public eastl::is_assignable::type, + typename eastl::add_lvalue_reference::type>::type> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_copy_assignable_v = is_copy_assignable::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_trivially_copy_assignable + // + // is_trivially_assignable::value is true. T shall be a + // complete type, (possibly cv-qualified) void, or an array of unknown bound. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_trivially_copy_assignable_CONFORMANCE EASTL_TYPE_TRAIT_is_trivially_assignable_CONFORMANCE + +#if EASTL_TYPE_TRAIT_is_trivially_copy_assignable_CONFORMANCE + template + struct is_trivially_copy_assignable + : public eastl::is_trivially_assignable::type, + typename eastl::add_lvalue_reference::type>::type> {}; +#else + template + struct is_trivially_copy_assignable + : public integral_constant::value || eastl::is_pod::value || eastl::is_trivially_assignable::type, typename eastl::add_lvalue_reference::type>::type>::value + > {}; +#endif + + #define EASTL_DECLARE_IS_TRIVIALLY_COPY_ASSIGNABLE(T, isTriviallyCopyAssignable) \ + namespace eastl { \ + template <> struct is_trivially_copy_assignable : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_copy_assignable_v = is_trivially_copy_assignable::value; + #endif + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_copy_assignable + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_nothrow_copy_assignable_CONFORMANCE EASTL_TYPE_TRAIT_is_nothrow_assignable_CONFORMANCE + + template + struct is_nothrow_copy_assignable + : public eastl::is_nothrow_assignable::type, + typename eastl::add_lvalue_reference::type>::type> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_nothrow_copy_assignable_v = is_nothrow_copy_assignable::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_move_assignable + // + // is_assignable::value is true. T shall be a complete type, + // (possibly cv -qualified) void, or an array of unknown bound. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_move_assignable_CONFORMANCE EASTL_TYPE_TRAIT_is_assignable_CONFORMANCE + + template + struct is_move_assignable + : public eastl::is_assignable::type, + typename eastl::add_rvalue_reference::type> {}; + + #define EASTL_DECLARE_IS_MOVE_ASSIGNABLE(T, isMoveAssignable) \ + namespace eastl{ \ + template <> struct is_move_assignable : public eastl::integral_constant { }; \ + template <> struct is_move_assignable : public eastl::integral_constant { }; \ + template <> struct is_move_assignable : public eastl::integral_constant { }; \ + template <> struct is_move_assignable : public eastl::integral_constant { }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_move_assignable_v = is_move_assignable::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_trivially_move_assignable + // + // is_trivially_-assignable::value is true. T shall be a complete type, + // (possibly cv-qualified) void, or an array of unknown bound. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_trivially_move_assignable_CONFORMANCE EASTL_TYPE_TRAIT_is_trivially_assignable_CONFORMANCE + + template + struct is_trivially_move_assignable + : public eastl::is_trivially_assignable::type, + typename eastl::add_rvalue_reference::type> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_move_assignable_v = is_trivially_move_assignable::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_move_assignable + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_nothrow_move_assignable_CONFORMANCE EASTL_TYPE_TRAIT_is_nothrow_assignable_CONFORMANCE + + template + struct is_nothrow_move_assignable + : public eastl::is_nothrow_assignable::type, + typename eastl::add_rvalue_reference::type> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_nothrow_move_assignable_v = is_nothrow_move_assignable::value; + #endif + + /////////////////////////////////////////////////////////////////////// + // is_destructible + // + // For a complete type T and given + // template + // struct test { U u; }; + // test::~test() is not deleted (C++11 "= delete"). + // T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound. + // + /////////////////////////////////////////////////////////////////////// + + #if 0 // defined(_MSC_VER) && (_MSC_VER >= 1800) // VS2013+ -- Disabled due to __is_destructible being broken in VC++ versions up to at least VS2013. A ticket will be submitted for this + #define EASTL_TYPE_TRAIT_is_destructible_CONFORMANCE 1 + + template + struct is_destructible + : integral_constant {}; + + #elif defined(EA_COMPILER_NO_DECLTYPE) || defined(EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) || defined(_MSC_VER) || defined(__EDG_VERSION__) // VS2012 and EDG mis-compile the conforming code below and so must be placed here. + #define EASTL_TYPE_TRAIT_is_destructible_CONFORMANCE 0 + + // This implementation works for almost all cases, with the primary exception being the + // case that the user declared the destructor as deleted. To deal with that case the + // user needs to use EASTL_DECLARE_IS_NOT_DESTRUCTIBLE to cause is_destructible::value + // to be false. + + template + struct is_destructible + : public eastl::integral_constant::value && + !eastl::is_void::value && + !eastl::is_function::value && + !eastl::is_abstract::value> {}; + #else + #define EASTL_TYPE_TRAIT_is_destructible_CONFORMANCE 1 + + template + struct destructible_test_helper{ U u; }; + + template + eastl::false_type destructible_test_function(...); + + template >().~destructible_test_helper())> + eastl::true_type destructible_test_function(int); + + template ::value || // Exclude these types from being considered destructible. + eastl::is_void::value || + eastl::is_function::value || + eastl::is_abstract::value> + struct is_destructible_helper + : public eastl::identity(0))>::type {}; // Need to wrap decltype with identity because some compilers otherwise don't like the bare decltype usage. + + template + struct is_destructible_helper + : public eastl::false_type {}; + + template + struct is_destructible + : public is_destructible_helper {}; + + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_destructible_v = is_destructible::value; + #endif + + #define EASTL_DECLARE_IS_DESTRUCTIBLE(T, isDestructible) \ + namespace eastl{ \ + template <> struct is_destructible : public eastl::integral_constant{}; \ + template <> struct is_destructible : public eastl::integral_constant{}; \ + template <> struct is_destructible : public eastl::integral_constant{}; \ + template <> struct is_destructible : public eastl::integral_constant{}; \ + } + + + + /////////////////////////////////////////////////////////////////////// + // is_trivially_destructible + // + // is_destructible::value is true and the indicated destructor is + // known to be trivial. T shall be a complete type, (possibly cv-qualified) + // void, or an array of unknown bound. + // + // A destructor is trivial if it is not user-provided and if: + // - the destructor is not virtual, + // - all of the direct base classes of its class have trivial destructors, and + // - for all of the non-static data members of its class that are of + // class type (or array thereof), each such class has a trivial destructor. + // + /////////////////////////////////////////////////////////////////////// + + #if 0 // defined(_MSC_VER) && (_MSC_VER >= 1800) // VS2013+ -- Disabled due to __is_trivially_destructible being broken in VC++ versions up to at least VS2013. A ticket will be submitted for this + #define EASTL_TYPE_TRAIT_is_trivially_destructible_CONFORMANCE 1 + + template + struct is_trivially_destructible + : integral_constant {}; + + #elif EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || defined(__clang__)) + #define EASTL_TYPE_TRAIT_is_trivially_destructible_CONFORMANCE EASTL_TYPE_TRAIT_is_destructible_CONFORMANCE + + template + struct is_trivially_destructible // Can't use just __has_trivial_destructor(T) because some compilers give it slightly different meaning, and are just plain broken, such as VC++'s __has_trivial_destructor, which says false for fundamental types. + : public integral_constant::value && ((__has_trivial_destructor(T) && !eastl::is_hat_type::value)|| eastl::is_scalar::type>::value)> {}; + + #else + #define EASTL_TYPE_TRAIT_is_trivially_destructible_CONFORMANCE 0 + + template + struct is_trivially_destructible_helper + : public integral_constant::value || eastl::is_scalar::value || eastl::is_reference::value) && !eastl::is_void::value> {}; + + template + struct is_trivially_destructible + : public eastl::is_trivially_destructible_helper::type> {}; + #endif + + #define EASTL_DECLARE_IS_TRIVIALLY_DESTRUCTIBLE(T, isTriviallyDestructible) \ + namespace eastl{ \ + template <> struct is_trivially_destructible : public eastl::integral_constant{}; \ + template <> struct is_trivially_destructible : public eastl::integral_constant{}; \ + template <> struct is_trivially_destructible : public eastl::integral_constant{}; \ + template <> struct is_trivially_destructible : public eastl::integral_constant{}; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_trivially_destructible_v = is_trivially_destructible::value; + #endif + + + + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_destructible + // + // is_destructible::value is true and the indicated destructor is + // known not to throw any exceptions (5.3.7). T shall be a complete type, + // (possibly cv-qualified) void, or an array of unknown bound. + // + /////////////////////////////////////////////////////////////////////// + + #if 0 // defined(_MSC_VER) && (_MSC_VER >= 1800) // VS2013+ -- Disabled due to __is_nothrow_destructible being broken in VC++ versions up to at least VS2013. A ticket will be submitted for this + #define EASTL_TYPE_TRAIT_is_nothrow_destructible_CONFORMANCE ((_MSC_VER >= 1900) ? 1 : 0) // VS2013 (1800) doesn't support noexcept and so can't support all usage of this properly (in particular default exception specifications defined in [C++11 Standard, 15.4 paragraph 14]. + + template + struct is_nothrow_destructible + : integral_constant {}; + + #elif defined(EA_COMPILER_NO_NOEXCEPT) + #define EASTL_TYPE_TRAIT_is_nothrow_destructible_CONFORMANCE 0 + + template + struct is_nothrow_destructible_helper + : public eastl::integral_constant::value || eastl::is_reference::value> {}; + + template + struct is_nothrow_destructible + : public eastl::is_nothrow_destructible_helper::type> {}; + + #else + #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION < 4008) + #define EASTL_TYPE_TRAIT_is_nothrow_destructible_CONFORMANCE 0 // GCC up to v4.7's noexcept is broken and fails to generate true for the case of compiler-generated destructors. + #else + #define EASTL_TYPE_TRAIT_is_nothrow_destructible_CONFORMANCE EASTL_TYPE_TRAIT_is_destructible_CONFORMANCE + #endif + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // *_noexcept_wrapper implements a workaround for VS2015. A standards conforming noexcept operator allows variadic template expansion. + // There appears to be an issue with VS2015 that prevents variadic template expansion into a noexcept operator that is passed directly + // to a template parameter. + // + // The fix hoists the noexcept expression into a separate struct and caches the result of the expression. This result is then passed to integral_constant. + // + // Example code from Clang libc++ + // template + // struct __libcpp_is_nothrow_constructible<[>is constructible*/true, /*is reference<]false, _Tp, _Args...> + // : public integral_constant()...))> { }; + // + + template + struct is_nothrow_destructible_helper_noexcept_wrapper + { static const bool value = noexcept(eastl::declval().~T()); }; + + template + struct is_nothrow_destructible_helper; + + template + struct is_nothrow_destructible_helper + : public eastl::false_type {}; + + template + struct is_nothrow_destructible_helper // If the expression T::~T is a noexcept expression then it's nothrow. + : public eastl::integral_constant::value > {}; + + template + struct is_nothrow_destructible // A type needs to at least be destructible before it could be nothrow destructible. + : public eastl::is_nothrow_destructible_helper::value> {}; + + template // An array is nothrow destructible if its element type is nothrow destructible. + struct is_nothrow_destructible // To consider: Replace this with a remove_all_extents pathway. + : public eastl::is_nothrow_destructible {}; + + template + struct is_nothrow_destructible // A reference type cannot throw while being destructed. It's just a reference. + : public eastl::true_type {}; + + template + struct is_nothrow_destructible // An rvalue reference type cannot throw while being destructed. + : public eastl::true_type {}; + + #endif + + #define EASTL_DECLARE_IS_NOTHROW_DESTRUCTIBLE(T, isNoThrowDestructible) \ + namespace eastl{ \ + template <> struct is_nothrow_destructible { static const bool value = isNoThrowDestructible; }; \ + template <> struct is_nothrow_destructible { static const bool value = isNoThrowDestructible; }; \ + template <> struct is_nothrow_destructible { static const bool value = isNoThrowDestructible; }; \ + template <> struct is_nothrow_destructible { static const bool value = isNoThrowDestructible; }; \ + } + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_nothrow_destructible_v = is_nothrow_destructible::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_default_constructible + // + /////////////////////////////////////////////////////////////////////// + #define EASTL_TYPE_TRAIT_is_nothrow_default_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_nothrow_constructible_CONFORMANCE + + template + struct is_nothrow_default_constructible + : public eastl::is_nothrow_constructible {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_nothrow_default_constructible_v = is_nothrow_default_constructible::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_nothrow_move_constructible + // + /////////////////////////////////////////////////////////////////////// + #define EASTL_TYPE_TRAIT_is_nothrow_move_constructible_CONFORMANCE EASTL_TYPE_TRAIT_is_nothrow_constructible_CONFORMANCE + + template + struct is_nothrow_move_constructible + : public eastl::is_nothrow_constructible::type> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_nothrow_move_constructible_v = is_nothrow_move_constructible::value; + #endif + + +} // namespace eastl + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/internal/type_properties.h b/lib/EASTL/include/EASTL/internal/type_properties.h new file mode 100644 index 000000000..df90fcbf9 --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/type_properties.h @@ -0,0 +1,437 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_TYPE_PROPERTIES_H +#define EASTL_INTERNAL_TYPE_PROPERTIES_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include +#include + + +namespace eastl +{ + + + /////////////////////////////////////////////////////////////////////// + // underlying_type + // + // Defines a member typedef type of type that is the underlying type for the enumeration T. + // Requires explicit compiler support to implement. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && ((defined(_MSC_VER) && (_MSC_VER >= 1700)) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007)) || defined(__clang__)) // VS2012+ + #define EASTL_TYPE_TRAIT_underlying_type_CONFORMANCE 1 // underlying_type is conforming. + + template + struct underlying_type{ typedef __underlying_type(T) type; }; + + #else + #define EASTL_TYPE_TRAIT_underlying_type_CONFORMANCE 0 + + template + struct underlying_type{ typedef int type; }; // This is of course wrong, but we emulate libstdc++ and typedef it as int. + #endif + + #if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + using underlying_type_t = typename underlying_type::type; + #endif + + /////////////////////////////////////////////////////////////////////// + // to_underlying + // + // Cast a enum value to its underlying type. + // For example: + // + // enum class MyEnum : uint8_t { Value = 0; } + // auto x = MyEnum::Value; + // std::cout << to_underlying(x); // equivalent to sts::cout << static_cast(x); + /////////////////////////////////////////////////////////////////////// + + #if EASTL_VARIABLE_TEMPLATES_ENABLED && !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + constexpr underlying_type_t to_underlying(T value) noexcept + { + return static_cast>(value); + } + #endif + + + /////////////////////////////////////////////////////////////////////// + // has_unique_object_representations + // + // If T is TriviallyCopyable and if any two objects of type T with the same + // value have the same object representation, value is true. For any other + // type, value is false. + // + // http://en.cppreference.com/w/cpp/types/has_unique_object_representations + /////////////////////////////////////////////////////////////////////// + #if EASTL_HAS_UNIQUE_OBJECT_REPRESENTATIONS_AVAILABLE + #define EASTL_TYPE_TRAIT_has_unique_object_representations_CONFORMANCE 1 + + template + struct has_unique_object_representations + : public integral_constant>)> + { + }; + + #else + #define EASTL_TYPE_TRAIT_has_unique_object_representations_CONFORMANCE 0 + + template + struct has_unique_object_representations + : public integral_constant>>> // only integral types (floating point types excluded). + { + }; + + #endif + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR auto has_unique_object_representations_v = has_unique_object_representations::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_signed + // + // is_signed::value == true if and only if T is one of the following types: + // [const] [volatile] char (maybe) + // [const] [volatile] signed char + // [const] [volatile] short + // [const] [volatile] int + // [const] [volatile] long + // [const] [volatile] long long + // [const] [volatile] float + // [const] [volatile] double + // [const] [volatile] long double + // + // Used to determine if a integral type is signed or unsigned. + // Given that there are some user-made classes which emulate integral + // types, we provide the EASTL_DECLARE_SIGNED macro to allow you to + // set a given class to be identified as a signed type. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_signed_CONFORMANCE 1 // is_signed is conforming. + + template struct is_signed_helper : public false_type{}; + + template <> struct is_signed_helper : public true_type{}; + template <> struct is_signed_helper : public true_type{}; + template <> struct is_signed_helper : public true_type{}; + template <> struct is_signed_helper : public true_type{}; + template <> struct is_signed_helper : public true_type{}; + template <> struct is_signed_helper : public true_type{}; + template <> struct is_signed_helper : public true_type{}; + template <> struct is_signed_helper : public true_type{}; + + #if (CHAR_MAX == SCHAR_MAX) + template <> struct is_signed_helper : public true_type{}; + #endif + #ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type... + #if defined(__WCHAR_MAX__) && ((__WCHAR_MAX__ == 2147483647) || (__WCHAR_MAX__ == 32767)) // GCC defines __WCHAR_MAX__ for most platforms. + template <> struct is_signed_helper : public true_type{}; + #endif + #endif + + template + struct is_signed : public eastl::is_signed_helper::type>{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_signed_v = is_signed::value; + #endif + + #define EASTL_DECLARE_SIGNED(T) \ + namespace eastl{ \ + template <> struct is_signed : public true_type{}; \ + template <> struct is_signed : public true_type{}; \ + template <> struct is_signed : public true_type{}; \ + template <> struct is_signed : public true_type{}; \ + } + + + + /////////////////////////////////////////////////////////////////////// + // is_unsigned + // + // is_unsigned::value == true if and only if T is one of the following types: + // [const] [volatile] char (maybe) + // [const] [volatile] unsigned char + // [const] [volatile] unsigned short + // [const] [volatile] unsigned int + // [const] [volatile] unsigned long + // [const] [volatile] unsigned long long + // + // Used to determine if a integral type is signed or unsigned. + // Given that there are some user-made classes which emulate integral + // types, we provide the EASTL_DECLARE_UNSIGNED macro to allow you to + // set a given class to be identified as an unsigned type. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_unsigned_CONFORMANCE 1 // is_unsigned is conforming. + + template struct is_unsigned_helper : public false_type{}; + + template <> struct is_unsigned_helper : public true_type{}; + template <> struct is_unsigned_helper : public true_type{}; + template <> struct is_unsigned_helper : public true_type{}; + template <> struct is_unsigned_helper : public true_type{}; + template <> struct is_unsigned_helper : public true_type{}; + + #if (CHAR_MAX == UCHAR_MAX) + template <> struct is_unsigned_helper : public true_type{}; + #endif + #ifndef EA_WCHAR_T_NON_NATIVE // If wchar_t is a native type instead of simply a define to an existing type... + #if defined(_MSC_VER) || (defined(__WCHAR_MAX__) && ((__WCHAR_MAX__ == 4294967295U) || (__WCHAR_MAX__ == 65535))) // GCC defines __WCHAR_MAX__ for most platforms. + template <> struct is_unsigned_helper : public true_type{}; + #endif + #endif + + template + struct is_unsigned : public eastl::is_unsigned_helper::type>{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_unsigned_v = is_unsigned::value; + #endif + + #define EASTL_DECLARE_UNSIGNED(T) \ + namespace eastl{ \ + template <> struct is_unsigned : public true_type{}; \ + template <> struct is_unsigned : public true_type{}; \ + template <> struct is_unsigned : public true_type{}; \ + template <> struct is_unsigned : public true_type{}; \ + } + + + + /////////////////////////////////////////////////////////////////////// + // alignment_of + // + // alignment_of::value is an integral value representing, in bytes, + // the memory alignment of objects of type T. + // + // alignment_of may only be applied to complete types. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_alignment_of_CONFORMANCE 1 // alignment_of is conforming. + + template + struct alignment_of_value{ static const size_t value = EASTL_ALIGN_OF(T); }; + + template + struct alignment_of : public integral_constant::value>{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR size_t alignment_of_v = alignment_of::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_aligned + // + // Defined as true if the type has alignment requirements greater + // than default alignment, which is taken to be 8. This allows for + // doing specialized object allocation and placement for such types. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_aligned_CONFORMANCE 1 // is_aligned is conforming. + + template + struct is_aligned_value{ static const bool value = (EASTL_ALIGN_OF(T) > 8); }; + + template + struct is_aligned : public integral_constant::value>{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR size_t is_aligned_v = is_aligned::value; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // rank + // + // rank::value is an integral value representing the number of + // dimensions possessed by an array type. For example, given a + // multi-dimensional array type T[M][N], std::tr1::rank::value == 2. + // For a given non-array type T, std::tr1::rank::value == 0. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_rank_CONFORMANCE 1 // rank is conforming. + + template + struct rank : public eastl::integral_constant {}; + + template + struct rank : public eastl::integral_constant::value + 1> {}; + + template + struct rank : public eastl::integral_constant::value + 1> {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR auto rank_v = rank::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_base_of + // + // Given two (possibly identical) types Base and Derived, is_base_of::value == true + // if and only if Base is a direct or indirect base class of Derived, + // or Base and Derived are the same type. + // + // is_base_of may only be applied to complete types. + // + /////////////////////////////////////////////////////////////////////// + + #if EASTL_COMPILER_INTRINSIC_TYPE_TRAITS_AVAILABLE && (defined(_MSC_VER) || defined(EA_COMPILER_GNUC) || ((defined(__clang__)) && EA_COMPILER_HAS_FEATURE(is_base_of))) + #define EASTL_TYPE_TRAIT_is_base_of_CONFORMANCE 1 // is_base_of is conforming. + + template + struct is_base_of : public eastl::integral_constant::value>{}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_base_of_v = is_base_of::value; + #endif + #else + // Not implemented yet. + // This appears to be implementable. + #define EASTL_TYPE_TRAIT_is_base_of_CONFORMANCE 0 + #endif + + + + /////////////////////////////////////////////////////////////////////// + // is_lvalue_reference + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_lvalue_reference_CONFORMANCE 1 // is_lvalue_reference is conforming. + + template struct is_lvalue_reference : public eastl::false_type {}; + template struct is_lvalue_reference : public eastl::true_type {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_lvalue_reference_v = is_lvalue_reference::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // is_rvalue_reference + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_is_rvalue_reference_CONFORMANCE 1 // is_rvalue_reference is conforming. + + template struct is_rvalue_reference : public eastl::false_type {}; + template struct is_rvalue_reference : public eastl::true_type {}; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR bool is_rvalue_reference_v = is_rvalue_reference::value; + #endif + + + /////////////////////////////////////////////////////////////////////// + // result_of + // + /////////////////////////////////////////////////////////////////////// + #define EASTL_TYPE_TRAIT_result_of_CONFORMANCE 1 // result_of is conforming. + + template struct result_of; + + template + struct result_of + { typedef decltype(eastl::declval()(eastl::declval()...)) type; }; + + + // result_of_t is the C++14 using typedef for typename result_of::type. + // We provide a backwards-compatible means to access it through a macro for pre-C++11 compilers. + #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + #define EASTL_RESULT_OF_T(T) typename result_of::type + #else + template + using result_of_t = typename result_of::type; + #define EASTL_RESULT_OF_T(T) result_of_t + #endif + + + /////////////////////////////////////////////////////////////////////// + // has_equality + // + // Determines if the specified type can be tested for equality. + // + /////////////////////////////////////////////////////////////////////// + template > + struct has_equality : eastl::false_type {}; + + template + struct has_equality() == eastl::declval())>> : eastl::true_type + { + }; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + EA_CONSTEXPR auto has_equality_v = has_equality::value; + #endif + + namespace internal + { + /////////////////////////////////////////////////////////////////////// + // is_complete_type + // + // Determines if the specified type is complete + // + // Warning: Be careful when using is_complete_type since the value is fixed at first instantiation. + // Consider the following: + // + // struct Foo; + // is_complete_type_v // false + // struct Foo {}; + // is_complete_type_v // still false + /////////////////////////////////////////////////////////////////////// + + template + struct is_complete_type : public false_type {}; + + template + struct is_complete_type> : public true_type {}; + + template<> + struct is_complete_type : public false_type {}; + template<> + struct is_complete_type : public false_type {}; + template<> + struct is_complete_type : public false_type {}; + template<> + struct is_complete_type : public false_type {}; + + template + struct is_complete_type>> : public true_type {}; + + template + EASTL_CPP17_INLINE_VARIABLE EA_CONSTEXPR bool is_complete_type_v = is_complete_type::value; + } + +} // namespace eastl + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/internal/type_transformations.h b/lib/EASTL/include/EASTL/internal/type_transformations.h new file mode 100644 index 000000000..2d77a556f --- /dev/null +++ b/lib/EASTL/include/EASTL/internal/type_transformations.h @@ -0,0 +1,800 @@ +///////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTERNAL_TYPE_TRANFORMATIONS_H +#define EASTL_INTERNAL_TYPE_TRANFORMATIONS_H + + +#include +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once +#endif + +#include + + +namespace eastl +{ + + /////////////////////////////////////////////////////////////////////// + // add_const + // + // Add const to a type. + // + // Tor a given type T, add_const::type is equivalent to T + // const if is_const::value == false, and + // - is_void::value == true, or + // - is_object::value == true. + // + // Otherwise, add_const::type is equivalent to T. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_add_const_CONFORMANCE 1 // add_const is conforming. + + template ::value || eastl::is_reference::value || eastl::is_function::value> + struct add_const_helper + { typedef T type; }; + + template + struct add_const_helper + { typedef const T type; }; + + template + struct add_const + { typedef typename eastl::add_const_helper::type type; }; + + // add_const_t is the C++17 using typedef for typename add_const::type. + // We provide a backwards-compatible means to access it through a macro for pre-C++11 compilers. + #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + #define EASTL_ADD_CONST_T(T) typename add_const::type + #else + template + using add_const_t = typename add_const::type; + #define EASTL_ADD_CONST_T(T) add_const_t + #endif + + + /////////////////////////////////////////////////////////////////////// + // add_volatile + // + // Add volatile to a type. + // + // For a given type T, add_volatile::type is equivalent to T volatile + // if is_volatile::value == false, and + // - is_void::value == true, or + // - is_object::value == true. + // + // Otherwise, add_volatile::type is equivalent to T. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_add_volatile_CONFORMANCE 1 // add_volatile is conforming. + + template ::value || eastl::is_reference::value || eastl::is_function::value> + struct add_volatile_helper + { typedef T type; }; + + template + struct add_volatile_helper + { typedef volatile T type; }; + + template struct add_volatile + { typedef typename eastl::add_volatile_helper::type type; }; + + template using add_volatile_t = typename add_volatile::type; + + + /////////////////////////////////////////////////////////////////////// + // add_cv + // + // The add_cv transformation trait adds const and volatile qualification + // to the type to which it is applied. For a given type T, + // add_volatile::type is equivalent to add_const::type>::type. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_add_cv_CONFORMANCE 1 // add_cv is conforming. + + template + struct add_cv + { + typedef typename add_const::type>::type type; + }; + + template using add_cv_t = typename add_cv::type; + + + /////////////////////////////////////////////////////////////////////// + // make_signed + // + // Used to convert an integral type to its signed equivalent, if not already. + // T shall be a (possibly const and/or volatile-qualified) integral type + // or enumeration but not a bool type.; + // + // The user can define their own make_signed overrides for their own + // types by making a template specialization like done below and adding + // it to the user's code. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_make_signed_CONFORMANCE 1 + + namespace internal + { + template || eastl::is_integral_v> + struct make_signed_helper_0 + { + struct char_helper + { + typedef signed char type; + }; + + struct short_helper + { + typedef signed short type; + }; + + struct int_helper + { + typedef signed int type; + }; + + struct long_helper + { + typedef signed long type; + }; + + struct longlong_helper + { + typedef signed long long type; + }; + + struct int128_helper + { + #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + typedef __int128_t type; + #endif + }; + + struct no_type_helper + { + }; + + typedef typename + eastl::conditional + #else + no_type_helper + #endif + > + > + > + > + >::type type; + }; + + template + struct make_signed_helper_0 + { + struct no_type_helper + { + }; + + typedef no_type_helper type; + }; + + template + struct make_signed_helper_1 + { + typedef typename T::type type; + }; + + template + struct make_signed_helper + { + typedef typename eastl::internal::make_signed_helper_1::type>::type type; + }; + + } // namespace internal + + template + struct make_signed + { + typedef typename eastl::internal::make_signed_helper::type type; + }; + + template <> struct make_signed {}; + template <> struct make_signed { typedef signed char type; }; + template <> struct make_signed { typedef signed char type; }; + template <> struct make_signed { typedef signed short type; }; + template <> struct make_signed { typedef signed short type; }; + template <> struct make_signed { typedef signed int type; }; + template <> struct make_signed { typedef signed int type; }; + template <> struct make_signed { typedef signed long type; }; + template <> struct make_signed { typedef signed long type; }; + template <> struct make_signed { typedef signed long long type; }; + template <> struct make_signed { typedef signed long long type; }; + #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + template <> struct make_signed<__int128_t> { typedef __int128_t type; }; + template <> struct make_signed<__uint128_t> { typedef __int128_t type; }; + #endif + + + #if (defined(CHAR_MAX) && defined(UCHAR_MAX) && (CHAR_MAX == UCHAR_MAX)) // If char is unsigned, we convert char to signed char. However, if char is signed then make_signed returns char itself and not signed char. + template <> struct make_signed { typedef signed char type; }; + #endif + + template + struct make_signed + { + typedef eastl::add_const_t::type> type; + }; + + template + struct make_signed + { + typedef eastl::add_volatile_t::type> type; + }; + + template + struct make_signed + { + typedef eastl::add_cv_t::type> type; + }; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + using make_signed_t = typename make_signed::type; + #endif + + + /////////////////////////////////////////////////////////////////////// + // add_signed + // + // This is not a C++11 type trait, and is here for backwards compatibility + // only. Use the C++11 make_unsigned type trait instead. + /////////////////////////////////////////////////////////////////////// + + template + struct add_signed : public make_signed + { typedef typename eastl::make_signed::type type; }; + + + + + /////////////////////////////////////////////////////////////////////// + // make_unsigned + // + // Used to convert an integral type to its unsigned equivalent, if not already. + // T shall be a (possibly const and/or volatile-qualified) integral type + // or enumeration but not a bool type.; + // + // The user can define their own make_unsigned overrides for their own + // types by making a template specialization like done below and adding + // it to the user's code. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_make_unsigned_CONFORMANCE 1 + + namespace internal + { + + template ::value || eastl::is_integral::value> + struct make_unsigned_helper_0 + { + struct char_helper + { + typedef unsigned char type; + }; + + struct short_helper + { + typedef unsigned short type; + }; + + struct int_helper + { + typedef unsigned int type; + }; + + struct long_helper + { + typedef unsigned long type; + }; + + struct longlong_helper + { + typedef unsigned long long type; + }; + + struct int128_helper + { + #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + typedef __uint128_t type; + #endif + }; + + struct no_type_helper + { + }; + + + typedef typename + eastl::conditional + #else + no_type_helper + #endif + > + > + > + > + >::type type; + }; + + + template + struct make_unsigned_helper_0 + { + struct no_type_helper + { + }; + + typedef no_type_helper type; + }; + + template + struct make_unsigned_helper_1 + { + typedef typename T::type type; + }; + + template + struct make_unsigned_helper + { + typedef typename eastl::internal::make_unsigned_helper_1::type>::type type; + }; + + } // namespace internal + + template + struct make_unsigned + { + typedef typename eastl::internal::make_unsigned_helper::type type; + }; + + template <> struct make_unsigned {}; + template <> struct make_unsigned { typedef unsigned char type; }; + template <> struct make_unsigned { typedef unsigned char type; }; + template <> struct make_unsigned { typedef unsigned short type; }; + template <> struct make_unsigned { typedef unsigned short type; }; + template <> struct make_unsigned { typedef unsigned int type; }; + template <> struct make_unsigned { typedef unsigned int type; }; + template <> struct make_unsigned { typedef unsigned long type; }; + template <> struct make_unsigned { typedef unsigned long type; }; + template <> struct make_unsigned { typedef unsigned long long type; }; + template <> struct make_unsigned { typedef unsigned long long type; }; + #if EASTL_INT128_SUPPORTED && (defined(EA_COMPILER_GNUC) || defined(__clang__)) + template <> struct make_unsigned<__int128_t> { typedef __uint128_t type; }; + template <> struct make_unsigned<__uint128_t> { typedef __uint128_t type; }; + #endif + + #if (CHAR_MIN < 0) // If char is signed, we convert char to unsigned char. However, if char is unsigned then make_unsigned returns char itself and not unsigned char. + template <> struct make_unsigned { typedef unsigned char type; }; + #endif + + #if defined(EA_CHAR8_UNIQUE) && EA_CHAR8_UNIQUE + template <> struct make_unsigned { typedef unsigned char type; }; + #endif + + template + struct make_unsigned + { + typedef eastl::add_const_t::type> type; + }; + + template + struct make_unsigned + { + typedef eastl::add_volatile_t::type> type; + }; + + template + struct make_unsigned + { + typedef eastl::add_cv_t::type> type; + }; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + using make_unsigned_t = typename make_unsigned::type; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // add_unsigned + // + // This is not a C++11 type trait, and is here for backwards compatibility + // only. Use the C++11 make_unsigned type trait instead. + // + // Adds unsigned-ness to the given type. + // Modifies only integral values; has no effect on others. + // add_unsigned::type is unsigned int + // add_unsigned::type is unsigned int + // + /////////////////////////////////////////////////////////////////////// + + template + struct add_unsigned : public make_unsigned + { typedef typename eastl::make_signed::type type; }; + + + + /////////////////////////////////////////////////////////////////////// + // remove_pointer + // + // Remove pointer from a type. + // + // The remove_pointer transformation trait removes top-level indirection + // by pointer (if any) from the type to which it is applied. Pointers to + // members are not affected. For a given type T, remove_pointer::type + // is equivalent to T. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_remove_pointer_CONFORMANCE 1 + + template struct remove_pointer { typedef T type; }; + template struct remove_pointer { typedef T type; }; + template struct remove_pointer { typedef T type; }; + template struct remove_pointer { typedef T type; }; + template struct remove_pointer { typedef T type; }; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + using remove_pointer_t = typename remove_pointer::type; + #endif + + + /////////////////////////////////////////////////////////////////////// + // add_pointer + // + // Add pointer to a type. + // Provides the member typedef type which is the type T*. If T is a + // reference type, then type is a pointer to the referred type. + // + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_add_pointer_CONFORMANCE 1 + + template + struct add_pointer { typedef typename eastl::remove_reference::type* type; }; + + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + using add_pointer_t = typename add_pointer::type; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // remove_extent + // + // The remove_extent transformation trait removes a dimension from an array. + // For a given non-array type T, remove_extent::type is equivalent to T. + // For a given array type T[N], remove_extent::type is equivalent to T. + // For a given array type const T[N], remove_extent::type is equivalent to const T. + // For example, given a multi-dimensional array type T[M][N], remove_extent::type is equivalent to T[N]. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_remove_extent_CONFORMANCE 1 // remove_extent is conforming. + + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; + + #if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + using remove_extent_t = typename remove_extent::type; + #endif + + + /////////////////////////////////////////////////////////////////////// + // remove_all_extents + // + // The remove_all_extents transformation trait removes all dimensions from an array. + // For a given non-array type T, remove_all_extents::type is equivalent to T. + // For a given array type T[N], remove_all_extents::type is equivalent to T. + // For a given array type const T[N], remove_all_extents::type is equivalent to const T. + // For example, given a multi-dimensional array type T[M][N], remove_all_extents::type is equivalent to T. + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_remove_all_extents_CONFORMANCE 1 // remove_all_extents is conforming. + + template struct remove_all_extents { typedef T type; }; + template struct remove_all_extents { typedef typename eastl::remove_all_extents::type type; }; + template struct remove_all_extents { typedef typename eastl::remove_all_extents::type type; }; + + #if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + template + using remove_all_extents_t = typename remove_all_extents::type; + #endif + + + + /////////////////////////////////////////////////////////////////////// + // aligned_storage + // + // The aligned_storage transformation trait provides a type that is + // suitably aligned to store an object whose size is does not exceed length + // and whose alignment is a divisor of alignment. When using aligned_storage, + // length must be non-zero, and alignment must >= alignment_of::value + // for some type T. We require the alignment value to be a power-of-two. + // + // GCC versions prior to 4.4 don't properly support this with stack-based + // variables. The EABase EA_ALIGN_MAX_AUTOMATIC define identifies the + // extent to which stack (automatic) variables can be aligned for the + // given compiler/platform combination. + // + // Example usage: + // aligned_storage::type widget; + // Widget* pWidget = new(&widget) Widget; + // + // aligned_storage::type widgetAlignedTo64; + // Widget* pWidget = new(&widgetAlignedTo64) Widget; + // + // aligned_storage::type widgetArray[37]; + // Widget* pWidgetArray = new(widgetArray) Widget[37]; + /////////////////////////////////////////////////////////////////////// + + #define EASTL_TYPE_TRAIT_aligned_storage_CONFORMANCE 1 // aligned_storage is conforming. + + #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4008) + // New versions of GCC do not support using 'alignas' with a value greater than 128. + // However, this code using the GNU standard alignment attribute works properly. + template + struct aligned_storage + { + struct type { unsigned char mCharData[N]; } EA_ALIGN(Align); + }; + #elif (EABASE_VERSION_N >= 20040) && !defined(EA_COMPILER_NO_ALIGNAS) // If C++11 alignas is supported... + template + struct aligned_storage + { + typedef struct { + alignas(Align) unsigned char mCharData[N]; + } type; + }; + + #elif defined(EA_COMPILER_MSVC) || (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION < 4007)) || defined(EA_COMPILER_EDG) // At some point GCC fixed their attribute(align) to support non-literals, though it's not clear what version aside from being no later than 4.7 and no earlier than 4.2. + // Some compilers don't allow you to to use EA_ALIGNED with anything by a numeric literal, + // so we can't use the simpler code like we do further below for other compilers. We support + // only up to so much of an alignment value here. + template + struct aligned_storage_helper { struct type{ unsigned char mCharData[N]; }; }; + + template struct aligned_storage_helper { struct EA_ALIGN( 2) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN( 4) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN( 8) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN( 16) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN( 32) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN( 64) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN( 128) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN( 256) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN( 512) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN(1024) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN(2048) type{ unsigned char mCharData[N]; }; }; + template struct aligned_storage_helper { struct EA_ALIGN(4096) type{ unsigned char mCharData[N]; }; }; + + template + struct aligned_storage + { + typedef typename aligned_storage_helper::type type; + }; + + #else + template + struct aligned_storage + { + union type + { + unsigned char mCharData[N]; + struct EA_ALIGN(Align) mStruct{ }; + }; + }; + #endif + + #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + #define EASTL_ALIGNED_STORAGE_T(N, Align) typename eastl::aligned_storage_t::type + #else + template + using aligned_storage_t = typename aligned_storage::type; + #define EASTL_ALIGNED_STORAGE_T(N, Align) eastl::aligned_storage_t + #endif + + + + /////////////////////////////////////////////////////////////////////// + // aligned_union + // + // The member typedef type shall be a POD type suitable for use as + // uninitialized storage for any object whose type is listed in Types; + // its size shall be at least Len. The static member alignment_value + // shall be an integral constant of type std::size_t whose value is + // the strictest alignment of all types listed in Types. + // Note that the resulting type is not a C/C++ union, but simply memory + // block (of pod type) that can be used to placement-new an actual + // C/C++ union of the types. The actual union you declare can be a non-POD union. + // + // Example usage: + // union MyUnion { + // char c; + // int i; + // float f; + // + // MyUnion(float fValue) : f(fValue) {} + // }; + // + // aligned_union::type myUnionStorage; + // MyUnion* pMyUnion = new(&myUnionStorage) MyUnion(21.4f); + // pMyUnion->i = 37; + // + /////////////////////////////////////////////////////////////////////// + + #if defined(EA_COMPILER_NO_VARIADIC_TEMPLATES) || !EASTL_TYPE_TRAIT_static_max_CONFORMANCE + #define EASTL_TYPE_TRAIT_aligned_union_CONFORMANCE 0 // aligned_union is not conforming, as it supports only a two-member unions. + + // To consider: Expand this to include more possible types. We may want to convert this to be a recursive + // template instead of like below. + template + struct aligned_union + { + static const size_t size0 = eastl::static_max::value; + static const size_t size1 = eastl::static_max::value; + static const size_t size2 = eastl::static_max::value; + static const size_t size = eastl::static_max::value; + + static const size_t alignment0 = eastl::static_max::value; + static const size_t alignment1 = eastl::static_max::value; + static const size_t alignment_value = eastl::static_max::value; + + typedef typename eastl::aligned_storage::type type; + }; + + #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + // To do: define macro. + #else + template + using aligned_union_t = typename aligned_union::type; + #endif + #else + #define EASTL_TYPE_TRAIT_aligned_union_CONFORMANCE 1 // aligned_union is conforming. + + template + struct aligned_union + { + static const size_t size = eastl::static_max::value; + static const size_t alignment_value = eastl::static_max::value; + + typedef typename eastl::aligned_storage::type type; + }; + + #if defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + // To do: define macro. + #else + template + using aligned_union_t = typename aligned_union::type; + #endif + + #endif + + + /////////////////////////////////////////////////////////////////////// + // union_cast + // + // Safely converts between unrelated types that have a binary equivalency. + // This appoach is required by strictly conforming C++ compilers because + // directly using a C or C++ cast between unrelated types is fraught with + // the possibility of undefined runtime behavior due to type aliasing. + // The Source and Dest types must be POD types due to the use of a union + // in C++ versions prior to C++11. C++11 relaxes the definition of a POD + // such that it allows a classes with trivial default constructors whereas + // previous versions did not, so beware of this when writing portable code. + // + // Example usage: + // float f32 = 1.234f; + // uint32_t n32 = union_cast(f32); + // + // Example possible mis-usage: + // The following is valid only if you are aliasing the pointer value and + // not what it points to. Most of the time the user intends the latter, + // which isn't strictly possible. + // Widget* pWidget = CreateWidget(); + // Foo* pFoo = union_cast(pWidget); + /////////////////////////////////////////////////////////////////////// + + template + DestType union_cast(SourceType sourceValue) + { + EASTL_CT_ASSERT((sizeof(DestType) == sizeof(SourceType)) && + (EA_ALIGN_OF(DestType) == EA_ALIGN_OF(SourceType))); // To support differening alignments, we would need to use a memcpy-based solution or find a way to make the two union members align with each other. + //EASTL_CT_ASSERT(is_pod::value && is_pod::value); // Disabled because we don't want to restrict what the user can do, as some compiler's definitions of is_pod aren't up to C++11 Standards. + //EASTL_CT_ASSERT(!is_pointer::value && !is_pointer::value); // Disabled because it's valid to alias pointers as long as you are aliasong the pointer value and not what it points to. + + union { + SourceType sourceValue; + DestType destValue; + } u; + u.sourceValue = sourceValue; + + return u.destValue; + } + + + + /////////////////////////////////////////////////////////////////////// + // void_t + // + // Maps a sequence of any types to void. This utility class is used in + // template meta programming to simplify compile time reflection mechanisms + // required by the standard library. + // + // http://en.cppreference.com/w/cpp/types/void_t + // + // Example: + // template + // struct is_iterable : false_type {}; + // + // template + // struct is_iterable().begin()), + // decltype(declval().end())>> : true_type {}; + // + /////////////////////////////////////////////////////////////////////// + #if EASTL_VARIABLE_TEMPLATES_ENABLED + template + using void_t = void; + #endif + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/intrusive_hash_map.h b/lib/EASTL/include/EASTL/intrusive_hash_map.h new file mode 100644 index 000000000..37f161883 --- /dev/null +++ b/lib/EASTL/include/EASTL/intrusive_hash_map.h @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_INTRUSIVE_HASH_MAP_H +#define EASTL_INTRUSIVE_HASH_MAP_H + + +#include +#include +#include +#include + +#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_hash_map + /// + /// Template parameters: + /// Key The key object (key in the key/value pair). T must contain a member of type Key named mKey. + /// T The type of object the map holds (a.k.a. value). + /// bucketCount The number of buckets to use. Best if it's a prime number. + /// Hash Hash function. See functional.h for examples of hash functions. + /// Equal Equality testing predicate; tells if two elements are equal. + /// + template , typename Equal = eastl::equal_to > + class intrusive_hash_map : public intrusive_hashtable + { + public: + typedef intrusive_hashtable base_type; + typedef intrusive_hash_map this_type; + + public: + explicit intrusive_hash_map(const Hash& h = Hash(), const Equal& eq = Equal()) + : base_type(h, eq) + { + // Empty + } + + // To consider: Is this feasible, given how initializer_list works by creating a temporary array? Even if it is feasible, is it a good idea? + //intrusive_hash_map(std::initializer_list ilist); + + }; // intrusive_hash_map + + + + + /// intrusive_hash_multimap + /// + /// Implements a intrusive_hash_multimap, which is the same thing as a intrusive_hash_map + /// except that contained elements need not be unique. See the documentation + /// for intrusive_hash_map for details. + /// + /// Template parameters: + /// Key The key object (key in the key/value pair). T must contain a member of type Key named mKey. + /// T The type of object the map holds (a.k.a. value). + /// bucketCount The number of buckets to use. Best if it's a prime number. + /// Hash Hash function. See functional.h for examples of hash functions. + /// Equal Equality testing predicate; tells if two elements are equal. + /// + template , typename Equal = eastl::equal_to > + class intrusive_hash_multimap : public intrusive_hashtable + { + public: + typedef intrusive_hashtable base_type; + typedef intrusive_hash_multimap this_type; + + public: + explicit intrusive_hash_multimap(const Hash& h = Hash(), const Equal& eq = Equal()) + : base_type(h, eq) + { + // Empty + } + + // To consider: Is this feasible, given how initializer_list works by creating a temporary array? Even if it is feasible, is it a good idea? + //intrusive_hash_multimap(std::initializer_list ilist); + + }; // intrusive_hash_multimap + + + + +} // namespace eastl + + +#endif // Header include guard + + + + + + diff --git a/lib/EASTL/include/EASTL/intrusive_hash_set.h b/lib/EASTL/include/EASTL/intrusive_hash_set.h new file mode 100644 index 000000000..a25d03a62 --- /dev/null +++ b/lib/EASTL/include/EASTL/intrusive_hash_set.h @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EASTL_INTRUSIVE_HASH_SET_H +#define EASTL_INTRUSIVE_HASH_SET_H + + +#include +#include +#include +#include + +#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_hash_set + /// + /// Template parameters: + /// T The type of object the set holds (a.k.a. value). + /// bucketCount The number of buckets to use. Best if it's a prime number. + /// Hash Hash function. See functional.h for examples of hash functions. + /// Equal Equality testing predicate; tells if two elements are equal. + /// + template , typename Equal = eastl::equal_to > + class intrusive_hash_set : public intrusive_hashtable + { + public: + typedef intrusive_hashtable base_type; + typedef intrusive_hash_set this_type; + + public: + explicit intrusive_hash_set(const Hash& h = Hash(), const Equal& eq = Equal()) + : base_type(h, eq) + { + // Empty + } + + // To consider: Is this feasible, given how initializer_list works by creating a temporary array? Even if it is feasible, is it a good idea? + //intrusive_hash_set(std::initializer_list ilist); + + }; // intrusive_hash_set + + + + + /// intrusive_hash_multiset + /// + /// Implements a intrusive_hash_multiset, which is the same thing as a intrusive_hash_set + /// except that contained elements need not be unique. See the documentation + /// for intrusive_hash_set for details. + /// + /// Template parameters: + /// T The type of object the set holds (a.k.a. value). + /// bucketCount The number of buckets to use. Best if it's a prime number. + /// Hash Hash function. See functional.h for examples of hash functions. + /// Equal Equality testing predicate; tells if two elements are equal. + /// + template , typename Equal = eastl::equal_to > + class intrusive_hash_multiset : public intrusive_hashtable + { + public: + typedef intrusive_hashtable base_type; + typedef intrusive_hash_multiset this_type; + + public: + explicit intrusive_hash_multiset(const Hash& h = Hash(), const Equal& eq = Equal()) + : base_type(h, eq) + { + // Empty + } + + // To consider: Is this feasible, given how initializer_list works by creating a temporary array? Even if it is feasible, is it a good idea? + //intrusive_hash_multiset(std::initializer_list ilist); + + }; // intrusive_hash_multiset + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/intrusive_list.h b/lib/EASTL/include/EASTL/intrusive_list.h new file mode 100644 index 000000000..dc0129f30 --- /dev/null +++ b/lib/EASTL/include/EASTL/intrusive_list.h @@ -0,0 +1,1323 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// The intrusive list container is similar to a list, with the primary +// different being that intrusive lists allow you to control memory +// allocation. +// +// * Intrusive lists store the nodes directly in the data items. This +// is done by deriving the object from intrusive_list_node. +// +// * The container does no memory allocation -- it works entirely with +// the submitted nodes. This does mean that it is the client's job to +// free the nodes in an intrusive list, though. +// +// * Valid node pointers can be converted back to iterators in O(1). +// This is because objects in the list are also nodes in the list. +// +// * intrusive_list does not support copy construction or assignment; +// the push, pop, and insert operations take ownership of the +// passed object. +// +// Usage notes: +// +// * You can use an intrusive_list directly with the standard nodes +// if you have some other way of converting the node pointer back +// to your data pointer. +// +// * Remember that the list destructor doesn't deallocate nodes -- it can't. +// +// * The size is not cached; this makes size() linear time but splice() is +// constant time. This does mean that you can remove() an element without +// having to figure out which list it is in, however. +// +// * You can insert a node into multiple intrusive_lists. One way to do so +// is to (ab)use inheritance: +// +// struct NodeA : public intrusive_list_node {}; +// struct NodeB : public intrusive_list_node {}; +// struct Object : public NodeA, nodeB {}; +// +// intrusive_list listA; +// intrusive_list listB; +// +// listA.push_back(obj); +// listB.push_back(obj); +// +// * find() vs. locate() +// The find(v) algorithm returns an iterator p such that *p == v; intrusive_list::locate(v) +// returns an iterator p such that &*p == &v. intrusive_list<> doesn't have find() mainly +// because list<> doesn't have it either, but there's no reason it couldn't. intrusive_list +// uses the name 'find' because: +// - So as not to confuse the member function with the well-defined free function from algorithm.h. +// - Because it is not API-compatible with eastl::find(). +// - Because it simply locates an object within the list based on its node entry and doesn't perform before any value-based searches or comparisons. +// +// Differences between intrusive_list and std::list: +// +// Issue std::list intrusive_list +// -------------------------------------------------------------- +// Automatic node ctor/dtor Yes No +// Can memmove() container Maybe* No +// Same item in list twice Yes(copy/byref) No +// Can store non-copyable items No Yes +// size() O(1) or O(n) O(n) +// clear() O(n) O(1) +// erase(range) O(n) O(1) +// splice(range) O(1) or O(n) O(1) +// Convert reference to iterator No O(1) +// Remove without container No O(1) +// Nodes in mixed allocators No Yes +// +// *) Not required by standard but can be done with some STL implementations. +// +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTRUSIVE_LIST_H +#define EASTL_INTRUSIVE_LIST_H + + +#include +#include +#include + +#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_list_node + /// + /// By design this must be a POD, as user structs will be inheriting from + /// it and they may wish to remain POD themselves. However, if the + /// EASTL_VALIDATE_INTRUSIVE_LIST option is enabled + /// + struct intrusive_list_node + { + intrusive_list_node* mpNext; + intrusive_list_node* mpPrev; + + #if EASTL_VALIDATE_INTRUSIVE_LIST + intrusive_list_node() // Implemented inline because GCC can't deal with member functions + { // of may-alias classes being defined outside the declaration. + mpNext = mpPrev = NULL; + } + + ~intrusive_list_node() + { + #if EASTL_ASSERT_ENABLED + if(mpNext || mpPrev) + EASTL_FAIL_MSG("~intrusive_list_node(): List is non-empty."); + #endif + } + #endif + } EASTL_MAY_ALIAS; // It's not clear if this really should be needed. An old GCC compatible compiler is generating some crashing optimized code when strict aliasing is enabled, but analysis of it seems to blame the compiler. However, this topic can be tricky. + + + + /// intrusive_list_iterator + /// + template + class intrusive_list_iterator + { + public: + typedef intrusive_list_iterator this_type; + typedef intrusive_list_iterator iterator; + typedef intrusive_list_iterator const_iterator; + typedef T value_type; + typedef T node_type; + typedef ptrdiff_t difference_type; + typedef Pointer pointer; + typedef Reference reference; + typedef EASTL_ITC_NS::bidirectional_iterator_tag iterator_category; + + public: + pointer mpNode; // Needs to be public for operator==() to work + + public: + intrusive_list_iterator(); + explicit intrusive_list_iterator(pointer pNode); // Note that you can also construct an iterator from T via this, since value_type == node_type. + intrusive_list_iterator(const iterator& x); + intrusive_list_iterator& operator=(const iterator& x); + + reference operator*() const; + pointer operator->() const; + + intrusive_list_iterator& operator++(); + intrusive_list_iterator& operator--(); + + intrusive_list_iterator operator++(int); + intrusive_list_iterator operator--(int); + + }; // class intrusive_list_iterator + + + + /// intrusive_list_base + /// + class intrusive_list_base + { + public: + typedef eastl_size_t size_type; // See config.h for the definition of this, which defaults to size_t. + typedef ptrdiff_t difference_type; + + protected: + intrusive_list_node mAnchor; ///< Sentinel node (end). All data nodes are linked in a ring from this node. + + public: + intrusive_list_base(); + ~intrusive_list_base(); + + bool empty() const EA_NOEXCEPT; + eastl_size_t size() const EA_NOEXCEPT; ///< Returns the number of elements in the list; O(n). + void clear() EA_NOEXCEPT; ///< 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 exist, but is not deallocated. + void pop_back(); ///< Removes an element from the back of the list; O(1). The element must exist, but is not deallocated. + EASTL_API void reverse() EA_NOEXCEPT; ///< Reverses a list so that front and back are swapped; O(n). + + EASTL_API 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_list_base + + + + /// intrusive_list + /// + /// Example usage: + /// struct IntNode : public eastl::intrusive_list_node { + /// int mX; + /// IntNode(int x) : mX(x) { } + /// }; + /// + /// IntNode nodeA(0); + /// IntNode nodeB(1); + /// + /// intrusive_list intList; + /// intList.push_back(nodeA); + /// intList.push_back(nodeB); + /// intList.remove(nodeA); + /// + template + class intrusive_list : public intrusive_list_base + { + public: + typedef intrusive_list this_type; + typedef intrusive_list_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 intrusive_list_iterator iterator; + typedef intrusive_list_iterator const_iterator; + typedef eastl::reverse_iterator reverse_iterator; + typedef eastl::reverse_iterator const_reverse_iterator; + + public: + intrusive_list(); ///< Creates an empty list. + intrusive_list(const this_type& x); ///< Creates an empty list; ignores the argument. + //intrusive_list(std::initializer_list ilist); To consider: Is this feasible, given how initializer_list works by creating a temporary array? Even if it is feasible, is it a good idea? + + this_type& operator=(const this_type& x); ///< Clears the list; ignores the argument. + void swap(this_type&); ///< Swaps the contents of two intrusive lists; O(1). + + iterator begin() EA_NOEXCEPT; ///< Returns an iterator pointing to the first element in the list. + const_iterator begin() const EA_NOEXCEPT; ///< Returns a const_iterator pointing to the first element in the list. + const_iterator cbegin() const EA_NOEXCEPT; ///< Returns a const_iterator pointing to the first element in the list. + + iterator end() EA_NOEXCEPT; ///< Returns an iterator pointing one-after the last element in the list. + const_iterator end() const EA_NOEXCEPT; ///< Returns a const_iterator pointing one-after the last element in the list. + const_iterator cend() const EA_NOEXCEPT; ///< Returns a const_iterator pointing one-after the last element in the list. + + reverse_iterator rbegin() EA_NOEXCEPT; ///< Returns a reverse_iterator pointing at the end of the list (start of the reverse sequence). + const_reverse_iterator rbegin() const EA_NOEXCEPT; ///< Returns a const_reverse_iterator pointing at the end of the list (start of the reverse sequence). + const_reverse_iterator crbegin() const EA_NOEXCEPT; ///< Returns a const_reverse_iterator pointing at the end of the list (start of the reverse sequence). + + reverse_iterator rend() EA_NOEXCEPT; ///< Returns a reverse_iterator pointing at the start of the list (end of the reverse sequence). + const_reverse_iterator rend() const EA_NOEXCEPT; ///< Returns a const_reverse_iterator pointing at the start of the list (end of the reverse sequence). + const_reverse_iterator crend() const EA_NOEXCEPT; ///< Returns a const_reverse_iterator pointing at the start of the list (end of the reverse sequence). + + reference front(); ///< Returns a reference to the first element. The list must be non-empty. + const_reference front() const; ///< Returns a const reference to the first element. The list must be non-empty. + reference back(); ///< Returns a reference to the last element. The list must be non-empty. + const_reference back() const; ///< Returns a const reference to the last element. The list must be non-empty. + + void push_front(value_type& x); ///< 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& x); ///< Adds an element to the back of the list; O(1). The element is not copied. The element must not be in any other list. + + bool contains(const value_type& x) const; ///< Returns true if the given element is in the list; O(n). Equivalent to (locate(x) != end()). + + iterator locate(value_type& x); ///< 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& x) 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(const_iterator pos, value_type& x); ///< Inserts an element before the element pointed to by the iterator. O(1) + iterator erase(const_iterator pos); ///< Erases the element pointed to by the iterator. O(1) + iterator erase(const_iterator pos, const_iterator last); ///< Erases elements within the iterator range [pos, last). O(1) + + reverse_iterator erase(const_reverse_iterator pos); + reverse_iterator erase(const_reverse_iterator pos, const_reverse_iterator last); + + 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(const_iterator pos, value_type& x); + ///< Moves the given element into this list before the element pointed to by pos; O(1). + ///< Required: x must be in some list or have first/next pointers that point it itself. + + void splice(const_iterator pos, intrusive_list& x); + ///< Moves the contents of a list into this list before the element pointed to by pos; O(1). + ///< Required: &x != this (same as std::list). + + void splice(const_iterator pos, intrusive_list& x, const_iterator i); + ///< Moves the given element pointed to i within the list x into the current list before + ///< the element pointed to by pos; O(1). + + void splice(const_iterator pos, intrusive_list& x, const_iterator first, const_iterator last); + ///< Moves the range of elements [first, last) from list x into the current list before + ///< the element pointed to by pos; O(1). + ///< Required: pos must not be in [first, last). (same as std::list). + + public: + // Sorting functionality + // This is independent of the global sort algorithms, as lists are + // linked nodes and can be sorted more efficiently by moving nodes + // around in ways that global sort algorithms aren't privy to. + + void merge(this_type& x); + + template + void merge(this_type& x, Compare compare); + + void unique(); + + template + void unique(BinaryPredicate); + + void sort(); + + template + void sort(Compare compare); + + public: + // bool validate() const; // Inherited from parent. + int validate_iterator(const_iterator i) const; + + }; // intrusive_list + + + + + /////////////////////////////////////////////////////////////////////// + // intrusive_list_node + /////////////////////////////////////////////////////////////////////// + + // Moved to be inline within the class because the may-alias attribute is + // triggering what appears to be a bug in GCC that effectively requires + // may-alias structs to implement inline member functions within the class + // declaration. We don't have a .cpp file for + // #if EASTL_VALIDATE_INTRUSIVE_LIST + // inline intrusive_list_node::intrusive_list_node() + // { + // mpNext = mpPrev = NULL; + // } + // + // inline intrusive_list_node::~intrusive_list_node() + // { + // #if EASTL_ASSERT_ENABLED + // if(mpNext || mpPrev) + // EASTL_FAIL_MSG("~intrusive_list_node(): List is non-empty."); + // #endif + // } + // #endif + + + /////////////////////////////////////////////////////////////////////// + // intrusive_list_iterator + /////////////////////////////////////////////////////////////////////// + + template + inline intrusive_list_iterator::intrusive_list_iterator() + { + #if EASTL_DEBUG + mpNode = NULL; + #endif + } + + + template + inline intrusive_list_iterator::intrusive_list_iterator(pointer pNode) + : mpNode(pNode) + { + // Empty + } + + + template + inline intrusive_list_iterator::intrusive_list_iterator(const iterator& x) + : mpNode(x.mpNode) + { + // Empty + } + + template + inline typename intrusive_list_iterator::this_type& + intrusive_list_iterator::operator=(const iterator& x) + { + mpNode = x.mpNode; + return *this; + } + + template + inline typename intrusive_list_iterator::reference + intrusive_list_iterator::operator*() const + { + return *mpNode; + } + + + template + inline typename intrusive_list_iterator::pointer + intrusive_list_iterator::operator->() const + { + return mpNode; + } + + + template + inline typename intrusive_list_iterator::this_type& + intrusive_list_iterator::operator++() + { + mpNode = static_cast(mpNode->mpNext); + return *this; + } + + + template + inline typename intrusive_list_iterator::this_type + intrusive_list_iterator::operator++(int) + { + intrusive_list_iterator it(*this); + mpNode = static_cast(mpNode->mpNext); + return it; + } + + + template + inline typename intrusive_list_iterator::this_type& + intrusive_list_iterator::operator--() + { + mpNode = static_cast(mpNode->mpPrev); + return *this; + } + + + template + inline typename intrusive_list_iterator::this_type + intrusive_list_iterator::operator--(int) + { + intrusive_list_iterator it(*this); + mpNode = static_cast(mpNode->mpPrev); + return it; + } + + + // 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 + inline bool operator==(const intrusive_list_iterator& a, + const intrusive_list_iterator& b) + { + return a.mpNode == b.mpNode; + } + + + template + inline bool operator!=(const intrusive_list_iterator& a, + const intrusive_list_iterator& 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 + inline bool operator!=(const intrusive_list_iterator& a, + const intrusive_list_iterator& b) + { + return a.mpNode != b.mpNode; + } + + + + + /////////////////////////////////////////////////////////////////////// + // intrusive_list_base + /////////////////////////////////////////////////////////////////////// + + inline intrusive_list_base::intrusive_list_base() + { + mAnchor.mpNext = mAnchor.mpPrev = &mAnchor; + } + + inline intrusive_list_base::~intrusive_list_base() + { + #if EASTL_VALIDATE_INTRUSIVE_LIST + clear(); + mAnchor.mpNext = mAnchor.mpPrev = NULL; + #endif + } + + + inline bool intrusive_list_base::empty() const EA_NOEXCEPT + { + return mAnchor.mpPrev == &mAnchor; + } + + + inline intrusive_list_base::size_type intrusive_list_base::size() const EA_NOEXCEPT + { + const intrusive_list_node* p = &mAnchor; + size_type n = (size_type)-1; + + do { + ++n; + p = p->mpNext; + } while(p != &mAnchor); + + return n; + } + + + inline void intrusive_list_base::clear() EA_NOEXCEPT + { + #if EASTL_VALIDATE_INTRUSIVE_LIST + // Need to clear out all the next/prev pointers in the elements; + // this makes this operation O(n) instead of O(1). + intrusive_list_node* pNode = mAnchor.mpNext; + + while(pNode != &mAnchor) + { + intrusive_list_node* const pNextNode = pNode->mpNext; + pNode->mpNext = pNode->mpPrev = NULL; + pNode = pNextNode; + } + #endif + + mAnchor.mpNext = mAnchor.mpPrev = &mAnchor; + } + + + inline void intrusive_list_base::pop_front() + { + #if EASTL_VALIDATE_INTRUSIVE_LIST + intrusive_list_node* const pNode = mAnchor.mpNext; + #endif + + mAnchor.mpNext->mpNext->mpPrev = &mAnchor; + mAnchor.mpNext = mAnchor.mpNext->mpNext; + + #if EASTL_VALIDATE_INTRUSIVE_LIST + if(pNode != &mAnchor) + pNode->mpNext = pNode->mpPrev = NULL; + #if EASTL_ASSERT_ENABLED + else + EASTL_FAIL_MSG("intrusive_list::pop_front(): empty list."); + #endif + #endif + } + + + inline void intrusive_list_base::pop_back() + { + #if EASTL_VALIDATE_INTRUSIVE_LIST + intrusive_list_node* const pNode = mAnchor.mpPrev; + #endif + + mAnchor.mpPrev->mpPrev->mpNext = &mAnchor; + mAnchor.mpPrev = mAnchor.mpPrev->mpPrev; + + #if EASTL_VALIDATE_INTRUSIVE_LIST + if(pNode != &mAnchor) + pNode->mpNext = pNode->mpPrev = NULL; + #if EASTL_ASSERT_ENABLED + else + EASTL_FAIL_MSG("intrusive_list::pop_back(): empty list."); + #endif + #endif + } + + + + + /////////////////////////////////////////////////////////////////////// + // intrusive_list + /////////////////////////////////////////////////////////////////////// + + template + inline intrusive_list::intrusive_list() + { + } + + + template + inline intrusive_list::intrusive_list(const this_type& /*x*/) + : intrusive_list_base() + { + // We intentionally ignore argument x. + // To consider: Shouldn't this function simply not exist? Is there a useful purpose for having this function? + // There should be a comment here about it, though my first guess is that this exists to quell VC++ level 4/-Wall compiler warnings. + } + + + template + inline typename intrusive_list::this_type& intrusive_list::operator=(const this_type& /*x*/) + { + // We intentionally ignore argument x. + // See notes above in the copy constructor about questioning the existence of this function. + return *this; + } + + + template + inline typename intrusive_list::iterator intrusive_list::begin() EA_NOEXCEPT + { + return iterator(static_cast(mAnchor.mpNext)); + } + + + template + inline typename intrusive_list::const_iterator intrusive_list::begin() const EA_NOEXCEPT + { + return const_iterator(static_cast(mAnchor.mpNext)); + } + + + template + inline typename intrusive_list::const_iterator intrusive_list::cbegin() const EA_NOEXCEPT + { + return const_iterator(static_cast(mAnchor.mpNext)); + } + + + template + inline typename intrusive_list::iterator intrusive_list::end() EA_NOEXCEPT + { + return iterator(static_cast(&mAnchor)); + } + + + template + inline typename intrusive_list::const_iterator intrusive_list::end() const EA_NOEXCEPT + { + return const_iterator(static_cast(&mAnchor)); + } + + + template + inline typename intrusive_list::const_iterator intrusive_list::cend() const EA_NOEXCEPT + { + return const_iterator(static_cast(&mAnchor)); + } + + + template + inline typename intrusive_list::reverse_iterator intrusive_list::rbegin() EA_NOEXCEPT + { + return reverse_iterator(iterator(static_cast(&mAnchor))); + } + + + template + inline typename intrusive_list::const_reverse_iterator intrusive_list::rbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(const_iterator(static_cast(&mAnchor))); + } + + + template + inline typename intrusive_list::const_reverse_iterator intrusive_list::crbegin() const EA_NOEXCEPT + { + return const_reverse_iterator(const_iterator(static_cast(&mAnchor))); + } + + + template + inline typename intrusive_list::reverse_iterator intrusive_list::rend() EA_NOEXCEPT + { + return reverse_iterator(iterator(static_cast(mAnchor.mpNext))); + } + + + template + inline typename intrusive_list::const_reverse_iterator intrusive_list::rend() const EA_NOEXCEPT + { + return const_reverse_iterator(const_iterator(static_cast(mAnchor.mpNext))); + } + + + template + inline typename intrusive_list::const_reverse_iterator intrusive_list::crend() const EA_NOEXCEPT + { + return const_reverse_iterator(const_iterator(static_cast(mAnchor.mpNext))); + } + + + template + inline typename intrusive_list::reference intrusive_list::front() + { + #if EASTL_VALIDATE_INTRUSIVE_LIST && EASTL_ASSERT_ENABLED + if(mAnchor.mpNext == &mAnchor) + EASTL_FAIL_MSG("intrusive_list::front(): empty list."); + #endif + + return *static_cast(mAnchor.mpNext); + } + + + template + inline typename intrusive_list::const_reference intrusive_list::front() const + { + #if EASTL_VALIDATE_INTRUSIVE_LIST && EASTL_ASSERT_ENABLED + if(mAnchor.mpNext == &mAnchor) + EASTL_FAIL_MSG("intrusive_list::front(): empty list."); + #endif + + return *static_cast(mAnchor.mpNext); + } + + + template + inline typename intrusive_list::reference intrusive_list::back() + { + #if EASTL_VALIDATE_INTRUSIVE_LIST && EASTL_ASSERT_ENABLED + if(mAnchor.mpNext == &mAnchor) + EASTL_FAIL_MSG("intrusive_list::back(): empty list."); + #endif + + return *static_cast(mAnchor.mpPrev); + } + + + template + inline typename intrusive_list::const_reference intrusive_list::back() const + { + #if EASTL_VALIDATE_INTRUSIVE_LIST && EASTL_ASSERT_ENABLED + if(mAnchor.mpNext == &mAnchor) + EASTL_FAIL_MSG("intrusive_list::back(): empty list."); + #endif + + return *static_cast(mAnchor.mpPrev); + } + + + template + inline void intrusive_list::push_front(value_type& x) + { + #if EASTL_VALIDATE_INTRUSIVE_LIST && EASTL_ASSERT_ENABLED + if(x.mpNext || x.mpPrev) + EASTL_FAIL_MSG("intrusive_list::push_front(): element already on a list."); + #endif + + x.mpNext = mAnchor.mpNext; + x.mpPrev = &mAnchor; + mAnchor.mpNext = &x; + x.mpNext->mpPrev = &x; + } + + + template + inline void intrusive_list::push_back(value_type& x) + { + #if EASTL_VALIDATE_INTRUSIVE_LIST && EASTL_ASSERT_ENABLED + if(x.mpNext || x.mpPrev) + EASTL_FAIL_MSG("intrusive_list::push_back(): element already on a list."); + #endif + + x.mpPrev = mAnchor.mpPrev; + x.mpNext = &mAnchor; + mAnchor.mpPrev = &x; + x.mpPrev->mpNext = &x; + } + + + template + inline bool intrusive_list::contains(const value_type& x) const + { + for(const intrusive_list_node* p = mAnchor.mpNext; p != &mAnchor; p = p->mpNext) + { + if(p == &x) + return true; + } + + return false; + } + + + template + inline typename intrusive_list::iterator intrusive_list::locate(value_type& x) + { + for(intrusive_list_node* p = (T*)mAnchor.mpNext; p != &mAnchor; p = p->mpNext) + { + if(p == &x) + return iterator(static_cast(p)); + } + + return iterator((T*)&mAnchor); + } + + + template + inline typename intrusive_list::const_iterator intrusive_list::locate(const value_type& x) const + { + for(const intrusive_list_node* p = mAnchor.mpNext; p != &mAnchor; p = p->mpNext) + { + if(p == &x) + return const_iterator(static_cast(p)); + } + + return const_iterator((T*)&mAnchor); + } + + + template + inline typename intrusive_list::iterator intrusive_list::insert(const_iterator pos, value_type& x) + { + #if EASTL_VALIDATE_INTRUSIVE_LIST && EASTL_ASSERT_ENABLED + if(x.mpNext || x.mpPrev) + EASTL_FAIL_MSG("intrusive_list::insert(): element already on a list."); + #endif + + intrusive_list_node& next = *const_cast(pos.mpNode); + intrusive_list_node& prev = *static_cast(next.mpPrev); + prev.mpNext = next.mpPrev = &x; + x.mpPrev = &prev; + x.mpNext = &next; + + return iterator(&x); + } + + + template + inline typename intrusive_list::iterator + intrusive_list::erase(const_iterator pos) + { + intrusive_list_node& prev = *static_cast(pos.mpNode->mpPrev); + intrusive_list_node& next = *static_cast(pos.mpNode->mpNext); + prev.mpNext = &next; + next.mpPrev = &prev; + + #if EASTL_VALIDATE_INTRUSIVE_LIST + iterator ii(const_cast(pos.mpNode)); + ii.mpNode->mpPrev = ii.mpNode->mpNext = NULL; + #endif + + return iterator(static_cast(&next)); + } + + + template + inline typename intrusive_list::iterator + intrusive_list::erase(const_iterator first, const_iterator last) + { + intrusive_list_node& prev = *static_cast(first.mpNode->mpPrev); + intrusive_list_node& next = *const_cast(last.mpNode); + + #if EASTL_VALIDATE_INTRUSIVE_LIST + // need to clear out all the next/prev pointers in the elements; + // this makes this operation O(n) instead of O(1), sadly, although + // it's technically amortized O(1) since you could count yourself + // as paying this cost with each insert. + intrusive_list_node* pCur = const_cast(first.mpNode); + + while(pCur != &next) + { + intrusive_list_node* const pCurNext = pCur->mpNext; + pCur->mpPrev = pCur->mpNext = NULL; + pCur = pCurNext; + } + #endif + + prev.mpNext = &next; + next.mpPrev = &prev; + + return iterator(const_cast(last.mpNode)); + } + + + template + inline typename intrusive_list::reverse_iterator + intrusive_list::erase(const_reverse_iterator position) + { + return reverse_iterator(erase((++position).base())); + } + + + template + inline typename intrusive_list::reverse_iterator + intrusive_list::erase(const_reverse_iterator first, const_reverse_iterator last) + { + // Version which erases in order from first to last. + // difference_type i(first.base() - last.base()); + // while(i--) + // first = erase(first); + // return first; + + // Version which erases in order from last to first, but is slightly more efficient: + return reverse_iterator(erase((++last).base(), (++first).base())); + } + + + template + void intrusive_list::swap(intrusive_list& x) + { + // swap anchors + intrusive_list_node temp(mAnchor); + mAnchor = x.mAnchor; + x.mAnchor = temp; + + // Fixup node pointers into the anchor, since the addresses of + // the anchors must stay the same with each list. + if(mAnchor.mpNext == &x.mAnchor) + mAnchor.mpNext = mAnchor.mpPrev = &mAnchor; + else + mAnchor.mpNext->mpPrev = mAnchor.mpPrev->mpNext = &mAnchor; + + if(x.mAnchor.mpNext == &mAnchor) + x.mAnchor.mpNext = x.mAnchor.mpPrev = &x.mAnchor; + else + x.mAnchor.mpNext->mpPrev = x.mAnchor.mpPrev->mpNext = &x.mAnchor; + + #if EASTL_VALIDATE_INTRUSIVE_LIST + temp.mpPrev = temp.mpNext = NULL; + #endif + } + + + template + void intrusive_list::splice(const_iterator pos, value_type& value) + { + // Note that splice(pos, x, pos) and splice(pos+1, x, pos) + // are valid and need to be handled correctly. + + if(pos.mpNode != &value) + { + // Unlink item from old list. + intrusive_list_node& oldNext = *value.mpNext; + intrusive_list_node& oldPrev = *value.mpPrev; + oldNext.mpPrev = &oldPrev; + oldPrev.mpNext = &oldNext; + + // Relink item into new list. + intrusive_list_node& newNext = *const_cast(pos.mpNode); + intrusive_list_node& newPrev = *newNext.mpPrev; + + newPrev.mpNext = &value; + newNext.mpPrev = &value; + value.mpPrev = &newPrev; + value.mpNext = &newNext; + } + } + + + template + void intrusive_list::splice(const_iterator pos, intrusive_list& x) + { + // Note: &x == this is prohibited, so self-insertion is not a problem. + if(x.mAnchor.mpNext != &x.mAnchor) // If the list 'x' isn't empty... + { + intrusive_list_node& next = *const_cast(pos.mpNode); + intrusive_list_node& prev = *static_cast(next.mpPrev); + intrusive_list_node& insertPrev = *static_cast(x.mAnchor.mpNext); + intrusive_list_node& insertNext = *static_cast(x.mAnchor.mpPrev); + + prev.mpNext = &insertPrev; + insertPrev.mpPrev = &prev; + insertNext.mpNext = &next; + next.mpPrev = &insertNext; + x.mAnchor.mpPrev = x.mAnchor.mpNext = &x.mAnchor; + } + } + + + template + void intrusive_list::splice(const_iterator pos, intrusive_list& /*x*/, const_iterator i) + { + // Note: &x == this is prohibited, so self-insertion is not a problem. + + // Note that splice(pos, x, pos) and splice(pos + 1, x, pos) + // are valid and need to be handled correctly. + + // We don't need to check if the source list is empty, because + // this function expects a valid iterator from the source list, + // and thus the list cannot be empty in such a situation. + + iterator ii(const_cast(i.mpNode)); // Make a temporary non-const version. + + if(pos != ii) + { + // Unlink item from old list. + intrusive_list_node& oldNext = *ii.mpNode->mpNext; + intrusive_list_node& oldPrev = *ii.mpNode->mpPrev; + oldNext.mpPrev = &oldPrev; + oldPrev.mpNext = &oldNext; + + // Relink item into new list. + intrusive_list_node& newNext = *const_cast(pos.mpNode); + intrusive_list_node& newPrev = *newNext.mpPrev; + + newPrev.mpNext = ii.mpNode; + newNext.mpPrev = ii.mpNode; + ii.mpNode->mpPrev = &newPrev; + ii.mpNode->mpNext = &newNext; + } + } + + + template + void intrusive_list::splice(const_iterator pos, intrusive_list& /*x*/, const_iterator first, const_iterator last) + { + // Note: &x == this is prohibited, so self-insertion is not a problem. + if(first != last) + { + intrusive_list_node& insertPrev = *const_cast(first.mpNode); + intrusive_list_node& insertNext = *static_cast(last.mpNode->mpPrev); + + // remove from old list + insertNext.mpNext->mpPrev = insertPrev.mpPrev; + insertPrev.mpPrev->mpNext = insertNext.mpNext; + + // insert into this list + intrusive_list_node& next = *const_cast(pos.mpNode); + intrusive_list_node& prev = *static_cast(next.mpPrev); + + prev.mpNext = &insertPrev; + insertPrev.mpPrev = &prev; + insertNext.mpNext = &next; + next.mpPrev = &insertNext; + } + } + + + template + inline void intrusive_list::remove(value_type& value) + { + intrusive_list_node& prev = *value.mpPrev; + intrusive_list_node& next = *value.mpNext; + prev.mpNext = &next; + next.mpPrev = &prev; + + #if EASTL_VALIDATE_INTRUSIVE_LIST + value.mpPrev = value.mpNext = NULL; + #endif + } + + + template + void intrusive_list::merge(this_type& x) + { + if(this != &x) + { + iterator first(begin()); + iterator firstX(x.begin()); + const iterator last(end()); + const iterator lastX(x.end()); + + while((first != last) && (firstX != lastX)) + { + if(*firstX < *first) + { + iterator next(firstX); + + splice(first, x, firstX, ++next); + firstX = next; + } + else + ++first; + } + + if(firstX != lastX) + splice(last, x, firstX, lastX); + } + } + + + template + template + void intrusive_list::merge(this_type& x, Compare compare) + { + if(this != &x) + { + iterator first(begin()); + iterator firstX(x.begin()); + const iterator last(end()); + const iterator lastX(x.end()); + + while((first != last) && (firstX != lastX)) + { + if(compare(*firstX, *first)) + { + iterator next(firstX); + + splice(first, x, firstX, ++next); + firstX = next; + } + else + ++first; + } + + if(firstX != lastX) + splice(last, x, firstX, lastX); + } + } + + + template + void intrusive_list::unique() + { + iterator first(begin()); + const iterator last(end()); + + if(first != last) + { + iterator next(first); + + while(++next != last) + { + if(*first == *next) + erase(next); + else + first = next; + next = first; + } + } + } + + + template + template + void intrusive_list::unique(BinaryPredicate predicate) + { + iterator first(begin()); + const iterator last(end()); + + if(first != last) + { + iterator next(first); + + while(++next != last) + { + if(predicate(*first, *next)) + erase(next); + else + first = next; + next = first; + } + } + } + + + template + void intrusive_list::sort() + { + // We implement the algorithm employed by Chris Caulfield whereby we use recursive + // function calls to sort the list. The sorting of a very large list may fail due to stack overflow + // if the stack is exhausted. The limit depends on the platform and the avaialble stack space. + + // Easier-to-understand version of the 'if' statement: + // iterator i(begin()); + // if((i != end()) && (++i != end())) // If the size is >= 2 (without calling the more expensive size() function)... + + // Faster, more inlinable version of the 'if' statement: + if((static_cast(mAnchor.mpNext) != &mAnchor) && + (static_cast(mAnchor.mpNext) != static_cast(mAnchor.mpPrev))) + { + // Split the array into 2 roughly equal halves. + this_type leftList; // This should cause no memory allocation. + this_type rightList; + + // We find an iterator which is in the middle of the list. The fastest way to do + // this is to iterate from the base node both forwards and backwards with two + // iterators and stop when they meet each other. Recall that our size() function + // is not O(1) but is instead O(n), at least when EASTL_LIST_SIZE_CACHE is disabled. + #if EASTL_LIST_SIZE_CACHE + iterator mid(begin()); + eastl::advance(mid, size() / 2); + #else + iterator mid(begin()), tail(end()); + + while((mid != tail) && (++mid != tail)) + --tail; + #endif + + // Move the left half of this into leftList and the right half into rightList. + leftList.splice(leftList.begin(), *this, begin(), mid); + rightList.splice(rightList.begin(), *this); + + // Sort the sub-lists. + leftList.sort(); + rightList.sort(); + + // Merge the two halves into this list. + splice(begin(), leftList); + merge(rightList); + } + } + + + template + template + void intrusive_list::sort(Compare compare) + { + // We implement the algorithm employed by Chris Caulfield whereby we use recursive + // function calls to sort the list. The sorting of a very large list may fail due to stack overflow + // if the stack is exhausted. The limit depends on the platform and the avaialble stack space. + + // Easier-to-understand version of the 'if' statement: + // iterator i(begin()); + // if((i != end()) && (++i != end())) // If the size is >= 2 (without calling the more expensive size() function)... + + // Faster, more inlinable version of the 'if' statement: + if((static_cast(mAnchor.mpNext) != &mAnchor) && + (static_cast(mAnchor.mpNext) != static_cast(mAnchor.mpPrev))) + { + // Split the array into 2 roughly equal halves. + this_type leftList; // This should cause no memory allocation. + this_type rightList; + + // We find an iterator which is in the middle of the list. The fastest way to do + // this is to iterate from the base node both forwards and backwards with two + // iterators and stop when they meet each other. Recall that our size() function + // is not O(1) but is instead O(n), at least when EASTL_LIST_SIZE_CACHE is disabled. + #if EASTL_LIST_SIZE_CACHE + iterator mid(begin()); + eastl::advance(mid, size() / 2); + #else + iterator mid(begin()), tail(end()); + + while((mid != tail) && (++mid != tail)) + --tail; + #endif + + // Move the left half of this into leftList and the right half into rightList. + leftList.splice(leftList.begin(), *this, begin(), mid); + rightList.splice(rightList.begin(), *this); + + // Sort the sub-lists. + leftList.sort(compare); + rightList.sort(compare); + + // Merge the two halves into this list. + splice(begin(), leftList); + merge(rightList, compare); + } + } + + + template + inline int intrusive_list::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 + bool operator==(const intrusive_list& a, const intrusive_list& b) + { + // If we store an mSize member for intrusive_list, we want to take advantage of it here. + typename intrusive_list::const_iterator ia = a.begin(); + typename intrusive_list::const_iterator ib = b.begin(); + typename intrusive_list::const_iterator enda = a.end(); + typename intrusive_list::const_iterator endb = b.end(); + + while((ia != enda) && (ib != endb) && (*ia == *ib)) + { + ++ia; + ++ib; + } + return (ia == enda) && (ib == endb); + } + + template + bool operator!=(const intrusive_list& a, const intrusive_list& b) + { + return !(a == b); + } + + template + bool operator<(const intrusive_list& a, const intrusive_list& b) + { + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + template + bool operator>(const intrusive_list& a, const intrusive_list& b) + { + return b < a; + } + + template + bool operator<=(const intrusive_list& a, const intrusive_list& b) + { + return !(b < a); + } + + template + bool operator>=(const intrusive_list& a, const intrusive_list& b) + { + return !(a < b); + } + + template + void swap(intrusive_list& a, intrusive_list& b) + { + a.swap(b); + } + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/intrusive_ptr.h b/lib/EASTL/include/EASTL/intrusive_ptr.h new file mode 100644 index 000000000..af4e686f2 --- /dev/null +++ b/lib/EASTL/include/EASTL/intrusive_ptr.h @@ -0,0 +1,426 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_INTRUSIVE_PTR_H +#define EASTL_INTRUSIVE_PTR_H + + +#include +#include + +#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 +{ + // We provide default implementations of AddRef and Release in the eastl namespace. + // The user can override these on a per-class basis by defining their own specialized + // intrusive_ptr_add_ref and intrusive_ptr_release functions. User-defined specializations + // do not need to exist in the eastl namespace, but should preferably be in the namespace + // of the templated class T. + template + void intrusive_ptr_add_ref(T* p) + { + p->AddRef(); + } + + template + void intrusive_ptr_release(T* p) + { + p->Release(); + } + + + ////////////////////////////////////////////////////////////////////////////// + /// intrusive_ptr + /// + /// This is a class that acts like the C++ auto_ptr class except that instead + /// of deleting its member data when it goes out of scope, it releases its + /// member data when it goes out of scope. This class thus requires that the + /// templated data type have an AddRef and Release function (or whatever is + /// configured to be the two refcount functions). + /// + /// This class is useful for automatically releasing an object when this + /// class goes out of scope. See below for some usage. + /// You should be careful about putting instances of this class as members of + /// another class. If you do so, then the intrusive_ptr destructor will only + /// be called if the object that owns it is destructed. This creates a potential + /// chicken-and-egg situation. What if the intrusive_ptr member contains a + /// pointer to an object that has a reference on the object that owns the + /// intrusive_ptr member? The answer is that the neither object can ever be + /// destructed. The solution is to: + /// 1) Be very careful about what objects you put into member intrusive_ptr objects. + /// 2) Clear out your intrusive_ptr members in your shutdown function. + /// 3) Simply don't use intrusive_ptr objects as class members. + /// + /// Example usage: + /// intrusive_ptr pWidget = new Widget; + /// pWidget = new Widget; + /// pWidget->Reset(); + /// + template + class intrusive_ptr + { + protected: + // Friend declarations. + template friend class intrusive_ptr; + typedef intrusive_ptr this_type; + + T* mpObject; + + public: + /// element_type + /// This typedef is present for consistency with the C++ standard library + /// auto_ptr template. It allows users to refer to the templated type via + /// a typedef. This is sometimes useful to be able to do. + /// + /// Example usage: + /// intrusive_ptr ip; + /// void DoSomething(intrusive_ptr::element_type someType); + /// + typedef T element_type; + + /// intrusive_ptr + /// Default constructor. The member object is set to NULL. + intrusive_ptr() + : mpObject(NULL) + { + // Empty + } + + /// intrusive_ptr + /// Provides a constructor which takes ownership of a pointer. + /// The incoming pointer is AddRefd. + /// + /// Example usage: + /// intrusive_ptr pWidget(new Widget); + intrusive_ptr(T* p, bool bAddRef = true) + : mpObject(p) + { + if(mpObject && bAddRef) + intrusive_ptr_add_ref(mpObject); // Intentionally do not prefix the call with eastl:: but instead allow namespace lookup to resolve the namespace. + } + + /// intrusive_ptr + /// Construction from self type. + intrusive_ptr(const intrusive_ptr& ip) + : mpObject(ip.mpObject) + { + if(mpObject) + intrusive_ptr_add_ref(mpObject); + } + + + /// intrusive_ptr + /// move constructor + intrusive_ptr(intrusive_ptr&& ip) + : mpObject(nullptr) + { + swap(ip); + } + + /// intrusive_ptr + /// Provides a constructor which copies a pointer from another intrusive_ptr. + /// The incoming pointer is AddRefd. The source intrusive_ptr object maintains + /// its AddRef on the pointer. + /// + /// Example usage: + /// intrusive_ptr pWidget1; + /// intrusive_ptr pWidget2(pWidget1); + template + intrusive_ptr(const intrusive_ptr& ip) + : mpObject(ip.mpObject) + { + if(mpObject) + intrusive_ptr_add_ref(mpObject); + } + + /// intrusive_ptr + /// Releases the owned pointer. + ~intrusive_ptr() + { + if(mpObject) + intrusive_ptr_release(mpObject); + } + + + /// operator= + /// Assignment to self type. + intrusive_ptr& operator=(const intrusive_ptr& ip) + { + return operator=(ip.mpObject); + } + + + /// operator= + /// Move assignment operator + intrusive_ptr& operator=(intrusive_ptr&& ip) + { + swap(ip); + return *this; + } + + + /// operator = + /// Assigns an intrusive_ptr object to this intrusive_ptr object. + /// The incoming pointer is AddRefd. The source intrusive_ptr object + /// maintains its AddRef on the pointer. If there is an existing member + /// pointer, it is Released before the incoming pointer is assigned. + /// If the incoming pointer is equal to the existing pointer, no + /// action is taken. The incoming pointer is AddRefd before any + /// member pointer is Released. + template + intrusive_ptr& operator=(const intrusive_ptr& ip) + { + return operator=(ip.mpObject); + } + + /// operator= + /// Assigns an intrusive_ptr object to this intrusive_ptr object. + /// The incoming pointer is AddRefd. If there is an existing member + /// pointer, it is Released before the incoming pointer is assigned. + /// If the incoming pointer is equal to the existing pointer, no + /// action is taken. The incoming pointer is AddRefd before any + /// member pointer is Released. + intrusive_ptr& operator=(T* pObject) + { + if(pObject != mpObject) + { + T* const pTemp = mpObject; // Create temporary to prevent possible problems with re-entrancy. + if(pObject) + intrusive_ptr_add_ref(pObject); + mpObject = pObject; + if(pTemp) + intrusive_ptr_release(pTemp); + } + return *this; + } + + /// operator * + /// Returns a reference to the contained object. + T& operator *() const + { + return *mpObject; + } + + /// operator * + /// Returns a pointer to the contained object, allowing the + /// user to use this container as if it were contained pointer itself. + T* operator ->() const + { + return mpObject; + } + + /// get() + /// Returns a pointer to the contained object. + T* get() const + { + return mpObject; + } + + /// reset + /// Releases the owned object and clears our reference to it. + void reset() + { + T* const pTemp = mpObject; + mpObject = NULL; + if(pTemp) + intrusive_ptr_release(pTemp); + } + + /// swap + /// Exchanges the owned pointer beween two intrusive_ptr objects. + void swap(this_type& ip) + { + T* const pTemp = mpObject; + mpObject = ip.mpObject; + ip.mpObject = pTemp; + } + + /// attach + /// Sets an intrusive_ptr pointer without calling AddRef() on + /// the pointed object. The intrusive_ptr thus eventually only does a + /// Release() on the object. This is useful for assuming a reference + /// that someone else has handed you and making sure it is always + /// released, even if you return in the middle of a function or an + /// exception is thrown. + /// + void attach(T* pObject) + { + T* const pTemp = mpObject; + mpObject = pObject; + if(pTemp) + intrusive_ptr_release(pTemp); + } + + /// detach + /// Surrenders the reference held by an intrusive_ptr pointer -- + /// it returns the current reference and nulls the pointer. If the returned + /// pointer is non-null it must be released. This is useful in functions + /// that must return a reference while possibly being aborted by a return + /// or thrown exception: + /// + /// bool GetFoo(T** pp){ + /// intrusive_ptr p(PrivateGetFoo()); + /// if(p->Method()) + /// return false; + /// *pp = p.detach(); + /// return true; + /// } + T* detach() + { + T* const pTemp = mpObject; + mpObject = NULL; + return pTemp; + } + + /// Implicit operator bool + /// Allows for using a intrusive_ptr as a boolean. + /// Example usage: + /// intrusive_ptr ptr = new Widget; + /// if(ptr) + /// ++*ptr; + /// + /// Note that below we do not use operator bool(). The reason for this + /// is that booleans automatically convert up to short, int, float, etc. + /// The result is that this: if(intrusivePtr == 1) would yield true (bad). + typedef T* (this_type::*bool_)() const; + operator bool_() const + { + if(mpObject) + return &this_type::get; + return NULL; + } + + /// operator! + /// This returns the opposite of operator bool; it returns true if + /// the owned pointer is null. Some compilers require this and some don't. + /// intrusive_ptr ptr = new Widget; + /// if(!ptr) + /// assert(false); + bool operator!() const + { + return (mpObject == NULL); + } + + }; // class intrusive_ptr + + + /// get_pointer + /// returns intrusive_ptr::get() via the input intrusive_ptr. + template + inline T* get_pointer(const intrusive_ptr& intrusivePtr) + { + return intrusivePtr.get(); + } + + /// swap + /// Exchanges the owned pointer beween two intrusive_ptr objects. + /// This non-member version is useful for compatibility of intrusive_ptr + /// objects with the C++ Standard Library and other libraries. + template + inline void swap(intrusive_ptr& intrusivePtr1, intrusive_ptr& intrusivePtr2) + { + intrusivePtr1.swap(intrusivePtr2); + } + + + template + bool operator==(intrusive_ptr const& iPtr1, intrusive_ptr const& iPtr2) + { + return (iPtr1.get() == iPtr2.get()); + } + + template + bool operator!=(intrusive_ptr const& iPtr1, intrusive_ptr const& iPtr2) + { + return (iPtr1.get() != iPtr2.get()); + } + + template + bool operator==(intrusive_ptr const& iPtr1, T* p) + { + return (iPtr1.get() == p); + } + + template + bool operator!=(intrusive_ptr const& iPtr1, T* p) + { + return (iPtr1.get() != p); + } + + template + bool operator==(T* p, intrusive_ptr const& iPtr2) + { + return (p == iPtr2.get()); + } + + template + bool operator!=(T* p, intrusive_ptr const& iPtr2) + { + return (p != iPtr2.get()); + } + + template + bool operator<(intrusive_ptr const& iPtr1, intrusive_ptr const& iPtr2) + { + return ((uintptr_t)iPtr1.get() < (uintptr_t)iPtr2.get()); + } + + + /// static_pointer_cast + /// Returns an intrusive_ptr static-casted from a intrusive_ptr. + template + intrusive_ptr static_pointer_cast(const intrusive_ptr& intrusivePtr) + { + return static_cast(intrusivePtr.get()); + } + + + #if EASTL_RTTI_ENABLED + + /// dynamic_pointer_cast + /// Returns an intrusive_ptr dynamic-casted from a intrusive_ptr. + template + intrusive_ptr dynamic_pointer_cast(const intrusive_ptr& intrusivePtr) + { + return dynamic_cast(intrusivePtr.get()); + } + + #endif + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/iterator.h b/lib/EASTL/include/EASTL/iterator.h new file mode 100644 index 000000000..033bdc866 --- /dev/null +++ b/lib/EASTL/include/EASTL/iterator.h @@ -0,0 +1,1197 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_ITERATOR_H +#define EASTL_ITERATOR_H + + +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS(); + +#include + +EA_RESTORE_ALL_VC_WARNINGS(); + +// If the user has specified that we use std iterator +// categories instead of EASTL iterator categories, +// then #include . +#if EASTL_STD_ITERATOR_CATEGORY_ENABLED + EA_DISABLE_ALL_VC_WARNINGS(); + + #include + + EA_RESTORE_ALL_VC_WARNINGS(); +#endif + + +EA_DISABLE_VC_WARNING(4619); // There is no warning number 'number'. +EA_DISABLE_VC_WARNING(4217); // Member template functions cannot be used for copy-assignment or copy-construction. + +#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 +{ + /// iterator_status_flag + /// + /// Defines the validity status of an iterator. This is primarily used for + /// iterator validation in debug builds. These are implemented as OR-able + /// flags (as opposed to mutually exclusive values) in order to deal with + /// the nature of iterator status. In particular, an iterator may be valid + /// but not dereferencable, as in the case with an iterator to container end(). + /// An iterator may be valid but also dereferencable, as in the case with an + /// iterator to container begin(). + /// + enum iterator_status_flag + { + isf_none = 0x00, /// This is called none and not called invalid because it is not strictly the opposite of invalid. + isf_valid = 0x01, /// The iterator is valid, which means it is in the range of [begin, end]. + isf_current = 0x02, /// The iterator is valid and points to the same element it did when created. For example, if an iterator points to vector::begin() but an element is inserted at the front, the iterator is valid but not current. Modification of elements in place do not make iterators non-current. + isf_can_dereference = 0x04 /// The iterator is dereferencable, which means it is in the range of [begin, end). It may or may not be current. + }; + + + + // The following declarations are taken directly from the C++ standard document. + // input_iterator_tag, etc. + // iterator + // iterator_traits + // reverse_iterator + + // Iterator categories + // Every iterator is defined as belonging to one of the iterator categories that + // we define here. These categories come directly from the C++ standard. + #if !EASTL_STD_ITERATOR_CATEGORY_ENABLED // If we are to use our own iterator category definitions... + struct input_iterator_tag { }; + struct output_iterator_tag { }; + struct forward_iterator_tag : public input_iterator_tag { }; + struct bidirectional_iterator_tag : public forward_iterator_tag { }; + struct random_access_iterator_tag : public bidirectional_iterator_tag { }; + struct contiguous_iterator_tag : public random_access_iterator_tag { }; // Extension to the C++ standard. Contiguous ranges are more than random access, they are physically contiguous. + #endif + + + // struct iterator + template + struct iterator + { + typedef Category iterator_category; + typedef T value_type; + typedef Distance difference_type; + typedef Pointer pointer; + typedef Reference reference; + }; + + + // struct iterator_traits + template + struct iterator_traits + { + typedef typename Iterator::iterator_category iterator_category; + typedef typename Iterator::value_type value_type; + typedef typename Iterator::difference_type difference_type; + typedef typename Iterator::pointer pointer; + typedef typename Iterator::reference reference; + }; + + template + struct iterator_traits + { + typedef EASTL_ITC_NS::random_access_iterator_tag iterator_category; // To consider: Change this to contiguous_iterator_tag for the case that + typedef T value_type; // EASTL_ITC_NS is "eastl" instead of "std". + typedef ptrdiff_t difference_type; + typedef T* pointer; + typedef T& reference; + }; + + template + struct iterator_traits + { + typedef EASTL_ITC_NS::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef ptrdiff_t difference_type; + typedef const T* pointer; + typedef const T& reference; + }; + + + + + /// is_iterator_wrapper + /// + /// Tells if an Iterator type is a wrapper type as opposed to a regular type. + /// Relies on the class declaring a typedef called wrapped_iterator_type. + /// + /// Examples of wrapping iterators: + /// reverse_iterator + /// generic_iterator + /// move_iterator + /// Examples of non-wrapping iterators: + /// iterator + /// list::iterator + /// char* + /// + /// Example behavior: + /// is_iterator_wrapper(int*)::value => false + /// is_iterator_wrapper(eastl::array*)::value => false + /// is_iterator_wrapper(eastl::vector::iterator)::value => false + /// is_iterator_wrapper(eastl::generic_iterator)::value => true + /// is_iterator_wrapper(eastl::move_iterator::iterator>)::value => true + /// + template + class is_iterator_wrapper + { + template + static eastl::no_type test(...); + + template + static eastl::yes_type test(typename U::wrapped_iterator_type*, typename eastl::enable_if::value>::type* = 0); + + public: + EA_DISABLE_VC_WARNING(6334) + static const bool value = (sizeof(test(NULL)) == sizeof(eastl::yes_type)); + EA_RESTORE_VC_WARNING() + }; + + + /// unwrap_iterator + /// + /// Takes a wrapper Iterator (e.g. move_iterator, reverse_iterator, generic_iterator) instance + /// and returns the wrapped iterator type. If Iterator is not a wrapper (including being a pointer), + /// or is not an iterator, then this function returns it as-is. + /// unwrap_iterator unwraps only a single layer of iterator at a time. You need to call it twice, + /// for example, to unwrap two layers of iterators. + /// + /// Example usage: + /// int* pInt = unwrap_iterator(&pIntArray[15]); + /// int* pInt = unwrap_iterator(generic_iterator(&pIntArray[15])); + /// MyVector::iterator it = unwrap_iterator(myVector.begin()); + /// MyVector::iterator it = unwrap_iterator(move_iterator(myVector.begin())); + /// + template + struct is_iterator_wrapper_helper + { + typedef Iterator iterator_type; + + static iterator_type get_base(Iterator it) + { return it; } + }; + + + template + struct is_iterator_wrapper_helper + { + typedef typename Iterator::iterator_type iterator_type; + + static iterator_type get_base(Iterator it) + { return it.base(); } + }; + + template + inline typename is_iterator_wrapper_helper::value>::iterator_type unwrap_iterator(Iterator it) + { return eastl::is_iterator_wrapper_helper::value>::get_base(it); } + + + + /// reverse_iterator + /// + /// From the C++ standard: + /// Bidirectional and random access iterators have corresponding reverse + /// iterator adaptors that iterate through the data structure in the + /// opposite direction. They have the same signatures as the corresponding + /// iterators. The fundamental relation between a reverse iterator and its + /// corresponding iterator i is established by the identity: + /// &*(reverse_iterator(i)) == &*(i - 1). + /// This mapping is dictated by the fact that while there is always a pointer + /// past the end of an array, there might not be a valid pointer before the + /// beginning of an array. + /// + template + class reverse_iterator : public iterator::iterator_category, + typename eastl::iterator_traits::value_type, + typename eastl::iterator_traits::difference_type, + typename eastl::iterator_traits::pointer, + typename eastl::iterator_traits::reference> + { + public: + typedef Iterator iterator_type; + typedef iterator_type wrapped_iterator_type; // This is not in the C++ Standard; it's used by use to identify it as a wrapping iterator type. + typedef typename eastl::iterator_traits::pointer pointer; + typedef typename eastl::iterator_traits::reference reference; + typedef typename eastl::iterator_traits::difference_type difference_type; + + protected: + Iterator mIterator; + + public: + EA_CPP14_CONSTEXPR reverse_iterator() // It's important that we construct mIterator, because if Iterator + : mIterator() { } // is a pointer, there's a difference between doing it and not. + + EA_CPP14_CONSTEXPR explicit reverse_iterator(iterator_type i) + : mIterator(i) { } + + EA_CPP14_CONSTEXPR reverse_iterator(const reverse_iterator& ri) + : mIterator(ri.mIterator) { } + + template + EA_CPP14_CONSTEXPR reverse_iterator(const reverse_iterator& ri) + : mIterator(ri.base()) { } + + // This operator= isn't in the standard, but the the C++ + // library working group has tentatively approved it, as it + // allows const and non-const reverse_iterators to interoperate. + template + EA_CPP14_CONSTEXPR reverse_iterator& operator=(const reverse_iterator& ri) + { mIterator = ri.base(); return *this; } + + EA_CPP14_CONSTEXPR iterator_type base() const + { return mIterator; } + + EA_CPP14_CONSTEXPR auto operator*() const + { + iterator_type i(mIterator); + return *--i; + } + + EA_CPP14_CONSTEXPR pointer operator->() const + { return &(operator*()); } + + EA_CPP14_CONSTEXPR reverse_iterator& operator++() + { --mIterator; return *this; } + + EA_CPP14_CONSTEXPR reverse_iterator operator++(int) + { + reverse_iterator ri(*this); + --mIterator; + return ri; + } + + EA_CPP14_CONSTEXPR reverse_iterator& operator--() + { ++mIterator; return *this; } + + EA_CPP14_CONSTEXPR reverse_iterator operator--(int) + { + reverse_iterator ri(*this); + ++mIterator; + return ri; + } + + EA_CPP14_CONSTEXPR reverse_iterator operator+(difference_type n) const + { return reverse_iterator(mIterator - n); } + + EA_CPP14_CONSTEXPR reverse_iterator& operator+=(difference_type n) + { mIterator -= n; return *this; } + + EA_CPP14_CONSTEXPR reverse_iterator operator-(difference_type n) const + { return reverse_iterator(mIterator + n); } + + EA_CPP14_CONSTEXPR reverse_iterator& operator-=(difference_type n) + { mIterator += n; return *this; } + + // http://cplusplus.github.io/LWG/lwg-defects.html#386, + // http://llvm.org/bugs/show_bug.cgi?id=17883 + // random_access_iterator operator[] is merely required to return something convertible to reference. + // reverse_iterator operator[] can't necessarily know what to return as the underlying iterator + // operator[] may return something other than reference. + EA_CPP14_CONSTEXPR reference operator[](difference_type n) const + { return mIterator[-n - 1]; } + }; + + + // The C++ library working group has tentatively approved the usage of two + // template parameters (Iterator1 and Iterator2) in order to allow reverse_iterators + // and const_reverse iterators to be comparable. This is a similar issue to the + // C++ defect report #179 regarding comparison of container iterators and const_iterators. + // + // libstdc++ reports that std::relops breaks the usage of two iterator types and if we + // want to support relops then we need to also make versions of each of below with + // a single template parameter to placate std::relops. But relops is hardly used due to + // the troubles it causes and so we are avoiding support here until somebody complains about it. + template + EA_CPP14_CONSTEXPR inline bool + operator==(const reverse_iterator& a, const reverse_iterator& b) + { return a.base() == b.base(); } + + + template + EA_CPP14_CONSTEXPR inline bool + operator<(const reverse_iterator& a, const reverse_iterator& b) + { return a.base() > b.base(); } + + + template + EA_CPP14_CONSTEXPR inline bool + operator!=(const reverse_iterator& a, const reverse_iterator& b) + { return a.base() != b.base(); } + + + template + EA_CPP14_CONSTEXPR inline bool + operator>(const reverse_iterator& a, const reverse_iterator& b) + { return a.base() < b.base(); } + + + template + EA_CPP14_CONSTEXPR inline bool + operator<=(const reverse_iterator& a, const reverse_iterator& b) + { return a.base() >= b.base(); } + + + template + EA_CPP14_CONSTEXPR inline bool + operator>=(const reverse_iterator& a, const reverse_iterator& b) + { return a.base() <= b.base(); } + + + template + EA_CPP14_CONSTEXPR inline typename reverse_iterator::difference_type + operator-(const reverse_iterator& a, const reverse_iterator& b) + { return b.base() - a.base(); } + + + template + EA_CPP14_CONSTEXPR inline reverse_iterator + operator+(typename reverse_iterator::difference_type n, const reverse_iterator& a) + { return reverse_iterator(a.base() - n); } + + + /// is_reverse_iterator + /// + /// This is a type traits extension utility. + /// Given an iterator, tells if it's a reverse_iterator vs anything else. + /// If it's a reverse iterator wrapped by another iterator then value is false. + /// To consider: Detect that if it's a move_iterator and unwrap + /// move_iterator so we can detect that underneath it's reverse_iterator. + /// + template + struct is_reverse_iterator + : public eastl::false_type {}; + + template + struct is_reverse_iterator< eastl::reverse_iterator > + : public eastl::true_type {}; + + + + /// unwrap_reverse_iterator + /// + /// Returns Iterator::get_base() if it's a reverse_iterator, else returns Iterator as-is. + /// + /// Example usage: + /// vector intVector; + /// eastl::reverse_iterator::iterator> reverseIterator(intVector.begin()); + /// vector::iterator it = unwrap_reverse_iterator(reverseIterator); + /// + /// Disabled until there is considered a good use for it. + /// template + /// inline typename eastl::is_iterator_wrapper_helper::value>::iterator_type unwrap_reverse_iterator(Iterator it) + /// { return eastl::is_iterator_wrapper_helper::value>::get_base(it); } + + + + /// move_iterator + /// + /// From the C++11 Standard, section 24.5.3.1: + /// Class template move_iterator is an iterator adaptor with the same behavior as the underlying iterator + /// except that its dereference operator implicitly converts the value returned by the underlying iterator's + /// dereference operator to an rvalue reference. Some generic algorithms can be called with move iterators to + /// replace copying with moving. + + template + class move_iterator // Don't inherit from iterator. + { + private: + using WrappedIteratorReference = typename iterator_traits::reference; + + public: + typedef Iterator iterator_type; + typedef iterator_type wrapped_iterator_type; // This is not in the C++ Standard; it's used by use to identify it as a wrapping iterator type. + typedef iterator_traits traits_type; + typedef typename traits_type::iterator_category iterator_category; + typedef typename traits_type::value_type value_type; + typedef typename traits_type::difference_type difference_type; + typedef Iterator pointer; + using reference = conditional_t::value, + remove_reference_t&&, + WrappedIteratorReference>; + + protected: + iterator_type mIterator; + + public: + move_iterator() + : mIterator() + { + } + + explicit move_iterator(iterator_type mi) + : mIterator(mi) { } + + template + move_iterator(const move_iterator& mi) + : mIterator(mi.base()) + { + } + + iterator_type base() const + { return mIterator; } + + reference operator*() const { return static_cast(*mIterator); } + + pointer operator->() const + { return mIterator; } + + move_iterator& operator++() + { + ++mIterator; + return *this; + } + + move_iterator operator++(int) + { + move_iterator tempMoveIterator = *this; + ++mIterator; + return tempMoveIterator; + } + + move_iterator& operator--() + { + --mIterator; + return *this; + } + + move_iterator operator--(int) + { + move_iterator tempMoveIterator = *this; + --mIterator; + return tempMoveIterator; + } + + move_iterator operator+(difference_type n) const + { return move_iterator(mIterator + n); } + + move_iterator& operator+=(difference_type n) + { + mIterator += n; + return *this; + } + + move_iterator operator-(difference_type n) const + { return move_iterator(mIterator - n); } + + move_iterator& operator-=(difference_type n) + { + mIterator -= n; + return *this; + } + + reference operator[](difference_type n) const + { return eastl::move(mIterator[n]); } + }; + + template + inline bool + operator==(const move_iterator& a, const move_iterator& b) + { return a.base() == b.base(); } + + + template + inline bool + operator!=(const move_iterator& a, const move_iterator& b) + { return !(a == b); } + + + template + inline bool + operator<(const move_iterator& a, const move_iterator& b) + { return a.base() < b.base(); } + + + template + inline bool + operator<=(const move_iterator& a, const move_iterator& b) + { return !(b < a); } + + + template + inline bool + operator>(const move_iterator& a, const move_iterator& b) + { return b < a; } + + + template + inline bool + operator>=(const move_iterator& a, const move_iterator& b) + { return !(a < b); } + + + template + inline auto + operator-(const move_iterator& a, const move_iterator& b) -> decltype(a.base() - b.base()) + { return a.base() - b.base(); } + + + template + inline move_iterator + operator+(typename move_iterator::difference_type n, const move_iterator& a) + { return a + n; } + + + template + inline move_iterator make_move_iterator(Iterator i) + { return move_iterator(i); } + + + // make_move_if_noexcept_iterator returns move_iterator if the Iterator is of a noexcept type; + // otherwise returns Iterator as-is. The point of this is to be able to avoid moves that can generate exceptions and instead + // fall back to copies or whatever the default IteratorType::operator* returns for use by copy/move algorithms. + // To consider: merge the conditional expression usage here with the one used by move_if_noexcept, as they are the same condition. + #if EASTL_EXCEPTIONS_ENABLED + template ::value_type>::value || + !eastl::is_copy_constructible::value_type>::value, + eastl::move_iterator, Iterator>::type> + inline IteratorType make_move_if_noexcept_iterator(Iterator i) + { return IteratorType(i); } + #else + // Else there are no exceptions and thus we always return a move_iterator. + template + inline eastl::move_iterator make_move_if_noexcept_iterator(Iterator i) + { return eastl::move_iterator(i); } + #endif + + + + /// is_move_iterator + /// + /// This is a type traits extension utility. + /// Given an iterator, tells if it's a move iterator vs anything else. + /// Example usage (though somewhat useless): + /// template + /// bool IsMoveIterator() { return typename eastl::is_move_iterator::value; } + /// + template + struct is_move_iterator + : public eastl::false_type {}; + + template + struct is_move_iterator< eastl::move_iterator > + : public eastl::true_type {}; + + + /// unwrap_move_iterator + /// + /// Returns Iterator::get_base() if it's a move_iterator, else returns Iterator as-is. + /// + /// Example usage: + /// vector intVector; + /// eastl::move_iterator::iterator> moveIterator(intVector.begin()); + /// vector::iterator it = unwrap_move_iterator(moveIterator); + /// + template + inline typename eastl::is_iterator_wrapper_helper::value>::iterator_type unwrap_move_iterator(Iterator it) + { return eastl::is_iterator_wrapper_helper::value>::get_base(it); } + + + + + /// back_insert_iterator + /// + /// A back_insert_iterator is simply a class that acts like an iterator but when you + /// assign a value to it, it calls push_back on the container with the value. + /// + template + class back_insert_iterator : public iterator + { + public: + typedef back_insert_iterator this_type; + typedef Container container_type; + typedef typename Container::const_reference const_reference; + + protected: + Container& container; + + public: + //back_insert_iterator(); // Not valid. Must construct with a Container. + + //back_insert_iterator(const this_type& x) // Compiler-implemented + // : container(x.container) { } + + explicit back_insert_iterator(Container& x) + : container(x) { } + + back_insert_iterator& operator=(const_reference value) + { container.push_back(value); return *this; } + + back_insert_iterator& operator=(typename Container::value_type&& value) + { container.push_back(eastl::move(value)); return *this; } + + back_insert_iterator& operator*() + { return *this; } + + back_insert_iterator& operator++() + { return *this; } // This is by design. + + back_insert_iterator operator++(int) + { return *this; } // This is by design. + + protected: + void operator=(const this_type&){} // Declared to avoid compiler warnings about inability to generate this function. + }; + + + /// back_inserter + /// + /// Creates an instance of a back_insert_iterator. + /// + template + inline back_insert_iterator + back_inserter(Container& x) + { return back_insert_iterator(x); } + + + + + /// front_insert_iterator + /// + /// A front_insert_iterator is simply a class that acts like an iterator but when you + /// assign a value to it, it calls push_front on the container with the value. + /// + template + class front_insert_iterator : public iterator + { + public: + typedef front_insert_iterator this_type; + typedef Container container_type; + typedef typename Container::const_reference const_reference; + + protected: + Container& container; + + public: + //front_insert_iterator(); // Not valid. Must construct with a Container. + + //front_insert_iterator(const this_type& x) // Compiler-implemented + // : container(x.container) { } + + explicit front_insert_iterator(Container& x) + : container(x) { } + + front_insert_iterator& operator=(const_reference value) + { container.push_front(value); return *this; } + + front_insert_iterator& operator*() + { return *this; } + + front_insert_iterator& operator++() + { return *this; } // This is by design. + + front_insert_iterator operator++(int) + { return *this; } // This is by design. + + protected: + void operator=(const this_type&){} // Declared to avoid compiler warnings about inability to generate this function. + }; + + + /// front_inserter + /// + /// Creates an instance of a front_insert_iterator. + /// + template + inline front_insert_iterator + front_inserter(Container& x) + { return front_insert_iterator(x); } + + + + + /// insert_iterator + /// + /// An insert_iterator is like an iterator except that when you assign a value to it, + /// the insert_iterator inserts the value into the container and increments the iterator. + /// + /// insert_iterator is an iterator adaptor that functions as an OutputIterator: + /// assignment through an insert_iterator inserts an object into a container. + /// Specifically, if ii is an insert_iterator, then ii keeps track of a container c and + /// an insertion point p; the expression *ii = x performs the insertion container.insert(p, x). + /// + /// If you assign through an insert_iterator several times, then you will be inserting + /// several elements into the underlying container. In the case of a sequence, they will + /// appear at a particular location in the underlying sequence, in the order in which + /// they were inserted: one of the arguments to insert_iterator's constructor is an + /// iterator p, and the new range will be inserted immediately before p. + /// + template + class insert_iterator : public iterator + { + public: + typedef Container container_type; + typedef typename Container::iterator iterator_type; + typedef typename Container::const_reference const_reference; + + protected: + Container& container; + iterator_type it; + + public: + // This assignment operator is defined more to stop compiler warnings (e.g. VC++ C4512) + // than to be useful. However, it does allow an insert_iterator to be assigned to another + // insert iterator provided that they point to the same container. + insert_iterator& operator=(const insert_iterator& x) + { + EASTL_ASSERT(&x.container == &container); + it = x.it; + return *this; + } + + insert_iterator(Container& x, iterator_type itNew) + : container(x), it(itNew) {} + + insert_iterator& operator=(const_reference value) + { + it = container.insert(it, value); + ++it; + return *this; + } + + insert_iterator& operator*() + { return *this; } + + insert_iterator& operator++() + { return *this; } // This is by design. + + insert_iterator& operator++(int) + { return *this; } // This is by design. + + }; // insert_iterator + + + /// inserter + /// + /// Creates an instance of an insert_iterator. + /// + template + inline eastl::insert_iterator + inserter(Container& x, Iterator i) + { + typedef typename Container::iterator iterator; + return eastl::insert_iterator(x, iterator(i)); + } + + + /// is_insert_iterator + /// + /// This is a type traits extension utility. + /// Given an iterator, tells if it's an insert_iterator vs anything else. + /// If it's a insert_iterator wrapped by another iterator then value is false. + /// + template + struct is_insert_iterator + : public eastl::false_type {}; + + template + struct is_insert_iterator< eastl::insert_iterator > + : public eastl::true_type {}; + + + + + ////////////////////////////////////////////////////////////////////////////////// + /// distance + /// + /// Implements the distance() function. There are two versions, one for + /// random access iterators (e.g. with vector) and one for regular input + /// iterators (e.g. with list). The former is more efficient. + /// + template + EA_CONSTEXPR + inline typename eastl::iterator_traits::difference_type + distance_impl(InputIterator first, InputIterator last, EASTL_ITC_NS::input_iterator_tag) + { + typename eastl::iterator_traits::difference_type n = 0; + + while(first != last) + { + ++first; + ++n; + } + return n; + } + + template + EA_CONSTEXPR + inline typename eastl::iterator_traits::difference_type + distance_impl(RandomAccessIterator first, RandomAccessIterator last, EASTL_ITC_NS::random_access_iterator_tag) + { + return last - first; + } + + // Special version defined so that std C++ iterators can be recognized by + // this function. Unfortunately, this function treats all foreign iterators + // as InputIterators and thus can seriously hamper performance in the case + // of large ranges of bidirectional_iterator_tag iterators. + //template + //inline typename eastl::iterator_traits::difference_type + //distance_impl(InputIterator first, InputIterator last, ...) + //{ + // typename eastl::iterator_traits::difference_type n = 0; + // + // while(first != last) + // { + // ++first; + // ++n; + // } + // return n; + //} + + template + EA_CONSTEXPR + inline typename eastl::iterator_traits::difference_type + distance(InputIterator first, InputIterator last) + { + typedef typename eastl::iterator_traits::iterator_category IC; + + return eastl::distance_impl(first, last, IC()); + } + + + + + ////////////////////////////////////////////////////////////////////////////////// + /// advance + /// + /// Implements the advance() function. There are three versions, one for + /// random access iterators (e.g. with vector), one for bidirectional + /// iterators (list) and one for regular input iterators (e.g. with slist). + /// + template + inline void + advance_impl(InputIterator& i, Distance n, EASTL_ITC_NS::input_iterator_tag) + { + while(n--) + ++i; + } + + template + struct advance_bi_impl + { + template + static void advance_impl(BidirectionalIterator& i, Distance n) // Specialization for unsigned distance type. + { + while(n--) + ++i; + } + }; + + template <> + struct advance_bi_impl + { + template + static void advance_impl(BidirectionalIterator& i, Distance n) // Specialization for signed distance type. + { + if(n > 0) + { + while(n--) + ++i; + } + else + { + while(n++) + --i; + } + } + }; + + template + inline void + advance_impl(BidirectionalIterator& i, Distance n, EASTL_ITC_NS::bidirectional_iterator_tag) + { + advance_bi_impl::value>::advance_impl(i, n); + } + + template + inline void + advance_impl(RandomAccessIterator& i, Distance n, EASTL_ITC_NS::random_access_iterator_tag) + { + i += n; + } + + // Special version defined so that std C++ iterators can be recognized by + // this function. Unfortunately, this function treats all foreign iterators + // as InputIterators and thus can seriously hamper performance in the case + // of large ranges of bidirectional_iterator_tag iterators. + //template + //inline void + //advance_impl(InputIterator& i, Distance n, ...) + //{ + // while(n--) + // ++i; + //} + + template + inline void + advance(InputIterator& i, Distance n) + { + typedef typename eastl::iterator_traits::iterator_category IC; + + eastl::advance_impl(i, n, IC()); + } + + + // eastl::next / eastl::prev + // Return the nth/-nth successor of iterator it. + // + // http://en.cppreference.com/w/cpp/iterator/next + // + template + inline InputIterator + next(InputIterator it, typename eastl::iterator_traits::difference_type n = 1) + { + eastl::advance(it, n); + return it; + } + + template + inline InputIterator + prev(InputIterator it, typename eastl::iterator_traits::difference_type n = 1) + { + eastl::advance(it, -n); + return it; + } + + +#if defined(EA_COMPILER_CPP11_ENABLED) && EA_COMPILER_CPP11_ENABLED + + // eastl::data + // + // http://en.cppreference.com/w/cpp/iterator/data + // + template + EA_CPP14_CONSTEXPR auto data(Container& c) -> decltype(c.data()) + { return c.data(); } + + template + EA_CPP14_CONSTEXPR auto data(const Container& c) -> decltype(c.data()) + { return c.data(); } + + template + EA_CPP14_CONSTEXPR T* data(T(&array)[N]) EA_NOEXCEPT + { return array; } + + template + EA_CPP14_CONSTEXPR const E* data(std::initializer_list il) EA_NOEXCEPT + { return il.begin(); } + + + // eastl::size + // + // http://en.cppreference.com/w/cpp/iterator/size + // + template + EA_CPP14_CONSTEXPR auto size(const C& c) -> decltype(c.size()) + { return c.size(); } + + template + EA_CPP14_CONSTEXPR size_t size(const T (&)[N]) EA_NOEXCEPT + { return N; } + + + // eastl::ssize + // + // https://en.cppreference.com/w/cpp/iterator/size + // + template + EA_CPP14_CONSTEXPR ptrdiff_t ssize(const T(&)[N]) EA_NOEXCEPT + { return N; } + + template + EA_CPP14_CONSTEXPR auto ssize(const C& c) + -> eastl::common_type_t> + { + using R = eastl::common_type_t>; + return static_cast(c.size()); + } + + + // eastl::empty + // + // http://en.cppreference.com/w/cpp/iterator/empty + // + template + EA_CPP14_CONSTEXPR auto empty(const Container& c) -> decltype(c.empty()) + { return c.empty(); } + + template + EA_CPP14_CONSTEXPR bool empty(const T (&)[N]) EA_NOEXCEPT + { return false; } + + template + EA_CPP14_CONSTEXPR bool empty(std::initializer_list il) EA_NOEXCEPT + { return il.size() == 0; } + +#endif // defined(EA_COMPILER_CPP11_ENABLED) && EA_COMPILER_CPP11_ENABLED + + + // eastl::begin / eastl::end + // http://en.cppreference.com/w/cpp/iterator/begin + // + // In order to enable eastl::begin and eastl::end, the compiler needs to have conforming support + // for argument-dependent lookup if it supports C++11 range-based for loops. The reason for this is + // that in C++11 range-based for loops result in usage of std::begin/std::end, but allow that to + // be overridden by argument-dependent lookup: + // C++11 Standard, section 6.5.4, paragraph 1. + // "otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, + // where begin and end are looked up with argument-dependent lookup (3.4.2). For the + // purposes of this name lookup, namespace std is an associated namespace." + // It turns out that one compiler has a problem: GCC 4.6. That version added support for + // range-based for loops but has broken argument-dependent lookup which was fixed in GCC 4.7. + // + #if (defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION == 4006)) + #define EASTL_BEGIN_END_ENABLED 0 + #else + #define EASTL_BEGIN_END_ENABLED 1 + #endif + + #if EASTL_BEGIN_END_ENABLED + template + EA_CPP14_CONSTEXPR inline auto begin(Container& container) -> decltype(container.begin()) + { + return container.begin(); + } + + template + EA_CPP14_CONSTEXPR inline auto begin(const Container& container) -> decltype(container.begin()) + { + return container.begin(); + } + + template + EA_CPP14_CONSTEXPR inline T* begin(T (&arrayObject)[arraySize]) EA_NOEXCEPT + { + return arrayObject; + } + + template + EA_CPP14_CONSTEXPR inline auto cbegin(const Container& container) -> decltype(eastl::begin(container)) + { + return eastl::begin(container); + } + + template + EA_CPP14_CONSTEXPR inline auto end(Container& container) -> decltype(container.end()) + { + return container.end(); + } + + template + EA_CPP14_CONSTEXPR inline auto end(const Container& container) -> decltype(container.end()) + { + return container.end(); + } + + template + EA_CPP14_CONSTEXPR inline T* end(T (&arrayObject)[arraySize]) EA_NOEXCEPT + { + return (arrayObject + arraySize); + } + + template + EA_CPP14_CONSTEXPR inline auto cend(const Container& container) -> decltype(eastl::end(container)) + { + return eastl::end(container); + } + + template + EA_CPP14_CONSTEXPR inline auto rbegin(Container& container) -> decltype(container.rbegin()) + { + return container.rbegin(); + } + + template + EA_CPP14_CONSTEXPR inline auto rbegin(const Container& container) -> decltype(container.rbegin()) + { + return container.rbegin(); + } + + template + EA_CPP14_CONSTEXPR inline auto rend(Container& container) -> decltype(container.rend()) + { + return container.rend(); + } + + template + EA_CPP14_CONSTEXPR inline auto rend(const Container& container) -> decltype(container.rend()) + { + return container.rend(); + } + + template + EA_CPP14_CONSTEXPR inline auto crbegin(const Container& container) -> decltype(eastl::rbegin(container)) + { + return container.rbegin(); + } + + template + EA_CPP14_CONSTEXPR inline auto crend(const Container& container) -> decltype(eastl::rend(container)) + { + return container.rend(); + } + + + template + EA_CPP14_CONSTEXPR inline reverse_iterator rbegin(T (&arrayObject)[arraySize]) + { + return reverse_iterator(arrayObject + arraySize); + } + + template + EA_CPP14_CONSTEXPR inline reverse_iterator rend(T (&arrayObject)[arraySize]) + { + return reverse_iterator(arrayObject); + } + + template + EA_CPP14_CONSTEXPR inline reverse_iterator rbegin(std::initializer_list ilist) + { + return eastl::reverse_iterator(ilist.end()); + } + + template + EA_CPP14_CONSTEXPR inline reverse_iterator rend(std::initializer_list ilist) + { + return eastl::reverse_iterator(ilist.begin()); + } + + template + EA_CPP14_CONSTEXPR reverse_iterator make_reverse_iterator(Iterator i) + { return reverse_iterator(i); } + + #endif // EASTL_BEGIN_END_ENABLED + +} // namespace eastl + + + +// Some compilers (e.g. GCC 4.6) support range-based for loops, but have a bug with +// respect to argument-dependent lookup which results on them unilaterally using std::begin/end +// with range-based for loops. To work around this we #include for this case in +// order to make std::begin/end visible to users of , for portability. +#if !EASTL_BEGIN_END_ENABLED && !defined(EA_COMPILER_NO_RANGE_BASED_FOR_LOOP) + #include +#endif + + + +EA_RESTORE_VC_WARNING(); +EA_RESTORE_VC_WARNING(); + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/linked_array.h b/lib/EASTL/include/EASTL/linked_array.h new file mode 100644 index 000000000..88d991463 --- /dev/null +++ b/lib/EASTL/include/EASTL/linked_array.h @@ -0,0 +1,336 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This class implements a linked_array template, which is an array version +// of linked_ptr. See linked_ptr for detailed documentation. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_LINKED_ARRAY_H +#define EASTL_LINKED_ARRAY_H + + +#include +#include // Defines smart_array_deleter +#include // Defines linked_ptr_base +#include // Definition of ptrdiff_t + +#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 +{ + + /// class linked_array + /// + /// This class implements a linked_array template, which is an array version + /// of linked_ptr. See linked_ptr for detailed documentation. + /// + template > + class linked_array + { + + protected: + + /// this_type + /// This is an alias for linked_array, this class. + typedef linked_array this_type; + + /// deleter_type + typedef Deleter deleter_type; + + T* mpArray; + mutable const this_type* mpPrev; + mutable const this_type* mpNext; + + void link(const linked_array& linkedArray) + { // This code can only be called when we are in a reset state. + // assert(!mpArray && (mpNext == mpPrev)); + mpNext = linkedArray.mpNext; + mpNext->mpPrev = this; + mpPrev = &linkedArray; + linkedArray.mpNext = this; + } + + public: + /// element_type + /// Synonym for type T, useful for external code to reference the + /// type in a generic way. + typedef T element_type; + + + /// linked_array + /// Takes ownership of the pointer. It is OK if the input pointer is null. + explicit linked_array(T* pArray = NULL) + : mpArray(pArray) + { + mpPrev = mpNext = this; + } + + + /// linked_array + /// Shares ownership of a pointer with another instance of linked_array. + linked_array(const linked_array& linkedArray) + : mpArray(linkedArray.mpArray) + { + if(mpArray) + link(linkedArray); + else + mpPrev = mpNext = this; + } + + + /// ~linked_array + /// Removes this object from the of objects using the shared pointer. + /// If this object is the last owner of the shared pointer, the shared + /// pointer is deleted. + ~linked_array() + { + reset(); + } + + + /// operator= + /// Copies another linked_array to this object. Note that this object + /// may already own a shared pointer with another different pointer + /// (but still of the same type) before this call. In that case, + /// this function removes ownership of the old pointer and takes shared + /// ownership of the new pointer and increments its reference count. + linked_array& operator=(const linked_array& linkedArray) + { + if(linkedArray.mpArray != mpArray) + { + reset(linkedArray.mpArray); + if(linkedArray.mpArray) + link(linkedArray); + } + return *this; + } + + + /// operator= + /// Assigns a new pointer. If the new pointer is equivalent + /// to the current pointer, nothing is done. Otherwise the + /// current pointer is unlinked and possibly destroyed. + /// The new pointer can be NULL. + linked_array& operator=(T* pArray) + { + reset(pArray); + return *this; + } + + + /// reset + /// Releases the owned pointer and takes ownership of the + /// passed in pointer. If the passed in pointer is the same + /// as the owned pointer, nothing is done. The passed in pointer + /// can be null, in which case the use count is set to 1. + void reset(T* pArray = NULL) + { + if(pArray != mpArray) + { + if(unique()) + { + deleter_type del; + del(mpArray); + } + else + { + mpPrev->mpNext = mpNext; + mpNext->mpPrev = mpPrev; + mpPrev = mpNext = this; + } + mpArray = pArray; + } + } + + + /// swap + /// Exchanges the owned pointer beween two linkedArray objects. + /// + /// This function is disabled as it is currently deemed unsafe. + /// The problem is that the only way to implement this function + /// is to transfer pointers between the objects; you cannot + /// transfer the linked list membership between the objects. + /// Thus unless both linked_array objects were 'unique()', the + /// shared pointers would be duplicated amongst containers, + /// resulting in a crash. + //void swap(linked_array& linkedArray) + //{ + // if(linkedArray.mpArray != mpArray) + // { // This is only safe if both linked_arrays are unique(). + // linkedArray::element_type* const pArrayTemp = linkedArray.mpArray; + // linkedArray.reset(mpArray); + // reset(pArrayTemp); + // } + //} + + + /// operator[] + /// Returns a reference to the specified item in the owned pointer array. + T& operator[](ptrdiff_t i) const + { + // assert(mpArray && (i >= 0)); + return mpArray[i]; + } + + + /// operator* + /// Returns the owner pointer dereferenced. + T& operator*() const + { + return *mpArray; + } + + + /// operator-> + /// Allows access to the owned pointer via operator->() + T* operator->() const + { + return mpArray; + } + + + /// get + /// Returns the owned pointer. Note that this class does + /// not provide an operator T() function. This is because such + /// a thing (automatic conversion) is deemed unsafe. + T* get() const + { + return mpArray; + } + + + /// use_count + /// Returns the use count of the shared pointer. + /// The return value is one if the owned pointer is null. + /// This function is provided for compatibility with the + /// proposed C++ standard and for debugging purposes. It is not + /// intended for runtime use given that its execution time is + /// not constant. + int use_count() const + { + int useCount(1); + + for(const linked_ptr_base* pCurrent = this; pCurrent->mpNext != this; pCurrent = pCurrent->mpNext) + ++useCount; + + return useCount; + } + + + /// unique + /// Returns true if the use count of the owned pointer is one. + /// The return value is true if the owned pointer is null. + bool unique() const + { + return (mpNext == this); + } + + + /// Implicit operator bool + /// Allows for using a linked_array as a boolean. + /// Note that below we do not use operator bool(). The reason for this + /// is that booleans automatically convert up to short, int, float, etc. + /// The result is that this: if(linkedArray == 1) would yield true (bad). + typedef T* (this_type::*bool_)() const; + operator bool_() const + { + if(mpArray) + return &this_type::get; + return NULL; + } + + + /// operator! + /// This returns the opposite of operator bool; it returns true if + /// the owned pointer is null. Some compilers require this and some don't. + bool operator!() + { + return (mpArray == NULL); + } + + + /// force_delete + /// Forces deletion of the shared pointer. Fixes all references to the + /// pointer by any other owners to be NULL. + void force_delete() + { + T* const pArray = mpArray; + + this_type* p = this; + do + { + this_type* const pNext = const_cast(p->mpNext); + p->mpArray = NULL; + p->mpNext = p->mpPrev = p; + p = pNext; + } + while(p != this); + + deleter_type del; + del(pArray); + } + + }; // class linked_array + + + + /// get_pointer + /// Returns linked_array::get() via the input linked_array. Provided for compatibility + /// with certain well-known libraries that use this functionality. + template + inline T* get_pointer(const linked_array& linkedArray) + { + return linkedArray.get(); + } + + + /// operator== + /// Compares two linked_array objects for equality. Equality is defined as + /// being true when the pointer shared between two linked_array objects is equal. + template + inline bool operator==(const linked_array& linkedArray1, const linked_array& linkedArray2) + { + return (linkedArray1.get() == linkedArray2.get()); + } + + + /// operator!= + /// Compares two linked_array objects for inequality. Equality is defined as + /// being true when the pointer shared between two linked_array objects is equal. + template + inline bool operator!=(const linked_array& linkedArray1, const linked_array& linkedArray2) + { + return (linkedArray1.get() != linkedArray2.get()); + } + + + /// operator< + /// Returns which linked_array is 'less' than the other. Useful when storing + /// sorted containers of linked_array objects. + template + inline bool operator<(const linked_array& linkedArray1, const linked_array& linkedArray2) + { + return (linkedArray1.get() < linkedArray2.get()); + } + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/linked_ptr.h b/lib/EASTL/include/EASTL/linked_ptr.h new file mode 100644 index 000000000..f57681a9c --- /dev/null +++ b/lib/EASTL/include/EASTL/linked_ptr.h @@ -0,0 +1,426 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_LINKED_PTR_H +#define EASTL_LINKED_PTR_H + + + +#include +#include // Defines smart_ptr_deleter +#include +#include + +#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 +{ + + /// linked_ptr_base + /// + /// This class allows linked_ptr and linked_ptr to share the same + /// base nodes and thus be in the same linked list. + /// + struct linked_ptr_base + { + mutable linked_ptr_base* mpPrev; + mutable linked_ptr_base* mpNext; + }; + + + /// linked_ptr + /// + /// This class implements a linked_ptr template. A linked_ptr is like the C++ + /// Standard Library auto_ptr except that it allows sharing of pointers between + /// instances of auto_ptr via reference counting. linked_ptr objects can safely + /// be copied and can safely be used in C++ Standard Library containers such + /// as std::vector or std::list. This implementation, however, is not thread-safe. + /// you would need to use a separate linked_ptr_mt (multi-threaded) to get + /// thread safety. + /// + /// linked_ptr is a variation of shared_ptr (a.k.a. counted_ptr) which differs + /// in that instead of being implemented by a shared integer stored on the heap, + /// it is implemented by linked list stored within the linked_ptr object itself. + /// The result is that no memory is explicitly allocated from the heap, though + /// the cost of each linked_ptr object is 12 bytes of memory (32 bit machine) + /// instead of 4 bytes for the case of shared_ptr (depending on the heap). + /// + template > + class linked_ptr : public linked_ptr_base + { + protected: + template friend class linked_ptr; + + /// this_type + /// This is an alias for linked_ptr, this class. + typedef linked_ptr this_type; + + /// deleter_type + typedef Deleter deleter_type; + + T* mpValue; /// The owned pointer. + + template + void link(const linked_ptr& linkedPtr) + { // This code can only be called when we are in a reset state. + // assert(!mpValue && (mpNext == mpPrev)); + mpNext = linkedPtr.mpNext; + mpNext->mpPrev = this; + mpPrev = const_cast*>(&linkedPtr); + linkedPtr.mpNext = this; + } + + public: + /// element_type + /// Synonym for type T, useful for external code to reference the + /// type in a generic way. + typedef T element_type; + + + /// linked_ptr + /// Default constructor. + linked_ptr() + : mpValue(NULL) + { + mpPrev = mpNext = this; + } + + + /// linked_ptr + /// Takes ownership of the pointer. It is OK if the input pointer is null. + template + explicit linked_ptr(U* pValue) + : mpValue(pValue) + { + mpPrev = mpNext = this; + } + + + /// linked_ptr + /// Construction with self type. + /// If we want a shared_ptr constructor that is templated on linked_ptr, + /// then we need to make it in addition to this function, as otherwise + /// the compiler will generate this function and things will go wrong. + linked_ptr(const linked_ptr& linkedPtr) + : mpValue(linkedPtr.mpValue) + { + if(mpValue) + link(linkedPtr); + else + mpPrev = mpNext = this; + } + + + /// linked_ptr + /// Shares ownership of a pointer with another instance of linked_ptr. + template + linked_ptr(const linked_ptr& linkedPtr) + : mpValue(linkedPtr.mpValue) + { + if(mpValue) + link(linkedPtr); + else + mpPrev = mpNext = this; + } + + + /// ~linked_ptr + /// Removes this object from the of objects using the shared pointer. + /// If this object is the last owner of the shared pointer, the shared + /// pointer is deleted. + ~linked_ptr() + { + reset(); + } + + + /// operator= + /// If we want a shared_ptr operator= that is templated on linked_ptr, + /// then we need to make it in addition to this function, as otherwise + /// the compiler will generate this function and things will go wrong. + linked_ptr& operator=(const linked_ptr& linkedPtr) + { + if(linkedPtr.mpValue != mpValue) + { + reset(linkedPtr.mpValue); + if(linkedPtr.mpValue) + link(linkedPtr); + } + return *this; + } + + + /// operator= + /// Copies another linked_ptr to this object. Note that this object + /// may already own a shared pointer with another different pointer + /// (but still of the same type) before this call. In that case, + /// this function removes ownership of the old pointer and takes shared + /// ownership of the new pointer and increments its reference count. + template + linked_ptr& operator=(const linked_ptr& linkedPtr) + { + if(linkedPtr.mpValue != mpValue) + { + reset(linkedPtr.mpValue); + if(linkedPtr.mpValue) + link(linkedPtr); + } + return *this; + } + + + /// operator= + /// Assigns a new pointer. If the new pointer is equivalent + /// to the current pointer, nothing is done. Otherwise the + /// current pointer is unlinked and possibly destroyed. + /// The new pointer can be NULL. + template + linked_ptr& operator=(U* pValue) + { + reset(pValue); + return *this; + } + + + /// reset + /// Releases the owned pointer and takes ownership of the + /// passed in pointer. If the passed in pointer is the same + /// as the owned pointer, nothing is done. The passed in pointer + /// can be NULL, in which case the use count is set to 1. + template + void reset(U* pValue) + { + if(pValue != mpValue) + { + if(unique()) + { + deleter_type del; + del(mpValue); + } + else + { + mpPrev->mpNext = mpNext; + mpNext->mpPrev = mpPrev; + mpPrev = mpNext = this; + } + mpValue = pValue; + } + } + + + /// reset + /// Resets the container with NULL. If the current pointer + /// is non-NULL, it is unlinked and possibly destroyed. + void reset() + { + reset((T*)NULL); + } + + + /// swap + /// Exchanges the owned pointer beween two linkedPtr objects. + /// + /// This function is disabled as it is currently deemed unsafe. + /// The problem is that the only way to implement this function + /// is to transfer pointers between the objects; you cannot + /// transfer the linked list membership between the objects. + /// Thus unless both linked_ptr objects were 'unique()', the + /// shared pointers would be duplicated amongst containers, + /// resulting in a crash. + //template + //void swap(linked_ptr& linkedPtr) + //{ + // if(linkedPtr.mpValue != mpValue) + // { // This is only safe if both linked_ptrs are unique(). + // linkedPtr::element_type* const pValueTemp = linkedPtr.mpValue; + // linkedPtr.reset(mpValue); + // reset(pValueTemp); + // } + //} + + + /// operator* + /// Returns the owner pointer dereferenced. + T& operator*() const + { + return *mpValue; + } + + + /// operator-> + /// Allows access to the owned pointer via operator->() + T* operator->() const + { + return mpValue; + } + + + /// get + /// Returns the owned pointer. Note that this class does + /// not provide an operator T() function. This is because such + /// a thing (automatic conversion) is deemed unsafe. + T* get() const + { + return mpValue; + } + + + /// use_count + /// Returns the use count of the shared pointer. + /// The return value is one if the owned pointer is null. + /// This function is provided for compatibility with the + /// proposed C++ standard and for debugging purposes. It is not + /// intended for runtime use given that its execution time is + /// not constant. + int use_count() const + { + int useCount(1); + + for(const linked_ptr_base* pCurrent = static_cast(this); + pCurrent->mpNext != static_cast(this); pCurrent = pCurrent->mpNext) + ++useCount; + + return useCount; + } + + + /// unique + /// Returns true if the use count of the owned pointer is one. + /// The return value is true if the owned pointer is null. + bool unique() const + { + return (mpNext == static_cast(this)); + } + + + /// Implicit operator bool + /// Allows for using a linked_ptr as a boolean. + /// Note that below we do not use operator bool(). The reason for this + /// is that booleans automatically convert up to short, int, float, etc. + /// The result is that this: if(linkedPtr == 1) would yield true (bad). + typedef T* (this_type::*bool_)() const; + operator bool_() const + { + if(mpValue) + return &this_type::get; + return NULL; + } + + + /// operator! + /// This returns the opposite of operator bool; it returns true if + /// the owned pointer is null. Some compilers require this and some don't. + bool operator!() + { + return (mpValue == NULL); + } + + + /// detach + /// Returns ownership of the pointer to the caller. Fixes all + /// references to the pointer by any other owners to be NULL. + /// This function can work properly only if all entries in the list + /// refer to type T and none refer to any other type (e.g. U). + T* detach() + { + T* const pValue = mpValue; + + linked_ptr_base* p = this; + do + { + linked_ptr_base* const pNext = p->mpNext; + static_cast(p)->mpValue = NULL; + p->mpNext = p->mpPrev = p; + p = pNext; + } + while(p != this); + + return pValue; + } + + /// force_delete + /// Forces deletion of the shared pointer. Fixes all references to the + /// pointer by any other owners to be NULL. + /// This function can work properly only if all entries in the list + /// refer to type T and none refer to any other type (e.g. U). + void force_delete() + { + T* const pValue = detach(); + Deleter del; + del(pValue); + } + + }; // class linked_ptr + + + + /// get_pointer + /// Returns linked_ptr::get() via the input linked_ptr. Provided for compatibility + /// with certain well-known libraries that use this functionality. + template + inline T* get_pointer(const linked_ptr& linkedPtr) + { + return linkedPtr.get(); + } + + + /// operator== + /// Compares two linked_ptr objects for equality. Equality is defined as + /// being true when the pointer shared between two linked_ptr objects is equal. + template + inline bool operator==(const linked_ptr& linkedPtr1, const linked_ptr& linkedPtr2) + { + return (linkedPtr1.get() == linkedPtr2.get()); + } + + + /// operator!= + /// Compares two linked_ptr objects for inequality. Equality is defined as + /// being true when the pointer shared between two linked_ptr objects is equal. + template + inline bool operator!=(const linked_ptr& linkedPtr1, const linked_ptr& linkedPtr2) + { + return (linkedPtr1.get() != linkedPtr2.get()); + } + + + /// operator< + /// Returns which linked_ptr is 'less' than the other. Useful when storing + /// sorted containers of linked_ptr objects. + template + inline bool operator<(const linked_ptr& linkedPtr1, const linked_ptr& linkedPtr2) + { + return (linkedPtr1.get() < linkedPtr2.get()); + } + + +} // namespace eastl + + +#endif // Header include guard + + + + + + + + + + + + + + + + + + + + diff --git a/lib/EASTL/include/EASTL/list.h b/lib/EASTL/include/EASTL/list.h new file mode 100644 index 000000000..5e7943793 --- /dev/null +++ b/lib/EASTL/include/EASTL/list.h @@ -0,0 +1,2168 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This file implements a doubly-linked list, much like the C++ std::list class. +// The primary distinctions between this list and std::list are: +// - list doesn't implement some of the less-frequently used functions +// of std::list. Any required functions can be added at a later time. +// - list has a couple extension functions that increase performance. +// - list can contain objects with alignment requirements. std::list cannot +// do so without a bit of tedious non-portable effort. +// - list has optimizations that don't exist in the STL implementations +// supplied by library vendors for our targeted platforms. +// - list supports debug memory naming natively. +// - list::size() by default is not a constant time function, like the list::size +// in some std implementations such as STLPort and SGI STL but unlike the +// list in Dinkumware and Metrowerks. The EASTL_LIST_SIZE_CACHE option can change this. +// - list provides a guaranteed portable node definition that allows users +// to write custom fixed size node allocators that are portable. +// - list is easier to read, debug, and visualize. +// - list is savvy to an environment that doesn't have exception handling, +// as is sometimes the case with console or embedded environments. +// - list has less deeply nested function calls and allows the user to +// enable forced inlining in debug builds in order to reduce bloat. +// - list doesn't keep a member size variable. This means that list is +// smaller than std::list (depends on std::list) and that for most operations +// it is faster than std::list. However, the list::size function is slower. +// - list::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_LIST_H +#define EASTL_LIST_H + + +#include +#include +#include +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + + +// 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc +// 4345 - Behavior change: an object of POD type constructed with an initializer of the form () will be default-initialized +// 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. +// 4623 - default constructor was implicitly defined as deleted +EA_DISABLE_VC_WARNING(4530 4345 4571 4623); + + +#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_LIST_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_LIST_DEFAULT_NAME + #define EASTL_LIST_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " list" // Unless the user overrides something, this is "EASTL list". + #endif + + + /// EASTL_LIST_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_LIST_DEFAULT_ALLOCATOR + #define EASTL_LIST_DEFAULT_ALLOCATOR allocator_type(EASTL_LIST_DEFAULT_NAME) + #endif + + + + /// ListNodeBase + /// + /// We define a ListNodeBase separately from ListNode (below), because it allows + /// us to have non-templated operations such as insert, remove (below), and it + /// makes it so that the list 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 a list harder, given that the node pointers are of + /// type ListNodeBase and not ListNode. However, see ListNodeBaseProxy below. + /// + struct ListNodeBase + { + ListNodeBase* mpNext; + ListNodeBase* mpPrev; + + void insert(ListNodeBase* pNext) EA_NOEXCEPT; // Inserts this standalone node before the node pNext in pNext's list. + void remove() EA_NOEXCEPT; // Removes this node from the list it's in. Leaves this node's mpNext/mpPrev invalid. + void splice(ListNodeBase* pFirst, ListNodeBase* pLast) EA_NOEXCEPT; // Removes [pFirst,pLast) from the list it's in and inserts it before this in this node's list. + void reverse() EA_NOEXCEPT; // Reverses the order of nodes in the circular list this node is a part of. + static void swap(ListNodeBase& a, ListNodeBase& b) EA_NOEXCEPT; // Swaps the nodes a and b in the lists to which they belong. + + void insert_range(ListNodeBase* pFirst, ListNodeBase* pFinal) EA_NOEXCEPT; // Differs from splice in that first/final aren't in another list. + static void remove_range(ListNodeBase* pFirst, ListNodeBase* pFinal) EA_NOEXCEPT; // + } EASTL_LIST_PROXY_MAY_ALIAS; + + + #if EASTL_LIST_PROXY_ENABLED + + /// ListNodeBaseProxy + /// + /// In debug builds, we define ListNodeBaseProxy to be the same thing as + /// ListNodeBase, except it is templated on the parent ListNode class. + /// We do this because we want users in debug builds to be able to easily + /// view the list's contents in a debugger GUI. We do this only in a debug + /// build for the reasons described above: that ListNodeBase needs to be + /// as efficient as possible and not cause code bloat or extra function + /// calls (inlined or not). + /// + /// ListNodeBaseProxy *must* be separate from its parent class ListNode + /// because the list class must have a member node which contains no T value. + /// It is thus incorrect for us to have one single ListNode class which + /// has mpNext, mpPrev, and mValue. So we do a recursive template trick in + /// the definition and use of SListNodeBaseProxy. + /// + template + struct ListNodeBaseProxy + { + LN* mpNext; + LN* mpPrev; + }; + + template + struct ListNode : public ListNodeBaseProxy< ListNode > + { + T mValue; + }; + + #else + + EA_DISABLE_VC_WARNING(4625 4626) + template + struct ListNode : public ListNodeBase + { + T mValue; + }; + EA_RESTORE_VC_WARNING() + + #endif + + + + + /// ListIterator + /// + template + struct ListIterator + { + typedef ListIterator this_type; + typedef ListIterator iterator; + typedef ListIterator 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 ListNode node_type; + typedef Pointer pointer; + typedef Reference reference; + typedef EASTL_ITC_NS::bidirectional_iterator_tag iterator_category; + + public: + node_type* mpNode; + + public: + ListIterator() EA_NOEXCEPT; + ListIterator(const ListNodeBase* pNode) EA_NOEXCEPT; + ListIterator(const iterator& x) EA_NOEXCEPT; + + this_type next() const EA_NOEXCEPT; + this_type prev() const EA_NOEXCEPT; + + reference operator*() const EA_NOEXCEPT; + pointer operator->() const EA_NOEXCEPT; + + this_type& operator++() EA_NOEXCEPT; + this_type operator++(int) EA_NOEXCEPT; + + this_type& operator--() EA_NOEXCEPT; + this_type operator--(int) EA_NOEXCEPT; + + }; // ListIterator + + + + + /// ListBase + /// + /// See VectorBase (class vector) for an explanation of why we + /// create this separate base class. + /// + template + class ListBase + { + public: + typedef T value_type; + typedef Allocator allocator_type; + typedef ListNode node_type; + 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; + #if EASTL_LIST_PROXY_ENABLED + typedef ListNodeBaseProxy< ListNode > base_node_type; + #else + typedef ListNodeBase base_node_type; // We use ListNodeBase instead of ListNode because we don't want to create a T. + #endif + + protected: + eastl::compressed_pair mNodeAllocator; + #if EASTL_LIST_SIZE_CACHE + size_type mSize; + #endif + + base_node_type& internalNode() EA_NOEXCEPT { return mNodeAllocator.first(); } + base_node_type const& internalNode() const EA_NOEXCEPT { return mNodeAllocator.first(); } + allocator_type& internalAllocator() EA_NOEXCEPT { return mNodeAllocator.second(); } + const allocator_type& internalAllocator() const EA_NOEXCEPT { return mNodeAllocator.second(); } + + public: + const allocator_type& get_allocator() const EA_NOEXCEPT; + allocator_type& get_allocator() EA_NOEXCEPT; + void set_allocator(const allocator_type& allocator); + + protected: + ListBase(); + ListBase(const allocator_type& a); + ~ListBase(); + + node_type* DoAllocateNode(); + void DoFreeNode(node_type* pNode); + + void DoInit() EA_NOEXCEPT; + void DoClear(); + + }; // ListBase + + + + + /// list + /// + /// -- size() is O(n) -- + /// Note that as of this writing, list::size() is an O(n) operation when EASTL_LIST_SIZE_CACHE is disabled. + /// That is, getting the size of the 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 a tiny amount + /// of processing to functions such as insert and erase, and would only serve to improve the size + /// function, but no others. 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. The EASTL_LIST_SIZE_CACHE option changes this. + /// To consider: Make size caching an optional template parameter. + /// + /// Pool allocation + /// If you want to make a custom memory pool for a list container, your pool + /// needs to contain items of type list::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 WidgetList; // Delare your WidgetList type. + /// MemoryPool myPool(sizeof(WidgetList::node_type), 100); // Make a pool of 100 Widget nodes. + /// WidgetList myList(&myPool); // Create a list that uses the pool. + /// + template + class list : public ListBase + { + typedef ListBase base_type; + typedef list this_type; + + public: + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef ListIterator iterator; + typedef ListIterator const_iterator; + typedef eastl::reverse_iterator reverse_iterator; + typedef eastl::reverse_iterator const_reverse_iterator; + typedef typename base_type::size_type size_type; + typedef typename base_type::difference_type difference_type; + typedef typename base_type::allocator_type allocator_type; + typedef typename base_type::node_type node_type; + typedef typename base_type::base_node_type base_node_type; + + using base_type::mNodeAllocator; + using base_type::DoAllocateNode; + using base_type::DoFreeNode; + using base_type::DoClear; + using base_type::DoInit; + using base_type::get_allocator; + #if EASTL_LIST_SIZE_CACHE + using base_type::mSize; + #endif + using base_type::internalNode; + using base_type::internalAllocator; + + public: + list(); + list(const allocator_type& allocator); + explicit list(size_type n, const allocator_type& allocator = EASTL_LIST_DEFAULT_ALLOCATOR); + list(size_type n, const value_type& value, const allocator_type& allocator = EASTL_LIST_DEFAULT_ALLOCATOR); + list(const this_type& x); + list(const this_type& x, const allocator_type& allocator); + list(this_type&& x); + list(this_type&&, const allocator_type&); + list(std::initializer_list ilist, const allocator_type& allocator = EASTL_LIST_DEFAULT_ALLOCATOR); + + template + list(InputIterator first, InputIterator last); // allocator arg removed because VC7.1 fails on the default arg. To do: Make a second version of this function without a default arg. + + this_type& operator=(const this_type& x); + this_type& operator=(std::initializer_list ilist); + this_type& operator=(this_type&& x); + + // In the case that the two containers' allocators are unequal, swap copies elements instead + // of replacing them in place. In this case swap is an O(n) operation instead of O(1). + void swap(this_type& x); + + void assign(size_type n, const value_type& value); + + template // It turns out that the C++ std::list specifies a two argument + void assign(InputIterator first, InputIterator last); // version of assign that takes (int size, int value). These are not + // iterators, so we need to do a template compiler trick to do the right thing. + void assign(std::initializer_list ilist); + + 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; + + bool empty() const EA_NOEXCEPT; + size_type size() const EA_NOEXCEPT; + + void resize(size_type n, const value_type& value); + void resize(size_type n); + + reference front(); + const_reference front() const; + + reference back(); + const_reference back() const; + + template + void emplace_front(Args&&... args); + + template + void emplace_back(Args&&... args); + + void push_front(const value_type& value); + void push_front(value_type&& x); + reference push_front(); + void* push_front_uninitialized(); + + void push_back(const value_type& value); + void push_back(value_type&& x); + reference push_back(); + void* push_back_uninitialized(); + + void pop_front(); + void pop_back(); + + template + iterator emplace(const_iterator position, Args&&... args); + + iterator insert(const_iterator position); + iterator insert(const_iterator position, const value_type& value); + iterator insert(const_iterator position, value_type&& x); + iterator insert(const_iterator position, std::initializer_list ilist); + iterator insert(const_iterator position, size_type n, const value_type& value); + + template + iterator insert(const_iterator position, InputIterator first, InputIterator last); + + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + + reverse_iterator erase(const_reverse_iterator position); + reverse_iterator erase(const_reverse_iterator first, const_reverse_iterator last); + + void clear() EA_NOEXCEPT; + void reset_lose_memory() EA_NOEXCEPT; // This is a unilateral reset to an initially empty state. No destructors are called, no deallocation occurs. + + void remove(const T& x); + + template + void remove_if(Predicate); + + void reverse() EA_NOEXCEPT; + + // splice inserts elements in the range [first,last) before position and removes the elements from x. + // In the case that the two containers' allocators are unequal, splice copies elements + // instead of splicing them. In this case elements are not removed from x, and iterators + // into the spliced elements from x continue to point to the original values in x. + void splice(const_iterator position, this_type& x); + void splice(const_iterator position, this_type& x, const_iterator i); + void splice(const_iterator position, this_type& x, const_iterator first, const_iterator last); + void splice(const_iterator position, this_type&& x); + void splice(const_iterator position, this_type&& x, const_iterator i); + void splice(const_iterator position, this_type&& x, const_iterator first, const_iterator last); + + public: + // For merge, see notes for splice regarding the handling of unequal allocators. + void merge(this_type& x); + void merge(this_type&& x); + + template + void merge(this_type& x, Compare compare); + + template + void merge(this_type&& x, Compare compare); + + void unique(); + + template + void unique(BinaryPredicate); + + // Sorting functionality + // This is independent of the global sort algorithms, as lists are + // linked nodes and can be sorted more efficiently by moving nodes + // around in ways that global sort algorithms aren't privy to. + void sort(); + + template + void sort(Compare compare); + + public: + bool validate() const; + int validate_iterator(const_iterator i) const; + + protected: + node_type* DoCreateNode(); + + template + node_type* DoCreateNode(Args&&... args); + + template + void DoAssign(Integer n, Integer value, true_type); + + template + void DoAssign(InputIterator first, InputIterator last, false_type); + + void DoAssignValues(size_type n, const value_type& value); + + template + void DoInsert(ListNodeBase* pNode, Integer n, Integer value, true_type); + + template + void DoInsert(ListNodeBase* pNode, InputIterator first, InputIterator last, false_type); + + void DoInsertValues(ListNodeBase* pNode, size_type n, const value_type& value); + + template + void DoInsertValue(ListNodeBase* pNode, Args&&... args); + + void DoErase(ListNodeBase* pNode); + + void DoSwap(this_type& x); + + template + iterator DoSort(iterator i1, iterator end2, size_type n, Compare& compare); + + }; // class list + + + + + + /////////////////////////////////////////////////////////////////////// + // ListNodeBase + /////////////////////////////////////////////////////////////////////// + + // Swaps the nodes a and b in the lists to which they belong. This is similar to + // splicing a into b's list and b into a's list at the same time. + // Works by swapping the members of a and b, and fixes up the lists that a and b + // were part of to point to the new members. + inline void ListNodeBase::swap(ListNodeBase& a, ListNodeBase& b) EA_NOEXCEPT + { + const ListNodeBase temp(a); + a = b; + b = temp; + + if(a.mpNext == &b) + a.mpNext = a.mpPrev = &a; + else + a.mpNext->mpPrev = a.mpPrev->mpNext = &a; + + if(b.mpNext == &a) + b.mpNext = b.mpPrev = &b; + else + b.mpNext->mpPrev = b.mpPrev->mpNext = &b; + } + + + // splices the [first,last) range from its current list into our list before this node. + inline void ListNodeBase::splice(ListNodeBase* first, ListNodeBase* last) EA_NOEXCEPT + { + // We assume that [first, last] are not within our list. + last->mpPrev->mpNext = this; + first->mpPrev->mpNext = last; + this->mpPrev->mpNext = first; + + ListNodeBase* const pTemp = this->mpPrev; + this->mpPrev = last->mpPrev; + last->mpPrev = first->mpPrev; + first->mpPrev = pTemp; + } + + + inline void ListNodeBase::reverse() EA_NOEXCEPT + { + ListNodeBase* pNode = this; + do + { + EA_ANALYSIS_ASSUME(pNode != NULL); + ListNodeBase* const pTemp = pNode->mpNext; + pNode->mpNext = pNode->mpPrev; + pNode->mpPrev = pTemp; + pNode = pNode->mpPrev; + } + while(pNode != this); + } + + + inline void ListNodeBase::insert(ListNodeBase* pNext) EA_NOEXCEPT + { + mpNext = pNext; + mpPrev = pNext->mpPrev; + pNext->mpPrev->mpNext = this; + pNext->mpPrev = this; + } + + + // Removes this node from the list that it's in. Assumes that the + // node is within a list and thus that its prev/next pointers are valid. + inline void ListNodeBase::remove() EA_NOEXCEPT + { + mpNext->mpPrev = mpPrev; + mpPrev->mpNext = mpNext; + } + + + // Inserts the standalone range [pFirst, pFinal] before pPosition. Assumes that the + // range is not within a list and thus that it's prev/next pointers are not valid. + // Assumes that this node is within a list and thus that its prev/next pointers are valid. + inline void ListNodeBase::insert_range(ListNodeBase* pFirst, ListNodeBase* pFinal) EA_NOEXCEPT + { + mpPrev->mpNext = pFirst; + pFirst->mpPrev = mpPrev; + mpPrev = pFinal; + pFinal->mpNext = this; + } + + + // Removes the range [pFirst, pFinal] from the list that it's in. Assumes that the + // range is within a list and thus that its prev/next pointers are valid. + inline void ListNodeBase::remove_range(ListNodeBase* pFirst, ListNodeBase* pFinal) EA_NOEXCEPT + { + pFinal->mpNext->mpPrev = pFirst->mpPrev; + pFirst->mpPrev->mpNext = pFinal->mpNext; + } + + + /////////////////////////////////////////////////////////////////////// + // ListIterator + /////////////////////////////////////////////////////////////////////// + + template + inline ListIterator::ListIterator() EA_NOEXCEPT + : mpNode() // To consider: Do we really need to intialize mpNode? + { + // Empty + } + + + template + inline ListIterator::ListIterator(const ListNodeBase* pNode) EA_NOEXCEPT + : mpNode(static_cast((ListNode*)const_cast(pNode))) // All this casting is in the name of making runtime debugging much easier on the user. + { + // Empty + } + + + template + inline ListIterator::ListIterator(const iterator& x) EA_NOEXCEPT + : mpNode(const_cast(x.mpNode)) + { + // Empty + } + + + template + inline typename ListIterator::this_type + ListIterator::next() const EA_NOEXCEPT + { + return ListIterator(mpNode->mpNext); + } + + + template + inline typename ListIterator::this_type + ListIterator::prev() const EA_NOEXCEPT + { + return ListIterator(mpNode->mpPrev); + } + + + template + inline typename ListIterator::reference + ListIterator::operator*() const EA_NOEXCEPT + { + return mpNode->mValue; + } + + + template + inline typename ListIterator::pointer + ListIterator::operator->() const EA_NOEXCEPT + { + return &mpNode->mValue; + } + + + template + inline typename ListIterator::this_type& + ListIterator::operator++() EA_NOEXCEPT + { + mpNode = static_cast(mpNode->mpNext); + return *this; + } + + + template + inline typename ListIterator::this_type + ListIterator::operator++(int) EA_NOEXCEPT + { + this_type temp(*this); + mpNode = static_cast(mpNode->mpNext); + return temp; + } + + + template + inline typename ListIterator::this_type& + ListIterator::operator--() EA_NOEXCEPT + { + mpNode = static_cast(mpNode->mpPrev); + return *this; + } + + + template + inline typename ListIterator::this_type + ListIterator::operator--(int) EA_NOEXCEPT + { + this_type temp(*this); + mpNode = static_cast(mpNode->mpPrev); + 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 + inline bool operator==(const ListIterator& a, + const ListIterator& b) EA_NOEXCEPT + { + return a.mpNode == b.mpNode; + } + + + template + inline bool operator!=(const ListIterator& a, + const ListIterator& b) EA_NOEXCEPT + { + 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 + inline bool operator!=(const ListIterator& a, + const ListIterator& b) EA_NOEXCEPT + { + return a.mpNode != b.mpNode; + } + + + + /////////////////////////////////////////////////////////////////////// + // ListBase + /////////////////////////////////////////////////////////////////////// + + template + inline ListBase::ListBase() + : mNodeAllocator(base_node_type(), allocator_type(EASTL_LIST_DEFAULT_NAME)) + #if EASTL_LIST_SIZE_CACHE + , mSize(0) + #endif + { + DoInit(); + } + + template + inline ListBase::ListBase(const allocator_type& allocator) + : mNodeAllocator(base_node_type(), allocator) + #if EASTL_LIST_SIZE_CACHE + , mSize(0) + #endif + { + DoInit(); + } + + + template + inline ListBase::~ListBase() + { + DoClear(); + } + + + template + const typename ListBase::allocator_type& + ListBase::get_allocator() const EA_NOEXCEPT + { + return internalAllocator(); + } + + + template + typename ListBase::allocator_type& + ListBase::get_allocator() EA_NOEXCEPT + { + return internalAllocator(); + } + + + template + inline void ListBase::set_allocator(const allocator_type& allocator) + { + EASTL_ASSERT((internalAllocator() == allocator) || (static_cast(internalNode().mpNext) == &internalNode())); // We can only assign a different allocator if we are empty of elements. + internalAllocator() = allocator; + } + + + template + inline typename ListBase::node_type* + ListBase::DoAllocateNode() + { + node_type* pNode = (node_type*)allocate_memory(internalAllocator(), sizeof(node_type), EASTL_ALIGN_OF(node_type), 0); + EASTL_ASSERT(pNode != nullptr); + return pNode; + } + + + template + inline void ListBase::DoFreeNode(node_type* p) + { + EASTLFree(internalAllocator(), p, sizeof(node_type)); + } + + + template + inline void ListBase::DoInit() EA_NOEXCEPT + { + internalNode().mpNext = (ListNode*)&internalNode(); + internalNode().mpPrev = (ListNode*)&internalNode(); + } + + + template + inline void ListBase::DoClear() + { + node_type* p = static_cast(internalNode().mpNext); + + while(p != &internalNode()) + { + node_type* const pTemp = p; + p = static_cast(p->mpNext); + pTemp->~node_type(); + EASTLFree(internalAllocator(), pTemp, sizeof(node_type)); + } + } + + + + /////////////////////////////////////////////////////////////////////// + // list + /////////////////////////////////////////////////////////////////////// + + template + inline list::list() + : base_type() + { + // Empty + } + + + template + inline list::list(const allocator_type& allocator) + : base_type(allocator) + { + // Empty + } + + + template + inline list::list(size_type n, const allocator_type& allocator) + : base_type(allocator) + { + DoInsertValues((ListNodeBase*)&internalNode(), n, value_type()); + } + + + template + inline list::list(size_type n, const value_type& value, const allocator_type& allocator) + : base_type(allocator) + { + DoInsertValues((ListNodeBase*)&internalNode(), n, value); + } + + + template + inline list::list(const this_type& x) + : base_type(x.internalAllocator()) + { + DoInsert((ListNodeBase*)&internalNode(), const_iterator((ListNodeBase*)x.internalNode().mpNext), const_iterator((ListNodeBase*)&x.internalNode()), false_type()); + } + + + template + inline list::list(const this_type& x, const allocator_type& allocator) + : base_type(allocator) + { + DoInsert((ListNodeBase*)&internalNode(), const_iterator((ListNodeBase*)x.internalNode().mpNext), const_iterator((ListNodeBase*)&x.internalNode()), false_type()); + } + + + template + inline list::list(this_type&& x) + : base_type(eastl::move(x.internalAllocator())) + { + swap(x); + } + + + template + inline list::list(this_type&& x, const allocator_type& allocator) + : base_type(allocator) + { + swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. + } + + + template + inline list::list(std::initializer_list ilist, const allocator_type& allocator) + : base_type(allocator) + { + DoInsert((ListNodeBase*)&internalNode(), ilist.begin(), ilist.end(), false_type()); + } + + + template + template + list::list(InputIterator first, InputIterator last) + : base_type(EASTL_LIST_DEFAULT_ALLOCATOR) + { + //insert(const_iterator((ListNodeBase*)&internalNode()), first, last); + DoInsert((ListNodeBase*)&internalNode(), first, last, is_integral()); + } + + + template + typename list::iterator + inline list::begin() EA_NOEXCEPT + { + return iterator((ListNodeBase*)internalNode().mpNext); + } + + + template + inline typename list::const_iterator + list::begin() const EA_NOEXCEPT + { + return const_iterator((ListNodeBase*)internalNode().mpNext); + } + + + template + inline typename list::const_iterator + list::cbegin() const EA_NOEXCEPT + { + return const_iterator((ListNodeBase*)internalNode().mpNext); + } + + + template + inline typename list::iterator + list::end() EA_NOEXCEPT + { + return iterator((ListNodeBase*)&internalNode()); + } + + + template + inline typename list::const_iterator + list::end() const EA_NOEXCEPT + { + return const_iterator((ListNodeBase*)&internalNode()); + } + + + template + inline typename list::const_iterator + list::cend() const EA_NOEXCEPT + { + return const_iterator((ListNodeBase*)&internalNode()); + } + + + template + inline typename list::reverse_iterator + list::rbegin() EA_NOEXCEPT + { + return reverse_iterator((ListNodeBase*)&internalNode()); + } + + + template + inline typename list::const_reverse_iterator + list::rbegin() const EA_NOEXCEPT + { + return const_reverse_iterator((ListNodeBase*)&internalNode()); + } + + + template + inline typename list::const_reverse_iterator + list::crbegin() const EA_NOEXCEPT + { + return const_reverse_iterator((ListNodeBase*)&internalNode()); + } + + + template + inline typename list::reverse_iterator + list::rend() EA_NOEXCEPT + { + return reverse_iterator((ListNodeBase*)internalNode().mpNext); + } + + + template + inline typename list::const_reverse_iterator + list::rend() const EA_NOEXCEPT + { + return const_reverse_iterator((ListNodeBase*)internalNode().mpNext); + } + + + template + inline typename list::const_reverse_iterator + list::crend() const EA_NOEXCEPT + { + return const_reverse_iterator((ListNodeBase*)internalNode().mpNext); + } + + + template + inline typename list::reference + list::front() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) + EASTL_FAIL_MSG("list::front -- empty container"); + #else + // We allow the user to reference an empty container. + #endif + + return static_cast(internalNode().mpNext)->mValue; + } + + + template + inline typename list::const_reference + list::front() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) + EASTL_FAIL_MSG("list::front -- empty container"); + #else + // We allow the user to reference an empty container. + #endif + + return static_cast(internalNode().mpNext)->mValue; + } + + + template + inline typename list::reference + list::back() + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) + EASTL_FAIL_MSG("list::back -- empty container"); + #else + // We allow the user to reference an empty container. + #endif + + return static_cast(internalNode().mpPrev)->mValue; + } + + + template + inline typename list::const_reference + list::back() const + { + #if EASTL_ASSERT_ENABLED && EASTL_EMPTY_REFERENCE_ASSERT_ENABLED + if (EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) + EASTL_FAIL_MSG("list::back -- empty container"); + #else + // We allow the user to reference an empty container. + #endif + + return static_cast(internalNode().mpPrev)->mValue; + } + + + template + inline bool list::empty() const EA_NOEXCEPT + { + #if EASTL_LIST_SIZE_CACHE + return (mSize == 0); + #else + return static_cast(internalNode().mpNext) == &internalNode(); + #endif + } + + + template + inline typename list::size_type + list::size() const EA_NOEXCEPT + { + #if EASTL_LIST_SIZE_CACHE + return mSize; + #else + #if EASTL_DEBUG + const ListNodeBase* p = (ListNodeBase*)internalNode().mpNext; + size_type n = 0; + while(p != (ListNodeBase*)&internalNode()) + { + ++n; + p = (ListNodeBase*)p->mpNext; + } + return n; + #else + // The following optimizes to slightly better code than the code above. + return (size_type)eastl::distance(const_iterator((ListNodeBase*)internalNode().mpNext), const_iterator((ListNodeBase*)&internalNode())); + #endif + #endif + } + + + template + typename list::this_type& + list::operator=(const this_type& x) + { + if(this != &x) // If not assigning to self... + { + // If (EASTL_ALLOCATOR_COPY_ENABLED == 1) and the current contents are allocated by an + // allocator that's unequal to x's allocator, we need to reallocate our elements with + // our current allocator and reallocate it with x's allocator. If the allocators are + // equal then we can use a more optimal algorithm that doesn't reallocate our elements + // but instead can copy them in place. + + #if EASTL_ALLOCATOR_COPY_ENABLED + bool bSlowerPathwayRequired = (internalAllocator() != x.internalAllocator()); + #else + bool bSlowerPathwayRequired = false; + #endif + + if(bSlowerPathwayRequired) + { + clear(); + + #if EASTL_ALLOCATOR_COPY_ENABLED + internalAllocator() = x.internalAllocator(); + #endif + } + + DoAssign(x.begin(), x.end(), eastl::false_type()); + } + + return *this; + } + + + template + typename list::this_type& + list::operator=(this_type&& x) + { + if(this != &x) + { + clear(); // To consider: Are we really required to clear here? x is going away soon and will clear itself in its dtor. + swap(x); // member swap handles the case that x has a different allocator than our allocator by doing a copy. + } + return *this; + } + + + template + typename list::this_type& + list::operator=(std::initializer_list ilist) + { + DoAssign(ilist.begin(), ilist.end(), false_type()); + return *this; + } + + + template + inline void list::assign(size_type n, const value_type& value) + { + DoAssignValues(n, value); + } + + + // It turns out that the C++ std::list specifies a two argument + // version of assign that takes (int size, int value). These are not + // iterators, so we need to do a template compiler trick to do the right thing. + template + template + inline void list::assign(InputIterator first, InputIterator last) + { + DoAssign(first, last, is_integral()); + } + + + template + inline void list::assign(std::initializer_list ilist) + { + DoAssign(ilist.begin(), ilist.end(), false_type()); + } + + + template + inline void list::clear() EA_NOEXCEPT + { + DoClear(); + DoInit(); + #if EASTL_LIST_SIZE_CACHE + mSize = 0; + #endif + } + + + template + inline void list::reset_lose_memory() EA_NOEXCEPT + { + // The reset_lose_memory 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. + DoInit(); + #if EASTL_LIST_SIZE_CACHE + mSize = 0; + #endif + } + + + template + void list::resize(size_type n, const value_type& value) + { + iterator current((ListNodeBase*)internalNode().mpNext); + size_type i = 0; + + while((current.mpNode != &internalNode()) && (i < n)) + { + ++current; + ++i; + } + if(i == n) + erase(current, (ListNodeBase*)&internalNode()); + else + insert((ListNodeBase*)&internalNode(), n - i, value); + } + + + template + inline void list::resize(size_type n) + { + resize(n, value_type()); + } + + + template + template + void list::emplace_front(Args&&... args) + { + DoInsertValue((ListNodeBase*)internalNode().mpNext, eastl::forward(args)...); + } + + template + template + void list::emplace_back(Args&&... args) + { + DoInsertValue((ListNodeBase*)&internalNode(), eastl::forward(args)...); + } + + + template + inline void list::push_front(const value_type& value) + { + DoInsertValue((ListNodeBase*)internalNode().mpNext, value); + } + + + template + inline void list::push_front(value_type&& value) + { + emplace(begin(), eastl::move(value)); + } + + + template + inline typename list::reference + list::push_front() + { + node_type* const pNode = DoCreateNode(); + ((ListNodeBase*)pNode)->insert((ListNodeBase*)internalNode().mpNext); + #if EASTL_LIST_SIZE_CACHE + ++mSize; + #endif + return static_cast(internalNode().mpNext)->mValue; // Same as return front(); + } + + + template + inline void* list::push_front_uninitialized() + { + node_type* const pNode = DoAllocateNode(); + ((ListNodeBase*)pNode)->insert((ListNodeBase*)internalNode().mpNext); + #if EASTL_LIST_SIZE_CACHE + ++mSize; + #endif + return &pNode->mValue; + } + + + template + inline void list::pop_front() + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) + EASTL_FAIL_MSG("list::pop_front -- empty container"); + #endif + + DoErase((ListNodeBase*)internalNode().mpNext); + } + + + template + inline void list::push_back(const value_type& value) + { + DoInsertValue((ListNodeBase*)&internalNode(), value); + } + + + template + inline void list::push_back(value_type&& value) + { + emplace(end(), eastl::move(value)); + } + + + template + inline typename list::reference + list::push_back() + { + node_type* const pNode = DoCreateNode(); + ((ListNodeBase*)pNode)->insert((ListNodeBase*)&internalNode()); + #if EASTL_LIST_SIZE_CACHE + ++mSize; + #endif + return static_cast(internalNode().mpPrev)->mValue; // Same as return back(); + } + + + template + inline void* list::push_back_uninitialized() + { + node_type* const pNode = DoAllocateNode(); + ((ListNodeBase*)pNode)->insert((ListNodeBase*)&internalNode()); + #if EASTL_LIST_SIZE_CACHE + ++mSize; + #endif + return &pNode->mValue; + } + + + template + inline void list::pop_back() + { + #if EASTL_ASSERT_ENABLED + if(EASTL_UNLIKELY(static_cast(internalNode().mpNext) == &internalNode())) + EASTL_FAIL_MSG("list::pop_back -- empty container"); + #endif + + DoErase((ListNodeBase*)internalNode().mpPrev); + } + + + template + template + inline typename list::iterator + list::emplace(const_iterator position, Args&&... args) + { + DoInsertValue(position.mpNode, eastl::forward(args)...); + return iterator(position.mpNode->mpPrev); + } + + + template + inline typename list::iterator + list::insert(const_iterator position) + { + node_type* const pNode = DoCreateNode(value_type()); + ((ListNodeBase*)pNode)->insert((ListNodeBase*)position.mpNode); + #if EASTL_LIST_SIZE_CACHE + ++mSize; + #endif + return (ListNodeBase*)pNode; + } + + + template + inline typename list::iterator + list::insert(const_iterator position, const value_type& value) + { + node_type* const pNode = DoCreateNode(value); + ((ListNodeBase*)pNode)->insert((ListNodeBase*)position.mpNode); + #if EASTL_LIST_SIZE_CACHE + ++mSize; + #endif + return (ListNodeBase*)pNode; + } + + + template + inline typename list::iterator + list::insert(const_iterator position, value_type&& value) + { + return emplace(position, eastl::move(value)); + } + + template + inline typename list::iterator + list::insert(const_iterator position, size_type n, const value_type& value) + { + iterator itPrev(position.mpNode); + --itPrev; + DoInsertValues((ListNodeBase*)position.mpNode, n, value); + return ++itPrev; // Inserts in front of position, returns iterator to new elements. + } + + + template + template + inline typename list::iterator + list::insert(const_iterator position, InputIterator first, InputIterator last) + { + iterator itPrev(position.mpNode); + --itPrev; + DoInsert((ListNodeBase*)position.mpNode, first, last, is_integral()); + return ++itPrev; // Inserts in front of position, returns iterator to new elements. + } + + + template + inline typename list::iterator + list::insert(const_iterator position, std::initializer_list ilist) + { + iterator itPrev(position.mpNode); + --itPrev; + DoInsert((ListNodeBase*)position.mpNode, ilist.begin(), ilist.end(), false_type()); + return ++itPrev; // Inserts in front of position, returns iterator to new elements. + } + + + template + inline typename list::iterator + list::erase(const_iterator position) + { + ++position; + DoErase((ListNodeBase*)position.mpNode->mpPrev); + return iterator(position.mpNode); + } + + + template + typename list::iterator + list::erase(const_iterator first, const_iterator last) + { + while(first != last) + first = erase(first); + return iterator(last.mpNode); + } + + + template + inline typename list::reverse_iterator + list::erase(const_reverse_iterator position) + { + return reverse_iterator(erase((++position).base())); + } + + + template + typename list::reverse_iterator + list::erase(const_reverse_iterator first, const_reverse_iterator last) + { + // Version which erases in order from first to last. + // difference_type i(first.base() - last.base()); + // while(i--) + // first = erase(first); + // return first; + + // Version which erases in order from last to first, but is slightly more efficient: + const_iterator itLastBase((++last).base()); + const_iterator itFirstBase((++first).base()); + + return reverse_iterator(erase(itLastBase, itFirstBase)); + } + + + template + void list::remove(const value_type& value) + { + iterator current((ListNodeBase*)internalNode().mpNext); + + while(current.mpNode != &internalNode()) + { + if(EASTL_LIKELY(!(*current == value))) + ++current; // We have duplicate '++current' statements here and below, but the logic here forces this. + else + { + ++current; + DoErase((ListNodeBase*)current.mpNode->mpPrev); + } + } + } + + + template + template + inline void list::remove_if(Predicate predicate) + { + for(iterator first((ListNodeBase*)internalNode().mpNext), last((ListNodeBase*)&internalNode()); first != last; ) + { + iterator temp(first); + ++temp; + if(predicate(first.mpNode->mValue)) + DoErase((ListNodeBase*)first.mpNode); + first = temp; + } + } + + + template + inline void list::reverse() EA_NOEXCEPT + { + ((ListNodeBase&)internalNode()).reverse(); + } + + + template + inline void list::splice(const_iterator position, this_type& x) + { + // Splicing operations cannot succeed if the two containers use unequal allocators. + // This issue is not addressed in the C++ 1998 standard but is discussed in the + // LWG defect reports, such as #431. There is no simple solution to this problem. + // One option is to throw an exception. Another option which probably captures the + // user intent most of the time is to copy the range from the source to the dest and + // remove it from the source. + + if(internalAllocator() == x.internalAllocator()) + { + #if EASTL_LIST_SIZE_CACHE + if(x.mSize) + { + ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)x.internalNode().mpNext, (ListNodeBase*)&x.internalNode()); + mSize += x.mSize; + x.mSize = 0; + } + #else + if(!x.empty()) + ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)x.internalNode().mpNext, (ListNodeBase*)&x.internalNode()); + #endif + } + else + { + insert(position, x.begin(), x.end()); + x.clear(); + } + } + + template + inline void list::splice(const_iterator position, this_type&& x) + { + return splice(position, x); // This will call splice(const_iterator, const this_type&); + } + + + template + inline void list::splice(const_iterator position, list& x, const_iterator i) + { + if(internalAllocator() == x.internalAllocator()) + { + iterator i2(i.mpNode); + ++i2; + if((position != i) && (position != i2)) + { + ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)i.mpNode, (ListNodeBase*)i2.mpNode); + + #if EASTL_LIST_SIZE_CACHE + ++mSize; + --x.mSize; + #endif + } + } + else + { + insert(position, *i); + x.erase(i); + } + } + + + template + inline void list::splice(const_iterator position, list&& x, const_iterator i) + { + return splice(position, x, i); // This will call splice(const_iterator, const this_type&, const_iterator); + } + + + template + inline void list::splice(const_iterator position, this_type& x, const_iterator first, const_iterator last) + { + if(internalAllocator() == x.internalAllocator()) + { + #if EASTL_LIST_SIZE_CACHE + const size_type n = (size_type)eastl::distance(first, last); + + if(n) + { + ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)first.mpNode, (ListNodeBase*)last.mpNode); + mSize += n; + x.mSize -= n; + } + #else + if(first != last) + ((ListNodeBase*)position.mpNode)->splice((ListNodeBase*)first.mpNode, (ListNodeBase*)last.mpNode); + #endif + } + else + { + insert(position, first, last); + x.erase(first, last); + } + } + + + template + inline void list::splice(const_iterator position, list&& x, const_iterator first, const_iterator last) + { + return splice(position, x, first, last); // This will call splice(const_iterator, const this_type&, const_iterator, const_iterator); + } + + + template + inline void list::swap(this_type& x) + { + if(internalAllocator() == x.internalAllocator()) // If allocators are equivalent... + DoSwap(x); + else // else swap the contents. + { + const this_type temp(*this); // Can't call eastl::swap because that would + *this = x; // itself call this member swap function. + x = temp; + } + } + + + template + void list::merge(this_type& x) + { + if(this != &x) + { + iterator first(begin()); + iterator firstX(x.begin()); + const iterator last(end()); + const iterator lastX(x.end()); + + while((first != last) && (firstX != lastX)) + { + if(*firstX < *first) + { + iterator next(firstX); + + splice(first, x, firstX, ++next); + firstX = next; + } + else + ++first; + } + + if(firstX != lastX) + splice(last, x, firstX, lastX); + } + } + + + template + void list::merge(this_type&& x) + { + return merge(x); // This will call merge(this_type&) + } + + + template + template + void list::merge(this_type& x, Compare compare) + { + if(this != &x) + { + iterator first(begin()); + iterator firstX(x.begin()); + const iterator last(end()); + const iterator lastX(x.end()); + + while((first != last) && (firstX != lastX)) + { + if(compare(*firstX, *first)) + { + iterator next(firstX); + + splice(first, x, firstX, ++next); + firstX = next; + } + else + ++first; + } + + if(firstX != lastX) + splice(last, x, firstX, lastX); + } + } + + + template + template + void list::merge(this_type&& x, Compare compare) + { + return merge(x, compare); // This will call merge(this_type&, Compare) + } + + + template + void list::unique() + { + iterator first(begin()); + const iterator last(end()); + + if(first != last) + { + iterator next(first); + + while(++next != last) + { + if(*first == *next) + DoErase((ListNodeBase*)next.mpNode); + else + first = next; + next = first; + } + } + } + + + template + template + void list::unique(BinaryPredicate predicate) + { + iterator first(begin()); + const iterator last(end()); + + if(first != last) + { + iterator next(first); + + while(++next != last) + { + if(predicate(*first, *next)) + DoErase((ListNodeBase*)next.mpNode); + else + first = next; + next = first; + } + } + } + + + template + void list::sort() + { + eastl::less compare; + DoSort(begin(), end(), size(), compare); + } + + + template + template + void list::sort(Compare compare) + { + DoSort(begin(), end(), size(), compare); + } + + + template + template + typename list::iterator + list::DoSort(iterator i1, iterator end2, size_type n, Compare& compare) + { + // A previous version of this function did this by creating temporary lists, + // but that was incompatible with fixed_list because the sizes could be too big. + // We sort subsegments by recursive descent. Then merge as we ascend. + // Return an iterator to the beginning of the sorted subsegment. + // Start with a special case for small node counts. + switch (n) + { + case 0: + case 1: + return i1; + + case 2: + // Potentialy swap these two nodes and return the resulting first of them. + if(compare(*--end2, *i1)) + { + end2.mpNode->remove(); + end2.mpNode->insert(i1.mpNode); + return end2; + } + return i1; + + case 3: + { + // We do a list insertion sort. Measurements showed this improved performance 3-12%. + iterator lowest = i1; + + for(iterator current = i1.next(); current != end2; ++current) + { + if(compare(*current, *lowest)) + lowest = current; + } + + if(lowest == i1) + ++i1; + else + { + lowest.mpNode->remove(); + lowest.mpNode->insert(i1.mpNode); + } + + if(compare(*--end2, *i1)) // At this point, i1 refers to the second element in this three element segment. + { + end2.mpNode->remove(); + end2.mpNode->insert(i1.mpNode); + } + + return lowest; + } + } + + // Divide the range into two parts are recursively sort each part. Upon return we will have + // two halves that are each sorted but we'll need to merge the two together before returning. + iterator result; + size_type nMid = (n / 2); + iterator end1 = eastl::next(i1, (difference_type)nMid); + i1 = DoSort(i1, end1, nMid, compare); // Return the new beginning of the first sorted sub-range. + iterator i2 = DoSort(end1, end2, n - nMid, compare); // Return the new beginning of the second sorted sub-range. + + // If the start of the second list is before the start of the first list, insert the first list + // into the second at an appropriate starting place. + if(compare(*i2, *i1)) + { + // Find the position to insert the first list into the second list. + iterator ix = i2.next(); + while((ix != end2) && compare(*ix, *i1)) + ++ix; + + // Cut out the initial segment of the second list and move it to be in front of the first list. + ListNodeBase* i2Cut = i2.mpNode; + ListNodeBase* i2CutLast = ix.mpNode->mpPrev; + result = i2; + end1 = i2 = ix; + ListNodeBase::remove_range(i2Cut, i2CutLast); + i1.mpNode->insert_range(i2Cut, i2CutLast); + } + else + { + result = i1; + end1 = i2; + } + + // Merge the two segments. We do this by merging the second sub-segment into the first, by walking forward in each of the two sub-segments. + for(++i1; (i1 != end1) && (i2 != end2); ++i1) // while still working on either segment... + { + if(compare(*i2, *i1)) // If i2 is less than i1 and it needs to be merged in front of i1... + { + // Find the position to insert the i2 list into the i1 list. + iterator ix = i2.next(); + while((ix != end2) && compare(*ix, *i1)) + ++ix; + + // Cut this section of the i2 sub-segment out and merge into the appropriate place in the i1 list. + ListNodeBase* i2Cut = i2.mpNode; + ListNodeBase* i2CutLast = ix.mpNode->mpPrev; + if(end1 == i2) + end1 = ix; + i2 = ix; + ListNodeBase::remove_range(i2Cut, i2CutLast); + i1.mpNode->insert_range(i2Cut, i2CutLast); + } + } + + return result; + } + + + template + template + inline typename list::node_type* + list::DoCreateNode(Args&&... args) + { + node_type* const pNode = DoAllocateNode(); // pNode is of type node_type, but it's uninitialized memory. + + #if EASTL_EXCEPTIONS_ENABLED + try + { + ::new((void*)&pNode->mValue) value_type(eastl::forward(args)...); + } + catch(...) + { + DoFreeNode(pNode); + throw; + } + #else + ::new((void*)&pNode->mValue) value_type(eastl::forward(args)...); + #endif + + return pNode; + } + + + template + inline typename list::node_type* + list::DoCreateNode() + { + node_type* const pNode = DoAllocateNode(); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + ::new((void*)&pNode->mValue) value_type(); + } + catch(...) + { + DoFreeNode(pNode); + throw; + } + #else + ::new((void*)&pNode->mValue) value_type; + #endif + + return pNode; + } + + + template + template + inline void list::DoAssign(Integer n, Integer value, true_type) + { + DoAssignValues(static_cast(n), static_cast(value)); + } + + + template + template + void list::DoAssign(InputIterator first, InputIterator last, false_type) + { + node_type* pNode = static_cast(internalNode().mpNext); + + for(; (pNode != &internalNode()) && (first != last); ++first) + { + pNode->mValue = *first; + pNode = static_cast(pNode->mpNext); + } + + if(first == last) + erase(const_iterator((ListNodeBase*)pNode), (ListNodeBase*)&internalNode()); + else + DoInsert((ListNodeBase*)&internalNode(), first, last, false_type()); + } + + + template + void list::DoAssignValues(size_type n, const value_type& value) + { + node_type* pNode = static_cast(internalNode().mpNext); + + for(; (pNode != &internalNode()) && (n > 0); --n) + { + pNode->mValue = value; + pNode = static_cast(pNode->mpNext); + } + + if(n) + DoInsertValues((ListNodeBase*)&internalNode(), n, value); + else + erase(const_iterator((ListNodeBase*)pNode), (ListNodeBase*)&internalNode()); + } + + + template + template + inline void list::DoInsert(ListNodeBase* pNode, Integer n, Integer value, true_type) + { + DoInsertValues(pNode, static_cast(n), static_cast(value)); + } + + + template + template + inline void list::DoInsert(ListNodeBase* pNode, InputIterator first, InputIterator last, false_type) + { + for(; first != last; ++first) + DoInsertValue(pNode, *first); + } + + + template + inline void list::DoInsertValues(ListNodeBase* pNode, size_type n, const value_type& value) + { + for(; n > 0; --n) + DoInsertValue(pNode, value); + } + + + template + template + inline void list::DoInsertValue(ListNodeBase* pNode, Args&&... args) + { + node_type* const pNodeNew = DoCreateNode(eastl::forward(args)...); + ((ListNodeBase*)pNodeNew)->insert(pNode); + #if EASTL_LIST_SIZE_CACHE + ++mSize; + #endif + } + + + template + inline void list::DoErase(ListNodeBase* pNode) + { + pNode->remove(); + ((node_type*)pNode)->~node_type(); + DoFreeNode(((node_type*)pNode)); + #if EASTL_LIST_SIZE_CACHE + --mSize; + #endif + + /* Test version that uses union intermediates + union + { + ListNodeBase* mpBase; + node_type* mpNode; + } node = { pNode }; + + node.mpNode->~node_type(); + node.mpBase->remove(); + DoFreeNode(node.mpNode); + #if EASTL_LIST_SIZE_CACHE + --mSize; + #endif + */ + } + + + template + inline void list::DoSwap(this_type& x) + { + ListNodeBase::swap((ListNodeBase&)internalNode(), (ListNodeBase&)x.internalNode()); // We need to implement a special swap because we can't do a shallow swap. + eastl::swap(internalAllocator(), x.internalAllocator()); // We do this even if EASTL_ALLOCATOR_COPY_ENABLED is 0. + #if EASTL_LIST_SIZE_CACHE + eastl::swap(mSize, x.mSize); + #endif + } + + + template + inline bool list::validate() const + { + #if EASTL_LIST_SIZE_CACHE + size_type n = 0; + + for(const_iterator i(begin()), iEnd(end()); i != iEnd; ++i) + ++n; + + if(n != mSize) + return false; + #endif + + // To do: More validation. + return true; + } + + + template + inline int list::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 + bool operator==(const list& a, const list& b) + { + typename list::const_iterator ia = a.begin(); + typename list::const_iterator ib = b.begin(); + typename list::const_iterator enda = a.end(); + + #if EASTL_LIST_SIZE_CACHE + if(a.size() == b.size()) + { + while((ia != enda) && (*ia == *ib)) + { + ++ia; + ++ib; + } + return (ia == enda); + } + return false; + #else + typename list::const_iterator endb = b.end(); + + while((ia != enda) && (ib != endb) && (*ia == *ib)) + { + ++ia; + ++ib; + } + return (ia == enda) && (ib == endb); + #endif + } + + template + bool operator<(const list& a, const list& b) + { + return eastl::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } + + template + bool operator!=(const list& a, const list& b) + { + return !(a == b); + } + + template + bool operator>(const list& a, const list& b) + { + return b < a; + } + + template + bool operator<=(const list& a, const list& b) + { + return !(b < a); + } + + template + bool operator>=(const list& a, const list& b) + { + return !(a < b); + } + + template + void swap(list& a, list& b) + { + a.swap(b); + } + + + /////////////////////////////////////////////////////////////////////// + // erase / erase_if + // + // https://en.cppreference.com/w/cpp/container/list/erase2 + /////////////////////////////////////////////////////////////////////// + template + void erase(list& c, const U& value) + { + // Erases all elements that compare equal to value from the container. + c.remove_if([&](auto& elem) { return elem == value; }); + } + + template + void erase_if(list& c, Predicate predicate) + { + // Erases all elements that satisfy the predicate pred from the container. + c.remove_if(predicate); + } + + +} // namespace eastl + + +EA_RESTORE_SN_WARNING() + +EA_RESTORE_VC_WARNING(); + + +#endif // Header include guard diff --git a/lib/EASTL/include/EASTL/map.h b/lib/EASTL/include/EASTL/map.h new file mode 100644 index 000000000..0e6c1d0ff --- /dev/null +++ b/lib/EASTL/include/EASTL/map.h @@ -0,0 +1,684 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_MAP_H +#define EASTL_MAP_H + + +#include +#include +#include +#include + +#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_MAP_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_MAP_DEFAULT_NAME + #define EASTL_MAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " map" // Unless the user overrides something, this is "EASTL map". + #endif + + + /// EASTL_MULTIMAP_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_MULTIMAP_DEFAULT_NAME + #define EASTL_MULTIMAP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " multimap" // Unless the user overrides something, this is "EASTL multimap". + #endif + + + /// EASTL_MAP_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_MAP_DEFAULT_ALLOCATOR + #define EASTL_MAP_DEFAULT_ALLOCATOR allocator_type(EASTL_MAP_DEFAULT_NAME) + #endif + + /// EASTL_MULTIMAP_DEFAULT_ALLOCATOR + /// + #ifndef EASTL_MULTIMAP_DEFAULT_ALLOCATOR + #define EASTL_MULTIMAP_DEFAULT_ALLOCATOR allocator_type(EASTL_MULTIMAP_DEFAULT_NAME) + #endif + + + + /// map + /// + /// Implements a canonical map. + /// + /// The large majority of the implementation of this class is found in the rbtree + /// base class. We control the behaviour of rbtree via template parameters. + /// + /// Pool allocation + /// If you want to make a custom memory pool for a map container, your pool + /// needs to contain items of type 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 map, 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 Allocator = EASTLAllocatorType> + class map + : public rbtree, Compare, Allocator, eastl::use_first >, true, true> + { + public: + typedef rbtree, Compare, Allocator, + eastl::use_first >, true, true> base_type; + typedef map 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; + typedef typename base_type::node_type node_type; + typedef typename base_type::iterator iterator; + typedef typename base_type::const_iterator const_iterator; + typedef typename base_type::allocator_type allocator_type; + typedef typename base_type::insert_return_type insert_return_type; + typedef typename base_type::extract_key extract_key; + // Other types are inherited from the base class. + + using base_type::begin; + using base_type::end; + using base_type::find; + using base_type::lower_bound; + using base_type::upper_bound; + using base_type::insert; + using base_type::erase; + + protected: + using base_type::compare; + using base_type::get_compare; + + public: + class value_compare + { + protected: + friend class map; + Compare compare; + value_compare(Compare c) : compare(c) {} + + public: + typedef bool result_type; + typedef value_type first_argument_type; + typedef value_type second_argument_type; + + bool operator()(const value_type& x, const value_type& y) const + { return compare(x.first, y.first); } + }; + + public: + map(const allocator_type& allocator = EASTL_MAP_DEFAULT_ALLOCATOR); + map(const Compare& compare, const allocator_type& allocator = EASTL_MAP_DEFAULT_ALLOCATOR); + map(const this_type& x); + map(this_type&& x); + map(this_type&& x, const allocator_type& allocator); + map(std::initializer_list ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_MAP_DEFAULT_ALLOCATOR); + + template + map(Iterator itBegin, Iterator itEnd); // allocator arg removed because VC7.1 fails on the default arg. To consider: Make a second version of this function without a default arg. + + this_type& operator=(const this_type& x) { return (this_type&)base_type::operator=(x); } + this_type& operator=(std::initializer_list ilist) { return (this_type&)base_type::operator=(ilist); } + this_type& operator=(this_type&& x) { return (this_type&)base_type::operator=(eastl::move(x)); } + + public: + /// 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. Note that C++11 move insertions and variadic emplace + /// support make this extension mostly no longer necessary. + insert_return_type insert(const Key& key); + + value_compare value_comp() const; + + size_type erase(const Key& key); + size_type count(const Key& key) const; + + eastl::pair equal_range(const Key& key); + eastl::pair equal_range(const Key& key) const; + + T& operator[](const Key& key); // Of map, multimap, set, and multimap, only map has operator[]. + T& operator[](Key&& key); + + T& at(const Key& key); + const T& at(const Key& key) const; + + }; // map + + + + + + + /// multimap + /// + /// Implements a canonical multimap. + /// + /// The large majority of the implementation of this class is found in the rbtree + /// base class. We control the behaviour of rbtree via template parameters. + /// + /// Pool allocation + /// If you want to make a custom memory pool for a multimap container, your pool + /// needs to contain items of type multimap::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 multimap, 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 Allocator = EASTLAllocatorType> + class multimap + : public rbtree, Compare, Allocator, eastl::use_first >, true, false> + { + public: + typedef rbtree, Compare, Allocator, + eastl::use_first >, true, false> base_type; + typedef multimap 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; + typedef typename base_type::node_type node_type; + typedef typename base_type::iterator iterator; + typedef typename base_type::const_iterator const_iterator; + typedef typename base_type::allocator_type allocator_type; + typedef typename base_type::insert_return_type insert_return_type; + typedef typename base_type::extract_key extract_key; + // Other types are inherited from the base class. + + using base_type::begin; + using base_type::end; + using base_type::find; + using base_type::lower_bound; + using base_type::upper_bound; + using base_type::insert; + using base_type::erase; + + protected: + using base_type::compare; + using base_type::get_compare; + + public: + class value_compare + { + protected: + friend class multimap; + Compare compare; + value_compare(Compare c) : compare(c) {} + + public: + typedef bool result_type; + typedef value_type first_argument_type; + typedef value_type second_argument_type; + + bool operator()(const value_type& x, const value_type& y) const + { return compare(x.first, y.first); } + }; + + public: + multimap(const allocator_type& allocator = EASTL_MULTIMAP_DEFAULT_ALLOCATOR); + multimap(const Compare& compare, const allocator_type& allocator = EASTL_MULTIMAP_DEFAULT_ALLOCATOR); + multimap(const this_type& x); + multimap(this_type&& x); + multimap(this_type&& x, const allocator_type& allocator); + multimap(std::initializer_list ilist, const Compare& compare = Compare(), const allocator_type& allocator = EASTL_MULTIMAP_DEFAULT_ALLOCATOR); + + template + multimap(Iterator itBegin, Iterator itEnd); // allocator arg removed because VC7.1 fails on the default arg. To consider: Make a second version of this function without a default arg. + + this_type& operator=(const this_type& x) { return (this_type&)base_type::operator=(x); } + this_type& operator=(std::initializer_list ilist) { return (this_type&)base_type::operator=(ilist); } + this_type& operator=(this_type&& x) { return (this_type&)base_type::operator=(eastl::move(x)); } + + public: + /// 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. Note that C++11 move insertions and variadic emplace + /// support make this extension mostly no longer necessary. + insert_return_type insert(const Key& key); + + value_compare value_comp() const; + + size_type erase(const Key& key); + size_type count(const Key& key) const; + + eastl::pair equal_range(const Key& key); + eastl::pair equal_range(const Key& key) const; + + /// equal_range_small + /// This is a special version of equal_range which is optimized for the + /// case of there being few or no duplicated keys in the tree. + eastl::pair equal_range_small(const Key& key); + eastl::pair equal_range_small(const Key& key) const; + + private: + // these base member functions are not included in multimaps + using base_type::try_emplace; + using base_type::insert_or_assign; + }; // multimap + + + + + + /////////////////////////////////////////////////////////////////////// + // map + /////////////////////////////////////////////////////////////////////// + + template + inline map::map(const allocator_type& allocator) + : base_type(allocator) + { + } + + + template + inline map::map(const Compare& compare, const allocator_type& allocator) + : base_type(compare, allocator) + { + } + + + template + inline map::map(const this_type& x) + : base_type(x) + { + } + + + template + inline map::map(this_type&& x) + : base_type(eastl::move(x)) + { + } + + template + inline map::map(this_type&& x, const allocator_type& allocator) + : base_type(eastl::move(x), allocator) + { + } + + + template + inline map::map(std::initializer_list ilist, const Compare& compare, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), compare, allocator) + { + } + + + template + template + inline map::map(Iterator itBegin, Iterator itEnd) + : base_type(itBegin, itEnd, Compare(), EASTL_MAP_DEFAULT_ALLOCATOR) + { + } + + + template + inline typename map::insert_return_type + map::insert(const Key& key) + { + return base_type::DoInsertKey(true_type(), key); + } + + + template + inline typename map::value_compare + map::value_comp() const + { + return value_compare(get_compare()); + } + + + template + inline typename map::size_type + map::erase(const Key& key) + { + const iterator it(find(key)); + + if(it != end()) // If it exists... + { + base_type::erase(it); + return 1; + } + return 0; + } + + + template + inline typename map::size_type + map::count(const Key& key) const + { + const const_iterator it(find(key)); + return (it != end()) ? 1 : 0; + } + + + template + inline eastl::pair::iterator, + typename map::iterator> + map::equal_range(const Key& key) + { + // The resulting range will either be empty or have one element, + // so instead of doing two tree searches (one for lower_bound and + // one for upper_bound), we do just lower_bound and see if the + // result is a range of size zero or one. + const iterator itLower(lower_bound(key)); + + if((itLower == end()) || compare(key, itLower.mpNode->mValue.first)) // If at the end or if (key is < itLower)... + return eastl::pair(itLower, itLower); + + iterator itUpper(itLower); + return eastl::pair(itLower, ++itUpper); + } + + + template + inline eastl::pair::const_iterator, + typename map::const_iterator> + map::equal_range(const Key& key) const + { + // See equal_range above for comments. + const const_iterator itLower(lower_bound(key)); + + if((itLower == end()) || compare(key, itLower.mpNode->mValue.first)) // If at the end or if (key is < itLower)... + return eastl::pair(itLower, itLower); + + const_iterator itUpper(itLower); + return eastl::pair(itLower, ++itUpper); + } + + + template + inline T& map::operator[](const Key& key) + { + iterator itLower(lower_bound(key)); // itLower->first is >= key. + + if((itLower == end()) || compare(key, (*itLower).first)) + { + itLower = base_type::DoInsertKey(true_type(), itLower, key); + } + + return (*itLower).second; + + // Reference implementation of this function, which may not be as fast: + //iterator it(base_type::insert(eastl::pair(key, T())).first); + //return it->second; + } + + + template + inline T& map::operator[](Key&& key) + { + iterator itLower(lower_bound(key)); // itLower->first is >= key. + + if((itLower == end()) || compare(key, (*itLower).first)) + { + itLower = base_type::DoInsertKey(true_type(), itLower, eastl::move(key)); + } + + return (*itLower).second; + + // Reference implementation of this function, which may not be as fast: + //iterator it(base_type::insert(eastl::pair(key, T())).first); + //return it->second; + } + + + template + inline T& map::at(const Key& key) + { + iterator itLower(lower_bound(key)); // itLower->first is >= key. + + if(itLower == end()) + { + #if EASTL_EXCEPTIONS_ENABLED + throw std::out_of_range("map::at key does not exist"); + #else + EASTL_FAIL_MSG("map::at key does not exist"); + #endif + } + + return (*itLower).second; + } + + + template + inline const T& map::at(const Key& key) const + { + const_iterator itLower(lower_bound(key)); // itLower->first is >= key. + + if(itLower == end()) + { + #if EASTL_EXCEPTIONS_ENABLED + throw std::out_of_range("map::at key does not exist"); + #else + EASTL_FAIL_MSG("map::at key does not exist"); + #endif + } + + return (*itLower).second; + } + + + /////////////////////////////////////////////////////////////////////// + // erase_if + // + // https://en.cppreference.com/w/cpp/container/map/erase_if + /////////////////////////////////////////////////////////////////////// + template + void erase_if(map& c, Predicate predicate) + { + for (auto i = c.begin(), last = c.end(); i != last;) + { + if (predicate(*i)) + { + i = c.erase(i); + } + else + { + ++i; + } + } + } + + + /////////////////////////////////////////////////////////////////////// + // multimap + /////////////////////////////////////////////////////////////////////// + + template + inline multimap::multimap(const allocator_type& allocator) + : base_type(allocator) + { + } + + + template + inline multimap::multimap(const Compare& compare, const allocator_type& allocator) + : base_type(compare, allocator) + { + } + + + template + inline multimap::multimap(const this_type& x) + : base_type(x) + { + } + + + template + inline multimap::multimap(this_type&& x) + : base_type(eastl::move(x)) + { + } + + template + inline multimap::multimap(this_type&& x, const allocator_type& allocator) + : base_type(eastl::move(x), allocator) + { + } + + + template + inline multimap::multimap(std::initializer_list ilist, const Compare& compare, const allocator_type& allocator) + : base_type(ilist.begin(), ilist.end(), compare, allocator) + { + } + + + template + template + inline multimap::multimap(Iterator itBegin, Iterator itEnd) + : base_type(itBegin, itEnd, Compare(), EASTL_MULTIMAP_DEFAULT_ALLOCATOR) + { + } + + + template + inline typename multimap::insert_return_type + multimap::insert(const Key& key) + { + return base_type::DoInsertKey(false_type(), key); + } + + + template + inline typename multimap::value_compare + multimap::value_comp() const + { + return value_compare(get_compare()); + } + + + template + inline typename multimap::size_type + multimap::erase(const Key& key) + { + const eastl::pair range(equal_range(key)); + const size_type n = (size_type)eastl::distance(range.first, range.second); + base_type::erase(range.first, range.second); + return n; + } + + + template + inline typename multimap::size_type + multimap::count(const Key& key) const + { + const eastl::pair range(equal_range(key)); + return (size_type)eastl::distance(range.first, range.second); + } + + + template + inline eastl::pair::iterator, + typename multimap::iterator> + multimap::equal_range(const Key& key) + { + // There are multiple ways to implement equal_range. The implementation mentioned + // in the C++ standard and which is used by most (all?) commercial STL implementations + // is this: + // return eastl::pair(lower_bound(key), upper_bound(key)); + // + // This does two tree searches -- one for the lower bound and one for the + // upper bound. This works well for the case whereby you have a large container + // and there are lots of duplicated values. We provide an alternative version + // of equal_range called equal_range_small for cases where the user is confident + // that the number of duplicated items is only a few. + + return eastl::pair(lower_bound(key), upper_bound(key)); + } + + + template + inline eastl::pair::const_iterator, + typename multimap::const_iterator> + multimap::equal_range(const Key& key) const + { + // See comments above in the non-const version of equal_range. + return eastl::pair(lower_bound(key), upper_bound(key)); + } + + + template + inline eastl::pair::iterator, + typename multimap::iterator> + multimap::equal_range_small(const Key& key) + { + // We provide alternative version of equal_range here which works faster + // for the case where there are at most small number of potential duplicated keys. + const iterator itLower(lower_bound(key)); + iterator itUpper(itLower); + + while((itUpper != end()) && !compare(key, itUpper.mpNode->mValue.first)) + ++itUpper; + + return eastl::pair(itLower, itUpper); + } + + + template + inline eastl::pair::const_iterator, + typename multimap::const_iterator> + multimap::equal_range_small(const Key& key) const + { + // We provide alternative version of equal_range here which works faster + // for the case where there are at most small number of potential duplicated keys. + const const_iterator itLower(lower_bound(key)); + const_iterator itUpper(itLower); + + while((itUpper != end()) && !compare(key, itUpper.mpNode->mValue.first)) + ++itUpper; + + return eastl::pair(itLower, itUpper); + } + + + + /////////////////////////////////////////////////////////////////////// + // erase_if + // + // https://en.cppreference.com/w/cpp/container/multimap/erase_if + /////////////////////////////////////////////////////////////////////// + template + void erase_if(multimap& c, Predicate predicate) + { + // 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; + } + } + } + +} // namespace eastl + + +#endif // Header include guard + + + + diff --git a/lib/EASTL/include/EASTL/memory.h b/lib/EASTL/include/EASTL/memory.h new file mode 100644 index 000000000..1993b8ee2 --- /dev/null +++ b/lib/EASTL/include/EASTL/memory.h @@ -0,0 +1,1691 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This file implements the following functions from the C++ standard that +// are found in the header: +// +// Temporary memory: +// get_temporary_buffer +// return_temporary_buffer +// +// Utility: +// late_constructed - Extention to standard functionality. +// +// Uninitialized operations: +// These are the same as the copy, fill, and fill_n algorithms, except that +// they *construct* the destination with the source values rather than assign +// the destination with the source values. +// +// uninitialized_copy +// uninitialized_copy_n +// uninitialized_default_construct +// uninitialized_default_construct_n +// uninitialized_move +// uninitialized_move_if_noexcept - Extention to standard functionality. +// uninitialized_move_n +// uninitialized_fill +// uninitialized_fill_n +// uninitialized_value_construct +// uninitialized_value_construct_n +// uninitialized_default_fill - Extention to standard functionality. +// uninitialized_default_fill_n - Extention to standard functionality. +// uninitialized_relocate - Extention to standard functionality. +// uninitialized_copy_ptr - Extention to standard functionality. +// uninitialized_move_ptr - Extention to standard functionality. +// uninitialized_move_ptr_if_noexcept- Extention to standard functionality. +// uninitialized_fill_ptr - Extention to standard functionality. +// uninitialized_fill_n_ptr - Extention to standard functionality. +// uninitialized_copy_fill - Extention to standard functionality. +// uninitialized_fill_copy - Extention to standard functionality. +// uninitialized_copy_copy - Extention to standard functionality. +// +// In-place destructor helpers: +// destruct(T*) - Non-standard extension. +// destruct(first, last) - Non-standard extension. +// destroy_at(T*) +// destroy(first, last) +// destroy_n(first, n) +// +// Alignment +// align +// align_advance - Extention to standard functionality. +// +// Allocator-related +// uses_allocator +// allocator_arg_t +// allocator_arg +// +// Pointers +// pointer_traits +// +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EASTL_MEMORY_H +#define EASTL_MEMORY_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + + +// 4530 - C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc +// 4146 - unary minus operator applied to unsigned type, result still unsigned +// 4571 - catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught. +EA_DISABLE_VC_WARNING(4530 4146 4571); + + +#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_TEMP_DEFAULT_NAME + /// + /// Defines a default container name in the absence of a user-provided name. + /// + #ifndef EASTL_TEMP_DEFAULT_NAME + #define EASTL_TEMP_DEFAULT_NAME EASTL_DEFAULT_NAME_PREFIX " temp" // Unless the user overrides something, this is "EASTL temp". + #endif + + + /// get_temporary_buffer + /// + /// From the C++ standard, section 20.4.3: + /// 1 Effects: Obtains a pointer to storage sufficient to store up to n adjacent T objects. + /// 2 Returns: A pair containing the buffer's address and capacity (in the units of sizeof(T)), + /// or a pair of 0 values if no storage can be obtained. + /// + /// Note: The return value is space to hold T elements, but no T elements are constructed. + /// + /// Our implementation here differs slightly in that we have alignment, alignmentOffset, and pName arguments. + /// Note that you can use the EASTL_NAME_VAL macro to make names go away in release builds. + /// + /// Example usage: + /// pair pr = get_temporary_buffer(100, 0, 0, EASTL_NAME_VAL("Temp int array")); + /// memset(pr.first, 0, 100 * sizeof(int)); + /// return_temporary_buffer(pr.first); + /// + template + eastl::pair get_temporary_buffer(ptrdiff_t n, size_t alignment = 1, size_t alignmentOffset = 0, const char* pName = EASTL_TEMP_DEFAULT_NAME) + { + EASTLAllocatorType allocator(*EASTLAllocatorDefault(), pName); + return eastl::pair(static_cast(EASTLAllocAligned(allocator, n * sizeof(T), alignment, alignmentOffset)), n); + } + + + /// return_temporary_buffer + /// + /// From the C++ standard, section 20.4.3: + /// 3 Effects: Deallocates the buffer to which p points. + /// 4 Requires: The buffer shall have been previously allocated by get_temporary_buffer. + /// + /// Note: This function merely frees space and does not destruct any T elements. + /// + /// Example usage: + /// pair pr = get_temporary_buffer(300); + /// memset(pr.first, 0, 300 * sizeof(int)); + /// return_temporary_buffer(pr.first, pr.second); + /// + template + void return_temporary_buffer(T* p, ptrdiff_t n = 0) + { + EASTLAllocatorType& allocator(*EASTLAllocatorDefault()); + EASTLFree(allocator, p, n * sizeof(T)); + } + + + + /// late_constructed + /// + /// Implements a smart pointer type which separates the memory allocation of an object from + /// the object's construction. The primary use case is to declare a global variable of the + /// late_construction type, which allows the memory to be global but the constructor executes + /// at some point after main() begins as opposed to before main, which is often dangerous + /// for non-trivial types. + /// + /// The autoConstruct template parameter controls whether the object is automatically default + /// constructed upon first reference or must be manually constructed upon the first use of + /// operator * or ->. autoConstruct is convenient but it causes * and -> to be slightly slower + /// and may result in construction at an inconvenient time. + /// + /// The autoDestruct template parameter controls whether the object, if constructed, is automatically + /// destructed when ~late_constructed() is called or must be manually destructed via a call to + /// destruct(). + /// + /// While construction can be automatic or manual, automatic destruction support is always present. + /// Thus you aren't required in any case to manually call destruct. However, you may safely manually + /// destruct the object at any time before the late_constructed destructor is executed. + /// + /// You may still use late_constructed after calling destruct(), including calling construct() + /// again to reconstruct the instance. destruct returns the late_constructed instance to a + /// state equivalent to before construct was called. + /// + /// Caveat: While late_constructed instances can be declared in global scope and initialize + /// prior to main() executing, you cannot otherwise use such globally declared instances prior + /// to main with guaranteed behavior unless you can ensure that the late_constructed instance + /// is itself constructed prior to your use of it. + /// + /// Example usage (demonstrating manual-construction): + /// late_constructed gWidget; + /// + /// void main(){ + /// gWidget.construct(kScrollbarType, kVertical, "MyScrollbar"); + /// gWidget->SetValue(15); + /// gWidget.destruct(); + /// } + /// + /// Example usage (demonstrating auto-construction): + /// late_constructed gWidget; + /// + /// void main(){ + /// gWidget->SetValue(15); + /// // You may want to call destruct here, but aren't required to do so unless the Widget type requires it. + /// } + /// + template + class late_constructed + { + public: + using this_type = late_constructed; + using value_type = T; + using storage_type = eastl::aligned_storage_t>; + + late_constructed() EA_NOEXCEPT // In the case of the late_constructed instance being at global scope, we rely on the + : mStorage(), mpValue(nullptr) {} // compiler executing this constructor or placing the instance in auto-zeroed-at-startup memory. + + ~late_constructed() + { + if (autoDestruct && mpValue) + (*mpValue).~value_type(); + } + + template + void construct(Args&&... args) + { + if(!mpValue) + mpValue = new (&mStorage) value_type(eastl::forward(args)...); + } + + bool is_constructed() const EA_NOEXCEPT + { return mpValue != nullptr; } + + void destruct() + { + if(mpValue) + { + (*mpValue).~value_type(); + mpValue = nullptr; + } + } + + value_type& operator*() EA_NOEXCEPT + { + if(!mpValue) + construct(); + + EA_ANALYSIS_ASSUME(mpValue); + return *mpValue; + } + + const value_type& operator*() const EA_NOEXCEPT + { + if(!mpValue) + construct(); + + EA_ANALYSIS_ASSUME(mpValue); + return *mpValue; + } + + value_type* operator->() EA_NOEXCEPT + { + if(!mpValue) + construct(); + return mpValue; + } + + const value_type* operator->() const EA_NOEXCEPT + { + if(!mpValue) + construct(); + return mpValue; + } + + value_type* get() EA_NOEXCEPT + { + if(!mpValue) + construct(); + return mpValue; + } + + const value_type* get() const EA_NOEXCEPT + { + if(!mpValue) + construct(); + return mpValue; + } + + protected: + storage_type mStorage; // Declared first because it may have aligment requirements, and it would be more space-efficient if it was first. + value_type* mpValue; + }; + + + // Specialization that doesn't auto-construct on demand. + template + class late_constructed : public late_constructed + { + public: + typedef late_constructed base_type; + + typename base_type::value_type& operator*() EA_NOEXCEPT + { EASTL_ASSERT(base_type::mpValue); return *base_type::mpValue; } + + const typename base_type::value_type& operator*() const EA_NOEXCEPT + { EASTL_ASSERT(base_type::mpValue); return *base_type::mpValue; } + + typename base_type::value_type* operator->() EA_NOEXCEPT + { EASTL_ASSERT(base_type::mpValue); return base_type::mpValue; } + + const typename base_type::value_type* operator->() const EA_NOEXCEPT + { EASTL_ASSERT(base_type::mpValue); return base_type::mpValue; } + + typename base_type::value_type* get() EA_NOEXCEPT + { return base_type::mpValue; } + + const typename base_type::value_type* get() const EA_NOEXCEPT + { return base_type::mpValue; } + }; + + + + /// raw_storage_iterator + /// + /// From the C++11 Standard, section 20.6.10 p1 + /// raw_storage_iterator is provided to enable algorithms to store their results into uninitialized memory. + /// The formal template parameter OutputIterator is required to have its operator* return an object for + /// which operator& is defined and returns a pointer to T, and is also required to satisfy the requirements + /// of an output iterator (24.2.4). + + template + class raw_storage_iterator : public iterator + { + protected: + OutputIterator mIterator; + + public: + explicit raw_storage_iterator(OutputIterator iterator) + : mIterator(iterator) + { + } + + raw_storage_iterator& operator*() + { + return *this; + } + + raw_storage_iterator& operator=(const T& value) + { + ::new(eastl::addressof(*mIterator)) T(value); + return *this; + } + + raw_storage_iterator& operator++() + { + ++mIterator; + return *this; + } + + raw_storage_iterator operator++(int) + { + raw_storage_iterator tempIterator = *this; + ++mIterator; + return tempIterator; + } + }; + + + /// uninitialized_relocate (formerly named uninitialized_move prior to C++11) + /// + /// This utility is deprecated in favor of C++11 rvalue move functionality. + /// + /// uninitialized_relocate takes a constructed sequence of objects and an + /// uninitialized destination buffer. In the case of any exception thrown + /// while moving the objects, any newly constructed objects are guaranteed + /// to be destructed and the input left fully constructed. + /// + /// In the case where you need to do multiple moves atomically, split the + /// calls into uninitialized_relocate_start/abort/commit. + /// + /// uninitialized_relocate_start can possibly throw an exception. If it does, + /// you don't need to do anything. However, if it returns without throwing + /// an exception you need to guarantee that either uninitialized_relocate_abort + /// or uninitialized_relocate_commit is called. + /// + /// Both uninitialized_relocate_abort and uninitialize_move_commit are + /// guaranteed to not throw C++ exceptions. + namespace Internal + { + template + struct uninitialized_relocate_impl + { + template + static ForwardIteratorDest do_move_start(ForwardIterator first, ForwardIterator last, ForwardIteratorDest dest) + { + typedef typename eastl::iterator_traits::value_type value_type; + + #if EASTL_EXCEPTIONS_ENABLED + ForwardIteratorDest origDest(dest); + try + { + #endif + for(; first != last; ++first, ++dest) + ::new((void*)eastl::addressof(*dest)) value_type(*first); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; origDest < dest; ++origDest) + (*origDest).~value_type(); + throw; + } + #endif + + return dest; + } + + template + static ForwardIteratorDest do_move_commit(ForwardIterator first, ForwardIterator last, ForwardIteratorDest dest) //throw() + { + typedef typename eastl::iterator_traits::value_type value_type; + for(; first != last; ++first, ++dest) + (*first).~value_type(); + + return dest; + } + + template + static ForwardIteratorDest do_move_abort(ForwardIterator first, ForwardIterator last, ForwardIteratorDest dest) //throw() + { + typedef typename eastl::iterator_traits::value_type value_type; + for(; first != last; ++first, ++dest) + (*dest).~value_type(); + return dest; + } + }; + + template <> + struct uninitialized_relocate_impl + { + template + static T* do_move_start(T* first, T* last, T* dest) + { + if (EASTL_UNLIKELY(first == last)) + return dest; + + return (T*)memcpy(dest, first, (size_t)((uintptr_t)last - (uintptr_t)first)) + (last - first); + } + + template + static T* do_move_commit(T* first, T* last, T* dest) + { + return dest + (last - first); + } + + template + static T* do_move_abort(T* first, T* last, T* dest) + { + return dest + (last - first); + } + }; + } + + + /// uninitialized_relocate_start, uninitialized_relocate_commit, uninitialized_relocate_abort + /// + /// This utility is deprecated in favor of C++11 rvalue move functionality. + /// + /// After calling uninitialized_relocate_start, if it doesn't throw an exception, + /// both the source and destination iterators point to undefined data. If it + /// does throw an exception, the destination remains uninitialized and the source + /// is as it was before. + /// + /// In order to make the iterators valid again you need to call either uninitialized_relocate_abort + /// or uninitialized_relocate_commit. The abort call makes the original source + /// iterator valid again, and commit makes the destination valid. Both abort + /// and commit are guaranteed to not throw C++ exceptions. + /// + /// Example usage: + /// iterator dest2 = uninitialized_relocate_start(first, last, dest); + /// try { + /// // some code here that might throw an exception + /// } + /// catch(...) + /// { + /// uninitialized_relocate_abort(first, last, dest); + /// throw; + /// } + /// uninitialized_relocate_commit(first, last, dest); + /// + template + inline ForwardIteratorDest uninitialized_relocate_start(ForwardIterator first, ForwardIterator last, ForwardIteratorDest dest) + { + typedef typename eastl::iterator_traits::iterator_category IC; + typedef typename eastl::iterator_traits::value_type value_type_input; + typedef typename eastl::iterator_traits::value_type value_type_output; + + const bool bHasTrivialMove = type_and::value, + is_pointer::value, + is_pointer::value, + is_same::value>::value; + + return Internal::uninitialized_relocate_impl::do_move_start(first, last, dest); + } + + template + inline ForwardIteratorDest uninitialized_relocate_commit(ForwardIterator first, ForwardIterator last, ForwardIteratorDest dest) + { + typedef typename eastl::iterator_traits::iterator_category IC; + typedef typename eastl::iterator_traits::value_type value_type_input; + typedef typename eastl::iterator_traits::value_type value_type_output; + + const bool bHasTrivialMove = type_and::value, + is_pointer::value, + is_pointer::value, + is_same::value>::value; + + return Internal::uninitialized_relocate_impl::do_move_commit(first, last, dest); + } + + template + inline ForwardIteratorDest uninitialized_relocate_abort(ForwardIterator first, ForwardIterator last, ForwardIteratorDest dest) + { + typedef typename eastl::iterator_traits::iterator_category IC; + typedef typename eastl::iterator_traits::value_type value_type_input; + typedef typename eastl::iterator_traits::value_type value_type_output; + + const bool bHasTrivialMove = type_and::value, + is_pointer::value, + is_pointer::value, + is_same::value>::value; + + return Internal::uninitialized_relocate_impl::do_move_abort(first, last, dest); + } + + /// uninitialized_relocate + /// + /// See above for documentation. + /// + template + inline ForwardIteratorDest uninitialized_relocate(ForwardIterator first, ForwardIterator last, ForwardIteratorDest dest) + { + ForwardIteratorDest result = uninitialized_relocate_start(first, last, dest); + eastl::uninitialized_relocate_commit(first, last, dest); + + return result; + } + + + + + + // uninitialized_copy + // + namespace Internal + { + template + inline ForwardIterator uninitialized_copy_impl(InputIterator first, InputIterator last, ForwardIterator dest, true_type) + { + return eastl::copy(first, last, dest); // The copy() in turn will use memcpy for POD types. + } + + template + inline ForwardIterator uninitialized_copy_impl(InputIterator first, InputIterator last, ForwardIterator dest, false_type) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(dest); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(; first != last; ++first, ++currentDest) + ::new(static_cast(eastl::addressof(*currentDest))) value_type(*first); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; dest < currentDest; ++dest) + (*dest).~value_type(); + throw; + } + #endif + + return currentDest; + } + } + + /// uninitialized_copy + /// + /// Copies a source range to a destination, copy-constructing the destination with + /// the source values (and not *assigning* the destination with the source values). + /// Returns the end of the destination range (i.e. dest + (last - first)). + /// + /// Declaration: + /// template + /// ForwardIterator uninitialized_copy(InputIterator sourceFirst, InputIterator sourceLast, ForwardIterator destination); + /// + /// Example usage: + /// SomeClass* pArray = malloc(10 * sizeof(SomeClass)); + /// uninitialized_copy(pSourceDataBegin, pSourceDataBegin + 10, pArray); + /// + template + inline ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result) + { + typedef typename eastl::iterator_traits::value_type value_type; + + // We use is_trivial, which in the C++11 Standard means is_trivially_copyable and is_trivially_default_constructible. + return Internal::uninitialized_copy_impl(first, last, result, eastl::is_trivial()); + } + + + /// uninitialized_copy_n + /// + /// Copies count elements from a range beginning at first to an uninitialized memory area + /// beginning at dest. The elements in the uninitialized area are constructed using copy constructor. + /// If an exception is thrown during the initialization, the function has no final effects. + /// + /// first: Beginning of the range of the elements to copy. + /// dest: Beginning of the destination range. + /// return value: Iterator of dest type to the element past the last element copied. + /// + namespace Internal + { + template + struct uninitialized_copy_n_impl + { + static ForwardIterator impl(InputIterator first, Count n, ForwardIterator dest) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(dest); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(; n > 0; --n, ++first, ++currentDest) + ::new((void*)(eastl::addressof(*currentDest))) value_type(*first); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; dest < currentDest; ++dest) + (*dest).~value_type(); + throw; + } + #endif + + return currentDest; + } + }; + + template + struct uninitialized_copy_n_impl + { + static inline ForwardIterator impl(InputIterator first, Count n, ForwardIterator dest) + { + return eastl::uninitialized_copy(first, first + n, dest); + } + }; + } + + template + inline ForwardIterator uninitialized_copy_n(InputIterator first, Count n, ForwardIterator dest) + { + typedef typename eastl::iterator_traits::iterator_category IC; + return Internal::uninitialized_copy_n_impl::impl(first, n, dest); + } + + + + /// uninitialized_copy_ptr + /// + /// This is a specialization of uninitialized_copy for iterators that are pointers. We use it because + /// internally it uses generic_iterator to make pointers act like regular eastl::iterator. + /// + template + inline Result uninitialized_copy_ptr(First first, Last last, Result result) + { + typedef typename eastl::iterator_traits >::value_type value_type; + const generic_iterator i(Internal::uninitialized_copy_impl(eastl::generic_iterator(first), // generic_iterator makes a pointer act like an iterator. + eastl::generic_iterator(last), + eastl::generic_iterator(result), + eastl::is_trivially_copy_assignable())); + return i.base(); + } + + + + /// uninitialized_move_ptr + /// + /// This is a specialization of uninitialized_move for iterators that are pointers. We use it because + /// internally it uses generic_iterator to make pointers act like regular eastl::iterator. + /// + namespace Internal + { + template + inline ForwardIterator uninitialized_move_impl(InputIterator first, InputIterator last, ForwardIterator dest, true_type) + { + return eastl::copy(first, last, dest); // The copy() in turn will use memcpy for is_trivially_copy_assignable (e.g. POD) types. + } + + template + inline ForwardIterator uninitialized_move_impl(InputIterator first, InputIterator last, ForwardIterator dest, false_type) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(dest); + + // We must run a loop over every element and move-construct it at the new location. + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(; first != last; ++first, ++currentDest) + ::new((void*)eastl::addressof(*currentDest)) value_type(eastl::move(*first)); // If value_type has a move constructor then it will be used here. + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + // We have a problem here: If an exception occurs while doing the loop below then we will + // have values that were moved from the source to the dest that may need to be moved back + // in the catch. What does the C++11 Standard say about this? And what happens if there's an + // exception while moving them back? We may want to trace through a conforming C++11 Standard + // Library to see what it does and do something similar. Given that rvalue references are + // objects that are going away, we may not need to move the values back, though that has the + // side effect of a certain kind of lost elements problem. + for(; dest < currentDest; ++dest) + (*dest).~value_type(); + throw; + } + #endif + + return currentDest; + } + } + + template + inline Result uninitialized_move_ptr(First first, Last last, Result dest) + { + typedef typename eastl::iterator_traits >::value_type value_type; + const generic_iterator i(Internal::uninitialized_move_impl(eastl::generic_iterator(first), // generic_iterator makes a pointer act like an iterator. + eastl::generic_iterator(last), + eastl::generic_iterator(dest), + eastl::is_trivially_copy_assignable())); // is_trivially_copy_assignable identifies if copy assignment would be as valid as move assignment, which means we have the opportunity to memcpy/memmove optimization. + return i.base(); + } + + + + + /// uninitialized_move + /// + /// Moves a source range to a destination, move-constructing the destination with + /// the source values (and not *assigning* the destination with the source values). + /// Returns the end of the destination range (i.e. dest + (last - first)). + /// + /// uninitialized_move is not part of any current C++ Standard, up to C++14. + /// + /// Declaration: + /// template + /// ForwardIterator uninitialized_move(InputIterator sourceFirst, InputIterator sourceLast, ForwardIterator destination); + /// + /// Example usage: + /// SomeClass* pArray = malloc(10 * sizeof(SomeClass)); + /// uninitialized_move(pSourceDataBegin, pSourceDataBegin + 10, pArray); + /// + template + inline ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator dest) + { + return eastl::uninitialized_copy(eastl::make_move_iterator(first), eastl::make_move_iterator(last), dest); + } + + + /// uninitialized_move_if_noexcept + /// + /// If the iterated type can be moved without exceptions, move construct the dest with the input. Else copy-construct + /// the dest witih the input. If move isn't supported by the compiler, do regular copy. + /// + template + inline ForwardIterator uninitialized_move_if_noexcept(InputIterator first, InputIterator last, ForwardIterator dest) + { + return eastl::uninitialized_copy(eastl::make_move_if_noexcept_iterator(first), eastl::make_move_if_noexcept_iterator(last), dest); + } + + + /// uninitialized_move_ptr_if_noexcept + /// + template + inline Result uninitialized_move_ptr_if_noexcept(First first, Last last, Result dest) + { + #if EASTL_EXCEPTIONS_ENABLED + return eastl::uninitialized_move_if_noexcept(first, last, dest); + #else + return eastl::uninitialized_move_ptr(first, last, dest); + #endif + } + + + /// uninitialized_move_n + /// + /// Moves count elements from a range beginning at first to an uninitialized memory area + /// beginning at dest. The elements in the uninitialized area are constructed using copy constructor. + /// If an exception is thrown during the initialization, the function has no final effects. + /// + /// first: Beginning of the range of the elements to move. + /// dest: Beginning of the destination range. + /// return value: Iterator of dest type to the element past the last element moved. + /// + template + inline ForwardIterator uninitialized_move_n(InputIterator first, Count n, ForwardIterator dest) + { + return eastl::uninitialized_copy_n(eastl::make_move_iterator(first), n, dest); + } + + // Disable warning C4345 - behavior change: an object of POD type constructed with an initializer of the form () + // will be default-initialized. + // This is the behavior we intend below. + EA_DISABLE_VC_WARNING(4345) + /// uninitialized_default_fill + /// + /// Default-constructs the elements in the destination range. + /// Returns void. It wouldn't be useful to return the end of the destination range, + /// as that is the same as the 'last' input parameter. + /// + /// Declaration: + /// template + /// void uninitialized_default_fill(ForwardIterator destinationFirst, ForwardIterator destinationLast); + /// + template + inline void uninitialized_default_fill(ForwardIterator first, ForwardIterator last) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(first); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for (; currentDest != last; ++currentDest) + ::new (eastl::addressof(*currentDest)) value_type(); + #if EASTL_EXCEPTIONS_ENABLED + } + catch (...) + { + for (; first < currentDest; ++first) + (*first).~value_type(); + throw; + } + #endif + } + + /// uninitialized_default_fill_n + /// + /// Default-constructs the range of [first, first + n). + /// Returns void as per the C++ standard, though returning the end input iterator + /// value may be of use. + /// + /// Declaration: + /// template + /// void uninitialized_default_fill_n(ForwardIterator destination, Count n); + /// + namespace Internal + { + template + inline void uninitialized_default_fill_n_impl(ForwardIterator first, Count n, false_type) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(first); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for (; n > 0; --n, ++currentDest) + ::new (eastl::addressof(*currentDest)) value_type(); + #if EASTL_EXCEPTIONS_ENABLED + } + catch (...) + { + for (; first < currentDest; ++first) + (*first).~value_type(); + throw; + } + #endif + } + + template + inline void uninitialized_default_fill_n_impl(ForwardIterator first, Count n, true_type) + { + if (EASTL_UNLIKELY(n == 0)) + return; + + typedef typename eastl::iterator_traits::value_type value_type; + memset(first, 0, sizeof(value_type) * n); + } + } + + template + inline void uninitialized_default_fill_n(ForwardIterator first, Count n) + { + typedef typename eastl::iterator_traits::value_type value_type; + Internal::uninitialized_default_fill_n_impl(first, n, is_scalar()); + } + EA_RESTORE_VC_WARNING() + + /// uninitialized_default_construct + /// + /// Constructs objects in the uninitialized storage designated by the range [first, last) by default-initialization. + /// + /// Default-initialization: + /// If T is a class, the default constructor is called; otherwise, no initialization is done, resulting in + /// indeterminate values. + /// + /// http://en.cppreference.com/w/cpp/memory/uninitialized_default_construct + /// + template + inline void uninitialized_default_construct(ForwardIterator first, ForwardIterator last) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(first); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for (; currentDest != last; ++currentDest) + ::new (eastl::addressof(*currentDest)) value_type; + #if EASTL_EXCEPTIONS_ENABLED + } + catch (...) + { + for (; first < currentDest; ++first) + (*first).~value_type(); + throw; + } + #endif + } + + /// uninitialized_default_construct_n + /// + /// Constructs n objects in the uninitialized storage starting at first by default-initialization. + /// + /// http://en.cppreference.com/w/cpp/memory/uninitialized_default_construct_n + /// + template + inline ForwardIterator uninitialized_default_construct_n(ForwardIterator first, Count n) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(first); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for (; n > 0; --n, ++currentDest) + ::new (eastl::addressof(*currentDest)) value_type; + return currentDest; + #if EASTL_EXCEPTIONS_ENABLED + } + catch (...) + { + for (; first < currentDest; ++first) + (*first).~value_type(); + throw; + } + #endif + } + + /// uninitialized_fill + /// + /// Copy-constructs the elements in the destination range with the given input value. + /// Returns void. It wouldn't be useful to return the end of the destination range, + /// as that is the same as the 'last' input parameter. + /// + /// Declaration: + /// template + /// void uninitialized_fill(ForwardIterator destinationFirst, ForwardIterator destinationLast, const T& value); + /// + namespace Internal + { + template + inline void uninitialized_fill_impl(ForwardIterator first, ForwardIterator last, const T& value, true_type) + { + eastl::fill(first, last, value); + } + + template + void uninitialized_fill_impl(ForwardIterator first, ForwardIterator last, const T& value, false_type) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(first); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(; currentDest != last; ++currentDest) + ::new((void*)eastl::addressof(*currentDest)) value_type(value); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; first < currentDest; ++first) + (*first).~value_type(); + throw; + } + #endif + } + } + + template + inline void uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& value) + { + typedef typename eastl::iterator_traits::value_type value_type; + Internal::uninitialized_fill_impl(first, last, value, eastl::is_trivially_copy_assignable()); + } + + /// uninitialized_value_construct + /// + /// Constructs objects in the uninitialized storage range [first, last) by value-initialization. + /// + /// Value-Initialization: + /// If T is a class, the object is default-initialized (after being zero-initialized if T's default + /// constructor is not user-provided/deleted); otherwise, the object is zero-initialized. + /// + /// http://en.cppreference.com/w/cpp/memory/uninitialized_value_construct + /// + template + void uninitialized_value_construct(ForwardIterator first, ForwardIterator last) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(first); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for (; currentDest != last; ++currentDest) + ::new (eastl::addressof(*currentDest)) value_type(); + #if EASTL_EXCEPTIONS_ENABLED + } + catch (...) + { + for (; first < currentDest; ++first) + (*first).~value_type(); + throw; + } + #endif + } + + /// uninitialized_value_construct_n + /// + /// Constructs n objects in the uninitialized storage starting at first by value-initialization. + /// + /// Value-Initialization: + /// If T is a class, the object is default-initialized (after being zero-initialized if T's default + /// constructor is not user-provided/deleted); otherwise, the object is zero-initialized. + /// + /// http://en.cppreference.com/w/cpp/memory/uninitialized_value_construct_n + /// + template + ForwardIterator uninitialized_value_construct_n(ForwardIterator first, Count n) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(first); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for (; n > 0; --n, ++currentDest) + ::new (eastl::addressof(*currentDest)) value_type(); + return currentDest; + #if EASTL_EXCEPTIONS_ENABLED + } + catch (...) + { + for (; first < currentDest; ++first) + (*first).~value_type(); + throw; + } + #endif + } + + /// uninitialized_fill_ptr + /// + /// This is a specialization of uninitialized_fill for iterators that are pointers. + /// It exists so that we can declare a value_type for the iterator, which you + /// can't do with a pointer by itself. + /// + template + inline void uninitialized_fill_ptr(T* first, T* last, const T& value) + { + typedef typename eastl::iterator_traits >::value_type value_type; + Internal::uninitialized_fill_impl(eastl::generic_iterator(first), + eastl::generic_iterator(last), value, + eastl::is_trivially_copy_assignable()); + } + + /// uninitialized_fill_n + /// + /// Copy-constructs the range of [first, first + n) with the given input value. + /// Returns void as per the C++ standard, though returning the end input iterator + /// value may be of use. + /// + /// Declaration: + /// template + /// void uninitialized_fill_n(ForwardIterator destination, Count n, const T& value); + /// + namespace Internal + { + template + inline void uninitialized_fill_n_impl(ForwardIterator first, Count n, const T& value, true_type) + { + eastl::fill_n(first, n, value); + } + + template + void uninitialized_fill_n_impl(ForwardIterator first, Count n, const T& value, false_type) + { + typedef typename eastl::iterator_traits::value_type value_type; + ForwardIterator currentDest(first); + + #if EASTL_EXCEPTIONS_ENABLED + try + { + #endif + for(; n > 0; --n, ++currentDest) + ::new((void*)eastl::addressof(*currentDest)) value_type(value); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; first < currentDest; ++first) + (*first).~value_type(); + throw; + } + #endif + } + } + + template + inline void uninitialized_fill_n(ForwardIterator first, Count n, const T& value) + { + typedef typename eastl::iterator_traits::value_type value_type; + Internal::uninitialized_fill_n_impl(first, n, value, eastl::is_trivially_copy_assignable()); + } + + + + /// uninitialized_fill_n_ptr + /// + /// This is a specialization of uninitialized_fill_n for iterators that are pointers. + /// It exists so that we can declare a value_type for the iterator, which you + /// can't do with a pointer by itself. + /// + template + inline void uninitialized_fill_n_ptr(T* first, Count n, const T& value) + { + typedef typename eastl::iterator_traits >::value_type value_type; + Internal::uninitialized_fill_n_impl(eastl::generic_iterator(first), n, value, eastl::is_trivially_copy_assignable()); + } + + + + + /// uninitialized_copy_fill + /// + /// Copies [first1, last1) into [first2, first2 + (last1 - first1)) then + /// fills [first2 + (last1 - first1), last2) with value. + /// + template + inline void uninitialized_copy_fill(InputIterator first1, InputIterator last1, + ForwardIterator first2, ForwardIterator last2, const T& value) + { + const ForwardIterator mid(eastl::uninitialized_copy(first1, last1, first2)); + + #if EASTL_EXCEPTIONS_ENABLED + typedef typename eastl::iterator_traits::value_type value_type; + try + { + #endif + eastl::uninitialized_fill(mid, last2, value); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; first2 < mid; ++first2) + (*first2).~value_type(); + throw; + } + #endif + } + + + /// uninitialized_move_fill + /// + /// Moves [first1, last1) into [first2, first2 + (last1 - first1)) then + /// fills [first2 + (last1 - first1), last2) with value. + /// + template + inline void uninitialized_move_fill(InputIterator first1, InputIterator last1, + ForwardIterator first2, ForwardIterator last2, const T& value) + { + const ForwardIterator mid(eastl::uninitialized_move(first1, last1, first2)); + + #if EASTL_EXCEPTIONS_ENABLED + typedef typename eastl::iterator_traits::value_type value_type; + try + { + #endif + eastl::uninitialized_fill(mid, last2, value); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; first2 < mid; ++first2) + (*first2).~value_type(); + throw; + } + #endif + } + + + + + + /// uninitialized_fill_copy + /// + /// Fills [result, mid) with value then copies [first, last) into [mid, mid + (last - first)). + /// + template + inline ForwardIterator + uninitialized_fill_copy(ForwardIterator result, ForwardIterator mid, const T& value, InputIterator first, InputIterator last) + { + eastl::uninitialized_fill(result, mid, value); + + #if EASTL_EXCEPTIONS_ENABLED + typedef typename eastl::iterator_traits::value_type value_type; + try + { + #endif + return eastl::uninitialized_copy(first, last, mid); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; result < mid; ++result) + (*result).~value_type(); + throw; + } + #endif + } + + + /// uninitialized_fill_move + /// + /// Fills [result, mid) with value then copies [first, last) into [mid, mid + (last - first)). + /// + template + inline ForwardIterator + uninitialized_fill_move(ForwardIterator result, ForwardIterator mid, const T& value, InputIterator first, InputIterator last) + { + eastl::uninitialized_fill(result, mid, value); + + #if EASTL_EXCEPTIONS_ENABLED + typedef typename eastl::iterator_traits::value_type value_type; + try + { + #endif + return eastl::uninitialized_move(first, last, mid); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; result < mid; ++result) + (*result).~value_type(); + throw; + } + #endif + } + + + + /// uninitialized_copy_copy + /// + /// Copies [first1, last1) into [result, result + (last1 - first1)) then + /// copies [first2, last2) into [result, result + (last1 - first1) + (last2 - first2)). + /// + template + inline ForwardIterator + uninitialized_copy_copy(InputIterator1 first1, InputIterator1 last1, + InputIterator2 first2, InputIterator2 last2, + ForwardIterator result) + { + const ForwardIterator mid(eastl::uninitialized_copy(first1, last1, result)); + + #if EASTL_EXCEPTIONS_ENABLED + typedef typename eastl::iterator_traits::value_type value_type; + try + { + #endif + return eastl::uninitialized_copy(first2, last2, mid); + #if EASTL_EXCEPTIONS_ENABLED + } + catch(...) + { + for(; result < mid; ++result) + (*result).~value_type(); + throw; + } + #endif + } + + + + /// destruct + /// + /// Calls the destructor of a given object. + /// + /// Note that we don't have a specialized version of this for objects + /// with trivial destructors, such as integers. This is because the + /// compiler can already see in our version here that the destructor + /// is a no-op. + /// + template + inline void destruct(T* p) + { + // https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(C4100)&rd=true + // "C4100 can also be issued when code calls a destructor on a otherwise unreferenced parameter + // of primitive type. This is a limitation of the Visual C++ compiler." + EA_UNUSED(p); + p->~T(); + } + + + + // destruct(first, last) + // + template + inline void destruct_impl(ForwardIterator /*first*/, ForwardIterator /*last*/, true_type) // true means the type has a trivial destructor. + { + // Empty. The type has a trivial destructor. + } + + template + inline void destruct_impl(ForwardIterator first, ForwardIterator last, false_type) // false means the type has a significant destructor. + { + typedef typename eastl::iterator_traits::value_type value_type; + + for(; first != last; ++first) + (*first).~value_type(); + } + + /// destruct + /// + /// Calls the destructor on a range of objects. + /// + /// We have a specialization for objects with trivial destructors, such as + /// PODs. In this specialization the destruction of the range is a no-op. + /// + template + inline void destruct(ForwardIterator first, ForwardIterator last) + { + typedef typename eastl::iterator_traits::value_type value_type; + destruct_impl(first, last, eastl::has_trivial_destructor()); + } + + + /// destroy_at + /// + /// Calls the destructor of a given object. + /// + /// Note that we don't have a specialized version of this for objects + /// with trivial destructors, such as integers. This is because the + /// compiler can already see in our version here that the destructor + /// is a no-op. + /// + /// This is the same as eastl::destruct but we included for C++17 compliance. + /// + /// http://en.cppreference.com/w/cpp/memory/destroy_at + /// + template + inline void destroy_at(T* p) + { + EA_UNUSED(p); + p->~T(); + } + + + /// destroy + /// + /// Calls the destructor on a range of objects. + /// + /// http://en.cppreference.com/w/cpp/memory/destroy + /// + template + inline void destroy(ForwardIterator first, ForwardIterator last) + { + for (; first != last; ++first) + eastl::destroy_at(eastl::addressof(*first)); + } + + + /// destroy_n + /// + /// Calls the destructor on the n objects in the range. + /// + /// http://en.cppreference.com/w/cpp/memory/destroy_n + /// + template + ForwardIterator destroy_n(ForwardIterator first, Size n) + { + for (; n > 0; ++first, --n) + eastl::destroy_at(eastl::addressof(*first)); + + return first; + } + + + /// align + /// + /// Same as C++11 std::align. http://en.cppreference.com/w/cpp/memory/align + /// If it is possible to fit size bytes of storage aligned by alignment into the buffer pointed to by + /// ptr with length space, the function updates ptr to point to the first possible address of such storage, + /// decreases space by the number of bytes used for alignment, and returns the new ptr value. Otherwise, + /// the function returns NULL and leaves ptr and space unmodified. + /// + /// Example usage: + /// char buffer[512]; + /// size_t space = sizeof(buffer); + /// void* p = buffer; + /// void* p1 = eastl::align(16, 3, p, space); p = (char*)p + 3; space -= 3; + /// void* p2 = eastl::align(32, 78, p, space); p = (char*)p + 78; space -= 78; + /// void* p3 = eastl::align(64, 9, p, space); p = (char*)p + 9; space -= 9; + + inline void* align(size_t alignment, size_t size, void*& ptr, size_t& space) + { + if(space >= size) + { + char* ptrAligned = (char*)(((size_t)ptr + (alignment - 1)) & -alignment); + size_t offset = (size_t)(ptrAligned - (char*)ptr); + + if((space - size) >= offset) // Have to implement this in terms of subtraction instead of addition in order to handle possible overflow. + { + ptr = ptrAligned; + space -= offset; + + return ptrAligned; + } + } + + return NULL; + } + + + /// align_advance + /// + /// Same as align except ptr and space can be adjusted to reflect remaining space. + /// Not present in the C++ Standard. + /// Note that the example code here is similar to align but simpler. + /// + /// Example usage: + /// char buffer[512]; + /// size_t space = sizeof(buffer); + /// void* p = buffer; + /// void* p1 = eastl::align_advance(16, 3, p, space, &p, &space); // p is advanced and space reduced accordingly. + /// void* p2 = eastl::align_advance(32, 78, p, space, &p, &space); + /// void* p3 = eastl::align_advance(64, 9, p, space, &p, &space); + /// void* p4 = eastl::align_advance(16, 33, p, space); + + inline void* align_advance(size_t alignment, size_t size, void* ptr, size_t space, void** ptrAdvanced = NULL, size_t* spaceReduced = NULL) + { + if(space >= size) + { + char* ptrAligned = (char*)(((size_t)ptr + (alignment - 1)) & -alignment); + size_t offset = (size_t)(ptrAligned - (char*)ptr); + + if((space - size) >= offset) // Have to implement this in terms of subtraction instead of addition in order to handle possible overflow. + { + if(ptrAdvanced) + *ptrAdvanced = (ptrAligned + size); + if(spaceReduced) + *spaceReduced = (space - (offset + size)); + + return ptrAligned; + } + } + + return NULL; + } + + + /////////////////////////////////////////////////////////////////////// + // uses_allocator + // + // Determines if the class T has an allocator_type member typedef + // which Allocator is convertible to. + // + // http://en.cppreference.com/w/cpp/memory/uses_allocator + // + // A program may specialize this template to derive from true_type for a + // user-defined type T that does not have a nested allocator_type but + // nonetheless can be constructed with an allocator where either: + // - the first argument of a constructor has type allocator_arg_t and + // the second argument has type Allocator. + // or + // - the last argument of a constructor has type Allocator. + // + // Example behavilor: + // uses_allocator::value => true + // uses_allocator::value => false + // + // This is useful for writing generic code for containers when you can't + // know ahead of time that the container has an allocator_type. + /////////////////////////////////////////////////////////////////////// + + template + struct has_allocator_type_helper + { + private: + template + static eastl::no_type test(...); + + template + static eastl::yes_type test(typename U::allocator_type* = NULL); + + public: + static const bool value = sizeof(test(NULL)) == sizeof(eastl::yes_type); + }; + + + template ::value> + struct uses_allocator_impl + : public integral_constant::value> + { + }; + + template + struct uses_allocator_impl + : public eastl::false_type + { + }; + + template + struct uses_allocator + : public uses_allocator_impl{ }; + + + + + + /////////////////////////////////////////////////////////////////////// + // pointer_traits + // + // C++11 Standard section 20.6.3 + // Provides information about a pointer type, mostly for the purpose + // of handling the case where the pointer type isn't a built-in T* but + // rather is a class that acts like a pointer. + // + // A user-defined Pointer has the following properties, by example: + // template + // struct Pointer + // { + // typedef Pointer pointer; // required for use by pointer_traits. + // typedef T1 element_type; // optional for use by pointer_traits. + // typedef T2 difference_type; // optional for use by pointer_traits. + // + // template + // using rebind = typename Ptr; // optional for use by pointer_traits. + // + // static pointer pointer_to(element_type& obj); // required for use by pointer_traits. + // }; + // + // + // Example usage: + // template + // typename pointer_traits::element_type& GetElementPointedTo(Pointer p) + // { return *p; } + // + /////////////////////////////////////////////////////////////////////// + + namespace Internal + { + // pointer_element_type + template + struct has_element_type // has_element_type::value is true if T has an element_type member typedef. + { + private: + template static eastl::no_type test(...); + template static eastl::yes_type test(typename U::element_type* = 0); + public: + static const bool value = sizeof(test(0)) == sizeof(eastl::yes_type); + }; + + template ::value> + struct pointer_element_type + { + using type = Pointer; + }; + + template + struct pointer_element_type + { typedef typename Pointer::element_type type; }; + + template

+ + + + + Win32.VC71.MS.Debug