admin
2022-11-05 3680049029e6a193eae069596be04ce0fb2b1303
'增加l2稳定性'
127个文件已添加
22个文件已修改
16708 ■■■■■ 已修改文件
.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/ConsoleApplication.vcxproj 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/ImgUtil.cpp 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/L2DataCapture.cpp 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/L2DataCapture.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/LogUtil.cpp 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/LogUtil.h 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/OpenCLExcuter.cpp 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/OpenCLExcuter.h 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/RecognitionManager.cpp 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/THSActionUtil.cpp 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/THSActionUtil.h 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/THSL2RepairTool.cpp 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/jemallocd.dll 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/kernel.cl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/logs/l2_2022_11_04.log 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/main.cpp 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/test_log4cpp1.log 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Setup1/Setup1.vdproj 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/app.aps 补丁 | 查看 | 原始文档 | blame | 历史
app/app.rc 补丁 | 查看 | 原始文档 | blame | 历史
app/app.vcxproj 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/app.vcxproj.user 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/appDlg.cpp 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/jemallocd.dll 补丁 | 查看 | 原始文档 | blame | 历史
app/user.txt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
dependency/jemallocd.dll 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/arena_externs.h 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/arena_inlines_a.h 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/arena_inlines_b.h 427 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/arena_stats.h 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/arena_structs_a.h 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/arena_structs_b.h 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/arena_types.h 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/assert.h 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/atomic.h 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/atomic_c11.h 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/atomic_gcc_atomic.h 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/atomic_gcc_sync.h 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/atomic_msvc.h 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/background_thread_externs.h 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/background_thread_inlines.h 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/background_thread_structs.h 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/base_externs.h 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/base_inlines.h 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/base_structs.h 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/base_types.h 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/bin.h 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/bin_stats.h 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/bin_types.h 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/bit_util.h 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/bitmap.h 369 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/cache_bin.h 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/ckh.h 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/ctl.h 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/div.h 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/emitter.h 486 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/extent_dss.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/extent_externs.h 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/extent_inlines.h 501 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/extent_mmap.h 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/extent_structs.h 256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/extent_types.h 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/hash.h 319 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/hook.h 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_decls.h 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_defs.h 367 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_defs.h.in 366 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_externs.h 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_includes.h 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_inlines_a.h 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_inlines_b.h 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_inlines_c.h 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_macros.h 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_types.h 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_preamble.h 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/jemalloc_preamble.h.in 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/large_externs.h 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/log.h 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/malloc_io.h 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/mutex.h 288 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/mutex_pool.h 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/mutex_prof.h 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/nstime.h 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/pages.h 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/ph.h 391 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/private_namespace.sh 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/private_symbols.awk 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/private_symbols.sh 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/private_symbols_jet.awk 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/prng.h 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/prof_externs.h 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/prof_inlines_a.h 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/prof_inlines_b.h 250 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/prof_structs.h 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/prof_types.h 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/public_namespace.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/public_namespace.sh 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/public_symbols.txt 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/public_unnamespace.h 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/public_unnamespace.sh 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/ql.h 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/qr.h 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/quantum.h 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/rb.h 1006 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/rtree.h 528 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/rtree_tsd.h 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/safety_check.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/sc.h 333 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/seq.h 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/smoothstep.h 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/smoothstep.sh 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/spin.h 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/stats.h 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/sz.h 318 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tcache_externs.h 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tcache_inlines.h 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tcache_structs.h 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tcache_types.h 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/test_hooks.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/ticker.h 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tsd.h 415 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tsd_generic.h 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tsd_malloc_thread_cleanup.h 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tsd_tls.h 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tsd_types.h 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/tsd_win.h 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/util.h 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/internal/witness.h 347 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc.h 428 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc.sh 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_defs.h 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_defs.h.in 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_macros.h 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_macros.h.in 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_mangle.h 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_mangle.sh 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_mangle_jet.h 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_protos.h 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_protos.h.in 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_protos_jet.h 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_rename.h 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_rename.sh 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_typedefs.h 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/jemalloc/jemalloc_typedefs.h.in 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/msvc_compat/C99/stdbool.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/msvc_compat/C99/stdint.h 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/msvc_compat/strings.h 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jemalloc/x64/include/msvc_compat/windows_extra.h 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -2,6 +2,7 @@
*.ldb
*.sln
*.config
*.exe
Debug/
Release/
obj/
ConsoleApplication/ConsoleApplication.vcxproj
@@ -119,14 +119,14 @@
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <SDLCheck>false</SDLCheck>
      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <PreprocessorDefinitions>JEMALLOC_EXPORT=;JEMALLOC_STATIC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
      <AdditionalIncludeDirectories>D:\Program Files (x86)\OCL_SDK_Light\include</AdditionalIncludeDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <AdditionalDependencies>opencv_world455d.lib;OpenCL.lib;%(AdditionalDependencies)</AdditionalDependencies>
      <AdditionalDependencies>opencv_world455d.lib;OpenCL.lib;jemallocd.lib;%(AdditionalDependencies)</AdditionalDependencies>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
ConsoleApplication/ImgUtil.cpp
@@ -390,7 +390,6 @@
        int start = **ele;
        int end = *(*ele + 1);
        LogUtil::debug("start-%d end-%d", start, end);
        free(*ele);
        cv::Mat img = cv::Mat(binary, cv::Rect(start, 0, end - start + 1, rows));
        resultList.push_back(img);
@@ -568,7 +567,6 @@
list<int*> ImgUtil::divideImg(cv::Mat img, bool save) {
    //cv::imwrite("C:\\Users\\Administrator\\Desktop\\ocr\\demo\\0_gray.jpg", img);
    clock_t start_time = clock();
    LogUtil::info("开始分隔图像");
    list<list<int*>> resultList;
    int  cols = img.cols - 1;
@@ -580,7 +578,6 @@
    for (int i = 10;i < rows;i++) {
        bool full = ImgDivider::isRowFull(img, i);
        if (full) {
            LogUtil::debug("满数据行标:%d ", i);
            if (startf < 0)
            {
                startf = i;
@@ -593,7 +590,6 @@
        else {
            if (startf > -1 && endf > -1) {
                int width = endf - startf + 1;
                LogUtil::debug("标题分隔线:%d  分割线宽:%d\n", i, width);
                contentStartRow = i;
                break;
            }
@@ -606,8 +602,6 @@
        throw string("图像分隔出错:title分隔出错");
    }
    LogUtil::debug("\n\n\n\n");
    LogUtil::info("分隔标题完成");
    //分隔每一列数据
    startf = -1;
@@ -621,7 +615,6 @@
        bool full = ImgDivider::isColFull(img, i, contentStartRow);
        if (full) {
            LogUtil::debug("满数据列标:%d ", i);
            if (startf < 0)
            {
                startf = i;
@@ -634,12 +627,10 @@
        else {
            if (startf > -1 && endf > -1) {
                int width = endf - startf + 1;
                LogUtil::debug("列数据分隔线:%d  分割线宽:%d\n", i, width);
                int* dd = (int*)malloc(sizeof(int) * 2);
                *dd = startIndex;
                *(dd + 1) = startf - 1;
                LogUtil::debug("列数据:%d-%d\n", *dd, *(dd + 1));
                dataColIndexs.push_back(dd);
                startIndex = i;
@@ -656,7 +647,6 @@
        *dd = startIndex;
        *(dd + 1) = startf - 1;
        LogUtil::debug("列数据:%d-%d\n", *dd, *(dd + 1));
        dataColIndexs.push_back(dd);
@@ -664,7 +654,6 @@
        endf = -1;
    }
    LogUtil::info("列数据分割完成");
    //分隔每一行的数据
    std::list<int*> dataItemList;
@@ -720,7 +709,6 @@
            }
        }
        LogUtil::debug("内容数量:%d \n", dataCount);
    }
@@ -738,7 +726,6 @@
        int startCol = *(*ele + 1);
        int endRow = *(*ele + 2);
        int endCol = *(*ele + 3);
        LogUtil::debug("%d %d %d %d\n", startRow, startCol, endRow, endCol);
        //保存行数据
        if (save) {
@@ -804,8 +791,6 @@
        LogUtil::debug("行数据数量:%d\n", rowDataSize);
        //如果没有满7个数据,需要在中间补空数据
        if (rowDataSize < 7) {
            //补买撤 ,补涨停
@@ -839,7 +824,6 @@
        resultList.push_back(rowDataList);
        LogUtil::debug("%d行数据量:%zd\n", index, rowDataList.size());
        //保存数据
        if (save)
ConsoleApplication/L2DataCapture.cpp
@@ -5,6 +5,11 @@
#include <thread>
#include "TaskChecker.h"
#include "GPUtil.h"
#include "LogUtil.h"
//#define malloc(size) malloc(size)
//#define free(ptr) free(ptr)
bool L2DataCapture::inited;
bool L2DataCapture::tradeTimeCapture;
@@ -25,8 +30,6 @@
string L2DataCapture::gpCodes[THS_FRAME_COUNT];
void* L2DataCapture::context;
//运行
void L2DataCapture::_run(int index)
@@ -49,13 +52,32 @@
            try {
                clock_t start = clock();
                cv::Mat mat = CaptureUtil::capture(index, CAPTURE_TYPE_L2);
                if (mat.cols <= 400 || mat.rows <= 1800) {
                    mat.release();
                    mat = NULL;
                    Sleep(100);
                    throw string("截图出错");
                }
                list<TradeData> resultList = captureLevel2TradeData(mat, index);
                long processTime = clock() - start;
                data_callback(index, code, start , processTime, resultList, context);
                cout << "时间消耗:" << processTime << endl;
            }
            catch (string st) {
               //delete (openCLExcuter[index]);
               //openCLExcuter[index] = new OpenCLExcuter();
               //openCLExcuter[index]->init();
                LogUtil::getInstance()->getL2Logger().error(string("识别出错:").append(st));
            }
            catch (int error_code) {
                LogUtil::getInstance()->getL2Logger().error(string("识别出错:code-").append(to_string(error_code)));
            }
            catch (exception& e) {
                LogUtil::getInstance()->getL2Logger().error(string("识别出错:").append(e.what()));
            }
            catch (...) {
                LogUtil::getInstance()->getL2Logger().error(string("识别出错:").append("未知错误"));
            }
        }
        Sleep(2);
@@ -189,7 +211,7 @@
    return inited;
}
list<TradeData>  L2DataCapture::captureLevel2TradeData(cv::Mat oimg, int identify) {
list<TradeData>  L2DataCapture::captureLevel2TradeData(cv::Mat& oimg, int identify) {
    if (oimg.rows == 0 || oimg.cols == 0) {
        throw ERROR_CODE_CAPTURE_FAIL;
    }
@@ -203,17 +225,20 @@
    return resultList;
}
list<TradeData> L2DataCapture::captureLevel2TradeData(OpenCLExcuter* openCLExcuter, cv::Mat oimg, int identify)
list<TradeData> L2DataCapture::captureLevel2TradeData(OpenCLExcuter* openCLExcuter, cv::Mat& oimg, int identify)
{
    if (oimg.cols <= 0 || oimg.rows <= 0) {
        throw string("图像数据错误");
    }
    //LogUtil::debug("截图完成");
    clock_t time_1 = clock();
    //std::cout << "截图完成: threadid-" << std::this_thread::get_id() << " 耗时:" << (time_1 - starttime) << endl;
    //灰度化
    cv::Mat img = cv::Mat::zeros(oimg.rows, oimg.cols, CV_8UC1);//ImgUtil::grayImage(oimg);
    cv::Mat img = cv::Mat::zeros(oimg.rows, oimg.cols, CV_8UC1);
    uchar* imgData = (uchar*)malloc(sizeof(uchar) * oimg.rows * oimg.cols);
    //
    if (oimg.channels() == 1) {
        //黑白图片
@@ -223,26 +248,21 @@
        try {
            if (oimg.channels() == 3)
            {
                openCLExcuter->rgb2Gray(oimg, imgData);
                openCLExcuter->rgb2Gray(oimg, img.data);
            }
            else {
                openCLExcuter->rgba2Gray(oimg, imgData);
                openCLExcuter->rgba2Gray(oimg, img.data);
            }
            oimg.release();
        }
        catch (...) {
            cv::imwrite("D:\\imgs\\l2.png", oimg);
            if (imgData != NULL)
            {
                free(imgData);
            }
            imgData = NULL;
            oimg.release();
            oimg = NULL;
            img.release();
            img = NULL;
            throw string("灰度出错");
        }
        img.data = imgData;
        oimg.release();
        //img.data = imgData;
        clock_t time_2_ = clock();
        if (identify == 0)
        {
@@ -267,10 +287,8 @@
        }
    }
    catch (...) {
        //释放内存
        free(imgData);
        imgData = NULL;
        img.release();
        img = NULL;
        throw int(ERROR_CODE_DIVIDE_IMG_FAIL);
    }
    //一维行图像数据
@@ -293,9 +311,10 @@
        free(rowDataOneLevel);
    }
    catch (...) {
        img.release();
        img = NULL;
        free(rowDataOneLevel);
        free(rowSplitDataOneLevel);
        free(imgData);
        throw string("图片分隔出错");
    }
    
@@ -474,10 +493,17 @@
    clock_t time_33 = clock();
    if (identify == 0)
        std::cout << "数据准备-0数据准备: threadid-" << std::this_thread::get_id() << " 耗时:" << time_33 - time_32 << "总耗时:" << time_33 - time_1 << endl;
    openCLExcuter->splitL2Num(imgData, img.cols, img.rows, pos, 4 * rowDataList.size(), zeroData, _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER, totalNumberData);
    try {
        openCLExcuter->splitL2Num(img.data, img.cols, img.rows, pos, 4 * rowDataList.size(), zeroData, _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER, totalNumberData);
    free(zeroData);
    free(pos);
    }
    catch (...) {
        free(zeroData);
        free(pos);
        free(totalNumberData);
        throw string("splitL2Num出错");
    }
@@ -496,8 +522,15 @@
    //准备模板数字
    uchar* templateNums = (unsigned char*)malloc(sizeof(unsigned char) * (_NUMBER_L2_HEIGHT * rowDataList.size()) * _NUMBER_L2_WIDTH * 10 * _NUMBER_L2_TOTAL_NUMBER);
    try {
    openCLExcuter->createNumberTemplates(rowDataList.size(), _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER, ImgUtil::numsOneLevel_level2, templateNums);
    }
    catch (...) {
        free(totalNumberData);
        free(templateNums);
        throw string("创建数据模板出错");
    }
    //ImgUtil::createTemplateNumData(data.size());
    clock_t time_4 = clock();
@@ -520,12 +553,21 @@
    }
    */
    uchar** numberResult=nullptr;
    //数字识别
    uchar** numberResult = openCLExcuter->recognition_numbers(totalNumberData, templateNums, rowDataList.size() * _NUMBER_L2_HEIGHT, _NUMBER_L2_WIDTH * 10 * _NUMBER_L2_TOTAL_NUMBER, _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER);
    try {
         numberResult = openCLExcuter->recognition_numbers(totalNumberData, templateNums, rowDataList.size() * _NUMBER_L2_HEIGHT, _NUMBER_L2_WIDTH * 10 * _NUMBER_L2_TOTAL_NUMBER, _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER);
    //释放内存
    free(totalNumberData);
    free(templateNums);
    }
    catch (...) {
        //释放内存
        free(totalNumberData);
        free(templateNums);
        throw string("数字识别出错");
    }
    //循环读取数字
    list<TradeData>::iterator tradeEle;
    index = 0;
@@ -571,8 +613,6 @@
        std::cout << "数字识别完成: threadid-" << std::this_thread::get_id() << " 耗时:" << time_6 - time_5 << "总耗时:" << time_6 - time_1 << endl;
    //释放内存
    //img.release();
    free(imgData);
    img.release();
    rowDataList.clear();
    list<int*>().swap(rowDataList);
ConsoleApplication/L2DataCapture.h
@@ -76,8 +76,8 @@
    //捕获level2的盘口数据
    static list<TradeData>  captureLevel2TradeData(HWND hwnd,int index) throw(int);
    static list<TradeData>  captureLevel2TradeData(cv::Mat oimg,int identify);
    static list<TradeData>  captureLevel2TradeData(OpenCLExcuter *openCLExcuter, cv::Mat oimg, int identify);
    static list<TradeData>  captureLevel2TradeData(cv::Mat& oimg,int identify);
    static list<TradeData>  captureLevel2TradeData(OpenCLExcuter *openCLExcuter, cv::Mat& oimg, int identify);
    //设置代码
    static void setGPCode(int index, string code);
    //获取代码
ConsoleApplication/LogUtil.cpp
@@ -1,18 +1,44 @@
#include "LogUtil.h"
#include <string>
#include <time.h>
 void LogUtil::info(std::string format, ...) {
     return;
     va_list args;
     va_start(args, format);
LogUtil* LogUtil::instance;
     SYSTEMTIME sys;
     GetLocalTime(&sys);
     std::string formatFull = "%4d/%02d/%02d %02d:%02d:%02d.%03d INFO ";
     formatFull.append(format);
     formatFull.append("\n");
     printf(formatFull.c_str(), sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond, sys.wMilliseconds, args);
LogUtil::LogUtil()
{
    time_t t = time(0);
    char tmp[64];
    strftime(tmp, sizeof(tmp), "%Y_%m_%d", localtime(&t));
    log4cpp::PatternLayout* pLayout = new log4cpp::PatternLayout();
    pLayout->setConversionPattern("%d: %p %c %x: %m%n");
    // L2 对象
    string path = string("C:/Program Files (x86)/yeshi/GP/logs/l2_").append(string(tmp)).append(".log");
    log4cpp::Appender* appender = new log4cpp::FileAppender("FileAppender", path);
    // 3. 把layout对象附着在appender对象上
    appender->setLayout(pLayout);
    log4cpp::Category&  l2_log =  log4cpp::Category::getInstance("l2");
    // 5. 设置additivity为false,替换已有的appender
    l2_log.setAdditivity(false);
    // 5. 把appender对象附到category上
    l2_log.setAppender(appender);
    // 6. 设置category的优先级,低于此优先级的日志不被记录
    l2_log.setPriority(log4cpp::Priority::INFO);
}
LogUtil* LogUtil::getInstance()
{
    if (instance == nullptr) {
        instance = new LogUtil();
    }
    return instance;
}
log4cpp::Category& LogUtil::getL2Logger()
{
    return log4cpp::Category::getInstance("l2");
}
 void  LogUtil::debug(const char* format, ...) {
     return;
     va_list args;
@@ -24,13 +50,5 @@
     formatFull.append("\n");
     printf(formatFull.c_str(), sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond, sys.wMilliseconds, args);
}
 void  LogUtil::error(const char* format, ...) {
     va_list args;
     va_start(args, format);
     SYSTEMTIME sys;
     GetLocalTime(&sys);
     std::string formatFull = "%4d/%02d/%02d %02d:%02d:%02d.%03d ERROR ";
     formatFull.append(format);
     formatFull.append("\n");
     printf(formatFull.c_str(), sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond, sys.wMilliseconds, args);
}
ConsoleApplication/LogUtil.h
@@ -1,13 +1,22 @@
#pragma once
#include <iostream>
#include <Windows.h>
#include "log4cpp/Category.hh"
#include "log4cpp/FileAppender.hh"
#include "log4cpp/StringQueueAppender.hh"
#include "log4cpp/BasicLayout.hh"
#include <log4cpp/PatternLayout.hh>
using namespace std;
class LogUtil
{
private:
    static LogUtil* instance;
public:
    static void info(std::string format, ...);
    LogUtil();
    static LogUtil* getInstance();
    log4cpp::Category& getL2Logger();
    static void debug(const char* format, ...);
    static void error(const char* format, ...);
};
ConsoleApplication/OpenCLExcuter.cpp
@@ -52,6 +52,96 @@
string OpenCLExcuter::rootPath;
void OpenCLExcuter::reCreateQueue()
{
    //获取平台
    error = clGetPlatformIDs(1, &platforms, &num_of_platforms);
    if (error != 0) {
        throw string("Get platform failed!");
    }
    error = clGetDeviceIDs(platforms, CL_DEVICE_TYPE_GPU, 1, &devices, NULL);
    if (error != 0) {
        error = clGetDeviceIDs(platforms, CL_DEVICE_TYPE_CPU, 1, &devices, NULL);
        if (error != CL_SUCCESS) {
            throw string("Get device failed!");
        }
    }
    //创建上下文
    context = clCreateContext(NULL, 1, &devices, NULL, NULL, &error);
    if (error != CL_SUCCESS) {
        throw string("Creat context failed!");
    }
    //创建程序;注意要用"rb"
    string kernel_path = "";
    if (rootPath.length() > 0) {
        kernel_path = kernel_path.append(rootPath);
    }
    kernel_path.append("kernel.cl");
    cout << "kernel_path:" << kernel_path << endl;
    fopen_s(&program_handle, kernel_path.c_str(), "rb");
    if (program_handle == NULL) {
        throw string("The kernle can not be opened!");
    }
    fseek(program_handle, 0, SEEK_END);
    program_size = ftell(program_handle);
    rewind(program_handle);
    program_buffer = (char*)malloc(program_size + 1);
    program_buffer[program_size] = '\0';
    error = fread(program_buffer, sizeof(char), program_size, program_handle);
    if (error == 0) {
        throw string("Read kernel failed!");
    }
    fclose(program_handle);
    program = clCreateProgramWithSource(context, 1, (const char**)&program_buffer,
        &program_size, &error);
    if (error < 0) {
        program = clCreateProgramWithSource(context, 0, (const char**)&program_buffer,
            &program_size, &error);
        if (error < 0) {
            string st = "Couldn't create the program!";
            st.append("  error:").append(to_string(error));
            throw st;
        }
    }
    //编译程序
    error = clBuildProgram(program, 1, &devices, NULL, NULL, NULL);
    if (error < 0) {
        //确定日志文件的大小
        clGetProgramBuildInfo(program, devices, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);
        program_log = (char*)malloc(log_size + 1);
        program_log[log_size] = '\0';
        //读取日志
        clGetProgramBuildInfo(program, devices, CL_PROGRAM_BUILD_LOG,
            log_size + 1, program_log, NULL);
        printf("%s\n", program_log);
        free(program_log);
        throw string("Build Program Failed");
    }
    free(program_buffer);
    //创建命令队列
    //queue = clCreateCommandQueue(context, devices, CL_QUEUE_PROFILING_ENABLE, &error);
    queue = clCreateCommandQueueWithProperties(context, devices, 0, &error);
    if (error < 0) {
        queue = clCreateCommandQueue(context, devices, CL_QUEUE_PROFILING_ENABLE, &error);
        if (error < 0) {
            throw string("Coudn't create the command queue");
        }
    }
}
void OpenCLExcuter::excuteError()
{
    destory();
    reCreateQueue();
}
void OpenCLExcuter::init() {
    showDeviceInfo();
    //获取平台
@@ -68,18 +158,19 @@
    //    }
    //}
    devices = nullptr;
    error = clGetDeviceIDs(platforms, CL_DEVICE_TYPE_GPU , 1, &devices, NULL);
    if (error != 0) {
        error = clGetDeviceIDs(platforms, CL_DEVICE_TYPE_CPU, 1, &devices, NULL);
        if (error != 0) {
        if (error != CL_SUCCESS) {
            throw string("Get device failed!");
        }
    }
    //创建上下文
    context = clCreateContext(NULL, 1, &devices, NULL, NULL, &error);
    if (error != 0) {
    if (error != CL_SUCCESS) {
        throw string("Creat context failed!");
    }
    //创建程序;注意要用"rb"
@@ -187,10 +278,10 @@
    error |= clSetKernelArg(kernel, 5, sizeof(int), &line_number_count);
    error |= clSetKernelArg(kernel, 6, sizeof(cl_mem), &memObject4);
    if (error != CL_SUCCESS) {
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject4);
        clReleaseKernel(kernel);
        throw string("Error setting kernel arguments!\n");
    }
    //执行内核
@@ -202,10 +293,10 @@
    error = clEnqueueNDRangeKernel(queue, kernel, work_dim, NULL, globalWorkSize,
        localWorkSize, 0, NULL, NULL);
    if (error != CL_SUCCESS) {
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject4);
        clReleaseKernel(kernel);
        throw string("Error queuing kernel for execution!\n");
    }
@@ -215,10 +306,10 @@
        result, 0, NULL, NULL);
    if (error != CL_SUCCESS) {
        free(result);
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject4);
        clReleaseKernel(kernel);
        throw string("Error reading result buffer!\n");
    }
    /*
@@ -263,12 +354,12 @@
        localWorkSize, 0, NULL, NULL);
    if (error != CL_SUCCESS) {
        free(result);
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject4);
        clReleaseMemObject(memObject21);
        clReleaseMemObject(memObject22);
        clReleaseKernel(kernel);
        throw string("Error queuing kernel for execution!\n");
    }
@@ -279,12 +370,12 @@
    if (error != CL_SUCCESS) {
        free(result);
        free(result2);
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject4);
        clReleaseMemObject(memObject21);
        clReleaseMemObject(memObject22);
        clReleaseKernel(kernel);
        throw string("Error reading result buffer!\n");
    }
@@ -525,6 +616,7 @@
        clReleaseMemObject(memObject3);
        clReleaseMemObject(memObject4);
        clReleaseMemObject(memObject5);
        excuteError();
        throw st;
    }
    std::cout << " 耗时:" << (clock() - time_0) << std::endl;
@@ -627,6 +719,7 @@
        clReleaseMemObject(memObject3);
        clReleaseMemObject(memObject4);
        clReleaseMemObject(memObject5);
        excuteError();
        throw st;
    }
@@ -741,6 +834,7 @@
        clReleaseMemObject(memObject3);
        clReleaseMemObject(memObject4);
        clReleaseMemObject(memObject5);
        excuteError();
        throw st;
    }
@@ -847,6 +941,7 @@
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject3);
        clReleaseMemObject(memObject4);
        excuteError();
        throw st;
    }
}
@@ -916,6 +1011,7 @@
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        excuteError();
        throw st;
    }
@@ -993,6 +1089,7 @@
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject3);
        excuteError();
        throw st;
    }
@@ -1075,6 +1172,7 @@
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject3);
        excuteError();
        throw st;
    }
@@ -1156,6 +1254,7 @@
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject3);
        excuteError();
        throw st;
    }
@@ -1240,6 +1339,7 @@
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseMemObject(memObject3);
        excuteError();
        throw st;
    }
@@ -1311,6 +1411,7 @@
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        excuteError();
        throw st;
    }
@@ -1372,14 +1473,16 @@
            throw string("Error reading result buffer!\n");
        }
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseKernel(kernel);
    }
    catch (string st) {
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        clReleaseKernel(kernel);
        excuteError();
        throw st;
    }
@@ -1454,6 +1557,7 @@
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        excuteError();
        throw st;
    }
@@ -1522,6 +1626,7 @@
        clReleaseKernel(kernel);
        clReleaseMemObject(memObject1);
        clReleaseMemObject(memObject2);
        excuteError();
        throw st;
    }
@@ -1532,8 +1637,8 @@
void OpenCLExcuter::destory() {
    //释放资源
    clReleaseDevice(devices);
    clReleaseContext(context);
    clReleaseProgram(program);
    clReleaseCommandQueue(queue);
    clReleaseProgram(program);
    clReleaseContext(context);
    clReleaseDevice(devices);
}
ConsoleApplication/OpenCLExcuter.h
@@ -50,6 +50,11 @@
    cl_command_queue queue;
    //重新创建队列
    void reCreateQueue();
    //执行出错
    void excuteError();
public:
    static string rootPath;
    void init();
ConsoleApplication/RecognitionManager.cpp
@@ -119,7 +119,6 @@
std::string RecognitionManager::recognitionTime(cv::Mat src) {
    //打印出数据
    cv::Mat binary;
    LogUtil::debug("分隔开始", NULL);
    //去除上下的空白图
    threshold(src, binary, 20, 255, cv::THRESH_BINARY);
    int rows = src.rows;
ConsoleApplication/THSActionUtil.cpp
@@ -1337,6 +1337,7 @@
    SendMessage(close, WM_CLOSE, 0, 0);
}
std::list<GPCodeArea> THSActionUtil::getListenL2GPAreaAndCode(RecognitionManager* recognitionManager) {
    //截图当前有哪些股票代码
    list<HWND> wlist = Win32Util::searchWindow("同花顺(");
@@ -1364,6 +1365,7 @@
    //截图
    cv::Mat oimg = CaptureUtil::capture(content);
    cv::Mat img = ImgUtil::grayImage(oimg);
    oimg.release();
    //分隔图片
@@ -1420,6 +1422,124 @@
}
// 获取操作区域
std::list<std::list<GPCodeArea>> THSActionUtil::getListenL2GPAreaActionBar() {
    //截图当前有哪些股票代码
    list<HWND> wlist = Win32Util::searchWindow("同花顺(");
    HWND mainPage = 0;
    list<HWND>::iterator ele;
    for (ele = wlist.begin(); ele != wlist.end(); ele++) {
        HWND hwnd = *ele;
        string str = Win32Util::getWindowName(hwnd);
        if (isL2Screen(str)) {
            cout << hwnd << endl;
            //获取尺寸
            RECT rc;
            GetWindowRect(hwnd, &rc);
            if (rc.right - rc.left > 200 && rc.bottom - rc.top > 200) {
                mainPage = hwnd;
                break;
            }
        }
    }
    if (mainPage <= 0) {
        throw string("L2监听未打开(25)");
    }
    HWND content = FindWindowExA(mainPage, NULL, "AfxFrameOrView100s", NULL);
    //截图
    cv::Mat oimg = CaptureUtil::capture(content);
    cv::Mat img = ImgUtil::grayImage(oimg);
    oimg.release();
    //分隔图片
    std::list<GPCodeArea>  areaList = splitL2Cate(img);
    std::list<GPCodeArea> fresultList;
    int index = 0;
    for (std::list<GPCodeArea>::iterator ele = areaList.begin(); ele != areaList.end(); ele++) {
        GPCodeArea area = *ele;
        int startRow = -1;
        int endRow = -1;
        for (int r = area.endy; r >= area.starty; r--) {
            if (!ImgDivider::isRowEmpty(img, r, area.startx,  (area.endx + area.startx)/2)) {
                if (startRow < 0)
                {
                    startRow = r;
                    endRow = r;
                }
                endRow = r;
            }
            else {
                if (startRow > 0 && endRow > 0) {
                    if (startRow - endRow > 10) {
                        GPCodeArea farea;
                        farea.startx = area.startx;
                        farea.endx = area.endx;
                        farea.starty = endRow;
                        farea.endy = startRow;
                        fresultList.push_back(farea);
                        // TODO 暂时打开
                        if (false) {
                            string path = "C:\\Users\\Administrator\\Desktop\\ocr\\gpcode\\";
                            path.append("cate_").append(to_string(index)).append(".jpg");
                            cv::imwrite(path, cv::Mat(img, cv::Rect(farea.startx, farea.starty, farea.endx - farea.startx + 1, farea.endy - farea.starty + 1)));
                        }
                        break;
                    }
                    else {
                        startRow = endRow;
                    }
                }
            }
        }
        index++;
    }
    index = 0;
    std::list<list<GPCodeArea>> ffresultList;
    //切割每一块的菜单
    for (std::list<GPCodeArea>::iterator ele = fresultList.begin(); ele != fresultList.end(); ele++) {
        GPCodeArea area = *ele;
        int start_col = area.startx;
        list<GPCodeArea> tempList;
        for (int c = area.startx; c <= area.endx; c++) {
            //通过第一行的纯黑点做分隔
            if (img.ptr<uchar>(area.starty)[c] <= 5) {
                if (c - start_col > 10) {
                    //获取到分隔点
                    GPCodeArea child;
                    child.startx = start_col;
                    child.endx = c -1;
                    child.starty = area.starty;
                    child.endy = area.endy;
                    tempList.push_back(child);
                    start_col = c;
                    if (false) {
                        string path = "C:\\Users\\Administrator\\Desktop\\ocr\\gpcode\\";
                        path.append("cate_").append(to_string(index)).append("_").append(to_string(c)).append(".jpg");
                        cv::imwrite(path, cv::Mat(img, cv::Rect(child.startx, child.starty, child.endx - child.startx + 1, child.endy - child.starty + 1)));
                    }
                }
            }
        }
        index++;
        ffresultList.push_back(tempList);
    }
    return ffresultList;
}
cv::Mat getTHSTimeCapture() {
    HWND hwnd = THSActionUtil::getMainWin();
    if (hwnd <= 0) {
ConsoleApplication/THSActionUtil.h
@@ -100,12 +100,13 @@
    //识别板块内容区域
    static GPCodeArea recognitionPlateContentArea(cv::Mat grayImg);
    //添加目标标的
    static void addTargetCodes(list<std::string> codeList, RecognitionManager* recognitionManager);
    static std::list<GPCodeArea> getListenL2GPAreaAndCode(RecognitionManager* recognitionManager);
    static std::list<std::list<GPCodeArea>> getListenL2GPAreaActionBar();
    //同花顺是否卡死
    static bool thsIsDead();
ConsoleApplication/THSL2RepairTool.cpp
@@ -13,7 +13,8 @@
    try {
        list<TradeData> dataList;
        try {
            dataList = L2DataCapture::captureLevel2TradeData(openCLExcuter, CaptureUtil::capture(index, CAPTURE_TYPE_L2), index);
            cv::Mat mat = CaptureUtil::capture(index, CAPTURE_TYPE_L2);
            dataList = L2DataCapture::captureLevel2TradeData(openCLExcuter, mat, index);
        }
        catch (int c) {
            cout << "获取L2数据出错:code-" << c << endl;
@@ -31,7 +32,8 @@
            Win32Util::rollMouseWheel(true, win, rect.left + 20, rect.top + 10);
            Sleep(500);
            try {
                dataList = L2DataCapture::captureLevel2TradeData(openCLExcuter, CaptureUtil::capture(index, CAPTURE_TYPE_L2), index);
                cv::Mat mat = CaptureUtil::capture(index, CAPTURE_TYPE_L2);
                dataList = L2DataCapture::captureLevel2TradeData(openCLExcuter, mat, index);
                cout << "获取到的数据:" << dataList.size() << endl;
            }
            catch (int c) {
ConsoleApplication/jemallocd.dll
Binary files differ
ConsoleApplication/kernel.cl
@@ -2740,6 +2740,7 @@
        unsigned char G = imgs[start + 1];
        unsigned char B = imgs[start + 2];
        result[index] = (76 * R + 150 * G + 30 * B) >> 8;
        //printf("%d\n", index);
    }
}
ConsoleApplication/logs/l2_2022_11_04.log
New file
@@ -0,0 +1,2 @@
2022-11-04 15:22:51,094: INFO l2 : test
2022-11-04 15:22:51,095: INFO l2 : 测试
ConsoleApplication/main.cpp
@@ -10,7 +10,9 @@
#include "log4cpp/FileAppender.hh"
#include "log4cpp/StringQueueAppender.hh"
#include "log4cpp/BasicLayout.hh"
#include <log4cpp/PatternLayout.hh>
#include "TradeQueueCaptureManager.h"
#include "LogUtil.h"
using namespace log4cpp;
void gray();
@@ -31,7 +33,7 @@
    {
        //同花顺(v9.10.50) - 深圳Level-2分时走势
        //同花顺(v9.10.50) - pppp
        list<TradeData>  map = creenDataCapture->captureLevel2TradeData(cv::imread("C:\\Users\\Administrator\\Desktop\\ocr\\demo\\0_388.jpg"), p);
        //list<TradeData>  map = creenDataCapture->captureLevel2TradeData(cv::imread("C:\\Users\\Administrator\\Desktop\\ocr\\demo\\0_388.jpg"), p);
        //break;
    }
@@ -129,7 +131,35 @@
    return stoi(string(tmp_h)) * 3600 + stoi(string(tmp_m)) * 60 + stoi(string(tmp_s));
}
int testLog() {
    LogUtil::getInstance()->getL2Logger().info("测试");
    return 0;
}
void testL2() {
    ImgUtil::init();
    OpenCLExcuter* openCLExcuter = new OpenCLExcuter();
    openCLExcuter->init();
    L2DataCapture* l2DataCapture = new L2DataCapture();
    try {
        ;
        // l2DataCapture->init(NULL, NULL, NULL);
    }
    catch (...) {
    }
    cv::Mat mat;
}
int main() {
    if (TRUE) {
        return 0;
    }
    cout << "开始程序" << endl;
    ImgUtil::init();
@@ -247,7 +277,6 @@
    imwrite("E:\\temp\\506518_gray.jpg", img);
    clock_t time_2 = clock();
    LogUtil::debug("灰度完成");
    //图像分割
    list<int*>  data;
    try {
ConsoleApplication/test_log4cpp1.log
New file
@@ -0,0 +1,2 @@
1667542422 WARN mywarn : 这是一个测试
2022-11-04 14:25:31,047: WARN mywarn : 这是一个测试
Setup1/Setup1.vdproj
@@ -2981,6 +2981,17 @@
                            }
                        }
                    }
                    "{9EF0B969-E518-4E46-987F-47570745A589}:_4DCCD85206654EBB9E62E6A20A7867F5"
                    {
                    "Name" = "8:logs"
                    "AlwaysCreate" = "11:FALSE"
                    "Condition" = "8:"
                    "Transitive" = "11:FALSE"
                    "Property" = "8:_80B7237F2163487087FEDD77E677EB86"
                        "Folders"
                        {
                        }
                    }
                }
            }
        }
app/app.aps
Binary files differ
app/app.rc
Binary files differ
app/app.vcxproj
@@ -77,7 +77,7 @@
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <LinkIncremental>true</LinkIncremental>
    <IncludePath>$(IncludePath);$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
    <IncludePath>$(IncludePath);$(VC_IncludePath);$(WindowsSDK_IncludePath);D:\workspace\CPlusTest\ConsoleApplication\jemalloc\x64\include;D:\workspace\CPlusTest\ConsoleApplication\jemalloc\x64\include\msvc_compat</IncludePath>
    <LibraryPath>$(LibraryPath)</LibraryPath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@@ -115,7 +115,8 @@
      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ResourceCompile>
    <PostBuildEvent>
      <Command>editbin /SUBSYSTEM:CONSOLE $(OutDir)\$(ProjectName).exe</Command>
      <Command>
      </Command>
    </PostBuildEvent>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
app/app.vcxproj.user
@@ -4,10 +4,13 @@
    <RESOURCE_FILE>app.rc</RESOURCE_FILE>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
    <RemoteDebuggerCommand>\\192.168.3.250\FXDMounter\Debug\DataCapture.exe</RemoteDebuggerCommand>
    <RemoteDebuggerWorkingDirectory>\\192.168.3.250\FXDMounter\</RemoteDebuggerWorkingDirectory>
    <RemoteDebuggerServerName>192.168.3.250</RemoteDebuggerServerName>
    <DebuggerFlavor>WindowsRemoteDebugger</DebuggerFlavor>
    <RemoteDebuggerCommand>\\192.168.3.241\FXDMounter\Debug\DataCapture.exe</RemoteDebuggerCommand>
    <RemoteDebuggerWorkingDirectory>\\192.168.3.241\FXDMounter\</RemoteDebuggerWorkingDirectory>
    <RemoteDebuggerServerName>192.168.3.241</RemoteDebuggerServerName>
    <RemoteDebuggerConnection>RemoteWithoutAuthentication</RemoteDebuggerConnection>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <DebuggerFlavor>WindowsRemoteDebugger</DebuggerFlavor>
  </PropertyGroup>
</Project>
app/appDlg.cpp
@@ -615,14 +615,19 @@
            //暂停涨停识别
            LimitUpCapture::pause = TRUE;
            //打开Level2监控屏幕
            THSActionUtil::openL2Screen();
            bool result = THSActionUtil::setGP(quickCode, codeList, recognitionManager);
            if (!result) {
                throw string("设置版块监控的GP失败");
            }
            //刷新L2数据
            THSActionUtil::setListenL2GP(index, code, recognitionManager);
            //打开Level2监控屏幕
            THSActionUtil::openL2Screen();
            map<int, string> results = THSActionUtil::getListenL2GPCodes(recognitionManager);
            //设置代码
            for (map<int, string>::iterator ele = results.begin();ele != results.end();ele++) {
@@ -1259,6 +1264,10 @@
        capture->start();
        capture->startAll();
        btn->SetWindowText(_T("暂停识别任务"));
        //TODO 测试图片切割
        //THSActionUtil::getListenL2GPAreaActionBar();
    }
    else {
        capture->stop();
@@ -1815,6 +1824,16 @@
void CappDlg::OnBnClickedButtonThs()
{
    // TODO 测试设置最小化
    //HWND main = THSActionUtil::getMainWin();
    //RECT rect;
    //Win32Util::getWindowRect(main,&rect);
    //Win32Util::moveWin(main,rect.left,rect.top,1800,1000);
    //return;
    try {
        bool result = THSActionUtil::thsIsDead();
        if (result) {
app/jemallocd.dll
Binary files differ
app/user.txt
@@ -1,3 +1,3 @@
auxiliary
admin
123456
0
dependency/jemallocd.dll
Binary files differ
jemalloc/x64/include/jemalloc/internal/arena_externs.h
New file
@@ -0,0 +1,104 @@
#ifndef JEMALLOC_INTERNAL_ARENA_EXTERNS_H
#define JEMALLOC_INTERNAL_ARENA_EXTERNS_H
#include "jemalloc/internal/bin.h"
#include "jemalloc/internal/extent_dss.h"
#include "jemalloc/internal/hook.h"
#include "jemalloc/internal/pages.h"
#include "jemalloc/internal/stats.h"
extern ssize_t opt_dirty_decay_ms;
extern ssize_t opt_muzzy_decay_ms;
extern percpu_arena_mode_t opt_percpu_arena;
extern const char *percpu_arena_mode_names[];
extern const uint64_t h_steps[SMOOTHSTEP_NSTEPS];
extern malloc_mutex_t arenas_lock;
extern size_t opt_oversize_threshold;
extern size_t oversize_threshold;
void arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena,
    unsigned *nthreads, const char **dss, ssize_t *dirty_decay_ms,
    ssize_t *muzzy_decay_ms, size_t *nactive, size_t *ndirty, size_t *nmuzzy);
void arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
    const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,
    size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,
    bin_stats_t *bstats, arena_stats_large_t *lstats,
    arena_stats_extents_t *estats);
void arena_extents_dirty_dalloc(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extent_t *extent);
#ifdef JEMALLOC_JET
size_t arena_slab_regind(extent_t *slab, szind_t binind, const void *ptr);
#endif
extent_t *arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena,
    size_t usize, size_t alignment, bool *zero);
void arena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena,
    extent_t *extent);
void arena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena,
    extent_t *extent, size_t oldsize);
void arena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena,
    extent_t *extent, size_t oldsize);
ssize_t arena_dirty_decay_ms_get(arena_t *arena);
bool arena_dirty_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);
ssize_t arena_muzzy_decay_ms_get(arena_t *arena);
bool arena_muzzy_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);
void arena_decay(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,
    bool all);
void arena_reset(tsd_t *tsd, arena_t *arena);
void arena_destroy(tsd_t *tsd, arena_t *arena);
void arena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
    cache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes);
void arena_alloc_junk_small(void *ptr, const bin_info_t *bin_info,
    bool zero);
typedef void (arena_dalloc_junk_small_t)(void *, const bin_info_t *);
extern arena_dalloc_junk_small_t *JET_MUTABLE arena_dalloc_junk_small;
void *arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size,
    szind_t ind, bool zero);
void *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize,
    size_t alignment, bool zero, tcache_t *tcache);
void arena_prof_promote(tsdn_t *tsdn, void *ptr, size_t usize);
void arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
    bool slow_path);
void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
    szind_t binind, extent_t *extent, void *ptr);
void arena_dalloc_small(tsdn_t *tsdn, void *ptr);
bool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
    size_t extra, bool zero, size_t *newsize);
void *arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
    size_t size, size_t alignment, bool zero, tcache_t *tcache,
    hook_ralloc_args_t *hook_args);
dss_prec_t arena_dss_prec_get(arena_t *arena);
bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
ssize_t arena_dirty_decay_ms_default_get(void);
bool arena_dirty_decay_ms_default_set(ssize_t decay_ms);
ssize_t arena_muzzy_decay_ms_default_get(void);
bool arena_muzzy_decay_ms_default_set(ssize_t decay_ms);
bool arena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena,
    size_t *old_limit, size_t *new_limit);
unsigned arena_nthreads_get(arena_t *arena, bool internal);
void arena_nthreads_inc(arena_t *arena, bool internal);
void arena_nthreads_dec(arena_t *arena, bool internal);
size_t arena_extent_sn_next(arena_t *arena);
arena_t *arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
bool arena_init_huge(void);
bool arena_is_huge(unsigned arena_ind);
arena_t *arena_choose_huge(tsd_t *tsd);
bin_t *arena_bin_choose_lock(tsdn_t *tsdn, arena_t *arena, szind_t binind,
    unsigned *binshard);
void arena_boot(sc_data_t *sc_data);
void arena_prefork0(tsdn_t *tsdn, arena_t *arena);
void arena_prefork1(tsdn_t *tsdn, arena_t *arena);
void arena_prefork2(tsdn_t *tsdn, arena_t *arena);
void arena_prefork3(tsdn_t *tsdn, arena_t *arena);
void arena_prefork4(tsdn_t *tsdn, arena_t *arena);
void arena_prefork5(tsdn_t *tsdn, arena_t *arena);
void arena_prefork6(tsdn_t *tsdn, arena_t *arena);
void arena_prefork7(tsdn_t *tsdn, arena_t *arena);
void arena_postfork_parent(tsdn_t *tsdn, arena_t *arena);
void arena_postfork_child(tsdn_t *tsdn, arena_t *arena);
#endif /* JEMALLOC_INTERNAL_ARENA_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/arena_inlines_a.h
New file
@@ -0,0 +1,57 @@
#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_A_H
#define JEMALLOC_INTERNAL_ARENA_INLINES_A_H
static inline unsigned
arena_ind_get(const arena_t *arena) {
    return base_ind_get(arena->base);
}
static inline void
arena_internal_add(arena_t *arena, size_t size) {
    atomic_fetch_add_zu(&arena->stats.internal, size, ATOMIC_RELAXED);
}
static inline void
arena_internal_sub(arena_t *arena, size_t size) {
    atomic_fetch_sub_zu(&arena->stats.internal, size, ATOMIC_RELAXED);
}
static inline size_t
arena_internal_get(arena_t *arena) {
    return atomic_load_zu(&arena->stats.internal, ATOMIC_RELAXED);
}
static inline bool
arena_prof_accum(tsdn_t *tsdn, arena_t *arena, uint64_t accumbytes) {
    cassert(config_prof);
    if (likely(prof_interval == 0 || !prof_active_get_unlocked())) {
        return false;
    }
    return prof_accum_add(tsdn, &arena->prof_accum, accumbytes);
}
static inline void
percpu_arena_update(tsd_t *tsd, unsigned cpu) {
    assert(have_percpu_arena);
    arena_t *oldarena = tsd_arena_get(tsd);
    assert(oldarena != NULL);
    unsigned oldind = arena_ind_get(oldarena);
    if (oldind != cpu) {
        unsigned newind = cpu;
        arena_t *newarena = arena_get(tsd_tsdn(tsd), newind, true);
        assert(newarena != NULL);
        /* Set new arena/tcache associations. */
        arena_migrate(tsd, oldind, newind);
        tcache_t *tcache = tcache_get(tsd);
        if (tcache != NULL) {
            tcache_arena_reassociate(tsd_tsdn(tsd), tcache,
                newarena);
        }
    }
}
#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_A_H */
jemalloc/x64/include/jemalloc/internal/arena_inlines_b.h
New file
@@ -0,0 +1,427 @@
#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_B_H
#define JEMALLOC_INTERNAL_ARENA_INLINES_B_H
#include "jemalloc/internal/jemalloc_internal_types.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/rtree.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/sz.h"
#include "jemalloc/internal/ticker.h"
JEMALLOC_ALWAYS_INLINE bool
arena_has_default_hooks(arena_t *arena) {
    return (extent_hooks_get(arena) == &extent_hooks_default);
}
JEMALLOC_ALWAYS_INLINE arena_t *
arena_choose_maybe_huge(tsd_t *tsd, arena_t *arena, size_t size) {
    if (arena != NULL) {
        return arena;
    }
    /*
     * For huge allocations, use the dedicated huge arena if both are true:
     * 1) is using auto arena selection (i.e. arena == NULL), and 2) the
     * thread is not assigned to a manual arena.
     */
    if (unlikely(size >= oversize_threshold)) {
        arena_t *tsd_arena = tsd_arena_get(tsd);
        if (tsd_arena == NULL || arena_is_auto(tsd_arena)) {
            return arena_choose_huge(tsd);
        }
    }
    return arena_choose(tsd, NULL);
}
JEMALLOC_ALWAYS_INLINE prof_tctx_t *
arena_prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
    cassert(config_prof);
    assert(ptr != NULL);
    /* Static check. */
    if (alloc_ctx == NULL) {
        const extent_t *extent = iealloc(tsdn, ptr);
        if (unlikely(!extent_slab_get(extent))) {
            return large_prof_tctx_get(tsdn, extent);
        }
    } else {
        if (unlikely(!alloc_ctx->slab)) {
            return large_prof_tctx_get(tsdn, iealloc(tsdn, ptr));
        }
    }
    return (prof_tctx_t *)(uintptr_t)1U;
}
JEMALLOC_ALWAYS_INLINE void
arena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,
    alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {
    cassert(config_prof);
    assert(ptr != NULL);
    /* Static check. */
    if (alloc_ctx == NULL) {
        extent_t *extent = iealloc(tsdn, ptr);
        if (unlikely(!extent_slab_get(extent))) {
            large_prof_tctx_set(tsdn, extent, tctx);
        }
    } else {
        if (unlikely(!alloc_ctx->slab)) {
            large_prof_tctx_set(tsdn, iealloc(tsdn, ptr), tctx);
        }
    }
}
static inline void
arena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) {
    cassert(config_prof);
    assert(ptr != NULL);
    extent_t *extent = iealloc(tsdn, ptr);
    assert(!extent_slab_get(extent));
    large_prof_tctx_reset(tsdn, extent);
}
JEMALLOC_ALWAYS_INLINE nstime_t
arena_prof_alloc_time_get(tsdn_t *tsdn, const void *ptr,
    alloc_ctx_t *alloc_ctx) {
    cassert(config_prof);
    assert(ptr != NULL);
    extent_t *extent = iealloc(tsdn, ptr);
    /*
     * Unlike arena_prof_prof_tctx_{get, set}, we only call this once we're
     * sure we have a sampled allocation.
     */
    assert(!extent_slab_get(extent));
    return large_prof_alloc_time_get(extent);
}
JEMALLOC_ALWAYS_INLINE void
arena_prof_alloc_time_set(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx,
    nstime_t t) {
    cassert(config_prof);
    assert(ptr != NULL);
    extent_t *extent = iealloc(tsdn, ptr);
    assert(!extent_slab_get(extent));
    large_prof_alloc_time_set(extent, t);
}
JEMALLOC_ALWAYS_INLINE void
arena_decay_ticks(tsdn_t *tsdn, arena_t *arena, unsigned nticks) {
    tsd_t *tsd;
    ticker_t *decay_ticker;
    if (unlikely(tsdn_null(tsdn))) {
        return;
    }
    tsd = tsdn_tsd(tsdn);
    decay_ticker = decay_ticker_get(tsd, arena_ind_get(arena));
    if (unlikely(decay_ticker == NULL)) {
        return;
    }
    if (unlikely(ticker_ticks(decay_ticker, nticks))) {
        arena_decay(tsdn, arena, false, false);
    }
}
JEMALLOC_ALWAYS_INLINE void
arena_decay_tick(tsdn_t *tsdn, arena_t *arena) {
    malloc_mutex_assert_not_owner(tsdn, &arena->decay_dirty.mtx);
    malloc_mutex_assert_not_owner(tsdn, &arena->decay_muzzy.mtx);
    arena_decay_ticks(tsdn, arena, 1);
}
/* Purge a single extent to retained / unmapped directly. */
JEMALLOC_ALWAYS_INLINE void
arena_decay_extent(tsdn_t *tsdn,arena_t *arena, extent_hooks_t **r_extent_hooks,
    extent_t *extent) {
    size_t extent_size = extent_size_get(extent);
    extent_dalloc_wrapper(tsdn, arena,
        r_extent_hooks, extent);
    if (config_stats) {
        /* Update stats accordingly. */
        arena_stats_lock(tsdn, &arena->stats);
        arena_stats_add_u64(tsdn, &arena->stats,
            &arena->decay_dirty.stats->nmadvise, 1);
        arena_stats_add_u64(tsdn, &arena->stats,
            &arena->decay_dirty.stats->purged, extent_size >> LG_PAGE);
        arena_stats_sub_zu(tsdn, &arena->stats, &arena->stats.mapped,
            extent_size);
        arena_stats_unlock(tsdn, &arena->stats);
    }
}
JEMALLOC_ALWAYS_INLINE void *
arena_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, bool zero,
    tcache_t *tcache, bool slow_path) {
    assert(!tsdn_null(tsdn) || tcache == NULL);
    if (likely(tcache != NULL)) {
        if (likely(size <= SC_SMALL_MAXCLASS)) {
            return tcache_alloc_small(tsdn_tsd(tsdn), arena,
                tcache, size, ind, zero, slow_path);
        }
        if (likely(size <= tcache_maxclass)) {
            return tcache_alloc_large(tsdn_tsd(tsdn), arena,
                tcache, size, ind, zero, slow_path);
        }
        /* (size > tcache_maxclass) case falls through. */
        assert(size > tcache_maxclass);
    }
    return arena_malloc_hard(tsdn, arena, size, ind, zero);
}
JEMALLOC_ALWAYS_INLINE arena_t *
arena_aalloc(tsdn_t *tsdn, const void *ptr) {
    return extent_arena_get(iealloc(tsdn, ptr));
}
JEMALLOC_ALWAYS_INLINE size_t
arena_salloc(tsdn_t *tsdn, const void *ptr) {
    assert(ptr != NULL);
    rtree_ctx_t rtree_ctx_fallback;
    rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
    szind_t szind = rtree_szind_read(tsdn, &extents_rtree, rtree_ctx,
        (uintptr_t)ptr, true);
    assert(szind != SC_NSIZES);
    return sz_index2size(szind);
}
JEMALLOC_ALWAYS_INLINE size_t
arena_vsalloc(tsdn_t *tsdn, const void *ptr) {
    /*
     * Return 0 if ptr is not within an extent managed by jemalloc.  This
     * function has two extra costs relative to isalloc():
     * - The rtree calls cannot claim to be dependent lookups, which induces
     *   rtree lookup load dependencies.
     * - The lookup may fail, so there is an extra branch to check for
     *   failure.
     */
    rtree_ctx_t rtree_ctx_fallback;
    rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
    extent_t *extent;
    szind_t szind;
    if (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx,
        (uintptr_t)ptr, false, &extent, &szind)) {
        return 0;
    }
    if (extent == NULL) {
        return 0;
    }
    assert(extent_state_get(extent) == extent_state_active);
    /* Only slab members should be looked up via interior pointers. */
    assert(extent_addr_get(extent) == ptr || extent_slab_get(extent));
    assert(szind != SC_NSIZES);
    return sz_index2size(szind);
}
static inline void
arena_dalloc_large_no_tcache(tsdn_t *tsdn, void *ptr, szind_t szind) {
    if (config_prof && unlikely(szind < SC_NBINS)) {
        arena_dalloc_promoted(tsdn, ptr, NULL, true);
    } else {
        extent_t *extent = iealloc(tsdn, ptr);
        large_dalloc(tsdn, extent);
    }
}
static inline void
arena_dalloc_no_tcache(tsdn_t *tsdn, void *ptr) {
    assert(ptr != NULL);
    rtree_ctx_t rtree_ctx_fallback;
    rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
    szind_t szind;
    bool slab;
    rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,
        true, &szind, &slab);
    if (config_debug) {
        extent_t *extent = rtree_extent_read(tsdn, &extents_rtree,
            rtree_ctx, (uintptr_t)ptr, true);
        assert(szind == extent_szind_get(extent));
        assert(szind < SC_NSIZES);
        assert(slab == extent_slab_get(extent));
    }
    if (likely(slab)) {
        /* Small allocation. */
        arena_dalloc_small(tsdn, ptr);
    } else {
        arena_dalloc_large_no_tcache(tsdn, ptr, szind);
    }
}
JEMALLOC_ALWAYS_INLINE void
arena_dalloc_large(tsdn_t *tsdn, void *ptr, tcache_t *tcache, szind_t szind,
    bool slow_path) {
    if (szind < nhbins) {
        if (config_prof && unlikely(szind < SC_NBINS)) {
            arena_dalloc_promoted(tsdn, ptr, tcache, slow_path);
        } else {
            tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr, szind,
                slow_path);
        }
    } else {
        extent_t *extent = iealloc(tsdn, ptr);
        large_dalloc(tsdn, extent);
    }
}
JEMALLOC_ALWAYS_INLINE void
arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
    alloc_ctx_t *alloc_ctx, bool slow_path) {
    assert(!tsdn_null(tsdn) || tcache == NULL);
    assert(ptr != NULL);
    if (unlikely(tcache == NULL)) {
        arena_dalloc_no_tcache(tsdn, ptr);
        return;
    }
    szind_t szind;
    bool slab;
    rtree_ctx_t *rtree_ctx;
    if (alloc_ctx != NULL) {
        szind = alloc_ctx->szind;
        slab = alloc_ctx->slab;
        assert(szind != SC_NSIZES);
    } else {
        rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
        rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
            (uintptr_t)ptr, true, &szind, &slab);
    }
    if (config_debug) {
        rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
        extent_t *extent = rtree_extent_read(tsdn, &extents_rtree,
            rtree_ctx, (uintptr_t)ptr, true);
        assert(szind == extent_szind_get(extent));
        assert(szind < SC_NSIZES);
        assert(slab == extent_slab_get(extent));
    }
    if (likely(slab)) {
        /* Small allocation. */
        tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,
            slow_path);
    } else {
        arena_dalloc_large(tsdn, ptr, tcache, szind, slow_path);
    }
}
static inline void
arena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) {
    assert(ptr != NULL);
    assert(size <= SC_LARGE_MAXCLASS);
    szind_t szind;
    bool slab;
    if (!config_prof || !opt_prof) {
        /*
         * There is no risk of being confused by a promoted sampled
         * object, so base szind and slab on the given size.
         */
        szind = sz_size2index(size);
        slab = (szind < SC_NBINS);
    }
    if ((config_prof && opt_prof) || config_debug) {
        rtree_ctx_t rtree_ctx_fallback;
        rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,
            &rtree_ctx_fallback);
        rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
            (uintptr_t)ptr, true, &szind, &slab);
        assert(szind == sz_size2index(size));
        assert((config_prof && opt_prof) || slab == (szind < SC_NBINS));
        if (config_debug) {
            extent_t *extent = rtree_extent_read(tsdn,
                &extents_rtree, rtree_ctx, (uintptr_t)ptr, true);
            assert(szind == extent_szind_get(extent));
            assert(slab == extent_slab_get(extent));
        }
    }
    if (likely(slab)) {
        /* Small allocation. */
        arena_dalloc_small(tsdn, ptr);
    } else {
        arena_dalloc_large_no_tcache(tsdn, ptr, szind);
    }
}
JEMALLOC_ALWAYS_INLINE void
arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
    alloc_ctx_t *alloc_ctx, bool slow_path) {
    assert(!tsdn_null(tsdn) || tcache == NULL);
    assert(ptr != NULL);
    assert(size <= SC_LARGE_MAXCLASS);
    if (unlikely(tcache == NULL)) {
        arena_sdalloc_no_tcache(tsdn, ptr, size);
        return;
    }
    szind_t szind;
    bool slab;
    alloc_ctx_t local_ctx;
    if (config_prof && opt_prof) {
        if (alloc_ctx == NULL) {
            /* Uncommon case and should be a static check. */
            rtree_ctx_t rtree_ctx_fallback;
            rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,
                &rtree_ctx_fallback);
            rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
                (uintptr_t)ptr, true, &local_ctx.szind,
                &local_ctx.slab);
            assert(local_ctx.szind == sz_size2index(size));
            alloc_ctx = &local_ctx;
        }
        slab = alloc_ctx->slab;
        szind = alloc_ctx->szind;
    } else {
        /*
         * There is no risk of being confused by a promoted sampled
         * object, so base szind and slab on the given size.
         */
        szind = sz_size2index(size);
        slab = (szind < SC_NBINS);
    }
    if (config_debug) {
        rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
        rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
            (uintptr_t)ptr, true, &szind, &slab);
        extent_t *extent = rtree_extent_read(tsdn,
            &extents_rtree, rtree_ctx, (uintptr_t)ptr, true);
        assert(szind == extent_szind_get(extent));
        assert(slab == extent_slab_get(extent));
    }
    if (likely(slab)) {
        /* Small allocation. */
        tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,
            slow_path);
    } else {
        arena_dalloc_large(tsdn, ptr, tcache, szind, slow_path);
    }
}
#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_B_H */
jemalloc/x64/include/jemalloc/internal/arena_stats.h
New file
@@ -0,0 +1,271 @@
#ifndef JEMALLOC_INTERNAL_ARENA_STATS_H
#define JEMALLOC_INTERNAL_ARENA_STATS_H
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/mutex_prof.h"
#include "jemalloc/internal/sc.h"
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
/*
 * In those architectures that support 64-bit atomics, we use atomic updates for
 * our 64-bit values.  Otherwise, we use a plain uint64_t and synchronize
 * externally.
 */
#ifdef JEMALLOC_ATOMIC_U64
typedef atomic_u64_t arena_stats_u64_t;
#else
/* Must hold the arena stats mutex while reading atomically. */
typedef uint64_t arena_stats_u64_t;
#endif
typedef struct arena_stats_large_s arena_stats_large_t;
struct arena_stats_large_s {
    /*
     * Total number of allocation/deallocation requests served directly by
     * the arena.
     */
    arena_stats_u64_t    nmalloc;
    arena_stats_u64_t    ndalloc;
    /*
     * Number of allocation requests that correspond to this size class.
     * This includes requests served by tcache, though tcache only
     * periodically merges into this counter.
     */
    arena_stats_u64_t    nrequests; /* Partially derived. */
    /*
     * Number of tcache fills / flushes for large (similarly, periodically
     * merged).  Note that there is no large tcache batch-fill currently
     * (i.e. only fill 1 at a time); however flush may be batched.
     */
    arena_stats_u64_t    nfills; /* Partially derived. */
    arena_stats_u64_t    nflushes; /* Partially derived. */
    /* Current number of allocations of this size class. */
    size_t        curlextents; /* Derived. */
};
typedef struct arena_stats_decay_s arena_stats_decay_t;
struct arena_stats_decay_s {
    /* Total number of purge sweeps. */
    arena_stats_u64_t    npurge;
    /* Total number of madvise calls made. */
    arena_stats_u64_t    nmadvise;
    /* Total number of pages purged. */
    arena_stats_u64_t    purged;
};
typedef struct arena_stats_extents_s arena_stats_extents_t;
struct arena_stats_extents_s {
    /*
     * Stats for a given index in the range [0, SC_NPSIZES] in an extents_t.
     * We track both bytes and # of extents: two extents in the same bucket
     * may have different sizes if adjacent size classes differ by more than
     * a page, so bytes cannot always be derived from # of extents.
     */
    atomic_zu_t ndirty;
    atomic_zu_t dirty_bytes;
    atomic_zu_t nmuzzy;
    atomic_zu_t muzzy_bytes;
    atomic_zu_t nretained;
    atomic_zu_t retained_bytes;
};
/*
 * Arena stats.  Note that fields marked "derived" are not directly maintained
 * within the arena code; rather their values are derived during stats merge
 * requests.
 */
typedef struct arena_stats_s arena_stats_t;
struct arena_stats_s {
#ifndef JEMALLOC_ATOMIC_U64
    malloc_mutex_t        mtx;
#endif
    /* Number of bytes currently mapped, excluding retained memory. */
    atomic_zu_t        mapped; /* Partially derived. */
    /*
     * Number of unused virtual memory bytes currently retained.  Retained
     * bytes are technically mapped (though always decommitted or purged),
     * but they are excluded from the mapped statistic (above).
     */
    atomic_zu_t        retained; /* Derived. */
    /* Number of extent_t structs allocated by base, but not being used. */
    atomic_zu_t        extent_avail;
    arena_stats_decay_t    decay_dirty;
    arena_stats_decay_t    decay_muzzy;
    atomic_zu_t        base; /* Derived. */
    atomic_zu_t        internal;
    atomic_zu_t        resident; /* Derived. */
    atomic_zu_t        metadata_thp;
    atomic_zu_t        allocated_large; /* Derived. */
    arena_stats_u64_t    nmalloc_large; /* Derived. */
    arena_stats_u64_t    ndalloc_large; /* Derived. */
    arena_stats_u64_t    nfills_large; /* Derived. */
    arena_stats_u64_t    nflushes_large; /* Derived. */
    arena_stats_u64_t    nrequests_large; /* Derived. */
    /* VM space had to be leaked (undocumented).  Normally 0. */
    atomic_zu_t        abandoned_vm;
    /* Number of bytes cached in tcache associated with this arena. */
    atomic_zu_t        tcache_bytes; /* Derived. */
    mutex_prof_data_t mutex_prof_data[mutex_prof_num_arena_mutexes];
    /* One element for each large size class. */
    arena_stats_large_t    lstats[SC_NSIZES - SC_NBINS];
    /* Arena uptime. */
    nstime_t        uptime;
};
static inline bool
arena_stats_init(tsdn_t *tsdn, arena_stats_t *arena_stats) {
    if (config_debug) {
        for (size_t i = 0; i < sizeof(arena_stats_t); i++) {
            assert(((char *)arena_stats)[i] == 0);
        }
    }
#ifndef JEMALLOC_ATOMIC_U64
    if (malloc_mutex_init(&arena_stats->mtx, "arena_stats",
        WITNESS_RANK_ARENA_STATS, malloc_mutex_rank_exclusive)) {
        return true;
    }
#endif
    /* Memory is zeroed, so there is no need to clear stats. */
    return false;
}
static inline void
arena_stats_lock(tsdn_t *tsdn, arena_stats_t *arena_stats) {
#ifndef JEMALLOC_ATOMIC_U64
    malloc_mutex_lock(tsdn, &arena_stats->mtx);
#endif
}
static inline void
arena_stats_unlock(tsdn_t *tsdn, arena_stats_t *arena_stats) {
#ifndef JEMALLOC_ATOMIC_U64
    malloc_mutex_unlock(tsdn, &arena_stats->mtx);
#endif
}
static inline uint64_t
arena_stats_read_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
    arena_stats_u64_t *p) {
#ifdef JEMALLOC_ATOMIC_U64
    return atomic_load_u64(p, ATOMIC_RELAXED);
#else
    malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
    return *p;
#endif
}
static inline void
arena_stats_add_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
    arena_stats_u64_t *p, uint64_t x) {
#ifdef JEMALLOC_ATOMIC_U64
    atomic_fetch_add_u64(p, x, ATOMIC_RELAXED);
#else
    malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
    *p += x;
#endif
}
static inline void
arena_stats_sub_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
    arena_stats_u64_t *p, uint64_t x) {
#ifdef JEMALLOC_ATOMIC_U64
    uint64_t r = atomic_fetch_sub_u64(p, x, ATOMIC_RELAXED);
    assert(r - x <= r);
#else
    malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
    *p -= x;
    assert(*p + x >= *p);
#endif
}
/*
 * Non-atomically sets *dst += src.  *dst needs external synchronization.
 * This lets us avoid the cost of a fetch_add when its unnecessary (note that
 * the types here are atomic).
 */
static inline void
arena_stats_accum_u64(arena_stats_u64_t *dst, uint64_t src) {
#ifdef JEMALLOC_ATOMIC_U64
    uint64_t cur_dst = atomic_load_u64(dst, ATOMIC_RELAXED);
    atomic_store_u64(dst, src + cur_dst, ATOMIC_RELAXED);
#else
    *dst += src;
#endif
}
static inline size_t
arena_stats_read_zu(tsdn_t *tsdn, arena_stats_t *arena_stats,
    atomic_zu_t *p) {
#ifdef JEMALLOC_ATOMIC_U64
    return atomic_load_zu(p, ATOMIC_RELAXED);
#else
    malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
    return atomic_load_zu(p, ATOMIC_RELAXED);
#endif
}
static inline void
arena_stats_add_zu(tsdn_t *tsdn, arena_stats_t *arena_stats,
    atomic_zu_t *p, size_t x) {
#ifdef JEMALLOC_ATOMIC_U64
    atomic_fetch_add_zu(p, x, ATOMIC_RELAXED);
#else
    malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
    size_t cur = atomic_load_zu(p, ATOMIC_RELAXED);
    atomic_store_zu(p, cur + x, ATOMIC_RELAXED);
#endif
}
static inline void
arena_stats_sub_zu(tsdn_t *tsdn, arena_stats_t *arena_stats,
    atomic_zu_t *p, size_t x) {
#ifdef JEMALLOC_ATOMIC_U64
    size_t r = atomic_fetch_sub_zu(p, x, ATOMIC_RELAXED);
    assert(r - x <= r);
#else
    malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
    size_t cur = atomic_load_zu(p, ATOMIC_RELAXED);
    atomic_store_zu(p, cur - x, ATOMIC_RELAXED);
#endif
}
/* Like the _u64 variant, needs an externally synchronized *dst. */
static inline void
arena_stats_accum_zu(atomic_zu_t *dst, size_t src) {
    size_t cur_dst = atomic_load_zu(dst, ATOMIC_RELAXED);
    atomic_store_zu(dst, src + cur_dst, ATOMIC_RELAXED);
}
static inline void
arena_stats_large_flush_nrequests_add(tsdn_t *tsdn, arena_stats_t *arena_stats,
    szind_t szind, uint64_t nrequests) {
    arena_stats_lock(tsdn, arena_stats);
    arena_stats_large_t *lstats = &arena_stats->lstats[szind - SC_NBINS];
    arena_stats_add_u64(tsdn, arena_stats, &lstats->nrequests, nrequests);
    arena_stats_add_u64(tsdn, arena_stats, &lstats->nflushes, 1);
    arena_stats_unlock(tsdn, arena_stats);
}
static inline void
arena_stats_mapped_add(tsdn_t *tsdn, arena_stats_t *arena_stats, size_t size) {
    arena_stats_lock(tsdn, arena_stats);
    arena_stats_add_zu(tsdn, arena_stats, &arena_stats->mapped, size);
    arena_stats_unlock(tsdn, arena_stats);
}
#endif /* JEMALLOC_INTERNAL_ARENA_STATS_H */
jemalloc/x64/include/jemalloc/internal/arena_structs_a.h
New file
@@ -0,0 +1,11 @@
#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H
#define JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H
#include "jemalloc/internal/bitmap.h"
struct arena_slab_data_s {
    /* Per region allocated/deallocated bitmap. */
    bitmap_t    bitmap[BITMAP_GROUPS_MAX];
};
#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H */
jemalloc/x64/include/jemalloc/internal/arena_structs_b.h
New file
@@ -0,0 +1,232 @@
#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H
#define JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H
#include "jemalloc/internal/arena_stats.h"
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/bin.h"
#include "jemalloc/internal/bitmap.h"
#include "jemalloc/internal/extent_dss.h"
#include "jemalloc/internal/jemalloc_internal_types.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/nstime.h"
#include "jemalloc/internal/ql.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/smoothstep.h"
#include "jemalloc/internal/ticker.h"
struct arena_decay_s {
    /* Synchronizes all non-atomic fields. */
    malloc_mutex_t        mtx;
    /*
     * True if a thread is currently purging the extents associated with
     * this decay structure.
     */
    bool            purging;
    /*
     * Approximate time in milliseconds from the creation of a set of unused
     * dirty pages until an equivalent set of unused dirty pages is purged
     * and/or reused.
     */
    atomic_zd_t        time_ms;
    /* time / SMOOTHSTEP_NSTEPS. */
    nstime_t        interval;
    /*
     * Time at which the current decay interval logically started.  We do
     * not actually advance to a new epoch until sometime after it starts
     * because of scheduling and computation delays, and it is even possible
     * to completely skip epochs.  In all cases, during epoch advancement we
     * merge all relevant activity into the most recently recorded epoch.
     */
    nstime_t        epoch;
    /* Deadline randomness generator. */
    uint64_t        jitter_state;
    /*
     * Deadline for current epoch.  This is the sum of interval and per
     * epoch jitter which is a uniform random variable in [0..interval).
     * Epochs always advance by precise multiples of interval, but we
     * randomize the deadline to reduce the likelihood of arenas purging in
     * lockstep.
     */
    nstime_t        deadline;
    /*
     * Number of unpurged pages at beginning of current epoch.  During epoch
     * advancement we use the delta between arena->decay_*.nunpurged and
     * extents_npages_get(&arena->extents_*) to determine how many dirty
     * pages, if any, were generated.
     */
    size_t            nunpurged;
    /*
     * Trailing log of how many unused dirty pages were generated during
     * each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last
     * element is the most recent epoch.  Corresponding epoch times are
     * relative to epoch.
     */
    size_t            backlog[SMOOTHSTEP_NSTEPS];
    /*
     * Pointer to associated stats.  These stats are embedded directly in
     * the arena's stats due to how stats structures are shared between the
     * arena and ctl code.
     *
     * Synchronization: Same as associated arena's stats field. */
    arena_stats_decay_t    *stats;
    /* Peak number of pages in associated extents.  Used for debug only. */
    uint64_t        ceil_npages;
};
struct arena_s {
    /*
     * Number of threads currently assigned to this arena.  Each thread has
     * two distinct assignments, one for application-serving allocation, and
     * the other for internal metadata allocation.  Internal metadata must
     * not be allocated from arenas explicitly created via the arenas.create
     * mallctl, because the arena.<i>.reset mallctl indiscriminately
     * discards all allocations for the affected arena.
     *
     *   0: Application allocation.
     *   1: Internal metadata allocation.
     *
     * Synchronization: atomic.
     */
    atomic_u_t        nthreads[2];
    /* Next bin shard for binding new threads. Synchronization: atomic. */
    atomic_u_t        binshard_next;
    /*
     * When percpu_arena is enabled, to amortize the cost of reading /
     * updating the current CPU id, track the most recent thread accessing
     * this arena, and only read CPU if there is a mismatch.
     */
    tsdn_t        *last_thd;
    /* Synchronization: internal. */
    arena_stats_t        stats;
    /*
     * Lists of tcaches and cache_bin_array_descriptors for extant threads
     * associated with this arena.  Stats from these are merged
     * incrementally, and at exit if opt_stats_print is enabled.
     *
     * Synchronization: tcache_ql_mtx.
     */
    ql_head(tcache_t)            tcache_ql;
    ql_head(cache_bin_array_descriptor_t)    cache_bin_array_descriptor_ql;
    malloc_mutex_t                tcache_ql_mtx;
    /* Synchronization: internal. */
    prof_accum_t        prof_accum;
    /*
     * PRNG state for cache index randomization of large allocation base
     * pointers.
     *
     * Synchronization: atomic.
     */
    atomic_zu_t        offset_state;
    /*
     * Extent serial number generator state.
     *
     * Synchronization: atomic.
     */
    atomic_zu_t        extent_sn_next;
    /*
     * Represents a dss_prec_t, but atomically.
     *
     * Synchronization: atomic.
     */
    atomic_u_t        dss_prec;
    /*
     * Number of pages in active extents.
     *
     * Synchronization: atomic.
     */
    atomic_zu_t        nactive;
    /*
     * Extant large allocations.
     *
     * Synchronization: large_mtx.
     */
    extent_list_t        large;
    /* Synchronizes all large allocation/update/deallocation. */
    malloc_mutex_t        large_mtx;
    /*
     * Collections of extents that were previously allocated.  These are
     * used when allocating extents, in an attempt to re-use address space.
     *
     * Synchronization: internal.
     */
    extents_t        extents_dirty;
    extents_t        extents_muzzy;
    extents_t        extents_retained;
    /*
     * Decay-based purging state, responsible for scheduling extent state
     * transitions.
     *
     * Synchronization: internal.
     */
    arena_decay_t        decay_dirty; /* dirty --> muzzy */
    arena_decay_t        decay_muzzy; /* muzzy --> retained */
    /*
     * Next extent size class in a growing series to use when satisfying a
     * request via the extent hooks (only if opt_retain).  This limits the
     * number of disjoint virtual memory ranges so that extent merging can
     * be effective even if multiple arenas' extent allocation requests are
     * highly interleaved.
     *
     * retain_grow_limit is the max allowed size ind to expand (unless the
     * required size is greater).  Default is no limit, and controlled
     * through mallctl only.
     *
     * Synchronization: extent_grow_mtx
     */
    pszind_t        extent_grow_next;
    pszind_t        retain_grow_limit;
    malloc_mutex_t        extent_grow_mtx;
    /*
     * Available extent structures that were allocated via
     * base_alloc_extent().
     *
     * Synchronization: extent_avail_mtx.
     */
    extent_tree_t        extent_avail;
    atomic_zu_t        extent_avail_cnt;
    malloc_mutex_t        extent_avail_mtx;
    /*
     * bins is used to store heaps of free regions.
     *
     * Synchronization: internal.
     */
    bins_t            bins[SC_NBINS];
    /*
     * Base allocator, from which arena metadata are allocated.
     *
     * Synchronization: internal.
     */
    base_t            *base;
    /* Used to determine uptime.  Read-only after initialization. */
    nstime_t        create_time;
};
/* Used in conjunction with tsd for fast arena-related context lookup. */
struct arena_tdata_s {
    ticker_t        decay_ticker;
};
/* Used to pass rtree lookup context down the path. */
struct alloc_ctx_s {
    szind_t szind;
    bool slab;
};
#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H */
jemalloc/x64/include/jemalloc/internal/arena_types.h
New file
@@ -0,0 +1,51 @@
#ifndef JEMALLOC_INTERNAL_ARENA_TYPES_H
#define JEMALLOC_INTERNAL_ARENA_TYPES_H
#include "jemalloc/internal/sc.h"
/* Maximum number of regions in one slab. */
#define LG_SLAB_MAXREGS        (LG_PAGE - SC_LG_TINY_MIN)
#define SLAB_MAXREGS        (1U << LG_SLAB_MAXREGS)
/* Default decay times in milliseconds. */
#define DIRTY_DECAY_MS_DEFAULT    ZD(10 * 1000)
#define MUZZY_DECAY_MS_DEFAULT    (0)
/* Number of event ticks between time checks. */
#define DECAY_NTICKS_PER_UPDATE    1000
typedef struct arena_slab_data_s arena_slab_data_t;
typedef struct arena_decay_s arena_decay_t;
typedef struct arena_s arena_t;
typedef struct arena_tdata_s arena_tdata_t;
typedef struct alloc_ctx_s alloc_ctx_t;
typedef enum {
    percpu_arena_mode_names_base   = 0, /* Used for options processing. */
    /*
     * *_uninit are used only during bootstrapping, and must correspond
     * to initialized variant plus percpu_arena_mode_enabled_base.
     */
    percpu_arena_uninit            = 0,
    per_phycpu_arena_uninit        = 1,
    /* All non-disabled modes must come after percpu_arena_disabled. */
    percpu_arena_disabled          = 2,
    percpu_arena_mode_names_limit  = 3, /* Used for options processing. */
    percpu_arena_mode_enabled_base = 3,
    percpu_arena                   = 3,
    per_phycpu_arena               = 4  /* Hyper threads share arena. */
} percpu_arena_mode_t;
#define PERCPU_ARENA_ENABLED(m)    ((m) >= percpu_arena_mode_enabled_base)
#define PERCPU_ARENA_DEFAULT    percpu_arena_disabled
/*
 * When allocation_size >= oversize_threshold, use the dedicated huge arena
 * (unless have explicitly spicified arena index).  0 disables the feature.
 */
#define OVERSIZE_THRESHOLD_DEFAULT (8 << 20)
#endif /* JEMALLOC_INTERNAL_ARENA_TYPES_H */
jemalloc/x64/include/jemalloc/internal/assert.h
New file
@@ -0,0 +1,56 @@
#include "jemalloc/internal/malloc_io.h"
#include "jemalloc/internal/util.h"
/*
 * Define a custom assert() in order to reduce the chances of deadlock during
 * assertion failure.
 */
#ifndef assert
#define assert(e) do {                            \
    if (unlikely(config_debug && !(e))) {                \
        malloc_printf(                        \
            "<jemalloc>: %s:%d: Failed assertion: \"%s\"\n",    \
            __FILE__, __LINE__, #e);                \
        abort();                        \
    }                                \
} while (0)
#endif
#ifndef not_reached
#define not_reached() do {                        \
    if (config_debug) {                        \
        malloc_printf(                        \
            "<jemalloc>: %s:%d: Unreachable code reached\n",    \
            __FILE__, __LINE__);                \
        abort();                        \
    }                                \
    unreachable();                            \
} while (0)
#endif
#ifndef not_implemented
#define not_implemented() do {                        \
    if (config_debug) {                        \
        malloc_printf("<jemalloc>: %s:%d: Not implemented\n",    \
            __FILE__, __LINE__);                \
        abort();                        \
    }                                \
} while (0)
#endif
#ifndef assert_not_implemented
#define assert_not_implemented(e) do {                    \
    if (unlikely(config_debug && !(e))) {                \
        not_implemented();                    \
    }                                \
} while (0)
#endif
/* Use to assert a particular configuration, e.g., cassert(config_debug). */
#ifndef cassert
#define cassert(c) do {                            \
    if (unlikely(!(c))) {                        \
        not_reached();                        \
    }                                \
} while (0)
#endif
jemalloc/x64/include/jemalloc/internal/atomic.h
New file
@@ -0,0 +1,86 @@
#ifndef JEMALLOC_INTERNAL_ATOMIC_H
#define JEMALLOC_INTERNAL_ATOMIC_H
#define ATOMIC_INLINE JEMALLOC_ALWAYS_INLINE
#define JEMALLOC_U8_ATOMICS
#if defined(JEMALLOC_GCC_ATOMIC_ATOMICS)
#  include "jemalloc/internal/atomic_gcc_atomic.h"
#  if !defined(JEMALLOC_GCC_U8_ATOMIC_ATOMICS)
#    undef JEMALLOC_U8_ATOMICS
#  endif
#elif defined(JEMALLOC_GCC_SYNC_ATOMICS)
#  include "jemalloc/internal/atomic_gcc_sync.h"
#  if !defined(JEMALLOC_GCC_U8_SYNC_ATOMICS)
#    undef JEMALLOC_U8_ATOMICS
#  endif
#elif defined(_MSC_VER)
#  include "jemalloc/internal/atomic_msvc.h"
#elif defined(JEMALLOC_C11_ATOMICS)
#  include "jemalloc/internal/atomic_c11.h"
#else
#  error "Don't have atomics implemented on this platform."
#endif
/*
 * This header gives more or less a backport of C11 atomics. The user can write
 * JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_sizeof_type); to generate
 * counterparts of the C11 atomic functions for type, as so:
 *   JEMALLOC_GENERATE_ATOMICS(int *, pi, 3);
 * and then write things like:
 *   int *some_ptr;
 *   atomic_pi_t atomic_ptr_to_int;
 *   atomic_store_pi(&atomic_ptr_to_int, some_ptr, ATOMIC_RELAXED);
 *   int *prev_value = atomic_exchange_pi(&ptr_to_int, NULL, ATOMIC_ACQ_REL);
 *   assert(some_ptr == prev_value);
 * and expect things to work in the obvious way.
 *
 * Also included (with naming differences to avoid conflicts with the standard
 * library):
 *   atomic_fence(atomic_memory_order_t) (mimics C11's atomic_thread_fence).
 *   ATOMIC_INIT (mimics C11's ATOMIC_VAR_INIT).
 */
/*
 * Pure convenience, so that we don't have to type "atomic_memory_order_"
 * quite so often.
 */
#define ATOMIC_RELAXED atomic_memory_order_relaxed
#define ATOMIC_ACQUIRE atomic_memory_order_acquire
#define ATOMIC_RELEASE atomic_memory_order_release
#define ATOMIC_ACQ_REL atomic_memory_order_acq_rel
#define ATOMIC_SEQ_CST atomic_memory_order_seq_cst
/*
 * Not all platforms have 64-bit atomics.  If we do, this #define exposes that
 * fact.
 */
#if (LG_SIZEOF_PTR == 3 || LG_SIZEOF_INT == 3)
#  define JEMALLOC_ATOMIC_U64
#endif
JEMALLOC_GENERATE_ATOMICS(void *, p, LG_SIZEOF_PTR)
/*
 * There's no actual guarantee that sizeof(bool) == 1, but it's true on the only
 * platform that actually needs to know the size, MSVC.
 */
JEMALLOC_GENERATE_ATOMICS(bool, b, 0)
JEMALLOC_GENERATE_INT_ATOMICS(unsigned, u, LG_SIZEOF_INT)
JEMALLOC_GENERATE_INT_ATOMICS(size_t, zu, LG_SIZEOF_PTR)
JEMALLOC_GENERATE_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)
JEMALLOC_GENERATE_INT_ATOMICS(uint8_t, u8, 0)
JEMALLOC_GENERATE_INT_ATOMICS(uint32_t, u32, 2)
#ifdef JEMALLOC_ATOMIC_U64
JEMALLOC_GENERATE_INT_ATOMICS(uint64_t, u64, 3)
#endif
#undef ATOMIC_INLINE
#endif /* JEMALLOC_INTERNAL_ATOMIC_H */
jemalloc/x64/include/jemalloc/internal/atomic_c11.h
New file
@@ -0,0 +1,97 @@
#ifndef JEMALLOC_INTERNAL_ATOMIC_C11_H
#define JEMALLOC_INTERNAL_ATOMIC_C11_H
#include <stdatomic.h>
#define ATOMIC_INIT(...) ATOMIC_VAR_INIT(__VA_ARGS__)
#define atomic_memory_order_t memory_order
#define atomic_memory_order_relaxed memory_order_relaxed
#define atomic_memory_order_acquire memory_order_acquire
#define atomic_memory_order_release memory_order_release
#define atomic_memory_order_acq_rel memory_order_acq_rel
#define atomic_memory_order_seq_cst memory_order_seq_cst
#define atomic_fence atomic_thread_fence
#define JEMALLOC_GENERATE_ATOMICS(type, short_type,            \
    /* unused */ lg_size)                        \
typedef _Atomic(type) atomic_##short_type##_t;                \
                                    \
ATOMIC_INLINE type                            \
atomic_load_##short_type(const atomic_##short_type##_t *a,        \
    atomic_memory_order_t mo) {                        \
    /*                                \
     * A strict interpretation of the C standard prevents        \
     * atomic_load from taking a const argument, but it's        \
     * convenient for our purposes. This cast is a workaround.    \
     */                                \
    atomic_##short_type##_t* a_nonconst =                \
        (atomic_##short_type##_t*)a;                \
    return atomic_load_explicit(a_nonconst, mo);            \
}                                    \
                                    \
ATOMIC_INLINE void                            \
atomic_store_##short_type(atomic_##short_type##_t *a,            \
    type val, atomic_memory_order_t mo) {                \
    atomic_store_explicit(a, val, mo);                \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return atomic_exchange_explicit(a, val, mo);            \
}                                    \
                                    \
ATOMIC_INLINE bool                            \
atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,    \
    type *expected, type desired, atomic_memory_order_t success_mo,    \
    atomic_memory_order_t failure_mo) {                    \
    return atomic_compare_exchange_weak_explicit(a, expected,    \
        desired, success_mo, failure_mo);                \
}                                    \
                                    \
ATOMIC_INLINE bool                            \
atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,    \
    type *expected, type desired, atomic_memory_order_t success_mo,    \
    atomic_memory_order_t failure_mo) {                    \
    return atomic_compare_exchange_strong_explicit(a, expected,    \
        desired, success_mo, failure_mo);                \
}
/*
 * Integral types have some special operations available that non-integral ones
 * lack.
 */
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type,         \
    /* unused */ lg_size)                        \
JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size)    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_add_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    return atomic_fetch_add_explicit(a, val, mo);            \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_sub_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    return atomic_fetch_sub_explicit(a, val, mo);            \
}                                    \
ATOMIC_INLINE type                            \
atomic_fetch_and_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    return atomic_fetch_and_explicit(a, val, mo);            \
}                                    \
ATOMIC_INLINE type                            \
atomic_fetch_or_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    return atomic_fetch_or_explicit(a, val, mo);            \
}                                    \
ATOMIC_INLINE type                            \
atomic_fetch_xor_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    return atomic_fetch_xor_explicit(a, val, mo);            \
}
#endif /* JEMALLOC_INTERNAL_ATOMIC_C11_H */
jemalloc/x64/include/jemalloc/internal/atomic_gcc_atomic.h
New file
@@ -0,0 +1,129 @@
#ifndef JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H
#define JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H
#include "jemalloc/internal/assert.h"
#define ATOMIC_INIT(...) {__VA_ARGS__}
typedef enum {
    atomic_memory_order_relaxed,
    atomic_memory_order_acquire,
    atomic_memory_order_release,
    atomic_memory_order_acq_rel,
    atomic_memory_order_seq_cst
} atomic_memory_order_t;
ATOMIC_INLINE int
atomic_enum_to_builtin(atomic_memory_order_t mo) {
    switch (mo) {
    case atomic_memory_order_relaxed:
        return __ATOMIC_RELAXED;
    case atomic_memory_order_acquire:
        return __ATOMIC_ACQUIRE;
    case atomic_memory_order_release:
        return __ATOMIC_RELEASE;
    case atomic_memory_order_acq_rel:
        return __ATOMIC_ACQ_REL;
    case atomic_memory_order_seq_cst:
        return __ATOMIC_SEQ_CST;
    }
    /* Can't happen; the switch is exhaustive. */
    not_reached();
}
ATOMIC_INLINE void
atomic_fence(atomic_memory_order_t mo) {
    __atomic_thread_fence(atomic_enum_to_builtin(mo));
}
#define JEMALLOC_GENERATE_ATOMICS(type, short_type,            \
    /* unused */ lg_size)                        \
typedef struct {                            \
    type repr;                            \
} atomic_##short_type##_t;                        \
                                    \
ATOMIC_INLINE type                            \
atomic_load_##short_type(const atomic_##short_type##_t *a,        \
    atomic_memory_order_t mo) {                        \
    type result;                            \
    __atomic_load(&a->repr, &result, atomic_enum_to_builtin(mo));    \
    return result;                            \
}                                    \
                                    \
ATOMIC_INLINE void                            \
atomic_store_##short_type(atomic_##short_type##_t *a, type val,        \
    atomic_memory_order_t mo) {                        \
    __atomic_store(&a->repr, &val, atomic_enum_to_builtin(mo));    \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    type result;                            \
    __atomic_exchange(&a->repr, &val, &result,            \
        atomic_enum_to_builtin(mo));                \
    return result;                            \
}                                    \
                                    \
ATOMIC_INLINE bool                            \
atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,    \
    UNUSED type *expected, type desired,                \
    atomic_memory_order_t success_mo,                    \
    atomic_memory_order_t failure_mo) {                    \
    return __atomic_compare_exchange(&a->repr, expected, &desired,    \
        true, atomic_enum_to_builtin(success_mo),            \
        atomic_enum_to_builtin(failure_mo));            \
}                                    \
                                    \
ATOMIC_INLINE bool                            \
atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,    \
    UNUSED type *expected, type desired,                \
    atomic_memory_order_t success_mo,                    \
    atomic_memory_order_t failure_mo) {                    \
    return __atomic_compare_exchange(&a->repr, expected, &desired,    \
        false,                            \
        atomic_enum_to_builtin(success_mo),                \
        atomic_enum_to_builtin(failure_mo));            \
}
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type,            \
    /* unused */ lg_size)                        \
JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size)    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_add_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __atomic_fetch_add(&a->repr, val,            \
        atomic_enum_to_builtin(mo));                \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_sub_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __atomic_fetch_sub(&a->repr, val,            \
        atomic_enum_to_builtin(mo));                \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_and_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __atomic_fetch_and(&a->repr, val,            \
        atomic_enum_to_builtin(mo));                \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_or_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __atomic_fetch_or(&a->repr, val,                \
        atomic_enum_to_builtin(mo));                \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_xor_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __atomic_fetch_xor(&a->repr, val,            \
        atomic_enum_to_builtin(mo));                \
}
#endif /* JEMALLOC_INTERNAL_ATOMIC_GCC_ATOMIC_H */
jemalloc/x64/include/jemalloc/internal/atomic_gcc_sync.h
New file
@@ -0,0 +1,195 @@
#ifndef JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H
#define JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H
#define ATOMIC_INIT(...) {__VA_ARGS__}
typedef enum {
    atomic_memory_order_relaxed,
    atomic_memory_order_acquire,
    atomic_memory_order_release,
    atomic_memory_order_acq_rel,
    atomic_memory_order_seq_cst
} atomic_memory_order_t;
ATOMIC_INLINE void
atomic_fence(atomic_memory_order_t mo) {
    /* Easy cases first: no barrier, and full barrier. */
    if (mo == atomic_memory_order_relaxed) {
        asm volatile("" ::: "memory");
        return;
    }
    if (mo == atomic_memory_order_seq_cst) {
        asm volatile("" ::: "memory");
        __sync_synchronize();
        asm volatile("" ::: "memory");
        return;
    }
    asm volatile("" ::: "memory");
#  if defined(__i386__) || defined(__x86_64__)
    /* This is implicit on x86. */
#  elif defined(__ppc64__)
    asm volatile("lwsync");
#  elif defined(__ppc__)
    asm volatile("sync");
#  elif defined(__sparc__) && defined(__arch64__)
    if (mo == atomic_memory_order_acquire) {
        asm volatile("membar #LoadLoad | #LoadStore");
    } else if (mo == atomic_memory_order_release) {
        asm volatile("membar #LoadStore | #StoreStore");
    } else {
        asm volatile("membar #LoadLoad | #LoadStore | #StoreStore");
    }
#  else
    __sync_synchronize();
#  endif
    asm volatile("" ::: "memory");
}
/*
 * A correct implementation of seq_cst loads and stores on weakly ordered
 * architectures could do either of the following:
 *   1. store() is weak-fence -> store -> strong fence, load() is load ->
 *      strong-fence.
 *   2. store() is strong-fence -> store, load() is strong-fence -> load ->
 *      weak-fence.
 * The tricky thing is, load() and store() above can be the load or store
 * portions of a gcc __sync builtin, so we have to follow GCC's lead, which
 * means going with strategy 2.
 * On strongly ordered architectures, the natural strategy is to stick a strong
 * fence after seq_cst stores, and have naked loads.  So we want the strong
 * fences in different places on different architectures.
 * atomic_pre_sc_load_fence and atomic_post_sc_store_fence allow us to
 * accomplish this.
 */
ATOMIC_INLINE void
atomic_pre_sc_load_fence() {
#  if defined(__i386__) || defined(__x86_64__) ||            \
    (defined(__sparc__) && defined(__arch64__))
    atomic_fence(atomic_memory_order_relaxed);
#  else
    atomic_fence(atomic_memory_order_seq_cst);
#  endif
}
ATOMIC_INLINE void
atomic_post_sc_store_fence() {
#  if defined(__i386__) || defined(__x86_64__) ||            \
    (defined(__sparc__) && defined(__arch64__))
    atomic_fence(atomic_memory_order_seq_cst);
#  else
    atomic_fence(atomic_memory_order_relaxed);
#  endif
}
#define JEMALLOC_GENERATE_ATOMICS(type, short_type,            \
    /* unused */ lg_size)                        \
typedef struct {                            \
    type volatile repr;                        \
} atomic_##short_type##_t;                        \
                                    \
ATOMIC_INLINE type                            \
atomic_load_##short_type(const atomic_##short_type##_t *a,        \
    atomic_memory_order_t mo) {                        \
    if (mo == atomic_memory_order_seq_cst) {            \
        atomic_pre_sc_load_fence();                \
    }                                \
    type result = a->repr;                        \
    if (mo != atomic_memory_order_relaxed) {            \
        atomic_fence(atomic_memory_order_acquire);        \
    }                                \
    return result;                            \
}                                    \
                                    \
ATOMIC_INLINE void                            \
atomic_store_##short_type(atomic_##short_type##_t *a,            \
    type val, atomic_memory_order_t mo) {                \
    if (mo != atomic_memory_order_relaxed) {            \
        atomic_fence(atomic_memory_order_release);        \
    }                                \
    a->repr = val;                            \
    if (mo == atomic_memory_order_seq_cst) {            \
        atomic_post_sc_store_fence();                \
    }                                \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
    atomic_memory_order_t mo) {                                       \
    /*                                \
     * Because of FreeBSD, we care about gcc 4.2, which doesn't have\
     * an atomic exchange builtin.  We fake it with a CAS loop.    \
     */                                \
    while (true) {                            \
        type old = a->repr;                    \
        if (__sync_bool_compare_and_swap(&a->repr, old, val)) {    \
            return old;                    \
        }                            \
    }                                \
}                                    \
                                    \
ATOMIC_INLINE bool                            \
atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,    \
    type *expected, type desired,                                     \
    atomic_memory_order_t success_mo,                          \
    atomic_memory_order_t failure_mo) {                                \
    type prev = __sync_val_compare_and_swap(&a->repr, *expected,    \
        desired);                            \
    if (prev == *expected) {                    \
        return true;                        \
    } else {                            \
        *expected = prev;                    \
        return false;                        \
    }                                \
}                                    \
ATOMIC_INLINE bool                            \
atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,    \
    type *expected, type desired,                                       \
    atomic_memory_order_t success_mo,                            \
    atomic_memory_order_t failure_mo) {                          \
    type prev = __sync_val_compare_and_swap(&a->repr, *expected,    \
        desired);                            \
    if (prev == *expected) {                    \
        return true;                        \
    } else {                            \
        *expected = prev;                    \
        return false;                        \
    }                                \
}
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type,            \
    /* unused */ lg_size)                        \
JEMALLOC_GENERATE_ATOMICS(type, short_type, /* unused */ lg_size)    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_add_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __sync_fetch_and_add(&a->repr, val);            \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_sub_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __sync_fetch_and_sub(&a->repr, val);            \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_and_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __sync_fetch_and_and(&a->repr, val);            \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_or_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __sync_fetch_and_or(&a->repr, val);            \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_xor_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return __sync_fetch_and_xor(&a->repr, val);            \
}
#endif /* JEMALLOC_INTERNAL_ATOMIC_GCC_SYNC_H */
jemalloc/x64/include/jemalloc/internal/atomic_msvc.h
New file
@@ -0,0 +1,158 @@
#ifndef JEMALLOC_INTERNAL_ATOMIC_MSVC_H
#define JEMALLOC_INTERNAL_ATOMIC_MSVC_H
#define ATOMIC_INIT(...) {__VA_ARGS__}
typedef enum {
    atomic_memory_order_relaxed,
    atomic_memory_order_acquire,
    atomic_memory_order_release,
    atomic_memory_order_acq_rel,
    atomic_memory_order_seq_cst
} atomic_memory_order_t;
typedef char atomic_repr_0_t;
typedef short atomic_repr_1_t;
typedef long atomic_repr_2_t;
typedef __int64 atomic_repr_3_t;
ATOMIC_INLINE void
atomic_fence(atomic_memory_order_t mo) {
    _ReadWriteBarrier();
#  if defined(_M_ARM) || defined(_M_ARM64)
    /* ARM needs a barrier for everything but relaxed. */
    if (mo != atomic_memory_order_relaxed) {
        MemoryBarrier();
    }
#  elif defined(_M_IX86) || defined (_M_X64)
    /* x86 needs a barrier only for seq_cst. */
    if (mo == atomic_memory_order_seq_cst) {
        MemoryBarrier();
    }
#  else
#  error "Don't know how to create atomics for this platform for MSVC."
#  endif
    _ReadWriteBarrier();
}
#define ATOMIC_INTERLOCKED_REPR(lg_size) atomic_repr_ ## lg_size ## _t
#define ATOMIC_CONCAT(a, b) ATOMIC_RAW_CONCAT(a, b)
#define ATOMIC_RAW_CONCAT(a, b) a ## b
#define ATOMIC_INTERLOCKED_NAME(base_name, lg_size) ATOMIC_CONCAT(    \
    base_name, ATOMIC_INTERLOCKED_SUFFIX(lg_size))
#define ATOMIC_INTERLOCKED_SUFFIX(lg_size)                \
    ATOMIC_CONCAT(ATOMIC_INTERLOCKED_SUFFIX_, lg_size)
#define ATOMIC_INTERLOCKED_SUFFIX_0 8
#define ATOMIC_INTERLOCKED_SUFFIX_1 16
#define ATOMIC_INTERLOCKED_SUFFIX_2
#define ATOMIC_INTERLOCKED_SUFFIX_3 64
#define JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size)        \
typedef struct {                            \
    ATOMIC_INTERLOCKED_REPR(lg_size) repr;                \
} atomic_##short_type##_t;                        \
                                    \
ATOMIC_INLINE type                            \
atomic_load_##short_type(const atomic_##short_type##_t *a,        \
    atomic_memory_order_t mo) {                        \
    ATOMIC_INTERLOCKED_REPR(lg_size) ret = a->repr;            \
    if (mo != atomic_memory_order_relaxed) {            \
        atomic_fence(atomic_memory_order_acquire);        \
    }                                \
    return (type) ret;                        \
}                                    \
                                    \
ATOMIC_INLINE void                            \
atomic_store_##short_type(atomic_##short_type##_t *a,            \
    type val, atomic_memory_order_t mo) {                \
    if (mo != atomic_memory_order_relaxed) {            \
        atomic_fence(atomic_memory_order_release);        \
    }                                \
    a->repr = (ATOMIC_INTERLOCKED_REPR(lg_size)) val;        \
    if (mo == atomic_memory_order_seq_cst) {            \
        atomic_fence(atomic_memory_order_seq_cst);        \
    }                                \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_exchange_##short_type(atomic_##short_type##_t *a, type val,    \
    atomic_memory_order_t mo) {                        \
    return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchange,    \
        lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);    \
}                                    \
                                    \
ATOMIC_INLINE bool                            \
atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a,    \
    type *expected, type desired, atomic_memory_order_t success_mo,    \
    atomic_memory_order_t failure_mo) {                    \
    ATOMIC_INTERLOCKED_REPR(lg_size) e =                \
        (ATOMIC_INTERLOCKED_REPR(lg_size))*expected;        \
    ATOMIC_INTERLOCKED_REPR(lg_size) d =                \
        (ATOMIC_INTERLOCKED_REPR(lg_size))desired;            \
    ATOMIC_INTERLOCKED_REPR(lg_size) old =                \
        ATOMIC_INTERLOCKED_NAME(_InterlockedCompareExchange,     \
        lg_size)(&a->repr, d, e);                \
    if (old == e) {                            \
        return true;                        \
    } else {                            \
        *expected = (type)old;                    \
        return false;                        \
    }                                \
}                                    \
                                    \
ATOMIC_INLINE bool                            \
atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a,    \
    type *expected, type desired, atomic_memory_order_t success_mo,    \
    atomic_memory_order_t failure_mo) {                    \
    /* We implement the weak version with strong semantics. */    \
    return atomic_compare_exchange_weak_##short_type(a, expected,    \
        desired, success_mo, failure_mo);                \
}
#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, lg_size)    \
JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size)            \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_add_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchangeAdd,    \
        lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);    \
}                                    \
                                    \
ATOMIC_INLINE type                            \
atomic_fetch_sub_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    /*                                \
     * MSVC warns on negation of unsigned operands, but for us it    \
     * gives exactly the right semantics (MAX_TYPE + 1 - operand).    \
     */                                \
    __pragma(warning(push))                        \
    __pragma(warning(disable: 4146))                \
    return atomic_fetch_add_##short_type(a, -val, mo);        \
    __pragma(warning(pop))                        \
}                                    \
ATOMIC_INLINE type                            \
atomic_fetch_and_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedAnd, lg_size)(    \
        &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);        \
}                                    \
ATOMIC_INLINE type                            \
atomic_fetch_or_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedOr, lg_size)(    \
        &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);        \
}                                    \
ATOMIC_INLINE type                            \
atomic_fetch_xor_##short_type(atomic_##short_type##_t *a,        \
    type val, atomic_memory_order_t mo) {                \
    return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedXor, lg_size)(    \
        &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val);        \
}
#endif /* JEMALLOC_INTERNAL_ATOMIC_MSVC_H */
jemalloc/x64/include/jemalloc/internal/background_thread_externs.h
New file
@@ -0,0 +1,32 @@
#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H
#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H
extern bool opt_background_thread;
extern size_t opt_max_background_threads;
extern malloc_mutex_t background_thread_lock;
extern atomic_b_t background_thread_enabled_state;
extern size_t n_background_threads;
extern size_t max_background_threads;
extern background_thread_info_t *background_thread_info;
bool background_thread_create(tsd_t *tsd, unsigned arena_ind);
bool background_threads_enable(tsd_t *tsd);
bool background_threads_disable(tsd_t *tsd);
void background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,
    arena_decay_t *decay, size_t npages_new);
void background_thread_prefork0(tsdn_t *tsdn);
void background_thread_prefork1(tsdn_t *tsdn);
void background_thread_postfork_parent(tsdn_t *tsdn);
void background_thread_postfork_child(tsdn_t *tsdn);
bool background_thread_stats_read(tsdn_t *tsdn,
    background_thread_stats_t *stats);
void background_thread_ctl_init(tsdn_t *tsdn);
#ifdef JEMALLOC_PTHREAD_CREATE_WRAPPER
extern int pthread_create_wrapper(pthread_t *__restrict, const pthread_attr_t *,
    void *(*)(void *), void *__restrict);
#endif
bool background_thread_boot0(void);
bool background_thread_boot1(tsdn_t *tsdn);
#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/background_thread_inlines.h
New file
@@ -0,0 +1,62 @@
#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H
#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H
JEMALLOC_ALWAYS_INLINE bool
background_thread_enabled(void) {
    return atomic_load_b(&background_thread_enabled_state, ATOMIC_RELAXED);
}
JEMALLOC_ALWAYS_INLINE void
background_thread_enabled_set(tsdn_t *tsdn, bool state) {
    malloc_mutex_assert_owner(tsdn, &background_thread_lock);
    atomic_store_b(&background_thread_enabled_state, state, ATOMIC_RELAXED);
}
JEMALLOC_ALWAYS_INLINE background_thread_info_t *
arena_background_thread_info_get(arena_t *arena) {
    unsigned arena_ind = arena_ind_get(arena);
    return &background_thread_info[arena_ind % max_background_threads];
}
JEMALLOC_ALWAYS_INLINE background_thread_info_t *
background_thread_info_get(size_t ind) {
    return &background_thread_info[ind % max_background_threads];
}
JEMALLOC_ALWAYS_INLINE uint64_t
background_thread_wakeup_time_get(background_thread_info_t *info) {
    uint64_t next_wakeup = nstime_ns(&info->next_wakeup);
    assert(atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE) ==
        (next_wakeup == BACKGROUND_THREAD_INDEFINITE_SLEEP));
    return next_wakeup;
}
JEMALLOC_ALWAYS_INLINE void
background_thread_wakeup_time_set(tsdn_t *tsdn, background_thread_info_t *info,
    uint64_t wakeup_time) {
    malloc_mutex_assert_owner(tsdn, &info->mtx);
    atomic_store_b(&info->indefinite_sleep,
        wakeup_time == BACKGROUND_THREAD_INDEFINITE_SLEEP, ATOMIC_RELEASE);
    nstime_init(&info->next_wakeup, wakeup_time);
}
JEMALLOC_ALWAYS_INLINE bool
background_thread_indefinite_sleep(background_thread_info_t *info) {
    return atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE);
}
JEMALLOC_ALWAYS_INLINE void
arena_background_thread_inactivity_check(tsdn_t *tsdn, arena_t *arena,
    bool is_background_thread) {
    if (!background_thread_enabled() || is_background_thread) {
        return;
    }
    background_thread_info_t *info =
        arena_background_thread_info_get(arena);
    if (background_thread_indefinite_sleep(info)) {
        background_thread_interval_check(tsdn, arena,
            &arena->decay_dirty, 0);
    }
}
#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H */
jemalloc/x64/include/jemalloc/internal/background_thread_structs.h
New file
@@ -0,0 +1,54 @@
#ifndef JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H
#define JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H
/* This file really combines "structs" and "types", but only transitionally. */
#if defined(JEMALLOC_BACKGROUND_THREAD) || defined(JEMALLOC_LAZY_LOCK)
#  define JEMALLOC_PTHREAD_CREATE_WRAPPER
#endif
#define BACKGROUND_THREAD_INDEFINITE_SLEEP UINT64_MAX
#define MAX_BACKGROUND_THREAD_LIMIT MALLOCX_ARENA_LIMIT
#define DEFAULT_NUM_BACKGROUND_THREAD 4
typedef enum {
    background_thread_stopped,
    background_thread_started,
    /* Thread waits on the global lock when paused (for arena_reset). */
    background_thread_paused,
} background_thread_state_t;
struct background_thread_info_s {
#ifdef JEMALLOC_BACKGROUND_THREAD
    /* Background thread is pthread specific. */
    pthread_t        thread;
    pthread_cond_t        cond;
#endif
    malloc_mutex_t        mtx;
    background_thread_state_t    state;
    /* When true, it means no wakeup scheduled. */
    atomic_b_t        indefinite_sleep;
    /* Next scheduled wakeup time (absolute time in ns). */
    nstime_t        next_wakeup;
    /*
     *  Since the last background thread run, newly added number of pages
     *  that need to be purged by the next wakeup.  This is adjusted on
     *  epoch advance, and is used to determine whether we should signal the
     *  background thread to wake up earlier.
     */
    size_t            npages_to_purge_new;
    /* Stats: total number of runs since started. */
    uint64_t        tot_n_runs;
    /* Stats: total sleep time since started. */
    nstime_t        tot_sleep_time;
};
typedef struct background_thread_info_s background_thread_info_t;
struct background_thread_stats_s {
    size_t num_threads;
    uint64_t num_runs;
    nstime_t run_interval;
};
typedef struct background_thread_stats_s background_thread_stats_t;
#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_STRUCTS_H */
jemalloc/x64/include/jemalloc/internal/base_externs.h
New file
@@ -0,0 +1,22 @@
#ifndef JEMALLOC_INTERNAL_BASE_EXTERNS_H
#define JEMALLOC_INTERNAL_BASE_EXTERNS_H
extern metadata_thp_mode_t opt_metadata_thp;
extern const char *metadata_thp_mode_names[];
base_t *b0get(void);
base_t *base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
void base_delete(tsdn_t *tsdn, base_t *base);
extent_hooks_t *base_extent_hooks_get(base_t *base);
extent_hooks_t *base_extent_hooks_set(base_t *base,
    extent_hooks_t *extent_hooks);
void *base_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment);
extent_t *base_alloc_extent(tsdn_t *tsdn, base_t *base);
void base_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated,
    size_t *resident, size_t *mapped, size_t *n_thp);
void base_prefork(tsdn_t *tsdn, base_t *base);
void base_postfork_parent(tsdn_t *tsdn, base_t *base);
void base_postfork_child(tsdn_t *tsdn, base_t *base);
bool base_boot(tsdn_t *tsdn);
#endif /* JEMALLOC_INTERNAL_BASE_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/base_inlines.h
New file
@@ -0,0 +1,13 @@
#ifndef JEMALLOC_INTERNAL_BASE_INLINES_H
#define JEMALLOC_INTERNAL_BASE_INLINES_H
static inline unsigned
base_ind_get(const base_t *base) {
    return base->ind;
}
static inline bool
metadata_thp_enabled(void) {
    return (opt_metadata_thp != metadata_thp_disabled);
}
#endif /* JEMALLOC_INTERNAL_BASE_INLINES_H */
jemalloc/x64/include/jemalloc/internal/base_structs.h
New file
@@ -0,0 +1,59 @@
#ifndef JEMALLOC_INTERNAL_BASE_STRUCTS_H
#define JEMALLOC_INTERNAL_BASE_STRUCTS_H
#include "jemalloc/internal/jemalloc_internal_types.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/sc.h"
/* Embedded at the beginning of every block of base-managed virtual memory. */
struct base_block_s {
    /* Total size of block's virtual memory mapping. */
    size_t        size;
    /* Next block in list of base's blocks. */
    base_block_t    *next;
    /* Tracks unused trailing space. */
    extent_t    extent;
};
struct base_s {
    /* Associated arena's index within the arenas array. */
    unsigned    ind;
    /*
     * User-configurable extent hook functions.  Points to an
     * extent_hooks_t.
     */
    atomic_p_t    extent_hooks;
    /* Protects base_alloc() and base_stats_get() operations. */
    malloc_mutex_t    mtx;
    /* Using THP when true (metadata_thp auto mode). */
    bool        auto_thp_switched;
    /*
     * Most recent size class in the series of increasingly large base
     * extents.  Logarithmic spacing between subsequent allocations ensures
     * that the total number of distinct mappings remains small.
     */
    pszind_t    pind_last;
    /* Serial number generation state. */
    size_t        extent_sn_next;
    /* Chain of all blocks associated with base. */
    base_block_t    *blocks;
    /* Heap of extents that track unused trailing space within blocks. */
    extent_heap_t    avail[SC_NSIZES];
    /* Stats, only maintained if config_stats. */
    size_t        allocated;
    size_t        resident;
    size_t        mapped;
    /* Number of THP regions touched. */
    size_t        n_thp;
};
#endif /* JEMALLOC_INTERNAL_BASE_STRUCTS_H */
jemalloc/x64/include/jemalloc/internal/base_types.h
New file
@@ -0,0 +1,33 @@
#ifndef JEMALLOC_INTERNAL_BASE_TYPES_H
#define JEMALLOC_INTERNAL_BASE_TYPES_H
typedef struct base_block_s base_block_t;
typedef struct base_s base_t;
#define METADATA_THP_DEFAULT metadata_thp_disabled
/*
 * In auto mode, arenas switch to huge pages for the base allocator on the
 * second base block.  a0 switches to thp on the 5th block (after 20 megabytes
 * of metadata), since more metadata (e.g. rtree nodes) come from a0's base.
 */
#define BASE_AUTO_THP_THRESHOLD    2
#define BASE_AUTO_THP_THRESHOLD_A0 5
typedef enum {
    metadata_thp_disabled   = 0,
    /*
     * Lazily enable hugepage for metadata. To avoid high RSS caused by THP
     * + low usage arena (i.e. THP becomes a significant percentage), the
     * "auto" option only starts using THP after a base allocator used up
     * the first THP region.  Starting from the second hugepage (in a single
     * arena), "auto" behaves the same as "always", i.e. madvise hugepage
     * right away.
     */
    metadata_thp_auto       = 1,
    metadata_thp_always     = 2,
    metadata_thp_mode_limit = 3
} metadata_thp_mode_t;
#endif /* JEMALLOC_INTERNAL_BASE_TYPES_H */
jemalloc/x64/include/jemalloc/internal/bin.h
New file
@@ -0,0 +1,123 @@
#ifndef JEMALLOC_INTERNAL_BIN_H
#define JEMALLOC_INTERNAL_BIN_H
#include "jemalloc/internal/bin_stats.h"
#include "jemalloc/internal/bin_types.h"
#include "jemalloc/internal/extent_types.h"
#include "jemalloc/internal/extent_structs.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/sc.h"
/*
 * A bin contains a set of extents that are currently being used for slab
 * allocations.
 */
/*
 * Read-only information associated with each element of arena_t's bins array
 * is stored separately, partly to reduce memory usage (only one copy, rather
 * than one per arena), but mainly to avoid false cacheline sharing.
 *
 * Each slab has the following layout:
 *
 *   /--------------------\
 *   | region 0           |
 *   |--------------------|
 *   | region 1           |
 *   |--------------------|
 *   | ...                |
 *   | ...                |
 *   | ...                |
 *   |--------------------|
 *   | region nregs-1     |
 *   \--------------------/
 */
typedef struct bin_info_s bin_info_t;
struct bin_info_s {
    /* Size of regions in a slab for this bin's size class. */
    size_t            reg_size;
    /* Total size of a slab for this bin's size class. */
    size_t            slab_size;
    /* Total number of regions in a slab for this bin's size class. */
    uint32_t        nregs;
    /* Number of sharded bins in each arena for this size class. */
    uint32_t        n_shards;
    /*
     * Metadata used to manipulate bitmaps for slabs associated with this
     * bin.
     */
    bitmap_info_t        bitmap_info;
};
extern bin_info_t bin_infos[SC_NBINS];
typedef struct bin_s bin_t;
struct bin_s {
    /* All operations on bin_t fields require lock ownership. */
    malloc_mutex_t        lock;
    /*
     * Current slab being used to service allocations of this bin's size
     * class.  slabcur is independent of slabs_{nonfull,full}; whenever
     * slabcur is reassigned, the previous slab must be deallocated or
     * inserted into slabs_{nonfull,full}.
     */
    extent_t        *slabcur;
    /*
     * Heap of non-full slabs.  This heap is used to assure that new
     * allocations come from the non-full slab that is oldest/lowest in
     * memory.
     */
    extent_heap_t        slabs_nonfull;
    /* List used to track full slabs. */
    extent_list_t        slabs_full;
    /* Bin statistics. */
    bin_stats_t    stats;
};
/* A set of sharded bins of the same size class. */
typedef struct bins_s bins_t;
struct bins_s {
    /* Sharded bins.  Dynamically sized. */
    bin_t *bin_shards;
};
void bin_shard_sizes_boot(unsigned bin_shards[SC_NBINS]);
bool bin_update_shard_size(unsigned bin_shards[SC_NBINS], size_t start_size,
    size_t end_size, size_t nshards);
void bin_boot(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]);
/* Initializes a bin to empty.  Returns true on error. */
bool bin_init(bin_t *bin);
/* Forking. */
void bin_prefork(tsdn_t *tsdn, bin_t *bin);
void bin_postfork_parent(tsdn_t *tsdn, bin_t *bin);
void bin_postfork_child(tsdn_t *tsdn, bin_t *bin);
/* Stats. */
static inline void
bin_stats_merge(tsdn_t *tsdn, bin_stats_t *dst_bin_stats, bin_t *bin) {
    malloc_mutex_lock(tsdn, &bin->lock);
    malloc_mutex_prof_accum(tsdn, &dst_bin_stats->mutex_data, &bin->lock);
    dst_bin_stats->nmalloc += bin->stats.nmalloc;
    dst_bin_stats->ndalloc += bin->stats.ndalloc;
    dst_bin_stats->nrequests += bin->stats.nrequests;
    dst_bin_stats->curregs += bin->stats.curregs;
    dst_bin_stats->nfills += bin->stats.nfills;
    dst_bin_stats->nflushes += bin->stats.nflushes;
    dst_bin_stats->nslabs += bin->stats.nslabs;
    dst_bin_stats->reslabs += bin->stats.reslabs;
    dst_bin_stats->curslabs += bin->stats.curslabs;
    dst_bin_stats->nonfull_slabs += bin->stats.nonfull_slabs;
    malloc_mutex_unlock(tsdn, &bin->lock);
}
#endif /* JEMALLOC_INTERNAL_BIN_H */
jemalloc/x64/include/jemalloc/internal/bin_stats.h
New file
@@ -0,0 +1,54 @@
#ifndef JEMALLOC_INTERNAL_BIN_STATS_H
#define JEMALLOC_INTERNAL_BIN_STATS_H
#include "jemalloc/internal/mutex_prof.h"
typedef struct bin_stats_s bin_stats_t;
struct bin_stats_s {
    /*
     * Total number of allocation/deallocation requests served directly by
     * the bin.  Note that tcache may allocate an object, then recycle it
     * many times, resulting many increments to nrequests, but only one
     * each to nmalloc and ndalloc.
     */
    uint64_t    nmalloc;
    uint64_t    ndalloc;
    /*
     * Number of allocation requests that correspond to the size of this
     * bin.  This includes requests served by tcache, though tcache only
     * periodically merges into this counter.
     */
    uint64_t    nrequests;
    /*
     * Current number of regions of this size class, including regions
     * currently cached by tcache.
     */
    size_t        curregs;
    /* Number of tcache fills from this bin. */
    uint64_t    nfills;
    /* Number of tcache flushes to this bin. */
    uint64_t    nflushes;
    /* Total number of slabs created for this bin's size class. */
    uint64_t    nslabs;
    /*
     * Total number of slabs reused by extracting them from the slabs heap
     * for this bin's size class.
     */
    uint64_t    reslabs;
    /* Current number of slabs in this bin. */
    size_t        curslabs;
    /* Current size of nonfull slabs heap in this bin. */
    size_t        nonfull_slabs;
    mutex_prof_data_t mutex_data;
};
#endif /* JEMALLOC_INTERNAL_BIN_STATS_H */
jemalloc/x64/include/jemalloc/internal/bin_types.h
New file
@@ -0,0 +1,17 @@
#ifndef JEMALLOC_INTERNAL_BIN_TYPES_H
#define JEMALLOC_INTERNAL_BIN_TYPES_H
#include "jemalloc/internal/sc.h"
#define BIN_SHARDS_MAX (1 << EXTENT_BITS_BINSHARD_WIDTH)
#define N_BIN_SHARDS_DEFAULT 1
/* Used in TSD static initializer only. Real init in arena_bind(). */
#define TSD_BINSHARDS_ZERO_INITIALIZER {{UINT8_MAX}}
typedef struct tsd_binshards_s tsd_binshards_t;
struct tsd_binshards_s {
    uint8_t binshard[SC_NBINS];
};
#endif /* JEMALLOC_INTERNAL_BIN_TYPES_H */
jemalloc/x64/include/jemalloc/internal/bit_util.h
New file
@@ -0,0 +1,239 @@
#ifndef JEMALLOC_INTERNAL_BIT_UTIL_H
#define JEMALLOC_INTERNAL_BIT_UTIL_H
#include "jemalloc/internal/assert.h"
#define BIT_UTIL_INLINE static inline
/* Sanity check. */
#if !defined(JEMALLOC_INTERNAL_FFSLL) || !defined(JEMALLOC_INTERNAL_FFSL) \
    || !defined(JEMALLOC_INTERNAL_FFS)
#  error JEMALLOC_INTERNAL_FFS{,L,LL} should have been defined by configure
#endif
BIT_UTIL_INLINE unsigned
ffs_llu(unsigned long long bitmap) {
    return JEMALLOC_INTERNAL_FFSLL(bitmap);
}
BIT_UTIL_INLINE unsigned
ffs_lu(unsigned long bitmap) {
    return JEMALLOC_INTERNAL_FFSL(bitmap);
}
BIT_UTIL_INLINE unsigned
ffs_u(unsigned bitmap) {
    return JEMALLOC_INTERNAL_FFS(bitmap);
}
#ifdef JEMALLOC_INTERNAL_POPCOUNTL
BIT_UTIL_INLINE unsigned
popcount_lu(unsigned long bitmap) {
  return JEMALLOC_INTERNAL_POPCOUNTL(bitmap);
}
#endif
/*
 * Clears first unset bit in bitmap, and returns
 * place of bit.  bitmap *must not* be 0.
 */
BIT_UTIL_INLINE size_t
cfs_lu(unsigned long* bitmap) {
    size_t bit = ffs_lu(*bitmap) - 1;
    *bitmap ^= ZU(1) << bit;
    return bit;
}
BIT_UTIL_INLINE unsigned
ffs_zu(size_t bitmap) {
#if LG_SIZEOF_PTR == LG_SIZEOF_INT
    return ffs_u(bitmap);
#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG
    return ffs_lu(bitmap);
#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG_LONG
    return ffs_llu(bitmap);
#else
#error No implementation for size_t ffs()
#endif
}
BIT_UTIL_INLINE unsigned
ffs_u64(uint64_t bitmap) {
#if LG_SIZEOF_LONG == 3
    return ffs_lu(bitmap);
#elif LG_SIZEOF_LONG_LONG == 3
    return ffs_llu(bitmap);
#else
#error No implementation for 64-bit ffs()
#endif
}
BIT_UTIL_INLINE unsigned
ffs_u32(uint32_t bitmap) {
#if LG_SIZEOF_INT == 2
    return ffs_u(bitmap);
#else
#error No implementation for 32-bit ffs()
#endif
    return ffs_u(bitmap);
}
BIT_UTIL_INLINE uint64_t
pow2_ceil_u64(uint64_t x) {
#if (defined(__amd64__) || defined(__x86_64__) || defined(JEMALLOC_HAVE_BUILTIN_CLZ))
    if(unlikely(x <= 1)) {
        return x;
    }
    size_t msb_on_index;
#if (defined(__amd64__) || defined(__x86_64__))
    asm ("bsrq %1, %0"
            : "=r"(msb_on_index) // Outputs.
            : "r"(x-1)           // Inputs.
        );
#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
    msb_on_index = (63 ^ __builtin_clzll(x - 1));
#endif
    assert(msb_on_index < 63);
    return 1ULL << (msb_on_index + 1);
#else
    x--;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    x |= x >> 32;
    x++;
    return x;
#endif
}
BIT_UTIL_INLINE uint32_t
pow2_ceil_u32(uint32_t x) {
#if ((defined(__i386__) || defined(JEMALLOC_HAVE_BUILTIN_CLZ)) && (!defined(__s390__)))
    if(unlikely(x <= 1)) {
        return x;
    }
    size_t msb_on_index;
#if (defined(__i386__))
    asm ("bsr %1, %0"
            : "=r"(msb_on_index) // Outputs.
            : "r"(x-1)           // Inputs.
        );
#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
    msb_on_index = (31 ^ __builtin_clz(x - 1));
#endif
    assert(msb_on_index < 31);
    return 1U << (msb_on_index + 1);
#else
    x--;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    x++;
    return x;
#endif
}
/* Compute the smallest power of 2 that is >= x. */
BIT_UTIL_INLINE size_t
pow2_ceil_zu(size_t x) {
#if (LG_SIZEOF_PTR == 3)
    return pow2_ceil_u64(x);
#else
    return pow2_ceil_u32(x);
#endif
}
#if (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))
BIT_UTIL_INLINE unsigned
lg_floor(size_t x) {
    size_t ret;
    assert(x != 0);
    asm ("bsr %1, %0"
        : "=r"(ret) // Outputs.
        : "r"(x)    // Inputs.
        );
    assert(ret < UINT_MAX);
    return (unsigned)ret;
}
#elif (defined(_MSC_VER))
BIT_UTIL_INLINE unsigned
lg_floor(size_t x) {
    unsigned long ret;
    assert(x != 0);
#if (LG_SIZEOF_PTR == 3)
    _BitScanReverse64(&ret, x);
#elif (LG_SIZEOF_PTR == 2)
    _BitScanReverse(&ret, x);
#else
#  error "Unsupported type size for lg_floor()"
#endif
    assert(ret < UINT_MAX);
    return (unsigned)ret;
}
#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
BIT_UTIL_INLINE unsigned
lg_floor(size_t x) {
    assert(x != 0);
#if (LG_SIZEOF_PTR == LG_SIZEOF_INT)
    return ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clz(x);
#elif (LG_SIZEOF_PTR == LG_SIZEOF_LONG)
    return ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clzl(x);
#else
#  error "Unsupported type size for lg_floor()"
#endif
}
#else
BIT_UTIL_INLINE unsigned
lg_floor(size_t x) {
    assert(x != 0);
    x |= (x >> 1);
    x |= (x >> 2);
    x |= (x >> 4);
    x |= (x >> 8);
    x |= (x >> 16);
#if (LG_SIZEOF_PTR == 3)
    x |= (x >> 32);
#endif
    if (x == SIZE_T_MAX) {
        return (8 << LG_SIZEOF_PTR) - 1;
    }
    x++;
    return ffs_zu(x) - 2;
}
#endif
BIT_UTIL_INLINE unsigned
lg_ceil(size_t x) {
    return lg_floor(x) + ((x & (x - 1)) == 0 ? 0 : 1);
}
#undef BIT_UTIL_INLINE
/* A compile-time version of lg_floor and lg_ceil. */
#define LG_FLOOR_1(x) 0
#define LG_FLOOR_2(x) (x < (1ULL << 1) ? LG_FLOOR_1(x) : 1 + LG_FLOOR_1(x >> 1))
#define LG_FLOOR_4(x) (x < (1ULL << 2) ? LG_FLOOR_2(x) : 2 + LG_FLOOR_2(x >> 2))
#define LG_FLOOR_8(x) (x < (1ULL << 4) ? LG_FLOOR_4(x) : 4 + LG_FLOOR_4(x >> 4))
#define LG_FLOOR_16(x) (x < (1ULL << 8) ? LG_FLOOR_8(x) : 8 + LG_FLOOR_8(x >> 8))
#define LG_FLOOR_32(x) (x < (1ULL << 16) ? LG_FLOOR_16(x) : 16 + LG_FLOOR_16(x >> 16))
#define LG_FLOOR_64(x) (x < (1ULL << 32) ? LG_FLOOR_32(x) : 32 + LG_FLOOR_32(x >> 32))
#if LG_SIZEOF_PTR == 2
#  define LG_FLOOR(x) LG_FLOOR_32((x))
#else
#  define LG_FLOOR(x) LG_FLOOR_64((x))
#endif
#define LG_CEIL(x) (LG_FLOOR(x) + (((x) & ((x) - 1)) == 0 ? 0 : 1))
#endif /* JEMALLOC_INTERNAL_BIT_UTIL_H */
jemalloc/x64/include/jemalloc/internal/bitmap.h
New file
@@ -0,0 +1,369 @@
#ifndef JEMALLOC_INTERNAL_BITMAP_H
#define JEMALLOC_INTERNAL_BITMAP_H
#include "jemalloc/internal/arena_types.h"
#include "jemalloc/internal/bit_util.h"
#include "jemalloc/internal/sc.h"
typedef unsigned long bitmap_t;
#define LG_SIZEOF_BITMAP    LG_SIZEOF_LONG
/* Maximum bitmap bit count is 2^LG_BITMAP_MAXBITS. */
#if LG_SLAB_MAXREGS > LG_CEIL(SC_NSIZES)
/* Maximum bitmap bit count is determined by maximum regions per slab. */
#  define LG_BITMAP_MAXBITS    LG_SLAB_MAXREGS
#else
/* Maximum bitmap bit count is determined by number of extent size classes. */
#  define LG_BITMAP_MAXBITS    LG_CEIL(SC_NSIZES)
#endif
#define BITMAP_MAXBITS        (ZU(1) << LG_BITMAP_MAXBITS)
/* Number of bits per group. */
#define LG_BITMAP_GROUP_NBITS        (LG_SIZEOF_BITMAP + 3)
#define BITMAP_GROUP_NBITS        (1U << LG_BITMAP_GROUP_NBITS)
#define BITMAP_GROUP_NBITS_MASK        (BITMAP_GROUP_NBITS-1)
/*
 * Do some analysis on how big the bitmap is before we use a tree.  For a brute
 * force linear search, if we would have to call ffs_lu() more than 2^3 times,
 * use a tree instead.
 */
#if LG_BITMAP_MAXBITS - LG_BITMAP_GROUP_NBITS > 3
#  define BITMAP_USE_TREE
#endif
/* Number of groups required to store a given number of bits. */
#define BITMAP_BITS2GROUPS(nbits)                    \
    (((nbits) + BITMAP_GROUP_NBITS_MASK) >> LG_BITMAP_GROUP_NBITS)
/*
 * Number of groups required at a particular level for a given number of bits.
 */
#define BITMAP_GROUPS_L0(nbits)                        \
    BITMAP_BITS2GROUPS(nbits)
#define BITMAP_GROUPS_L1(nbits)                        \
    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(nbits))
#define BITMAP_GROUPS_L2(nbits)                        \
    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))
#define BITMAP_GROUPS_L3(nbits)                        \
    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(        \
    BITMAP_BITS2GROUPS((nbits)))))
#define BITMAP_GROUPS_L4(nbits)                        \
    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS(        \
    BITMAP_BITS2GROUPS(BITMAP_BITS2GROUPS((nbits))))))
/*
 * Assuming the number of levels, number of groups required for a given number
 * of bits.
 */
#define BITMAP_GROUPS_1_LEVEL(nbits)                    \
    BITMAP_GROUPS_L0(nbits)
#define BITMAP_GROUPS_2_LEVEL(nbits)                    \
    (BITMAP_GROUPS_1_LEVEL(nbits) + BITMAP_GROUPS_L1(nbits))
#define BITMAP_GROUPS_3_LEVEL(nbits)                    \
    (BITMAP_GROUPS_2_LEVEL(nbits) + BITMAP_GROUPS_L2(nbits))
#define BITMAP_GROUPS_4_LEVEL(nbits)                    \
    (BITMAP_GROUPS_3_LEVEL(nbits) + BITMAP_GROUPS_L3(nbits))
#define BITMAP_GROUPS_5_LEVEL(nbits)                    \
    (BITMAP_GROUPS_4_LEVEL(nbits) + BITMAP_GROUPS_L4(nbits))
/*
 * Maximum number of groups required to support LG_BITMAP_MAXBITS.
 */
#ifdef BITMAP_USE_TREE
#if LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS
#  define BITMAP_GROUPS(nbits)    BITMAP_GROUPS_1_LEVEL(nbits)
#  define BITMAP_GROUPS_MAX    BITMAP_GROUPS_1_LEVEL(BITMAP_MAXBITS)
#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 2
#  define BITMAP_GROUPS(nbits)    BITMAP_GROUPS_2_LEVEL(nbits)
#  define BITMAP_GROUPS_MAX    BITMAP_GROUPS_2_LEVEL(BITMAP_MAXBITS)
#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 3
#  define BITMAP_GROUPS(nbits)    BITMAP_GROUPS_3_LEVEL(nbits)
#  define BITMAP_GROUPS_MAX    BITMAP_GROUPS_3_LEVEL(BITMAP_MAXBITS)
#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 4
#  define BITMAP_GROUPS(nbits)    BITMAP_GROUPS_4_LEVEL(nbits)
#  define BITMAP_GROUPS_MAX    BITMAP_GROUPS_4_LEVEL(BITMAP_MAXBITS)
#elif LG_BITMAP_MAXBITS <= LG_BITMAP_GROUP_NBITS * 5
#  define BITMAP_GROUPS(nbits)    BITMAP_GROUPS_5_LEVEL(nbits)
#  define BITMAP_GROUPS_MAX    BITMAP_GROUPS_5_LEVEL(BITMAP_MAXBITS)
#else
#  error "Unsupported bitmap size"
#endif
/*
 * Maximum number of levels possible.  This could be statically computed based
 * on LG_BITMAP_MAXBITS:
 *
 * #define BITMAP_MAX_LEVELS \
 *     (LG_BITMAP_MAXBITS / LG_SIZEOF_BITMAP) \
 *     + !!(LG_BITMAP_MAXBITS % LG_SIZEOF_BITMAP)
 *
 * However, that would not allow the generic BITMAP_INFO_INITIALIZER() macro, so
 * instead hardcode BITMAP_MAX_LEVELS to the largest number supported by the
 * various cascading macros.  The only additional cost this incurs is some
 * unused trailing entries in bitmap_info_t structures; the bitmaps themselves
 * are not impacted.
 */
#define BITMAP_MAX_LEVELS    5
#define BITMAP_INFO_INITIALIZER(nbits) {                \
    /* nbits. */                            \
    nbits,                                \
    /* nlevels. */                            \
    (BITMAP_GROUPS_L0(nbits) > BITMAP_GROUPS_L1(nbits)) +        \
        (BITMAP_GROUPS_L1(nbits) > BITMAP_GROUPS_L2(nbits)) +    \
        (BITMAP_GROUPS_L2(nbits) > BITMAP_GROUPS_L3(nbits)) +    \
        (BITMAP_GROUPS_L3(nbits) > BITMAP_GROUPS_L4(nbits)) + 1,    \
    /* levels. */                            \
    {                                \
        {0},                            \
        {BITMAP_GROUPS_L0(nbits)},                \
        {BITMAP_GROUPS_L1(nbits) + BITMAP_GROUPS_L0(nbits)},    \
        {BITMAP_GROUPS_L2(nbits) + BITMAP_GROUPS_L1(nbits) +    \
            BITMAP_GROUPS_L0(nbits)},                \
        {BITMAP_GROUPS_L3(nbits) + BITMAP_GROUPS_L2(nbits) +    \
            BITMAP_GROUPS_L1(nbits) + BITMAP_GROUPS_L0(nbits)},    \
        {BITMAP_GROUPS_L4(nbits) + BITMAP_GROUPS_L3(nbits) +    \
             BITMAP_GROUPS_L2(nbits) + BITMAP_GROUPS_L1(nbits)    \
             + BITMAP_GROUPS_L0(nbits)}                \
    }                                \
}
#else /* BITMAP_USE_TREE */
#define BITMAP_GROUPS(nbits)    BITMAP_BITS2GROUPS(nbits)
#define BITMAP_GROUPS_MAX    BITMAP_BITS2GROUPS(BITMAP_MAXBITS)
#define BITMAP_INFO_INITIALIZER(nbits) {                \
    /* nbits. */                            \
    nbits,                                \
    /* ngroups. */                            \
    BITMAP_BITS2GROUPS(nbits)                    \
}
#endif /* BITMAP_USE_TREE */
typedef struct bitmap_level_s {
    /* Offset of this level's groups within the array of groups. */
    size_t group_offset;
} bitmap_level_t;
typedef struct bitmap_info_s {
    /* Logical number of bits in bitmap (stored at bottom level). */
    size_t nbits;
#ifdef BITMAP_USE_TREE
    /* Number of levels necessary for nbits. */
    unsigned nlevels;
    /*
     * Only the first (nlevels+1) elements are used, and levels are ordered
     * bottom to top (e.g. the bottom level is stored in levels[0]).
     */
    bitmap_level_t levels[BITMAP_MAX_LEVELS+1];
#else /* BITMAP_USE_TREE */
    /* Number of groups necessary for nbits. */
    size_t ngroups;
#endif /* BITMAP_USE_TREE */
} bitmap_info_t;
void bitmap_info_init(bitmap_info_t *binfo, size_t nbits);
void bitmap_init(bitmap_t *bitmap, const bitmap_info_t *binfo, bool fill);
size_t bitmap_size(const bitmap_info_t *binfo);
static inline bool
bitmap_full(bitmap_t *bitmap, const bitmap_info_t *binfo) {
#ifdef BITMAP_USE_TREE
    size_t rgoff = binfo->levels[binfo->nlevels].group_offset - 1;
    bitmap_t rg = bitmap[rgoff];
    /* The bitmap is full iff the root group is 0. */
    return (rg == 0);
#else
    size_t i;
    for (i = 0; i < binfo->ngroups; i++) {
        if (bitmap[i] != 0) {
            return false;
        }
    }
    return true;
#endif
}
static inline bool
bitmap_get(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {
    size_t goff;
    bitmap_t g;
    assert(bit < binfo->nbits);
    goff = bit >> LG_BITMAP_GROUP_NBITS;
    g = bitmap[goff];
    return !(g & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)));
}
static inline void
bitmap_set(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {
    size_t goff;
    bitmap_t *gp;
    bitmap_t g;
    assert(bit < binfo->nbits);
    assert(!bitmap_get(bitmap, binfo, bit));
    goff = bit >> LG_BITMAP_GROUP_NBITS;
    gp = &bitmap[goff];
    g = *gp;
    assert(g & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)));
    g ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);
    *gp = g;
    assert(bitmap_get(bitmap, binfo, bit));
#ifdef BITMAP_USE_TREE
    /* Propagate group state transitions up the tree. */
    if (g == 0) {
        unsigned i;
        for (i = 1; i < binfo->nlevels; i++) {
            bit = goff;
            goff = bit >> LG_BITMAP_GROUP_NBITS;
            gp = &bitmap[binfo->levels[i].group_offset + goff];
            g = *gp;
            assert(g & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)));
            g ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);
            *gp = g;
            if (g != 0) {
                break;
            }
        }
    }
#endif
}
/* ffu: find first unset >= bit. */
static inline size_t
bitmap_ffu(const bitmap_t *bitmap, const bitmap_info_t *binfo, size_t min_bit) {
    assert(min_bit < binfo->nbits);
#ifdef BITMAP_USE_TREE
    size_t bit = 0;
    for (unsigned level = binfo->nlevels; level--;) {
        size_t lg_bits_per_group = (LG_BITMAP_GROUP_NBITS * (level +
            1));
        bitmap_t group = bitmap[binfo->levels[level].group_offset + (bit
            >> lg_bits_per_group)];
        unsigned group_nmask = (unsigned)(((min_bit > bit) ? (min_bit -
            bit) : 0) >> (lg_bits_per_group - LG_BITMAP_GROUP_NBITS));
        assert(group_nmask <= BITMAP_GROUP_NBITS);
        bitmap_t group_mask = ~((1LU << group_nmask) - 1);
        bitmap_t group_masked = group & group_mask;
        if (group_masked == 0LU) {
            if (group == 0LU) {
                return binfo->nbits;
            }
            /*
             * min_bit was preceded by one or more unset bits in
             * this group, but there are no other unset bits in this
             * group.  Try again starting at the first bit of the
             * next sibling.  This will recurse at most once per
             * non-root level.
             */
            size_t sib_base = bit + (ZU(1) << lg_bits_per_group);
            assert(sib_base > min_bit);
            assert(sib_base > bit);
            if (sib_base >= binfo->nbits) {
                return binfo->nbits;
            }
            return bitmap_ffu(bitmap, binfo, sib_base);
        }
        bit += ((size_t)(ffs_lu(group_masked) - 1)) <<
            (lg_bits_per_group - LG_BITMAP_GROUP_NBITS);
    }
    assert(bit >= min_bit);
    assert(bit < binfo->nbits);
    return bit;
#else
    size_t i = min_bit >> LG_BITMAP_GROUP_NBITS;
    bitmap_t g = bitmap[i] & ~((1LU << (min_bit & BITMAP_GROUP_NBITS_MASK))
        - 1);
    size_t bit;
    do {
        bit = ffs_lu(g);
        if (bit != 0) {
            return (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);
        }
        i++;
        g = bitmap[i];
    } while (i < binfo->ngroups);
    return binfo->nbits;
#endif
}
/* sfu: set first unset. */
static inline size_t
bitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo) {
    size_t bit;
    bitmap_t g;
    unsigned i;
    assert(!bitmap_full(bitmap, binfo));
#ifdef BITMAP_USE_TREE
    i = binfo->nlevels - 1;
    g = bitmap[binfo->levels[i].group_offset];
    bit = ffs_lu(g) - 1;
    while (i > 0) {
        i--;
        g = bitmap[binfo->levels[i].group_offset + bit];
        bit = (bit << LG_BITMAP_GROUP_NBITS) + (ffs_lu(g) - 1);
    }
#else
    i = 0;
    g = bitmap[0];
    while ((bit = ffs_lu(g)) == 0) {
        i++;
        g = bitmap[i];
    }
    bit = (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);
#endif
    bitmap_set(bitmap, binfo, bit);
    return bit;
}
static inline void
bitmap_unset(bitmap_t *bitmap, const bitmap_info_t *binfo, size_t bit) {
    size_t goff;
    bitmap_t *gp;
    bitmap_t g;
    UNUSED bool propagate;
    assert(bit < binfo->nbits);
    assert(bitmap_get(bitmap, binfo, bit));
    goff = bit >> LG_BITMAP_GROUP_NBITS;
    gp = &bitmap[goff];
    g = *gp;
    propagate = (g == 0);
    assert((g & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK))) == 0);
    g ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);
    *gp = g;
    assert(!bitmap_get(bitmap, binfo, bit));
#ifdef BITMAP_USE_TREE
    /* Propagate group state transitions up the tree. */
    if (propagate) {
        unsigned i;
        for (i = 1; i < binfo->nlevels; i++) {
            bit = goff;
            goff = bit >> LG_BITMAP_GROUP_NBITS;
            gp = &bitmap[binfo->levels[i].group_offset + goff];
            g = *gp;
            propagate = (g == 0);
            assert((g & (ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK)))
                == 0);
            g ^= ZU(1) << (bit & BITMAP_GROUP_NBITS_MASK);
            *gp = g;
            if (!propagate) {
                break;
            }
        }
    }
#endif /* BITMAP_USE_TREE */
}
#endif /* JEMALLOC_INTERNAL_BITMAP_H */
jemalloc/x64/include/jemalloc/internal/cache_bin.h
New file
@@ -0,0 +1,131 @@
#ifndef JEMALLOC_INTERNAL_CACHE_BIN_H
#define JEMALLOC_INTERNAL_CACHE_BIN_H
#include "jemalloc/internal/ql.h"
/*
 * The cache_bins are the mechanism that the tcache and the arena use to
 * communicate.  The tcache fills from and flushes to the arena by passing a
 * cache_bin_t to fill/flush.  When the arena needs to pull stats from the
 * tcaches associated with it, it does so by iterating over its
 * cache_bin_array_descriptor_t objects and reading out per-bin stats it
 * contains.  This makes it so that the arena need not know about the existence
 * of the tcache at all.
 */
/*
 * The count of the number of cached allocations in a bin.  We make this signed
 * so that negative numbers can encode "invalid" states (e.g. a low water mark
 * of -1 for a cache that has been depleted).
 */
typedef int32_t cache_bin_sz_t;
typedef struct cache_bin_stats_s cache_bin_stats_t;
struct cache_bin_stats_s {
    /*
     * Number of allocation requests that corresponded to the size of this
     * bin.
     */
    uint64_t nrequests;
};
/*
 * Read-only information associated with each element of tcache_t's tbins array
 * is stored separately, mainly to reduce memory usage.
 */
typedef struct cache_bin_info_s cache_bin_info_t;
struct cache_bin_info_s {
    /* Upper limit on ncached. */
    cache_bin_sz_t ncached_max;
};
typedef struct cache_bin_s cache_bin_t;
struct cache_bin_s {
    /* Min # cached since last GC. */
    cache_bin_sz_t low_water;
    /* # of cached objects. */
    cache_bin_sz_t ncached;
    /*
     * ncached and stats are both modified frequently.  Let's keep them
     * close so that they have a higher chance of being on the same
     * cacheline, thus less write-backs.
     */
    cache_bin_stats_t tstats;
    /*
     * Stack of available objects.
     *
     * To make use of adjacent cacheline prefetch, the items in the avail
     * stack goes to higher address for newer allocations.  avail points
     * just above the available space, which means that
     * avail[-ncached, ... -1] are available items and the lowest item will
     * be allocated first.
     */
    void **avail;
};
typedef struct cache_bin_array_descriptor_s cache_bin_array_descriptor_t;
struct cache_bin_array_descriptor_s {
    /*
     * The arena keeps a list of the cache bins associated with it, for
     * stats collection.
     */
    ql_elm(cache_bin_array_descriptor_t) link;
    /* Pointers to the tcache bins. */
    cache_bin_t *bins_small;
    cache_bin_t *bins_large;
};
static inline void
cache_bin_array_descriptor_init(cache_bin_array_descriptor_t *descriptor,
    cache_bin_t *bins_small, cache_bin_t *bins_large) {
    ql_elm_new(descriptor, link);
    descriptor->bins_small = bins_small;
    descriptor->bins_large = bins_large;
}
JEMALLOC_ALWAYS_INLINE void *
cache_bin_alloc_easy(cache_bin_t *bin, bool *success) {
    void *ret;
    bin->ncached--;
    /*
     * Check for both bin->ncached == 0 and ncached < low_water
     * in a single branch.
     */
    if (unlikely(bin->ncached <= bin->low_water)) {
        bin->low_water = bin->ncached;
        if (bin->ncached == -1) {
            bin->ncached = 0;
            *success = false;
            return NULL;
        }
    }
    /*
     * success (instead of ret) should be checked upon the return of this
     * function.  We avoid checking (ret == NULL) because there is never a
     * null stored on the avail stack (which is unknown to the compiler),
     * and eagerly checking ret would cause pipeline stall (waiting for the
     * cacheline).
     */
    *success = true;
    ret = *(bin->avail - (bin->ncached + 1));
    return ret;
}
JEMALLOC_ALWAYS_INLINE bool
cache_bin_dalloc_easy(cache_bin_t *bin, cache_bin_info_t *bin_info, void *ptr) {
    if (unlikely(bin->ncached == bin_info->ncached_max)) {
        return false;
    }
    assert(bin->ncached < bin_info->ncached_max);
    bin->ncached++;
    *(bin->avail - bin->ncached) = ptr;
    return true;
}
#endif /* JEMALLOC_INTERNAL_CACHE_BIN_H */
jemalloc/x64/include/jemalloc/internal/ckh.h
New file
@@ -0,0 +1,101 @@
#ifndef JEMALLOC_INTERNAL_CKH_H
#define JEMALLOC_INTERNAL_CKH_H
#include "jemalloc/internal/tsd.h"
/* Cuckoo hashing implementation.  Skip to the end for the interface. */
/******************************************************************************/
/* INTERNAL DEFINITIONS -- IGNORE */
/******************************************************************************/
/* Maintain counters used to get an idea of performance. */
/* #define CKH_COUNT */
/* Print counter values in ckh_delete() (requires CKH_COUNT). */
/* #define CKH_VERBOSE */
/*
 * There are 2^LG_CKH_BUCKET_CELLS cells in each hash table bucket.  Try to fit
 * one bucket per L1 cache line.
 */
#define LG_CKH_BUCKET_CELLS (LG_CACHELINE - LG_SIZEOF_PTR - 1)
/* Typedefs to allow easy function pointer passing. */
typedef void ckh_hash_t (const void *, size_t[2]);
typedef bool ckh_keycomp_t (const void *, const void *);
/* Hash table cell. */
typedef struct {
    const void *key;
    const void *data;
} ckhc_t;
/* The hash table itself. */
typedef struct {
#ifdef CKH_COUNT
    /* Counters used to get an idea of performance. */
    uint64_t ngrows;
    uint64_t nshrinks;
    uint64_t nshrinkfails;
    uint64_t ninserts;
    uint64_t nrelocs;
#endif
    /* Used for pseudo-random number generation. */
    uint64_t prng_state;
    /* Total number of items. */
    size_t count;
    /*
     * Minimum and current number of hash table buckets.  There are
     * 2^LG_CKH_BUCKET_CELLS cells per bucket.
     */
    unsigned lg_minbuckets;
    unsigned lg_curbuckets;
    /* Hash and comparison functions. */
    ckh_hash_t *hash;
    ckh_keycomp_t *keycomp;
    /* Hash table with 2^lg_curbuckets buckets. */
    ckhc_t *tab;
} ckh_t;
/******************************************************************************/
/* BEGIN PUBLIC API */
/******************************************************************************/
/* Lifetime management.  Minitems is the initial capacity. */
bool ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,
    ckh_keycomp_t *keycomp);
void ckh_delete(tsd_t *tsd, ckh_t *ckh);
/* Get the number of elements in the set. */
size_t ckh_count(ckh_t *ckh);
/*
 * To iterate over the elements in the table, initialize *tabind to 0 and call
 * this function until it returns true.  Each call that returns false will
 * update *key and *data to the next element in the table, assuming the pointers
 * are non-NULL.
 */
bool ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data);
/*
 * Basic hash table operations -- insert, removal, lookup.  For ckh_remove and
 * ckh_search, key or data can be NULL.  The hash-table only stores pointers to
 * the key and value, and doesn't do any lifetime management.
 */
bool ckh_insert(tsd_t *tsd, ckh_t *ckh, const void *key, const void *data);
bool ckh_remove(tsd_t *tsd, ckh_t *ckh, const void *searchkey, void **key,
    void **data);
bool ckh_search(ckh_t *ckh, const void *searchkey, void **key, void **data);
/* Some useful hash and comparison functions for strings and pointers. */
void ckh_string_hash(const void *key, size_t r_hash[2]);
bool ckh_string_keycomp(const void *k1, const void *k2);
void ckh_pointer_hash(const void *key, size_t r_hash[2]);
bool ckh_pointer_keycomp(const void *k1, const void *k2);
#endif /* JEMALLOC_INTERNAL_CKH_H */
jemalloc/x64/include/jemalloc/internal/ctl.h
New file
@@ -0,0 +1,134 @@
#ifndef JEMALLOC_INTERNAL_CTL_H
#define JEMALLOC_INTERNAL_CTL_H
#include "jemalloc/internal/jemalloc_internal_types.h"
#include "jemalloc/internal/malloc_io.h"
#include "jemalloc/internal/mutex_prof.h"
#include "jemalloc/internal/ql.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/stats.h"
/* Maximum ctl tree depth. */
#define CTL_MAX_DEPTH    7
typedef struct ctl_node_s {
    bool named;
} ctl_node_t;
typedef struct ctl_named_node_s {
    ctl_node_t node;
    const char *name;
    /* If (nchildren == 0), this is a terminal node. */
    size_t nchildren;
    const ctl_node_t *children;
    int (*ctl)(tsd_t *, const size_t *, size_t, void *, size_t *, void *,
        size_t);
} ctl_named_node_t;
typedef struct ctl_indexed_node_s {
    struct ctl_node_s node;
    const ctl_named_node_t *(*index)(tsdn_t *, const size_t *, size_t,
        size_t);
} ctl_indexed_node_t;
typedef struct ctl_arena_stats_s {
    arena_stats_t astats;
    /* Aggregate stats for small size classes, based on bin stats. */
    size_t allocated_small;
    uint64_t nmalloc_small;
    uint64_t ndalloc_small;
    uint64_t nrequests_small;
    uint64_t nfills_small;
    uint64_t nflushes_small;
    bin_stats_t bstats[SC_NBINS];
    arena_stats_large_t lstats[SC_NSIZES - SC_NBINS];
    arena_stats_extents_t estats[SC_NPSIZES];
} ctl_arena_stats_t;
typedef struct ctl_stats_s {
    size_t allocated;
    size_t active;
    size_t metadata;
    size_t metadata_thp;
    size_t resident;
    size_t mapped;
    size_t retained;
    background_thread_stats_t background_thread;
    mutex_prof_data_t mutex_prof_data[mutex_prof_num_global_mutexes];
} ctl_stats_t;
typedef struct ctl_arena_s ctl_arena_t;
struct ctl_arena_s {
    unsigned arena_ind;
    bool initialized;
    ql_elm(ctl_arena_t) destroyed_link;
    /* Basic stats, supported even if !config_stats. */
    unsigned nthreads;
    const char *dss;
    ssize_t dirty_decay_ms;
    ssize_t muzzy_decay_ms;
    size_t pactive;
    size_t pdirty;
    size_t pmuzzy;
    /* NULL if !config_stats. */
    ctl_arena_stats_t *astats;
};
typedef struct ctl_arenas_s {
    uint64_t epoch;
    unsigned narenas;
    ql_head(ctl_arena_t) destroyed;
    /*
     * Element 0 corresponds to merged stats for extant arenas (accessed via
     * MALLCTL_ARENAS_ALL), element 1 corresponds to merged stats for
     * destroyed arenas (accessed via MALLCTL_ARENAS_DESTROYED), and the
     * remaining MALLOCX_ARENA_LIMIT elements correspond to arenas.
     */
    ctl_arena_t *arenas[2 + MALLOCX_ARENA_LIMIT];
} ctl_arenas_t;
int ctl_byname(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,
    void *newp, size_t newlen);
int ctl_nametomib(tsd_t *tsd, const char *name, size_t *mibp, size_t *miblenp);
int ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
    size_t *oldlenp, void *newp, size_t newlen);
bool ctl_boot(void);
void ctl_prefork(tsdn_t *tsdn);
void ctl_postfork_parent(tsdn_t *tsdn);
void ctl_postfork_child(tsdn_t *tsdn);
#define xmallctl(name, oldp, oldlenp, newp, newlen) do {        \
    if (je_mallctl(name, oldp, oldlenp, newp, newlen)        \
        != 0) {                            \
        malloc_printf(                        \
            "<jemalloc>: Failure in xmallctl(\"%s\", ...)\n",    \
            name);                        \
        abort();                        \
    }                                \
} while (0)
#define xmallctlnametomib(name, mibp, miblenp) do {            \
    if (je_mallctlnametomib(name, mibp, miblenp) != 0) {        \
        malloc_printf("<jemalloc>: Failure in "            \
            "xmallctlnametomib(\"%s\", ...)\n", name);        \
        abort();                        \
    }                                \
} while (0)
#define xmallctlbymib(mib, miblen, oldp, oldlenp, newp, newlen) do {    \
    if (je_mallctlbymib(mib, miblen, oldp, oldlenp, newp,        \
        newlen) != 0) {                        \
        malloc_write(                        \
            "<jemalloc>: Failure in xmallctlbymib()\n");    \
        abort();                        \
    }                                \
} while (0)
#endif /* JEMALLOC_INTERNAL_CTL_H */
jemalloc/x64/include/jemalloc/internal/div.h
New file
@@ -0,0 +1,41 @@
#ifndef JEMALLOC_INTERNAL_DIV_H
#define JEMALLOC_INTERNAL_DIV_H
#include "jemalloc/internal/assert.h"
/*
 * This module does the division that computes the index of a region in a slab,
 * given its offset relative to the base.
 * That is, given a divisor d, an n = i * d (all integers), we'll return i.
 * We do some pre-computation to do this more quickly than a CPU division
 * instruction.
 * We bound n < 2^32, and don't support dividing by one.
 */
typedef struct div_info_s div_info_t;
struct div_info_s {
    uint32_t magic;
#ifdef JEMALLOC_DEBUG
    size_t d;
#endif
};
void div_init(div_info_t *div_info, size_t divisor);
static inline size_t
div_compute(div_info_t *div_info, size_t n) {
    assert(n <= (uint32_t)-1);
    /*
     * This generates, e.g. mov; imul; shr on x86-64. On a 32-bit machine,
     * the compilers I tried were all smart enough to turn this into the
     * appropriate "get the high 32 bits of the result of a multiply" (e.g.
     * mul; mov edx eax; on x86, umull on arm, etc.).
     */
    size_t i = ((uint64_t)n * (uint64_t)div_info->magic) >> 32;
#ifdef JEMALLOC_DEBUG
    assert(i * div_info->d == n);
#endif
    return i;
}
#endif /* JEMALLOC_INTERNAL_DIV_H */
jemalloc/x64/include/jemalloc/internal/emitter.h
New file
@@ -0,0 +1,486 @@
#ifndef JEMALLOC_INTERNAL_EMITTER_H
#define JEMALLOC_INTERNAL_EMITTER_H
#include "jemalloc/internal/ql.h"
typedef enum emitter_output_e emitter_output_t;
enum emitter_output_e {
    emitter_output_json,
    emitter_output_table
};
typedef enum emitter_justify_e emitter_justify_t;
enum emitter_justify_e {
    emitter_justify_left,
    emitter_justify_right,
    /* Not for users; just to pass to internal functions. */
    emitter_justify_none
};
typedef enum emitter_type_e emitter_type_t;
enum emitter_type_e {
    emitter_type_bool,
    emitter_type_int,
    emitter_type_unsigned,
    emitter_type_uint32,
    emitter_type_uint64,
    emitter_type_size,
    emitter_type_ssize,
    emitter_type_string,
    /*
     * A title is a column title in a table; it's just a string, but it's
     * not quoted.
     */
    emitter_type_title,
};
typedef struct emitter_col_s emitter_col_t;
struct emitter_col_s {
    /* Filled in by the user. */
    emitter_justify_t justify;
    int width;
    emitter_type_t type;
    union {
        bool bool_val;
        int int_val;
        unsigned unsigned_val;
        uint32_t uint32_val;
        uint32_t uint32_t_val;
        uint64_t uint64_val;
        uint64_t uint64_t_val;
        size_t size_val;
        ssize_t ssize_val;
        const char *str_val;
    };
    /* Filled in by initialization. */
    ql_elm(emitter_col_t) link;
};
typedef struct emitter_row_s emitter_row_t;
struct emitter_row_s {
    ql_head(emitter_col_t) cols;
};
typedef struct emitter_s emitter_t;
struct emitter_s {
    emitter_output_t output;
    /* The output information. */
    void (*write_cb)(void *, const char *);
    void *cbopaque;
    int nesting_depth;
    /* True if we've already emitted a value at the given depth. */
    bool item_at_depth;
    /* True if we emitted a key and will emit corresponding value next. */
    bool emitted_key;
};
/* Internal convenience function.  Write to the emitter the given string. */
JEMALLOC_FORMAT_PRINTF(2, 3)
static inline void
emitter_printf(emitter_t *emitter, const char *format, ...) {
    va_list ap;
    va_start(ap, format);
    malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
    va_end(ap);
}
static inline const char * JEMALLOC_FORMAT_ARG(3)
emitter_gen_fmt(char *out_fmt, size_t out_size, const char *fmt_specifier,
    emitter_justify_t justify, int width) {
    size_t written;
    fmt_specifier++;
    if (justify == emitter_justify_none) {
        written = malloc_snprintf(out_fmt, out_size,
            "%%%s", fmt_specifier);
    } else if (justify == emitter_justify_left) {
        written = malloc_snprintf(out_fmt, out_size,
            "%%-%d%s", width, fmt_specifier);
    } else {
        written = malloc_snprintf(out_fmt, out_size,
            "%%%d%s", width, fmt_specifier);
    }
    /* Only happens in case of bad format string, which *we* choose. */
    assert(written <  out_size);
    return out_fmt;
}
/*
 * Internal.  Emit the given value type in the relevant encoding (so that the
 * bool true gets mapped to json "true", but the string "true" gets mapped to
 * json "\"true\"", for instance.
 *
 * Width is ignored if justify is emitter_justify_none.
 */
static inline void
emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
    emitter_type_t value_type, const void *value) {
    size_t str_written;
#define BUF_SIZE 256
#define FMT_SIZE 10
    /*
     * We dynamically generate a format string to emit, to let us use the
     * snprintf machinery.  This is kinda hacky, but gets the job done
     * quickly without having to think about the various snprintf edge
     * cases.
     */
    char fmt[FMT_SIZE];
    char buf[BUF_SIZE];
#define EMIT_SIMPLE(type, format)                    \
    emitter_printf(emitter,                        \
        emitter_gen_fmt(fmt, FMT_SIZE, format, justify, width),    \
        *(const type *)value);
    switch (value_type) {
    case emitter_type_bool:
        emitter_printf(emitter,
            emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width),
            *(const bool *)value ?  "true" : "false");
        break;
    case emitter_type_int:
        EMIT_SIMPLE(int, "%d")
        break;
    case emitter_type_unsigned:
        EMIT_SIMPLE(unsigned, "%u")
        break;
    case emitter_type_ssize:
        EMIT_SIMPLE(ssize_t, "%zd")
        break;
    case emitter_type_size:
        EMIT_SIMPLE(size_t, "%zu")
        break;
    case emitter_type_string:
        str_written = malloc_snprintf(buf, BUF_SIZE, "\"%s\"",
            *(const char *const *)value);
        /*
         * We control the strings we output; we shouldn't get anything
         * anywhere near the fmt size.
         */
        assert(str_written < BUF_SIZE);
        emitter_printf(emitter,
            emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width), buf);
        break;
    case emitter_type_uint32:
        EMIT_SIMPLE(uint32_t, "%" FMTu32)
        break;
    case emitter_type_uint64:
        EMIT_SIMPLE(uint64_t, "%" FMTu64)
        break;
    case emitter_type_title:
        EMIT_SIMPLE(char *const, "%s");
        break;
    default:
        unreachable();
    }
#undef BUF_SIZE
#undef FMT_SIZE
}
/* Internal functions.  In json mode, tracks nesting state. */
static inline void
emitter_nest_inc(emitter_t *emitter) {
    emitter->nesting_depth++;
    emitter->item_at_depth = false;
}
static inline void
emitter_nest_dec(emitter_t *emitter) {
    emitter->nesting_depth--;
    emitter->item_at_depth = true;
}
static inline void
emitter_indent(emitter_t *emitter) {
    int amount = emitter->nesting_depth;
    const char *indent_str;
    if (emitter->output == emitter_output_json) {
        indent_str = "\t";
    } else {
        amount *= 2;
        indent_str = " ";
    }
    for (int i = 0; i < amount; i++) {
        emitter_printf(emitter, "%s", indent_str);
    }
}
static inline void
emitter_json_key_prefix(emitter_t *emitter) {
    if (emitter->emitted_key) {
        emitter->emitted_key = false;
        return;
    }
    emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
    emitter_indent(emitter);
}
/******************************************************************************/
/* Public functions for emitter_t. */
static inline void
emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
    void (*write_cb)(void *, const char *), void *cbopaque) {
    emitter->output = emitter_output;
    emitter->write_cb = write_cb;
    emitter->cbopaque = cbopaque;
    emitter->item_at_depth = false;
    emitter->emitted_key = false;
    emitter->nesting_depth = 0;
}
/******************************************************************************/
/* JSON public API. */
/*
 * Emits a key (e.g. as appears in an object). The next json entity emitted will
 * be the corresponding value.
 */
static inline void
emitter_json_key(emitter_t *emitter, const char *json_key) {
    if (emitter->output == emitter_output_json) {
        emitter_json_key_prefix(emitter);
        emitter_printf(emitter, "\"%s\": ", json_key);
        emitter->emitted_key = true;
    }
}
static inline void
emitter_json_value(emitter_t *emitter, emitter_type_t value_type,
    const void *value) {
    if (emitter->output == emitter_output_json) {
        emitter_json_key_prefix(emitter);
        emitter_print_value(emitter, emitter_justify_none, -1,
            value_type, value);
        emitter->item_at_depth = true;
    }
}
/* Shorthand for calling emitter_json_key and then emitter_json_value. */
static inline void
emitter_json_kv(emitter_t *emitter, const char *json_key,
    emitter_type_t value_type, const void *value) {
    emitter_json_key(emitter, json_key);
    emitter_json_value(emitter, value_type, value);
}
static inline void
emitter_json_array_begin(emitter_t *emitter) {
    if (emitter->output == emitter_output_json) {
        emitter_json_key_prefix(emitter);
        emitter_printf(emitter, "[");
        emitter_nest_inc(emitter);
    }
}
/* Shorthand for calling emitter_json_key and then emitter_json_array_begin. */
static inline void
emitter_json_array_kv_begin(emitter_t *emitter, const char *json_key) {
    emitter_json_key(emitter, json_key);
    emitter_json_array_begin(emitter);
}
static inline void
emitter_json_array_end(emitter_t *emitter) {
    if (emitter->output == emitter_output_json) {
        assert(emitter->nesting_depth > 0);
        emitter_nest_dec(emitter);
        emitter_printf(emitter, "\n");
        emitter_indent(emitter);
        emitter_printf(emitter, "]");
    }
}
static inline void
emitter_json_object_begin(emitter_t *emitter) {
    if (emitter->output == emitter_output_json) {
        emitter_json_key_prefix(emitter);
        emitter_printf(emitter, "{");
        emitter_nest_inc(emitter);
    }
}
/* Shorthand for calling emitter_json_key and then emitter_json_object_begin. */
static inline void
emitter_json_object_kv_begin(emitter_t *emitter, const char *json_key) {
    emitter_json_key(emitter, json_key);
    emitter_json_object_begin(emitter);
}
static inline void
emitter_json_object_end(emitter_t *emitter) {
    if (emitter->output == emitter_output_json) {
        assert(emitter->nesting_depth > 0);
        emitter_nest_dec(emitter);
        emitter_printf(emitter, "\n");
        emitter_indent(emitter);
        emitter_printf(emitter, "}");
    }
}
/******************************************************************************/
/* Table public API. */
static inline void
emitter_table_dict_begin(emitter_t *emitter, const char *table_key) {
    if (emitter->output == emitter_output_table) {
        emitter_indent(emitter);
        emitter_printf(emitter, "%s\n", table_key);
        emitter_nest_inc(emitter);
    }
}
static inline void
emitter_table_dict_end(emitter_t *emitter) {
    if (emitter->output == emitter_output_table) {
        emitter_nest_dec(emitter);
    }
}
static inline void
emitter_table_kv_note(emitter_t *emitter, const char *table_key,
    emitter_type_t value_type, const void *value,
    const char *table_note_key, emitter_type_t table_note_value_type,
    const void *table_note_value) {
    if (emitter->output == emitter_output_table) {
        emitter_indent(emitter);
        emitter_printf(emitter, "%s: ", table_key);
        emitter_print_value(emitter, emitter_justify_none, -1,
            value_type, value);
        if (table_note_key != NULL) {
            emitter_printf(emitter, " (%s: ", table_note_key);
            emitter_print_value(emitter, emitter_justify_none, -1,
                table_note_value_type, table_note_value);
            emitter_printf(emitter, ")");
        }
        emitter_printf(emitter, "\n");
    }
    emitter->item_at_depth = true;
}
static inline void
emitter_table_kv(emitter_t *emitter, const char *table_key,
    emitter_type_t value_type, const void *value) {
    emitter_table_kv_note(emitter, table_key, value_type, value, NULL,
        emitter_type_bool, NULL);
}
/* Write to the emitter the given string, but only in table mode. */
JEMALLOC_FORMAT_PRINTF(2, 3)
static inline void
emitter_table_printf(emitter_t *emitter, const char *format, ...) {
    if (emitter->output == emitter_output_table) {
        va_list ap;
        va_start(ap, format);
        malloc_vcprintf(emitter->write_cb, emitter->cbopaque, format, ap);
        va_end(ap);
    }
}
static inline void
emitter_table_row(emitter_t *emitter, emitter_row_t *row) {
    if (emitter->output != emitter_output_table) {
        return;
    }
    emitter_col_t *col;
    ql_foreach(col, &row->cols, link) {
        emitter_print_value(emitter, col->justify, col->width,
            col->type, (const void *)&col->bool_val);
    }
    emitter_table_printf(emitter, "\n");
}
static inline void
emitter_row_init(emitter_row_t *row) {
    ql_new(&row->cols);
}
static inline void
emitter_col_init(emitter_col_t *col, emitter_row_t *row) {
    ql_elm_new(col, link);
    ql_tail_insert(&row->cols, col, link);
}
/******************************************************************************/
/*
 * Generalized public API. Emits using either JSON or table, according to
 * settings in the emitter_t. */
/*
 * Note emits a different kv pair as well, but only in table mode.  Omits the
 * note if table_note_key is NULL.
 */
static inline void
emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
    emitter_type_t value_type, const void *value,
    const char *table_note_key, emitter_type_t table_note_value_type,
    const void *table_note_value) {
    if (emitter->output == emitter_output_json) {
        emitter_json_key(emitter, json_key);
        emitter_json_value(emitter, value_type, value);
    } else {
        emitter_table_kv_note(emitter, table_key, value_type, value,
            table_note_key, table_note_value_type, table_note_value);
    }
    emitter->item_at_depth = true;
}
static inline void
emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
    emitter_type_t value_type, const void *value) {
    emitter_kv_note(emitter, json_key, table_key, value_type, value, NULL,
        emitter_type_bool, NULL);
}
static inline void
emitter_dict_begin(emitter_t *emitter, const char *json_key,
    const char *table_header) {
    if (emitter->output == emitter_output_json) {
        emitter_json_key(emitter, json_key);
        emitter_json_object_begin(emitter);
    } else {
        emitter_table_dict_begin(emitter, table_header);
    }
}
static inline void
emitter_dict_end(emitter_t *emitter) {
    if (emitter->output == emitter_output_json) {
        emitter_json_object_end(emitter);
    } else {
        emitter_table_dict_end(emitter);
    }
}
static inline void
emitter_begin(emitter_t *emitter) {
    if (emitter->output == emitter_output_json) {
        assert(emitter->nesting_depth == 0);
        emitter_printf(emitter, "{");
        emitter_nest_inc(emitter);
    } else {
        /*
         * This guarantees that we always call write_cb at least once.
         * This is useful if some invariant is established by each call
         * to write_cb, but doesn't hold initially: e.g., some buffer
         * holds a null-terminated string.
         */
        emitter_printf(emitter, "%s", "");
    }
}
static inline void
emitter_end(emitter_t *emitter) {
    if (emitter->output == emitter_output_json) {
        assert(emitter->nesting_depth == 1);
        emitter_nest_dec(emitter);
        emitter_printf(emitter, "\n}\n");
    }
}
#endif /* JEMALLOC_INTERNAL_EMITTER_H */
jemalloc/x64/include/jemalloc/internal/extent_dss.h
New file
@@ -0,0 +1,26 @@
#ifndef JEMALLOC_INTERNAL_EXTENT_DSS_H
#define JEMALLOC_INTERNAL_EXTENT_DSS_H
typedef enum {
    dss_prec_disabled  = 0,
    dss_prec_primary   = 1,
    dss_prec_secondary = 2,
    dss_prec_limit     = 3
} dss_prec_t;
#define DSS_PREC_DEFAULT dss_prec_secondary
#define DSS_DEFAULT "secondary"
extern const char *dss_prec_names[];
extern const char *opt_dss;
dss_prec_t extent_dss_prec_get(void);
bool extent_dss_prec_set(dss_prec_t dss_prec);
void *extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr,
    size_t size, size_t alignment, bool *zero, bool *commit);
bool extent_in_dss(void *addr);
bool extent_dss_mergeable(void *addr_a, void *addr_b);
void extent_dss_boot(void);
#endif /* JEMALLOC_INTERNAL_EXTENT_DSS_H */
jemalloc/x64/include/jemalloc/internal/extent_externs.h
New file
@@ -0,0 +1,83 @@
#ifndef JEMALLOC_INTERNAL_EXTENT_EXTERNS_H
#define JEMALLOC_INTERNAL_EXTENT_EXTERNS_H
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/mutex_pool.h"
#include "jemalloc/internal/ph.h"
#include "jemalloc/internal/rtree.h"
extern size_t opt_lg_extent_max_active_fit;
extern rtree_t extents_rtree;
extern const extent_hooks_t extent_hooks_default;
extern mutex_pool_t extent_mutex_pool;
extent_t *extent_alloc(tsdn_t *tsdn, arena_t *arena);
void extent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent);
extent_hooks_t *extent_hooks_get(arena_t *arena);
extent_hooks_t *extent_hooks_set(tsd_t *tsd, arena_t *arena,
    extent_hooks_t *extent_hooks);
#ifdef JEMALLOC_JET
size_t extent_size_quantize_floor(size_t size);
size_t extent_size_quantize_ceil(size_t size);
#endif
ph_proto(, extent_avail_, extent_tree_t, extent_t)
ph_proto(, extent_heap_, extent_heap_t, extent_t)
bool extents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,
    bool delay_coalesce);
extent_state_t extents_state_get(const extents_t *extents);
size_t extents_npages_get(extents_t *extents);
/* Get the number of extents in the given page size index. */
size_t extents_nextents_get(extents_t *extents, pszind_t ind);
/* Get the sum total bytes of the extents in the given page size index. */
size_t extents_nbytes_get(extents_t *extents, pszind_t ind);
extent_t *extents_alloc(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr,
    size_t size, size_t pad, size_t alignment, bool slab, szind_t szind,
    bool *zero, bool *commit);
void extents_dalloc(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extents_t *extents, extent_t *extent);
extent_t *extents_evict(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extents_t *extents, size_t npages_min);
void extents_prefork(tsdn_t *tsdn, extents_t *extents);
void extents_postfork_parent(tsdn_t *tsdn, extents_t *extents);
void extents_postfork_child(tsdn_t *tsdn, extents_t *extents);
extent_t *extent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,
    size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit);
void extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent);
void extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extent_t *extent);
void extent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extent_t *extent);
bool extent_commit_wrapper(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
    size_t length);
bool extent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
    size_t length);
bool extent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
    size_t length);
bool extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
    size_t length);
extent_t *extent_split_wrapper(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,
    szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b);
bool extent_merge_wrapper(tsdn_t *tsdn, arena_t *arena,
    extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b);
bool extent_boot(void);
void extent_util_stats_get(tsdn_t *tsdn, const void *ptr,
    size_t *nfree, size_t *nregs, size_t *size);
void extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
    size_t *nfree, size_t *nregs, size_t *size,
    size_t *bin_nfree, size_t *bin_nregs, void **slabcur_addr);
#endif /* JEMALLOC_INTERNAL_EXTENT_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/extent_inlines.h
New file
@@ -0,0 +1,501 @@
#ifndef JEMALLOC_INTERNAL_EXTENT_INLINES_H
#define JEMALLOC_INTERNAL_EXTENT_INLINES_H
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/mutex_pool.h"
#include "jemalloc/internal/pages.h"
#include "jemalloc/internal/prng.h"
#include "jemalloc/internal/ql.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/sz.h"
static inline void
extent_lock(tsdn_t *tsdn, extent_t *extent) {
    assert(extent != NULL);
    mutex_pool_lock(tsdn, &extent_mutex_pool, (uintptr_t)extent);
}
static inline void
extent_unlock(tsdn_t *tsdn, extent_t *extent) {
    assert(extent != NULL);
    mutex_pool_unlock(tsdn, &extent_mutex_pool, (uintptr_t)extent);
}
static inline void
extent_lock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {
    assert(extent1 != NULL && extent2 != NULL);
    mutex_pool_lock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,
        (uintptr_t)extent2);
}
static inline void
extent_unlock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {
    assert(extent1 != NULL && extent2 != NULL);
    mutex_pool_unlock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,
        (uintptr_t)extent2);
}
static inline unsigned
extent_arena_ind_get(const extent_t *extent) {
    unsigned arena_ind = (unsigned)((extent->e_bits &
        EXTENT_BITS_ARENA_MASK) >> EXTENT_BITS_ARENA_SHIFT);
    assert(arena_ind < MALLOCX_ARENA_LIMIT);
    return arena_ind;
}
static inline arena_t *
extent_arena_get(const extent_t *extent) {
    unsigned arena_ind = extent_arena_ind_get(extent);
    return (arena_t *)atomic_load_p(&arenas[arena_ind], ATOMIC_ACQUIRE);
}
static inline szind_t
extent_szind_get_maybe_invalid(const extent_t *extent) {
    szind_t szind = (szind_t)((extent->e_bits & EXTENT_BITS_SZIND_MASK) >>
        EXTENT_BITS_SZIND_SHIFT);
    assert(szind <= SC_NSIZES);
    return szind;
}
static inline szind_t
extent_szind_get(const extent_t *extent) {
    szind_t szind = extent_szind_get_maybe_invalid(extent);
    assert(szind < SC_NSIZES); /* Never call when "invalid". */
    return szind;
}
static inline size_t
extent_usize_get(const extent_t *extent) {
    return sz_index2size(extent_szind_get(extent));
}
static inline unsigned
extent_binshard_get(const extent_t *extent) {
    unsigned binshard = (unsigned)((extent->e_bits &
        EXTENT_BITS_BINSHARD_MASK) >> EXTENT_BITS_BINSHARD_SHIFT);
    assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
    return binshard;
}
static inline size_t
extent_sn_get(const extent_t *extent) {
    return (size_t)((extent->e_bits & EXTENT_BITS_SN_MASK) >>
        EXTENT_BITS_SN_SHIFT);
}
static inline extent_state_t
extent_state_get(const extent_t *extent) {
    return (extent_state_t)((extent->e_bits & EXTENT_BITS_STATE_MASK) >>
        EXTENT_BITS_STATE_SHIFT);
}
static inline bool
extent_zeroed_get(const extent_t *extent) {
    return (bool)((extent->e_bits & EXTENT_BITS_ZEROED_MASK) >>
        EXTENT_BITS_ZEROED_SHIFT);
}
static inline bool
extent_committed_get(const extent_t *extent) {
    return (bool)((extent->e_bits & EXTENT_BITS_COMMITTED_MASK) >>
        EXTENT_BITS_COMMITTED_SHIFT);
}
static inline bool
extent_dumpable_get(const extent_t *extent) {
    return (bool)((extent->e_bits & EXTENT_BITS_DUMPABLE_MASK) >>
        EXTENT_BITS_DUMPABLE_SHIFT);
}
static inline bool
extent_slab_get(const extent_t *extent) {
    return (bool)((extent->e_bits & EXTENT_BITS_SLAB_MASK) >>
        EXTENT_BITS_SLAB_SHIFT);
}
static inline unsigned
extent_nfree_get(const extent_t *extent) {
    assert(extent_slab_get(extent));
    return (unsigned)((extent->e_bits & EXTENT_BITS_NFREE_MASK) >>
        EXTENT_BITS_NFREE_SHIFT);
}
static inline void *
extent_base_get(const extent_t *extent) {
    assert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||
        !extent_slab_get(extent));
    return PAGE_ADDR2BASE(extent->e_addr);
}
static inline void *
extent_addr_get(const extent_t *extent) {
    assert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||
        !extent_slab_get(extent));
    return extent->e_addr;
}
static inline size_t
extent_size_get(const extent_t *extent) {
    return (extent->e_size_esn & EXTENT_SIZE_MASK);
}
static inline size_t
extent_esn_get(const extent_t *extent) {
    return (extent->e_size_esn & EXTENT_ESN_MASK);
}
static inline size_t
extent_bsize_get(const extent_t *extent) {
    return extent->e_bsize;
}
static inline void *
extent_before_get(const extent_t *extent) {
    return (void *)((uintptr_t)extent_base_get(extent) - PAGE);
}
static inline void *
extent_last_get(const extent_t *extent) {
    return (void *)((uintptr_t)extent_base_get(extent) +
        extent_size_get(extent) - PAGE);
}
static inline void *
extent_past_get(const extent_t *extent) {
    return (void *)((uintptr_t)extent_base_get(extent) +
        extent_size_get(extent));
}
static inline arena_slab_data_t *
extent_slab_data_get(extent_t *extent) {
    assert(extent_slab_get(extent));
    return &extent->e_slab_data;
}
static inline const arena_slab_data_t *
extent_slab_data_get_const(const extent_t *extent) {
    assert(extent_slab_get(extent));
    return &extent->e_slab_data;
}
static inline prof_tctx_t *
extent_prof_tctx_get(const extent_t *extent) {
    return (prof_tctx_t *)atomic_load_p(&extent->e_prof_tctx,
        ATOMIC_ACQUIRE);
}
static inline nstime_t
extent_prof_alloc_time_get(const extent_t *extent) {
    return extent->e_alloc_time;
}
static inline void
extent_arena_set(extent_t *extent, arena_t *arena) {
    unsigned arena_ind = (arena != NULL) ? arena_ind_get(arena) : ((1U <<
        MALLOCX_ARENA_BITS) - 1);
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_ARENA_MASK) |
        ((uint64_t)arena_ind << EXTENT_BITS_ARENA_SHIFT);
}
static inline void
extent_binshard_set(extent_t *extent, unsigned binshard) {
    /* The assertion assumes szind is set already. */
    assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_BINSHARD_MASK) |
        ((uint64_t)binshard << EXTENT_BITS_BINSHARD_SHIFT);
}
static inline void
extent_addr_set(extent_t *extent, void *addr) {
    extent->e_addr = addr;
}
static inline void
extent_addr_randomize(tsdn_t *tsdn, extent_t *extent, size_t alignment) {
    assert(extent_base_get(extent) == extent_addr_get(extent));
    if (alignment < PAGE) {
        unsigned lg_range = LG_PAGE -
            lg_floor(CACHELINE_CEILING(alignment));
        size_t r;
        if (!tsdn_null(tsdn)) {
            tsd_t *tsd = tsdn_tsd(tsdn);
            r = (size_t)prng_lg_range_u64(
                tsd_offset_statep_get(tsd), lg_range);
        } else {
            r = prng_lg_range_zu(
                &extent_arena_get(extent)->offset_state,
                lg_range, true);
        }
        uintptr_t random_offset = ((uintptr_t)r) << (LG_PAGE -
            lg_range);
        extent->e_addr = (void *)((uintptr_t)extent->e_addr +
            random_offset);
        assert(ALIGNMENT_ADDR2BASE(extent->e_addr, alignment) ==
            extent->e_addr);
    }
}
static inline void
extent_size_set(extent_t *extent, size_t size) {
    assert((size & ~EXTENT_SIZE_MASK) == 0);
    extent->e_size_esn = size | (extent->e_size_esn & ~EXTENT_SIZE_MASK);
}
static inline void
extent_esn_set(extent_t *extent, size_t esn) {
    extent->e_size_esn = (extent->e_size_esn & ~EXTENT_ESN_MASK) | (esn &
        EXTENT_ESN_MASK);
}
static inline void
extent_bsize_set(extent_t *extent, size_t bsize) {
    extent->e_bsize = bsize;
}
static inline void
extent_szind_set(extent_t *extent, szind_t szind) {
    assert(szind <= SC_NSIZES); /* SC_NSIZES means "invalid". */
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SZIND_MASK) |
        ((uint64_t)szind << EXTENT_BITS_SZIND_SHIFT);
}
static inline void
extent_nfree_set(extent_t *extent, unsigned nfree) {
    assert(extent_slab_get(extent));
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_NFREE_MASK) |
        ((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);
}
static inline void
extent_nfree_binshard_set(extent_t *extent, unsigned nfree, unsigned binshard) {
    /* The assertion assumes szind is set already. */
    assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
    extent->e_bits = (extent->e_bits &
        (~EXTENT_BITS_NFREE_MASK & ~EXTENT_BITS_BINSHARD_MASK)) |
        ((uint64_t)binshard << EXTENT_BITS_BINSHARD_SHIFT) |
        ((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);
}
static inline void
extent_nfree_inc(extent_t *extent) {
    assert(extent_slab_get(extent));
    extent->e_bits += ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);
}
static inline void
extent_nfree_dec(extent_t *extent) {
    assert(extent_slab_get(extent));
    extent->e_bits -= ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);
}
static inline void
extent_nfree_sub(extent_t *extent, uint64_t n) {
    assert(extent_slab_get(extent));
    extent->e_bits -= (n << EXTENT_BITS_NFREE_SHIFT);
}
static inline void
extent_sn_set(extent_t *extent, size_t sn) {
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SN_MASK) |
        ((uint64_t)sn << EXTENT_BITS_SN_SHIFT);
}
static inline void
extent_state_set(extent_t *extent, extent_state_t state) {
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_STATE_MASK) |
        ((uint64_t)state << EXTENT_BITS_STATE_SHIFT);
}
static inline void
extent_zeroed_set(extent_t *extent, bool zeroed) {
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_ZEROED_MASK) |
        ((uint64_t)zeroed << EXTENT_BITS_ZEROED_SHIFT);
}
static inline void
extent_committed_set(extent_t *extent, bool committed) {
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_COMMITTED_MASK) |
        ((uint64_t)committed << EXTENT_BITS_COMMITTED_SHIFT);
}
static inline void
extent_dumpable_set(extent_t *extent, bool dumpable) {
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_DUMPABLE_MASK) |
        ((uint64_t)dumpable << EXTENT_BITS_DUMPABLE_SHIFT);
}
static inline void
extent_slab_set(extent_t *extent, bool slab) {
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SLAB_MASK) |
        ((uint64_t)slab << EXTENT_BITS_SLAB_SHIFT);
}
static inline void
extent_prof_tctx_set(extent_t *extent, prof_tctx_t *tctx) {
    atomic_store_p(&extent->e_prof_tctx, tctx, ATOMIC_RELEASE);
}
static inline void
extent_prof_alloc_time_set(extent_t *extent, nstime_t t) {
    nstime_copy(&extent->e_alloc_time, &t);
}
static inline bool
extent_is_head_get(extent_t *extent) {
    if (maps_coalesce) {
        not_reached();
    }
    return (bool)((extent->e_bits & EXTENT_BITS_IS_HEAD_MASK) >>
        EXTENT_BITS_IS_HEAD_SHIFT);
}
static inline void
extent_is_head_set(extent_t *extent, bool is_head) {
    if (maps_coalesce) {
        not_reached();
    }
    extent->e_bits = (extent->e_bits & ~EXTENT_BITS_IS_HEAD_MASK) |
        ((uint64_t)is_head << EXTENT_BITS_IS_HEAD_SHIFT);
}
static inline void
extent_init(extent_t *extent, arena_t *arena, void *addr, size_t size,
    bool slab, szind_t szind, size_t sn, extent_state_t state, bool zeroed,
    bool committed, bool dumpable, extent_head_state_t is_head) {
    assert(addr == PAGE_ADDR2BASE(addr) || !slab);
    extent_arena_set(extent, arena);
    extent_addr_set(extent, addr);
    extent_size_set(extent, size);
    extent_slab_set(extent, slab);
    extent_szind_set(extent, szind);
    extent_sn_set(extent, sn);
    extent_state_set(extent, state);
    extent_zeroed_set(extent, zeroed);
    extent_committed_set(extent, committed);
    extent_dumpable_set(extent, dumpable);
    ql_elm_new(extent, ql_link);
    if (!maps_coalesce) {
        extent_is_head_set(extent, (is_head == EXTENT_IS_HEAD) ? true :
            false);
    }
    if (config_prof) {
        extent_prof_tctx_set(extent, NULL);
    }
}
static inline void
extent_binit(extent_t *extent, void *addr, size_t bsize, size_t sn) {
    extent_arena_set(extent, NULL);
    extent_addr_set(extent, addr);
    extent_bsize_set(extent, bsize);
    extent_slab_set(extent, false);
    extent_szind_set(extent, SC_NSIZES);
    extent_sn_set(extent, sn);
    extent_state_set(extent, extent_state_active);
    extent_zeroed_set(extent, true);
    extent_committed_set(extent, true);
    extent_dumpable_set(extent, true);
}
static inline void
extent_list_init(extent_list_t *list) {
    ql_new(list);
}
static inline extent_t *
extent_list_first(const extent_list_t *list) {
    return ql_first(list);
}
static inline extent_t *
extent_list_last(const extent_list_t *list) {
    return ql_last(list, ql_link);
}
static inline void
extent_list_append(extent_list_t *list, extent_t *extent) {
    ql_tail_insert(list, extent, ql_link);
}
static inline void
extent_list_prepend(extent_list_t *list, extent_t *extent) {
    ql_head_insert(list, extent, ql_link);
}
static inline void
extent_list_replace(extent_list_t *list, extent_t *to_remove,
    extent_t *to_insert) {
    ql_after_insert(to_remove, to_insert, ql_link);
    ql_remove(list, to_remove, ql_link);
}
static inline void
extent_list_remove(extent_list_t *list, extent_t *extent) {
    ql_remove(list, extent, ql_link);
}
static inline int
extent_sn_comp(const extent_t *a, const extent_t *b) {
    size_t a_sn = extent_sn_get(a);
    size_t b_sn = extent_sn_get(b);
    return (a_sn > b_sn) - (a_sn < b_sn);
}
static inline int
extent_esn_comp(const extent_t *a, const extent_t *b) {
    size_t a_esn = extent_esn_get(a);
    size_t b_esn = extent_esn_get(b);
    return (a_esn > b_esn) - (a_esn < b_esn);
}
static inline int
extent_ad_comp(const extent_t *a, const extent_t *b) {
    uintptr_t a_addr = (uintptr_t)extent_addr_get(a);
    uintptr_t b_addr = (uintptr_t)extent_addr_get(b);
    return (a_addr > b_addr) - (a_addr < b_addr);
}
static inline int
extent_ead_comp(const extent_t *a, const extent_t *b) {
    uintptr_t a_eaddr = (uintptr_t)a;
    uintptr_t b_eaddr = (uintptr_t)b;
    return (a_eaddr > b_eaddr) - (a_eaddr < b_eaddr);
}
static inline int
extent_snad_comp(const extent_t *a, const extent_t *b) {
    int ret;
    ret = extent_sn_comp(a, b);
    if (ret != 0) {
        return ret;
    }
    ret = extent_ad_comp(a, b);
    return ret;
}
static inline int
extent_esnead_comp(const extent_t *a, const extent_t *b) {
    int ret;
    ret = extent_esn_comp(a, b);
    if (ret != 0) {
        return ret;
    }
    ret = extent_ead_comp(a, b);
    return ret;
}
#endif /* JEMALLOC_INTERNAL_EXTENT_INLINES_H */
jemalloc/x64/include/jemalloc/internal/extent_mmap.h
New file
@@ -0,0 +1,10 @@
#ifndef JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H
#define JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H
extern bool opt_retain;
void *extent_alloc_mmap(void *new_addr, size_t size, size_t alignment,
    bool *zero, bool *commit);
bool extent_dalloc_mmap(void *addr, size_t size);
#endif /* JEMALLOC_INTERNAL_EXTENT_MMAP_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/extent_structs.h
New file
@@ -0,0 +1,256 @@
#ifndef JEMALLOC_INTERNAL_EXTENT_STRUCTS_H
#define JEMALLOC_INTERNAL_EXTENT_STRUCTS_H
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/bit_util.h"
#include "jemalloc/internal/bitmap.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/ql.h"
#include "jemalloc/internal/ph.h"
#include "jemalloc/internal/sc.h"
typedef enum {
    extent_state_active   = 0,
    extent_state_dirty    = 1,
    extent_state_muzzy    = 2,
    extent_state_retained = 3
} extent_state_t;
/* Extent (span of pages).  Use accessor functions for e_* fields. */
struct extent_s {
    /*
     * Bitfield containing several fields:
     *
     * a: arena_ind
     * b: slab
     * c: committed
     * d: dumpable
     * z: zeroed
     * t: state
     * i: szind
     * f: nfree
     * s: bin_shard
     * n: sn
     *
     * nnnnnnnn ... nnnnnnss ssssffff ffffffii iiiiiitt zdcbaaaa aaaaaaaa
     *
     * arena_ind: Arena from which this extent came, or all 1 bits if
     *            unassociated.
     *
     * slab: The slab flag indicates whether the extent is used for a slab
     *       of small regions.  This helps differentiate small size classes,
     *       and it indicates whether interior pointers can be looked up via
     *       iealloc().
     *
     * committed: The committed flag indicates whether physical memory is
     *            committed to the extent, whether explicitly or implicitly
     *            as on a system that overcommits and satisfies physical
     *            memory needs on demand via soft page faults.
     *
     * dumpable: The dumpable flag indicates whether or not we've set the
     *           memory in question to be dumpable.  Note that this
     *           interacts somewhat subtly with user-specified extent hooks,
     *           since we don't know if *they* are fiddling with
     *           dumpability (in which case, we don't want to undo whatever
     *           they're doing).  To deal with this scenario, we:
     *             - Make dumpable false only for memory allocated with the
     *               default hooks.
     *             - Only allow memory to go from non-dumpable to dumpable,
     *               and only once.
     *             - Never make the OS call to allow dumping when the
     *               dumpable bit is already set.
     *           These three constraints mean that we will never
     *           accidentally dump user memory that the user meant to set
     *           nondumpable with their extent hooks.
     *
     *
     * zeroed: The zeroed flag is used by extent recycling code to track
     *         whether memory is zero-filled.
     *
     * state: The state flag is an extent_state_t.
     *
     * szind: The szind flag indicates usable size class index for
     *        allocations residing in this extent, regardless of whether the
     *        extent is a slab.  Extent size and usable size often differ
     *        even for non-slabs, either due to sz_large_pad or promotion of
     *        sampled small regions.
     *
     * nfree: Number of free regions in slab.
     *
     * bin_shard: the shard of the bin from which this extent came.
     *
     * sn: Serial number (potentially non-unique).
     *
     *     Serial numbers may wrap around if !opt_retain, but as long as
     *     comparison functions fall back on address comparison for equal
     *     serial numbers, stable (if imperfect) ordering is maintained.
     *
     *     Serial numbers may not be unique even in the absence of
     *     wrap-around, e.g. when splitting an extent and assigning the same
     *     serial number to both resulting adjacent extents.
     */
    uint64_t        e_bits;
#define MASK(CURRENT_FIELD_WIDTH, CURRENT_FIELD_SHIFT) ((((((uint64_t)0x1U) << (CURRENT_FIELD_WIDTH)) - 1)) << (CURRENT_FIELD_SHIFT))
#define EXTENT_BITS_ARENA_WIDTH  MALLOCX_ARENA_BITS
#define EXTENT_BITS_ARENA_SHIFT  0
#define EXTENT_BITS_ARENA_MASK  MASK(EXTENT_BITS_ARENA_WIDTH, EXTENT_BITS_ARENA_SHIFT)
#define EXTENT_BITS_SLAB_WIDTH  1
#define EXTENT_BITS_SLAB_SHIFT  (EXTENT_BITS_ARENA_WIDTH + EXTENT_BITS_ARENA_SHIFT)
#define EXTENT_BITS_SLAB_MASK  MASK(EXTENT_BITS_SLAB_WIDTH, EXTENT_BITS_SLAB_SHIFT)
#define EXTENT_BITS_COMMITTED_WIDTH  1
#define EXTENT_BITS_COMMITTED_SHIFT  (EXTENT_BITS_SLAB_WIDTH + EXTENT_BITS_SLAB_SHIFT)
#define EXTENT_BITS_COMMITTED_MASK  MASK(EXTENT_BITS_COMMITTED_WIDTH, EXTENT_BITS_COMMITTED_SHIFT)
#define EXTENT_BITS_DUMPABLE_WIDTH  1
#define EXTENT_BITS_DUMPABLE_SHIFT  (EXTENT_BITS_COMMITTED_WIDTH + EXTENT_BITS_COMMITTED_SHIFT)
#define EXTENT_BITS_DUMPABLE_MASK  MASK(EXTENT_BITS_DUMPABLE_WIDTH, EXTENT_BITS_DUMPABLE_SHIFT)
#define EXTENT_BITS_ZEROED_WIDTH  1
#define EXTENT_BITS_ZEROED_SHIFT  (EXTENT_BITS_DUMPABLE_WIDTH + EXTENT_BITS_DUMPABLE_SHIFT)
#define EXTENT_BITS_ZEROED_MASK  MASK(EXTENT_BITS_ZEROED_WIDTH, EXTENT_BITS_ZEROED_SHIFT)
#define EXTENT_BITS_STATE_WIDTH  2
#define EXTENT_BITS_STATE_SHIFT  (EXTENT_BITS_ZEROED_WIDTH + EXTENT_BITS_ZEROED_SHIFT)
#define EXTENT_BITS_STATE_MASK  MASK(EXTENT_BITS_STATE_WIDTH, EXTENT_BITS_STATE_SHIFT)
#define EXTENT_BITS_SZIND_WIDTH  LG_CEIL(SC_NSIZES)
#define EXTENT_BITS_SZIND_SHIFT  (EXTENT_BITS_STATE_WIDTH + EXTENT_BITS_STATE_SHIFT)
#define EXTENT_BITS_SZIND_MASK  MASK(EXTENT_BITS_SZIND_WIDTH, EXTENT_BITS_SZIND_SHIFT)
#define EXTENT_BITS_NFREE_WIDTH  (LG_SLAB_MAXREGS + 1)
#define EXTENT_BITS_NFREE_SHIFT  (EXTENT_BITS_SZIND_WIDTH + EXTENT_BITS_SZIND_SHIFT)
#define EXTENT_BITS_NFREE_MASK  MASK(EXTENT_BITS_NFREE_WIDTH, EXTENT_BITS_NFREE_SHIFT)
#define EXTENT_BITS_BINSHARD_WIDTH  6
#define EXTENT_BITS_BINSHARD_SHIFT  (EXTENT_BITS_NFREE_WIDTH + EXTENT_BITS_NFREE_SHIFT)
#define EXTENT_BITS_BINSHARD_MASK  MASK(EXTENT_BITS_BINSHARD_WIDTH, EXTENT_BITS_BINSHARD_SHIFT)
#define EXTENT_BITS_IS_HEAD_WIDTH 1
#define EXTENT_BITS_IS_HEAD_SHIFT  (EXTENT_BITS_BINSHARD_WIDTH + EXTENT_BITS_BINSHARD_SHIFT)
#define EXTENT_BITS_IS_HEAD_MASK  MASK(EXTENT_BITS_IS_HEAD_WIDTH, EXTENT_BITS_IS_HEAD_SHIFT)
#define EXTENT_BITS_SN_SHIFT   (EXTENT_BITS_IS_HEAD_WIDTH + EXTENT_BITS_IS_HEAD_SHIFT)
#define EXTENT_BITS_SN_MASK  (UINT64_MAX << EXTENT_BITS_SN_SHIFT)
    /* Pointer to the extent that this structure is responsible for. */
    void            *e_addr;
    union {
        /*
         * Extent size and serial number associated with the extent
         * structure (different than the serial number for the extent at
         * e_addr).
         *
         * ssssssss [...] ssssssss ssssnnnn nnnnnnnn
         */
        size_t            e_size_esn;
    #define EXTENT_SIZE_MASK    ((size_t)~(PAGE-1))
    #define EXTENT_ESN_MASK        ((size_t)PAGE-1)
        /* Base extent size, which may not be a multiple of PAGE. */
        size_t            e_bsize;
    };
    /*
     * List linkage, used by a variety of lists:
     * - bin_t's slabs_full
     * - extents_t's LRU
     * - stashed dirty extents
     * - arena's large allocations
     */
    ql_elm(extent_t)    ql_link;
    /*
     * Linkage for per size class sn/address-ordered heaps, and
     * for extent_avail
     */
    phn(extent_t)        ph_link;
    union {
        /* Small region slab metadata. */
        arena_slab_data_t    e_slab_data;
        /* Profiling data, used for large objects. */
        struct {
            /* Time when this was allocated. */
            nstime_t        e_alloc_time;
            /* Points to a prof_tctx_t. */
            atomic_p_t        e_prof_tctx;
        };
    };
};
typedef ql_head(extent_t) extent_list_t;
typedef ph(extent_t) extent_tree_t;
typedef ph(extent_t) extent_heap_t;
/* Quantized collection of extents, with built-in LRU queue. */
struct extents_s {
    malloc_mutex_t        mtx;
    /*
     * Quantized per size class heaps of extents.
     *
     * Synchronization: mtx.
     */
    extent_heap_t        heaps[SC_NPSIZES + 1];
    atomic_zu_t        nextents[SC_NPSIZES + 1];
    atomic_zu_t        nbytes[SC_NPSIZES + 1];
    /*
     * Bitmap for which set bits correspond to non-empty heaps.
     *
     * Synchronization: mtx.
     */
    bitmap_t        bitmap[BITMAP_GROUPS(SC_NPSIZES + 1)];
    /*
     * LRU of all extents in heaps.
     *
     * Synchronization: mtx.
     */
    extent_list_t        lru;
    /*
     * Page sum for all extents in heaps.
     *
     * The synchronization here is a little tricky.  Modifications to npages
     * must hold mtx, but reads need not (though, a reader who sees npages
     * without holding the mutex can't assume anything about the rest of the
     * state of the extents_t).
     */
    atomic_zu_t        npages;
    /* All stored extents must be in the same state. */
    extent_state_t        state;
    /*
     * If true, delay coalescing until eviction; otherwise coalesce during
     * deallocation.
     */
    bool            delay_coalesce;
};
/*
 * The following two structs are for experimental purposes. See
 * experimental_utilization_query_ctl and
 * experimental_utilization_batch_query_ctl in src/ctl.c.
 */
struct extent_util_stats_s {
    size_t nfree;
    size_t nregs;
    size_t size;
};
struct extent_util_stats_verbose_s {
    void *slabcur_addr;
    size_t nfree;
    size_t nregs;
    size_t size;
    size_t bin_nfree;
    size_t bin_nregs;
};
#endif /* JEMALLOC_INTERNAL_EXTENT_STRUCTS_H */
jemalloc/x64/include/jemalloc/internal/extent_types.h
New file
@@ -0,0 +1,23 @@
#ifndef JEMALLOC_INTERNAL_EXTENT_TYPES_H
#define JEMALLOC_INTERNAL_EXTENT_TYPES_H
typedef struct extent_s extent_t;
typedef struct extents_s extents_t;
typedef struct extent_util_stats_s extent_util_stats_t;
typedef struct extent_util_stats_verbose_s extent_util_stats_verbose_t;
#define EXTENT_HOOKS_INITIALIZER    NULL
/*
 * When reuse (and split) an active extent, (1U << opt_lg_extent_max_active_fit)
 * is the max ratio between the size of the active extent and the new extent.
 */
#define LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT 6
typedef enum {
    EXTENT_NOT_HEAD,
    EXTENT_IS_HEAD   /* Only relevant for Windows && opt.retain. */
} extent_head_state_t;
#endif /* JEMALLOC_INTERNAL_EXTENT_TYPES_H */
jemalloc/x64/include/jemalloc/internal/hash.h
New file
@@ -0,0 +1,319 @@
#ifndef JEMALLOC_INTERNAL_HASH_H
#define JEMALLOC_INTERNAL_HASH_H
#include "jemalloc/internal/assert.h"
/*
 * The following hash function is based on MurmurHash3, placed into the public
 * domain by Austin Appleby.  See https://github.com/aappleby/smhasher for
 * details.
 */
/******************************************************************************/
/* Internal implementation. */
static inline uint32_t
hash_rotl_32(uint32_t x, int8_t r) {
    return ((x << r) | (x >> (32 - r)));
}
static inline uint64_t
hash_rotl_64(uint64_t x, int8_t r) {
    return ((x << r) | (x >> (64 - r)));
}
static inline uint32_t
hash_get_block_32(const uint32_t *p, int i) {
    /* Handle unaligned read. */
    if (unlikely((uintptr_t)p & (sizeof(uint32_t)-1)) != 0) {
        uint32_t ret;
        memcpy(&ret, (uint8_t *)(p + i), sizeof(uint32_t));
        return ret;
    }
    return p[i];
}
static inline uint64_t
hash_get_block_64(const uint64_t *p, int i) {
    /* Handle unaligned read. */
    if (unlikely((uintptr_t)p & (sizeof(uint64_t)-1)) != 0) {
        uint64_t ret;
        memcpy(&ret, (uint8_t *)(p + i), sizeof(uint64_t));
        return ret;
    }
    return p[i];
}
static inline uint32_t
hash_fmix_32(uint32_t h) {
    h ^= h >> 16;
    h *= 0x85ebca6b;
    h ^= h >> 13;
    h *= 0xc2b2ae35;
    h ^= h >> 16;
    return h;
}
static inline uint64_t
hash_fmix_64(uint64_t k) {
    k ^= k >> 33;
    k *= KQU(0xff51afd7ed558ccd);
    k ^= k >> 33;
    k *= KQU(0xc4ceb9fe1a85ec53);
    k ^= k >> 33;
    return k;
}
static inline uint32_t
hash_x86_32(const void *key, int len, uint32_t seed) {
    const uint8_t *data = (const uint8_t *) key;
    const int nblocks = len / 4;
    uint32_t h1 = seed;
    const uint32_t c1 = 0xcc9e2d51;
    const uint32_t c2 = 0x1b873593;
    /* body */
    {
        const uint32_t *blocks = (const uint32_t *) (data + nblocks*4);
        int i;
        for (i = -nblocks; i; i++) {
            uint32_t k1 = hash_get_block_32(blocks, i);
            k1 *= c1;
            k1 = hash_rotl_32(k1, 15);
            k1 *= c2;
            h1 ^= k1;
            h1 = hash_rotl_32(h1, 13);
            h1 = h1*5 + 0xe6546b64;
        }
    }
    /* tail */
    {
        const uint8_t *tail = (const uint8_t *) (data + nblocks*4);
        uint32_t k1 = 0;
        switch (len & 3) {
        case 3: k1 ^= tail[2] << 16; JEMALLOC_FALLTHROUGH
        case 2: k1 ^= tail[1] << 8; JEMALLOC_FALLTHROUGH
        case 1: k1 ^= tail[0]; k1 *= c1; k1 = hash_rotl_32(k1, 15);
            k1 *= c2; h1 ^= k1;
        }
    }
    /* finalization */
    h1 ^= len;
    h1 = hash_fmix_32(h1);
    return h1;
}
static inline void
hash_x86_128(const void *key, const int len, uint32_t seed,
    uint64_t r_out[2]) {
    const uint8_t * data = (const uint8_t *) key;
    const int nblocks = len / 16;
    uint32_t h1 = seed;
    uint32_t h2 = seed;
    uint32_t h3 = seed;
    uint32_t h4 = seed;
    const uint32_t c1 = 0x239b961b;
    const uint32_t c2 = 0xab0e9789;
    const uint32_t c3 = 0x38b34ae5;
    const uint32_t c4 = 0xa1e38b93;
    /* body */
    {
        const uint32_t *blocks = (const uint32_t *) (data + nblocks*16);
        int i;
        for (i = -nblocks; i; i++) {
            uint32_t k1 = hash_get_block_32(blocks, i*4 + 0);
            uint32_t k2 = hash_get_block_32(blocks, i*4 + 1);
            uint32_t k3 = hash_get_block_32(blocks, i*4 + 2);
            uint32_t k4 = hash_get_block_32(blocks, i*4 + 3);
            k1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;
            h1 = hash_rotl_32(h1, 19); h1 += h2;
            h1 = h1*5 + 0x561ccd1b;
            k2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;
            h2 = hash_rotl_32(h2, 17); h2 += h3;
            h2 = h2*5 + 0x0bcaa747;
            k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;
            h3 = hash_rotl_32(h3, 15); h3 += h4;
            h3 = h3*5 + 0x96cd1c35;
            k4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;
            h4 = hash_rotl_32(h4, 13); h4 += h1;
            h4 = h4*5 + 0x32ac3b17;
        }
    }
    /* tail */
    {
        const uint8_t *tail = (const uint8_t *) (data + nblocks*16);
        uint32_t k1 = 0;
        uint32_t k2 = 0;
        uint32_t k3 = 0;
        uint32_t k4 = 0;
        switch (len & 15) {
        case 15: k4 ^= tail[14] << 16; JEMALLOC_FALLTHROUGH
        case 14: k4 ^= tail[13] << 8; JEMALLOC_FALLTHROUGH
        case 13: k4 ^= tail[12] << 0;
            k4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;
      JEMALLOC_FALLTHROUGH
        case 12: k3 ^= tail[11] << 24; JEMALLOC_FALLTHROUGH
        case 11: k3 ^= tail[10] << 16; JEMALLOC_FALLTHROUGH
        case 10: k3 ^= tail[ 9] << 8; JEMALLOC_FALLTHROUGH
        case  9: k3 ^= tail[ 8] << 0;
             k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;
         JEMALLOC_FALLTHROUGH
        case  8: k2 ^= tail[ 7] << 24; JEMALLOC_FALLTHROUGH
        case  7: k2 ^= tail[ 6] << 16; JEMALLOC_FALLTHROUGH
        case  6: k2 ^= tail[ 5] << 8; JEMALLOC_FALLTHROUGH
        case  5: k2 ^= tail[ 4] << 0;
            k2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;
      JEMALLOC_FALLTHROUGH
        case  4: k1 ^= tail[ 3] << 24; JEMALLOC_FALLTHROUGH
        case  3: k1 ^= tail[ 2] << 16; JEMALLOC_FALLTHROUGH
        case  2: k1 ^= tail[ 1] << 8; JEMALLOC_FALLTHROUGH
        case  1: k1 ^= tail[ 0] << 0;
            k1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;
      JEMALLOC_FALLTHROUGH
        }
    }
    /* finalization */
    h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
    h1 += h2; h1 += h3; h1 += h4;
    h2 += h1; h3 += h1; h4 += h1;
    h1 = hash_fmix_32(h1);
    h2 = hash_fmix_32(h2);
    h3 = hash_fmix_32(h3);
    h4 = hash_fmix_32(h4);
    h1 += h2; h1 += h3; h1 += h4;
    h2 += h1; h3 += h1; h4 += h1;
    r_out[0] = (((uint64_t) h2) << 32) | h1;
    r_out[1] = (((uint64_t) h4) << 32) | h3;
}
static inline void
hash_x64_128(const void *key, const int len, const uint32_t seed,
    uint64_t r_out[2]) {
    const uint8_t *data = (const uint8_t *) key;
    const int nblocks = len / 16;
    uint64_t h1 = seed;
    uint64_t h2 = seed;
    const uint64_t c1 = KQU(0x87c37b91114253d5);
    const uint64_t c2 = KQU(0x4cf5ad432745937f);
    /* body */
    {
        const uint64_t *blocks = (const uint64_t *) (data);
        int i;
        for (i = 0; i < nblocks; i++) {
            uint64_t k1 = hash_get_block_64(blocks, i*2 + 0);
            uint64_t k2 = hash_get_block_64(blocks, i*2 + 1);
            k1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;
            h1 = hash_rotl_64(h1, 27); h1 += h2;
            h1 = h1*5 + 0x52dce729;
            k2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;
            h2 = hash_rotl_64(h2, 31); h2 += h1;
            h2 = h2*5 + 0x38495ab5;
        }
    }
    /* tail */
    {
        const uint8_t *tail = (const uint8_t*)(data + nblocks*16);
        uint64_t k1 = 0;
        uint64_t k2 = 0;
        switch (len & 15) {
        case 15: k2 ^= ((uint64_t)(tail[14])) << 48; JEMALLOC_FALLTHROUGH
        case 14: k2 ^= ((uint64_t)(tail[13])) << 40; JEMALLOC_FALLTHROUGH
        case 13: k2 ^= ((uint64_t)(tail[12])) << 32; JEMALLOC_FALLTHROUGH
        case 12: k2 ^= ((uint64_t)(tail[11])) << 24; JEMALLOC_FALLTHROUGH
        case 11: k2 ^= ((uint64_t)(tail[10])) << 16; JEMALLOC_FALLTHROUGH
        case 10: k2 ^= ((uint64_t)(tail[ 9])) << 8;  JEMALLOC_FALLTHROUGH
        case  9: k2 ^= ((uint64_t)(tail[ 8])) << 0;
            k2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;
            JEMALLOC_FALLTHROUGH
        case  8: k1 ^= ((uint64_t)(tail[ 7])) << 56; JEMALLOC_FALLTHROUGH
        case  7: k1 ^= ((uint64_t)(tail[ 6])) << 48; JEMALLOC_FALLTHROUGH
        case  6: k1 ^= ((uint64_t)(tail[ 5])) << 40; JEMALLOC_FALLTHROUGH
        case  5: k1 ^= ((uint64_t)(tail[ 4])) << 32; JEMALLOC_FALLTHROUGH
        case  4: k1 ^= ((uint64_t)(tail[ 3])) << 24; JEMALLOC_FALLTHROUGH
        case  3: k1 ^= ((uint64_t)(tail[ 2])) << 16; JEMALLOC_FALLTHROUGH
        case  2: k1 ^= ((uint64_t)(tail[ 1])) << 8;  JEMALLOC_FALLTHROUGH
        case  1: k1 ^= ((uint64_t)(tail[ 0])) << 0;
            k1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;
        }
    }
    /* finalization */
    h1 ^= len; h2 ^= len;
    h1 += h2;
    h2 += h1;
    h1 = hash_fmix_64(h1);
    h2 = hash_fmix_64(h2);
    h1 += h2;
    h2 += h1;
    r_out[0] = h1;
    r_out[1] = h2;
}
/******************************************************************************/
/* API. */
static inline void
hash(const void *key, size_t len, const uint32_t seed, size_t r_hash[2]) {
    assert(len <= INT_MAX); /* Unfortunate implementation limitation. */
#if (LG_SIZEOF_PTR == 3 && !defined(JEMALLOC_BIG_ENDIAN))
    hash_x64_128(key, (int)len, seed, (uint64_t *)r_hash);
#else
    {
        uint64_t hashes[2];
        hash_x86_128(key, (int)len, seed, hashes);
        r_hash[0] = (size_t)hashes[0];
        r_hash[1] = (size_t)hashes[1];
    }
#endif
}
#endif /* JEMALLOC_INTERNAL_HASH_H */
jemalloc/x64/include/jemalloc/internal/hook.h
New file
@@ -0,0 +1,163 @@
#ifndef JEMALLOC_INTERNAL_HOOK_H
#define JEMALLOC_INTERNAL_HOOK_H
#include "jemalloc/internal/tsd.h"
/*
 * This API is *extremely* experimental, and may get ripped out, changed in API-
 * and ABI-incompatible ways, be insufficiently or incorrectly documented, etc.
 *
 * It allows hooking the stateful parts of the API to see changes as they
 * happen.
 *
 * Allocation hooks are called after the allocation is done, free hooks are
 * called before the free is done, and expand hooks are called after the
 * allocation is expanded.
 *
 * For realloc and rallocx, if the expansion happens in place, the expansion
 * hook is called.  If it is moved, then the alloc hook is called on the new
 * location, and then the free hook is called on the old location (i.e. both
 * hooks are invoked in between the alloc and the dalloc).
 *
 * If we return NULL from OOM, then usize might not be trustworthy.  Calling
 * realloc(NULL, size) only calls the alloc hook, and calling realloc(ptr, 0)
 * only calls the free hook.  (Calling realloc(NULL, 0) is treated as malloc(0),
 * and only calls the alloc hook).
 *
 * Reentrancy:
 *   Reentrancy is guarded against from within the hook implementation.  If you
 *   call allocator functions from within a hook, the hooks will not be invoked
 *   again.
 * Threading:
 *   The installation of a hook synchronizes with all its uses.  If you can
 *   prove the installation of a hook happens-before a jemalloc entry point,
 *   then the hook will get invoked (unless there's a racing removal).
 *
 *   Hook insertion appears to be atomic at a per-thread level (i.e. if a thread
 *   allocates and has the alloc hook invoked, then a subsequent free on the
 *   same thread will also have the free hook invoked).
 *
 *   The *removal* of a hook does *not* block until all threads are done with
 *   the hook.  Hook authors have to be resilient to this, and need some
 *   out-of-band mechanism for cleaning up any dynamically allocated memory
 *   associated with their hook.
 * Ordering:
 *   Order of hook execution is unspecified, and may be different than insertion
 *   order.
 */
#define HOOK_MAX 4
enum hook_alloc_e {
    hook_alloc_malloc,
    hook_alloc_posix_memalign,
    hook_alloc_aligned_alloc,
    hook_alloc_calloc,
    hook_alloc_memalign,
    hook_alloc_valloc,
    hook_alloc_mallocx,
    /* The reallocating functions have both alloc and dalloc variants */
    hook_alloc_realloc,
    hook_alloc_rallocx,
};
/*
 * We put the enum typedef after the enum, since this file may get included by
 * jemalloc_cpp.cpp, and C++ disallows enum forward declarations.
 */
typedef enum hook_alloc_e hook_alloc_t;
enum hook_dalloc_e {
    hook_dalloc_free,
    hook_dalloc_dallocx,
    hook_dalloc_sdallocx,
    /*
     * The dalloc halves of reallocation (not called if in-place expansion
     * happens).
     */
    hook_dalloc_realloc,
    hook_dalloc_rallocx,
};
typedef enum hook_dalloc_e hook_dalloc_t;
enum hook_expand_e {
    hook_expand_realloc,
    hook_expand_rallocx,
    hook_expand_xallocx,
};
typedef enum hook_expand_e hook_expand_t;
typedef void (*hook_alloc)(
    void *extra, hook_alloc_t type, void *result, uintptr_t result_raw,
    uintptr_t args_raw[3]);
typedef void (*hook_dalloc)(
    void *extra, hook_dalloc_t type, void *address, uintptr_t args_raw[3]);
typedef void (*hook_expand)(
    void *extra, hook_expand_t type, void *address, size_t old_usize,
    size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]);
typedef struct hooks_s hooks_t;
struct hooks_s {
    hook_alloc alloc_hook;
    hook_dalloc dalloc_hook;
    hook_expand expand_hook;
    void *extra;
};
/*
 * Begin implementation details; everything above this point might one day live
 * in a public API.  Everything below this point never will.
 */
/*
 * The realloc pathways haven't gotten any refactoring love in a while, and it's
 * fairly difficult to pass information from the entry point to the hooks.  We
 * put the informaiton the hooks will need into a struct to encapsulate
 * everything.
 *
 * Much of these pathways are force-inlined, so that the compiler can avoid
 * materializing this struct until we hit an extern arena function.  For fairly
 * goofy reasons, *many* of the realloc paths hit an extern arena function.
 * These paths are cold enough that it doesn't matter; eventually, we should
 * rewrite the realloc code to make the expand-in-place and the
 * free-then-realloc paths more orthogonal, at which point we don't need to
 * spread the hook logic all over the place.
 */
typedef struct hook_ralloc_args_s hook_ralloc_args_t;
struct hook_ralloc_args_s {
    /* I.e. as opposed to rallocx. */
    bool is_realloc;
    /*
     * The expand hook takes 4 arguments, even if only 3 are actually used;
     * we add an extra one in case the user decides to memcpy without
     * looking too closely at the hooked function.
     */
    uintptr_t args[4];
};
/*
 * Returns an opaque handle to be used when removing the hook.  NULL means that
 * we couldn't install the hook.
 */
bool hook_boot();
void *hook_install(tsdn_t *tsdn, hooks_t *hooks);
/* Uninstalls the hook with the handle previously returned from hook_install. */
void hook_remove(tsdn_t *tsdn, void *opaque);
/* Hooks */
void hook_invoke_alloc(hook_alloc_t type, void *result, uintptr_t result_raw,
    uintptr_t args_raw[3]);
void hook_invoke_dalloc(hook_dalloc_t type, void *address,
    uintptr_t args_raw[3]);
void hook_invoke_expand(hook_expand_t type, void *address, size_t old_usize,
    size_t new_usize, uintptr_t result_raw, uintptr_t args_raw[4]);
#endif /* JEMALLOC_INTERNAL_HOOK_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_decls.h
New file
@@ -0,0 +1,94 @@
#ifndef JEMALLOC_INTERNAL_DECLS_H
#define JEMALLOC_INTERNAL_DECLS_H
#include <math.h>
#ifdef _WIN32
#  include <windows.h>
#  include "msvc_compat/windows_extra.h"
#  ifdef _WIN64
#    if LG_VADDR <= 32
#      error Generate the headers using x64 vcargs
#    endif
#  else
#    if LG_VADDR > 32
#      undef LG_VADDR
#      define LG_VADDR 32
#    endif
#  endif
#else
#  include <sys/param.h>
#  include <sys/mman.h>
#  if !defined(__pnacl__) && !defined(__native_client__)
#    include <sys/syscall.h>
#    if !defined(SYS_write) && defined(__NR_write)
#      define SYS_write __NR_write
#    endif
#    if defined(SYS_open) && defined(__aarch64__)
       /* Android headers may define SYS_open to __NR_open even though
        * __NR_open may not exist on AArch64 (superseded by __NR_openat). */
#      undef SYS_open
#    endif
#    include <sys/uio.h>
#  endif
#  include <pthread.h>
#  ifdef __FreeBSD__
#  include <pthread_np.h>
#  endif
#  include <signal.h>
#  ifdef JEMALLOC_OS_UNFAIR_LOCK
#    include <os/lock.h>
#  endif
#  ifdef JEMALLOC_GLIBC_MALLOC_HOOK
#    include <sched.h>
#  endif
#  include <errno.h>
#  include <sys/time.h>
#  include <time.h>
#  ifdef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME
#    include <mach/mach_time.h>
#  endif
#endif
#include <sys/types.h>
#include <limits.h>
#ifndef SIZE_T_MAX
#  define SIZE_T_MAX    SIZE_MAX
#endif
#ifndef SSIZE_MAX
#  define SSIZE_MAX    ((ssize_t)(SIZE_T_MAX >> 1))
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#ifndef offsetof
#  define offsetof(type, member)    ((size_t)&(((type *)NULL)->member))
#endif
#include <string.h>
#include <strings.h>
#include <ctype.h>
#ifdef _MSC_VER
#  include <io.h>
typedef intptr_t ssize_t;
#  define PATH_MAX 1024
#  define STDERR_FILENO 2
#  define __func__ __FUNCTION__
#  ifdef JEMALLOC_HAS_RESTRICT
#    define restrict __restrict
#  endif
/* Disable warnings about deprecated system functions. */
#  pragma warning(disable: 4996)
#if _MSC_VER < 1800
static int
isblank(int c) {
    return (c == '\t' || c == ' ');
}
#endif
#else
#  include <unistd.h>
#endif
#include <fcntl.h>
#endif /* JEMALLOC_INTERNAL_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_defs.h
New file
@@ -0,0 +1,367 @@
/* include/jemalloc/internal/jemalloc_internal_defs.h.  Generated from jemalloc_internal_defs.h.in by configure.  */
#ifndef JEMALLOC_INTERNAL_DEFS_H_
#define JEMALLOC_INTERNAL_DEFS_H_
/*
 * If JEMALLOC_PREFIX is defined via --with-jemalloc-prefix, it will cause all
 * public APIs to be prefixed.  This makes it possible, with some care, to use
 * multiple allocators simultaneously.
 */
#define JEMALLOC_PREFIX "je_"
#define JEMALLOC_CPREFIX "JE_"
/*
 * Define overrides for non-standard allocator-related functions if they are
 * present on the system.
 */
/* #undef JEMALLOC_OVERRIDE___LIBC_CALLOC */
/* #undef JEMALLOC_OVERRIDE___LIBC_FREE */
/* #undef JEMALLOC_OVERRIDE___LIBC_MALLOC */
/* #undef JEMALLOC_OVERRIDE___LIBC_MEMALIGN */
/* #undef JEMALLOC_OVERRIDE___LIBC_REALLOC */
/* #undef JEMALLOC_OVERRIDE___LIBC_VALLOC */
/* #undef JEMALLOC_OVERRIDE___POSIX_MEMALIGN */
/*
 * JEMALLOC_PRIVATE_NAMESPACE is used as a prefix for all library-private APIs.
 * For shared libraries, symbol visibility mechanisms prevent these symbols
 * from being exported, but for static libraries, naming collisions are a real
 * possibility.
 */
#define JEMALLOC_PRIVATE_NAMESPACE je_
/*
 * Hyper-threaded CPUs may need a special instruction inside spin loops in
 * order to yield to another virtual CPU.
 */
#define CPU_SPINWAIT _mm_pause()
/* 1 if CPU_SPINWAIT is defined, 0 otherwise. */
#define HAVE_CPU_SPINWAIT 1
/*
 * Number of significant bits in virtual addresses.  This may be less than the
 * total number of bits in a pointer, e.g. on x64, for which the uppermost 16
 * bits are the same as bit 47.
 */
#define LG_VADDR 48
/* Defined if C11 atomics are available. */
/* #undef JEMALLOC_C11_ATOMICS */
/* Defined if GCC __atomic atomics are available. */
/* #undef JEMALLOC_GCC_ATOMIC_ATOMICS */
/* and the 8-bit variant support. */
/* #undef JEMALLOC_GCC_U8_ATOMIC_ATOMICS */
/* Defined if GCC __sync atomics are available. */
/* #undef JEMALLOC_GCC_SYNC_ATOMICS */
/* and the 8-bit variant support. */
/* #undef JEMALLOC_GCC_U8_SYNC_ATOMICS */
/*
 * Defined if __builtin_clz() and __builtin_clzl() are available.
 */
/* #undef JEMALLOC_HAVE_BUILTIN_CLZ */
/*
 * Defined if os_unfair_lock_*() functions are available, as provided by Darwin.
 */
/* #undef JEMALLOC_OS_UNFAIR_LOCK */
/* Defined if syscall(2) is usable. */
/* #undef JEMALLOC_USE_SYSCALL */
/*
 * Defined if secure_getenv(3) is available.
 */
/* #undef JEMALLOC_HAVE_SECURE_GETENV */
/*
 * Defined if issetugid(2) is available.
 */
/* #undef JEMALLOC_HAVE_ISSETUGID */
/* Defined if pthread_atfork(3) is available. */
/* #undef JEMALLOC_HAVE_PTHREAD_ATFORK */
/* Defined if pthread_setname_np(3) is available. */
/* #undef JEMALLOC_HAVE_PTHREAD_SETNAME_NP */
/*
 * Defined if clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is available.
 */
/* #undef JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE */
/*
 * Defined if clock_gettime(CLOCK_MONOTONIC, ...) is available.
 */
/* #undef JEMALLOC_HAVE_CLOCK_MONOTONIC */
/*
 * Defined if mach_absolute_time() is available.
 */
/* #undef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME */
/*
 * Defined if _malloc_thread_cleanup() exists.  At least in the case of
 * FreeBSD, pthread_key_create() allocates, which if used during malloc
 * bootstrapping will cause recursion into the pthreads library.  Therefore, if
 * _malloc_thread_cleanup() exists, use it as the basis for thread cleanup in
 * malloc_tsd.
 */
/* #undef JEMALLOC_MALLOC_THREAD_CLEANUP */
/*
 * Defined if threaded initialization is known to be safe on this platform.
 * Among other things, it must be possible to initialize a mutex without
 * triggering allocation in order for threaded allocation to be safe.
 */
/* #undef JEMALLOC_THREADED_INIT */
/*
 * Defined if the pthreads implementation defines
 * _pthread_mutex_init_calloc_cb(), in which case the function is used in order
 * to avoid recursive allocation during mutex initialization.
 */
/* #undef JEMALLOC_MUTEX_INIT_CB */
/* Non-empty if the tls_model attribute is supported. */
#define JEMALLOC_TLS_MODEL
/*
 * JEMALLOC_DEBUG enables assertions and other sanity checks, and disables
 * inline functions.
 */
/* #undef JEMALLOC_DEBUG */
/* JEMALLOC_STATS enables statistics calculation. */
#define JEMALLOC_STATS
/* JEMALLOC_EXPERIMENTAL_SMALLOCX_API enables experimental smallocx API. */
/* #undef JEMALLOC_EXPERIMENTAL_SMALLOCX_API */
/* JEMALLOC_PROF enables allocation profiling. */
/* #undef JEMALLOC_PROF */
/* Use libunwind for profile backtracing if defined. */
/* #undef JEMALLOC_PROF_LIBUNWIND */
/* Use libgcc for profile backtracing if defined. */
/* #undef JEMALLOC_PROF_LIBGCC */
/* Use gcc intrinsics for profile backtracing if defined. */
/* #undef JEMALLOC_PROF_GCC */
/*
 * JEMALLOC_DSS enables use of sbrk(2) to allocate extents from the data storage
 * segment (DSS).
 */
/* #undef JEMALLOC_DSS */
/* Support memory filling (junk/zero). */
#define JEMALLOC_FILL
/* Support utrace(2)-based tracing. */
/* #undef JEMALLOC_UTRACE */
/* Support optional abort() on OOM. */
/* #undef JEMALLOC_XMALLOC */
/* Support lazy locking (avoid locking unless a second thread is launched). */
/* #undef JEMALLOC_LAZY_LOCK */
/*
 * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
 * classes).
 */
/* #undef LG_QUANTUM */
/* One page is 2^LG_PAGE bytes. */
#define LG_PAGE 12
/*
 * One huge page is 2^LG_HUGEPAGE bytes.  Note that this is defined even if the
 * system does not explicitly support huge pages; system calls that require
 * explicit huge page support are separately configured.
 */
#define LG_HUGEPAGE 21
/*
 * If defined, adjacent virtual memory mappings with identical attributes
 * automatically coalesce, and they fragment when changes are made to subranges.
 * This is the normal order of things for mmap()/munmap(), but on Windows
 * VirtualAlloc()/VirtualFree() operations must be precisely matched, i.e.
 * mappings do *not* coalesce/fragment.
 */
/* #undef JEMALLOC_MAPS_COALESCE */
/*
 * If defined, retain memory for later reuse by default rather than using e.g.
 * munmap() to unmap freed extents.  This is enabled on 64-bit Linux because
 * common sequences of mmap()/munmap() calls will cause virtual memory map
 * holes.
 */
/* #undef JEMALLOC_RETAIN */
/* TLS is used to map arenas and magazine caches to threads. */
/* #undef JEMALLOC_TLS */
/*
 * Used to mark unreachable code to quiet "end of non-void" compiler warnings.
 * Don't use this directly; instead use unreachable() from util.h
 */
#define JEMALLOC_INTERNAL_UNREACHABLE abort
/*
 * ffs*() functions to use for bitmapping.  Don't use these directly; instead,
 * use ffs_*() from util.h.
 */
#define JEMALLOC_INTERNAL_FFSLL ffsll
#define JEMALLOC_INTERNAL_FFSL ffsl
#define JEMALLOC_INTERNAL_FFS ffs
/*
 * popcount*() functions to use for bitmapping.
 */
/* #undef JEMALLOC_INTERNAL_POPCOUNTL */
/* #undef JEMALLOC_INTERNAL_POPCOUNT */
/*
 * If defined, explicitly attempt to more uniformly distribute large allocation
 * pointer alignments across all cache indices.
 */
#define JEMALLOC_CACHE_OBLIVIOUS
/*
 * If defined, enable logging facilities.  We make this a configure option to
 * avoid taking extra branches everywhere.
 */
/* #undef JEMALLOC_LOG */
/*
 * If defined, use readlinkat() (instead of readlink()) to follow
 * /etc/malloc_conf.
 */
/* #undef JEMALLOC_READLINKAT */
/*
 * Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.
 */
/* #undef JEMALLOC_ZONE */
/*
 * Methods for determining whether the OS overcommits.
 * JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY: Linux's
 *                                         /proc/sys/vm.overcommit_memory file.
 * JEMALLOC_SYSCTL_VM_OVERCOMMIT: FreeBSD's vm.overcommit sysctl.
 */
/* #undef JEMALLOC_SYSCTL_VM_OVERCOMMIT */
/* #undef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY */
/* Defined if madvise(2) is available. */
/* #undef JEMALLOC_HAVE_MADVISE */
/*
 * Defined if transparent huge pages are supported via the MADV_[NO]HUGEPAGE
 * arguments to madvise(2).
 */
/* #undef JEMALLOC_HAVE_MADVISE_HUGE */
/*
 * Methods for purging unused pages differ between operating systems.
 *
 *   madvise(..., MADV_FREE) : This marks pages as being unused, such that they
 *                             will be discarded rather than swapped out.
 *   madvise(..., MADV_DONTNEED) : If JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS is
 *                                 defined, this immediately discards pages,
 *                                 such that new pages will be demand-zeroed if
 *                                 the address region is later touched;
 *                                 otherwise this behaves similarly to
 *                                 MADV_FREE, though typically with higher
 *                                 system overhead.
 */
/* #undef JEMALLOC_PURGE_MADVISE_FREE */
/* #undef JEMALLOC_PURGE_MADVISE_DONTNEED */
/* #undef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS */
/* Defined if madvise(2) is available but MADV_FREE is not (x86 Linux only). */
/* #undef JEMALLOC_DEFINE_MADVISE_FREE */
/*
 * Defined if MADV_DO[NT]DUMP is supported as an argument to madvise.
 */
/* #undef JEMALLOC_MADVISE_DONTDUMP */
/*
 * Defined if transparent huge pages (THPs) are supported via the
 * MADV_[NO]HUGEPAGE arguments to madvise(2), and THP support is enabled.
 */
/* #undef JEMALLOC_THP */
/* Define if operating system has alloca.h header. */
/* #undef JEMALLOC_HAS_ALLOCA_H */
/* C99 restrict keyword supported. */
/* #undef JEMALLOC_HAS_RESTRICT */
/* For use by hash code. */
/* #undef JEMALLOC_BIG_ENDIAN */
/* sizeof(int) == 2^LG_SIZEOF_INT. */
#define LG_SIZEOF_INT 2
/* sizeof(long) == 2^LG_SIZEOF_LONG. */
#define LG_SIZEOF_LONG 2
/* sizeof(long long) == 2^LG_SIZEOF_LONG_LONG. */
#define LG_SIZEOF_LONG_LONG 3
/* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */
#define LG_SIZEOF_INTMAX_T 3
/* glibc malloc hooks (__malloc_hook, __realloc_hook, __free_hook). */
/* #undef JEMALLOC_GLIBC_MALLOC_HOOK */
/* glibc memalign hook. */
/* #undef JEMALLOC_GLIBC_MEMALIGN_HOOK */
/* pthread support */
/* #undef JEMALLOC_HAVE_PTHREAD */
/* dlsym() support */
/* #undef JEMALLOC_HAVE_DLSYM */
/* Adaptive mutex support in pthreads. */
/* #undef JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */
/* GNU specific sched_getcpu support */
/* #undef JEMALLOC_HAVE_SCHED_GETCPU */
/* GNU specific sched_setaffinity support */
/* #undef JEMALLOC_HAVE_SCHED_SETAFFINITY */
/*
 * If defined, all the features necessary for background threads are present.
 */
/* #undef JEMALLOC_BACKGROUND_THREAD */
/*
 * If defined, jemalloc symbols are not exported (doesn't work when
 * JEMALLOC_PREFIX is not defined).
 */
/* #undef JEMALLOC_EXPORT */
/* config.malloc_conf options string. */
#define JEMALLOC_CONFIG_MALLOC_CONF ""
/* If defined, jemalloc takes the malloc/free/etc. symbol names. */
/* #undef JEMALLOC_IS_MALLOC */
/*
 * Defined if strerror_r returns char * if _GNU_SOURCE is defined.
 */
/* #undef JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE */
/* Performs additional safety checks when defined. */
/* #undef JEMALLOC_OPT_SAFETY_CHECKS */
#endif /* JEMALLOC_INTERNAL_DEFS_H_ */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_defs.h.in
New file
@@ -0,0 +1,366 @@
#ifndef JEMALLOC_INTERNAL_DEFS_H_
#define JEMALLOC_INTERNAL_DEFS_H_
/*
 * If JEMALLOC_PREFIX is defined via --with-jemalloc-prefix, it will cause all
 * public APIs to be prefixed.  This makes it possible, with some care, to use
 * multiple allocators simultaneously.
 */
#undef JEMALLOC_PREFIX
#undef JEMALLOC_CPREFIX
/*
 * Define overrides for non-standard allocator-related functions if they are
 * present on the system.
 */
#undef JEMALLOC_OVERRIDE___LIBC_CALLOC
#undef JEMALLOC_OVERRIDE___LIBC_FREE
#undef JEMALLOC_OVERRIDE___LIBC_MALLOC
#undef JEMALLOC_OVERRIDE___LIBC_MEMALIGN
#undef JEMALLOC_OVERRIDE___LIBC_REALLOC
#undef JEMALLOC_OVERRIDE___LIBC_VALLOC
#undef JEMALLOC_OVERRIDE___POSIX_MEMALIGN
/*
 * JEMALLOC_PRIVATE_NAMESPACE is used as a prefix for all library-private APIs.
 * For shared libraries, symbol visibility mechanisms prevent these symbols
 * from being exported, but for static libraries, naming collisions are a real
 * possibility.
 */
#undef JEMALLOC_PRIVATE_NAMESPACE
/*
 * Hyper-threaded CPUs may need a special instruction inside spin loops in
 * order to yield to another virtual CPU.
 */
#undef CPU_SPINWAIT
/* 1 if CPU_SPINWAIT is defined, 0 otherwise. */
#undef HAVE_CPU_SPINWAIT
/*
 * Number of significant bits in virtual addresses.  This may be less than the
 * total number of bits in a pointer, e.g. on x64, for which the uppermost 16
 * bits are the same as bit 47.
 */
#undef LG_VADDR
/* Defined if C11 atomics are available. */
#undef JEMALLOC_C11_ATOMICS
/* Defined if GCC __atomic atomics are available. */
#undef JEMALLOC_GCC_ATOMIC_ATOMICS
/* and the 8-bit variant support. */
#undef JEMALLOC_GCC_U8_ATOMIC_ATOMICS
/* Defined if GCC __sync atomics are available. */
#undef JEMALLOC_GCC_SYNC_ATOMICS
/* and the 8-bit variant support. */
#undef JEMALLOC_GCC_U8_SYNC_ATOMICS
/*
 * Defined if __builtin_clz() and __builtin_clzl() are available.
 */
#undef JEMALLOC_HAVE_BUILTIN_CLZ
/*
 * Defined if os_unfair_lock_*() functions are available, as provided by Darwin.
 */
#undef JEMALLOC_OS_UNFAIR_LOCK
/* Defined if syscall(2) is usable. */
#undef JEMALLOC_USE_SYSCALL
/*
 * Defined if secure_getenv(3) is available.
 */
#undef JEMALLOC_HAVE_SECURE_GETENV
/*
 * Defined if issetugid(2) is available.
 */
#undef JEMALLOC_HAVE_ISSETUGID
/* Defined if pthread_atfork(3) is available. */
#undef JEMALLOC_HAVE_PTHREAD_ATFORK
/* Defined if pthread_setname_np(3) is available. */
#undef JEMALLOC_HAVE_PTHREAD_SETNAME_NP
/*
 * Defined if clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is available.
 */
#undef JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE
/*
 * Defined if clock_gettime(CLOCK_MONOTONIC, ...) is available.
 */
#undef JEMALLOC_HAVE_CLOCK_MONOTONIC
/*
 * Defined if mach_absolute_time() is available.
 */
#undef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME
/*
 * Defined if _malloc_thread_cleanup() exists.  At least in the case of
 * FreeBSD, pthread_key_create() allocates, which if used during malloc
 * bootstrapping will cause recursion into the pthreads library.  Therefore, if
 * _malloc_thread_cleanup() exists, use it as the basis for thread cleanup in
 * malloc_tsd.
 */
#undef JEMALLOC_MALLOC_THREAD_CLEANUP
/*
 * Defined if threaded initialization is known to be safe on this platform.
 * Among other things, it must be possible to initialize a mutex without
 * triggering allocation in order for threaded allocation to be safe.
 */
#undef JEMALLOC_THREADED_INIT
/*
 * Defined if the pthreads implementation defines
 * _pthread_mutex_init_calloc_cb(), in which case the function is used in order
 * to avoid recursive allocation during mutex initialization.
 */
#undef JEMALLOC_MUTEX_INIT_CB
/* Non-empty if the tls_model attribute is supported. */
#undef JEMALLOC_TLS_MODEL
/*
 * JEMALLOC_DEBUG enables assertions and other sanity checks, and disables
 * inline functions.
 */
#undef JEMALLOC_DEBUG
/* JEMALLOC_STATS enables statistics calculation. */
#undef JEMALLOC_STATS
/* JEMALLOC_EXPERIMENTAL_SMALLOCX_API enables experimental smallocx API. */
#undef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
/* JEMALLOC_PROF enables allocation profiling. */
#undef JEMALLOC_PROF
/* Use libunwind for profile backtracing if defined. */
#undef JEMALLOC_PROF_LIBUNWIND
/* Use libgcc for profile backtracing if defined. */
#undef JEMALLOC_PROF_LIBGCC
/* Use gcc intrinsics for profile backtracing if defined. */
#undef JEMALLOC_PROF_GCC
/*
 * JEMALLOC_DSS enables use of sbrk(2) to allocate extents from the data storage
 * segment (DSS).
 */
#undef JEMALLOC_DSS
/* Support memory filling (junk/zero). */
#undef JEMALLOC_FILL
/* Support utrace(2)-based tracing. */
#undef JEMALLOC_UTRACE
/* Support optional abort() on OOM. */
#undef JEMALLOC_XMALLOC
/* Support lazy locking (avoid locking unless a second thread is launched). */
#undef JEMALLOC_LAZY_LOCK
/*
 * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
 * classes).
 */
#undef LG_QUANTUM
/* One page is 2^LG_PAGE bytes. */
#undef LG_PAGE
/*
 * One huge page is 2^LG_HUGEPAGE bytes.  Note that this is defined even if the
 * system does not explicitly support huge pages; system calls that require
 * explicit huge page support are separately configured.
 */
#undef LG_HUGEPAGE
/*
 * If defined, adjacent virtual memory mappings with identical attributes
 * automatically coalesce, and they fragment when changes are made to subranges.
 * This is the normal order of things for mmap()/munmap(), but on Windows
 * VirtualAlloc()/VirtualFree() operations must be precisely matched, i.e.
 * mappings do *not* coalesce/fragment.
 */
#undef JEMALLOC_MAPS_COALESCE
/*
 * If defined, retain memory for later reuse by default rather than using e.g.
 * munmap() to unmap freed extents.  This is enabled on 64-bit Linux because
 * common sequences of mmap()/munmap() calls will cause virtual memory map
 * holes.
 */
#undef JEMALLOC_RETAIN
/* TLS is used to map arenas and magazine caches to threads. */
#undef JEMALLOC_TLS
/*
 * Used to mark unreachable code to quiet "end of non-void" compiler warnings.
 * Don't use this directly; instead use unreachable() from util.h
 */
#undef JEMALLOC_INTERNAL_UNREACHABLE
/*
 * ffs*() functions to use for bitmapping.  Don't use these directly; instead,
 * use ffs_*() from util.h.
 */
#undef JEMALLOC_INTERNAL_FFSLL
#undef JEMALLOC_INTERNAL_FFSL
#undef JEMALLOC_INTERNAL_FFS
/*
 * popcount*() functions to use for bitmapping.
 */
#undef JEMALLOC_INTERNAL_POPCOUNTL
#undef JEMALLOC_INTERNAL_POPCOUNT
/*
 * If defined, explicitly attempt to more uniformly distribute large allocation
 * pointer alignments across all cache indices.
 */
#undef JEMALLOC_CACHE_OBLIVIOUS
/*
 * If defined, enable logging facilities.  We make this a configure option to
 * avoid taking extra branches everywhere.
 */
#undef JEMALLOC_LOG
/*
 * If defined, use readlinkat() (instead of readlink()) to follow
 * /etc/malloc_conf.
 */
#undef JEMALLOC_READLINKAT
/*
 * Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.
 */
#undef JEMALLOC_ZONE
/*
 * Methods for determining whether the OS overcommits.
 * JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY: Linux's
 *                                         /proc/sys/vm.overcommit_memory file.
 * JEMALLOC_SYSCTL_VM_OVERCOMMIT: FreeBSD's vm.overcommit sysctl.
 */
#undef JEMALLOC_SYSCTL_VM_OVERCOMMIT
#undef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY
/* Defined if madvise(2) is available. */
#undef JEMALLOC_HAVE_MADVISE
/*
 * Defined if transparent huge pages are supported via the MADV_[NO]HUGEPAGE
 * arguments to madvise(2).
 */
#undef JEMALLOC_HAVE_MADVISE_HUGE
/*
 * Methods for purging unused pages differ between operating systems.
 *
 *   madvise(..., MADV_FREE) : This marks pages as being unused, such that they
 *                             will be discarded rather than swapped out.
 *   madvise(..., MADV_DONTNEED) : If JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS is
 *                                 defined, this immediately discards pages,
 *                                 such that new pages will be demand-zeroed if
 *                                 the address region is later touched;
 *                                 otherwise this behaves similarly to
 *                                 MADV_FREE, though typically with higher
 *                                 system overhead.
 */
#undef JEMALLOC_PURGE_MADVISE_FREE
#undef JEMALLOC_PURGE_MADVISE_DONTNEED
#undef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS
/* Defined if madvise(2) is available but MADV_FREE is not (x86 Linux only). */
#undef JEMALLOC_DEFINE_MADVISE_FREE
/*
 * Defined if MADV_DO[NT]DUMP is supported as an argument to madvise.
 */
#undef JEMALLOC_MADVISE_DONTDUMP
/*
 * Defined if transparent huge pages (THPs) are supported via the
 * MADV_[NO]HUGEPAGE arguments to madvise(2), and THP support is enabled.
 */
#undef JEMALLOC_THP
/* Define if operating system has alloca.h header. */
#undef JEMALLOC_HAS_ALLOCA_H
/* C99 restrict keyword supported. */
#undef JEMALLOC_HAS_RESTRICT
/* For use by hash code. */
#undef JEMALLOC_BIG_ENDIAN
/* sizeof(int) == 2^LG_SIZEOF_INT. */
#undef LG_SIZEOF_INT
/* sizeof(long) == 2^LG_SIZEOF_LONG. */
#undef LG_SIZEOF_LONG
/* sizeof(long long) == 2^LG_SIZEOF_LONG_LONG. */
#undef LG_SIZEOF_LONG_LONG
/* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */
#undef LG_SIZEOF_INTMAX_T
/* glibc malloc hooks (__malloc_hook, __realloc_hook, __free_hook). */
#undef JEMALLOC_GLIBC_MALLOC_HOOK
/* glibc memalign hook. */
#undef JEMALLOC_GLIBC_MEMALIGN_HOOK
/* pthread support */
#undef JEMALLOC_HAVE_PTHREAD
/* dlsym() support */
#undef JEMALLOC_HAVE_DLSYM
/* Adaptive mutex support in pthreads. */
#undef JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
/* GNU specific sched_getcpu support */
#undef JEMALLOC_HAVE_SCHED_GETCPU
/* GNU specific sched_setaffinity support */
#undef JEMALLOC_HAVE_SCHED_SETAFFINITY
/*
 * If defined, all the features necessary for background threads are present.
 */
#undef JEMALLOC_BACKGROUND_THREAD
/*
 * If defined, jemalloc symbols are not exported (doesn't work when
 * JEMALLOC_PREFIX is not defined).
 */
#undef JEMALLOC_EXPORT
/* config.malloc_conf options string. */
#undef JEMALLOC_CONFIG_MALLOC_CONF
/* If defined, jemalloc takes the malloc/free/etc. symbol names. */
#undef JEMALLOC_IS_MALLOC
/*
 * Defined if strerror_r returns char * if _GNU_SOURCE is defined.
 */
#undef JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE
/* Performs additional safety checks when defined. */
#undef JEMALLOC_OPT_SAFETY_CHECKS
#endif /* JEMALLOC_INTERNAL_DEFS_H_ */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_externs.h
New file
@@ -0,0 +1,57 @@
#ifndef JEMALLOC_INTERNAL_EXTERNS_H
#define JEMALLOC_INTERNAL_EXTERNS_H
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/tsd_types.h"
/* TSD checks this to set thread local slow state accordingly. */
extern bool malloc_slow;
/* Run-time options. */
extern bool opt_abort;
extern bool opt_abort_conf;
extern bool opt_confirm_conf;
extern const char *opt_junk;
extern bool opt_junk_alloc;
extern bool opt_junk_free;
extern bool opt_utrace;
extern bool opt_xmalloc;
extern bool opt_zero;
extern unsigned opt_narenas;
/* Number of CPUs. */
extern unsigned ncpus;
/* Number of arenas used for automatic multiplexing of threads and arenas. */
extern unsigned narenas_auto;
/* Base index for manual arenas. */
extern unsigned manual_arena_base;
/*
 * Arenas that are used to service external requests.  Not all elements of the
 * arenas array are necessarily used; arenas are created lazily as needed.
 */
extern atomic_p_t arenas[];
void *a0malloc(size_t size);
void a0dalloc(void *ptr);
void *bootstrap_malloc(size_t size);
void *bootstrap_calloc(size_t num, size_t size);
void bootstrap_free(void *ptr);
void arena_set(unsigned ind, arena_t *arena);
unsigned narenas_total_get(void);
arena_t *arena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
arena_tdata_t *arena_tdata_get_hard(tsd_t *tsd, unsigned ind);
arena_t *arena_choose_hard(tsd_t *tsd, bool internal);
void arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind);
void iarena_cleanup(tsd_t *tsd);
void arena_cleanup(tsd_t *tsd);
void arenas_tdata_cleanup(tsd_t *tsd);
void jemalloc_prefork(void);
void jemalloc_postfork_parent(void);
void jemalloc_postfork_child(void);
bool malloc_initialized(void);
void je_sdallocx_noflags(void *ptr, size_t size);
#endif /* JEMALLOC_INTERNAL_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_includes.h
New file
@@ -0,0 +1,94 @@
#ifndef JEMALLOC_INTERNAL_INCLUDES_H
#define JEMALLOC_INTERNAL_INCLUDES_H
/*
 * jemalloc can conceptually be broken into components (arena, tcache, etc.),
 * but there are circular dependencies that cannot be broken without
 * substantial performance degradation.
 *
 * Historically, we dealt with this by each header into four sections (types,
 * structs, externs, and inlines), and included each header file multiple times
 * in this file, picking out the portion we want on each pass using the
 * following #defines:
 *   JEMALLOC_H_TYPES   : Preprocessor-defined constants and psuedo-opaque data
 *                        types.
 *   JEMALLOC_H_STRUCTS : Data structures.
 *   JEMALLOC_H_EXTERNS : Extern data declarations and function prototypes.
 *   JEMALLOC_H_INLINES : Inline functions.
 *
 * We're moving toward a world in which the dependencies are explicit; each file
 * will #include the headers it depends on (rather than relying on them being
 * implicitly available via this file including every header file in the
 * project).
 *
 * We're now in an intermediate state: we've broken up the header files to avoid
 * having to include each one multiple times, but have not yet moved the
 * dependency information into the header files (i.e. we still rely on the
 * ordering in this file to ensure all a header's dependencies are available in
 * its translation unit).  Each component is now broken up into multiple header
 * files, corresponding to the sections above (e.g. instead of "foo.h", we now
 * have "foo_types.h", "foo_structs.h", "foo_externs.h", "foo_inlines.h").
 *
 * Those files which have been converted to explicitly include their
 * inter-component dependencies are now in the initial HERMETIC HEADERS
 * section.  All headers may still rely on jemalloc_preamble.h (which, by fiat,
 * must be included first in every translation unit) for system headers and
 * global jemalloc definitions, however.
 */
/******************************************************************************/
/* TYPES */
/******************************************************************************/
#include "jemalloc/internal/extent_types.h"
#include "jemalloc/internal/base_types.h"
#include "jemalloc/internal/arena_types.h"
#include "jemalloc/internal/tcache_types.h"
#include "jemalloc/internal/prof_types.h"
/******************************************************************************/
/* STRUCTS */
/******************************************************************************/
#include "jemalloc/internal/arena_structs_a.h"
#include "jemalloc/internal/extent_structs.h"
#include "jemalloc/internal/base_structs.h"
#include "jemalloc/internal/prof_structs.h"
#include "jemalloc/internal/arena_structs_b.h"
#include "jemalloc/internal/tcache_structs.h"
#include "jemalloc/internal/background_thread_structs.h"
/******************************************************************************/
/* EXTERNS */
/******************************************************************************/
#include "jemalloc/internal/jemalloc_internal_externs.h"
#include "jemalloc/internal/extent_externs.h"
#include "jemalloc/internal/base_externs.h"
#include "jemalloc/internal/arena_externs.h"
#include "jemalloc/internal/large_externs.h"
#include "jemalloc/internal/tcache_externs.h"
#include "jemalloc/internal/prof_externs.h"
#include "jemalloc/internal/background_thread_externs.h"
/******************************************************************************/
/* INLINES */
/******************************************************************************/
#include "jemalloc/internal/jemalloc_internal_inlines_a.h"
#include "jemalloc/internal/base_inlines.h"
/*
 * Include portions of arena code interleaved with tcache code in order to
 * resolve circular dependencies.
 */
#include "jemalloc/internal/prof_inlines_a.h"
#include "jemalloc/internal/arena_inlines_a.h"
#include "jemalloc/internal/extent_inlines.h"
#include "jemalloc/internal/jemalloc_internal_inlines_b.h"
#include "jemalloc/internal/tcache_inlines.h"
#include "jemalloc/internal/arena_inlines_b.h"
#include "jemalloc/internal/jemalloc_internal_inlines_c.h"
#include "jemalloc/internal/prof_inlines_b.h"
#include "jemalloc/internal/background_thread_inlines.h"
#endif /* JEMALLOC_INTERNAL_INCLUDES_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_inlines_a.h
New file
@@ -0,0 +1,174 @@
#ifndef JEMALLOC_INTERNAL_INLINES_A_H
#define JEMALLOC_INTERNAL_INLINES_A_H
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/bit_util.h"
#include "jemalloc/internal/jemalloc_internal_types.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/ticker.h"
JEMALLOC_ALWAYS_INLINE malloc_cpuid_t
malloc_getcpu(void) {
    assert(have_percpu_arena);
#if defined(_WIN32)
    return GetCurrentProcessorNumber();
#elif defined(JEMALLOC_HAVE_SCHED_GETCPU)
    return (malloc_cpuid_t)sched_getcpu();
#else
    not_reached();
    return -1;
#endif
}
/* Return the chosen arena index based on current cpu. */
JEMALLOC_ALWAYS_INLINE unsigned
percpu_arena_choose(void) {
    assert(have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena));
    malloc_cpuid_t cpuid = malloc_getcpu();
    assert(cpuid >= 0);
    unsigned arena_ind;
    if ((opt_percpu_arena == percpu_arena) || ((unsigned)cpuid < ncpus /
        2)) {
        arena_ind = cpuid;
    } else {
        assert(opt_percpu_arena == per_phycpu_arena);
        /* Hyper threads on the same physical CPU share arena. */
        arena_ind = cpuid - ncpus / 2;
    }
    return arena_ind;
}
/* Return the limit of percpu auto arena range, i.e. arenas[0...ind_limit). */
JEMALLOC_ALWAYS_INLINE unsigned
percpu_arena_ind_limit(percpu_arena_mode_t mode) {
    assert(have_percpu_arena && PERCPU_ARENA_ENABLED(mode));
    if (mode == per_phycpu_arena && ncpus > 1) {
        if (ncpus % 2) {
            /* This likely means a misconfig. */
            return ncpus / 2 + 1;
        }
        return ncpus / 2;
    } else {
        return ncpus;
    }
}
static inline arena_tdata_t *
arena_tdata_get(tsd_t *tsd, unsigned ind, bool refresh_if_missing) {
    arena_tdata_t *tdata;
    arena_tdata_t *arenas_tdata = tsd_arenas_tdata_get(tsd);
    if (unlikely(arenas_tdata == NULL)) {
        /* arenas_tdata hasn't been initialized yet. */
        return arena_tdata_get_hard(tsd, ind);
    }
    if (unlikely(ind >= tsd_narenas_tdata_get(tsd))) {
        /*
         * ind is invalid, cache is old (too small), or tdata to be
         * initialized.
         */
        return (refresh_if_missing ? arena_tdata_get_hard(tsd, ind) :
            NULL);
    }
    tdata = &arenas_tdata[ind];
    if (likely(tdata != NULL) || !refresh_if_missing) {
        return tdata;
    }
    return arena_tdata_get_hard(tsd, ind);
}
static inline arena_t *
arena_get(tsdn_t *tsdn, unsigned ind, bool init_if_missing) {
    arena_t *ret;
    assert(ind < MALLOCX_ARENA_LIMIT);
    ret = (arena_t *)atomic_load_p(&arenas[ind], ATOMIC_ACQUIRE);
    if (unlikely(ret == NULL)) {
        if (init_if_missing) {
            ret = arena_init(tsdn, ind,
                (extent_hooks_t *)&extent_hooks_default);
        }
    }
    return ret;
}
static inline ticker_t *
decay_ticker_get(tsd_t *tsd, unsigned ind) {
    arena_tdata_t *tdata;
    tdata = arena_tdata_get(tsd, ind, true);
    if (unlikely(tdata == NULL)) {
        return NULL;
    }
    return &tdata->decay_ticker;
}
JEMALLOC_ALWAYS_INLINE cache_bin_t *
tcache_small_bin_get(tcache_t *tcache, szind_t binind) {
    assert(binind < SC_NBINS);
    return &tcache->bins_small[binind];
}
JEMALLOC_ALWAYS_INLINE cache_bin_t *
tcache_large_bin_get(tcache_t *tcache, szind_t binind) {
    assert(binind >= SC_NBINS &&binind < nhbins);
    return &tcache->bins_large[binind - SC_NBINS];
}
JEMALLOC_ALWAYS_INLINE bool
tcache_available(tsd_t *tsd) {
    /*
     * Thread specific auto tcache might be unavailable if: 1) during tcache
     * initialization, or 2) disabled through thread.tcache.enabled mallctl
     * or config options.  This check covers all cases.
     */
    if (likely(tsd_tcache_enabled_get(tsd))) {
        /* Associated arena == NULL implies tcache init in progress. */
        assert(tsd_tcachep_get(tsd)->arena == NULL ||
            tcache_small_bin_get(tsd_tcachep_get(tsd), 0)->avail !=
            NULL);
        return true;
    }
    return false;
}
JEMALLOC_ALWAYS_INLINE tcache_t *
tcache_get(tsd_t *tsd) {
    if (!tcache_available(tsd)) {
        return NULL;
    }
    return tsd_tcachep_get(tsd);
}
static inline void
pre_reentrancy(tsd_t *tsd, arena_t *arena) {
    /* arena is the current context.  Reentry from a0 is not allowed. */
    assert(arena != arena_get(tsd_tsdn(tsd), 0, false));
    bool fast = tsd_fast(tsd);
    assert(tsd_reentrancy_level_get(tsd) < INT8_MAX);
    ++*tsd_reentrancy_levelp_get(tsd);
    if (fast) {
        /* Prepare slow path for reentrancy. */
        tsd_slow_update(tsd);
        assert(tsd_state_get(tsd) == tsd_state_nominal_slow);
    }
}
static inline void
post_reentrancy(tsd_t *tsd) {
    int8_t *reentrancy_level = tsd_reentrancy_levelp_get(tsd);
    assert(*reentrancy_level > 0);
    if (--*reentrancy_level == 0) {
        tsd_slow_update(tsd);
    }
}
#endif /* JEMALLOC_INTERNAL_INLINES_A_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_inlines_b.h
New file
@@ -0,0 +1,87 @@
#ifndef JEMALLOC_INTERNAL_INLINES_B_H
#define JEMALLOC_INTERNAL_INLINES_B_H
#include "jemalloc/internal/rtree.h"
/* Choose an arena based on a per-thread value. */
static inline arena_t *
arena_choose_impl(tsd_t *tsd, arena_t *arena, bool internal) {
    arena_t *ret;
    if (arena != NULL) {
        return arena;
    }
    /* During reentrancy, arena 0 is the safest bet. */
    if (unlikely(tsd_reentrancy_level_get(tsd) > 0)) {
        return arena_get(tsd_tsdn(tsd), 0, true);
    }
    ret = internal ? tsd_iarena_get(tsd) : tsd_arena_get(tsd);
    if (unlikely(ret == NULL)) {
        ret = arena_choose_hard(tsd, internal);
        assert(ret);
        if (tcache_available(tsd)) {
            tcache_t *tcache = tcache_get(tsd);
            if (tcache->arena != NULL) {
                /* See comments in tcache_data_init().*/
                assert(tcache->arena ==
                    arena_get(tsd_tsdn(tsd), 0, false));
                if (tcache->arena != ret) {
                    tcache_arena_reassociate(tsd_tsdn(tsd),
                        tcache, ret);
                }
            } else {
                tcache_arena_associate(tsd_tsdn(tsd), tcache,
                    ret);
            }
        }
    }
    /*
     * Note that for percpu arena, if the current arena is outside of the
     * auto percpu arena range, (i.e. thread is assigned to a manually
     * managed arena), then percpu arena is skipped.
     */
    if (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena) &&
        !internal && (arena_ind_get(ret) <
        percpu_arena_ind_limit(opt_percpu_arena)) && (ret->last_thd !=
        tsd_tsdn(tsd))) {
        unsigned ind = percpu_arena_choose();
        if (arena_ind_get(ret) != ind) {
            percpu_arena_update(tsd, ind);
            ret = tsd_arena_get(tsd);
        }
        ret->last_thd = tsd_tsdn(tsd);
    }
    return ret;
}
static inline arena_t *
arena_choose(tsd_t *tsd, arena_t *arena) {
    return arena_choose_impl(tsd, arena, false);
}
static inline arena_t *
arena_ichoose(tsd_t *tsd, arena_t *arena) {
    return arena_choose_impl(tsd, arena, true);
}
static inline bool
arena_is_auto(arena_t *arena) {
    assert(narenas_auto > 0);
    return (arena_ind_get(arena) < manual_arena_base);
}
JEMALLOC_ALWAYS_INLINE extent_t *
iealloc(tsdn_t *tsdn, const void *ptr) {
    rtree_ctx_t rtree_ctx_fallback;
    rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
    return rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,
        (uintptr_t)ptr, true);
}
#endif /* JEMALLOC_INTERNAL_INLINES_B_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_inlines_c.h
New file
@@ -0,0 +1,222 @@
#ifndef JEMALLOC_INTERNAL_INLINES_C_H
#define JEMALLOC_INTERNAL_INLINES_C_H
#include "jemalloc/internal/hook.h"
#include "jemalloc/internal/jemalloc_internal_types.h"
#include "jemalloc/internal/sz.h"
#include "jemalloc/internal/witness.h"
/*
 * Translating the names of the 'i' functions:
 *   Abbreviations used in the first part of the function name (before
 *   alloc/dalloc) describe what that function accomplishes:
 *     a: arena (query)
 *     s: size (query, or sized deallocation)
 *     e: extent (query)
 *     p: aligned (allocates)
 *     vs: size (query, without knowing that the pointer is into the heap)
 *     r: rallocx implementation
 *     x: xallocx implementation
 *   Abbreviations used in the second part of the function name (after
 *   alloc/dalloc) describe the arguments it takes
 *     z: whether to return zeroed memory
 *     t: accepts a tcache_t * parameter
 *     m: accepts an arena_t * parameter
 */
JEMALLOC_ALWAYS_INLINE arena_t *
iaalloc(tsdn_t *tsdn, const void *ptr) {
    assert(ptr != NULL);
    return arena_aalloc(tsdn, ptr);
}
JEMALLOC_ALWAYS_INLINE size_t
isalloc(tsdn_t *tsdn, const void *ptr) {
    assert(ptr != NULL);
    return arena_salloc(tsdn, ptr);
}
JEMALLOC_ALWAYS_INLINE void *
iallocztm(tsdn_t *tsdn, size_t size, szind_t ind, bool zero, tcache_t *tcache,
    bool is_internal, arena_t *arena, bool slow_path) {
    void *ret;
    assert(!is_internal || tcache == NULL);
    assert(!is_internal || arena == NULL || arena_is_auto(arena));
    if (!tsdn_null(tsdn) && tsd_reentrancy_level_get(tsdn_tsd(tsdn)) == 0) {
        witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
            WITNESS_RANK_CORE, 0);
    }
    ret = arena_malloc(tsdn, arena, size, ind, zero, tcache, slow_path);
    if (config_stats && is_internal && likely(ret != NULL)) {
        arena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));
    }
    return ret;
}
JEMALLOC_ALWAYS_INLINE void *
ialloc(tsd_t *tsd, size_t size, szind_t ind, bool zero, bool slow_path) {
    return iallocztm(tsd_tsdn(tsd), size, ind, zero, tcache_get(tsd), false,
        NULL, slow_path);
}
JEMALLOC_ALWAYS_INLINE void *
ipallocztm(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
    tcache_t *tcache, bool is_internal, arena_t *arena) {
    void *ret;
    assert(usize != 0);
    assert(usize == sz_sa2u(usize, alignment));
    assert(!is_internal || tcache == NULL);
    assert(!is_internal || arena == NULL || arena_is_auto(arena));
    witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
        WITNESS_RANK_CORE, 0);
    ret = arena_palloc(tsdn, arena, usize, alignment, zero, tcache);
    assert(ALIGNMENT_ADDR2BASE(ret, alignment) == ret);
    if (config_stats && is_internal && likely(ret != NULL)) {
        arena_internal_add(iaalloc(tsdn, ret), isalloc(tsdn, ret));
    }
    return ret;
}
JEMALLOC_ALWAYS_INLINE void *
ipalloct(tsdn_t *tsdn, size_t usize, size_t alignment, bool zero,
    tcache_t *tcache, arena_t *arena) {
    return ipallocztm(tsdn, usize, alignment, zero, tcache, false, arena);
}
JEMALLOC_ALWAYS_INLINE void *
ipalloc(tsd_t *tsd, size_t usize, size_t alignment, bool zero) {
    return ipallocztm(tsd_tsdn(tsd), usize, alignment, zero,
        tcache_get(tsd), false, NULL);
}
JEMALLOC_ALWAYS_INLINE size_t
ivsalloc(tsdn_t *tsdn, const void *ptr) {
    return arena_vsalloc(tsdn, ptr);
}
JEMALLOC_ALWAYS_INLINE void
idalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache, alloc_ctx_t *alloc_ctx,
    bool is_internal, bool slow_path) {
    assert(ptr != NULL);
    assert(!is_internal || tcache == NULL);
    assert(!is_internal || arena_is_auto(iaalloc(tsdn, ptr)));
    witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
        WITNESS_RANK_CORE, 0);
    if (config_stats && is_internal) {
        arena_internal_sub(iaalloc(tsdn, ptr), isalloc(tsdn, ptr));
    }
    if (!is_internal && !tsdn_null(tsdn) &&
        tsd_reentrancy_level_get(tsdn_tsd(tsdn)) != 0) {
        assert(tcache == NULL);
    }
    arena_dalloc(tsdn, ptr, tcache, alloc_ctx, slow_path);
}
JEMALLOC_ALWAYS_INLINE void
idalloc(tsd_t *tsd, void *ptr) {
    idalloctm(tsd_tsdn(tsd), ptr, tcache_get(tsd), NULL, false, true);
}
JEMALLOC_ALWAYS_INLINE void
isdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
    alloc_ctx_t *alloc_ctx, bool slow_path) {
    witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
        WITNESS_RANK_CORE, 0);
    arena_sdalloc(tsdn, ptr, size, tcache, alloc_ctx, slow_path);
}
JEMALLOC_ALWAYS_INLINE void *
iralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
    size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,
    hook_ralloc_args_t *hook_args) {
    witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
        WITNESS_RANK_CORE, 0);
    void *p;
    size_t usize, copysize;
    usize = sz_sa2u(size, alignment);
    if (unlikely(usize == 0 || usize > SC_LARGE_MAXCLASS)) {
        return NULL;
    }
    p = ipalloct(tsdn, usize, alignment, zero, tcache, arena);
    if (p == NULL) {
        return NULL;
    }
    /*
     * Copy at most size bytes (not size+extra), since the caller has no
     * expectation that the extra bytes will be reliably preserved.
     */
    copysize = (size < oldsize) ? size : oldsize;
    memcpy(p, ptr, copysize);
    hook_invoke_alloc(hook_args->is_realloc
        ? hook_alloc_realloc : hook_alloc_rallocx, p, (uintptr_t)p,
        hook_args->args);
    hook_invoke_dalloc(hook_args->is_realloc
        ? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args);
    isdalloct(tsdn, ptr, oldsize, tcache, NULL, true);
    return p;
}
/*
 * is_realloc threads through the knowledge of whether or not this call comes
 * from je_realloc (as opposed to je_rallocx); this ensures that we pass the
 * correct entry point into any hooks.
 * Note that these functions are all force-inlined, so no actual bool gets
 * passed-around anywhere.
 */
JEMALLOC_ALWAYS_INLINE void *
iralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,
    bool zero, tcache_t *tcache, arena_t *arena, hook_ralloc_args_t *hook_args)
{
    assert(ptr != NULL);
    assert(size != 0);
    witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
        WITNESS_RANK_CORE, 0);
    if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))
        != 0) {
        /*
         * Existing object alignment is inadequate; allocate new space
         * and copy.
         */
        return iralloct_realign(tsdn, ptr, oldsize, size, alignment,
            zero, tcache, arena, hook_args);
    }
    return arena_ralloc(tsdn, arena, ptr, oldsize, size, alignment, zero,
        tcache, hook_args);
}
JEMALLOC_ALWAYS_INLINE void *
iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,
    bool zero, hook_ralloc_args_t *hook_args) {
    return iralloct(tsd_tsdn(tsd), ptr, oldsize, size, alignment, zero,
        tcache_get(tsd), NULL, hook_args);
}
JEMALLOC_ALWAYS_INLINE bool
ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
    size_t alignment, bool zero, size_t *newsize) {
    assert(ptr != NULL);
    assert(size != 0);
    witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
        WITNESS_RANK_CORE, 0);
    if (alignment != 0 && ((uintptr_t)ptr & ((uintptr_t)alignment-1))
        != 0) {
        /* Existing object alignment is inadequate. */
        *newsize = oldsize;
        return true;
    }
    return arena_ralloc_no_move(tsdn, ptr, oldsize, size, extra, zero,
        newsize);
}
#endif /* JEMALLOC_INTERNAL_INLINES_C_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_macros.h
New file
@@ -0,0 +1,114 @@
#ifndef JEMALLOC_INTERNAL_MACROS_H
#define JEMALLOC_INTERNAL_MACROS_H
#ifdef JEMALLOC_DEBUG
#  define JEMALLOC_ALWAYS_INLINE static inline
#else
#  define JEMALLOC_ALWAYS_INLINE JEMALLOC_ATTR(always_inline) static inline
#endif
#ifdef _MSC_VER
#  define inline _inline
#endif
#define UNUSED JEMALLOC_ATTR(unused)
#define ZU(z)    ((size_t)z)
#define ZD(z)    ((ssize_t)z)
#define QU(q)    ((uint64_t)q)
#define QD(q)    ((int64_t)q)
#define KZU(z)    ZU(z##ULL)
#define KZD(z)    ZD(z##LL)
#define KQU(q)    QU(q##ULL)
#define KQD(q)    QI(q##LL)
#ifndef __DECONST
#  define    __DECONST(type, var)    ((type)(uintptr_t)(const void *)(var))
#endif
#if !defined(JEMALLOC_HAS_RESTRICT) || defined(__cplusplus)
#  define restrict
#endif
/* Various function pointers are static and immutable except during testing. */
#ifdef JEMALLOC_JET
#  define JET_MUTABLE
#else
#  define JET_MUTABLE const
#endif
#define JEMALLOC_VA_ARGS_HEAD(head, ...) head
#define JEMALLOC_VA_ARGS_TAIL(head, ...) __VA_ARGS__
#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) \
  && defined(JEMALLOC_HAVE_ATTR) && (__GNUC__ >= 7)
#define JEMALLOC_FALLTHROUGH JEMALLOC_ATTR(fallthrough);
#else
#define JEMALLOC_FALLTHROUGH /* falls through */
#endif
/* Diagnostic suppression macros */
#if defined(_MSC_VER) && !defined(__clang__)
#  define JEMALLOC_DIAGNOSTIC_PUSH __pragma(warning(push))
#  define JEMALLOC_DIAGNOSTIC_POP __pragma(warning(pop))
#  define JEMALLOC_DIAGNOSTIC_IGNORE(W) __pragma(warning(disable:W))
#  define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
#  define JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS
#  define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN
#  define JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
/* #pragma GCC diagnostic first appeared in gcc 4.6. */
#elif (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && \
  (__GNUC_MINOR__ > 5)))) || defined(__clang__)
/*
 * The JEMALLOC_PRAGMA__ macro is an implementation detail of the GCC and Clang
 * diagnostic suppression macros and should not be used anywhere else.
 */
#  define JEMALLOC_PRAGMA__(X) _Pragma(#X)
#  define JEMALLOC_DIAGNOSTIC_PUSH JEMALLOC_PRAGMA__(GCC diagnostic push)
#  define JEMALLOC_DIAGNOSTIC_POP JEMALLOC_PRAGMA__(GCC diagnostic pop)
#  define JEMALLOC_DIAGNOSTIC_IGNORE(W) \
     JEMALLOC_PRAGMA__(GCC diagnostic ignored W)
/*
 * The -Wmissing-field-initializers warning is buggy in GCC versions < 5.1 and
 * all clang versions up to version 7 (currently trunk, unreleased).  This macro
 * suppresses the warning for the affected compiler versions only.
 */
#  if ((defined(__GNUC__) && !defined(__clang__)) && (__GNUC__ < 5)) || \
     defined(__clang__)
#    define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS  \
          JEMALLOC_DIAGNOSTIC_IGNORE("-Wmissing-field-initializers")
#  else
#    define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
#  endif
#  define JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS  \
     JEMALLOC_DIAGNOSTIC_IGNORE("-Wtype-limits")
#  define JEMALLOC_DIAGNOSTIC_IGNORE_UNUSED_PARAMETER \
     JEMALLOC_DIAGNOSTIC_IGNORE("-Wunused-parameter")
#  if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ >= 7)
#    define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN \
       JEMALLOC_DIAGNOSTIC_IGNORE("-Walloc-size-larger-than=")
#  else
#    define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN
#  endif
#  define JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS \
  JEMALLOC_DIAGNOSTIC_PUSH \
  JEMALLOC_DIAGNOSTIC_IGNORE_UNUSED_PARAMETER
#else
#  define JEMALLOC_DIAGNOSTIC_PUSH
#  define JEMALLOC_DIAGNOSTIC_POP
#  define JEMALLOC_DIAGNOSTIC_IGNORE(W)
#  define JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
#  define JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS
#  define JEMALLOC_DIAGNOSTIC_IGNORE_ALLOC_SIZE_LARGER_THAN
#  define JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
#endif
/*
 * Disables spurious diagnostics for all headers.  Since these headers are not
 * included by users directly, it does not affect their diagnostic settings.
 */
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
#endif /* JEMALLOC_INTERNAL_MACROS_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_internal_types.h
New file
@@ -0,0 +1,114 @@
#ifndef JEMALLOC_INTERNAL_TYPES_H
#define JEMALLOC_INTERNAL_TYPES_H
#include "jemalloc/internal/quantum.h"
/* Page size index type. */
typedef unsigned pszind_t;
/* Size class index type. */
typedef unsigned szind_t;
/* Processor / core id type. */
typedef int malloc_cpuid_t;
/*
 * Flags bits:
 *
 * a: arena
 * t: tcache
 * 0: unused
 * z: zero
 * n: alignment
 *
 * aaaaaaaa aaaatttt tttttttt 0znnnnnn
 */
#define MALLOCX_ARENA_BITS    12
#define MALLOCX_TCACHE_BITS    12
#define MALLOCX_LG_ALIGN_BITS    6
#define MALLOCX_ARENA_SHIFT    20
#define MALLOCX_TCACHE_SHIFT    8
#define MALLOCX_ARENA_MASK \
    (((1 << MALLOCX_ARENA_BITS) - 1) << MALLOCX_ARENA_SHIFT)
/* NB: Arena index bias decreases the maximum number of arenas by 1. */
#define MALLOCX_ARENA_LIMIT    ((1 << MALLOCX_ARENA_BITS) - 1)
#define MALLOCX_TCACHE_MASK \
    (((1 << MALLOCX_TCACHE_BITS) - 1) << MALLOCX_TCACHE_SHIFT)
#define MALLOCX_TCACHE_MAX    ((1 << MALLOCX_TCACHE_BITS) - 3)
#define MALLOCX_LG_ALIGN_MASK    ((1 << MALLOCX_LG_ALIGN_BITS) - 1)
/* Use MALLOCX_ALIGN_GET() if alignment may not be specified in flags. */
#define MALLOCX_ALIGN_GET_SPECIFIED(flags)                \
    (ZU(1) << (flags & MALLOCX_LG_ALIGN_MASK))
#define MALLOCX_ALIGN_GET(flags)                    \
    (MALLOCX_ALIGN_GET_SPECIFIED(flags) & (SIZE_T_MAX-1))
#define MALLOCX_ZERO_GET(flags)                        \
    ((bool)(flags & MALLOCX_ZERO))
#define MALLOCX_TCACHE_GET(flags)                    \
    (((unsigned)((flags & MALLOCX_TCACHE_MASK) >> MALLOCX_TCACHE_SHIFT)) - 2)
#define MALLOCX_ARENA_GET(flags)                    \
    (((unsigned)(((unsigned)flags) >> MALLOCX_ARENA_SHIFT)) - 1)
/* Smallest size class to support. */
#define TINY_MIN        (1U << LG_TINY_MIN)
#define LONG            ((size_t)(1U << LG_SIZEOF_LONG))
#define LONG_MASK        (LONG - 1)
/* Return the smallest long multiple that is >= a. */
#define LONG_CEILING(a)                            \
    (((a) + LONG_MASK) & ~LONG_MASK)
#define SIZEOF_PTR        (1U << LG_SIZEOF_PTR)
#define PTR_MASK        (SIZEOF_PTR - 1)
/* Return the smallest (void *) multiple that is >= a. */
#define PTR_CEILING(a)                            \
    (((a) + PTR_MASK) & ~PTR_MASK)
/*
 * Maximum size of L1 cache line.  This is used to avoid cache line aliasing.
 * In addition, this controls the spacing of cacheline-spaced size classes.
 *
 * CACHELINE cannot be based on LG_CACHELINE because __declspec(align()) can
 * only handle raw constants.
 */
#define LG_CACHELINE        6
#define CACHELINE        64
#define CACHELINE_MASK        (CACHELINE - 1)
/* Return the smallest cacheline multiple that is >= s. */
#define CACHELINE_CEILING(s)                        \
    (((s) + CACHELINE_MASK) & ~CACHELINE_MASK)
/* Return the nearest aligned address at or below a. */
#define ALIGNMENT_ADDR2BASE(a, alignment)                \
    ((void *)((uintptr_t)(a) & ((~(alignment)) + 1)))
/* Return the offset between a and the nearest aligned address at or below a. */
#define ALIGNMENT_ADDR2OFFSET(a, alignment)                \
    ((size_t)((uintptr_t)(a) & (alignment - 1)))
/* Return the smallest alignment multiple that is >= s. */
#define ALIGNMENT_CEILING(s, alignment)                    \
    (((s) + (alignment - 1)) & ((~(alignment)) + 1))
/* Declare a variable-length array. */
#if __STDC_VERSION__ < 199901L
#  ifdef _MSC_VER
#    include <malloc.h>
#    define alloca _alloca
#  else
#    ifdef JEMALLOC_HAS_ALLOCA_H
#      include <alloca.h>
#    else
#      include <stdlib.h>
#    endif
#  endif
#  define VARIABLE_ARRAY(type, name, count) \
    type *name = alloca(sizeof(type) * (count))
#else
#  define VARIABLE_ARRAY(type, name, count) type name[(count)]
#endif
#endif /* JEMALLOC_INTERNAL_TYPES_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_preamble.h
New file
@@ -0,0 +1,213 @@
#ifndef JEMALLOC_PREAMBLE_H
#define JEMALLOC_PREAMBLE_H
#include "jemalloc_internal_defs.h"
#include "jemalloc/internal/jemalloc_internal_decls.h"
#ifdef JEMALLOC_UTRACE
#include <sys/ktrace.h>
#endif
#define JEMALLOC_NO_DEMANGLE
#ifdef JEMALLOC_JET
#  undef JEMALLOC_IS_MALLOC
#  define JEMALLOC_N(n) jet_##n
#  include "jemalloc/internal/public_namespace.h"
#  define JEMALLOC_NO_RENAME
#  include "../jemalloc.h"
#  undef JEMALLOC_NO_RENAME
#else
#  define JEMALLOC_N(n) je_##n
#  include "../jemalloc.h"
#endif
#if defined(JEMALLOC_OSATOMIC)
#include <libkern/OSAtomic.h>
#endif
#ifdef JEMALLOC_ZONE
#include <mach/mach_error.h>
#include <mach/mach_init.h>
#include <mach/vm_map.h>
#endif
#include "jemalloc/internal/jemalloc_internal_macros.h"
/*
 * Note that the ordering matters here; the hook itself is name-mangled.  We
 * want the inclusion of hooks to happen early, so that we hook as much as
 * possible.
 */
#ifndef JEMALLOC_NO_PRIVATE_NAMESPACE
#  ifndef JEMALLOC_JET
#    include "jemalloc/internal/private_namespace.h"
#  else
#    include "jemalloc/internal/private_namespace_jet.h"
#  endif
#endif
#include "jemalloc/internal/test_hooks.h"
#ifdef JEMALLOC_DEFINE_MADVISE_FREE
#  define JEMALLOC_MADV_FREE 8
#endif
static const bool config_debug =
#ifdef JEMALLOC_DEBUG
    true
#else
    false
#endif
    ;
static const bool have_dss =
#ifdef JEMALLOC_DSS
    true
#else
    false
#endif
    ;
static const bool have_madvise_huge =
#ifdef JEMALLOC_HAVE_MADVISE_HUGE
    true
#else
    false
#endif
    ;
static const bool config_fill =
#ifdef JEMALLOC_FILL
    true
#else
    false
#endif
    ;
static const bool config_lazy_lock =
#ifdef JEMALLOC_LAZY_LOCK
    true
#else
    false
#endif
    ;
static const char * const config_malloc_conf = JEMALLOC_CONFIG_MALLOC_CONF;
static const bool config_prof =
#ifdef JEMALLOC_PROF
    true
#else
    false
#endif
    ;
static const bool config_prof_libgcc =
#ifdef JEMALLOC_PROF_LIBGCC
    true
#else
    false
#endif
    ;
static const bool config_prof_libunwind =
#ifdef JEMALLOC_PROF_LIBUNWIND
    true
#else
    false
#endif
    ;
static const bool maps_coalesce =
#ifdef JEMALLOC_MAPS_COALESCE
    true
#else
    false
#endif
    ;
static const bool config_stats =
#ifdef JEMALLOC_STATS
    true
#else
    false
#endif
    ;
static const bool config_tls =
#ifdef JEMALLOC_TLS
    true
#else
    false
#endif
    ;
static const bool config_utrace =
#ifdef JEMALLOC_UTRACE
    true
#else
    false
#endif
    ;
static const bool config_xmalloc =
#ifdef JEMALLOC_XMALLOC
    true
#else
    false
#endif
    ;
static const bool config_cache_oblivious =
#ifdef JEMALLOC_CACHE_OBLIVIOUS
    true
#else
    false
#endif
    ;
/*
 * Undocumented, for jemalloc development use only at the moment.  See the note
 * in jemalloc/internal/log.h.
 */
static const bool config_log =
#ifdef JEMALLOC_LOG
    true
#else
    false
#endif
    ;
/*
 * Are extra safety checks enabled; things like checking the size of sized
 * deallocations, double-frees, etc.
 */
static const bool config_opt_safety_checks =
#ifdef JEMALLOC_OPT_SAFETY_CHECKS
    true
#elif defined(JEMALLOC_DEBUG)
    /*
     * This lets us only guard safety checks by one flag instead of two; fast
     * checks can guard solely by config_opt_safety_checks and run in debug mode
     * too.
     */
    true
#else
    false
#endif
    ;
#if defined(_WIN32) || defined(JEMALLOC_HAVE_SCHED_GETCPU)
/* Currently percpu_arena depends on sched_getcpu. */
#define JEMALLOC_PERCPU_ARENA
#endif
static const bool have_percpu_arena =
#ifdef JEMALLOC_PERCPU_ARENA
    true
#else
    false
#endif
    ;
/*
 * Undocumented, and not recommended; the application should take full
 * responsibility for tracking provenance.
 */
static const bool force_ivsalloc =
#ifdef JEMALLOC_FORCE_IVSALLOC
    true
#else
    false
#endif
    ;
static const bool have_background_thread =
#ifdef JEMALLOC_BACKGROUND_THREAD
    true
#else
    false
#endif
    ;
#endif /* JEMALLOC_PREAMBLE_H */
jemalloc/x64/include/jemalloc/internal/jemalloc_preamble.h.in
New file
@@ -0,0 +1,213 @@
#ifndef JEMALLOC_PREAMBLE_H
#define JEMALLOC_PREAMBLE_H
#include "jemalloc_internal_defs.h"
#include "jemalloc/internal/jemalloc_internal_decls.h"
#ifdef JEMALLOC_UTRACE
#include <sys/ktrace.h>
#endif
#define JEMALLOC_NO_DEMANGLE
#ifdef JEMALLOC_JET
#  undef JEMALLOC_IS_MALLOC
#  define JEMALLOC_N(n) jet_##n
#  include "jemalloc/internal/public_namespace.h"
#  define JEMALLOC_NO_RENAME
#  include "../jemalloc@install_suffix@.h"
#  undef JEMALLOC_NO_RENAME
#else
#  define JEMALLOC_N(n) @private_namespace@##n
#  include "../jemalloc@install_suffix@.h"
#endif
#if defined(JEMALLOC_OSATOMIC)
#include <libkern/OSAtomic.h>
#endif
#ifdef JEMALLOC_ZONE
#include <mach/mach_error.h>
#include <mach/mach_init.h>
#include <mach/vm_map.h>
#endif
#include "jemalloc/internal/jemalloc_internal_macros.h"
/*
 * Note that the ordering matters here; the hook itself is name-mangled.  We
 * want the inclusion of hooks to happen early, so that we hook as much as
 * possible.
 */
#ifndef JEMALLOC_NO_PRIVATE_NAMESPACE
#  ifndef JEMALLOC_JET
#    include "jemalloc/internal/private_namespace.h"
#  else
#    include "jemalloc/internal/private_namespace_jet.h"
#  endif
#endif
#include "jemalloc/internal/test_hooks.h"
#ifdef JEMALLOC_DEFINE_MADVISE_FREE
#  define JEMALLOC_MADV_FREE 8
#endif
static const bool config_debug =
#ifdef JEMALLOC_DEBUG
    true
#else
    false
#endif
    ;
static const bool have_dss =
#ifdef JEMALLOC_DSS
    true
#else
    false
#endif
    ;
static const bool have_madvise_huge =
#ifdef JEMALLOC_HAVE_MADVISE_HUGE
    true
#else
    false
#endif
    ;
static const bool config_fill =
#ifdef JEMALLOC_FILL
    true
#else
    false
#endif
    ;
static const bool config_lazy_lock =
#ifdef JEMALLOC_LAZY_LOCK
    true
#else
    false
#endif
    ;
static const char * const config_malloc_conf = JEMALLOC_CONFIG_MALLOC_CONF;
static const bool config_prof =
#ifdef JEMALLOC_PROF
    true
#else
    false
#endif
    ;
static const bool config_prof_libgcc =
#ifdef JEMALLOC_PROF_LIBGCC
    true
#else
    false
#endif
    ;
static const bool config_prof_libunwind =
#ifdef JEMALLOC_PROF_LIBUNWIND
    true
#else
    false
#endif
    ;
static const bool maps_coalesce =
#ifdef JEMALLOC_MAPS_COALESCE
    true
#else
    false
#endif
    ;
static const bool config_stats =
#ifdef JEMALLOC_STATS
    true
#else
    false
#endif
    ;
static const bool config_tls =
#ifdef JEMALLOC_TLS
    true
#else
    false
#endif
    ;
static const bool config_utrace =
#ifdef JEMALLOC_UTRACE
    true
#else
    false
#endif
    ;
static const bool config_xmalloc =
#ifdef JEMALLOC_XMALLOC
    true
#else
    false
#endif
    ;
static const bool config_cache_oblivious =
#ifdef JEMALLOC_CACHE_OBLIVIOUS
    true
#else
    false
#endif
    ;
/*
 * Undocumented, for jemalloc development use only at the moment.  See the note
 * in jemalloc/internal/log.h.
 */
static const bool config_log =
#ifdef JEMALLOC_LOG
    true
#else
    false
#endif
    ;
/*
 * Are extra safety checks enabled; things like checking the size of sized
 * deallocations, double-frees, etc.
 */
static const bool config_opt_safety_checks =
#ifdef JEMALLOC_OPT_SAFETY_CHECKS
    true
#elif defined(JEMALLOC_DEBUG)
    /*
     * This lets us only guard safety checks by one flag instead of two; fast
     * checks can guard solely by config_opt_safety_checks and run in debug mode
     * too.
     */
    true
#else
    false
#endif
    ;
#if defined(_WIN32) || defined(JEMALLOC_HAVE_SCHED_GETCPU)
/* Currently percpu_arena depends on sched_getcpu. */
#define JEMALLOC_PERCPU_ARENA
#endif
static const bool have_percpu_arena =
#ifdef JEMALLOC_PERCPU_ARENA
    true
#else
    false
#endif
    ;
/*
 * Undocumented, and not recommended; the application should take full
 * responsibility for tracking provenance.
 */
static const bool force_ivsalloc =
#ifdef JEMALLOC_FORCE_IVSALLOC
    true
#else
    false
#endif
    ;
static const bool have_background_thread =
#ifdef JEMALLOC_BACKGROUND_THREAD
    true
#else
    false
#endif
    ;
#endif /* JEMALLOC_PREAMBLE_H */
jemalloc/x64/include/jemalloc/internal/large_externs.h
New file
@@ -0,0 +1,32 @@
#ifndef JEMALLOC_INTERNAL_LARGE_EXTERNS_H
#define JEMALLOC_INTERNAL_LARGE_EXTERNS_H
#include "jemalloc/internal/hook.h"
void *large_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero);
void *large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
    bool zero);
bool large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,
    size_t usize_max, bool zero);
void *large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize,
    size_t alignment, bool zero, tcache_t *tcache,
    hook_ralloc_args_t *hook_args);
typedef void (large_dalloc_junk_t)(void *, size_t);
extern large_dalloc_junk_t *JET_MUTABLE large_dalloc_junk;
typedef void (large_dalloc_maybe_junk_t)(void *, size_t);
extern large_dalloc_maybe_junk_t *JET_MUTABLE large_dalloc_maybe_junk;
void large_dalloc_prep_junked_locked(tsdn_t *tsdn, extent_t *extent);
void large_dalloc_finish(tsdn_t *tsdn, extent_t *extent);
void large_dalloc(tsdn_t *tsdn, extent_t *extent);
size_t large_salloc(tsdn_t *tsdn, const extent_t *extent);
prof_tctx_t *large_prof_tctx_get(tsdn_t *tsdn, const extent_t *extent);
void large_prof_tctx_set(tsdn_t *tsdn, extent_t *extent, prof_tctx_t *tctx);
void large_prof_tctx_reset(tsdn_t *tsdn, extent_t *extent);
nstime_t large_prof_alloc_time_get(const extent_t *extent);
void large_prof_alloc_time_set(extent_t *extent, nstime_t time);
#endif /* JEMALLOC_INTERNAL_LARGE_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/log.h
New file
@@ -0,0 +1,115 @@
#ifndef JEMALLOC_INTERNAL_LOG_H
#define JEMALLOC_INTERNAL_LOG_H
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/malloc_io.h"
#include "jemalloc/internal/mutex.h"
#ifdef JEMALLOC_LOG
#  define JEMALLOC_LOG_VAR_BUFSIZE 1000
#else
#  define JEMALLOC_LOG_VAR_BUFSIZE 1
#endif
#define JEMALLOC_LOG_BUFSIZE 4096
/*
 * The log malloc_conf option is a '|'-delimited list of log_var name segments
 * which should be logged.  The names are themselves hierarchical, with '.' as
 * the delimiter (a "segment" is just a prefix in the log namespace).  So, if
 * you have:
 *
 * log("arena", "log msg for arena"); // 1
 * log("arena.a", "log msg for arena.a"); // 2
 * log("arena.b", "log msg for arena.b"); // 3
 * log("arena.a.a", "log msg for arena.a.a"); // 4
 * log("extent.a", "log msg for extent.a"); // 5
 * log("extent.b", "log msg for extent.b"); // 6
 *
 * And your malloc_conf option is "log=arena.a|extent", then lines 2, 4, 5, and
 * 6 will print at runtime.  You can enable logging from all log vars by
 * writing "log=.".
 *
 * None of this should be regarded as a stable API for right now.  It's intended
 * as a debugging interface, to let us keep around some of our printf-debugging
 * statements.
 */
extern char log_var_names[JEMALLOC_LOG_VAR_BUFSIZE];
extern atomic_b_t log_init_done;
typedef struct log_var_s log_var_t;
struct log_var_s {
    /*
     * Lowest bit is "inited", second lowest is "enabled".  Putting them in
     * a single word lets us avoid any fences on weak architectures.
     */
    atomic_u_t state;
    const char *name;
};
#define LOG_NOT_INITIALIZED 0U
#define LOG_INITIALIZED_NOT_ENABLED 1U
#define LOG_ENABLED 2U
#define LOG_VAR_INIT(name_str) {ATOMIC_INIT(LOG_NOT_INITIALIZED), name_str}
/*
 * Returns the value we should assume for state (which is not necessarily
 * accurate; if logging is done before logging has finished initializing, then
 * we default to doing the safe thing by logging everything).
 */
unsigned log_var_update_state(log_var_t *log_var);
/* We factor out the metadata management to allow us to test more easily. */
#define log_do_begin(log_var)                        \
if (config_log) {                            \
    unsigned log_state = atomic_load_u(&(log_var).state,        \
        ATOMIC_RELAXED);                        \
    if (unlikely(log_state == LOG_NOT_INITIALIZED)) {        \
        log_state = log_var_update_state(&(log_var));        \
        assert(log_state != LOG_NOT_INITIALIZED);        \
    }                                \
    if (log_state == LOG_ENABLED) {                    \
        {
            /* User code executes here. */
#define log_do_end(log_var)                        \
        }                            \
    }                                \
}
/*
 * MSVC has some preprocessor bugs in its expansion of __VA_ARGS__ during
 * preprocessing.  To work around this, we take all potential extra arguments in
 * a var-args functions.  Since a varargs macro needs at least one argument in
 * the "...", we accept the format string there, and require that the first
 * argument in this "..." is a const char *.
 */
static inline void
log_impl_varargs(const char *name, ...) {
    char buf[JEMALLOC_LOG_BUFSIZE];
    va_list ap;
    va_start(ap, name);
    const char *format = va_arg(ap, const char *);
    size_t dst_offset = 0;
    dst_offset += malloc_snprintf(buf, JEMALLOC_LOG_BUFSIZE, "%s: ", name);
    dst_offset += malloc_vsnprintf(buf + dst_offset,
        JEMALLOC_LOG_BUFSIZE - dst_offset, format, ap);
    dst_offset += malloc_snprintf(buf + dst_offset,
        JEMALLOC_LOG_BUFSIZE - dst_offset, "\n");
    va_end(ap);
    malloc_write(buf);
}
/* Call as log("log.var.str", "format_string %d", arg_for_format_string); */
#define LOG(log_var_str, ...)                        \
do {                                    \
    static log_var_t log_var = LOG_VAR_INIT(log_var_str);        \
    log_do_begin(log_var)                        \
        log_impl_varargs((log_var).name, __VA_ARGS__);        \
    log_do_end(log_var)                        \
} while (0)
#endif /* JEMALLOC_INTERNAL_LOG_H */
jemalloc/x64/include/jemalloc/internal/malloc_io.h
New file
@@ -0,0 +1,102 @@
#ifndef JEMALLOC_INTERNAL_MALLOC_IO_H
#define JEMALLOC_INTERNAL_MALLOC_IO_H
#ifdef _WIN32
#  ifdef _WIN64
#    define FMT64_PREFIX "ll"
#    define FMTPTR_PREFIX "ll"
#  else
#    define FMT64_PREFIX "ll"
#    define FMTPTR_PREFIX ""
#  endif
#  define FMTd32 "d"
#  define FMTu32 "u"
#  define FMTx32 "x"
#  define FMTd64 FMT64_PREFIX "d"
#  define FMTu64 FMT64_PREFIX "u"
#  define FMTx64 FMT64_PREFIX "x"
#  define FMTdPTR FMTPTR_PREFIX "d"
#  define FMTuPTR FMTPTR_PREFIX "u"
#  define FMTxPTR FMTPTR_PREFIX "x"
#else
#  include <inttypes.h>
#  define FMTd32 PRId32
#  define FMTu32 PRIu32
#  define FMTx32 PRIx32
#  define FMTd64 PRId64
#  define FMTu64 PRIu64
#  define FMTx64 PRIx64
#  define FMTdPTR PRIdPTR
#  define FMTuPTR PRIuPTR
#  define FMTxPTR PRIxPTR
#endif
/* Size of stack-allocated buffer passed to buferror(). */
#define BUFERROR_BUF        64
/*
 * Size of stack-allocated buffer used by malloc_{,v,vc}printf().  This must be
 * large enough for all possible uses within jemalloc.
 */
#define MALLOC_PRINTF_BUFSIZE    4096
int buferror(int err, char *buf, size_t buflen);
uintmax_t malloc_strtoumax(const char *restrict nptr, char **restrict endptr,
    int base);
void malloc_write(const char *s);
/*
 * malloc_vsnprintf() supports a subset of snprintf(3) that avoids floating
 * point math.
 */
size_t malloc_vsnprintf(char *str, size_t size, const char *format,
    va_list ap);
size_t malloc_snprintf(char *str, size_t size, const char *format, ...)
    JEMALLOC_FORMAT_PRINTF(3, 4);
/*
 * The caller can set write_cb to null to choose to print with the
 * je_malloc_message hook.
 */
void malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
    const char *format, va_list ap);
void malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
    const char *format, ...) JEMALLOC_FORMAT_PRINTF(3, 4);
void malloc_printf(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);
static inline ssize_t
malloc_write_fd(int fd, const void *buf, size_t count) {
#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_write)
    /*
     * Use syscall(2) rather than write(2) when possible in order to avoid
     * the possibility of memory allocation within libc.  This is necessary
     * on FreeBSD; most operating systems do not have this problem though.
     *
     * syscall() returns long or int, depending on platform, so capture the
     * result in the widest plausible type to avoid compiler warnings.
     */
    long result = syscall(SYS_write, fd, buf, count);
#else
    ssize_t result = (ssize_t)write(fd, buf,
#ifdef _WIN32
        (unsigned int)
#endif
        count);
#endif
    return (ssize_t)result;
}
static inline ssize_t
malloc_read_fd(int fd, void *buf, size_t count) {
#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_read)
    long result = syscall(SYS_read, fd, buf, count);
#else
    ssize_t result = read(fd, buf,
#ifdef _WIN32
        (unsigned int)
#endif
        count);
#endif
    return (ssize_t)result;
}
#endif /* JEMALLOC_INTERNAL_MALLOC_IO_H */
jemalloc/x64/include/jemalloc/internal/mutex.h
New file
@@ -0,0 +1,288 @@
#ifndef JEMALLOC_INTERNAL_MUTEX_H
#define JEMALLOC_INTERNAL_MUTEX_H
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/mutex_prof.h"
#include "jemalloc/internal/tsd.h"
#include "jemalloc/internal/witness.h"
typedef enum {
    /* Can only acquire one mutex of a given witness rank at a time. */
    malloc_mutex_rank_exclusive,
    /*
     * Can acquire multiple mutexes of the same witness rank, but in
     * address-ascending order only.
     */
    malloc_mutex_address_ordered
} malloc_mutex_lock_order_t;
typedef struct malloc_mutex_s malloc_mutex_t;
struct malloc_mutex_s {
    union {
        struct {
            /*
             * prof_data is defined first to reduce cacheline
             * bouncing: the data is not touched by the mutex holder
             * during unlocking, while might be modified by
             * contenders.  Having it before the mutex itself could
             * avoid prefetching a modified cacheline (for the
             * unlocking thread).
             */
            mutex_prof_data_t    prof_data;
#ifdef _WIN32
#  if _WIN32_WINNT >= 0x0600
            SRWLOCK             lock;
#  else
            CRITICAL_SECTION    lock;
#  endif
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
            os_unfair_lock        lock;
#elif (defined(JEMALLOC_MUTEX_INIT_CB))
            pthread_mutex_t        lock;
            malloc_mutex_t        *postponed_next;
#else
            pthread_mutex_t        lock;
#endif
            /*
             * Hint flag to avoid exclusive cache line contention
             * during spin waiting
             */
            atomic_b_t        locked;
        };
        /*
         * We only touch witness when configured w/ debug.  However we
         * keep the field in a union when !debug so that we don't have
         * to pollute the code base with #ifdefs, while avoid paying the
         * memory cost.
         */
#if !defined(JEMALLOC_DEBUG)
        witness_t            witness;
        malloc_mutex_lock_order_t    lock_order;
#endif
    };
#if defined(JEMALLOC_DEBUG)
    witness_t            witness;
    malloc_mutex_lock_order_t    lock_order;
#endif
};
/*
 * Based on benchmark results, a fixed spin with this amount of retries works
 * well for our critical sections.
 */
#define MALLOC_MUTEX_MAX_SPIN 250
#ifdef _WIN32
#  if _WIN32_WINNT >= 0x0600
#    define MALLOC_MUTEX_LOCK(m)    AcquireSRWLockExclusive(&(m)->lock)
#    define MALLOC_MUTEX_UNLOCK(m)  ReleaseSRWLockExclusive(&(m)->lock)
#    define MALLOC_MUTEX_TRYLOCK(m) (!TryAcquireSRWLockExclusive(&(m)->lock))
#  else
#    define MALLOC_MUTEX_LOCK(m)    EnterCriticalSection(&(m)->lock)
#    define MALLOC_MUTEX_UNLOCK(m)  LeaveCriticalSection(&(m)->lock)
#    define MALLOC_MUTEX_TRYLOCK(m) (!TryEnterCriticalSection(&(m)->lock))
#  endif
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
#    define MALLOC_MUTEX_LOCK(m)    os_unfair_lock_lock(&(m)->lock)
#    define MALLOC_MUTEX_UNLOCK(m)  os_unfair_lock_unlock(&(m)->lock)
#    define MALLOC_MUTEX_TRYLOCK(m) (!os_unfair_lock_trylock(&(m)->lock))
#else
#    define MALLOC_MUTEX_LOCK(m)    pthread_mutex_lock(&(m)->lock)
#    define MALLOC_MUTEX_UNLOCK(m)  pthread_mutex_unlock(&(m)->lock)
#    define MALLOC_MUTEX_TRYLOCK(m) (pthread_mutex_trylock(&(m)->lock) != 0)
#endif
#define LOCK_PROF_DATA_INITIALIZER                    \
    {NSTIME_ZERO_INITIALIZER, NSTIME_ZERO_INITIALIZER, 0, 0, 0,        \
        ATOMIC_INIT(0), 0, NULL, 0}
#ifdef _WIN32
#  define MALLOC_MUTEX_INITIALIZER
#elif (defined(JEMALLOC_OS_UNFAIR_LOCK))
#  if defined(JEMALLOC_DEBUG)
#    define MALLOC_MUTEX_INITIALIZER                    \
  {{{LOCK_PROF_DATA_INITIALIZER, OS_UNFAIR_LOCK_INIT, ATOMIC_INIT(false)}}, \
         WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT), 0}
#  else
#    define MALLOC_MUTEX_INITIALIZER                      \
  {{{LOCK_PROF_DATA_INITIALIZER, OS_UNFAIR_LOCK_INIT, ATOMIC_INIT(false)}},  \
      WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
#  endif
#elif (defined(JEMALLOC_MUTEX_INIT_CB))
#  if (defined(JEMALLOC_DEBUG))
#     define MALLOC_MUTEX_INITIALIZER                    \
      {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, NULL, ATOMIC_INIT(false)}},    \
           WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT), 0}
#  else
#     define MALLOC_MUTEX_INITIALIZER                    \
      {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, NULL, ATOMIC_INIT(false)}},    \
           WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
#  endif
#else
#    define MALLOC_MUTEX_TYPE PTHREAD_MUTEX_DEFAULT
#  if defined(JEMALLOC_DEBUG)
#    define MALLOC_MUTEX_INITIALIZER                    \
     {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, ATOMIC_INIT(false)}}, \
           WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT), 0}
#  else
#    define MALLOC_MUTEX_INITIALIZER                          \
     {{{LOCK_PROF_DATA_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, ATOMIC_INIT(false)}},    \
      WITNESS_INITIALIZER("mutex", WITNESS_RANK_OMIT)}
#  endif
#endif
#ifdef JEMALLOC_LAZY_LOCK
extern bool isthreaded;
#else
#  undef isthreaded /* Undo private_namespace.h definition. */
#  define isthreaded true
#endif
bool malloc_mutex_init(malloc_mutex_t *mutex, const char *name,
    witness_rank_t rank, malloc_mutex_lock_order_t lock_order);
void malloc_mutex_prefork(tsdn_t *tsdn, malloc_mutex_t *mutex);
void malloc_mutex_postfork_parent(tsdn_t *tsdn, malloc_mutex_t *mutex);
void malloc_mutex_postfork_child(tsdn_t *tsdn, malloc_mutex_t *mutex);
bool malloc_mutex_boot(void);
void malloc_mutex_prof_data_reset(tsdn_t *tsdn, malloc_mutex_t *mutex);
void malloc_mutex_lock_slow(malloc_mutex_t *mutex);
static inline void
malloc_mutex_lock_final(malloc_mutex_t *mutex) {
    MALLOC_MUTEX_LOCK(mutex);
    atomic_store_b(&mutex->locked, true, ATOMIC_RELAXED);
}
static inline bool
malloc_mutex_trylock_final(malloc_mutex_t *mutex) {
    return MALLOC_MUTEX_TRYLOCK(mutex);
}
static inline void
mutex_owner_stats_update(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    if (config_stats) {
        mutex_prof_data_t *data = &mutex->prof_data;
        data->n_lock_ops++;
        if (data->prev_owner != tsdn) {
            data->prev_owner = tsdn;
            data->n_owner_switches++;
        }
    }
}
/* Trylock: return false if the lock is successfully acquired. */
static inline bool
malloc_mutex_trylock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    witness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    if (isthreaded) {
        if (malloc_mutex_trylock_final(mutex)) {
            atomic_store_b(&mutex->locked, true, ATOMIC_RELAXED);
            return true;
        }
        mutex_owner_stats_update(tsdn, mutex);
    }
    witness_lock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    return false;
}
/* Aggregate lock prof data. */
static inline void
malloc_mutex_prof_merge(mutex_prof_data_t *sum, mutex_prof_data_t *data) {
    nstime_add(&sum->tot_wait_time, &data->tot_wait_time);
    if (nstime_compare(&sum->max_wait_time, &data->max_wait_time) < 0) {
        nstime_copy(&sum->max_wait_time, &data->max_wait_time);
    }
    sum->n_wait_times += data->n_wait_times;
    sum->n_spin_acquired += data->n_spin_acquired;
    if (sum->max_n_thds < data->max_n_thds) {
        sum->max_n_thds = data->max_n_thds;
    }
    uint32_t cur_n_waiting_thds = atomic_load_u32(&sum->n_waiting_thds,
        ATOMIC_RELAXED);
    uint32_t new_n_waiting_thds = cur_n_waiting_thds + atomic_load_u32(
        &data->n_waiting_thds, ATOMIC_RELAXED);
    atomic_store_u32(&sum->n_waiting_thds, new_n_waiting_thds,
        ATOMIC_RELAXED);
    sum->n_owner_switches += data->n_owner_switches;
    sum->n_lock_ops += data->n_lock_ops;
}
static inline void
malloc_mutex_lock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    witness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    if (isthreaded) {
        if (malloc_mutex_trylock_final(mutex)) {
            malloc_mutex_lock_slow(mutex);
            atomic_store_b(&mutex->locked, true, ATOMIC_RELAXED);
        }
        mutex_owner_stats_update(tsdn, mutex);
    }
    witness_lock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
}
static inline void
malloc_mutex_unlock(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    atomic_store_b(&mutex->locked, false, ATOMIC_RELAXED);
    witness_unlock(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
    if (isthreaded) {
        MALLOC_MUTEX_UNLOCK(mutex);
    }
}
static inline void
malloc_mutex_assert_owner(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    witness_assert_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
}
static inline void
malloc_mutex_assert_not_owner(tsdn_t *tsdn, malloc_mutex_t *mutex) {
    witness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
}
/* Copy the prof data from mutex for processing. */
static inline void
malloc_mutex_prof_read(tsdn_t *tsdn, mutex_prof_data_t *data,
    malloc_mutex_t *mutex) {
    mutex_prof_data_t *source = &mutex->prof_data;
    /* Can only read holding the mutex. */
    malloc_mutex_assert_owner(tsdn, mutex);
    /*
     * Not *really* allowed (we shouldn't be doing non-atomic loads of
     * atomic data), but the mutex protection makes this safe, and writing
     * a member-for-member copy is tedious for this situation.
     */
    *data = *source;
    /* n_wait_thds is not reported (modified w/o locking). */
    atomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);
}
static inline void
malloc_mutex_prof_accum(tsdn_t *tsdn, mutex_prof_data_t *data,
    malloc_mutex_t *mutex) {
    mutex_prof_data_t *source = &mutex->prof_data;
    /* Can only read holding the mutex. */
    malloc_mutex_assert_owner(tsdn, mutex);
    nstime_add(&data->tot_wait_time, &source->tot_wait_time);
    if (nstime_compare(&source->max_wait_time, &data->max_wait_time) > 0) {
        nstime_copy(&data->max_wait_time, &source->max_wait_time);
    }
    data->n_wait_times += source->n_wait_times;
    data->n_spin_acquired += source->n_spin_acquired;
    if (data->max_n_thds < source->max_n_thds) {
        data->max_n_thds = source->max_n_thds;
    }
    /* n_wait_thds is not reported. */
    atomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);
    data->n_owner_switches += source->n_owner_switches;
    data->n_lock_ops += source->n_lock_ops;
}
#endif /* JEMALLOC_INTERNAL_MUTEX_H */
jemalloc/x64/include/jemalloc/internal/mutex_pool.h
New file
@@ -0,0 +1,94 @@
#ifndef JEMALLOC_INTERNAL_MUTEX_POOL_H
#define JEMALLOC_INTERNAL_MUTEX_POOL_H
#include "jemalloc/internal/hash.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/witness.h"
/* We do mod reductions by this value, so it should be kept a power of 2. */
#define MUTEX_POOL_SIZE 256
typedef struct mutex_pool_s mutex_pool_t;
struct mutex_pool_s {
    malloc_mutex_t mutexes[MUTEX_POOL_SIZE];
};
bool mutex_pool_init(mutex_pool_t *pool, const char *name, witness_rank_t rank);
/* Internal helper - not meant to be called outside this module. */
static inline malloc_mutex_t *
mutex_pool_mutex(mutex_pool_t *pool, uintptr_t key) {
    size_t hash_result[2];
    hash(&key, sizeof(key), 0xd50dcc1b, hash_result);
    return &pool->mutexes[hash_result[0] % MUTEX_POOL_SIZE];
}
static inline void
mutex_pool_assert_not_held(tsdn_t *tsdn, mutex_pool_t *pool) {
    for (int i = 0; i < MUTEX_POOL_SIZE; i++) {
        malloc_mutex_assert_not_owner(tsdn, &pool->mutexes[i]);
    }
}
/*
 * Note that a mutex pool doesn't work exactly the way an embdedded mutex would.
 * You're not allowed to acquire mutexes in the pool one at a time.  You have to
 * acquire all the mutexes you'll need in a single function call, and then
 * release them all in a single function call.
 */
static inline void
mutex_pool_lock(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {
    mutex_pool_assert_not_held(tsdn, pool);
    malloc_mutex_t *mutex = mutex_pool_mutex(pool, key);
    malloc_mutex_lock(tsdn, mutex);
}
static inline void
mutex_pool_unlock(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {
    malloc_mutex_t *mutex = mutex_pool_mutex(pool, key);
    malloc_mutex_unlock(tsdn, mutex);
    mutex_pool_assert_not_held(tsdn, pool);
}
static inline void
mutex_pool_lock2(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key1,
    uintptr_t key2) {
    mutex_pool_assert_not_held(tsdn, pool);
    malloc_mutex_t *mutex1 = mutex_pool_mutex(pool, key1);
    malloc_mutex_t *mutex2 = mutex_pool_mutex(pool, key2);
    if ((uintptr_t)mutex1 < (uintptr_t)mutex2) {
        malloc_mutex_lock(tsdn, mutex1);
        malloc_mutex_lock(tsdn, mutex2);
    } else if ((uintptr_t)mutex1 == (uintptr_t)mutex2) {
        malloc_mutex_lock(tsdn, mutex1);
    } else {
        malloc_mutex_lock(tsdn, mutex2);
        malloc_mutex_lock(tsdn, mutex1);
    }
}
static inline void
mutex_pool_unlock2(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key1,
    uintptr_t key2) {
    malloc_mutex_t *mutex1 = mutex_pool_mutex(pool, key1);
    malloc_mutex_t *mutex2 = mutex_pool_mutex(pool, key2);
    if (mutex1 == mutex2) {
        malloc_mutex_unlock(tsdn, mutex1);
    } else {
        malloc_mutex_unlock(tsdn, mutex1);
        malloc_mutex_unlock(tsdn, mutex2);
    }
    mutex_pool_assert_not_held(tsdn, pool);
}
static inline void
mutex_pool_assert_owner(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {
    malloc_mutex_assert_owner(tsdn, mutex_pool_mutex(pool, key));
}
#endif /* JEMALLOC_INTERNAL_MUTEX_POOL_H */
jemalloc/x64/include/jemalloc/internal/mutex_prof.h
New file
@@ -0,0 +1,108 @@
#ifndef JEMALLOC_INTERNAL_MUTEX_PROF_H
#define JEMALLOC_INTERNAL_MUTEX_PROF_H
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/nstime.h"
#include "jemalloc/internal/tsd_types.h"
#define MUTEX_PROF_GLOBAL_MUTEXES                    \
    OP(background_thread)                        \
    OP(ctl)                                \
    OP(prof)
typedef enum {
#define OP(mtx) global_prof_mutex_##mtx,
    MUTEX_PROF_GLOBAL_MUTEXES
#undef OP
    mutex_prof_num_global_mutexes
} mutex_prof_global_ind_t;
#define MUTEX_PROF_ARENA_MUTEXES                    \
    OP(large)                                \
    OP(extent_avail)                            \
    OP(extents_dirty)                            \
    OP(extents_muzzy)                            \
    OP(extents_retained)                        \
    OP(decay_dirty)                            \
    OP(decay_muzzy)                            \
    OP(base)                                \
    OP(tcache_list)
typedef enum {
#define OP(mtx) arena_prof_mutex_##mtx,
    MUTEX_PROF_ARENA_MUTEXES
#undef OP
    mutex_prof_num_arena_mutexes
} mutex_prof_arena_ind_t;
/*
 * The forth parameter is a boolean value that is true for derived rate counters
 * and false for real ones.
 */
#define MUTEX_PROF_UINT64_COUNTERS                    \
    OP(num_ops, uint64_t, "n_lock_ops", false, num_ops)                    \
    OP(num_ops_ps, uint64_t, "(#/sec)", true, num_ops)                \
    OP(num_wait, uint64_t, "n_waiting", false, num_wait)                \
    OP(num_wait_ps, uint64_t, "(#/sec)", true, num_wait)                \
    OP(num_spin_acq, uint64_t, "n_spin_acq", false, num_spin_acq)            \
    OP(num_spin_acq_ps, uint64_t, "(#/sec)", true, num_spin_acq)            \
    OP(num_owner_switch, uint64_t, "n_owner_switch", false, num_owner_switch)        \
    OP(num_owner_switch_ps, uint64_t, "(#/sec)", true, num_owner_switch)    \
    OP(total_wait_time, uint64_t, "total_wait_ns", false, total_wait_time)        \
    OP(total_wait_time_ps, uint64_t, "(#/sec)", true, total_wait_time)        \
    OP(max_wait_time, uint64_t, "max_wait_ns", false, max_wait_time)
#define MUTEX_PROF_UINT32_COUNTERS                    \
    OP(max_num_thds, uint32_t, "max_n_thds", false, max_num_thds)
#define MUTEX_PROF_COUNTERS                        \
        MUTEX_PROF_UINT64_COUNTERS                \
        MUTEX_PROF_UINT32_COUNTERS
#define OP(counter, type, human, derived, base_counter) mutex_counter_##counter,
#define COUNTER_ENUM(counter_list, t)                    \
        typedef enum {                        \
            counter_list                    \
            mutex_prof_num_##t##_counters            \
        } mutex_prof_##t##_counter_ind_t;
COUNTER_ENUM(MUTEX_PROF_UINT64_COUNTERS, uint64_t)
COUNTER_ENUM(MUTEX_PROF_UINT32_COUNTERS, uint32_t)
#undef COUNTER_ENUM
#undef OP
typedef struct {
    /*
     * Counters touched on the slow path, i.e. when there is lock
     * contention.  We update them once we have the lock.
     */
    /* Total time (in nano seconds) spent waiting on this mutex. */
    nstime_t        tot_wait_time;
    /* Max time (in nano seconds) spent on a single lock operation. */
    nstime_t        max_wait_time;
    /* # of times have to wait for this mutex (after spinning). */
    uint64_t        n_wait_times;
    /* # of times acquired the mutex through local spinning. */
    uint64_t        n_spin_acquired;
    /* Max # of threads waiting for the mutex at the same time. */
    uint32_t        max_n_thds;
    /* Current # of threads waiting on the lock.  Atomic synced. */
    atomic_u32_t        n_waiting_thds;
    /*
     * Data touched on the fast path.  These are modified right after we
     * grab the lock, so it's placed closest to the end (i.e. right before
     * the lock) so that we have a higher chance of them being on the same
     * cacheline.
     */
    /* # of times the mutex holder is different than the previous one. */
    uint64_t        n_owner_switches;
    /* Previous mutex holder, to facilitate n_owner_switches. */
    tsdn_t            *prev_owner;
    /* # of lock() operations in total. */
    uint64_t        n_lock_ops;
} mutex_prof_data_t;
#endif /* JEMALLOC_INTERNAL_MUTEX_PROF_H */
jemalloc/x64/include/jemalloc/internal/nstime.h
New file
@@ -0,0 +1,34 @@
#ifndef JEMALLOC_INTERNAL_NSTIME_H
#define JEMALLOC_INTERNAL_NSTIME_H
/* Maximum supported number of seconds (~584 years). */
#define NSTIME_SEC_MAX KQU(18446744072)
#define NSTIME_ZERO_INITIALIZER {0}
typedef struct {
    uint64_t ns;
} nstime_t;
void nstime_init(nstime_t *time, uint64_t ns);
void nstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec);
uint64_t nstime_ns(const nstime_t *time);
uint64_t nstime_sec(const nstime_t *time);
uint64_t nstime_msec(const nstime_t *time);
uint64_t nstime_nsec(const nstime_t *time);
void nstime_copy(nstime_t *time, const nstime_t *source);
int nstime_compare(const nstime_t *a, const nstime_t *b);
void nstime_add(nstime_t *time, const nstime_t *addend);
void nstime_iadd(nstime_t *time, uint64_t addend);
void nstime_subtract(nstime_t *time, const nstime_t *subtrahend);
void nstime_isubtract(nstime_t *time, uint64_t subtrahend);
void nstime_imultiply(nstime_t *time, uint64_t multiplier);
void nstime_idivide(nstime_t *time, uint64_t divisor);
uint64_t nstime_divide(const nstime_t *time, const nstime_t *divisor);
typedef bool (nstime_monotonic_t)(void);
extern nstime_monotonic_t *JET_MUTABLE nstime_monotonic;
typedef bool (nstime_update_t)(nstime_t *);
extern nstime_update_t *JET_MUTABLE nstime_update;
#endif /* JEMALLOC_INTERNAL_NSTIME_H */
jemalloc/x64/include/jemalloc/internal/pages.h
New file
@@ -0,0 +1,88 @@
#ifndef JEMALLOC_INTERNAL_PAGES_EXTERNS_H
#define JEMALLOC_INTERNAL_PAGES_EXTERNS_H
/* Page size.  LG_PAGE is determined by the configure script. */
#ifdef PAGE_MASK
#  undef PAGE_MASK
#endif
#define PAGE        ((size_t)(1U << LG_PAGE))
#define PAGE_MASK    ((size_t)(PAGE - 1))
/* Return the page base address for the page containing address a. */
#define PAGE_ADDR2BASE(a)                        \
    ((void *)((uintptr_t)(a) & ~PAGE_MASK))
/* Return the smallest pagesize multiple that is >= s. */
#define PAGE_CEILING(s)                            \
    (((s) + PAGE_MASK) & ~PAGE_MASK)
/* Huge page size.  LG_HUGEPAGE is determined by the configure script. */
#define HUGEPAGE    ((size_t)(1U << LG_HUGEPAGE))
#define HUGEPAGE_MASK    ((size_t)(HUGEPAGE - 1))
/* Return the huge page base address for the huge page containing address a. */
#define HUGEPAGE_ADDR2BASE(a)                        \
    ((void *)((uintptr_t)(a) & ~HUGEPAGE_MASK))
/* Return the smallest pagesize multiple that is >= s. */
#define HUGEPAGE_CEILING(s)                        \
    (((s) + HUGEPAGE_MASK) & ~HUGEPAGE_MASK)
/* PAGES_CAN_PURGE_LAZY is defined if lazy purging is supported. */
#if defined(_WIN32) || defined(JEMALLOC_PURGE_MADVISE_FREE)
#  define PAGES_CAN_PURGE_LAZY
#endif
/*
 * PAGES_CAN_PURGE_FORCED is defined if forced purging is supported.
 *
 * The only supported way to hard-purge on Windows is to decommit and then
 * re-commit, but doing so is racy, and if re-commit fails it's a pain to
 * propagate the "poisoned" memory state.  Since we typically decommit as the
 * next step after purging on Windows anyway, there's no point in adding such
 * complexity.
 */
#if !defined(_WIN32) && ((defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \
    defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)) || \
    defined(JEMALLOC_MAPS_COALESCE))
#  define PAGES_CAN_PURGE_FORCED
#endif
static const bool pages_can_purge_lazy =
#ifdef PAGES_CAN_PURGE_LAZY
    true
#else
    false
#endif
    ;
static const bool pages_can_purge_forced =
#ifdef PAGES_CAN_PURGE_FORCED
    true
#else
    false
#endif
    ;
typedef enum {
    thp_mode_default       = 0, /* Do not change hugepage settings. */
    thp_mode_always        = 1, /* Always set MADV_HUGEPAGE. */
    thp_mode_never         = 2, /* Always set MADV_NOHUGEPAGE. */
    thp_mode_names_limit   = 3, /* Used for option processing. */
    thp_mode_not_supported = 3  /* No THP support detected. */
} thp_mode_t;
#define THP_MODE_DEFAULT thp_mode_default
extern thp_mode_t opt_thp;
extern thp_mode_t init_system_thp_mode; /* Initial system wide state. */
extern const char *thp_mode_names[];
void *pages_map(void *addr, size_t size, size_t alignment, bool *commit);
void pages_unmap(void *addr, size_t size);
bool pages_commit(void *addr, size_t size);
bool pages_decommit(void *addr, size_t size);
bool pages_purge_lazy(void *addr, size_t size);
bool pages_purge_forced(void *addr, size_t size);
bool pages_huge(void *addr, size_t size);
bool pages_nohuge(void *addr, size_t size);
bool pages_dontdump(void *addr, size_t size);
bool pages_dodump(void *addr, size_t size);
bool pages_boot(void);
void pages_set_thp_state (void *ptr, size_t size);
#endif /* JEMALLOC_INTERNAL_PAGES_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/ph.h
New file
@@ -0,0 +1,391 @@
/*
 * A Pairing Heap implementation.
 *
 * "The Pairing Heap: A New Form of Self-Adjusting Heap"
 * https://www.cs.cmu.edu/~sleator/papers/pairing-heaps.pdf
 *
 * With auxiliary twopass list, described in a follow on paper.
 *
 * "Pairing Heaps: Experiments and Analysis"
 * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.106.2988&rep=rep1&type=pdf
 *
 *******************************************************************************
 */
#ifndef PH_H_
#define PH_H_
/* Node structure. */
#define phn(a_type)                            \
struct {                                \
    a_type    *phn_prev;                        \
    a_type    *phn_next;                        \
    a_type    *phn_lchild;                        \
}
/* Root structure. */
#define ph(a_type)                            \
struct {                                \
    a_type    *ph_root;                        \
}
/* Internal utility macros. */
#define phn_lchild_get(a_type, a_field, a_phn)                \
    (a_phn->a_field.phn_lchild)
#define phn_lchild_set(a_type, a_field, a_phn, a_lchild) do {        \
    a_phn->a_field.phn_lchild = a_lchild;                \
} while (0)
#define phn_next_get(a_type, a_field, a_phn)                \
    (a_phn->a_field.phn_next)
#define phn_prev_set(a_type, a_field, a_phn, a_prev) do {        \
    a_phn->a_field.phn_prev = a_prev;                \
} while (0)
#define phn_prev_get(a_type, a_field, a_phn)                \
    (a_phn->a_field.phn_prev)
#define phn_next_set(a_type, a_field, a_phn, a_next) do {        \
    a_phn->a_field.phn_next = a_next;                \
} while (0)
#define phn_merge_ordered(a_type, a_field, a_phn0, a_phn1, a_cmp) do {    \
    a_type *phn0child;                        \
                                    \
    assert(a_phn0 != NULL);                        \
    assert(a_phn1 != NULL);                        \
    assert(a_cmp(a_phn0, a_phn1) <= 0);                \
                                    \
    phn_prev_set(a_type, a_field, a_phn1, a_phn0);            \
    phn0child = phn_lchild_get(a_type, a_field, a_phn0);        \
    phn_next_set(a_type, a_field, a_phn1, phn0child);        \
    if (phn0child != NULL) {                    \
        phn_prev_set(a_type, a_field, phn0child, a_phn1);    \
    }                                \
    phn_lchild_set(a_type, a_field, a_phn0, a_phn1);        \
} while (0)
#define phn_merge(a_type, a_field, a_phn0, a_phn1, a_cmp, r_phn) do {    \
    if (a_phn0 == NULL) {                        \
        r_phn = a_phn1;                        \
    } else if (a_phn1 == NULL) {                    \
        r_phn = a_phn0;                        \
    } else if (a_cmp(a_phn0, a_phn1) < 0) {                \
        phn_merge_ordered(a_type, a_field, a_phn0, a_phn1,    \
            a_cmp);                        \
        r_phn = a_phn0;                        \
    } else {                            \
        phn_merge_ordered(a_type, a_field, a_phn1, a_phn0,    \
            a_cmp);                        \
        r_phn = a_phn1;                        \
    }                                \
} while (0)
#define ph_merge_siblings(a_type, a_field, a_phn, a_cmp, r_phn) do {    \
    a_type *head = NULL;                        \
    a_type *tail = NULL;                        \
    a_type *phn0 = a_phn;                        \
    a_type *phn1 = phn_next_get(a_type, a_field, phn0);        \
                                    \
    /*                                \
     * Multipass merge, wherein the first two elements of a FIFO    \
     * are repeatedly merged, and each result is appended to the    \
     * singly linked FIFO, until the FIFO contains only a single    \
     * element.  We start with a sibling list but no reference to    \
     * its tail, so we do a single pass over the sibling list to    \
     * populate the FIFO.                        \
     */                                \
    if (phn1 != NULL) {                        \
        a_type *phnrest = phn_next_get(a_type, a_field, phn1);    \
        if (phnrest != NULL) {                    \
            phn_prev_set(a_type, a_field, phnrest, NULL);    \
        }                            \
        phn_prev_set(a_type, a_field, phn0, NULL);        \
        phn_next_set(a_type, a_field, phn0, NULL);        \
        phn_prev_set(a_type, a_field, phn1, NULL);        \
        phn_next_set(a_type, a_field, phn1, NULL);        \
        phn_merge(a_type, a_field, phn0, phn1, a_cmp, phn0);    \
        head = tail = phn0;                    \
        phn0 = phnrest;                        \
        while (phn0 != NULL) {                    \
            phn1 = phn_next_get(a_type, a_field, phn0);    \
            if (phn1 != NULL) {                \
                phnrest = phn_next_get(a_type, a_field,    \
                    phn1);                \
                if (phnrest != NULL) {            \
                    phn_prev_set(a_type, a_field,    \
                        phnrest, NULL);        \
                }                    \
                phn_prev_set(a_type, a_field, phn0,    \
                    NULL);                \
                phn_next_set(a_type, a_field, phn0,    \
                    NULL);                \
                phn_prev_set(a_type, a_field, phn1,    \
                    NULL);                \
                phn_next_set(a_type, a_field, phn1,    \
                    NULL);                \
                phn_merge(a_type, a_field, phn0, phn1,    \
                    a_cmp, phn0);            \
                phn_next_set(a_type, a_field, tail,    \
                    phn0);                \
                tail = phn0;                \
                phn0 = phnrest;                \
            } else {                    \
                phn_next_set(a_type, a_field, tail,    \
                    phn0);                \
                tail = phn0;                \
                phn0 = NULL;                \
            }                        \
        }                            \
        phn0 = head;                        \
        phn1 = phn_next_get(a_type, a_field, phn0);        \
        if (phn1 != NULL) {                    \
            while (true) {                    \
                head = phn_next_get(a_type, a_field,    \
                    phn1);                \
                assert(phn_prev_get(a_type, a_field,    \
                    phn0) == NULL);            \
                phn_next_set(a_type, a_field, phn0,    \
                    NULL);                \
                assert(phn_prev_get(a_type, a_field,    \
                    phn1) == NULL);            \
                phn_next_set(a_type, a_field, phn1,    \
                    NULL);                \
                phn_merge(a_type, a_field, phn0, phn1,    \
                    a_cmp, phn0);            \
                if (head == NULL) {            \
                    break;                \
                }                    \
                phn_next_set(a_type, a_field, tail,    \
                    phn0);                \
                tail = phn0;                \
                phn0 = head;                \
                phn1 = phn_next_get(a_type, a_field,    \
                    phn0);                \
            }                        \
        }                            \
    }                                \
    r_phn = phn0;                            \
} while (0)
#define ph_merge_aux(a_type, a_field, a_ph, a_cmp) do {            \
    a_type *phn = phn_next_get(a_type, a_field, a_ph->ph_root);    \
    if (phn != NULL) {                        \
        phn_prev_set(a_type, a_field, a_ph->ph_root, NULL);    \
        phn_next_set(a_type, a_field, a_ph->ph_root, NULL);    \
        phn_prev_set(a_type, a_field, phn, NULL);        \
        ph_merge_siblings(a_type, a_field, phn, a_cmp, phn);    \
        assert(phn_next_get(a_type, a_field, phn) == NULL);    \
        phn_merge(a_type, a_field, a_ph->ph_root, phn, a_cmp,    \
            a_ph->ph_root);                    \
    }                                \
} while (0)
#define ph_merge_children(a_type, a_field, a_phn, a_cmp, r_phn) do {    \
    a_type *lchild = phn_lchild_get(a_type, a_field, a_phn);    \
    if (lchild == NULL) {                        \
        r_phn = NULL;                        \
    } else {                            \
        ph_merge_siblings(a_type, a_field, lchild, a_cmp,    \
            r_phn);                        \
    }                                \
} while (0)
/*
 * The ph_proto() macro generates function prototypes that correspond to the
 * functions generated by an equivalently parameterized call to ph_gen().
 */
#define ph_proto(a_attr, a_prefix, a_ph_type, a_type)            \
a_attr void    a_prefix##new(a_ph_type *ph);                \
a_attr bool    a_prefix##empty(a_ph_type *ph);                \
a_attr a_type    *a_prefix##first(a_ph_type *ph);            \
a_attr a_type    *a_prefix##any(a_ph_type *ph);                \
a_attr void    a_prefix##insert(a_ph_type *ph, a_type *phn);        \
a_attr a_type    *a_prefix##remove_first(a_ph_type *ph);            \
a_attr a_type    *a_prefix##remove_any(a_ph_type *ph);            \
a_attr void    a_prefix##remove(a_ph_type *ph, a_type *phn);
/*
 * The ph_gen() macro generates a type-specific pairing heap implementation,
 * based on the above cpp macros.
 */
#define ph_gen(a_attr, a_prefix, a_ph_type, a_type, a_field, a_cmp)    \
a_attr void                                \
a_prefix##new(a_ph_type *ph) {                        \
    memset(ph, 0, sizeof(ph(a_type)));                \
}                                    \
a_attr bool                                \
a_prefix##empty(a_ph_type *ph) {                    \
    return (ph->ph_root == NULL);                    \
}                                    \
a_attr a_type *                                \
a_prefix##first(a_ph_type *ph) {                    \
    if (ph->ph_root == NULL) {                    \
        return NULL;                        \
    }                                \
    ph_merge_aux(a_type, a_field, ph, a_cmp);            \
    return ph->ph_root;                        \
}                                    \
a_attr a_type *                                \
a_prefix##any(a_ph_type *ph) {                        \
    if (ph->ph_root == NULL) {                    \
        return NULL;                        \
    }                                \
    a_type *aux = phn_next_get(a_type, a_field, ph->ph_root);    \
    if (aux != NULL) {                        \
        return aux;                        \
    }                                \
    return ph->ph_root;                        \
}                                    \
a_attr void                                \
a_prefix##insert(a_ph_type *ph, a_type *phn) {                \
    memset(&phn->a_field, 0, sizeof(phn(a_type)));            \
                                    \
    /*                                \
     * Treat the root as an aux list during insertion, and lazily    \
     * merge during a_prefix##remove_first().  For elements that    \
     * are inserted, then removed via a_prefix##remove() before the    \
     * aux list is ever processed, this makes insert/remove        \
     * constant-time, whereas eager merging would make insert    \
     * O(log n).                            \
     */                                \
    if (ph->ph_root == NULL) {                    \
        ph->ph_root = phn;                    \
    } else {                            \
        phn_next_set(a_type, a_field, phn, phn_next_get(a_type,    \
            a_field, ph->ph_root));                \
        if (phn_next_get(a_type, a_field, ph->ph_root) !=    \
            NULL) {                        \
            phn_prev_set(a_type, a_field,            \
                phn_next_get(a_type, a_field, ph->ph_root),    \
                phn);                    \
        }                            \
        phn_prev_set(a_type, a_field, phn, ph->ph_root);    \
        phn_next_set(a_type, a_field, ph->ph_root, phn);    \
    }                                \
}                                    \
a_attr a_type *                                \
a_prefix##remove_first(a_ph_type *ph) {                    \
    a_type *ret;                            \
                                    \
    if (ph->ph_root == NULL) {                    \
        return NULL;                        \
    }                                \
    ph_merge_aux(a_type, a_field, ph, a_cmp);            \
                                    \
    ret = ph->ph_root;                        \
                                    \
    ph_merge_children(a_type, a_field, ph->ph_root, a_cmp,        \
        ph->ph_root);                        \
                                    \
    return ret;                            \
}                                    \
a_attr a_type *                                \
a_prefix##remove_any(a_ph_type *ph) {                    \
    /*                                \
     * Remove the most recently inserted aux list element, or the    \
     * root if the aux list is empty.  This has the effect of    \
     * behaving as a LIFO (and insertion/removal is therefore    \
     * constant-time) if a_prefix##[remove_]first() are never    \
     * called.                            \
     */                                \
    if (ph->ph_root == NULL) {                    \
        return NULL;                        \
    }                                \
    a_type *ret = phn_next_get(a_type, a_field, ph->ph_root);    \
    if (ret != NULL) {                        \
        a_type *aux = phn_next_get(a_type, a_field, ret);    \
        phn_next_set(a_type, a_field, ph->ph_root, aux);    \
        if (aux != NULL) {                    \
            phn_prev_set(a_type, a_field, aux,        \
                ph->ph_root);                \
        }                            \
        return ret;                        \
    }                                \
    ret = ph->ph_root;                        \
    ph_merge_children(a_type, a_field, ph->ph_root, a_cmp,        \
        ph->ph_root);                        \
    return ret;                            \
}                                    \
a_attr void                                \
a_prefix##remove(a_ph_type *ph, a_type *phn) {                \
    a_type *replace, *parent;                    \
                                    \
    if (ph->ph_root == phn) {                    \
        /*                            \
         * We can delete from aux list without merging it, but    \
         * we need to merge if we are dealing with the root    \
         * node and it has children.                \
         */                            \
        if (phn_lchild_get(a_type, a_field, phn) == NULL) {    \
            ph->ph_root = phn_next_get(a_type, a_field,    \
                phn);                    \
            if (ph->ph_root != NULL) {            \
                phn_prev_set(a_type, a_field,        \
                    ph->ph_root, NULL);            \
            }                        \
            return;                        \
        }                            \
        ph_merge_aux(a_type, a_field, ph, a_cmp);        \
        if (ph->ph_root == phn) {                \
            ph_merge_children(a_type, a_field, ph->ph_root,    \
                a_cmp, ph->ph_root);            \
            return;                        \
        }                            \
    }                                \
                                    \
    /* Get parent (if phn is leftmost child) before mutating. */    \
    if ((parent = phn_prev_get(a_type, a_field, phn)) != NULL) {    \
        if (phn_lchild_get(a_type, a_field, parent) != phn) {    \
            parent = NULL;                    \
        }                            \
    }                                \
    /* Find a possible replacement node, and link to parent. */    \
    ph_merge_children(a_type, a_field, phn, a_cmp, replace);    \
    /* Set next/prev for sibling linked list. */            \
    if (replace != NULL) {                        \
        if (parent != NULL) {                    \
            phn_prev_set(a_type, a_field, replace, parent);    \
            phn_lchild_set(a_type, a_field, parent,        \
                replace);                    \
        } else {                        \
            phn_prev_set(a_type, a_field, replace,        \
                phn_prev_get(a_type, a_field, phn));    \
            if (phn_prev_get(a_type, a_field, phn) !=    \
                NULL) {                    \
                phn_next_set(a_type, a_field,        \
                    phn_prev_get(a_type, a_field, phn),    \
                    replace);                \
            }                        \
        }                            \
        phn_next_set(a_type, a_field, replace,            \
            phn_next_get(a_type, a_field, phn));        \
        if (phn_next_get(a_type, a_field, phn) != NULL) {    \
            phn_prev_set(a_type, a_field,            \
                phn_next_get(a_type, a_field, phn),        \
                replace);                    \
        }                            \
    } else {                            \
        if (parent != NULL) {                    \
            a_type *next = phn_next_get(a_type, a_field,    \
                phn);                    \
            phn_lchild_set(a_type, a_field, parent, next);    \
            if (next != NULL) {                \
                phn_prev_set(a_type, a_field, next,    \
                    parent);                \
            }                        \
        } else {                        \
            assert(phn_prev_get(a_type, a_field, phn) !=    \
                NULL);                    \
            phn_next_set(a_type, a_field,            \
                phn_prev_get(a_type, a_field, phn),        \
                phn_next_get(a_type, a_field, phn));    \
        }                            \
        if (phn_next_get(a_type, a_field, phn) != NULL) {    \
            phn_prev_set(a_type, a_field,            \
                phn_next_get(a_type, a_field, phn),        \
                phn_prev_get(a_type, a_field, phn));    \
        }                            \
    }                                \
}
#endif /* PH_H_ */
jemalloc/x64/include/jemalloc/internal/private_namespace.sh
New file
@@ -0,0 +1,5 @@
#!/bin/sh
for symbol in `cat "$@"` ; do
  echo "#define ${symbol} JEMALLOC_N(${symbol})"
done
jemalloc/x64/include/jemalloc/internal/private_symbols.awk
New file
@@ -0,0 +1,51 @@
#!/usr/bin/env awk -f
BEGIN {
  sym_prefix = ""
  split("\
        je_aligned_alloc \
        je_calloc \
        je_dallocx \
        je_free \
        je_mallctl \
        je_mallctlbymib \
        je_mallctlnametomib \
        je_malloc \
        je_malloc_conf \
        je_malloc_message \
        je_malloc_stats_print \
        je_malloc_usable_size \
        je_mallocx \
        je_smallocx_0000000000000000000000000000000000000000 \
        je_nallocx \
        je_posix_memalign \
        je_rallocx \
        je_realloc \
        je_sallocx \
        je_sdallocx \
        je_xallocx \
        tls_callback \
        ", exported_symbol_names)
  # Store exported symbol names as keys in exported_symbols.
  for (i in exported_symbol_names) {
    exported_symbols[exported_symbol_names[i]] = 1
  }
}
# Process 'nm -a <c_source.o>' output.
#
# Handle lines like:
#   0000000000000008 D opt_junk
#   0000000000007574 T malloc_initialized
(NF == 3 && $2 ~ /^[ABCDGRSTVW]$/ && !($3 in exported_symbols) && $3 ~ /^[A-Za-z0-9_]+$/) {
  print substr($3, 1+length(sym_prefix), length($3)-length(sym_prefix))
}
# Process 'dumpbin /SYMBOLS <c_source.obj>' output.
#
# Handle lines like:
#   353 00008098 SECT4  notype       External     | opt_junk
#   3F1 00000000 SECT7  notype ()    External     | malloc_initialized
($3 ~ /^SECT[0-9]+/ && $(NF-2) == "External" && !($NF in exported_symbols)) {
  print $NF
}
jemalloc/x64/include/jemalloc/internal/private_symbols.sh
New file
@@ -0,0 +1,51 @@
#!/bin/sh
#
# Generate private_symbols[_jet].awk.
#
# Usage: private_symbols.sh <sym_prefix> <sym>*
#
# <sym_prefix> is typically "" or "_".
sym_prefix=$1
shift
cat <<EOF
#!/usr/bin/env awk -f
BEGIN {
  sym_prefix = "${sym_prefix}"
  split("\\
EOF
for public_sym in "$@" ; do
  cat <<EOF
        ${sym_prefix}${public_sym} \\
EOF
done
cat <<"EOF"
        ", exported_symbol_names)
  # Store exported symbol names as keys in exported_symbols.
  for (i in exported_symbol_names) {
    exported_symbols[exported_symbol_names[i]] = 1
  }
}
# Process 'nm -a <c_source.o>' output.
#
# Handle lines like:
#   0000000000000008 D opt_junk
#   0000000000007574 T malloc_initialized
(NF == 3 && $2 ~ /^[ABCDGRSTVW]$/ && !($3 in exported_symbols) && $3 ~ /^[A-Za-z0-9_]+$/) {
  print substr($3, 1+length(sym_prefix), length($3)-length(sym_prefix))
}
# Process 'dumpbin /SYMBOLS <c_source.obj>' output.
#
# Handle lines like:
#   353 00008098 SECT4  notype       External     | opt_junk
#   3F1 00000000 SECT7  notype ()    External     | malloc_initialized
($3 ~ /^SECT[0-9]+/ && $(NF-2) == "External" && !($NF in exported_symbols)) {
  print $NF
}
EOF
jemalloc/x64/include/jemalloc/internal/private_symbols_jet.awk
New file
@@ -0,0 +1,51 @@
#!/usr/bin/env awk -f
BEGIN {
  sym_prefix = ""
  split("\
        jet_aligned_alloc \
        jet_calloc \
        jet_dallocx \
        jet_free \
        jet_mallctl \
        jet_mallctlbymib \
        jet_mallctlnametomib \
        jet_malloc \
        jet_malloc_conf \
        jet_malloc_message \
        jet_malloc_stats_print \
        jet_malloc_usable_size \
        jet_mallocx \
        jet_smallocx_0000000000000000000000000000000000000000 \
        jet_nallocx \
        jet_posix_memalign \
        jet_rallocx \
        jet_realloc \
        jet_sallocx \
        jet_sdallocx \
        jet_xallocx \
        tls_callback \
        ", exported_symbol_names)
  # Store exported symbol names as keys in exported_symbols.
  for (i in exported_symbol_names) {
    exported_symbols[exported_symbol_names[i]] = 1
  }
}
# Process 'nm -a <c_source.o>' output.
#
# Handle lines like:
#   0000000000000008 D opt_junk
#   0000000000007574 T malloc_initialized
(NF == 3 && $2 ~ /^[ABCDGRSTVW]$/ && !($3 in exported_symbols) && $3 ~ /^[A-Za-z0-9_]+$/) {
  print substr($3, 1+length(sym_prefix), length($3)-length(sym_prefix))
}
# Process 'dumpbin /SYMBOLS <c_source.obj>' output.
#
# Handle lines like:
#   353 00008098 SECT4  notype       External     | opt_junk
#   3F1 00000000 SECT7  notype ()    External     | malloc_initialized
($3 ~ /^SECT[0-9]+/ && $(NF-2) == "External" && !($NF in exported_symbols)) {
  print $NF
}
jemalloc/x64/include/jemalloc/internal/prng.h
New file
@@ -0,0 +1,185 @@
#ifndef JEMALLOC_INTERNAL_PRNG_H
#define JEMALLOC_INTERNAL_PRNG_H
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/bit_util.h"
/*
 * Simple linear congruential pseudo-random number generator:
 *
 *   prng(y) = (a*x + c) % m
 *
 * where the following constants ensure maximal period:
 *
 *   a == Odd number (relatively prime to 2^n), and (a-1) is a multiple of 4.
 *   c == Odd number (relatively prime to 2^n).
 *   m == 2^32
 *
 * See Knuth's TAOCP 3rd Ed., Vol. 2, pg. 17 for details on these constraints.
 *
 * This choice of m has the disadvantage that the quality of the bits is
 * proportional to bit position.  For example, the lowest bit has a cycle of 2,
 * the next has a cycle of 4, etc.  For this reason, we prefer to use the upper
 * bits.
 */
/******************************************************************************/
/* INTERNAL DEFINITIONS -- IGNORE */
/******************************************************************************/
#define PRNG_A_32    UINT32_C(1103515241)
#define PRNG_C_32    UINT32_C(12347)
#define PRNG_A_64    UINT64_C(6364136223846793005)
#define PRNG_C_64    UINT64_C(1442695040888963407)
JEMALLOC_ALWAYS_INLINE uint32_t
prng_state_next_u32(uint32_t state) {
    return (state * PRNG_A_32) + PRNG_C_32;
}
JEMALLOC_ALWAYS_INLINE uint64_t
prng_state_next_u64(uint64_t state) {
    return (state * PRNG_A_64) + PRNG_C_64;
}
JEMALLOC_ALWAYS_INLINE size_t
prng_state_next_zu(size_t state) {
#if LG_SIZEOF_PTR == 2
    return (state * PRNG_A_32) + PRNG_C_32;
#elif LG_SIZEOF_PTR == 3
    return (state * PRNG_A_64) + PRNG_C_64;
#else
#error Unsupported pointer size
#endif
}
/******************************************************************************/
/* BEGIN PUBLIC API */
/******************************************************************************/
/*
 * The prng_lg_range functions give a uniform int in the half-open range [0,
 * 2**lg_range).  If atomic is true, they do so safely from multiple threads.
 * Multithreaded 64-bit prngs aren't supported.
 */
JEMALLOC_ALWAYS_INLINE uint32_t
prng_lg_range_u32(atomic_u32_t *state, unsigned lg_range, bool atomic) {
    uint32_t ret, state0, state1;
    assert(lg_range > 0);
    assert(lg_range <= 32);
    state0 = atomic_load_u32(state, ATOMIC_RELAXED);
    if (atomic) {
        do {
            state1 = prng_state_next_u32(state0);
        } while (!atomic_compare_exchange_weak_u32(state, &state0,
            state1, ATOMIC_RELAXED, ATOMIC_RELAXED));
    } else {
        state1 = prng_state_next_u32(state0);
        atomic_store_u32(state, state1, ATOMIC_RELAXED);
    }
    ret = state1 >> (32 - lg_range);
    return ret;
}
JEMALLOC_ALWAYS_INLINE uint64_t
prng_lg_range_u64(uint64_t *state, unsigned lg_range) {
    uint64_t ret, state1;
    assert(lg_range > 0);
    assert(lg_range <= 64);
    state1 = prng_state_next_u64(*state);
    *state = state1;
    ret = state1 >> (64 - lg_range);
    return ret;
}
JEMALLOC_ALWAYS_INLINE size_t
prng_lg_range_zu(atomic_zu_t *state, unsigned lg_range, bool atomic) {
    size_t ret, state0, state1;
    assert(lg_range > 0);
    assert(lg_range <= ZU(1) << (3 + LG_SIZEOF_PTR));
    state0 = atomic_load_zu(state, ATOMIC_RELAXED);
    if (atomic) {
        do {
            state1 = prng_state_next_zu(state0);
        } while (atomic_compare_exchange_weak_zu(state, &state0,
            state1, ATOMIC_RELAXED, ATOMIC_RELAXED));
    } else {
        state1 = prng_state_next_zu(state0);
        atomic_store_zu(state, state1, ATOMIC_RELAXED);
    }
    ret = state1 >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) - lg_range);
    return ret;
}
/*
 * The prng_range functions behave like the prng_lg_range, but return a result
 * in [0, range) instead of [0, 2**lg_range).
 */
JEMALLOC_ALWAYS_INLINE uint32_t
prng_range_u32(atomic_u32_t *state, uint32_t range, bool atomic) {
    uint32_t ret;
    unsigned lg_range;
    assert(range > 1);
    /* Compute the ceiling of lg(range). */
    lg_range = ffs_u32(pow2_ceil_u32(range)) - 1;
    /* Generate a result in [0..range) via repeated trial. */
    do {
        ret = prng_lg_range_u32(state, lg_range, atomic);
    } while (ret >= range);
    return ret;
}
JEMALLOC_ALWAYS_INLINE uint64_t
prng_range_u64(uint64_t *state, uint64_t range) {
    uint64_t ret;
    unsigned lg_range;
    assert(range > 1);
    /* Compute the ceiling of lg(range). */
    lg_range = ffs_u64(pow2_ceil_u64(range)) - 1;
    /* Generate a result in [0..range) via repeated trial. */
    do {
        ret = prng_lg_range_u64(state, lg_range);
    } while (ret >= range);
    return ret;
}
JEMALLOC_ALWAYS_INLINE size_t
prng_range_zu(atomic_zu_t *state, size_t range, bool atomic) {
    size_t ret;
    unsigned lg_range;
    assert(range > 1);
    /* Compute the ceiling of lg(range). */
    lg_range = ffs_u64(pow2_ceil_u64(range)) - 1;
    /* Generate a result in [0..range) via repeated trial. */
    do {
        ret = prng_lg_range_zu(state, lg_range, atomic);
    } while (ret >= range);
    return ret;
}
#endif /* JEMALLOC_INTERNAL_PRNG_H */
jemalloc/x64/include/jemalloc/internal/prof_externs.h
New file
@@ -0,0 +1,105 @@
#ifndef JEMALLOC_INTERNAL_PROF_EXTERNS_H
#define JEMALLOC_INTERNAL_PROF_EXTERNS_H
#include "jemalloc/internal/mutex.h"
extern malloc_mutex_t    bt2gctx_mtx;
extern bool    opt_prof;
extern bool    opt_prof_active;
extern bool    opt_prof_thread_active_init;
extern size_t    opt_lg_prof_sample;   /* Mean bytes between samples. */
extern ssize_t    opt_lg_prof_interval; /* lg(prof_interval). */
extern bool    opt_prof_gdump;       /* High-water memory dumping. */
extern bool    opt_prof_final;       /* Final profile dumping. */
extern bool    opt_prof_leak;        /* Dump leak summary at exit. */
extern bool    opt_prof_accum;       /* Report cumulative bytes. */
extern bool    opt_prof_log;          /* Turn logging on at boot. */
extern char    opt_prof_prefix[
    /* Minimize memory bloat for non-prof builds. */
#ifdef JEMALLOC_PROF
    PATH_MAX +
#endif
    1];
/* Accessed via prof_active_[gs]et{_unlocked,}(). */
extern bool    prof_active;
/* Accessed via prof_gdump_[gs]et{_unlocked,}(). */
extern bool    prof_gdump_val;
/*
 * Profile dump interval, measured in bytes allocated.  Each arena triggers a
 * profile dump when it reaches this threshold.  The effect is that the
 * interval between profile dumps averages prof_interval, though the actual
 * interval between dumps will tend to be sporadic, and the interval will be a
 * maximum of approximately (prof_interval * narenas).
 */
extern uint64_t    prof_interval;
/*
 * Initialized as opt_lg_prof_sample, and potentially modified during profiling
 * resets.
 */
extern size_t    lg_prof_sample;
void prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated);
void prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,
    prof_tctx_t *tctx);
void prof_free_sampled_object(tsd_t *tsd, const void *ptr, size_t usize,
    prof_tctx_t *tctx);
void bt_init(prof_bt_t *bt, void **vec);
void prof_backtrace(prof_bt_t *bt);
prof_tctx_t *prof_lookup(tsd_t *tsd, prof_bt_t *bt);
#ifdef JEMALLOC_JET
size_t prof_tdata_count(void);
size_t prof_bt_count(void);
#endif
typedef int (prof_dump_open_t)(bool, const char *);
extern prof_dump_open_t *JET_MUTABLE prof_dump_open;
typedef bool (prof_dump_header_t)(tsdn_t *, bool, const prof_cnt_t *);
extern prof_dump_header_t *JET_MUTABLE prof_dump_header;
#ifdef JEMALLOC_JET
void prof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs,
    uint64_t *accumbytes);
#endif
bool prof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum);
void prof_idump(tsdn_t *tsdn);
bool prof_mdump(tsd_t *tsd, const char *filename);
void prof_gdump(tsdn_t *tsdn);
prof_tdata_t *prof_tdata_init(tsd_t *tsd);
prof_tdata_t *prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata);
void prof_reset(tsd_t *tsd, size_t lg_sample);
void prof_tdata_cleanup(tsd_t *tsd);
bool prof_active_get(tsdn_t *tsdn);
bool prof_active_set(tsdn_t *tsdn, bool active);
const char *prof_thread_name_get(tsd_t *tsd);
int prof_thread_name_set(tsd_t *tsd, const char *thread_name);
bool prof_thread_active_get(tsd_t *tsd);
bool prof_thread_active_set(tsd_t *tsd, bool active);
bool prof_thread_active_init_get(tsdn_t *tsdn);
bool prof_thread_active_init_set(tsdn_t *tsdn, bool active_init);
bool prof_gdump_get(tsdn_t *tsdn);
bool prof_gdump_set(tsdn_t *tsdn, bool active);
void prof_boot0(void);
void prof_boot1(void);
bool prof_boot2(tsd_t *tsd);
void prof_prefork0(tsdn_t *tsdn);
void prof_prefork1(tsdn_t *tsdn);
void prof_postfork_parent(tsdn_t *tsdn);
void prof_postfork_child(tsdn_t *tsdn);
void prof_sample_threshold_update(prof_tdata_t *tdata);
bool prof_log_start(tsdn_t *tsdn, const char *filename);
bool prof_log_stop(tsdn_t *tsdn);
#ifdef JEMALLOC_JET
size_t prof_log_bt_count(void);
size_t prof_log_alloc_count(void);
size_t prof_log_thr_count(void);
bool prof_log_is_logging(void);
bool prof_log_rep_check(void);
void prof_log_dummy_set(bool new_value);
#endif
#endif /* JEMALLOC_INTERNAL_PROF_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/prof_inlines_a.h
New file
@@ -0,0 +1,85 @@
#ifndef JEMALLOC_INTERNAL_PROF_INLINES_A_H
#define JEMALLOC_INTERNAL_PROF_INLINES_A_H
#include "jemalloc/internal/mutex.h"
static inline bool
prof_accum_add(tsdn_t *tsdn, prof_accum_t *prof_accum,
    uint64_t accumbytes) {
    cassert(config_prof);
    bool overflow;
    uint64_t a0, a1;
    /*
     * If the application allocates fast enough (and/or if idump is slow
     * enough), extreme overflow here (a1 >= prof_interval * 2) can cause
     * idump trigger coalescing.  This is an intentional mechanism that
     * avoids rate-limiting allocation.
     */
#ifdef JEMALLOC_ATOMIC_U64
    a0 = atomic_load_u64(&prof_accum->accumbytes, ATOMIC_RELAXED);
    do {
        a1 = a0 + accumbytes;
        assert(a1 >= a0);
        overflow = (a1 >= prof_interval);
        if (overflow) {
            a1 %= prof_interval;
        }
    } while (!atomic_compare_exchange_weak_u64(&prof_accum->accumbytes, &a0,
        a1, ATOMIC_RELAXED, ATOMIC_RELAXED));
#else
    malloc_mutex_lock(tsdn, &prof_accum->mtx);
    a0 = prof_accum->accumbytes;
    a1 = a0 + accumbytes;
    overflow = (a1 >= prof_interval);
    if (overflow) {
        a1 %= prof_interval;
    }
    prof_accum->accumbytes = a1;
    malloc_mutex_unlock(tsdn, &prof_accum->mtx);
#endif
    return overflow;
}
static inline void
prof_accum_cancel(tsdn_t *tsdn, prof_accum_t *prof_accum,
    size_t usize) {
    cassert(config_prof);
    /*
     * Cancel out as much of the excessive prof_accumbytes increase as
     * possible without underflowing.  Interval-triggered dumps occur
     * slightly more often than intended as a result of incomplete
     * canceling.
     */
    uint64_t a0, a1;
#ifdef JEMALLOC_ATOMIC_U64
    a0 = atomic_load_u64(&prof_accum->accumbytes, ATOMIC_RELAXED);
    do {
        a1 = (a0 >= SC_LARGE_MINCLASS - usize)
            ? a0 - (SC_LARGE_MINCLASS - usize) : 0;
    } while (!atomic_compare_exchange_weak_u64(&prof_accum->accumbytes, &a0,
        a1, ATOMIC_RELAXED, ATOMIC_RELAXED));
#else
    malloc_mutex_lock(tsdn, &prof_accum->mtx);
    a0 = prof_accum->accumbytes;
    a1 = (a0 >= SC_LARGE_MINCLASS - usize)
        ?  a0 - (SC_LARGE_MINCLASS - usize) : 0;
    prof_accum->accumbytes = a1;
    malloc_mutex_unlock(tsdn, &prof_accum->mtx);
#endif
}
JEMALLOC_ALWAYS_INLINE bool
prof_active_get_unlocked(void) {
    /*
     * Even if opt_prof is true, sampling can be temporarily disabled by
     * setting prof_active to false.  No locking is used when reading
     * prof_active in the fast path, so there are no guarantees regarding
     * how long it will take for all threads to notice state changes.
     */
    return prof_active;
}
#endif /* JEMALLOC_INTERNAL_PROF_INLINES_A_H */
jemalloc/x64/include/jemalloc/internal/prof_inlines_b.h
New file
@@ -0,0 +1,250 @@
#ifndef JEMALLOC_INTERNAL_PROF_INLINES_B_H
#define JEMALLOC_INTERNAL_PROF_INLINES_B_H
#include "jemalloc/internal/safety_check.h"
#include "jemalloc/internal/sz.h"
JEMALLOC_ALWAYS_INLINE bool
prof_gdump_get_unlocked(void) {
    /*
     * No locking is used when reading prof_gdump_val in the fast path, so
     * there are no guarantees regarding how long it will take for all
     * threads to notice state changes.
     */
    return prof_gdump_val;
}
JEMALLOC_ALWAYS_INLINE prof_tdata_t *
prof_tdata_get(tsd_t *tsd, bool create) {
    prof_tdata_t *tdata;
    cassert(config_prof);
    tdata = tsd_prof_tdata_get(tsd);
    if (create) {
        if (unlikely(tdata == NULL)) {
            if (tsd_nominal(tsd)) {
                tdata = prof_tdata_init(tsd);
                tsd_prof_tdata_set(tsd, tdata);
            }
        } else if (unlikely(tdata->expired)) {
            tdata = prof_tdata_reinit(tsd, tdata);
            tsd_prof_tdata_set(tsd, tdata);
        }
        assert(tdata == NULL || tdata->attached);
    }
    return tdata;
}
JEMALLOC_ALWAYS_INLINE prof_tctx_t *
prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
    cassert(config_prof);
    assert(ptr != NULL);
    return arena_prof_tctx_get(tsdn, ptr, alloc_ctx);
}
JEMALLOC_ALWAYS_INLINE void
prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,
    alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {
    cassert(config_prof);
    assert(ptr != NULL);
    arena_prof_tctx_set(tsdn, ptr, usize, alloc_ctx, tctx);
}
JEMALLOC_ALWAYS_INLINE void
prof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) {
    cassert(config_prof);
    assert(ptr != NULL);
    arena_prof_tctx_reset(tsdn, ptr, tctx);
}
JEMALLOC_ALWAYS_INLINE nstime_t
prof_alloc_time_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
    cassert(config_prof);
    assert(ptr != NULL);
    return arena_prof_alloc_time_get(tsdn, ptr, alloc_ctx);
}
JEMALLOC_ALWAYS_INLINE void
prof_alloc_time_set(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx,
    nstime_t t) {
    cassert(config_prof);
    assert(ptr != NULL);
    arena_prof_alloc_time_set(tsdn, ptr, alloc_ctx, t);
}
JEMALLOC_ALWAYS_INLINE bool
prof_sample_check(tsd_t *tsd, size_t usize, bool update) {
    ssize_t check = update ? 0 : usize;
    int64_t bytes_until_sample = tsd_bytes_until_sample_get(tsd);
    if (update) {
        bytes_until_sample -= usize;
        if (tsd_nominal(tsd)) {
            tsd_bytes_until_sample_set(tsd, bytes_until_sample);
        }
    }
    if (likely(bytes_until_sample >= check)) {
        return true;
    }
    return false;
}
JEMALLOC_ALWAYS_INLINE bool
prof_sample_accum_update(tsd_t *tsd, size_t usize, bool update,
             prof_tdata_t **tdata_out) {
    prof_tdata_t *tdata;
    cassert(config_prof);
    /* Fastpath: no need to load tdata */
    if (likely(prof_sample_check(tsd, usize, update))) {
        return true;
    }
    bool booted = tsd_prof_tdata_get(tsd);
    tdata = prof_tdata_get(tsd, true);
    if (unlikely((uintptr_t)tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)) {
        tdata = NULL;
    }
    if (tdata_out != NULL) {
        *tdata_out = tdata;
    }
    if (unlikely(tdata == NULL)) {
        return true;
    }
    /*
     * If this was the first creation of tdata, then
     * prof_tdata_get() reset bytes_until_sample, so decrement and
     * check it again
     */
    if (!booted && prof_sample_check(tsd, usize, update)) {
        return true;
    }
    if (tsd_reentrancy_level_get(tsd) > 0) {
        return true;
    }
    /* Compute new sample threshold. */
    if (update) {
        prof_sample_threshold_update(tdata);
    }
    return !tdata->active;
}
JEMALLOC_ALWAYS_INLINE prof_tctx_t *
prof_alloc_prep(tsd_t *tsd, size_t usize, bool prof_active, bool update) {
    prof_tctx_t *ret;
    prof_tdata_t *tdata;
    prof_bt_t bt;
    assert(usize == sz_s2u(usize));
    if (!prof_active || likely(prof_sample_accum_update(tsd, usize, update,
        &tdata))) {
        ret = (prof_tctx_t *)(uintptr_t)1U;
    } else {
        bt_init(&bt, tdata->vec);
        prof_backtrace(&bt);
        ret = prof_lookup(tsd, &bt);
    }
    return ret;
}
JEMALLOC_ALWAYS_INLINE void
prof_malloc(tsdn_t *tsdn, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx,
    prof_tctx_t *tctx) {
    cassert(config_prof);
    assert(ptr != NULL);
    assert(usize == isalloc(tsdn, ptr));
    if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {
        prof_malloc_sample_object(tsdn, ptr, usize, tctx);
    } else {
        prof_tctx_set(tsdn, ptr, usize, alloc_ctx,
            (prof_tctx_t *)(uintptr_t)1U);
    }
}
JEMALLOC_ALWAYS_INLINE void
prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,
    bool prof_active, bool updated, const void *old_ptr, size_t old_usize,
    prof_tctx_t *old_tctx) {
    bool sampled, old_sampled, moved;
    cassert(config_prof);
    assert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U);
    if (prof_active && !updated && ptr != NULL) {
        assert(usize == isalloc(tsd_tsdn(tsd), ptr));
        if (prof_sample_accum_update(tsd, usize, true, NULL)) {
            /*
             * Don't sample.  The usize passed to prof_alloc_prep()
             * was larger than what actually got allocated, so a
             * backtrace was captured for this allocation, even
             * though its actual usize was insufficient to cross the
             * sample threshold.
             */
            prof_alloc_rollback(tsd, tctx, true);
            tctx = (prof_tctx_t *)(uintptr_t)1U;
        }
    }
    sampled = ((uintptr_t)tctx > (uintptr_t)1U);
    old_sampled = ((uintptr_t)old_tctx > (uintptr_t)1U);
    moved = (ptr != old_ptr);
    if (unlikely(sampled)) {
        prof_malloc_sample_object(tsd_tsdn(tsd), ptr, usize, tctx);
    } else if (moved) {
        prof_tctx_set(tsd_tsdn(tsd), ptr, usize, NULL,
            (prof_tctx_t *)(uintptr_t)1U);
    } else if (unlikely(old_sampled)) {
        /*
         * prof_tctx_set() would work for the !moved case as well, but
         * prof_tctx_reset() is slightly cheaper, and the proper thing
         * to do here in the presence of explicit knowledge re: moved
         * state.
         */
        prof_tctx_reset(tsd_tsdn(tsd), ptr, tctx);
    } else {
        assert((uintptr_t)prof_tctx_get(tsd_tsdn(tsd), ptr, NULL) ==
            (uintptr_t)1U);
    }
    /*
     * The prof_free_sampled_object() call must come after the
     * prof_malloc_sample_object() call, because tctx and old_tctx may be
     * the same, in which case reversing the call order could cause the tctx
     * to be prematurely destroyed as a side effect of momentarily zeroed
     * counters.
     */
    if (unlikely(old_sampled)) {
        prof_free_sampled_object(tsd, ptr, old_usize, old_tctx);
    }
}
JEMALLOC_ALWAYS_INLINE void
prof_free(tsd_t *tsd, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx) {
    prof_tctx_t *tctx = prof_tctx_get(tsd_tsdn(tsd), ptr, alloc_ctx);
    cassert(config_prof);
    assert(usize == isalloc(tsd_tsdn(tsd), ptr));
    if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {
        prof_free_sampled_object(tsd, ptr, usize, tctx);
    }
}
#endif /* JEMALLOC_INTERNAL_PROF_INLINES_B_H */
jemalloc/x64/include/jemalloc/internal/prof_structs.h
New file
@@ -0,0 +1,200 @@
#ifndef JEMALLOC_INTERNAL_PROF_STRUCTS_H
#define JEMALLOC_INTERNAL_PROF_STRUCTS_H
#include "jemalloc/internal/ckh.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/prng.h"
#include "jemalloc/internal/rb.h"
struct prof_bt_s {
    /* Backtrace, stored as len program counters. */
    void        **vec;
    unsigned    len;
};
#ifdef JEMALLOC_PROF_LIBGCC
/* Data structure passed to libgcc _Unwind_Backtrace() callback functions. */
typedef struct {
    prof_bt_t    *bt;
    unsigned    max;
} prof_unwind_data_t;
#endif
struct prof_accum_s {
#ifndef JEMALLOC_ATOMIC_U64
    malloc_mutex_t    mtx;
    uint64_t    accumbytes;
#else
    atomic_u64_t    accumbytes;
#endif
};
struct prof_cnt_s {
    /* Profiling counters. */
    uint64_t    curobjs;
    uint64_t    curbytes;
    uint64_t    accumobjs;
    uint64_t    accumbytes;
};
typedef enum {
    prof_tctx_state_initializing,
    prof_tctx_state_nominal,
    prof_tctx_state_dumping,
    prof_tctx_state_purgatory /* Dumper must finish destroying. */
} prof_tctx_state_t;
struct prof_tctx_s {
    /* Thread data for thread that performed the allocation. */
    prof_tdata_t        *tdata;
    /*
     * Copy of tdata->thr_{uid,discrim}, necessary because tdata may be
     * defunct during teardown.
     */
    uint64_t        thr_uid;
    uint64_t        thr_discrim;
    /* Profiling counters, protected by tdata->lock. */
    prof_cnt_t        cnts;
    /* Associated global context. */
    prof_gctx_t        *gctx;
    /*
     * UID that distinguishes multiple tctx's created by the same thread,
     * but coexisting in gctx->tctxs.  There are two ways that such
     * coexistence can occur:
     * - A dumper thread can cause a tctx to be retained in the purgatory
     *   state.
     * - Although a single "producer" thread must create all tctx's which
     *   share the same thr_uid, multiple "consumers" can each concurrently
     *   execute portions of prof_tctx_destroy().  prof_tctx_destroy() only
     *   gets called once each time cnts.cur{objs,bytes} drop to 0, but this
     *   threshold can be hit again before the first consumer finishes
     *   executing prof_tctx_destroy().
     */
    uint64_t        tctx_uid;
    /* Linkage into gctx's tctxs. */
    rb_node(prof_tctx_t)    tctx_link;
    /*
     * True during prof_alloc_prep()..prof_malloc_sample_object(), prevents
     * sample vs destroy race.
     */
    bool            prepared;
    /* Current dump-related state, protected by gctx->lock. */
    prof_tctx_state_t    state;
    /*
     * Copy of cnts snapshotted during early dump phase, protected by
     * dump_mtx.
     */
    prof_cnt_t        dump_cnts;
};
typedef rb_tree(prof_tctx_t) prof_tctx_tree_t;
struct prof_gctx_s {
    /* Protects nlimbo, cnt_summed, and tctxs. */
    malloc_mutex_t        *lock;
    /*
     * Number of threads that currently cause this gctx to be in a state of
     * limbo due to one of:
     *   - Initializing this gctx.
     *   - Initializing per thread counters associated with this gctx.
     *   - Preparing to destroy this gctx.
     *   - Dumping a heap profile that includes this gctx.
     * nlimbo must be 1 (single destroyer) in order to safely destroy the
     * gctx.
     */
    unsigned        nlimbo;
    /*
     * Tree of profile counters, one for each thread that has allocated in
     * this context.
     */
    prof_tctx_tree_t    tctxs;
    /* Linkage for tree of contexts to be dumped. */
    rb_node(prof_gctx_t)    dump_link;
    /* Temporary storage for summation during dump. */
    prof_cnt_t        cnt_summed;
    /* Associated backtrace. */
    prof_bt_t        bt;
    /* Backtrace vector, variable size, referred to by bt. */
    void            *vec[1];
};
typedef rb_tree(prof_gctx_t) prof_gctx_tree_t;
struct prof_tdata_s {
    malloc_mutex_t        *lock;
    /* Monotonically increasing unique thread identifier. */
    uint64_t        thr_uid;
    /*
     * Monotonically increasing discriminator among tdata structures
     * associated with the same thr_uid.
     */
    uint64_t        thr_discrim;
    /* Included in heap profile dumps if non-NULL. */
    char            *thread_name;
    bool            attached;
    bool            expired;
    rb_node(prof_tdata_t)    tdata_link;
    /*
     * Counter used to initialize prof_tctx_t's tctx_uid.  No locking is
     * necessary when incrementing this field, because only one thread ever
     * does so.
     */
    uint64_t        tctx_uid_next;
    /*
     * Hash of (prof_bt_t *)-->(prof_tctx_t *).  Each thread tracks
     * backtraces for which it has non-zero allocation/deallocation counters
     * associated with thread-specific prof_tctx_t objects.  Other threads
     * may write to prof_tctx_t contents when freeing associated objects.
     */
    ckh_t            bt2tctx;
    /* Sampling state. */
    uint64_t        prng_state;
    /* State used to avoid dumping while operating on prof internals. */
    bool            enq;
    bool            enq_idump;
    bool            enq_gdump;
    /*
     * Set to true during an early dump phase for tdata's which are
     * currently being dumped.  New threads' tdata's have this initialized
     * to false so that they aren't accidentally included in later dump
     * phases.
     */
    bool            dumping;
    /*
     * True if profiling is active for this tdata's thread
     * (thread.prof.active mallctl).
     */
    bool            active;
    /* Temporary storage for summation during dump. */
    prof_cnt_t        cnt_summed;
    /* Backtrace vector, used for calls to prof_backtrace(). */
    void            *vec[PROF_BT_MAX];
};
typedef rb_tree(prof_tdata_t) prof_tdata_tree_t;
#endif /* JEMALLOC_INTERNAL_PROF_STRUCTS_H */
jemalloc/x64/include/jemalloc/internal/prof_types.h
New file
@@ -0,0 +1,56 @@
#ifndef JEMALLOC_INTERNAL_PROF_TYPES_H
#define JEMALLOC_INTERNAL_PROF_TYPES_H
typedef struct prof_bt_s prof_bt_t;
typedef struct prof_accum_s prof_accum_t;
typedef struct prof_cnt_s prof_cnt_t;
typedef struct prof_tctx_s prof_tctx_t;
typedef struct prof_gctx_s prof_gctx_t;
typedef struct prof_tdata_s prof_tdata_t;
/* Option defaults. */
#ifdef JEMALLOC_PROF
#  define PROF_PREFIX_DEFAULT        "jeprof"
#else
#  define PROF_PREFIX_DEFAULT        ""
#endif
#define LG_PROF_SAMPLE_DEFAULT        19
#define LG_PROF_INTERVAL_DEFAULT    -1
/*
 * Hard limit on stack backtrace depth.  The version of prof_backtrace() that
 * is based on __builtin_return_address() necessarily has a hard-coded number
 * of backtrace frame handlers, and should be kept in sync with this setting.
 */
#define PROF_BT_MAX            128
/* Initial hash table size. */
#define PROF_CKH_MINITEMS        64
/* Size of memory buffer to use when writing dump files. */
#define PROF_DUMP_BUFSIZE        65536
/* Size of stack-allocated buffer used by prof_printf(). */
#define PROF_PRINTF_BUFSIZE        128
/*
 * Number of mutexes shared among all gctx's.  No space is allocated for these
 * unless profiling is enabled, so it's okay to over-provision.
 */
#define PROF_NCTX_LOCKS            1024
/*
 * Number of mutexes shared among all tdata's.  No space is allocated for these
 * unless profiling is enabled, so it's okay to over-provision.
 */
#define PROF_NTDATA_LOCKS        256
/*
 * prof_tdata pointers close to NULL are used to encode state information that
 * is used for cleaning up during thread shutdown.
 */
#define PROF_TDATA_STATE_REINCARNATED    ((prof_tdata_t *)(uintptr_t)1)
#define PROF_TDATA_STATE_PURGATORY    ((prof_tdata_t *)(uintptr_t)2)
#define PROF_TDATA_STATE_MAX        PROF_TDATA_STATE_PURGATORY
#endif /* JEMALLOC_INTERNAL_PROF_TYPES_H */
jemalloc/x64/include/jemalloc/internal/public_namespace.h
New file
@@ -0,0 +1,21 @@
#define je_aligned_alloc JEMALLOC_N(aligned_alloc)
#define je_calloc JEMALLOC_N(calloc)
#define je_dallocx JEMALLOC_N(dallocx)
#define je_free JEMALLOC_N(free)
#define je_mallctl JEMALLOC_N(mallctl)
#define je_mallctlbymib JEMALLOC_N(mallctlbymib)
#define je_mallctlnametomib JEMALLOC_N(mallctlnametomib)
#define je_malloc JEMALLOC_N(malloc)
#define je_malloc_conf JEMALLOC_N(malloc_conf)
#define je_malloc_message JEMALLOC_N(malloc_message)
#define je_malloc_stats_print JEMALLOC_N(malloc_stats_print)
#define je_malloc_usable_size JEMALLOC_N(malloc_usable_size)
#define je_mallocx JEMALLOC_N(mallocx)
#define je_smallocx_0000000000000000000000000000000000000000 JEMALLOC_N(smallocx_0000000000000000000000000000000000000000)
#define je_nallocx JEMALLOC_N(nallocx)
#define je_posix_memalign JEMALLOC_N(posix_memalign)
#define je_rallocx JEMALLOC_N(rallocx)
#define je_realloc JEMALLOC_N(realloc)
#define je_sallocx JEMALLOC_N(sallocx)
#define je_sdallocx JEMALLOC_N(sdallocx)
#define je_xallocx JEMALLOC_N(xallocx)
jemalloc/x64/include/jemalloc/internal/public_namespace.sh
New file
@@ -0,0 +1,6 @@
#!/bin/sh
for nm in `cat $1` ; do
  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
  echo "#define je_${n} JEMALLOC_N(${n})"
done
jemalloc/x64/include/jemalloc/internal/public_symbols.txt
New file
@@ -0,0 +1,21 @@
aligned_alloc:je_aligned_alloc
calloc:je_calloc
dallocx:je_dallocx
free:je_free
mallctl:je_mallctl
mallctlbymib:je_mallctlbymib
mallctlnametomib:je_mallctlnametomib
malloc:je_malloc
malloc_conf:je_malloc_conf
malloc_message:je_malloc_message
malloc_stats_print:je_malloc_stats_print
malloc_usable_size:je_malloc_usable_size
mallocx:je_mallocx
smallocx_0000000000000000000000000000000000000000:je_smallocx_0000000000000000000000000000000000000000
nallocx:je_nallocx
posix_memalign:je_posix_memalign
rallocx:je_rallocx
realloc:je_realloc
sallocx:je_sallocx
sdallocx:je_sdallocx
xallocx:je_xallocx
jemalloc/x64/include/jemalloc/internal/public_unnamespace.h
New file
@@ -0,0 +1,21 @@
#undef je_aligned_alloc
#undef je_calloc
#undef je_dallocx
#undef je_free
#undef je_mallctl
#undef je_mallctlbymib
#undef je_mallctlnametomib
#undef je_malloc
#undef je_malloc_conf
#undef je_malloc_message
#undef je_malloc_stats_print
#undef je_malloc_usable_size
#undef je_mallocx
#undef je_smallocx_0000000000000000000000000000000000000000
#undef je_nallocx
#undef je_posix_memalign
#undef je_rallocx
#undef je_realloc
#undef je_sallocx
#undef je_sdallocx
#undef je_xallocx
jemalloc/x64/include/jemalloc/internal/public_unnamespace.sh
New file
@@ -0,0 +1,6 @@
#!/bin/sh
for nm in `cat $1` ; do
  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
  echo "#undef je_${n}"
done
jemalloc/x64/include/jemalloc/internal/ql.h
New file
@@ -0,0 +1,88 @@
#ifndef JEMALLOC_INTERNAL_QL_H
#define JEMALLOC_INTERNAL_QL_H
#include "jemalloc/internal/qr.h"
/* List definitions. */
#define ql_head(a_type)                            \
struct {                                \
    a_type *qlh_first;                        \
}
#define ql_head_initializer(a_head) {NULL}
#define ql_elm(a_type)    qr(a_type)
/* List functions. */
#define ql_new(a_head) do {                        \
    (a_head)->qlh_first = NULL;                    \
} while (0)
#define ql_elm_new(a_elm, a_field) qr_new((a_elm), a_field)
#define ql_first(a_head) ((a_head)->qlh_first)
#define ql_last(a_head, a_field)                    \
    ((ql_first(a_head) != NULL)                    \
        ? qr_prev(ql_first(a_head), a_field) : NULL)
#define ql_next(a_head, a_elm, a_field)                    \
    ((ql_last(a_head, a_field) != (a_elm))                \
        ? qr_next((a_elm), a_field)    : NULL)
#define ql_prev(a_head, a_elm, a_field)                    \
    ((ql_first(a_head) != (a_elm)) ? qr_prev((a_elm), a_field)    \
                       : NULL)
#define ql_before_insert(a_head, a_qlelm, a_elm, a_field) do {        \
    qr_before_insert((a_qlelm), (a_elm), a_field);            \
    if (ql_first(a_head) == (a_qlelm)) {                \
        ql_first(a_head) = (a_elm);                \
    }                                \
} while (0)
#define ql_after_insert(a_qlelm, a_elm, a_field)            \
    qr_after_insert((a_qlelm), (a_elm), a_field)
#define ql_head_insert(a_head, a_elm, a_field) do {            \
    if (ql_first(a_head) != NULL) {                    \
        qr_before_insert(ql_first(a_head), (a_elm), a_field);    \
    }                                \
    ql_first(a_head) = (a_elm);                    \
} while (0)
#define ql_tail_insert(a_head, a_elm, a_field) do {            \
    if (ql_first(a_head) != NULL) {                    \
        qr_before_insert(ql_first(a_head), (a_elm), a_field);    \
    }                                \
    ql_first(a_head) = qr_next((a_elm), a_field);            \
} while (0)
#define ql_remove(a_head, a_elm, a_field) do {                \
    if (ql_first(a_head) == (a_elm)) {                \
        ql_first(a_head) = qr_next(ql_first(a_head), a_field);    \
    }                                \
    if (ql_first(a_head) != (a_elm)) {                \
        qr_remove((a_elm), a_field);                \
    } else {                            \
        ql_first(a_head) = NULL;                \
    }                                \
} while (0)
#define ql_head_remove(a_head, a_type, a_field) do {            \
    a_type *t = ql_first(a_head);                    \
    ql_remove((a_head), t, a_field);                \
} while (0)
#define ql_tail_remove(a_head, a_type, a_field) do {            \
    a_type *t = ql_last(a_head, a_field);                \
    ql_remove((a_head), t, a_field);                \
} while (0)
#define ql_foreach(a_var, a_head, a_field)                \
    qr_foreach((a_var), ql_first(a_head), a_field)
#define ql_reverse_foreach(a_var, a_head, a_field)            \
    qr_reverse_foreach((a_var), ql_first(a_head), a_field)
#endif /* JEMALLOC_INTERNAL_QL_H */
jemalloc/x64/include/jemalloc/internal/qr.h
New file
@@ -0,0 +1,72 @@
#ifndef JEMALLOC_INTERNAL_QR_H
#define JEMALLOC_INTERNAL_QR_H
/* Ring definitions. */
#define qr(a_type)                            \
struct {                                \
    a_type    *qre_next;                        \
    a_type    *qre_prev;                        \
}
/* Ring functions. */
#define qr_new(a_qr, a_field) do {                    \
    (a_qr)->a_field.qre_next = (a_qr);                \
    (a_qr)->a_field.qre_prev = (a_qr);                \
} while (0)
#define qr_next(a_qr, a_field) ((a_qr)->a_field.qre_next)
#define qr_prev(a_qr, a_field) ((a_qr)->a_field.qre_prev)
#define qr_before_insert(a_qrelm, a_qr, a_field) do {            \
    (a_qr)->a_field.qre_prev = (a_qrelm)->a_field.qre_prev;        \
    (a_qr)->a_field.qre_next = (a_qrelm);                \
    (a_qr)->a_field.qre_prev->a_field.qre_next = (a_qr);        \
    (a_qrelm)->a_field.qre_prev = (a_qr);                \
} while (0)
#define qr_after_insert(a_qrelm, a_qr, a_field) do {            \
    (a_qr)->a_field.qre_next = (a_qrelm)->a_field.qre_next;        \
    (a_qr)->a_field.qre_prev = (a_qrelm);                \
    (a_qr)->a_field.qre_next->a_field.qre_prev = (a_qr);        \
    (a_qrelm)->a_field.qre_next = (a_qr);                \
} while (0)
#define qr_meld(a_qr_a, a_qr_b, a_type, a_field) do {            \
    a_type *t;                            \
    (a_qr_a)->a_field.qre_prev->a_field.qre_next = (a_qr_b);    \
    (a_qr_b)->a_field.qre_prev->a_field.qre_next = (a_qr_a);    \
    t = (a_qr_a)->a_field.qre_prev;                    \
    (a_qr_a)->a_field.qre_prev = (a_qr_b)->a_field.qre_prev;    \
    (a_qr_b)->a_field.qre_prev = t;                    \
} while (0)
/*
 * qr_meld() and qr_split() are functionally equivalent, so there's no need to
 * have two copies of the code.
 */
#define qr_split(a_qr_a, a_qr_b, a_type, a_field)            \
    qr_meld((a_qr_a), (a_qr_b), a_type, a_field)
#define qr_remove(a_qr, a_field) do {                    \
    (a_qr)->a_field.qre_prev->a_field.qre_next            \
        = (a_qr)->a_field.qre_next;                    \
    (a_qr)->a_field.qre_next->a_field.qre_prev            \
        = (a_qr)->a_field.qre_prev;                    \
    (a_qr)->a_field.qre_next = (a_qr);                \
    (a_qr)->a_field.qre_prev = (a_qr);                \
} while (0)
#define qr_foreach(var, a_qr, a_field)                    \
    for ((var) = (a_qr);                        \
        (var) != NULL;                        \
        (var) = (((var)->a_field.qre_next != (a_qr))        \
        ? (var)->a_field.qre_next : NULL))
#define qr_reverse_foreach(var, a_qr, a_field)                \
    for ((var) = ((a_qr) != NULL) ? qr_prev(a_qr, a_field) : NULL;    \
        (var) != NULL;                        \
        (var) = (((var) != (a_qr))                    \
        ? (var)->a_field.qre_prev : NULL))
#endif /* JEMALLOC_INTERNAL_QR_H */
jemalloc/x64/include/jemalloc/internal/quantum.h
New file
@@ -0,0 +1,77 @@
#ifndef JEMALLOC_INTERNAL_QUANTUM_H
#define JEMALLOC_INTERNAL_QUANTUM_H
/*
 * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
 * classes).
 */
#ifndef LG_QUANTUM
#  if (defined(__i386__) || defined(_M_IX86))
#    define LG_QUANTUM        4
#  endif
#  ifdef __ia64__
#    define LG_QUANTUM        4
#  endif
#  ifdef __alpha__
#    define LG_QUANTUM        4
#  endif
#  if (defined(__sparc64__) || defined(__sparcv9) || defined(__sparc_v9__))
#    define LG_QUANTUM        4
#  endif
#  if (defined(__amd64__) || defined(__x86_64__) || defined(_M_X64))
#    define LG_QUANTUM        4
#  endif
#  ifdef __arm__
#    define LG_QUANTUM        3
#  endif
#  ifdef __aarch64__
#    define LG_QUANTUM        4
#  endif
#  ifdef __hppa__
#    define LG_QUANTUM        4
#  endif
#  ifdef __m68k__
#    define LG_QUANTUM        3
#  endif
#  ifdef __mips__
#    define LG_QUANTUM        3
#  endif
#  ifdef __nios2__
#    define LG_QUANTUM        3
#  endif
#  ifdef __or1k__
#    define LG_QUANTUM        3
#  endif
#  ifdef __powerpc__
#    define LG_QUANTUM        4
#  endif
#  if defined(__riscv) || defined(__riscv__)
#    define LG_QUANTUM        4
#  endif
#  ifdef __s390__
#    define LG_QUANTUM        4
#  endif
#  if (defined (__SH3E__) || defined(__SH4_SINGLE__) || defined(__SH4__) || \
    defined(__SH4_SINGLE_ONLY__))
#    define LG_QUANTUM        4
#  endif
#  ifdef __tile__
#    define LG_QUANTUM        4
#  endif
#  ifdef __le32__
#    define LG_QUANTUM        4
#  endif
#  ifndef LG_QUANTUM
#    error "Unknown minimum alignment for architecture; specify via "
     "--with-lg-quantum"
#  endif
#endif
#define QUANTUM            ((size_t)(1U << LG_QUANTUM))
#define QUANTUM_MASK        (QUANTUM - 1)
/* Return the smallest quantum multiple that is >= a. */
#define QUANTUM_CEILING(a)                        \
    (((a) + QUANTUM_MASK) & ~QUANTUM_MASK)
#endif /* JEMALLOC_INTERNAL_QUANTUM_H */
jemalloc/x64/include/jemalloc/internal/rb.h
New file
@@ -0,0 +1,1006 @@
/*-
 *******************************************************************************
 *
 * cpp macro implementation of left-leaning 2-3 red-black trees.  Parent
 * pointers are not used, and color bits are stored in the least significant
 * bit of right-child pointers (if RB_COMPACT is defined), thus making node
 * linkage as compact as is possible for red-black trees.
 *
 * Usage:
 *
 *   #include <stdint.h>
 *   #include <stdbool.h>
 *   #define NDEBUG // (Optional, see assert(3).)
 *   #include <assert.h>
 *   #define RB_COMPACT // (Optional, embed color bits in right-child pointers.)
 *   #include <rb.h>
 *   ...
 *
 *******************************************************************************
 */
#ifndef RB_H_
#define RB_H_
#ifndef __PGI
#define RB_COMPACT
#endif
#ifdef RB_COMPACT
/* Node structure. */
#define rb_node(a_type)                            \
struct {                                \
    a_type *rbn_left;                            \
    a_type *rbn_right_red;                        \
}
#else
#define rb_node(a_type)                            \
struct {                                \
    a_type *rbn_left;                            \
    a_type *rbn_right;                            \
    bool rbn_red;                            \
}
#endif
/* Root structure. */
#define rb_tree(a_type)                            \
struct {                                \
    a_type *rbt_root;                            \
}
/* Left accessors. */
#define rbtn_left_get(a_type, a_field, a_node)                \
    ((a_node)->a_field.rbn_left)
#define rbtn_left_set(a_type, a_field, a_node, a_left) do {        \
    (a_node)->a_field.rbn_left = a_left;                \
} while (0)
#ifdef RB_COMPACT
/* Right accessors. */
#define rbtn_right_get(a_type, a_field, a_node)                \
    ((a_type *) (((intptr_t) (a_node)->a_field.rbn_right_red)        \
      & ((ssize_t)-2)))
#define rbtn_right_set(a_type, a_field, a_node, a_right) do {        \
    (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) a_right)    \
      | (((uintptr_t) (a_node)->a_field.rbn_right_red) & ((size_t)1)));    \
} while (0)
/* Color accessors. */
#define rbtn_red_get(a_type, a_field, a_node)                \
    ((bool) (((uintptr_t) (a_node)->a_field.rbn_right_red)        \
      & ((size_t)1)))
#define rbtn_color_set(a_type, a_field, a_node, a_red) do {        \
    (a_node)->a_field.rbn_right_red = (a_type *) ((((intptr_t)        \
      (a_node)->a_field.rbn_right_red) & ((ssize_t)-2))            \
      | ((ssize_t)a_red));                        \
} while (0)
#define rbtn_red_set(a_type, a_field, a_node) do {            \
    (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t)        \
      (a_node)->a_field.rbn_right_red) | ((size_t)1));            \
} while (0)
#define rbtn_black_set(a_type, a_field, a_node) do {            \
    (a_node)->a_field.rbn_right_red = (a_type *) (((intptr_t)        \
      (a_node)->a_field.rbn_right_red) & ((ssize_t)-2));        \
} while (0)
/* Node initializer. */
#define rbt_node_new(a_type, a_field, a_rbt, a_node) do {        \
    /* Bookkeeping bit cannot be used by node pointer. */        \
    assert(((uintptr_t)(a_node) & 0x1) == 0);                \
    rbtn_left_set(a_type, a_field, (a_node), NULL);    \
    rbtn_right_set(a_type, a_field, (a_node), NULL);    \
    rbtn_red_set(a_type, a_field, (a_node));                \
} while (0)
#else
/* Right accessors. */
#define rbtn_right_get(a_type, a_field, a_node)                \
    ((a_node)->a_field.rbn_right)
#define rbtn_right_set(a_type, a_field, a_node, a_right) do {        \
    (a_node)->a_field.rbn_right = a_right;                \
} while (0)
/* Color accessors. */
#define rbtn_red_get(a_type, a_field, a_node)                \
    ((a_node)->a_field.rbn_red)
#define rbtn_color_set(a_type, a_field, a_node, a_red) do {        \
    (a_node)->a_field.rbn_red = (a_red);                \
} while (0)
#define rbtn_red_set(a_type, a_field, a_node) do {            \
    (a_node)->a_field.rbn_red = true;                    \
} while (0)
#define rbtn_black_set(a_type, a_field, a_node) do {            \
    (a_node)->a_field.rbn_red = false;                    \
} while (0)
/* Node initializer. */
#define rbt_node_new(a_type, a_field, a_rbt, a_node) do {        \
    rbtn_left_set(a_type, a_field, (a_node), NULL);    \
    rbtn_right_set(a_type, a_field, (a_node), NULL);    \
    rbtn_red_set(a_type, a_field, (a_node));                \
} while (0)
#endif
/* Tree initializer. */
#define rb_new(a_type, a_field, a_rbt) do {                \
    (a_rbt)->rbt_root = NULL;                        \
} while (0)
/* Internal utility macros. */
#define rbtn_first(a_type, a_field, a_rbt, a_root, r_node) do {        \
    (r_node) = (a_root);                        \
    if ((r_node) != NULL) {                        \
    for (;                                \
      rbtn_left_get(a_type, a_field, (r_node)) != NULL;        \
      (r_node) = rbtn_left_get(a_type, a_field, (r_node))) {    \
    }                                \
    }                                    \
} while (0)
#define rbtn_last(a_type, a_field, a_rbt, a_root, r_node) do {        \
    (r_node) = (a_root);                        \
    if ((r_node) != NULL) {                        \
    for (; rbtn_right_get(a_type, a_field, (r_node)) != NULL;    \
      (r_node) = rbtn_right_get(a_type, a_field, (r_node))) {    \
    }                                \
    }                                    \
} while (0)
#define rbtn_rotate_left(a_type, a_field, a_node, r_node) do {        \
    (r_node) = rbtn_right_get(a_type, a_field, (a_node));        \
    rbtn_right_set(a_type, a_field, (a_node),                \
      rbtn_left_get(a_type, a_field, (r_node)));            \
    rbtn_left_set(a_type, a_field, (r_node), (a_node));            \
} while (0)
#define rbtn_rotate_right(a_type, a_field, a_node, r_node) do {        \
    (r_node) = rbtn_left_get(a_type, a_field, (a_node));        \
    rbtn_left_set(a_type, a_field, (a_node),                \
      rbtn_right_get(a_type, a_field, (r_node)));            \
    rbtn_right_set(a_type, a_field, (r_node), (a_node));        \
} while (0)
/*
 * The rb_proto() macro generates function prototypes that correspond to the
 * functions generated by an equivalently parameterized call to rb_gen().
 */
#define rb_proto(a_attr, a_prefix, a_rbt_type, a_type)            \
a_attr void                                \
a_prefix##new(a_rbt_type *rbtree);                    \
a_attr bool                                \
a_prefix##empty(a_rbt_type *rbtree);                    \
a_attr a_type *                                \
a_prefix##first(a_rbt_type *rbtree);                    \
a_attr a_type *                                \
a_prefix##last(a_rbt_type *rbtree);                    \
a_attr a_type *                                \
a_prefix##next(a_rbt_type *rbtree, a_type *node);            \
a_attr a_type *                                \
a_prefix##prev(a_rbt_type *rbtree, a_type *node);            \
a_attr a_type *                                \
a_prefix##search(a_rbt_type *rbtree, const a_type *key);        \
a_attr a_type *                                \
a_prefix##nsearch(a_rbt_type *rbtree, const a_type *key);        \
a_attr a_type *                                \
a_prefix##psearch(a_rbt_type *rbtree, const a_type *key);        \
a_attr void                                \
a_prefix##insert(a_rbt_type *rbtree, a_type *node);            \
a_attr void                                \
a_prefix##remove(a_rbt_type *rbtree, a_type *node);            \
a_attr a_type *                                \
a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)(    \
  a_rbt_type *, a_type *, void *), void *arg);                \
a_attr a_type *                                \
a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start,        \
  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg);        \
a_attr void                                \
a_prefix##destroy(a_rbt_type *rbtree, void (*cb)(a_type *, void *),    \
  void *arg);
/*
 * The rb_gen() macro generates a type-specific red-black tree implementation,
 * based on the above cpp macros.
 *
 * Arguments:
 *
 *   a_attr    : Function attribute for generated functions (ex: static).
 *   a_prefix  : Prefix for generated functions (ex: ex_).
 *   a_rb_type : Type for red-black tree data structure (ex: ex_t).
 *   a_type    : Type for red-black tree node data structure (ex: ex_node_t).
 *   a_field   : Name of red-black tree node linkage (ex: ex_link).
 *   a_cmp     : Node comparison function name, with the following prototype:
 *                 int (a_cmp *)(a_type *a_node, a_type *a_other);
 *                                       ^^^^^^
 *                                    or a_key
 *               Interpretation of comparison function return values:
 *                 -1 : a_node <  a_other
 *                  0 : a_node == a_other
 *                  1 : a_node >  a_other
 *               In all cases, the a_node or a_key macro argument is the first
 *               argument to the comparison function, which makes it possible
 *               to write comparison functions that treat the first argument
 *               specially.
 *
 * Assuming the following setup:
 *
 *   typedef struct ex_node_s ex_node_t;
 *   struct ex_node_s {
 *       rb_node(ex_node_t) ex_link;
 *   };
 *   typedef rb_tree(ex_node_t) ex_t;
 *   rb_gen(static, ex_, ex_t, ex_node_t, ex_link, ex_cmp)
 *
 * The following API is generated:
 *
 *   static void
 *   ex_new(ex_t *tree);
 *       Description: Initialize a red-black tree structure.
 *       Args:
 *         tree: Pointer to an uninitialized red-black tree object.
 *
 *   static bool
 *   ex_empty(ex_t *tree);
 *       Description: Determine whether tree is empty.
 *       Args:
 *         tree: Pointer to an initialized red-black tree object.
 *       Ret: True if tree is empty, false otherwise.
 *
 *   static ex_node_t *
 *   ex_first(ex_t *tree);
 *   static ex_node_t *
 *   ex_last(ex_t *tree);
 *       Description: Get the first/last node in tree.
 *       Args:
 *         tree: Pointer to an initialized red-black tree object.
 *       Ret: First/last node in tree, or NULL if tree is empty.
 *
 *   static ex_node_t *
 *   ex_next(ex_t *tree, ex_node_t *node);
 *   static ex_node_t *
 *   ex_prev(ex_t *tree, ex_node_t *node);
 *       Description: Get node's successor/predecessor.
 *       Args:
 *         tree: Pointer to an initialized red-black tree object.
 *         node: A node in tree.
 *       Ret: node's successor/predecessor in tree, or NULL if node is
 *            last/first.
 *
 *   static ex_node_t *
 *   ex_search(ex_t *tree, const ex_node_t *key);
 *       Description: Search for node that matches key.
 *       Args:
 *         tree: Pointer to an initialized red-black tree object.
 *         key : Search key.
 *       Ret: Node in tree that matches key, or NULL if no match.
 *
 *   static ex_node_t *
 *   ex_nsearch(ex_t *tree, const ex_node_t *key);
 *   static ex_node_t *
 *   ex_psearch(ex_t *tree, const ex_node_t *key);
 *       Description: Search for node that matches key.  If no match is found,
 *                    return what would be key's successor/predecessor, were
 *                    key in tree.
 *       Args:
 *         tree: Pointer to an initialized red-black tree object.
 *         key : Search key.
 *       Ret: Node in tree that matches key, or if no match, hypothetical node's
 *            successor/predecessor (NULL if no successor/predecessor).
 *
 *   static void
 *   ex_insert(ex_t *tree, ex_node_t *node);
 *       Description: Insert node into tree.
 *       Args:
 *         tree: Pointer to an initialized red-black tree object.
 *         node: Node to be inserted into tree.
 *
 *   static void
 *   ex_remove(ex_t *tree, ex_node_t *node);
 *       Description: Remove node from tree.
 *       Args:
 *         tree: Pointer to an initialized red-black tree object.
 *         node: Node in tree to be removed.
 *
 *   static ex_node_t *
 *   ex_iter(ex_t *tree, ex_node_t *start, ex_node_t *(*cb)(ex_t *,
 *     ex_node_t *, void *), void *arg);
 *   static ex_node_t *
 *   ex_reverse_iter(ex_t *tree, ex_node_t *start, ex_node *(*cb)(ex_t *,
 *     ex_node_t *, void *), void *arg);
 *       Description: Iterate forward/backward over tree, starting at node.  If
 *                    tree is modified, iteration must be immediately
 *                    terminated by the callback function that causes the
 *                    modification.
 *       Args:
 *         tree : Pointer to an initialized red-black tree object.
 *         start: Node at which to start iteration, or NULL to start at
 *                first/last node.
 *         cb   : Callback function, which is called for each node during
 *                iteration.  Under normal circumstances the callback function
 *                should return NULL, which causes iteration to continue.  If a
 *                callback function returns non-NULL, iteration is immediately
 *                terminated and the non-NULL return value is returned by the
 *                iterator.  This is useful for re-starting iteration after
 *                modifying tree.
 *         arg  : Opaque pointer passed to cb().
 *       Ret: NULL if iteration completed, or the non-NULL callback return value
 *            that caused termination of the iteration.
 *
 *   static void
 *   ex_destroy(ex_t *tree, void (*cb)(ex_node_t *, void *), void *arg);
 *       Description: Iterate over the tree with post-order traversal, remove
 *                    each node, and run the callback if non-null.  This is
 *                    used for destroying a tree without paying the cost to
 *                    rebalance it.  The tree must not be otherwise altered
 *                    during traversal.
 *       Args:
 *         tree: Pointer to an initialized red-black tree object.
 *         cb  : Callback function, which, if non-null, is called for each node
 *               during iteration.  There is no way to stop iteration once it
 *               has begun.
 *         arg : Opaque pointer passed to cb().
 */
#define rb_gen(a_attr, a_prefix, a_rbt_type, a_type, a_field, a_cmp)    \
a_attr void                                \
a_prefix##new(a_rbt_type *rbtree) {                    \
    rb_new(a_type, a_field, rbtree);                    \
}                                    \
a_attr bool                                \
a_prefix##empty(a_rbt_type *rbtree) {                    \
    return (rbtree->rbt_root == NULL);                    \
}                                    \
a_attr a_type *                                \
a_prefix##first(a_rbt_type *rbtree) {                    \
    a_type *ret;                            \
    rbtn_first(a_type, a_field, rbtree, rbtree->rbt_root, ret);        \
    return ret;                                \
}                                    \
a_attr a_type *                                \
a_prefix##last(a_rbt_type *rbtree) {                    \
    a_type *ret;                            \
    rbtn_last(a_type, a_field, rbtree, rbtree->rbt_root, ret);        \
    return ret;                                \
}                                    \
a_attr a_type *                                \
a_prefix##next(a_rbt_type *rbtree, a_type *node) {            \
    a_type *ret;                            \
    if (rbtn_right_get(a_type, a_field, node) != NULL) {        \
    rbtn_first(a_type, a_field, rbtree, rbtn_right_get(a_type,    \
      a_field, node), ret);                        \
    } else {                                \
    a_type *tnode = rbtree->rbt_root;                \
    assert(tnode != NULL);                        \
    ret = NULL;                            \
    while (true) {                            \
        int cmp = (a_cmp)(node, tnode);                \
        if (cmp < 0) {                        \
        ret = tnode;                        \
        tnode = rbtn_left_get(a_type, a_field, tnode);        \
        } else if (cmp > 0) {                    \
        tnode = rbtn_right_get(a_type, a_field, tnode);        \
        } else {                            \
        break;                            \
        }                                \
        assert(tnode != NULL);                    \
    }                                \
    }                                    \
    return ret;                                \
}                                    \
a_attr a_type *                                \
a_prefix##prev(a_rbt_type *rbtree, a_type *node) {            \
    a_type *ret;                            \
    if (rbtn_left_get(a_type, a_field, node) != NULL) {            \
    rbtn_last(a_type, a_field, rbtree, rbtn_left_get(a_type,    \
      a_field, node), ret);                        \
    } else {                                \
    a_type *tnode = rbtree->rbt_root;                \
    assert(tnode != NULL);                        \
    ret = NULL;                            \
    while (true) {                            \
        int cmp = (a_cmp)(node, tnode);                \
        if (cmp < 0) {                        \
        tnode = rbtn_left_get(a_type, a_field, tnode);        \
        } else if (cmp > 0) {                    \
        ret = tnode;                        \
        tnode = rbtn_right_get(a_type, a_field, tnode);        \
        } else {                            \
        break;                            \
        }                                \
        assert(tnode != NULL);                    \
    }                                \
    }                                    \
    return ret;                                \
}                                    \
a_attr a_type *                                \
a_prefix##search(a_rbt_type *rbtree, const a_type *key) {        \
    a_type *ret;                            \
    int cmp;                                \
    ret = rbtree->rbt_root;                        \
    while (ret != NULL                            \
      && (cmp = (a_cmp)(key, ret)) != 0) {                \
    if (cmp < 0) {                            \
        ret = rbtn_left_get(a_type, a_field, ret);            \
    } else {                            \
        ret = rbtn_right_get(a_type, a_field, ret);            \
    }                                \
    }                                    \
    return ret;                                \
}                                    \
a_attr a_type *                                \
a_prefix##nsearch(a_rbt_type *rbtree, const a_type *key) {        \
    a_type *ret;                            \
    a_type *tnode = rbtree->rbt_root;                    \
    ret = NULL;                                \
    while (tnode != NULL) {                        \
    int cmp = (a_cmp)(key, tnode);                    \
    if (cmp < 0) {                            \
        ret = tnode;                        \
        tnode = rbtn_left_get(a_type, a_field, tnode);        \
    } else if (cmp > 0) {                        \
        tnode = rbtn_right_get(a_type, a_field, tnode);        \
    } else {                            \
        ret = tnode;                        \
        break;                            \
    }                                \
    }                                    \
    return ret;                                \
}                                    \
a_attr a_type *                                \
a_prefix##psearch(a_rbt_type *rbtree, const a_type *key) {        \
    a_type *ret;                            \
    a_type *tnode = rbtree->rbt_root;                    \
    ret = NULL;                                \
    while (tnode != NULL) {                        \
    int cmp = (a_cmp)(key, tnode);                    \
    if (cmp < 0) {                            \
        tnode = rbtn_left_get(a_type, a_field, tnode);        \
    } else if (cmp > 0) {                        \
        ret = tnode;                        \
        tnode = rbtn_right_get(a_type, a_field, tnode);        \
    } else {                            \
        ret = tnode;                        \
        break;                            \
    }                                \
    }                                    \
    return ret;                                \
}                                    \
a_attr void                                \
a_prefix##insert(a_rbt_type *rbtree, a_type *node) {            \
    struct {                                \
    a_type *node;                            \
    int cmp;                            \
    } path[sizeof(void *) << 4], *pathp;                \
    rbt_node_new(a_type, a_field, rbtree, node);            \
    /* Wind. */                                \
    path->node = rbtree->rbt_root;                    \
    for (pathp = path; pathp->node != NULL; pathp++) {            \
    int cmp = pathp->cmp = a_cmp(node, pathp->node);        \
    assert(cmp != 0);                        \
    if (cmp < 0) {                            \
        pathp[1].node = rbtn_left_get(a_type, a_field,        \
          pathp->node);                        \
    } else {                            \
        pathp[1].node = rbtn_right_get(a_type, a_field,        \
          pathp->node);                        \
    }                                \
    }                                    \
    pathp->node = node;                            \
    /* Unwind. */                            \
    for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) {    \
    a_type *cnode = pathp->node;                    \
    if (pathp->cmp < 0) {                        \
        a_type *left = pathp[1].node;                \
        rbtn_left_set(a_type, a_field, cnode, left);        \
        if (rbtn_red_get(a_type, a_field, left)) {            \
        a_type *leftleft = rbtn_left_get(a_type, a_field, left);\
        if (leftleft != NULL && rbtn_red_get(a_type, a_field,    \
          leftleft)) {                        \
            /* Fix up 4-node. */                \
            a_type *tnode;                    \
            rbtn_black_set(a_type, a_field, leftleft);        \
            rbtn_rotate_right(a_type, a_field, cnode, tnode);    \
            cnode = tnode;                    \
        }                            \
        } else {                            \
        return;                            \
        }                                \
    } else {                            \
        a_type *right = pathp[1].node;                \
        rbtn_right_set(a_type, a_field, cnode, right);        \
        if (rbtn_red_get(a_type, a_field, right)) {            \
        a_type *left = rbtn_left_get(a_type, a_field, cnode);    \
        if (left != NULL && rbtn_red_get(a_type, a_field,    \
          left)) {                        \
            /* Split 4-node. */                    \
            rbtn_black_set(a_type, a_field, left);        \
            rbtn_black_set(a_type, a_field, right);        \
            rbtn_red_set(a_type, a_field, cnode);        \
        } else {                        \
            /* Lean left. */                    \
            a_type *tnode;                    \
            bool tred = rbtn_red_get(a_type, a_field, cnode);    \
            rbtn_rotate_left(a_type, a_field, cnode, tnode);    \
            rbtn_color_set(a_type, a_field, tnode, tred);    \
            rbtn_red_set(a_type, a_field, cnode);        \
            cnode = tnode;                    \
        }                            \
        } else {                            \
        return;                            \
        }                                \
    }                                \
    pathp->node = cnode;                        \
    }                                    \
    /* Set root, and make it black. */                    \
    rbtree->rbt_root = path->node;                    \
    rbtn_black_set(a_type, a_field, rbtree->rbt_root);            \
}                                    \
a_attr void                                \
a_prefix##remove(a_rbt_type *rbtree, a_type *node) {            \
    struct {                                \
    a_type *node;                            \
    int cmp;                            \
    } *pathp, *nodep, path[sizeof(void *) << 4];            \
    /* Wind. */                                \
    nodep = NULL; /* Silence compiler warning. */            \
    path->node = rbtree->rbt_root;                    \
    for (pathp = path; pathp->node != NULL; pathp++) {            \
    int cmp = pathp->cmp = a_cmp(node, pathp->node);        \
    if (cmp < 0) {                            \
        pathp[1].node = rbtn_left_get(a_type, a_field,        \
          pathp->node);                        \
    } else {                            \
        pathp[1].node = rbtn_right_get(a_type, a_field,        \
          pathp->node);                        \
        if (cmp == 0) {                        \
            /* Find node's successor, in preparation for swap. */    \
        pathp->cmp = 1;                        \
        nodep = pathp;                        \
        for (pathp++; pathp->node != NULL; pathp++) {        \
            pathp->cmp = -1;                    \
            pathp[1].node = rbtn_left_get(a_type, a_field,    \
              pathp->node);                    \
        }                            \
        break;                            \
        }                                \
    }                                \
    }                                    \
    assert(nodep->node == node);                    \
    pathp--;                                \
    if (pathp->node != node) {                        \
    /* Swap node with its successor. */                \
    bool tred = rbtn_red_get(a_type, a_field, pathp->node);        \
    rbtn_color_set(a_type, a_field, pathp->node,            \
      rbtn_red_get(a_type, a_field, node));                \
    rbtn_left_set(a_type, a_field, pathp->node,            \
      rbtn_left_get(a_type, a_field, node));            \
    /* If node's successor is its right child, the following code */\
    /* will do the wrong thing for the right child pointer.       */\
    /* However, it doesn't matter, because the pointer will be    */\
    /* properly set when the successor is pruned.                 */\
    rbtn_right_set(a_type, a_field, pathp->node,            \
      rbtn_right_get(a_type, a_field, node));            \
    rbtn_color_set(a_type, a_field, node, tred);            \
    /* The pruned leaf node's child pointers are never accessed   */\
    /* again, so don't bother setting them to nil.                */\
    nodep->node = pathp->node;                    \
    pathp->node = node;                        \
    if (nodep == path) {                        \
        rbtree->rbt_root = nodep->node;                \
    } else {                            \
        if (nodep[-1].cmp < 0) {                    \
        rbtn_left_set(a_type, a_field, nodep[-1].node,        \
          nodep->node);                        \
        } else {                            \
        rbtn_right_set(a_type, a_field, nodep[-1].node,        \
          nodep->node);                        \
        }                                \
    }                                \
    } else {                                \
    a_type *left = rbtn_left_get(a_type, a_field, node);        \
    if (left != NULL) {                        \
        /* node has no successor, but it has a left child.        */\
        /* Splice node out, without losing the left child.        */\
        assert(!rbtn_red_get(a_type, a_field, node));        \
        assert(rbtn_red_get(a_type, a_field, left));        \
        rbtn_black_set(a_type, a_field, left);            \
        if (pathp == path) {                    \
        rbtree->rbt_root = left;                \
        } else {                            \
        if (pathp[-1].cmp < 0) {                \
            rbtn_left_set(a_type, a_field, pathp[-1].node,    \
              left);                        \
        } else {                        \
            rbtn_right_set(a_type, a_field, pathp[-1].node,    \
              left);                        \
        }                            \
        }                                \
        return;                            \
    } else if (pathp == path) {                    \
        /* The tree only contained one node. */            \
        rbtree->rbt_root = NULL;                    \
        return;                            \
    }                                \
    }                                    \
    if (rbtn_red_get(a_type, a_field, pathp->node)) {            \
    /* Prune red node, which requires no fixup. */            \
    assert(pathp[-1].cmp < 0);                    \
    rbtn_left_set(a_type, a_field, pathp[-1].node, NULL);        \
    return;                                \
    }                                    \
    /* The node to be pruned is black, so unwind until balance is     */\
    /* restored.                                                      */\
    pathp->node = NULL;                            \
    for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) {    \
    assert(pathp->cmp != 0);                    \
    if (pathp->cmp < 0) {                        \
        rbtn_left_set(a_type, a_field, pathp->node,            \
          pathp[1].node);                        \
        if (rbtn_red_get(a_type, a_field, pathp->node)) {        \
        a_type *right = rbtn_right_get(a_type, a_field,        \
          pathp->node);                        \
        a_type *rightleft = rbtn_left_get(a_type, a_field,    \
          right);                        \
        a_type *tnode;                        \
        if (rightleft != NULL && rbtn_red_get(a_type, a_field,    \
          rightleft)) {                        \
            /* In the following diagrams, ||, //, and \\      */\
            /* indicate the path to the removed node.         */\
            /*                                                */\
            /*      ||                                        */\
            /*    pathp(r)                                    */\
            /*  //        \                                   */\
            /* (b)        (b)                                 */\
            /*           /                                    */\
            /*          (r)                                   */\
            /*                                                */\
            rbtn_black_set(a_type, a_field, pathp->node);    \
            rbtn_rotate_right(a_type, a_field, right, tnode);    \
            rbtn_right_set(a_type, a_field, pathp->node, tnode);\
            rbtn_rotate_left(a_type, a_field, pathp->node,    \
              tnode);                        \
        } else {                        \
            /*      ||                                        */\
            /*    pathp(r)                                    */\
            /*  //        \                                   */\
            /* (b)        (b)                                 */\
            /*           /                                    */\
            /*          (b)                                   */\
            /*                                                */\
            rbtn_rotate_left(a_type, a_field, pathp->node,    \
              tnode);                        \
        }                            \
        /* Balance restored, but rotation modified subtree    */\
        /* root.                                              */\
        assert((uintptr_t)pathp > (uintptr_t)path);        \
        if (pathp[-1].cmp < 0) {                \
            rbtn_left_set(a_type, a_field, pathp[-1].node,    \
              tnode);                        \
        } else {                        \
            rbtn_right_set(a_type, a_field, pathp[-1].node,    \
              tnode);                        \
        }                            \
        return;                            \
        } else {                            \
        a_type *right = rbtn_right_get(a_type, a_field,        \
          pathp->node);                        \
        a_type *rightleft = rbtn_left_get(a_type, a_field,    \
          right);                        \
        if (rightleft != NULL && rbtn_red_get(a_type, a_field,    \
          rightleft)) {                        \
            /*      ||                                        */\
            /*    pathp(b)                                    */\
            /*  //        \                                   */\
            /* (b)        (b)                                 */\
            /*           /                                    */\
            /*          (r)                                   */\
            a_type *tnode;                    \
            rbtn_black_set(a_type, a_field, rightleft);        \
            rbtn_rotate_right(a_type, a_field, right, tnode);    \
            rbtn_right_set(a_type, a_field, pathp->node, tnode);\
            rbtn_rotate_left(a_type, a_field, pathp->node,    \
              tnode);                        \
            /* Balance restored, but rotation modified        */\
            /* subtree root, which may actually be the tree   */\
            /* root.                                          */\
            if (pathp == path) {                \
            /* Set root. */                    \
            rbtree->rbt_root = tnode;            \
            } else {                        \
            if (pathp[-1].cmp < 0) {            \
                rbtn_left_set(a_type, a_field,        \
                  pathp[-1].node, tnode);            \
            } else {                    \
                rbtn_right_set(a_type, a_field,        \
                  pathp[-1].node, tnode);            \
            }                        \
            }                            \
            return;                        \
        } else {                        \
            /*      ||                                        */\
            /*    pathp(b)                                    */\
            /*  //        \                                   */\
            /* (b)        (b)                                 */\
            /*           /                                    */\
            /*          (b)                                   */\
            a_type *tnode;                    \
            rbtn_red_set(a_type, a_field, pathp->node);        \
            rbtn_rotate_left(a_type, a_field, pathp->node,    \
              tnode);                        \
            pathp->node = tnode;                \
        }                            \
        }                                \
    } else {                            \
        a_type *left;                        \
        rbtn_right_set(a_type, a_field, pathp->node,        \
          pathp[1].node);                        \
        left = rbtn_left_get(a_type, a_field, pathp->node);        \
        if (rbtn_red_get(a_type, a_field, left)) {            \
        a_type *tnode;                        \
        a_type *leftright = rbtn_right_get(a_type, a_field,    \
          left);                        \
        a_type *leftrightleft = rbtn_left_get(a_type, a_field,    \
          leftright);                        \
        if (leftrightleft != NULL && rbtn_red_get(a_type,    \
          a_field, leftrightleft)) {                \
            /*      ||                                        */\
            /*    pathp(b)                                    */\
            /*   /        \\                                  */\
            /* (r)        (b)                                 */\
            /*   \                                            */\
            /*   (b)                                          */\
            /*   /                                            */\
            /* (r)                                            */\
            a_type *unode;                    \
            rbtn_black_set(a_type, a_field, leftrightleft);    \
            rbtn_rotate_right(a_type, a_field, pathp->node,    \
              unode);                        \
            rbtn_rotate_right(a_type, a_field, pathp->node,    \
              tnode);                        \
            rbtn_right_set(a_type, a_field, unode, tnode);    \
            rbtn_rotate_left(a_type, a_field, unode, tnode);    \
        } else {                        \
            /*      ||                                        */\
            /*    pathp(b)                                    */\
            /*   /        \\                                  */\
            /* (r)        (b)                                 */\
            /*   \                                            */\
            /*   (b)                                          */\
            /*   /                                            */\
            /* (b)                                            */\
            assert(leftright != NULL);                \
            rbtn_red_set(a_type, a_field, leftright);        \
            rbtn_rotate_right(a_type, a_field, pathp->node,    \
              tnode);                        \
            rbtn_black_set(a_type, a_field, tnode);        \
        }                            \
        /* Balance restored, but rotation modified subtree    */\
        /* root, which may actually be the tree root.         */\
        if (pathp == path) {                    \
            /* Set root. */                    \
            rbtree->rbt_root = tnode;                \
        } else {                        \
            if (pathp[-1].cmp < 0) {                \
            rbtn_left_set(a_type, a_field, pathp[-1].node,    \
              tnode);                    \
            } else {                        \
            rbtn_right_set(a_type, a_field, pathp[-1].node,    \
              tnode);                    \
            }                            \
        }                            \
        return;                            \
        } else if (rbtn_red_get(a_type, a_field, pathp->node)) {    \
        a_type *leftleft = rbtn_left_get(a_type, a_field, left);\
        if (leftleft != NULL && rbtn_red_get(a_type, a_field,    \
          leftleft)) {                        \
            /*        ||                                      */\
            /*      pathp(r)                                  */\
            /*     /        \\                                */\
            /*   (b)        (b)                               */\
            /*   /                                            */\
            /* (r)                                            */\
            a_type *tnode;                    \
            rbtn_black_set(a_type, a_field, pathp->node);    \
            rbtn_red_set(a_type, a_field, left);        \
            rbtn_black_set(a_type, a_field, leftleft);        \
            rbtn_rotate_right(a_type, a_field, pathp->node,    \
              tnode);                        \
            /* Balance restored, but rotation modified        */\
            /* subtree root.                                  */\
            assert((uintptr_t)pathp > (uintptr_t)path);        \
            if (pathp[-1].cmp < 0) {                \
            rbtn_left_set(a_type, a_field, pathp[-1].node,    \
              tnode);                    \
            } else {                        \
            rbtn_right_set(a_type, a_field, pathp[-1].node,    \
              tnode);                    \
            }                            \
            return;                        \
        } else {                        \
            /*        ||                                      */\
            /*      pathp(r)                                  */\
            /*     /        \\                                */\
            /*   (b)        (b)                               */\
            /*   /                                            */\
            /* (b)                                            */\
            rbtn_red_set(a_type, a_field, left);        \
            rbtn_black_set(a_type, a_field, pathp->node);    \
            /* Balance restored. */                \
            return;                        \
        }                            \
        } else {                            \
        a_type *leftleft = rbtn_left_get(a_type, a_field, left);\
        if (leftleft != NULL && rbtn_red_get(a_type, a_field,    \
          leftleft)) {                        \
            /*               ||                               */\
            /*             pathp(b)                           */\
            /*            /        \\                         */\
            /*          (b)        (b)                        */\
            /*          /                                     */\
            /*        (r)                                     */\
            a_type *tnode;                    \
            rbtn_black_set(a_type, a_field, leftleft);        \
            rbtn_rotate_right(a_type, a_field, pathp->node,    \
              tnode);                        \
            /* Balance restored, but rotation modified        */\
            /* subtree root, which may actually be the tree   */\
            /* root.                                          */\
            if (pathp == path) {                \
            /* Set root. */                    \
            rbtree->rbt_root = tnode;            \
            } else {                        \
            if (pathp[-1].cmp < 0) {            \
                rbtn_left_set(a_type, a_field,        \
                  pathp[-1].node, tnode);            \
            } else {                    \
                rbtn_right_set(a_type, a_field,        \
                  pathp[-1].node, tnode);            \
            }                        \
            }                            \
            return;                        \
        } else {                        \
            /*               ||                               */\
            /*             pathp(b)                           */\
            /*            /        \\                         */\
            /*          (b)        (b)                        */\
            /*          /                                     */\
            /*        (b)                                     */\
            rbtn_red_set(a_type, a_field, left);        \
        }                            \
        }                                \
    }                                \
    }                                    \
    /* Set root. */                            \
    rbtree->rbt_root = path->node;                    \
    assert(!rbtn_red_get(a_type, a_field, rbtree->rbt_root));        \
}                                    \
a_attr a_type *                                \
a_prefix##iter_recurse(a_rbt_type *rbtree, a_type *node,        \
  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) {        \
    if (node == NULL) {                            \
    return NULL;                            \
    } else {                                \
    a_type *ret;                            \
    if ((ret = a_prefix##iter_recurse(rbtree, rbtn_left_get(a_type,    \
      a_field, node), cb, arg)) != NULL || (ret = cb(rbtree, node,    \
      arg)) != NULL) {                        \
        return ret;                            \
    }                                \
    return a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type,    \
      a_field, node), cb, arg);                    \
    }                                    \
}                                    \
a_attr a_type *                                \
a_prefix##iter_start(a_rbt_type *rbtree, a_type *start, a_type *node,    \
  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) {        \
    int cmp = a_cmp(start, node);                    \
    if (cmp < 0) {                            \
    a_type *ret;                            \
    if ((ret = a_prefix##iter_start(rbtree, start,            \
      rbtn_left_get(a_type, a_field, node), cb, arg)) != NULL ||    \
      (ret = cb(rbtree, node, arg)) != NULL) {            \
        return ret;                            \
    }                                \
    return a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type,    \
      a_field, node), cb, arg);                    \
    } else if (cmp > 0) {                        \
    return a_prefix##iter_start(rbtree, start,            \
      rbtn_right_get(a_type, a_field, node), cb, arg);        \
    } else {                                \
    a_type *ret;                            \
    if ((ret = cb(rbtree, node, arg)) != NULL) {            \
        return ret;                            \
    }                                \
    return a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type,    \
      a_field, node), cb, arg);                    \
    }                                    \
}                                    \
a_attr a_type *                                \
a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)(    \
  a_rbt_type *, a_type *, void *), void *arg) {                \
    a_type *ret;                            \
    if (start != NULL) {                        \
    ret = a_prefix##iter_start(rbtree, start, rbtree->rbt_root,    \
      cb, arg);                            \
    } else {                                \
    ret = a_prefix##iter_recurse(rbtree, rbtree->rbt_root, cb, arg);\
    }                                    \
    return ret;                                \
}                                    \
a_attr a_type *                                \
a_prefix##reverse_iter_recurse(a_rbt_type *rbtree, a_type *node,    \
  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) {        \
    if (node == NULL) {                            \
    return NULL;                            \
    } else {                                \
    a_type *ret;                            \
    if ((ret = a_prefix##reverse_iter_recurse(rbtree,        \
      rbtn_right_get(a_type, a_field, node), cb, arg)) != NULL ||    \
      (ret = cb(rbtree, node, arg)) != NULL) {            \
        return ret;                            \
    }                                \
    return a_prefix##reverse_iter_recurse(rbtree,            \
      rbtn_left_get(a_type, a_field, node), cb, arg);        \
    }                                    \
}                                    \
a_attr a_type *                                \
a_prefix##reverse_iter_start(a_rbt_type *rbtree, a_type *start,        \
  a_type *node, a_type *(*cb)(a_rbt_type *, a_type *, void *),        \
  void *arg) {                                \
    int cmp = a_cmp(start, node);                    \
    if (cmp > 0) {                            \
    a_type *ret;                            \
    if ((ret = a_prefix##reverse_iter_start(rbtree, start,        \
      rbtn_right_get(a_type, a_field, node), cb, arg)) != NULL ||    \
      (ret = cb(rbtree, node, arg)) != NULL) {            \
        return ret;                            \
    }                                \
    return a_prefix##reverse_iter_recurse(rbtree,            \
      rbtn_left_get(a_type, a_field, node), cb, arg);        \
    } else if (cmp < 0) {                        \
    return a_prefix##reverse_iter_start(rbtree, start,        \
      rbtn_left_get(a_type, a_field, node), cb, arg);        \
    } else {                                \
    a_type *ret;                            \
    if ((ret = cb(rbtree, node, arg)) != NULL) {            \
        return ret;                            \
    }                                \
    return a_prefix##reverse_iter_recurse(rbtree,            \
      rbtn_left_get(a_type, a_field, node), cb, arg);        \
    }                                    \
}                                    \
a_attr a_type *                                \
a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start,        \
  a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) {        \
    a_type *ret;                            \
    if (start != NULL) {                        \
    ret = a_prefix##reverse_iter_start(rbtree, start,        \
      rbtree->rbt_root, cb, arg);                    \
    } else {                                \
    ret = a_prefix##reverse_iter_recurse(rbtree, rbtree->rbt_root,    \
      cb, arg);                            \
    }                                    \
    return ret;                                \
}                                    \
a_attr void                                \
a_prefix##destroy_recurse(a_rbt_type *rbtree, a_type *node, void (*cb)(    \
  a_type *, void *), void *arg) {                    \
    if (node == NULL) {                            \
    return;                                \
    }                                    \
    a_prefix##destroy_recurse(rbtree, rbtn_left_get(a_type, a_field,    \
      node), cb, arg);                            \
    rbtn_left_set(a_type, a_field, (node), NULL);            \
    a_prefix##destroy_recurse(rbtree, rbtn_right_get(a_type, a_field,    \
      node), cb, arg);                            \
    rbtn_right_set(a_type, a_field, (node), NULL);            \
    if (cb) {                                \
    cb(node, arg);                            \
    }                                    \
}                                    \
a_attr void                                \
a_prefix##destroy(a_rbt_type *rbtree, void (*cb)(a_type *, void *),    \
  void *arg) {                                \
    a_prefix##destroy_recurse(rbtree, rbtree->rbt_root, cb, arg);    \
    rbtree->rbt_root = NULL;                        \
}
#endif /* RB_H_ */
jemalloc/x64/include/jemalloc/internal/rtree.h
New file
@@ -0,0 +1,528 @@
#ifndef JEMALLOC_INTERNAL_RTREE_H
#define JEMALLOC_INTERNAL_RTREE_H
#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/rtree_tsd.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/tsd.h"
/*
 * This radix tree implementation is tailored to the singular purpose of
 * associating metadata with extents that are currently owned by jemalloc.
 *
 *******************************************************************************
 */
/* Number of high insignificant bits. */
#define RTREE_NHIB ((1U << (LG_SIZEOF_PTR+3)) - LG_VADDR)
/* Number of low insigificant bits. */
#define RTREE_NLIB LG_PAGE
/* Number of significant bits. */
#define RTREE_NSB (LG_VADDR - RTREE_NLIB)
/* Number of levels in radix tree. */
#if RTREE_NSB <= 10
#  define RTREE_HEIGHT 1
#elif RTREE_NSB <= 36
#  define RTREE_HEIGHT 2
#elif RTREE_NSB <= 52
#  define RTREE_HEIGHT 3
#else
#  error Unsupported number of significant virtual address bits
#endif
/* Use compact leaf representation if virtual address encoding allows. */
#if RTREE_NHIB >= LG_CEIL(SC_NSIZES)
#  define RTREE_LEAF_COMPACT
#endif
/* Needed for initialization only. */
#define RTREE_LEAFKEY_INVALID ((uintptr_t)1)
typedef struct rtree_node_elm_s rtree_node_elm_t;
struct rtree_node_elm_s {
    atomic_p_t    child; /* (rtree_{node,leaf}_elm_t *) */
};
struct rtree_leaf_elm_s {
#ifdef RTREE_LEAF_COMPACT
    /*
     * Single pointer-width field containing all three leaf element fields.
     * For example, on a 64-bit x64 system with 48 significant virtual
     * memory address bits, the index, extent, and slab fields are packed as
     * such:
     *
     * x: index
     * e: extent
     * b: slab
     *
     *   00000000 xxxxxxxx eeeeeeee [...] eeeeeeee eeee000b
     */
    atomic_p_t    le_bits;
#else
    atomic_p_t    le_extent; /* (extent_t *) */
    atomic_u_t    le_szind; /* (szind_t) */
    atomic_b_t    le_slab; /* (bool) */
#endif
};
typedef struct rtree_level_s rtree_level_t;
struct rtree_level_s {
    /* Number of key bits distinguished by this level. */
    unsigned        bits;
    /*
     * Cumulative number of key bits distinguished by traversing to
     * corresponding tree level.
     */
    unsigned        cumbits;
};
typedef struct rtree_s rtree_t;
struct rtree_s {
    malloc_mutex_t        init_lock;
    /* Number of elements based on rtree_levels[0].bits. */
#if RTREE_HEIGHT > 1
    rtree_node_elm_t    root[1U << (RTREE_NSB/RTREE_HEIGHT)];
#else
    rtree_leaf_elm_t    root[1U << (RTREE_NSB/RTREE_HEIGHT)];
#endif
};
/*
 * Split the bits into one to three partitions depending on number of
 * significant bits.  It the number of bits does not divide evenly into the
 * number of levels, place one remainder bit per level starting at the leaf
 * level.
 */
static const rtree_level_t rtree_levels[] = {
#if RTREE_HEIGHT == 1
    {RTREE_NSB, RTREE_NHIB + RTREE_NSB}
#elif RTREE_HEIGHT == 2
    {RTREE_NSB/2, RTREE_NHIB + RTREE_NSB/2},
    {RTREE_NSB/2 + RTREE_NSB%2, RTREE_NHIB + RTREE_NSB}
#elif RTREE_HEIGHT == 3
    {RTREE_NSB/3, RTREE_NHIB + RTREE_NSB/3},
    {RTREE_NSB/3 + RTREE_NSB%3/2,
        RTREE_NHIB + RTREE_NSB/3*2 + RTREE_NSB%3/2},
    {RTREE_NSB/3 + RTREE_NSB%3 - RTREE_NSB%3/2, RTREE_NHIB + RTREE_NSB}
#else
#  error Unsupported rtree height
#endif
};
bool rtree_new(rtree_t *rtree, bool zeroed);
typedef rtree_node_elm_t *(rtree_node_alloc_t)(tsdn_t *, rtree_t *, size_t);
extern rtree_node_alloc_t *JET_MUTABLE rtree_node_alloc;
typedef rtree_leaf_elm_t *(rtree_leaf_alloc_t)(tsdn_t *, rtree_t *, size_t);
extern rtree_leaf_alloc_t *JET_MUTABLE rtree_leaf_alloc;
typedef void (rtree_node_dalloc_t)(tsdn_t *, rtree_t *, rtree_node_elm_t *);
extern rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc;
typedef void (rtree_leaf_dalloc_t)(tsdn_t *, rtree_t *, rtree_leaf_elm_t *);
extern rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc;
#ifdef JEMALLOC_JET
void rtree_delete(tsdn_t *tsdn, rtree_t *rtree);
#endif
rtree_leaf_elm_t *rtree_leaf_elm_lookup_hard(tsdn_t *tsdn, rtree_t *rtree,
    rtree_ctx_t *rtree_ctx, uintptr_t key, bool dependent, bool init_missing);
JEMALLOC_ALWAYS_INLINE uintptr_t
rtree_leafkey(uintptr_t key) {
    unsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);
    unsigned cumbits = (rtree_levels[RTREE_HEIGHT-1].cumbits -
        rtree_levels[RTREE_HEIGHT-1].bits);
    unsigned maskbits = ptrbits - cumbits;
    uintptr_t mask = ~((ZU(1) << maskbits) - 1);
    return (key & mask);
}
JEMALLOC_ALWAYS_INLINE size_t
rtree_cache_direct_map(uintptr_t key) {
    unsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);
    unsigned cumbits = (rtree_levels[RTREE_HEIGHT-1].cumbits -
        rtree_levels[RTREE_HEIGHT-1].bits);
    unsigned maskbits = ptrbits - cumbits;
    return (size_t)((key >> maskbits) & (RTREE_CTX_NCACHE - 1));
}
JEMALLOC_ALWAYS_INLINE uintptr_t
rtree_subkey(uintptr_t key, unsigned level) {
    unsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);
    unsigned cumbits = rtree_levels[level].cumbits;
    unsigned shiftbits = ptrbits - cumbits;
    unsigned maskbits = rtree_levels[level].bits;
    uintptr_t mask = (ZU(1) << maskbits) - 1;
    return ((key >> shiftbits) & mask);
}
/*
 * Atomic getters.
 *
 * dependent: Reading a value on behalf of a pointer to a valid allocation
 *            is guaranteed to be a clean read even without synchronization,
 *            because the rtree update became visible in memory before the
 *            pointer came into existence.
 * !dependent: An arbitrary read, e.g. on behalf of ivsalloc(), may not be
 *             dependent on a previous rtree write, which means a stale read
 *             could result if synchronization were omitted here.
 */
#  ifdef RTREE_LEAF_COMPACT
JEMALLOC_ALWAYS_INLINE uintptr_t
rtree_leaf_elm_bits_read(tsdn_t *tsdn, rtree_t *rtree,
    rtree_leaf_elm_t *elm, bool dependent) {
    return (uintptr_t)atomic_load_p(&elm->le_bits, dependent
        ? ATOMIC_RELAXED : ATOMIC_ACQUIRE);
}
JEMALLOC_ALWAYS_INLINE extent_t *
rtree_leaf_elm_bits_extent_get(uintptr_t bits) {
#    ifdef __aarch64__
    /*
     * aarch64 doesn't sign extend the highest virtual address bit to set
     * the higher ones.  Instead, the high bits gets zeroed.
     */
    uintptr_t high_bit_mask = ((uintptr_t)1 << LG_VADDR) - 1;
    /* Mask off the slab bit. */
    uintptr_t low_bit_mask = ~(uintptr_t)1;
    uintptr_t mask = high_bit_mask & low_bit_mask;
    return (extent_t *)(bits & mask);
#    else
    /* Restore sign-extended high bits, mask slab bit. */
    return (extent_t *)((uintptr_t)((intptr_t)(bits << RTREE_NHIB) >>
        RTREE_NHIB) & ~((uintptr_t)0x1));
#    endif
}
JEMALLOC_ALWAYS_INLINE szind_t
rtree_leaf_elm_bits_szind_get(uintptr_t bits) {
    return (szind_t)(bits >> LG_VADDR);
}
JEMALLOC_ALWAYS_INLINE bool
rtree_leaf_elm_bits_slab_get(uintptr_t bits) {
    return (bool)(bits & (uintptr_t)0x1);
}
#  endif
JEMALLOC_ALWAYS_INLINE extent_t *
rtree_leaf_elm_extent_read(tsdn_t *tsdn, rtree_t *rtree,
    rtree_leaf_elm_t *elm, bool dependent) {
#ifdef RTREE_LEAF_COMPACT
    uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
    return rtree_leaf_elm_bits_extent_get(bits);
#else
    extent_t *extent = (extent_t *)atomic_load_p(&elm->le_extent, dependent
        ? ATOMIC_RELAXED : ATOMIC_ACQUIRE);
    return extent;
#endif
}
JEMALLOC_ALWAYS_INLINE szind_t
rtree_leaf_elm_szind_read(tsdn_t *tsdn, rtree_t *rtree,
    rtree_leaf_elm_t *elm, bool dependent) {
#ifdef RTREE_LEAF_COMPACT
    uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
    return rtree_leaf_elm_bits_szind_get(bits);
#else
    return (szind_t)atomic_load_u(&elm->le_szind, dependent ? ATOMIC_RELAXED
        : ATOMIC_ACQUIRE);
#endif
}
JEMALLOC_ALWAYS_INLINE bool
rtree_leaf_elm_slab_read(tsdn_t *tsdn, rtree_t *rtree,
    rtree_leaf_elm_t *elm, bool dependent) {
#ifdef RTREE_LEAF_COMPACT
    uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
    return rtree_leaf_elm_bits_slab_get(bits);
#else
    return atomic_load_b(&elm->le_slab, dependent ? ATOMIC_RELAXED :
        ATOMIC_ACQUIRE);
#endif
}
static inline void
rtree_leaf_elm_extent_write(tsdn_t *tsdn, rtree_t *rtree,
    rtree_leaf_elm_t *elm, extent_t *extent) {
#ifdef RTREE_LEAF_COMPACT
    uintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, true);
    uintptr_t bits = ((uintptr_t)rtree_leaf_elm_bits_szind_get(old_bits) <<
        LG_VADDR) | ((uintptr_t)extent & (((uintptr_t)0x1 << LG_VADDR) - 1))
        | ((uintptr_t)rtree_leaf_elm_bits_slab_get(old_bits));
    atomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);
#else
    atomic_store_p(&elm->le_extent, extent, ATOMIC_RELEASE);
#endif
}
static inline void
rtree_leaf_elm_szind_write(tsdn_t *tsdn, rtree_t *rtree,
    rtree_leaf_elm_t *elm, szind_t szind) {
    assert(szind <= SC_NSIZES);
#ifdef RTREE_LEAF_COMPACT
    uintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,
        true);
    uintptr_t bits = ((uintptr_t)szind << LG_VADDR) |
        ((uintptr_t)rtree_leaf_elm_bits_extent_get(old_bits) &
        (((uintptr_t)0x1 << LG_VADDR) - 1)) |
        ((uintptr_t)rtree_leaf_elm_bits_slab_get(old_bits));
    atomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);
#else
    atomic_store_u(&elm->le_szind, szind, ATOMIC_RELEASE);
#endif
}
static inline void
rtree_leaf_elm_slab_write(tsdn_t *tsdn, rtree_t *rtree,
    rtree_leaf_elm_t *elm, bool slab) {
#ifdef RTREE_LEAF_COMPACT
    uintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,
        true);
    uintptr_t bits = ((uintptr_t)rtree_leaf_elm_bits_szind_get(old_bits) <<
        LG_VADDR) | ((uintptr_t)rtree_leaf_elm_bits_extent_get(old_bits) &
        (((uintptr_t)0x1 << LG_VADDR) - 1)) | ((uintptr_t)slab);
    atomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);
#else
    atomic_store_b(&elm->le_slab, slab, ATOMIC_RELEASE);
#endif
}
static inline void
rtree_leaf_elm_write(tsdn_t *tsdn, rtree_t *rtree,
    rtree_leaf_elm_t *elm, extent_t *extent, szind_t szind, bool slab) {
#ifdef RTREE_LEAF_COMPACT
    uintptr_t bits = ((uintptr_t)szind << LG_VADDR) |
        ((uintptr_t)extent & (((uintptr_t)0x1 << LG_VADDR) - 1)) |
        ((uintptr_t)slab);
    atomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);
#else
    rtree_leaf_elm_slab_write(tsdn, rtree, elm, slab);
    rtree_leaf_elm_szind_write(tsdn, rtree, elm, szind);
    /*
     * Write extent last, since the element is atomically considered valid
     * as soon as the extent field is non-NULL.
     */
    rtree_leaf_elm_extent_write(tsdn, rtree, elm, extent);
#endif
}
static inline void
rtree_leaf_elm_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree,
    rtree_leaf_elm_t *elm, szind_t szind, bool slab) {
    assert(!slab || szind < SC_NBINS);
    /*
     * The caller implicitly assures that it is the only writer to the szind
     * and slab fields, and that the extent field cannot currently change.
     */
    rtree_leaf_elm_slab_write(tsdn, rtree, elm, slab);
    rtree_leaf_elm_szind_write(tsdn, rtree, elm, szind);
}
JEMALLOC_ALWAYS_INLINE rtree_leaf_elm_t *
rtree_leaf_elm_lookup(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
    uintptr_t key, bool dependent, bool init_missing) {
    assert(key != 0);
    assert(!dependent || !init_missing);
    size_t slot = rtree_cache_direct_map(key);
    uintptr_t leafkey = rtree_leafkey(key);
    assert(leafkey != RTREE_LEAFKEY_INVALID);
    /* Fast path: L1 direct mapped cache. */
    if (likely(rtree_ctx->cache[slot].leafkey == leafkey)) {
        rtree_leaf_elm_t *leaf = rtree_ctx->cache[slot].leaf;
        assert(leaf != NULL);
        uintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);
        return &leaf[subkey];
    }
    /*
     * Search the L2 LRU cache.  On hit, swap the matching element into the
     * slot in L1 cache, and move the position in L2 up by 1.
     */
#define RTREE_CACHE_CHECK_L2(i) do {                    \
    if (likely(rtree_ctx->l2_cache[i].leafkey == leafkey)) {    \
        rtree_leaf_elm_t *leaf = rtree_ctx->l2_cache[i].leaf;    \
        assert(leaf != NULL);                    \
        if (i > 0) {                        \
            /* Bubble up by one. */                \
            rtree_ctx->l2_cache[i].leafkey =        \
                rtree_ctx->l2_cache[i - 1].leafkey;    \
            rtree_ctx->l2_cache[i].leaf =            \
                rtree_ctx->l2_cache[i - 1].leaf;    \
            rtree_ctx->l2_cache[i - 1].leafkey =        \
                rtree_ctx->cache[slot].leafkey;        \
            rtree_ctx->l2_cache[i - 1].leaf =        \
                rtree_ctx->cache[slot].leaf;        \
        } else {                        \
            rtree_ctx->l2_cache[0].leafkey =        \
                rtree_ctx->cache[slot].leafkey;        \
            rtree_ctx->l2_cache[0].leaf =            \
                rtree_ctx->cache[slot].leaf;        \
        }                            \
        rtree_ctx->cache[slot].leafkey = leafkey;        \
        rtree_ctx->cache[slot].leaf = leaf;            \
        uintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);    \
        return &leaf[subkey];                    \
    }                                \
} while (0)
    /* Check the first cache entry. */
    RTREE_CACHE_CHECK_L2(0);
    /* Search the remaining cache elements. */
    for (unsigned i = 1; i < RTREE_CTX_NCACHE_L2; i++) {
        RTREE_CACHE_CHECK_L2(i);
    }
#undef RTREE_CACHE_CHECK_L2
    return rtree_leaf_elm_lookup_hard(tsdn, rtree, rtree_ctx, key,
        dependent, init_missing);
}
static inline bool
rtree_write(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, uintptr_t key,
    extent_t *extent, szind_t szind, bool slab) {
    /* Use rtree_clear() to set the extent to NULL. */
    assert(extent != NULL);
    rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,
        key, false, true);
    if (elm == NULL) {
        return true;
    }
    assert(rtree_leaf_elm_extent_read(tsdn, rtree, elm, false) == NULL);
    rtree_leaf_elm_write(tsdn, rtree, elm, extent, szind, slab);
    return false;
}
JEMALLOC_ALWAYS_INLINE rtree_leaf_elm_t *
rtree_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, uintptr_t key,
    bool dependent) {
    rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,
        key, dependent, false);
    if (!dependent && elm == NULL) {
        return NULL;
    }
    assert(elm != NULL);
    return elm;
}
JEMALLOC_ALWAYS_INLINE extent_t *
rtree_extent_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
    uintptr_t key, bool dependent) {
    rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,
        dependent);
    if (!dependent && elm == NULL) {
        return NULL;
    }
    return rtree_leaf_elm_extent_read(tsdn, rtree, elm, dependent);
}
JEMALLOC_ALWAYS_INLINE szind_t
rtree_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
    uintptr_t key, bool dependent) {
    rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,
        dependent);
    if (!dependent && elm == NULL) {
        return SC_NSIZES;
    }
    return rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);
}
/*
 * rtree_slab_read() is intentionally omitted because slab is always read in
 * conjunction with szind, which makes rtree_szind_slab_read() a better choice.
 */
JEMALLOC_ALWAYS_INLINE bool
rtree_extent_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
    uintptr_t key, bool dependent, extent_t **r_extent, szind_t *r_szind) {
    rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,
        dependent);
    if (!dependent && elm == NULL) {
        return true;
    }
    *r_extent = rtree_leaf_elm_extent_read(tsdn, rtree, elm, dependent);
    *r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);
    return false;
}
/*
 * Try to read szind_slab from the L1 cache.  Returns true on a hit,
 * and fills in r_szind and r_slab.  Otherwise returns false.
 *
 * Key is allowed to be NULL in order to save an extra branch on the
 * fastpath.  returns false in this case.
 */
JEMALLOC_ALWAYS_INLINE bool
rtree_szind_slab_read_fast(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
                uintptr_t key, szind_t *r_szind, bool *r_slab) {
    rtree_leaf_elm_t *elm;
    size_t slot = rtree_cache_direct_map(key);
    uintptr_t leafkey = rtree_leafkey(key);
    assert(leafkey != RTREE_LEAFKEY_INVALID);
    if (likely(rtree_ctx->cache[slot].leafkey == leafkey)) {
        rtree_leaf_elm_t *leaf = rtree_ctx->cache[slot].leaf;
        assert(leaf != NULL);
        uintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);
        elm = &leaf[subkey];
#ifdef RTREE_LEAF_COMPACT
        uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree,
                              elm, true);
        *r_szind = rtree_leaf_elm_bits_szind_get(bits);
        *r_slab = rtree_leaf_elm_bits_slab_get(bits);
#else
        *r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, true);
        *r_slab = rtree_leaf_elm_slab_read(tsdn, rtree, elm, true);
#endif
        return true;
    } else {
        return false;
    }
}
JEMALLOC_ALWAYS_INLINE bool
rtree_szind_slab_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
    uintptr_t key, bool dependent, szind_t *r_szind, bool *r_slab) {
    rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,
        dependent);
    if (!dependent && elm == NULL) {
        return true;
    }
#ifdef RTREE_LEAF_COMPACT
    uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
    *r_szind = rtree_leaf_elm_bits_szind_get(bits);
    *r_slab = rtree_leaf_elm_bits_slab_get(bits);
#else
    *r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);
    *r_slab = rtree_leaf_elm_slab_read(tsdn, rtree, elm, dependent);
#endif
    return false;
}
static inline void
rtree_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
    uintptr_t key, szind_t szind, bool slab) {
    assert(!slab || szind < SC_NBINS);
    rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);
    rtree_leaf_elm_szind_slab_update(tsdn, rtree, elm, szind, slab);
}
static inline void
rtree_clear(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
    uintptr_t key) {
    rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);
    assert(rtree_leaf_elm_extent_read(tsdn, rtree, elm, false) !=
        NULL);
    rtree_leaf_elm_write(tsdn, rtree, elm, NULL, SC_NSIZES, false);
}
#endif /* JEMALLOC_INTERNAL_RTREE_H */
jemalloc/x64/include/jemalloc/internal/rtree_tsd.h
New file
@@ -0,0 +1,50 @@
#ifndef JEMALLOC_INTERNAL_RTREE_CTX_H
#define JEMALLOC_INTERNAL_RTREE_CTX_H
/*
 * Number of leafkey/leaf pairs to cache in L1 and L2 level respectively.  Each
 * entry supports an entire leaf, so the cache hit rate is typically high even
 * with a small number of entries.  In rare cases extent activity will straddle
 * the boundary between two leaf nodes.  Furthermore, an arena may use a
 * combination of dss and mmap.  Note that as memory usage grows past the amount
 * that this cache can directly cover, the cache will become less effective if
 * locality of reference is low, but the consequence is merely cache misses
 * while traversing the tree nodes.
 *
 * The L1 direct mapped cache offers consistent and low cost on cache hit.
 * However collision could affect hit rate negatively.  This is resolved by
 * combining with a L2 LRU cache, which requires linear search and re-ordering
 * on access but suffers no collision.  Note that, the cache will itself suffer
 * cache misses if made overly large, plus the cost of linear search in the LRU
 * cache.
 */
#define RTREE_CTX_LG_NCACHE 4
#define RTREE_CTX_NCACHE (1 << RTREE_CTX_LG_NCACHE)
#define RTREE_CTX_NCACHE_L2 8
/*
 * Zero initializer required for tsd initialization only.  Proper initialization
 * done via rtree_ctx_data_init().
 */
#define RTREE_CTX_ZERO_INITIALIZER {{{0, 0}}, {{0, 0}}}
typedef struct rtree_leaf_elm_s rtree_leaf_elm_t;
typedef struct rtree_ctx_cache_elm_s rtree_ctx_cache_elm_t;
struct rtree_ctx_cache_elm_s {
    uintptr_t        leafkey;
    rtree_leaf_elm_t    *leaf;
};
typedef struct rtree_ctx_s rtree_ctx_t;
struct rtree_ctx_s {
    /* Direct mapped cache. */
    rtree_ctx_cache_elm_t    cache[RTREE_CTX_NCACHE];
    /* L2 LRU cache. */
    rtree_ctx_cache_elm_t    l2_cache[RTREE_CTX_NCACHE_L2];
};
void rtree_ctx_data_init(rtree_ctx_t *ctx);
#endif /* JEMALLOC_INTERNAL_RTREE_CTX_H */
jemalloc/x64/include/jemalloc/internal/safety_check.h
New file
@@ -0,0 +1,26 @@
#ifndef JEMALLOC_INTERNAL_SAFETY_CHECK_H
#define JEMALLOC_INTERNAL_SAFETY_CHECK_H
void safety_check_fail(const char *format, ...);
/* Can set to NULL for a default. */
void safety_check_set_abort(void (*abort_fn)());
JEMALLOC_ALWAYS_INLINE void
safety_check_set_redzone(void *ptr, size_t usize, size_t bumped_usize) {
    assert(usize < bumped_usize);
    for (size_t i = usize; i < bumped_usize && i < usize + 32; ++i) {
        *((unsigned char *)ptr + i) = 0xBC;
    }
}
JEMALLOC_ALWAYS_INLINE void
safety_check_verify_redzone(const void *ptr, size_t usize, size_t bumped_usize)
{
    for (size_t i = usize; i < bumped_usize && i < usize + 32; ++i) {
        if (unlikely(*((unsigned char *)ptr + i) != 0xBC)) {
            safety_check_fail("Use after free error\n");
        }
    }
}
#endif /*JEMALLOC_INTERNAL_SAFETY_CHECK_H */
jemalloc/x64/include/jemalloc/internal/sc.h
New file
@@ -0,0 +1,333 @@
#ifndef JEMALLOC_INTERNAL_SC_H
#define JEMALLOC_INTERNAL_SC_H
#include "jemalloc/internal/jemalloc_internal_types.h"
/*
 * Size class computations:
 *
 * These are a little tricky; we'll first start by describing how things
 * generally work, and then describe some of the details.
 *
 * Ignore the first few size classes for a moment. We can then split all the
 * remaining size classes into groups. The size classes in a group are spaced
 * such that they cover allocation request sizes in a power-of-2 range. The
 * power of two is called the base of the group, and the size classes in it
 * satisfy allocations in the half-open range (base, base * 2]. There are
 * SC_NGROUP size classes in each group, equally spaced in the range, so that
 * each one covers allocations for base / SC_NGROUP possible allocation sizes.
 * We call that value (base / SC_NGROUP) the delta of the group. Each size class
 * is delta larger than the one before it (including the initial size class in a
 * group, which is delta larger than base, the largest size class in the
 * previous group).
 * To make the math all work out nicely, we require that SC_NGROUP is a power of
 * two, and define it in terms of SC_LG_NGROUP. We'll often talk in terms of
 * lg_base and lg_delta. For each of these groups then, we have that
 * lg_delta == lg_base - SC_LG_NGROUP.
 * The size classes in a group with a given lg_base and lg_delta (which, recall,
 * can be computed from lg_base for these groups) are therefore:
 *   base + 1 * delta
 *     which covers allocations in (base, base + 1 * delta]
 *   base + 2 * delta
 *     which covers allocations in (base + 1 * delta, base + 2 * delta].
 *   base + 3 * delta
 *     which covers allocations in (base + 2 * delta, base + 3 * delta].
 *   ...
 *   base + SC_NGROUP * delta ( == 2 * base)
 *     which covers allocations in (base + (SC_NGROUP - 1) * delta, 2 * base].
 * (Note that currently SC_NGROUP is always 4, so the "..." is empty in
 * practice.)
 * Note that the last size class in the group is the next power of two (after
 * base), so that we've set up the induction correctly for the next group's
 * selection of delta.
 *
 * Now, let's start considering the first few size classes. Two extra constants
 * come into play here: LG_QUANTUM and SC_LG_TINY_MIN. LG_QUANTUM ensures
 * correct platform alignment; all objects of size (1 << LG_QUANTUM) or larger
 * are at least (1 << LG_QUANTUM) aligned; this can be used to ensure that we
 * never return improperly aligned memory, by making (1 << LG_QUANTUM) equal the
 * highest required alignment of a platform. For allocation sizes smaller than
 * (1 << LG_QUANTUM) though, we can be more relaxed (since we don't support
 * platforms with types with alignment larger than their size). To allow such
 * allocations (without wasting space unnecessarily), we introduce tiny size
 * classes; one per power of two, up until we hit the quantum size. There are
 * therefore LG_QUANTUM - SC_LG_TINY_MIN such size classes.
 *
 * Next, we have a size class of size (1 << LG_QUANTUM).  This can't be the
 * start of a group in the sense we described above (covering a power of two
 * range) since, if we divided into it to pick a value of delta, we'd get a
 * delta smaller than (1 << LG_QUANTUM) for sizes >= (1 << LG_QUANTUM), which
 * is against the rules.
 *
 * The first base we can divide by SC_NGROUP while still being at least
 * (1 << LG_QUANTUM) is SC_NGROUP * (1 << LG_QUANTUM). We can get there by
 * having SC_NGROUP size classes, spaced (1 << LG_QUANTUM) apart. These size
 * classes are:
 *   1 * (1 << LG_QUANTUM)
 *   2 * (1 << LG_QUANTUM)
 *   3 * (1 << LG_QUANTUM)
 *   ... (although, as above, this "..." is empty in practice)
 *   SC_NGROUP * (1 << LG_QUANTUM).
 *
 * There are SC_NGROUP of these size classes, so we can regard it as a sort of
 * pseudo-group, even though it spans multiple powers of 2, is divided
 * differently, and both starts and ends on a power of 2 (as opposed to just
 * ending). SC_NGROUP is itself a power of two, so the first group after the
 * pseudo-group has the power-of-two base SC_NGROUP * (1 << LG_QUANTUM), for a
 * lg_base of LG_QUANTUM + SC_LG_NGROUP. We can divide this base into SC_NGROUP
 * sizes without violating our LG_QUANTUM requirements, so we can safely set
 * lg_delta = lg_base - SC_LG_GROUP (== LG_QUANTUM).
 *
 * So, in order, the size classes are:
 *
 * Tiny size classes:
 * - Count: LG_QUANTUM - SC_LG_TINY_MIN.
 * - Sizes:
 *     1 << SC_LG_TINY_MIN
 *     1 << (SC_LG_TINY_MIN + 1)
 *     1 << (SC_LG_TINY_MIN + 2)
 *     ...
 *     1 << (LG_QUANTUM - 1)
 *
 * Initial pseudo-group:
 * - Count: SC_NGROUP
 * - Sizes:
 *     1 * (1 << LG_QUANTUM)
 *     2 * (1 << LG_QUANTUM)
 *     3 * (1 << LG_QUANTUM)
 *     ...
 *     SC_NGROUP * (1 << LG_QUANTUM)
 *
 * Regular group 0:
 * - Count: SC_NGROUP
 * - Sizes:
 *   (relative to lg_base of LG_QUANTUM + SC_LG_NGROUP and lg_delta of
 *   lg_base - SC_LG_NGROUP)
 *     (1 << lg_base) + 1 * (1 << lg_delta)
 *     (1 << lg_base) + 2 * (1 << lg_delta)
 *     (1 << lg_base) + 3 * (1 << lg_delta)
 *     ...
 *     (1 << lg_base) + SC_NGROUP * (1 << lg_delta) [ == (1 << (lg_base + 1)) ]
 *
 * Regular group 1:
 * - Count: SC_NGROUP
 * - Sizes:
 *   (relative to lg_base of LG_QUANTUM + SC_LG_NGROUP + 1 and lg_delta of
 *   lg_base - SC_LG_NGROUP)
 *     (1 << lg_base) + 1 * (1 << lg_delta)
 *     (1 << lg_base) + 2 * (1 << lg_delta)
 *     (1 << lg_base) + 3 * (1 << lg_delta)
 *     ...
 *     (1 << lg_base) + SC_NGROUP * (1 << lg_delta) [ == (1 << (lg_base + 1)) ]
 *
 * ...
 *
 * Regular group N:
 * - Count: SC_NGROUP
 * - Sizes:
 *   (relative to lg_base of LG_QUANTUM + SC_LG_NGROUP + N and lg_delta of
 *   lg_base - SC_LG_NGROUP)
 *     (1 << lg_base) + 1 * (1 << lg_delta)
 *     (1 << lg_base) + 2 * (1 << lg_delta)
 *     (1 << lg_base) + 3 * (1 << lg_delta)
 *     ...
 *     (1 << lg_base) + SC_NGROUP * (1 << lg_delta) [ == (1 << (lg_base + 1)) ]
 *
 *
 * Representation of metadata:
 * To make the math easy, we'll mostly work in lg quantities. We record lg_base,
 * lg_delta, and ndelta (i.e. number of deltas above the base) on a
 * per-size-class basis, and maintain the invariant that, across all size
 * classes, size == (1 << lg_base) + ndelta * (1 << lg_delta).
 *
 * For regular groups (i.e. those with lg_base >= LG_QUANTUM + SC_LG_NGROUP),
 * lg_delta is lg_base - SC_LG_NGROUP, and ndelta goes from 1 to SC_NGROUP.
 *
 * For the initial tiny size classes (if any), lg_base is lg(size class size).
 * lg_delta is lg_base for the first size class, and lg_base - 1 for all
 * subsequent ones. ndelta is always 0.
 *
 * For the pseudo-group, if there are no tiny size classes, then we set
 * lg_base == LG_QUANTUM, lg_delta == LG_QUANTUM, and have ndelta range from 0
 * to SC_NGROUP - 1. (Note that delta == base, so base + (SC_NGROUP - 1) * delta
 * is just SC_NGROUP * base, or (1 << (SC_LG_NGROUP + LG_QUANTUM)), so we do
 * indeed get a power of two that way). If there *are* tiny size classes, then
 * the first size class needs to have lg_delta relative to the largest tiny size
 * class. We therefore set lg_base == LG_QUANTUM - 1,
 * lg_delta == LG_QUANTUM - 1, and ndelta == 1, keeping the rest of the
 * pseudo-group the same.
 *
 *
 * Other terminology:
 * "Small" size classes mean those that are allocated out of bins, which is the
 * same as those that are slab allocated.
 * "Large" size classes are those that are not small. The cutoff for counting as
 * large is page size * group size.
 */
/*
 * Size class N + (1 << SC_LG_NGROUP) twice the size of size class N.
 */
#define SC_LG_NGROUP 2
#define SC_LG_TINY_MIN 3
#if SC_LG_TINY_MIN == 0
/* The div module doesn't support division by 1, which this would require. */
#error "Unsupported LG_TINY_MIN"
#endif
/*
 * The definitions below are all determined by the above settings and system
 * characteristics.
 */
#define SC_NGROUP (1ULL << SC_LG_NGROUP)
#define SC_PTR_BITS ((1ULL << LG_SIZEOF_PTR) * 8)
#define SC_NTINY (LG_QUANTUM - SC_LG_TINY_MIN)
#define SC_LG_TINY_MAXCLASS (LG_QUANTUM > SC_LG_TINY_MIN ? LG_QUANTUM - 1 : -1)
#define SC_NPSEUDO SC_NGROUP
#define SC_LG_FIRST_REGULAR_BASE (LG_QUANTUM + SC_LG_NGROUP)
/*
 * We cap allocations to be less than 2 ** (ptr_bits - 1), so the highest base
 * we need is 2 ** (ptr_bits - 2). (This also means that the last group is 1
 * size class shorter than the others).
 * We could probably save some space in arenas by capping this at LG_VADDR size.
 */
#define SC_LG_BASE_MAX (SC_PTR_BITS - 2)
#define SC_NREGULAR (SC_NGROUP *                     \
    (SC_LG_BASE_MAX - SC_LG_FIRST_REGULAR_BASE + 1) - 1)
#define SC_NSIZES (SC_NTINY + SC_NPSEUDO + SC_NREGULAR)
/* The number of size classes that are a multiple of the page size. */
#define SC_NPSIZES (                            \
    /* Start with all the size classes. */                \
    SC_NSIZES                                \
    /* Subtract out those groups with too small a base. */        \
    - (LG_PAGE - 1 - SC_LG_FIRST_REGULAR_BASE) * SC_NGROUP        \
    /* And the pseudo-group. */                        \
    - SC_NPSEUDO                            \
    /* And the tiny group. */                        \
    - SC_NTINY                                \
    /* Sizes where ndelta*delta is not a multiple of the page size. */    \
    - (SC_LG_NGROUP * SC_NGROUP))
/*
 * Note that the last line is computed as the sum of the second column in the
 * following table:
 *                      lg(base) | count of sizes to exclude
 * ------------------------------|-----------------------------
 *                   LG_PAGE - 1 | SC_NGROUP - 1
 *                       LG_PAGE | SC_NGROUP - 1
 *                   LG_PAGE + 1 | SC_NGROUP - 2
 *                   LG_PAGE + 2 | SC_NGROUP - 4
 *                           ... | ...
 *  LG_PAGE + (SC_LG_NGROUP - 1) | SC_NGROUP - (SC_NGROUP / 2)
 */
/*
 * We declare a size class is binnable if size < page size * group. Or, in other
 * words, lg(size) < lg(page size) + lg(group size).
 */
#define SC_NBINS (                            \
    /* Sub-regular size classes. */                    \
    SC_NTINY + SC_NPSEUDO                        \
    /* Groups with lg_regular_min_base <= lg_base <= lg_base_max */    \
    + SC_NGROUP * (LG_PAGE + SC_LG_NGROUP - SC_LG_FIRST_REGULAR_BASE)    \
    /* Last SC of the last group hits the bound exactly; exclude it. */    \
    - 1)
/*
 * The size2index_tab lookup table uses uint8_t to encode each bin index, so we
 * cannot support more than 256 small size classes.
 */
#if (SC_NBINS > 256)
#  error "Too many small size classes"
#endif
/* The largest size class in the lookup table. */
#define SC_LOOKUP_MAXCLASS ((size_t)1 << 12)
/* Internal, only used for the definition of SC_SMALL_MAXCLASS. */
#define SC_SMALL_MAX_BASE ((size_t)1 << (LG_PAGE + SC_LG_NGROUP - 1))
#define SC_SMALL_MAX_DELTA ((size_t)1 << (LG_PAGE - 1))
/* The largest size class allocated out of a slab. */
#define SC_SMALL_MAXCLASS (SC_SMALL_MAX_BASE                \
    + (SC_NGROUP - 1) * SC_SMALL_MAX_DELTA)
/* The smallest size class not allocated out of a slab. */
#define SC_LARGE_MINCLASS ((size_t)1ULL << (LG_PAGE + SC_LG_NGROUP))
#define SC_LG_LARGE_MINCLASS (LG_PAGE + SC_LG_NGROUP)
/* Internal; only used for the definition of SC_LARGE_MAXCLASS. */
#define SC_MAX_BASE ((size_t)1 << (SC_PTR_BITS - 2))
#define SC_MAX_DELTA ((size_t)1 << (SC_PTR_BITS - 2 - SC_LG_NGROUP))
/* The largest size class supported. */
#define SC_LARGE_MAXCLASS (SC_MAX_BASE + (SC_NGROUP - 1) * SC_MAX_DELTA)
typedef struct sc_s sc_t;
struct sc_s {
    /* Size class index, or -1 if not a valid size class. */
    int index;
    /* Lg group base size (no deltas added). */
    int lg_base;
    /* Lg delta to previous size class. */
    int lg_delta;
    /* Delta multiplier.  size == 1<<lg_base + ndelta<<lg_delta */
    int ndelta;
    /*
     * True if the size class is a multiple of the page size, false
     * otherwise.
     */
    bool psz;
    /*
     * True if the size class is a small, bin, size class. False otherwise.
     */
    bool bin;
    /* The slab page count if a small bin size class, 0 otherwise. */
    int pgs;
    /* Same as lg_delta if a lookup table size class, 0 otherwise. */
    int lg_delta_lookup;
};
typedef struct sc_data_s sc_data_t;
struct sc_data_s {
    /* Number of tiny size classes. */
    unsigned ntiny;
    /* Number of bins supported by the lookup table. */
    int nlbins;
    /* Number of small size class bins. */
    int nbins;
    /* Number of size classes. */
    int nsizes;
    /* Number of bits required to store NSIZES. */
    int lg_ceil_nsizes;
    /* Number of size classes that are a multiple of (1U << LG_PAGE). */
    unsigned npsizes;
    /* Lg of maximum tiny size class (or -1, if none). */
    int lg_tiny_maxclass;
    /* Maximum size class included in lookup table. */
    size_t lookup_maxclass;
    /* Maximum small size class. */
    size_t small_maxclass;
    /* Lg of minimum large size class. */
    int lg_large_minclass;
    /* The minimum large size class. */
    size_t large_minclass;
    /* Maximum (large) size class. */
    size_t large_maxclass;
    /* True if the sc_data_t has been initialized (for debugging only). */
    bool initialized;
    sc_t sc[SC_NSIZES];
};
void sc_data_init(sc_data_t *data);
/*
 * Updates slab sizes in [begin, end] to be pgs pages in length, if possible.
 * Otherwise, does its best to accomodate the request.
 */
void sc_data_update_slab_size(sc_data_t *data, size_t begin, size_t end,
    int pgs);
void sc_boot(sc_data_t *data);
#endif /* JEMALLOC_INTERNAL_SC_H */
jemalloc/x64/include/jemalloc/internal/seq.h
New file
@@ -0,0 +1,55 @@
#ifndef JEMALLOC_INTERNAL_SEQ_H
#define JEMALLOC_INTERNAL_SEQ_H
#include "jemalloc/internal/atomic.h"
/*
 * A simple seqlock implementation.
 */
#define seq_define(type, short_type)                    \
typedef struct {                            \
    atomic_zu_t seq;                        \
    atomic_zu_t data[                        \
        (sizeof(type) + sizeof(size_t) - 1) / sizeof(size_t)];    \
} seq_##short_type##_t;                            \
                                    \
/*                                    \
 * No internal synchronization -- the caller must ensure that there's    \
 * only a single writer at a time.                    \
 */                                    \
static inline void                            \
seq_store_##short_type(seq_##short_type##_t *dst, type *src) {        \
    size_t buf[sizeof(dst->data) / sizeof(size_t)];            \
    buf[sizeof(buf) / sizeof(size_t) - 1] = 0;            \
    memcpy(buf, src, sizeof(type));                    \
    size_t old_seq = atomic_load_zu(&dst->seq, ATOMIC_RELAXED);    \
    atomic_store_zu(&dst->seq, old_seq + 1, ATOMIC_RELAXED);    \
    atomic_fence(ATOMIC_RELEASE);                    \
    for (size_t i = 0; i < sizeof(buf) / sizeof(size_t); i++) {    \
        atomic_store_zu(&dst->data[i], buf[i], ATOMIC_RELAXED);    \
    }                                \
    atomic_store_zu(&dst->seq, old_seq + 2, ATOMIC_RELEASE);    \
}                                    \
                                    \
/* Returns whether or not the read was consistent. */            \
static inline bool                            \
seq_try_load_##short_type(type *dst, seq_##short_type##_t *src) {    \
    size_t buf[sizeof(src->data) / sizeof(size_t)];            \
    size_t seq1 = atomic_load_zu(&src->seq, ATOMIC_ACQUIRE);    \
    if (seq1 % 2 != 0) {                        \
        return false;                        \
    }                                \
    for (size_t i = 0; i < sizeof(buf) / sizeof(size_t); i++) {    \
        buf[i] = atomic_load_zu(&src->data[i], ATOMIC_RELAXED);    \
    }                                \
    atomic_fence(ATOMIC_ACQUIRE);                    \
    size_t seq2 = atomic_load_zu(&src->seq, ATOMIC_RELAXED);    \
    if (seq1 != seq2) {                        \
        return false;                        \
    }                                \
    memcpy(dst, buf, sizeof(type));                    \
    return true;                            \
}
#endif /* JEMALLOC_INTERNAL_SEQ_H */
jemalloc/x64/include/jemalloc/internal/smoothstep.h
New file
@@ -0,0 +1,232 @@
#ifndef JEMALLOC_INTERNAL_SMOOTHSTEP_H
#define JEMALLOC_INTERNAL_SMOOTHSTEP_H
/*
 * This file was generated by the following command:
 *   sh smoothstep.sh smoother 200 24 3 15
 */
/******************************************************************************/
/*
 * This header defines a precomputed table based on the smoothstep family of
 * sigmoidal curves (https://en.wikipedia.org/wiki/Smoothstep) that grow from 0
 * to 1 in 0 <= x <= 1.  The table is stored as integer fixed point values so
 * that floating point math can be avoided.
 *
 *                      3     2
 *   smoothstep(x) = -2x  + 3x
 *
 *                       5      4      3
 *   smootherstep(x) = 6x  - 15x  + 10x
 *
 *                          7      6      5      4
 *   smootheststep(x) = -20x  + 70x  - 84x  + 35x
 */
#define SMOOTHSTEP_VARIANT    "smoother"
#define SMOOTHSTEP_NSTEPS    200
#define SMOOTHSTEP_BFP        24
#define SMOOTHSTEP \
 /* STEP(step, h,                            x,     y) */ \
    STEP(   1, UINT64_C(0x0000000000000014), 0.005, 0.000001240643750) \
    STEP(   2, UINT64_C(0x00000000000000a5), 0.010, 0.000009850600000) \
    STEP(   3, UINT64_C(0x0000000000000229), 0.015, 0.000032995181250) \
    STEP(   4, UINT64_C(0x0000000000000516), 0.020, 0.000077619200000) \
    STEP(   5, UINT64_C(0x00000000000009dc), 0.025, 0.000150449218750) \
    STEP(   6, UINT64_C(0x00000000000010e8), 0.030, 0.000257995800000) \
    STEP(   7, UINT64_C(0x0000000000001aa4), 0.035, 0.000406555756250) \
    STEP(   8, UINT64_C(0x0000000000002777), 0.040, 0.000602214400000) \
    STEP(   9, UINT64_C(0x00000000000037c2), 0.045, 0.000850847793750) \
    STEP(  10, UINT64_C(0x0000000000004be6), 0.050, 0.001158125000000) \
    STEP(  11, UINT64_C(0x000000000000643c), 0.055, 0.001529510331250) \
    STEP(  12, UINT64_C(0x000000000000811f), 0.060, 0.001970265600000) \
    STEP(  13, UINT64_C(0x000000000000a2e2), 0.065, 0.002485452368750) \
    STEP(  14, UINT64_C(0x000000000000c9d8), 0.070, 0.003079934200000) \
    STEP(  15, UINT64_C(0x000000000000f64f), 0.075, 0.003758378906250) \
    STEP(  16, UINT64_C(0x0000000000012891), 0.080, 0.004525260800000) \
    STEP(  17, UINT64_C(0x00000000000160e7), 0.085, 0.005384862943750) \
    STEP(  18, UINT64_C(0x0000000000019f95), 0.090, 0.006341279400000) \
    STEP(  19, UINT64_C(0x000000000001e4dc), 0.095, 0.007398417481250) \
    STEP(  20, UINT64_C(0x00000000000230fc), 0.100, 0.008560000000000) \
    STEP(  21, UINT64_C(0x0000000000028430), 0.105, 0.009829567518750) \
    STEP(  22, UINT64_C(0x000000000002deb0), 0.110, 0.011210480600000) \
    STEP(  23, UINT64_C(0x00000000000340b1), 0.115, 0.012705922056250) \
    STEP(  24, UINT64_C(0x000000000003aa67), 0.120, 0.014318899200000) \
    STEP(  25, UINT64_C(0x0000000000041c00), 0.125, 0.016052246093750) \
    STEP(  26, UINT64_C(0x00000000000495a8), 0.130, 0.017908625800000) \
    STEP(  27, UINT64_C(0x000000000005178b), 0.135, 0.019890532631250) \
    STEP(  28, UINT64_C(0x000000000005a1cf), 0.140, 0.022000294400000) \
    STEP(  29, UINT64_C(0x0000000000063498), 0.145, 0.024240074668750) \
    STEP(  30, UINT64_C(0x000000000006d009), 0.150, 0.026611875000000) \
    STEP(  31, UINT64_C(0x000000000007743f), 0.155, 0.029117537206250) \
    STEP(  32, UINT64_C(0x0000000000082157), 0.160, 0.031758745600000) \
    STEP(  33, UINT64_C(0x000000000008d76b), 0.165, 0.034537029243750) \
    STEP(  34, UINT64_C(0x0000000000099691), 0.170, 0.037453764200000) \
    STEP(  35, UINT64_C(0x00000000000a5edf), 0.175, 0.040510175781250) \
    STEP(  36, UINT64_C(0x00000000000b3067), 0.180, 0.043707340800000) \
    STEP(  37, UINT64_C(0x00000000000c0b38), 0.185, 0.047046189818750) \
    STEP(  38, UINT64_C(0x00000000000cef5e), 0.190, 0.050527509400000) \
    STEP(  39, UINT64_C(0x00000000000ddce6), 0.195, 0.054151944356250) \
    STEP(  40, UINT64_C(0x00000000000ed3d8), 0.200, 0.057920000000000) \
    STEP(  41, UINT64_C(0x00000000000fd439), 0.205, 0.061832044393750) \
    STEP(  42, UINT64_C(0x000000000010de0e), 0.210, 0.065888310600000) \
    STEP(  43, UINT64_C(0x000000000011f158), 0.215, 0.070088898931250) \
    STEP(  44, UINT64_C(0x0000000000130e17), 0.220, 0.074433779200000) \
    STEP(  45, UINT64_C(0x0000000000143448), 0.225, 0.078922792968750) \
    STEP(  46, UINT64_C(0x00000000001563e7), 0.230, 0.083555655800000) \
    STEP(  47, UINT64_C(0x0000000000169cec), 0.235, 0.088331959506250) \
    STEP(  48, UINT64_C(0x000000000017df4f), 0.240, 0.093251174400000) \
    STEP(  49, UINT64_C(0x0000000000192b04), 0.245, 0.098312651543750) \
    STEP(  50, UINT64_C(0x00000000001a8000), 0.250, 0.103515625000000) \
    STEP(  51, UINT64_C(0x00000000001bde32), 0.255, 0.108859214081250) \
    STEP(  52, UINT64_C(0x00000000001d458b), 0.260, 0.114342425600000) \
    STEP(  53, UINT64_C(0x00000000001eb5f8), 0.265, 0.119964156118750) \
    STEP(  54, UINT64_C(0x0000000000202f65), 0.270, 0.125723194200000) \
    STEP(  55, UINT64_C(0x000000000021b1bb), 0.275, 0.131618222656250) \
    STEP(  56, UINT64_C(0x0000000000233ce3), 0.280, 0.137647820800000) \
    STEP(  57, UINT64_C(0x000000000024d0c3), 0.285, 0.143810466693750) \
    STEP(  58, UINT64_C(0x0000000000266d40), 0.290, 0.150104539400000) \
    STEP(  59, UINT64_C(0x000000000028123d), 0.295, 0.156528321231250) \
    STEP(  60, UINT64_C(0x000000000029bf9c), 0.300, 0.163080000000000) \
    STEP(  61, UINT64_C(0x00000000002b753d), 0.305, 0.169757671268750) \
    STEP(  62, UINT64_C(0x00000000002d32fe), 0.310, 0.176559340600000) \
    STEP(  63, UINT64_C(0x00000000002ef8bc), 0.315, 0.183482925806250) \
    STEP(  64, UINT64_C(0x000000000030c654), 0.320, 0.190526259200000) \
    STEP(  65, UINT64_C(0x0000000000329b9f), 0.325, 0.197687089843750) \
    STEP(  66, UINT64_C(0x0000000000347875), 0.330, 0.204963085800000) \
    STEP(  67, UINT64_C(0x0000000000365cb0), 0.335, 0.212351836381250) \
    STEP(  68, UINT64_C(0x0000000000384825), 0.340, 0.219850854400000) \
    STEP(  69, UINT64_C(0x00000000003a3aa8), 0.345, 0.227457578418750) \
    STEP(  70, UINT64_C(0x00000000003c340f), 0.350, 0.235169375000000) \
    STEP(  71, UINT64_C(0x00000000003e342b), 0.355, 0.242983540956250) \
    STEP(  72, UINT64_C(0x0000000000403ace), 0.360, 0.250897305600000) \
    STEP(  73, UINT64_C(0x00000000004247c8), 0.365, 0.258907832993750) \
    STEP(  74, UINT64_C(0x0000000000445ae9), 0.370, 0.267012224200000) \
    STEP(  75, UINT64_C(0x0000000000467400), 0.375, 0.275207519531250) \
    STEP(  76, UINT64_C(0x00000000004892d8), 0.380, 0.283490700800000) \
    STEP(  77, UINT64_C(0x00000000004ab740), 0.385, 0.291858693568750) \
    STEP(  78, UINT64_C(0x00000000004ce102), 0.390, 0.300308369400000) \
    STEP(  79, UINT64_C(0x00000000004f0fe9), 0.395, 0.308836548106250) \
    STEP(  80, UINT64_C(0x00000000005143bf), 0.400, 0.317440000000000) \
    STEP(  81, UINT64_C(0x0000000000537c4d), 0.405, 0.326115448143750) \
    STEP(  82, UINT64_C(0x000000000055b95b), 0.410, 0.334859570600000) \
    STEP(  83, UINT64_C(0x000000000057fab1), 0.415, 0.343669002681250) \
    STEP(  84, UINT64_C(0x00000000005a4015), 0.420, 0.352540339200000) \
    STEP(  85, UINT64_C(0x00000000005c894e), 0.425, 0.361470136718750) \
    STEP(  86, UINT64_C(0x00000000005ed622), 0.430, 0.370454915800000) \
    STEP(  87, UINT64_C(0x0000000000612655), 0.435, 0.379491163256250) \
    STEP(  88, UINT64_C(0x00000000006379ac), 0.440, 0.388575334400000) \
    STEP(  89, UINT64_C(0x000000000065cfeb), 0.445, 0.397703855293750) \
    STEP(  90, UINT64_C(0x00000000006828d6), 0.450, 0.406873125000000) \
    STEP(  91, UINT64_C(0x00000000006a842f), 0.455, 0.416079517831250) \
    STEP(  92, UINT64_C(0x00000000006ce1bb), 0.460, 0.425319385600000) \
    STEP(  93, UINT64_C(0x00000000006f413a), 0.465, 0.434589059868750) \
    STEP(  94, UINT64_C(0x000000000071a270), 0.470, 0.443884854200000) \
    STEP(  95, UINT64_C(0x000000000074051d), 0.475, 0.453203066406250) \
    STEP(  96, UINT64_C(0x0000000000766905), 0.480, 0.462539980800000) \
    STEP(  97, UINT64_C(0x000000000078cde7), 0.485, 0.471891870443750) \
    STEP(  98, UINT64_C(0x00000000007b3387), 0.490, 0.481254999400000) \
    STEP(  99, UINT64_C(0x00000000007d99a4), 0.495, 0.490625624981250) \
    STEP( 100, UINT64_C(0x0000000000800000), 0.500, 0.500000000000000) \
    STEP( 101, UINT64_C(0x000000000082665b), 0.505, 0.509374375018750) \
    STEP( 102, UINT64_C(0x000000000084cc78), 0.510, 0.518745000600000) \
    STEP( 103, UINT64_C(0x0000000000873218), 0.515, 0.528108129556250) \
    STEP( 104, UINT64_C(0x00000000008996fa), 0.520, 0.537460019200000) \
    STEP( 105, UINT64_C(0x00000000008bfae2), 0.525, 0.546796933593750) \
    STEP( 106, UINT64_C(0x00000000008e5d8f), 0.530, 0.556115145800000) \
    STEP( 107, UINT64_C(0x000000000090bec5), 0.535, 0.565410940131250) \
    STEP( 108, UINT64_C(0x0000000000931e44), 0.540, 0.574680614400000) \
    STEP( 109, UINT64_C(0x0000000000957bd0), 0.545, 0.583920482168750) \
    STEP( 110, UINT64_C(0x000000000097d729), 0.550, 0.593126875000000) \
    STEP( 111, UINT64_C(0x00000000009a3014), 0.555, 0.602296144706250) \
    STEP( 112, UINT64_C(0x00000000009c8653), 0.560, 0.611424665600000) \
    STEP( 113, UINT64_C(0x00000000009ed9aa), 0.565, 0.620508836743750) \
    STEP( 114, UINT64_C(0x0000000000a129dd), 0.570, 0.629545084200000) \
    STEP( 115, UINT64_C(0x0000000000a376b1), 0.575, 0.638529863281250) \
    STEP( 116, UINT64_C(0x0000000000a5bfea), 0.580, 0.647459660800000) \
    STEP( 117, UINT64_C(0x0000000000a8054e), 0.585, 0.656330997318750) \
    STEP( 118, UINT64_C(0x0000000000aa46a4), 0.590, 0.665140429400000) \
    STEP( 119, UINT64_C(0x0000000000ac83b2), 0.595, 0.673884551856250) \
    STEP( 120, UINT64_C(0x0000000000aebc40), 0.600, 0.682560000000000) \
    STEP( 121, UINT64_C(0x0000000000b0f016), 0.605, 0.691163451893750) \
    STEP( 122, UINT64_C(0x0000000000b31efd), 0.610, 0.699691630600000) \
    STEP( 123, UINT64_C(0x0000000000b548bf), 0.615, 0.708141306431250) \
    STEP( 124, UINT64_C(0x0000000000b76d27), 0.620, 0.716509299200000) \
    STEP( 125, UINT64_C(0x0000000000b98c00), 0.625, 0.724792480468750) \
    STEP( 126, UINT64_C(0x0000000000bba516), 0.630, 0.732987775800000) \
    STEP( 127, UINT64_C(0x0000000000bdb837), 0.635, 0.741092167006250) \
    STEP( 128, UINT64_C(0x0000000000bfc531), 0.640, 0.749102694400000) \
    STEP( 129, UINT64_C(0x0000000000c1cbd4), 0.645, 0.757016459043750) \
    STEP( 130, UINT64_C(0x0000000000c3cbf0), 0.650, 0.764830625000000) \
    STEP( 131, UINT64_C(0x0000000000c5c557), 0.655, 0.772542421581250) \
    STEP( 132, UINT64_C(0x0000000000c7b7da), 0.660, 0.780149145600000) \
    STEP( 133, UINT64_C(0x0000000000c9a34f), 0.665, 0.787648163618750) \
    STEP( 134, UINT64_C(0x0000000000cb878a), 0.670, 0.795036914200000) \
    STEP( 135, UINT64_C(0x0000000000cd6460), 0.675, 0.802312910156250) \
    STEP( 136, UINT64_C(0x0000000000cf39ab), 0.680, 0.809473740800000) \
    STEP( 137, UINT64_C(0x0000000000d10743), 0.685, 0.816517074193750) \
    STEP( 138, UINT64_C(0x0000000000d2cd01), 0.690, 0.823440659400000) \
    STEP( 139, UINT64_C(0x0000000000d48ac2), 0.695, 0.830242328731250) \
    STEP( 140, UINT64_C(0x0000000000d64063), 0.700, 0.836920000000000) \
    STEP( 141, UINT64_C(0x0000000000d7edc2), 0.705, 0.843471678768750) \
    STEP( 142, UINT64_C(0x0000000000d992bf), 0.710, 0.849895460600000) \
    STEP( 143, UINT64_C(0x0000000000db2f3c), 0.715, 0.856189533306250) \
    STEP( 144, UINT64_C(0x0000000000dcc31c), 0.720, 0.862352179200000) \
    STEP( 145, UINT64_C(0x0000000000de4e44), 0.725, 0.868381777343750) \
    STEP( 146, UINT64_C(0x0000000000dfd09a), 0.730, 0.874276805800000) \
    STEP( 147, UINT64_C(0x0000000000e14a07), 0.735, 0.880035843881250) \
    STEP( 148, UINT64_C(0x0000000000e2ba74), 0.740, 0.885657574400000) \
    STEP( 149, UINT64_C(0x0000000000e421cd), 0.745, 0.891140785918750) \
    STEP( 150, UINT64_C(0x0000000000e58000), 0.750, 0.896484375000000) \
    STEP( 151, UINT64_C(0x0000000000e6d4fb), 0.755, 0.901687348456250) \
    STEP( 152, UINT64_C(0x0000000000e820b0), 0.760, 0.906748825600000) \
    STEP( 153, UINT64_C(0x0000000000e96313), 0.765, 0.911668040493750) \
    STEP( 154, UINT64_C(0x0000000000ea9c18), 0.770, 0.916444344200000) \
    STEP( 155, UINT64_C(0x0000000000ebcbb7), 0.775, 0.921077207031250) \
    STEP( 156, UINT64_C(0x0000000000ecf1e8), 0.780, 0.925566220800000) \
    STEP( 157, UINT64_C(0x0000000000ee0ea7), 0.785, 0.929911101068750) \
    STEP( 158, UINT64_C(0x0000000000ef21f1), 0.790, 0.934111689400000) \
    STEP( 159, UINT64_C(0x0000000000f02bc6), 0.795, 0.938167955606250) \
    STEP( 160, UINT64_C(0x0000000000f12c27), 0.800, 0.942080000000000) \
    STEP( 161, UINT64_C(0x0000000000f22319), 0.805, 0.945848055643750) \
    STEP( 162, UINT64_C(0x0000000000f310a1), 0.810, 0.949472490600000) \
    STEP( 163, UINT64_C(0x0000000000f3f4c7), 0.815, 0.952953810181250) \
    STEP( 164, UINT64_C(0x0000000000f4cf98), 0.820, 0.956292659200000) \
    STEP( 165, UINT64_C(0x0000000000f5a120), 0.825, 0.959489824218750) \
    STEP( 166, UINT64_C(0x0000000000f6696e), 0.830, 0.962546235800000) \
    STEP( 167, UINT64_C(0x0000000000f72894), 0.835, 0.965462970756250) \
    STEP( 168, UINT64_C(0x0000000000f7dea8), 0.840, 0.968241254400000) \
    STEP( 169, UINT64_C(0x0000000000f88bc0), 0.845, 0.970882462793750) \
    STEP( 170, UINT64_C(0x0000000000f92ff6), 0.850, 0.973388125000000) \
    STEP( 171, UINT64_C(0x0000000000f9cb67), 0.855, 0.975759925331250) \
    STEP( 172, UINT64_C(0x0000000000fa5e30), 0.860, 0.977999705600000) \
    STEP( 173, UINT64_C(0x0000000000fae874), 0.865, 0.980109467368750) \
    STEP( 174, UINT64_C(0x0000000000fb6a57), 0.870, 0.982091374200000) \
    STEP( 175, UINT64_C(0x0000000000fbe400), 0.875, 0.983947753906250) \
    STEP( 176, UINT64_C(0x0000000000fc5598), 0.880, 0.985681100800000) \
    STEP( 177, UINT64_C(0x0000000000fcbf4e), 0.885, 0.987294077943750) \
    STEP( 178, UINT64_C(0x0000000000fd214f), 0.890, 0.988789519400000) \
    STEP( 179, UINT64_C(0x0000000000fd7bcf), 0.895, 0.990170432481250) \
    STEP( 180, UINT64_C(0x0000000000fdcf03), 0.900, 0.991440000000000) \
    STEP( 181, UINT64_C(0x0000000000fe1b23), 0.905, 0.992601582518750) \
    STEP( 182, UINT64_C(0x0000000000fe606a), 0.910, 0.993658720600000) \
    STEP( 183, UINT64_C(0x0000000000fe9f18), 0.915, 0.994615137056250) \
    STEP( 184, UINT64_C(0x0000000000fed76e), 0.920, 0.995474739200000) \
    STEP( 185, UINT64_C(0x0000000000ff09b0), 0.925, 0.996241621093750) \
    STEP( 186, UINT64_C(0x0000000000ff3627), 0.930, 0.996920065800000) \
    STEP( 187, UINT64_C(0x0000000000ff5d1d), 0.935, 0.997514547631250) \
    STEP( 188, UINT64_C(0x0000000000ff7ee0), 0.940, 0.998029734400000) \
    STEP( 189, UINT64_C(0x0000000000ff9bc3), 0.945, 0.998470489668750) \
    STEP( 190, UINT64_C(0x0000000000ffb419), 0.950, 0.998841875000000) \
    STEP( 191, UINT64_C(0x0000000000ffc83d), 0.955, 0.999149152206250) \
    STEP( 192, UINT64_C(0x0000000000ffd888), 0.960, 0.999397785600000) \
    STEP( 193, UINT64_C(0x0000000000ffe55b), 0.965, 0.999593444243750) \
    STEP( 194, UINT64_C(0x0000000000ffef17), 0.970, 0.999742004200000) \
    STEP( 195, UINT64_C(0x0000000000fff623), 0.975, 0.999849550781250) \
    STEP( 196, UINT64_C(0x0000000000fffae9), 0.980, 0.999922380800000) \
    STEP( 197, UINT64_C(0x0000000000fffdd6), 0.985, 0.999967004818750) \
    STEP( 198, UINT64_C(0x0000000000ffff5a), 0.990, 0.999990149400000) \
    STEP( 199, UINT64_C(0x0000000000ffffeb), 0.995, 0.999998759356250) \
    STEP( 200, UINT64_C(0x0000000001000000), 1.000, 1.000000000000000) \
#endif /* JEMALLOC_INTERNAL_SMOOTHSTEP_H */
jemalloc/x64/include/jemalloc/internal/smoothstep.sh
New file
@@ -0,0 +1,101 @@
#!/bin/sh
#
# Generate a discrete lookup table for a sigmoid function in the smoothstep
# family (https://en.wikipedia.org/wiki/Smoothstep), where the lookup table
# entries correspond to x in [1/nsteps, 2/nsteps, ..., nsteps/nsteps].  Encode
# the entries using a binary fixed point representation.
#
# Usage: smoothstep.sh <variant> <nsteps> <bfp> <xprec> <yprec>
#
#        <variant> is in {smooth, smoother, smoothest}.
#        <nsteps> must be greater than zero.
#        <bfp> must be in [0..62]; reasonable values are roughly [10..30].
#        <xprec> is x decimal precision.
#        <yprec> is y decimal precision.
#set -x
cmd="sh smoothstep.sh $*"
variant=$1
nsteps=$2
bfp=$3
xprec=$4
yprec=$5
case "${variant}" in
  smooth)
    ;;
  smoother)
    ;;
  smoothest)
    ;;
  *)
    echo "Unsupported variant"
    exit 1
    ;;
esac
smooth() {
  step=$1
  y=`echo ${yprec} k ${step} ${nsteps} / sx _2 lx 3 ^ '*' 3 lx 2 ^ '*' + p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g'`
  h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g' | tr '.' ' ' | awk '{print $1}' `
}
smoother() {
  step=$1
  y=`echo ${yprec} k ${step} ${nsteps} / sx 6 lx 5 ^ '*' _15 lx 4 ^ '*' + 10 lx 3 ^ '*' + p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g'`
  h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g' | tr '.' ' ' | awk '{print $1}' `
}
smoothest() {
  step=$1
  y=`echo ${yprec} k ${step} ${nsteps} / sx _20 lx 7 ^ '*' 70 lx 6 ^ '*' + _84 lx 5 ^ '*' + 35 lx 4 ^ '*' + p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g'`
  h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g' | tr '.' ' ' | awk '{print $1}' `
}
cat <<EOF
#ifndef JEMALLOC_INTERNAL_SMOOTHSTEP_H
#define JEMALLOC_INTERNAL_SMOOTHSTEP_H
/*
 * This file was generated by the following command:
 *   $cmd
 */
/******************************************************************************/
/*
 * This header defines a precomputed table based on the smoothstep family of
 * sigmoidal curves (https://en.wikipedia.org/wiki/Smoothstep) that grow from 0
 * to 1 in 0 <= x <= 1.  The table is stored as integer fixed point values so
 * that floating point math can be avoided.
 *
 *                      3     2
 *   smoothstep(x) = -2x  + 3x
 *
 *                       5      4      3
 *   smootherstep(x) = 6x  - 15x  + 10x
 *
 *                          7      6      5      4
 *   smootheststep(x) = -20x  + 70x  - 84x  + 35x
 */
#define SMOOTHSTEP_VARIANT    "${variant}"
#define SMOOTHSTEP_NSTEPS    ${nsteps}
#define SMOOTHSTEP_BFP        ${bfp}
#define SMOOTHSTEP \\
 /* STEP(step, h,                            x,     y) */ \\
EOF
s=1
while [ $s -le $nsteps ] ; do
  $variant ${s}
  x=`echo ${xprec} k ${s} ${nsteps} / p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g'`
  printf '    STEP(%4d, UINT64_C(0x%016x), %s, %s) \\\n' ${s} ${h} ${x} ${y}
  s=$((s+1))
done
echo
cat <<EOF
#endif /* JEMALLOC_INTERNAL_SMOOTHSTEP_H */
EOF
jemalloc/x64/include/jemalloc/internal/spin.h
New file
@@ -0,0 +1,40 @@
#ifndef JEMALLOC_INTERNAL_SPIN_H
#define JEMALLOC_INTERNAL_SPIN_H
#define SPIN_INITIALIZER {0U}
typedef struct {
    unsigned iteration;
} spin_t;
static inline void
spin_cpu_spinwait() {
#  if HAVE_CPU_SPINWAIT
    CPU_SPINWAIT;
#  else
    volatile int x = 0;
    x = x;
#  endif
}
static inline void
spin_adaptive(spin_t *spin) {
    volatile uint32_t i;
    if (spin->iteration < 5) {
        for (i = 0; i < (1U << spin->iteration); i++) {
            spin_cpu_spinwait();
        }
        spin->iteration++;
    } else {
#ifdef _WIN32
        SwitchToThread();
#else
        sched_yield();
#endif
    }
}
#undef SPIN_INLINE
#endif /* JEMALLOC_INTERNAL_SPIN_H */
jemalloc/x64/include/jemalloc/internal/stats.h
New file
@@ -0,0 +1,31 @@
#ifndef JEMALLOC_INTERNAL_STATS_H
#define JEMALLOC_INTERNAL_STATS_H
/*  OPTION(opt,        var_name,    default,    set_value_to) */
#define STATS_PRINT_OPTIONS                        \
    OPTION('J',        json,        false,        true)        \
    OPTION('g',        general,    true,        false)        \
    OPTION('m',        merged,        config_stats,    false)        \
    OPTION('d',        destroyed,    config_stats,    false)        \
    OPTION('a',        unmerged,    config_stats,    false)        \
    OPTION('b',        bins,        true,        false)        \
    OPTION('l',        large,        true,        false)        \
    OPTION('x',        mutex,        true,        false)        \
    OPTION('e',        extents,    true,        false)
enum {
#define OPTION(o, v, d, s) stats_print_option_num_##v,
    STATS_PRINT_OPTIONS
#undef OPTION
    stats_print_tot_num_options
};
/* Options for stats_print. */
extern bool opt_stats_print;
extern char opt_stats_print_opts[stats_print_tot_num_options+1];
/* Implements je_malloc_stats_print. */
void stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
    const char *opts);
#endif /* JEMALLOC_INTERNAL_STATS_H */
jemalloc/x64/include/jemalloc/internal/sz.h
New file
@@ -0,0 +1,318 @@
#ifndef JEMALLOC_INTERNAL_SIZE_H
#define JEMALLOC_INTERNAL_SIZE_H
#include "jemalloc/internal/bit_util.h"
#include "jemalloc/internal/pages.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/util.h"
/*
 * sz module: Size computations.
 *
 * Some abbreviations used here:
 *   p: Page
 *   ind: Index
 *   s, sz: Size
 *   u: Usable size
 *   a: Aligned
 *
 * These are not always used completely consistently, but should be enough to
 * interpret function names.  E.g. sz_psz2ind converts page size to page size
 * index; sz_sa2u converts a (size, alignment) allocation request to the usable
 * size that would result from such an allocation.
 */
/*
 * sz_pind2sz_tab encodes the same information as could be computed by
 * sz_pind2sz_compute().
 */
extern size_t sz_pind2sz_tab[SC_NPSIZES + 1];
/*
 * sz_index2size_tab encodes the same information as could be computed (at
 * unacceptable cost in some code paths) by sz_index2size_compute().
 */
extern size_t sz_index2size_tab[SC_NSIZES];
/*
 * sz_size2index_tab is a compact lookup table that rounds request sizes up to
 * size classes.  In order to reduce cache footprint, the table is compressed,
 * and all accesses are via sz_size2index().
 */
extern uint8_t sz_size2index_tab[];
static const size_t sz_large_pad =
#ifdef JEMALLOC_CACHE_OBLIVIOUS
    PAGE
#else
    0
#endif
    ;
extern void sz_boot(const sc_data_t *sc_data);
JEMALLOC_ALWAYS_INLINE pszind_t
sz_psz2ind(size_t psz) {
    if (unlikely(psz > SC_LARGE_MAXCLASS)) {
        return SC_NPSIZES;
    }
    pszind_t x = lg_floor((psz<<1)-1);
    pszind_t shift = (x < SC_LG_NGROUP + LG_PAGE) ?
        0 : x - (SC_LG_NGROUP + LG_PAGE);
    pszind_t grp = shift << SC_LG_NGROUP;
    pszind_t lg_delta = (x < SC_LG_NGROUP + LG_PAGE + 1) ?
        LG_PAGE : x - SC_LG_NGROUP - 1;
    size_t delta_inverse_mask = ZU(-1) << lg_delta;
    pszind_t mod = ((((psz-1) & delta_inverse_mask) >> lg_delta)) &
        ((ZU(1) << SC_LG_NGROUP) - 1);
    pszind_t ind = grp + mod;
    return ind;
}
static inline size_t
sz_pind2sz_compute(pszind_t pind) {
    if (unlikely(pind == SC_NPSIZES)) {
        return SC_LARGE_MAXCLASS + PAGE;
    }
    size_t grp = pind >> SC_LG_NGROUP;
    size_t mod = pind & ((ZU(1) << SC_LG_NGROUP) - 1);
    size_t grp_size_mask = ~((!!grp)-1);
    size_t grp_size = ((ZU(1) << (LG_PAGE + (SC_LG_NGROUP-1))) << grp)
        & grp_size_mask;
    size_t shift = (grp == 0) ? 1 : grp;
    size_t lg_delta = shift + (LG_PAGE-1);
    size_t mod_size = (mod+1) << lg_delta;
    size_t sz = grp_size + mod_size;
    return sz;
}
static inline size_t
sz_pind2sz_lookup(pszind_t pind) {
    size_t ret = (size_t)sz_pind2sz_tab[pind];
    assert(ret == sz_pind2sz_compute(pind));
    return ret;
}
static inline size_t
sz_pind2sz(pszind_t pind) {
    assert(pind < SC_NPSIZES + 1);
    return sz_pind2sz_lookup(pind);
}
static inline size_t
sz_psz2u(size_t psz) {
    if (unlikely(psz > SC_LARGE_MAXCLASS)) {
        return SC_LARGE_MAXCLASS + PAGE;
    }
    size_t x = lg_floor((psz<<1)-1);
    size_t lg_delta = (x < SC_LG_NGROUP + LG_PAGE + 1) ?
        LG_PAGE : x - SC_LG_NGROUP - 1;
    size_t delta = ZU(1) << lg_delta;
    size_t delta_mask = delta - 1;
    size_t usize = (psz + delta_mask) & ~delta_mask;
    return usize;
}
static inline szind_t
sz_size2index_compute(size_t size) {
    if (unlikely(size > SC_LARGE_MAXCLASS)) {
        return SC_NSIZES;
    }
    if (size == 0) {
        return 0;
    }
#if (SC_NTINY != 0)
    if (size <= (ZU(1) << SC_LG_TINY_MAXCLASS)) {
        szind_t lg_tmin = SC_LG_TINY_MAXCLASS - SC_NTINY + 1;
        szind_t lg_ceil = lg_floor(pow2_ceil_zu(size));
        return (lg_ceil < lg_tmin ? 0 : lg_ceil - lg_tmin);
    }
#endif
    {
        szind_t x = lg_floor((size<<1)-1);
        szind_t shift = (x < SC_LG_NGROUP + LG_QUANTUM) ? 0 :
            x - (SC_LG_NGROUP + LG_QUANTUM);
        szind_t grp = shift << SC_LG_NGROUP;
        szind_t lg_delta = (x < SC_LG_NGROUP + LG_QUANTUM + 1)
            ? LG_QUANTUM : x - SC_LG_NGROUP - 1;
        size_t delta_inverse_mask = ZU(-1) << lg_delta;
        szind_t mod = ((((size-1) & delta_inverse_mask) >> lg_delta)) &
            ((ZU(1) << SC_LG_NGROUP) - 1);
        szind_t index = SC_NTINY + grp + mod;
        return index;
    }
}
JEMALLOC_ALWAYS_INLINE szind_t
sz_size2index_lookup(size_t size) {
    assert(size <= SC_LOOKUP_MAXCLASS);
    szind_t ret = (sz_size2index_tab[(size + (ZU(1) << SC_LG_TINY_MIN) - 1)
                     >> SC_LG_TINY_MIN]);
    assert(ret == sz_size2index_compute(size));
    return ret;
}
JEMALLOC_ALWAYS_INLINE szind_t
sz_size2index(size_t size) {
    if (likely(size <= SC_LOOKUP_MAXCLASS)) {
        return sz_size2index_lookup(size);
    }
    return sz_size2index_compute(size);
}
static inline size_t
sz_index2size_compute(szind_t index) {
#if (SC_NTINY > 0)
    if (index < SC_NTINY) {
        return (ZU(1) << (SC_LG_TINY_MAXCLASS - SC_NTINY + 1 + index));
    }
#endif
    {
        size_t reduced_index = index - SC_NTINY;
        size_t grp = reduced_index >> SC_LG_NGROUP;
        size_t mod = reduced_index & ((ZU(1) << SC_LG_NGROUP) -
            1);
        size_t grp_size_mask = ~((!!grp)-1);
        size_t grp_size = ((ZU(1) << (LG_QUANTUM +
            (SC_LG_NGROUP-1))) << grp) & grp_size_mask;
        size_t shift = (grp == 0) ? 1 : grp;
        size_t lg_delta = shift + (LG_QUANTUM-1);
        size_t mod_size = (mod+1) << lg_delta;
        size_t usize = grp_size + mod_size;
        return usize;
    }
}
JEMALLOC_ALWAYS_INLINE size_t
sz_index2size_lookup(szind_t index) {
    size_t ret = (size_t)sz_index2size_tab[index];
    assert(ret == sz_index2size_compute(index));
    return ret;
}
JEMALLOC_ALWAYS_INLINE size_t
sz_index2size(szind_t index) {
    assert(index < SC_NSIZES);
    return sz_index2size_lookup(index);
}
JEMALLOC_ALWAYS_INLINE size_t
sz_s2u_compute(size_t size) {
    if (unlikely(size > SC_LARGE_MAXCLASS)) {
        return 0;
    }
    if (size == 0) {
        size++;
    }
#if (SC_NTINY > 0)
    if (size <= (ZU(1) << SC_LG_TINY_MAXCLASS)) {
        size_t lg_tmin = SC_LG_TINY_MAXCLASS - SC_NTINY + 1;
        size_t lg_ceil = lg_floor(pow2_ceil_zu(size));
        return (lg_ceil < lg_tmin ? (ZU(1) << lg_tmin) :
            (ZU(1) << lg_ceil));
    }
#endif
    {
        size_t x = lg_floor((size<<1)-1);
        size_t lg_delta = (x < SC_LG_NGROUP + LG_QUANTUM + 1)
            ?  LG_QUANTUM : x - SC_LG_NGROUP - 1;
        size_t delta = ZU(1) << lg_delta;
        size_t delta_mask = delta - 1;
        size_t usize = (size + delta_mask) & ~delta_mask;
        return usize;
    }
}
JEMALLOC_ALWAYS_INLINE size_t
sz_s2u_lookup(size_t size) {
    size_t ret = sz_index2size_lookup(sz_size2index_lookup(size));
    assert(ret == sz_s2u_compute(size));
    return ret;
}
/*
 * Compute usable size that would result from allocating an object with the
 * specified size.
 */
JEMALLOC_ALWAYS_INLINE size_t
sz_s2u(size_t size) {
    if (likely(size <= SC_LOOKUP_MAXCLASS)) {
        return sz_s2u_lookup(size);
    }
    return sz_s2u_compute(size);
}
/*
 * Compute usable size that would result from allocating an object with the
 * specified size and alignment.
 */
JEMALLOC_ALWAYS_INLINE size_t
sz_sa2u(size_t size, size_t alignment) {
    size_t usize;
    assert(alignment != 0 && ((alignment - 1) & alignment) == 0);
    /* Try for a small size class. */
    if (size <= SC_SMALL_MAXCLASS && alignment < PAGE) {
        /*
         * Round size up to the nearest multiple of alignment.
         *
         * This done, we can take advantage of the fact that for each
         * small size class, every object is aligned at the smallest
         * power of two that is non-zero in the base two representation
         * of the size.  For example:
         *
         *   Size |   Base 2 | Minimum alignment
         *   -----+----------+------------------
         *     96 |  1100000 |  32
         *    144 | 10100000 |  32
         *    192 | 11000000 |  64
         */
        usize = sz_s2u(ALIGNMENT_CEILING(size, alignment));
        if (usize < SC_LARGE_MINCLASS) {
            return usize;
        }
    }
    /* Large size class.  Beware of overflow. */
    if (unlikely(alignment > SC_LARGE_MAXCLASS)) {
        return 0;
    }
    /* Make sure result is a large size class. */
    if (size <= SC_LARGE_MINCLASS) {
        usize = SC_LARGE_MINCLASS;
    } else {
        usize = sz_s2u(size);
        if (usize < size) {
            /* size_t overflow. */
            return 0;
        }
    }
    /*
     * Calculate the multi-page mapping that large_palloc() would need in
     * order to guarantee the alignment.
     */
    if (usize + sz_large_pad + PAGE_CEILING(alignment) - PAGE < usize) {
        /* size_t overflow. */
        return 0;
    }
    return usize;
}
#endif /* JEMALLOC_INTERNAL_SIZE_H */
jemalloc/x64/include/jemalloc/internal/tcache_externs.h
New file
@@ -0,0 +1,53 @@
#ifndef JEMALLOC_INTERNAL_TCACHE_EXTERNS_H
#define JEMALLOC_INTERNAL_TCACHE_EXTERNS_H
extern bool    opt_tcache;
extern ssize_t    opt_lg_tcache_max;
extern cache_bin_info_t    *tcache_bin_info;
/*
 * Number of tcache bins.  There are SC_NBINS small-object bins, plus 0 or more
 * large-object bins.
 */
extern unsigned    nhbins;
/* Maximum cached size class. */
extern size_t    tcache_maxclass;
/*
 * Explicit tcaches, managed via the tcache.{create,flush,destroy} mallctls and
 * usable via the MALLOCX_TCACHE() flag.  The automatic per thread tcaches are
 * completely disjoint from this data structure.  tcaches starts off as a sparse
 * array, so it has no physical memory footprint until individual pages are
 * touched.  This allows the entire array to be allocated the first time an
 * explicit tcache is created without a disproportionate impact on memory usage.
 */
extern tcaches_t    *tcaches;
size_t    tcache_salloc(tsdn_t *tsdn, const void *ptr);
void    tcache_event_hard(tsd_t *tsd, tcache_t *tcache);
void    *tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
    cache_bin_t *tbin, szind_t binind, bool *tcache_success);
void    tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,
    szind_t binind, unsigned rem);
void    tcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,
    unsigned rem, tcache_t *tcache);
void    tcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache,
    arena_t *arena);
tcache_t *tcache_create_explicit(tsd_t *tsd);
void    tcache_cleanup(tsd_t *tsd);
void    tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);
bool    tcaches_create(tsd_t *tsd, unsigned *r_ind);
void    tcaches_flush(tsd_t *tsd, unsigned ind);
void    tcaches_destroy(tsd_t *tsd, unsigned ind);
bool    tcache_boot(tsdn_t *tsdn);
void tcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);
void tcache_prefork(tsdn_t *tsdn);
void tcache_postfork_parent(tsdn_t *tsdn);
void tcache_postfork_child(tsdn_t *tsdn);
void tcache_flush(tsd_t *tsd);
bool tsd_tcache_data_init(tsd_t *tsd);
bool tsd_tcache_enabled_data_init(tsd_t *tsd);
#endif /* JEMALLOC_INTERNAL_TCACHE_EXTERNS_H */
jemalloc/x64/include/jemalloc/internal/tcache_inlines.h
New file
@@ -0,0 +1,227 @@
#ifndef JEMALLOC_INTERNAL_TCACHE_INLINES_H
#define JEMALLOC_INTERNAL_TCACHE_INLINES_H
#include "jemalloc/internal/bin.h"
#include "jemalloc/internal/jemalloc_internal_types.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/sz.h"
#include "jemalloc/internal/ticker.h"
#include "jemalloc/internal/util.h"
static inline bool
tcache_enabled_get(tsd_t *tsd) {
    return tsd_tcache_enabled_get(tsd);
}
static inline void
tcache_enabled_set(tsd_t *tsd, bool enabled) {
    bool was_enabled = tsd_tcache_enabled_get(tsd);
    if (!was_enabled && enabled) {
        tsd_tcache_data_init(tsd);
    } else if (was_enabled && !enabled) {
        tcache_cleanup(tsd);
    }
    /* Commit the state last.  Above calls check current state. */
    tsd_tcache_enabled_set(tsd, enabled);
    tsd_slow_update(tsd);
}
JEMALLOC_ALWAYS_INLINE void
tcache_event(tsd_t *tsd, tcache_t *tcache) {
    if (TCACHE_GC_INCR == 0) {
        return;
    }
    if (unlikely(ticker_tick(&tcache->gc_ticker))) {
        tcache_event_hard(tsd, tcache);
    }
}
JEMALLOC_ALWAYS_INLINE void *
tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
    size_t size, szind_t binind, bool zero, bool slow_path) {
    void *ret;
    cache_bin_t *bin;
    bool tcache_success;
    size_t usize JEMALLOC_CC_SILENCE_INIT(0);
    assert(binind < SC_NBINS);
    bin = tcache_small_bin_get(tcache, binind);
    ret = cache_bin_alloc_easy(bin, &tcache_success);
    assert(tcache_success == (ret != NULL));
    if (unlikely(!tcache_success)) {
        bool tcache_hard_success;
        arena = arena_choose(tsd, arena);
        if (unlikely(arena == NULL)) {
            return NULL;
        }
        ret = tcache_alloc_small_hard(tsd_tsdn(tsd), arena, tcache,
            bin, binind, &tcache_hard_success);
        if (tcache_hard_success == false) {
            return NULL;
        }
    }
    assert(ret);
    /*
     * Only compute usize if required.  The checks in the following if
     * statement are all static.
     */
    if (config_prof || (slow_path && config_fill) || unlikely(zero)) {
        usize = sz_index2size(binind);
        assert(tcache_salloc(tsd_tsdn(tsd), ret) == usize);
    }
    if (likely(!zero)) {
        if (slow_path && config_fill) {
            if (unlikely(opt_junk_alloc)) {
                arena_alloc_junk_small(ret, &bin_infos[binind],
                    false);
            } else if (unlikely(opt_zero)) {
                memset(ret, 0, usize);
            }
        }
    } else {
        if (slow_path && config_fill && unlikely(opt_junk_alloc)) {
            arena_alloc_junk_small(ret, &bin_infos[binind], true);
        }
        memset(ret, 0, usize);
    }
    if (config_stats) {
        bin->tstats.nrequests++;
    }
    if (config_prof) {
        tcache->prof_accumbytes += usize;
    }
    tcache_event(tsd, tcache);
    return ret;
}
JEMALLOC_ALWAYS_INLINE void *
tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
    szind_t binind, bool zero, bool slow_path) {
    void *ret;
    cache_bin_t *bin;
    bool tcache_success;
    assert(binind >= SC_NBINS &&binind < nhbins);
    bin = tcache_large_bin_get(tcache, binind);
    ret = cache_bin_alloc_easy(bin, &tcache_success);
    assert(tcache_success == (ret != NULL));
    if (unlikely(!tcache_success)) {
        /*
         * Only allocate one large object at a time, because it's quite
         * expensive to create one and not use it.
         */
        arena = arena_choose(tsd, arena);
        if (unlikely(arena == NULL)) {
            return NULL;
        }
        ret = large_malloc(tsd_tsdn(tsd), arena, sz_s2u(size), zero);
        if (ret == NULL) {
            return NULL;
        }
    } else {
        size_t usize JEMALLOC_CC_SILENCE_INIT(0);
        /* Only compute usize on demand */
        if (config_prof || (slow_path && config_fill) ||
            unlikely(zero)) {
            usize = sz_index2size(binind);
            assert(usize <= tcache_maxclass);
        }
        if (likely(!zero)) {
            if (slow_path && config_fill) {
                if (unlikely(opt_junk_alloc)) {
                    memset(ret, JEMALLOC_ALLOC_JUNK,
                        usize);
                } else if (unlikely(opt_zero)) {
                    memset(ret, 0, usize);
                }
            }
        } else {
            memset(ret, 0, usize);
        }
        if (config_stats) {
            bin->tstats.nrequests++;
        }
        if (config_prof) {
            tcache->prof_accumbytes += usize;
        }
    }
    tcache_event(tsd, tcache);
    return ret;
}
JEMALLOC_ALWAYS_INLINE void
tcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,
    bool slow_path) {
    cache_bin_t *bin;
    cache_bin_info_t *bin_info;
    assert(tcache_salloc(tsd_tsdn(tsd), ptr)
        <= SC_SMALL_MAXCLASS);
    if (slow_path && config_fill && unlikely(opt_junk_free)) {
        arena_dalloc_junk_small(ptr, &bin_infos[binind]);
    }
    bin = tcache_small_bin_get(tcache, binind);
    bin_info = &tcache_bin_info[binind];
    if (unlikely(!cache_bin_dalloc_easy(bin, bin_info, ptr))) {
        tcache_bin_flush_small(tsd, tcache, bin, binind,
            (bin_info->ncached_max >> 1));
        bool ret = cache_bin_dalloc_easy(bin, bin_info, ptr);
        assert(ret);
    }
    tcache_event(tsd, tcache);
}
JEMALLOC_ALWAYS_INLINE void
tcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,
    bool slow_path) {
    cache_bin_t *bin;
    cache_bin_info_t *bin_info;
    assert(tcache_salloc(tsd_tsdn(tsd), ptr)
        > SC_SMALL_MAXCLASS);
    assert(tcache_salloc(tsd_tsdn(tsd), ptr) <= tcache_maxclass);
    if (slow_path && config_fill && unlikely(opt_junk_free)) {
        large_dalloc_junk(ptr, sz_index2size(binind));
    }
    bin = tcache_large_bin_get(tcache, binind);
    bin_info = &tcache_bin_info[binind];
    if (unlikely(bin->ncached == bin_info->ncached_max)) {
        tcache_bin_flush_large(tsd, bin, binind,
            (bin_info->ncached_max >> 1), tcache);
    }
    assert(bin->ncached < bin_info->ncached_max);
    bin->ncached++;
    *(bin->avail - bin->ncached) = ptr;
    tcache_event(tsd, tcache);
}
JEMALLOC_ALWAYS_INLINE tcache_t *
tcaches_get(tsd_t *tsd, unsigned ind) {
    tcaches_t *elm = &tcaches[ind];
    if (unlikely(elm->tcache == NULL)) {
        malloc_printf("<jemalloc>: invalid tcache id (%u).\n", ind);
        abort();
    } else if (unlikely(elm->tcache == TCACHES_ELM_NEED_REINIT)) {
        elm->tcache = tcache_create_explicit(tsd);
    }
    return elm->tcache;
}
#endif /* JEMALLOC_INTERNAL_TCACHE_INLINES_H */
jemalloc/x64/include/jemalloc/internal/tcache_structs.h
New file
@@ -0,0 +1,70 @@
#ifndef JEMALLOC_INTERNAL_TCACHE_STRUCTS_H
#define JEMALLOC_INTERNAL_TCACHE_STRUCTS_H
#include "jemalloc/internal/cache_bin.h"
#include "jemalloc/internal/ql.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/ticker.h"
#include "jemalloc/internal/tsd_types.h"
/* Various uses of this struct need it to be a named type. */
typedef ql_elm(tsd_t) tsd_link_t;
struct tcache_s {
    /*
     * To minimize our cache-footprint, we put the frequently accessed data
     * together at the start of this struct.
     */
    /* Cleared after arena_prof_accum(). */
    uint64_t    prof_accumbytes;
    /* Drives incremental GC. */
    ticker_t    gc_ticker;
    /*
     * The pointer stacks associated with bins follow as a contiguous array.
     * During tcache initialization, the avail pointer in each element of
     * tbins is initialized to point to the proper offset within this array.
     */
    cache_bin_t    bins_small[SC_NBINS];
    /*
     * This data is less hot; we can be a little less careful with our
     * footprint here.
     */
    /* Lets us track all the tcaches in an arena. */
    ql_elm(tcache_t) link;
    /* Logically scoped to tsd, but put here for cache layout reasons. */
    ql_elm(tsd_t) tsd_link;
    bool in_hook;
    /*
     * The descriptor lets the arena find our cache bins without seeing the
     * tcache definition.  This enables arenas to aggregate stats across
     * tcaches without having a tcache dependency.
     */
    cache_bin_array_descriptor_t cache_bin_array_descriptor;
    /* The arena this tcache is associated with. */
    arena_t        *arena;
    /* Next bin to GC. */
    szind_t        next_gc_bin;
    /* For small bins, fill (ncached_max >> lg_fill_div). */
    uint8_t        lg_fill_div[SC_NBINS];
    /*
     * We put the cache bins for large size classes at the end of the
     * struct, since some of them might not get used.  This might end up
     * letting us avoid touching an extra page if we don't have to.
     */
    cache_bin_t    bins_large[SC_NSIZES-SC_NBINS];
};
/* Linkage for list of available (previously used) explicit tcache IDs. */
struct tcaches_s {
    union {
        tcache_t    *tcache;
        tcaches_t    *next;
    };
};
#endif /* JEMALLOC_INTERNAL_TCACHE_STRUCTS_H */
jemalloc/x64/include/jemalloc/internal/tcache_types.h
New file
@@ -0,0 +1,59 @@
#ifndef JEMALLOC_INTERNAL_TCACHE_TYPES_H
#define JEMALLOC_INTERNAL_TCACHE_TYPES_H
#include "jemalloc/internal/sc.h"
typedef struct tcache_s tcache_t;
typedef struct tcaches_s tcaches_t;
/*
 * tcache pointers close to NULL are used to encode state information that is
 * used for two purposes: preventing thread caching on a per thread basis and
 * cleaning up during thread shutdown.
 */
#define TCACHE_STATE_DISABLED        ((tcache_t *)(uintptr_t)1)
#define TCACHE_STATE_REINCARNATED    ((tcache_t *)(uintptr_t)2)
#define TCACHE_STATE_PURGATORY        ((tcache_t *)(uintptr_t)3)
#define TCACHE_STATE_MAX        TCACHE_STATE_PURGATORY
/*
 * Absolute minimum number of cache slots for each small bin.
 */
#define TCACHE_NSLOTS_SMALL_MIN        20
/*
 * Absolute maximum number of cache slots for each small bin in the thread
 * cache.  This is an additional constraint beyond that imposed as: twice the
 * number of regions per slab for this size class.
 *
 * This constant must be an even number.
 */
#define TCACHE_NSLOTS_SMALL_MAX        200
/* Number of cache slots for large size classes. */
#define TCACHE_NSLOTS_LARGE        20
/* (1U << opt_lg_tcache_max) is used to compute tcache_maxclass. */
#define LG_TCACHE_MAXCLASS_DEFAULT    15
/*
 * TCACHE_GC_SWEEP is the approximate number of allocation events between
 * full GC sweeps.  Integer rounding may cause the actual number to be
 * slightly higher, since GC is performed incrementally.
 */
#define TCACHE_GC_SWEEP            8192
/* Number of tcache allocation/deallocation events between incremental GCs. */
#define TCACHE_GC_INCR                            \
    ((TCACHE_GC_SWEEP / SC_NBINS) + ((TCACHE_GC_SWEEP / SC_NBINS == 0) ? 0 : 1))
/* Used in TSD static initializer only. Real init in tcache_data_init(). */
#define TCACHE_ZERO_INITIALIZER {0}
/* Used in TSD static initializer only. Will be initialized to opt_tcache. */
#define TCACHE_ENABLED_ZERO_INITIALIZER false
/* Used for explicit tcache only. Means flushed but not destroyed. */
#define TCACHES_ELM_NEED_REINIT ((tcache_t *)(uintptr_t)1)
#endif /* JEMALLOC_INTERNAL_TCACHE_TYPES_H */
jemalloc/x64/include/jemalloc/internal/test_hooks.h
New file
@@ -0,0 +1,19 @@
#ifndef JEMALLOC_INTERNAL_TEST_HOOKS_H
#define JEMALLOC_INTERNAL_TEST_HOOKS_H
extern JEMALLOC_EXPORT void (*test_hooks_arena_new_hook)();
extern JEMALLOC_EXPORT void (*test_hooks_libc_hook)();
#define JEMALLOC_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)
#define open JEMALLOC_HOOK(open, test_hooks_libc_hook)
#define read JEMALLOC_HOOK(read, test_hooks_libc_hook)
#define write JEMALLOC_HOOK(write, test_hooks_libc_hook)
#define readlink JEMALLOC_HOOK(readlink, test_hooks_libc_hook)
#define close JEMALLOC_HOOK(close, test_hooks_libc_hook)
#define creat JEMALLOC_HOOK(creat, test_hooks_libc_hook)
#define secure_getenv JEMALLOC_HOOK(secure_getenv, test_hooks_libc_hook)
/* Note that this is undef'd and re-define'd in src/prof.c. */
#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
#endif /* JEMALLOC_INTERNAL_TEST_HOOKS_H */
jemalloc/x64/include/jemalloc/internal/ticker.h
New file
@@ -0,0 +1,91 @@
#ifndef JEMALLOC_INTERNAL_TICKER_H
#define JEMALLOC_INTERNAL_TICKER_H
#include "jemalloc/internal/util.h"
/**
 * A ticker makes it easy to count-down events until some limit.  You
 * ticker_init the ticker to trigger every nticks events.  You then notify it
 * that an event has occurred with calls to ticker_tick (or that nticks events
 * have occurred with a call to ticker_ticks), which will return true (and reset
 * the counter) if the countdown hit zero.
 */
typedef struct {
    int32_t tick;
    int32_t nticks;
} ticker_t;
static inline void
ticker_init(ticker_t *ticker, int32_t nticks) {
    ticker->tick = nticks;
    ticker->nticks = nticks;
}
static inline void
ticker_copy(ticker_t *ticker, const ticker_t *other) {
    *ticker = *other;
}
static inline int32_t
ticker_read(const ticker_t *ticker) {
    return ticker->tick;
}
/*
 * Not intended to be a public API.  Unfortunately, on x86, neither gcc nor
 * clang seems smart enough to turn
 *   ticker->tick -= nticks;
 *   if (unlikely(ticker->tick < 0)) {
 *     fixup ticker
 *     return true;
 *   }
 *   return false;
 * into
 *   subq %nticks_reg, (%ticker_reg)
 *   js fixup ticker
 *
 * unless we force "fixup ticker" out of line.  In that case, gcc gets it right,
 * but clang now does worse than before.  So, on x86 with gcc, we force it out
 * of line, but otherwise let the inlining occur.  Ordinarily this wouldn't be
 * worth the hassle, but this is on the fast path of both malloc and free (via
 * tcache_event).
 */
#if defined(__GNUC__) && !defined(__clang__)                \
    && (defined(__x86_64__) || defined(__i386__))
JEMALLOC_NOINLINE
#endif
static bool
ticker_fixup(ticker_t *ticker) {
    ticker->tick = ticker->nticks;
    return true;
}
static inline bool
ticker_ticks(ticker_t *ticker, int32_t nticks) {
    ticker->tick -= nticks;
    if (unlikely(ticker->tick < 0)) {
        return ticker_fixup(ticker);
    }
    return false;
}
static inline bool
ticker_tick(ticker_t *ticker) {
    return ticker_ticks(ticker, 1);
}
/*
 * Try to tick.  If ticker would fire, return true, but rely on
 * slowpath to reset ticker.
 */
static inline bool
ticker_trytick(ticker_t *ticker) {
    --ticker->tick;
    if (unlikely(ticker->tick < 0)) {
        return true;
    }
    return false;
}
#endif /* JEMALLOC_INTERNAL_TICKER_H */
jemalloc/x64/include/jemalloc/internal/tsd.h
New file
@@ -0,0 +1,415 @@
#ifndef JEMALLOC_INTERNAL_TSD_H
#define JEMALLOC_INTERNAL_TSD_H
#include "jemalloc/internal/arena_types.h"
#include "jemalloc/internal/assert.h"
#include "jemalloc/internal/bin_types.h"
#include "jemalloc/internal/jemalloc_internal_externs.h"
#include "jemalloc/internal/prof_types.h"
#include "jemalloc/internal/ql.h"
#include "jemalloc/internal/rtree_tsd.h"
#include "jemalloc/internal/tcache_types.h"
#include "jemalloc/internal/tcache_structs.h"
#include "jemalloc/internal/util.h"
#include "jemalloc/internal/witness.h"
/*
 * Thread-Specific-Data layout
 * --- data accessed on tcache fast path: state, rtree_ctx, stats, prof ---
 * s: state
 * e: tcache_enabled
 * m: thread_allocated (config_stats)
 * f: thread_deallocated (config_stats)
 * p: prof_tdata (config_prof)
 * c: rtree_ctx (rtree cache accessed on deallocation)
 * t: tcache
 * --- data not accessed on tcache fast path: arena-related fields ---
 * d: arenas_tdata_bypass
 * r: reentrancy_level
 * x: narenas_tdata
 * i: iarena
 * a: arena
 * o: arenas_tdata
 * Loading TSD data is on the critical path of basically all malloc operations.
 * In particular, tcache and rtree_ctx rely on hot CPU cache to be effective.
 * Use a compact layout to reduce cache footprint.
 * +--- 64-bit and 64B cacheline; 1B each letter; First byte on the left. ---+
 * |----------------------------  1st cacheline  ----------------------------|
 * | sedrxxxx mmmmmmmm ffffffff pppppppp [c * 32  ........ ........ .......] |
 * |----------------------------  2nd cacheline  ----------------------------|
 * | [c * 64  ........ ........ ........ ........ ........ ........ .......] |
 * |----------------------------  3nd cacheline  ----------------------------|
 * | [c * 32  ........ ........ .......] iiiiiiii aaaaaaaa oooooooo [t...... |
 * +-------------------------------------------------------------------------+
 * Note: the entire tcache is embedded into TSD and spans multiple cachelines.
 *
 * The last 3 members (i, a and o) before tcache isn't really needed on tcache
 * fast path.  However we have a number of unused tcache bins and witnesses
 * (never touched unless config_debug) at the end of tcache, so we place them
 * there to avoid breaking the cachelines and possibly paging in an extra page.
 */
#ifdef JEMALLOC_JET
typedef void (*test_callback_t)(int *);
#  define MALLOC_TSD_TEST_DATA_INIT 0x72b65c10
#  define MALLOC_TEST_TSD \
    O(test_data,        int,            int)        \
    O(test_callback,        test_callback_t,    int)
#  define MALLOC_TEST_TSD_INITIALIZER , MALLOC_TSD_TEST_DATA_INIT, NULL
#else
#  define MALLOC_TEST_TSD
#  define MALLOC_TEST_TSD_INITIALIZER
#endif
/*  O(name,            type,            nullable type */
#define MALLOC_TSD                            \
    O(tcache_enabled,        bool,            bool)        \
    O(arenas_tdata_bypass,    bool,            bool)        \
    O(reentrancy_level,        int8_t,            int8_t)        \
    O(narenas_tdata,        uint32_t,        uint32_t)    \
    O(offset_state,        uint64_t,        uint64_t)    \
    O(thread_allocated,        uint64_t,        uint64_t)    \
    O(thread_deallocated,    uint64_t,        uint64_t)    \
    O(bytes_until_sample,    int64_t,        int64_t)    \
    O(prof_tdata,        prof_tdata_t *,        prof_tdata_t *)    \
    O(rtree_ctx,        rtree_ctx_t,        rtree_ctx_t)    \
    O(iarena,            arena_t *,        arena_t *)    \
    O(arena,            arena_t *,        arena_t *)    \
    O(arenas_tdata,        arena_tdata_t *,    arena_tdata_t *)\
    O(binshards,        tsd_binshards_t,    tsd_binshards_t)\
    O(tcache,            tcache_t,        tcache_t)    \
    O(witness_tsd,              witness_tsd_t,        witness_tsdn_t)    \
    MALLOC_TEST_TSD
#define TSD_INITIALIZER {                        \
    ATOMIC_INIT(tsd_state_uninitialized),                \
    TCACHE_ENABLED_ZERO_INITIALIZER,                    \
    false,                                \
    0,                                    \
    0,                                    \
    0,                                    \
    0,                                    \
    0,                                    \
    0,                                    \
    NULL,                                \
    RTREE_CTX_ZERO_INITIALIZER,                        \
    NULL,                                \
    NULL,                                \
    NULL,                                \
    TSD_BINSHARDS_ZERO_INITIALIZER,                    \
    TCACHE_ZERO_INITIALIZER,                        \
    WITNESS_TSD_INITIALIZER                        \
    MALLOC_TEST_TSD_INITIALIZER                        \
}
void *malloc_tsd_malloc(size_t size);
void malloc_tsd_dalloc(void *wrapper);
void malloc_tsd_cleanup_register(bool (*f)(void));
tsd_t *malloc_tsd_boot0(void);
void malloc_tsd_boot1(void);
void tsd_cleanup(void *arg);
tsd_t *tsd_fetch_slow(tsd_t *tsd, bool internal);
void tsd_state_set(tsd_t *tsd, uint8_t new_state);
void tsd_slow_update(tsd_t *tsd);
void tsd_prefork(tsd_t *tsd);
void tsd_postfork_parent(tsd_t *tsd);
void tsd_postfork_child(tsd_t *tsd);
/*
 * Call ..._inc when your module wants to take all threads down the slow paths,
 * and ..._dec when it no longer needs to.
 */
void tsd_global_slow_inc(tsdn_t *tsdn);
void tsd_global_slow_dec(tsdn_t *tsdn);
bool tsd_global_slow();
enum {
    /* Common case --> jnz. */
    tsd_state_nominal = 0,
    /* Initialized but on slow path. */
    tsd_state_nominal_slow = 1,
    /*
     * Some thread has changed global state in such a way that all nominal
     * threads need to recompute their fast / slow status the next time they
     * get a chance.
     *
     * Any thread can change another thread's status *to* recompute, but
     * threads are the only ones who can change their status *from*
     * recompute.
     */
    tsd_state_nominal_recompute = 2,
    /*
     * The above nominal states should be lower values.  We use
     * tsd_nominal_max to separate nominal states from threads in the
     * process of being born / dying.
     */
    tsd_state_nominal_max = 2,
    /*
     * A thread might free() during its death as its only allocator action;
     * in such scenarios, we need tsd, but set up in such a way that no
     * cleanup is necessary.
     */
    tsd_state_minimal_initialized = 3,
    /* States during which we know we're in thread death. */
    tsd_state_purgatory = 4,
    tsd_state_reincarnated = 5,
    /*
     * What it says on the tin; tsd that hasn't been initialized.  Note
     * that even when the tsd struct lives in TLS, when need to keep track
     * of stuff like whether or not our pthread destructors have been
     * scheduled, so this really truly is different than the nominal state.
     */
    tsd_state_uninitialized = 6
};
/*
 * Some TSD accesses can only be done in a nominal state.  To enforce this, we
 * wrap TSD member access in a function that asserts on TSD state, and mangle
 * field names to prevent touching them accidentally.
 */
#define TSD_MANGLE(n) cant_access_tsd_items_directly_use_a_getter_or_setter_##n
#ifdef JEMALLOC_U8_ATOMICS
#  define tsd_state_t atomic_u8_t
#  define tsd_atomic_load atomic_load_u8
#  define tsd_atomic_store atomic_store_u8
#  define tsd_atomic_exchange atomic_exchange_u8
#else
#  define tsd_state_t atomic_u32_t
#  define tsd_atomic_load atomic_load_u32
#  define tsd_atomic_store atomic_store_u32
#  define tsd_atomic_exchange atomic_exchange_u32
#endif
/* The actual tsd. */
struct tsd_s {
    /*
     * The contents should be treated as totally opaque outside the tsd
     * module.  Access any thread-local state through the getters and
     * setters below.
     */
    /*
     * We manually limit the state to just a single byte.  Unless the 8-bit
     * atomics are unavailable (which is rare).
     */
    tsd_state_t state;
#define O(n, t, nt)                            \
    t TSD_MANGLE(n);
MALLOC_TSD
#undef O
};
JEMALLOC_ALWAYS_INLINE uint8_t
tsd_state_get(tsd_t *tsd) {
    /*
     * This should be atomic.  Unfortunately, compilers right now can't tell
     * that this can be done as a memory comparison, and forces a load into
     * a register that hurts fast-path performance.
     */
    /* return atomic_load_u8(&tsd->state, ATOMIC_RELAXED); */
    return *(uint8_t *)&tsd->state;
}
/*
 * Wrapper around tsd_t that makes it possible to avoid implicit conversion
 * between tsd_t and tsdn_t, where tsdn_t is "nullable" and has to be
 * explicitly converted to tsd_t, which is non-nullable.
 */
struct tsdn_s {
    tsd_t tsd;
};
#define TSDN_NULL ((tsdn_t *)0)
JEMALLOC_ALWAYS_INLINE tsdn_t *
tsd_tsdn(tsd_t *tsd) {
    return (tsdn_t *)tsd;
}
JEMALLOC_ALWAYS_INLINE bool
tsdn_null(const tsdn_t *tsdn) {
    return tsdn == NULL;
}
JEMALLOC_ALWAYS_INLINE tsd_t *
tsdn_tsd(tsdn_t *tsdn) {
    assert(!tsdn_null(tsdn));
    return &tsdn->tsd;
}
/*
 * We put the platform-specific data declarations and inlines into their own
 * header files to avoid cluttering this file.  They define tsd_boot0,
 * tsd_boot1, tsd_boot, tsd_booted_get, tsd_get_allocates, tsd_get, and tsd_set.
 */
#ifdef JEMALLOC_MALLOC_THREAD_CLEANUP
#include "jemalloc/internal/tsd_malloc_thread_cleanup.h"
#elif (defined(JEMALLOC_TLS))
#include "jemalloc/internal/tsd_tls.h"
#elif (defined(_WIN32))
#include "jemalloc/internal/tsd_win.h"
#else
#include "jemalloc/internal/tsd_generic.h"
#endif
/*
 * tsd_foop_get_unsafe(tsd) returns a pointer to the thread-local instance of
 * foo.  This omits some safety checks, and so can be used during tsd
 * initialization and cleanup.
 */
#define O(n, t, nt)                            \
JEMALLOC_ALWAYS_INLINE t *                        \
tsd_##n##p_get_unsafe(tsd_t *tsd) {                    \
    return &tsd->TSD_MANGLE(n);                    \
}
MALLOC_TSD
#undef O
/* tsd_foop_get(tsd) returns a pointer to the thread-local instance of foo. */
#define O(n, t, nt)                            \
JEMALLOC_ALWAYS_INLINE t *                        \
tsd_##n##p_get(tsd_t *tsd) {                        \
    /*                                \
     * Because the state might change asynchronously if it's    \
     * nominal, we need to make sure that we only read it once.    \
     */                                \
    uint8_t state = tsd_state_get(tsd);                \
    assert(state == tsd_state_nominal ||                \
        state == tsd_state_nominal_slow ||                \
        state == tsd_state_nominal_recompute ||            \
        state == tsd_state_reincarnated ||                \
        state == tsd_state_minimal_initialized);            \
    return tsd_##n##p_get_unsafe(tsd);                \
}
MALLOC_TSD
#undef O
/*
 * tsdn_foop_get(tsdn) returns either the thread-local instance of foo (if tsdn
 * isn't NULL), or NULL (if tsdn is NULL), cast to the nullable pointer type.
 */
#define O(n, t, nt)                            \
JEMALLOC_ALWAYS_INLINE nt *                        \
tsdn_##n##p_get(tsdn_t *tsdn) {                        \
    if (tsdn_null(tsdn)) {                        \
        return NULL;                        \
    }                                \
    tsd_t *tsd = tsdn_tsd(tsdn);                    \
    return (nt *)tsd_##n##p_get(tsd);                \
}
MALLOC_TSD
#undef O
/* tsd_foo_get(tsd) returns the value of the thread-local instance of foo. */
#define O(n, t, nt)                            \
JEMALLOC_ALWAYS_INLINE t                        \
tsd_##n##_get(tsd_t *tsd) {                        \
    return *tsd_##n##p_get(tsd);                    \
}
MALLOC_TSD
#undef O
/* tsd_foo_set(tsd, val) updates the thread-local instance of foo to be val. */
#define O(n, t, nt)                            \
JEMALLOC_ALWAYS_INLINE void                        \
tsd_##n##_set(tsd_t *tsd, t val) {                    \
    assert(tsd_state_get(tsd) != tsd_state_reincarnated &&        \
        tsd_state_get(tsd) != tsd_state_minimal_initialized);    \
    *tsd_##n##p_get(tsd) = val;                    \
}
MALLOC_TSD
#undef O
JEMALLOC_ALWAYS_INLINE void
tsd_assert_fast(tsd_t *tsd) {
    /*
     * Note that our fastness assertion does *not* include global slowness
     * counters; it's not in general possible to ensure that they won't
     * change asynchronously from underneath us.
     */
    assert(!malloc_slow && tsd_tcache_enabled_get(tsd) &&
        tsd_reentrancy_level_get(tsd) == 0);
}
JEMALLOC_ALWAYS_INLINE bool
tsd_fast(tsd_t *tsd) {
    bool fast = (tsd_state_get(tsd) == tsd_state_nominal);
    if (fast) {
        tsd_assert_fast(tsd);
    }
    return fast;
}
JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_fetch_impl(bool init, bool minimal) {
    tsd_t *tsd = tsd_get(init);
    if (!init && tsd_get_allocates() && tsd == NULL) {
        return NULL;
    }
    assert(tsd != NULL);
    if (unlikely(tsd_state_get(tsd) != tsd_state_nominal)) {
        return tsd_fetch_slow(tsd, minimal);
    }
    assert(tsd_fast(tsd));
    tsd_assert_fast(tsd);
    return tsd;
}
/* Get a minimal TSD that requires no cleanup.  See comments in free(). */
JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_fetch_min(void) {
    return tsd_fetch_impl(true, true);
}
/* For internal background threads use only. */
JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_internal_fetch(void) {
    tsd_t *tsd = tsd_fetch_min();
    /* Use reincarnated state to prevent full initialization. */
    tsd_state_set(tsd, tsd_state_reincarnated);
    return tsd;
}
JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_fetch(void) {
    return tsd_fetch_impl(true, false);
}
static inline bool
tsd_nominal(tsd_t *tsd) {
    return (tsd_state_get(tsd) <= tsd_state_nominal_max);
}
JEMALLOC_ALWAYS_INLINE tsdn_t *
tsdn_fetch(void) {
    if (!tsd_booted_get()) {
        return NULL;
    }
    return tsd_tsdn(tsd_fetch_impl(false, false));
}
JEMALLOC_ALWAYS_INLINE rtree_ctx_t *
tsd_rtree_ctx(tsd_t *tsd) {
    return tsd_rtree_ctxp_get(tsd);
}
JEMALLOC_ALWAYS_INLINE rtree_ctx_t *
tsdn_rtree_ctx(tsdn_t *tsdn, rtree_ctx_t *fallback) {
    /*
     * If tsd cannot be accessed, initialize the fallback rtree_ctx and
     * return a pointer to it.
     */
    if (unlikely(tsdn_null(tsdn))) {
        rtree_ctx_data_init(fallback);
        return fallback;
    }
    return tsd_rtree_ctx(tsdn_tsd(tsdn));
}
#endif /* JEMALLOC_INTERNAL_TSD_H */
jemalloc/x64/include/jemalloc/internal/tsd_generic.h
New file
@@ -0,0 +1,163 @@
#ifdef JEMALLOC_INTERNAL_TSD_GENERIC_H
#error This file should be included only once, by tsd.h.
#endif
#define JEMALLOC_INTERNAL_TSD_GENERIC_H
typedef struct tsd_init_block_s tsd_init_block_t;
struct tsd_init_block_s {
    ql_elm(tsd_init_block_t) link;
    pthread_t thread;
    void *data;
};
/* Defined in tsd.c, to allow the mutex headers to have tsd dependencies. */
typedef struct tsd_init_head_s tsd_init_head_t;
typedef struct {
    bool initialized;
    tsd_t val;
} tsd_wrapper_t;
void *tsd_init_check_recursion(tsd_init_head_t *head,
    tsd_init_block_t *block);
void tsd_init_finish(tsd_init_head_t *head, tsd_init_block_t *block);
extern pthread_key_t tsd_tsd;
extern tsd_init_head_t tsd_init_head;
extern tsd_wrapper_t tsd_boot_wrapper;
extern bool tsd_booted;
/* Initialization/cleanup. */
JEMALLOC_ALWAYS_INLINE void
tsd_cleanup_wrapper(void *arg) {
    tsd_wrapper_t *wrapper = (tsd_wrapper_t *)arg;
    if (wrapper->initialized) {
        wrapper->initialized = false;
        tsd_cleanup(&wrapper->val);
        if (wrapper->initialized) {
            /* Trigger another cleanup round. */
            if (pthread_setspecific(tsd_tsd, (void *)wrapper) != 0)
            {
                malloc_write("<jemalloc>: Error setting TSD\n");
                if (opt_abort) {
                    abort();
                }
            }
            return;
        }
    }
    malloc_tsd_dalloc(wrapper);
}
JEMALLOC_ALWAYS_INLINE void
tsd_wrapper_set(tsd_wrapper_t *wrapper) {
    if (pthread_setspecific(tsd_tsd, (void *)wrapper) != 0) {
        malloc_write("<jemalloc>: Error setting TSD\n");
        abort();
    }
}
JEMALLOC_ALWAYS_INLINE tsd_wrapper_t *
tsd_wrapper_get(bool init) {
    tsd_wrapper_t *wrapper = (tsd_wrapper_t *)pthread_getspecific(tsd_tsd);
    if (init && unlikely(wrapper == NULL)) {
        tsd_init_block_t block;
        wrapper = (tsd_wrapper_t *)
            tsd_init_check_recursion(&tsd_init_head, &block);
        if (wrapper) {
            return wrapper;
        }
        wrapper = (tsd_wrapper_t *)
            malloc_tsd_malloc(sizeof(tsd_wrapper_t));
        block.data = (void *)wrapper;
        if (wrapper == NULL) {
            malloc_write("<jemalloc>: Error allocating TSD\n");
            abort();
        } else {
            wrapper->initialized = false;
      JEMALLOC_DIAGNOSTIC_PUSH
      JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
            tsd_t initializer = TSD_INITIALIZER;
      JEMALLOC_DIAGNOSTIC_POP
            wrapper->val = initializer;
        }
        tsd_wrapper_set(wrapper);
        tsd_init_finish(&tsd_init_head, &block);
    }
    return wrapper;
}
JEMALLOC_ALWAYS_INLINE bool
tsd_boot0(void) {
    if (pthread_key_create(&tsd_tsd, tsd_cleanup_wrapper) != 0) {
        return true;
    }
    tsd_wrapper_set(&tsd_boot_wrapper);
    tsd_booted = true;
    return false;
}
JEMALLOC_ALWAYS_INLINE void
tsd_boot1(void) {
    tsd_wrapper_t *wrapper;
    wrapper = (tsd_wrapper_t *)malloc_tsd_malloc(sizeof(tsd_wrapper_t));
    if (wrapper == NULL) {
        malloc_write("<jemalloc>: Error allocating TSD\n");
        abort();
    }
    tsd_boot_wrapper.initialized = false;
    tsd_cleanup(&tsd_boot_wrapper.val);
    wrapper->initialized = false;
  JEMALLOC_DIAGNOSTIC_PUSH
  JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
    tsd_t initializer = TSD_INITIALIZER;
  JEMALLOC_DIAGNOSTIC_POP
    wrapper->val = initializer;
    tsd_wrapper_set(wrapper);
}
JEMALLOC_ALWAYS_INLINE bool
tsd_boot(void) {
    if (tsd_boot0()) {
        return true;
    }
    tsd_boot1();
    return false;
}
JEMALLOC_ALWAYS_INLINE bool
tsd_booted_get(void) {
    return tsd_booted;
}
JEMALLOC_ALWAYS_INLINE bool
tsd_get_allocates(void) {
    return true;
}
/* Get/set. */
JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_get(bool init) {
    tsd_wrapper_t *wrapper;
    assert(tsd_booted);
    wrapper = tsd_wrapper_get(init);
    if (tsd_get_allocates() && !init && wrapper == NULL) {
        return NULL;
    }
    return &wrapper->val;
}
JEMALLOC_ALWAYS_INLINE void
tsd_set(tsd_t *val) {
    tsd_wrapper_t *wrapper;
    assert(tsd_booted);
    wrapper = tsd_wrapper_get(true);
    if (likely(&wrapper->val != val)) {
        wrapper->val = *(val);
    }
    wrapper->initialized = true;
}
jemalloc/x64/include/jemalloc/internal/tsd_malloc_thread_cleanup.h
New file
@@ -0,0 +1,61 @@
#ifdef JEMALLOC_INTERNAL_TSD_MALLOC_THREAD_CLEANUP_H
#error This file should be included only once, by tsd.h.
#endif
#define JEMALLOC_INTERNAL_TSD_MALLOC_THREAD_CLEANUP_H
#define JEMALLOC_TSD_TYPE_ATTR(type) __thread type JEMALLOC_TLS_MODEL
extern JEMALLOC_TSD_TYPE_ATTR(tsd_t) tsd_tls;
extern JEMALLOC_TSD_TYPE_ATTR(bool) tsd_initialized;
extern bool tsd_booted;
/* Initialization/cleanup. */
JEMALLOC_ALWAYS_INLINE bool
tsd_cleanup_wrapper(void) {
    if (tsd_initialized) {
        tsd_initialized = false;
        tsd_cleanup(&tsd_tls);
    }
    return tsd_initialized;
}
JEMALLOC_ALWAYS_INLINE bool
tsd_boot0(void) {
    malloc_tsd_cleanup_register(&tsd_cleanup_wrapper);
    tsd_booted = true;
    return false;
}
JEMALLOC_ALWAYS_INLINE void
tsd_boot1(void) {
    /* Do nothing. */
}
JEMALLOC_ALWAYS_INLINE bool
tsd_boot(void) {
    return tsd_boot0();
}
JEMALLOC_ALWAYS_INLINE bool
tsd_booted_get(void) {
    return tsd_booted;
}
JEMALLOC_ALWAYS_INLINE bool
tsd_get_allocates(void) {
    return false;
}
/* Get/set. */
JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_get(bool init) {
    return &tsd_tls;
}
JEMALLOC_ALWAYS_INLINE void
tsd_set(tsd_t *val) {
    assert(tsd_booted);
    if (likely(&tsd_tls != val)) {
        tsd_tls = (*val);
    }
    tsd_initialized = true;
}
jemalloc/x64/include/jemalloc/internal/tsd_tls.h
New file
@@ -0,0 +1,60 @@
#ifdef JEMALLOC_INTERNAL_TSD_TLS_H
#error This file should be included only once, by tsd.h.
#endif
#define JEMALLOC_INTERNAL_TSD_TLS_H
#define JEMALLOC_TSD_TYPE_ATTR(type) __thread type JEMALLOC_TLS_MODEL
extern JEMALLOC_TSD_TYPE_ATTR(tsd_t) tsd_tls;
extern pthread_key_t tsd_tsd;
extern bool tsd_booted;
/* Initialization/cleanup. */
JEMALLOC_ALWAYS_INLINE bool
tsd_boot0(void) {
    if (pthread_key_create(&tsd_tsd, &tsd_cleanup) != 0) {
        return true;
    }
    tsd_booted = true;
    return false;
}
JEMALLOC_ALWAYS_INLINE void
tsd_boot1(void) {
    /* Do nothing. */
}
JEMALLOC_ALWAYS_INLINE bool
tsd_boot(void) {
    return tsd_boot0();
}
JEMALLOC_ALWAYS_INLINE bool
tsd_booted_get(void) {
    return tsd_booted;
}
JEMALLOC_ALWAYS_INLINE bool
tsd_get_allocates(void) {
    return false;
}
/* Get/set. */
JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_get(bool init) {
    return &tsd_tls;
}
JEMALLOC_ALWAYS_INLINE void
tsd_set(tsd_t *val) {
    assert(tsd_booted);
    if (likely(&tsd_tls != val)) {
        tsd_tls = (*val);
    }
    if (pthread_setspecific(tsd_tsd, (void *)(&tsd_tls)) != 0) {
        malloc_write("<jemalloc>: Error setting tsd.\n");
        if (opt_abort) {
            abort();
        }
    }
}
jemalloc/x64/include/jemalloc/internal/tsd_types.h
New file
@@ -0,0 +1,10 @@
#ifndef JEMALLOC_INTERNAL_TSD_TYPES_H
#define JEMALLOC_INTERNAL_TSD_TYPES_H
#define MALLOC_TSD_CLEANUPS_MAX    2
typedef struct tsd_s tsd_t;
typedef struct tsdn_s tsdn_t;
typedef bool (*malloc_tsd_cleanup_t)(void);
#endif /* JEMALLOC_INTERNAL_TSD_TYPES_H */
jemalloc/x64/include/jemalloc/internal/tsd_win.h
New file
@@ -0,0 +1,139 @@
#ifdef JEMALLOC_INTERNAL_TSD_WIN_H
#error This file should be included only once, by tsd.h.
#endif
#define JEMALLOC_INTERNAL_TSD_WIN_H
typedef struct {
    bool initialized;
    tsd_t val;
} tsd_wrapper_t;
extern DWORD tsd_tsd;
extern tsd_wrapper_t tsd_boot_wrapper;
extern bool tsd_booted;
/* Initialization/cleanup. */
JEMALLOC_ALWAYS_INLINE bool
tsd_cleanup_wrapper(void) {
    DWORD error = GetLastError();
    tsd_wrapper_t *wrapper = (tsd_wrapper_t *)TlsGetValue(tsd_tsd);
    SetLastError(error);
    if (wrapper == NULL) {
        return false;
    }
    if (wrapper->initialized) {
        wrapper->initialized = false;
        tsd_cleanup(&wrapper->val);
        if (wrapper->initialized) {
            /* Trigger another cleanup round. */
            return true;
        }
    }
    malloc_tsd_dalloc(wrapper);
    return false;
}
JEMALLOC_ALWAYS_INLINE void
tsd_wrapper_set(tsd_wrapper_t *wrapper) {
    if (!TlsSetValue(tsd_tsd, (void *)wrapper)) {
        malloc_write("<jemalloc>: Error setting TSD\n");
        abort();
    }
}
JEMALLOC_ALWAYS_INLINE tsd_wrapper_t *
tsd_wrapper_get(bool init) {
    DWORD error = GetLastError();
    tsd_wrapper_t *wrapper = (tsd_wrapper_t *) TlsGetValue(tsd_tsd);
    SetLastError(error);
    if (init && unlikely(wrapper == NULL)) {
        wrapper = (tsd_wrapper_t *)
            malloc_tsd_malloc(sizeof(tsd_wrapper_t));
        if (wrapper == NULL) {
            malloc_write("<jemalloc>: Error allocating TSD\n");
            abort();
        } else {
            wrapper->initialized = false;
            /* MSVC is finicky about aggregate initialization. */
            tsd_t tsd_initializer = TSD_INITIALIZER;
            wrapper->val = tsd_initializer;
        }
        tsd_wrapper_set(wrapper);
    }
    return wrapper;
}
JEMALLOC_ALWAYS_INLINE bool
tsd_boot0(void) {
    tsd_tsd = TlsAlloc();
    if (tsd_tsd == TLS_OUT_OF_INDEXES) {
        return true;
    }
    malloc_tsd_cleanup_register(&tsd_cleanup_wrapper);
    tsd_wrapper_set(&tsd_boot_wrapper);
    tsd_booted = true;
    return false;
}
JEMALLOC_ALWAYS_INLINE void
tsd_boot1(void) {
    tsd_wrapper_t *wrapper;
    wrapper = (tsd_wrapper_t *)
        malloc_tsd_malloc(sizeof(tsd_wrapper_t));
    if (wrapper == NULL) {
        malloc_write("<jemalloc>: Error allocating TSD\n");
        abort();
    }
    tsd_boot_wrapper.initialized = false;
    tsd_cleanup(&tsd_boot_wrapper.val);
    wrapper->initialized = false;
    tsd_t initializer = TSD_INITIALIZER;
    wrapper->val = initializer;
    tsd_wrapper_set(wrapper);
}
JEMALLOC_ALWAYS_INLINE bool
tsd_boot(void) {
    if (tsd_boot0()) {
        return true;
    }
    tsd_boot1();
    return false;
}
JEMALLOC_ALWAYS_INLINE bool
tsd_booted_get(void) {
    return tsd_booted;
}
JEMALLOC_ALWAYS_INLINE bool
tsd_get_allocates(void) {
    return true;
}
/* Get/set. */
JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_get(bool init) {
    tsd_wrapper_t *wrapper;
    assert(tsd_booted);
    wrapper = tsd_wrapper_get(init);
    if (tsd_get_allocates() && !init && wrapper == NULL) {
        return NULL;
    }
    return &wrapper->val;
}
JEMALLOC_ALWAYS_INLINE void
tsd_set(tsd_t *val) {
    tsd_wrapper_t *wrapper;
    assert(tsd_booted);
    wrapper = tsd_wrapper_get(true);
    if (likely(&wrapper->val != val)) {
        wrapper->val = *(val);
    }
    wrapper->initialized = true;
}
jemalloc/x64/include/jemalloc/internal/util.h
New file
@@ -0,0 +1,67 @@
#ifndef JEMALLOC_INTERNAL_UTIL_H
#define JEMALLOC_INTERNAL_UTIL_H
#define UTIL_INLINE static inline
/* Junk fill patterns. */
#ifndef JEMALLOC_ALLOC_JUNK
#  define JEMALLOC_ALLOC_JUNK    ((uint8_t)0xa5)
#endif
#ifndef JEMALLOC_FREE_JUNK
#  define JEMALLOC_FREE_JUNK    ((uint8_t)0x5a)
#endif
/*
 * Wrap a cpp argument that contains commas such that it isn't broken up into
 * multiple arguments.
 */
#define JEMALLOC_ARG_CONCAT(...) __VA_ARGS__
/* cpp macro definition stringification. */
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
/*
 * Silence compiler warnings due to uninitialized values.  This is used
 * wherever the compiler fails to recognize that the variable is never used
 * uninitialized.
 */
#define JEMALLOC_CC_SILENCE_INIT(v) = v
#ifdef __GNUC__
#  define likely(x)   __builtin_expect(!!(x), 1)
#  define unlikely(x) __builtin_expect(!!(x), 0)
#else
#  define likely(x)   !!(x)
#  define unlikely(x) !!(x)
#endif
#if !defined(JEMALLOC_INTERNAL_UNREACHABLE)
#  error JEMALLOC_INTERNAL_UNREACHABLE should have been defined by configure
#endif
#define unreachable() JEMALLOC_INTERNAL_UNREACHABLE()
/* Set error code. */
UTIL_INLINE void
set_errno(int errnum) {
#ifdef _WIN32
    SetLastError(errnum);
#else
    errno = errnum;
#endif
}
/* Get last error code. */
UTIL_INLINE int
get_errno(void) {
#ifdef _WIN32
    return GetLastError();
#else
    return errno;
#endif
}
#undef UTIL_INLINE
#endif /* JEMALLOC_INTERNAL_UTIL_H */
jemalloc/x64/include/jemalloc/internal/witness.h
New file
@@ -0,0 +1,347 @@
#ifndef JEMALLOC_INTERNAL_WITNESS_H
#define JEMALLOC_INTERNAL_WITNESS_H
#include "jemalloc/internal/ql.h"
/******************************************************************************/
/* LOCK RANKS */
/******************************************************************************/
/*
 * Witnesses with rank WITNESS_RANK_OMIT are completely ignored by the witness
 * machinery.
 */
#define WITNESS_RANK_OMIT        0U
#define WITNESS_RANK_MIN        1U
#define WITNESS_RANK_INIT        1U
#define WITNESS_RANK_CTL        1U
#define WITNESS_RANK_TCACHES        2U
#define WITNESS_RANK_ARENAS        3U
#define WITNESS_RANK_BACKGROUND_THREAD_GLOBAL    4U
#define WITNESS_RANK_PROF_DUMP        5U
#define WITNESS_RANK_PROF_BT2GCTX    6U
#define WITNESS_RANK_PROF_TDATAS    7U
#define WITNESS_RANK_PROF_TDATA        8U
#define WITNESS_RANK_PROF_LOG        9U
#define WITNESS_RANK_PROF_GCTX        10U
#define WITNESS_RANK_BACKGROUND_THREAD    11U
/*
 * Used as an argument to witness_assert_depth_to_rank() in order to validate
 * depth excluding non-core locks with lower ranks.  Since the rank argument to
 * witness_assert_depth_to_rank() is inclusive rather than exclusive, this
 * definition can have the same value as the minimally ranked core lock.
 */
#define WITNESS_RANK_CORE        12U
#define WITNESS_RANK_DECAY        12U
#define WITNESS_RANK_TCACHE_QL        13U
#define WITNESS_RANK_EXTENT_GROW    14U
#define WITNESS_RANK_EXTENTS        15U
#define WITNESS_RANK_EXTENT_AVAIL    16U
#define WITNESS_RANK_EXTENT_POOL    17U
#define WITNESS_RANK_RTREE        18U
#define WITNESS_RANK_BASE        19U
#define WITNESS_RANK_ARENA_LARGE    20U
#define WITNESS_RANK_HOOK        21U
#define WITNESS_RANK_LEAF        0xffffffffU
#define WITNESS_RANK_BIN        WITNESS_RANK_LEAF
#define WITNESS_RANK_ARENA_STATS    WITNESS_RANK_LEAF
#define WITNESS_RANK_DSS        WITNESS_RANK_LEAF
#define WITNESS_RANK_PROF_ACTIVE    WITNESS_RANK_LEAF
#define WITNESS_RANK_PROF_ACCUM        WITNESS_RANK_LEAF
#define WITNESS_RANK_PROF_DUMP_SEQ    WITNESS_RANK_LEAF
#define WITNESS_RANK_PROF_GDUMP        WITNESS_RANK_LEAF
#define WITNESS_RANK_PROF_NEXT_THR_UID    WITNESS_RANK_LEAF
#define WITNESS_RANK_PROF_THREAD_ACTIVE_INIT    WITNESS_RANK_LEAF
/******************************************************************************/
/* PER-WITNESS DATA */
/******************************************************************************/
#if defined(JEMALLOC_DEBUG)
#  define WITNESS_INITIALIZER(name, rank) {name, rank, NULL, NULL, {NULL, NULL}}
#else
#  define WITNESS_INITIALIZER(name, rank)
#endif
typedef struct witness_s witness_t;
typedef unsigned witness_rank_t;
typedef ql_head(witness_t) witness_list_t;
typedef int witness_comp_t (const witness_t *, void *, const witness_t *,
    void *);
struct witness_s {
    /* Name, used for printing lock order reversal messages. */
    const char        *name;
    /*
     * Witness rank, where 0 is lowest and UINT_MAX is highest.  Witnesses
     * must be acquired in order of increasing rank.
     */
    witness_rank_t        rank;
    /*
     * If two witnesses are of equal rank and they have the samp comp
     * function pointer, it is called as a last attempt to differentiate
     * between witnesses of equal rank.
     */
    witness_comp_t        *comp;
    /* Opaque data, passed to comp(). */
    void            *opaque;
    /* Linkage for thread's currently owned locks. */
    ql_elm(witness_t)    link;
};
/******************************************************************************/
/* PER-THREAD DATA */
/******************************************************************************/
typedef struct witness_tsd_s witness_tsd_t;
struct witness_tsd_s {
    witness_list_t witnesses;
    bool forking;
};
#define WITNESS_TSD_INITIALIZER { ql_head_initializer(witnesses), false }
#define WITNESS_TSDN_NULL ((witness_tsdn_t *)0)
/******************************************************************************/
/* (PER-THREAD) NULLABILITY HELPERS */
/******************************************************************************/
typedef struct witness_tsdn_s witness_tsdn_t;
struct witness_tsdn_s {
    witness_tsd_t witness_tsd;
};
JEMALLOC_ALWAYS_INLINE witness_tsdn_t *
witness_tsd_tsdn(witness_tsd_t *witness_tsd) {
    return (witness_tsdn_t *)witness_tsd;
}
JEMALLOC_ALWAYS_INLINE bool
witness_tsdn_null(witness_tsdn_t *witness_tsdn) {
    return witness_tsdn == NULL;
}
JEMALLOC_ALWAYS_INLINE witness_tsd_t *
witness_tsdn_tsd(witness_tsdn_t *witness_tsdn) {
    assert(!witness_tsdn_null(witness_tsdn));
    return &witness_tsdn->witness_tsd;
}
/******************************************************************************/
/* API */
/******************************************************************************/
void witness_init(witness_t *witness, const char *name, witness_rank_t rank,
    witness_comp_t *comp, void *opaque);
typedef void (witness_lock_error_t)(const witness_list_t *, const witness_t *);
extern witness_lock_error_t *JET_MUTABLE witness_lock_error;
typedef void (witness_owner_error_t)(const witness_t *);
extern witness_owner_error_t *JET_MUTABLE witness_owner_error;
typedef void (witness_not_owner_error_t)(const witness_t *);
extern witness_not_owner_error_t *JET_MUTABLE witness_not_owner_error;
typedef void (witness_depth_error_t)(const witness_list_t *,
    witness_rank_t rank_inclusive, unsigned depth);
extern witness_depth_error_t *JET_MUTABLE witness_depth_error;
void witnesses_cleanup(witness_tsd_t *witness_tsd);
void witness_prefork(witness_tsd_t *witness_tsd);
void witness_postfork_parent(witness_tsd_t *witness_tsd);
void witness_postfork_child(witness_tsd_t *witness_tsd);
/* Helper, not intended for direct use. */
static inline bool
witness_owner(witness_tsd_t *witness_tsd, const witness_t *witness) {
    witness_list_t *witnesses;
    witness_t *w;
    cassert(config_debug);
    witnesses = &witness_tsd->witnesses;
    ql_foreach(w, witnesses, link) {
        if (w == witness) {
            return true;
        }
    }
    return false;
}
static inline void
witness_assert_owner(witness_tsdn_t *witness_tsdn, const witness_t *witness) {
    witness_tsd_t *witness_tsd;
    if (!config_debug) {
        return;
    }
    if (witness_tsdn_null(witness_tsdn)) {
        return;
    }
    witness_tsd = witness_tsdn_tsd(witness_tsdn);
    if (witness->rank == WITNESS_RANK_OMIT) {
        return;
    }
    if (witness_owner(witness_tsd, witness)) {
        return;
    }
    witness_owner_error(witness);
}
static inline void
witness_assert_not_owner(witness_tsdn_t *witness_tsdn,
    const witness_t *witness) {
    witness_tsd_t *witness_tsd;
    witness_list_t *witnesses;
    witness_t *w;
    if (!config_debug) {
        return;
    }
    if (witness_tsdn_null(witness_tsdn)) {
        return;
    }
    witness_tsd = witness_tsdn_tsd(witness_tsdn);
    if (witness->rank == WITNESS_RANK_OMIT) {
        return;
    }
    witnesses = &witness_tsd->witnesses;
    ql_foreach(w, witnesses, link) {
        if (w == witness) {
            witness_not_owner_error(witness);
        }
    }
}
static inline void
witness_assert_depth_to_rank(witness_tsdn_t *witness_tsdn,
    witness_rank_t rank_inclusive, unsigned depth) {
    witness_tsd_t *witness_tsd;
    unsigned d;
    witness_list_t *witnesses;
    witness_t *w;
    if (!config_debug) {
        return;
    }
    if (witness_tsdn_null(witness_tsdn)) {
        return;
    }
    witness_tsd = witness_tsdn_tsd(witness_tsdn);
    d = 0;
    witnesses = &witness_tsd->witnesses;
    w = ql_last(witnesses, link);
    if (w != NULL) {
        ql_reverse_foreach(w, witnesses, link) {
            if (w->rank < rank_inclusive) {
                break;
            }
            d++;
        }
    }
    if (d != depth) {
        witness_depth_error(witnesses, rank_inclusive, depth);
    }
}
static inline void
witness_assert_depth(witness_tsdn_t *witness_tsdn, unsigned depth) {
    witness_assert_depth_to_rank(witness_tsdn, WITNESS_RANK_MIN, depth);
}
static inline void
witness_assert_lockless(witness_tsdn_t *witness_tsdn) {
    witness_assert_depth(witness_tsdn, 0);
}
static inline void
witness_lock(witness_tsdn_t *witness_tsdn, witness_t *witness) {
    witness_tsd_t *witness_tsd;
    witness_list_t *witnesses;
    witness_t *w;
    if (!config_debug) {
        return;
    }
    if (witness_tsdn_null(witness_tsdn)) {
        return;
    }
    witness_tsd = witness_tsdn_tsd(witness_tsdn);
    if (witness->rank == WITNESS_RANK_OMIT) {
        return;
    }
    witness_assert_not_owner(witness_tsdn, witness);
    witnesses = &witness_tsd->witnesses;
    w = ql_last(witnesses, link);
    if (w == NULL) {
        /* No other locks; do nothing. */
    } else if (witness_tsd->forking && w->rank <= witness->rank) {
        /* Forking, and relaxed ranking satisfied. */
    } else if (w->rank > witness->rank) {
        /* Not forking, rank order reversal. */
        witness_lock_error(witnesses, witness);
    } else if (w->rank == witness->rank && (w->comp == NULL || w->comp !=
        witness->comp || w->comp(w, w->opaque, witness, witness->opaque) >
        0)) {
        /*
         * Missing/incompatible comparison function, or comparison
         * function indicates rank order reversal.
         */
        witness_lock_error(witnesses, witness);
    }
    ql_elm_new(witness, link);
    ql_tail_insert(witnesses, witness, link);
}
static inline void
witness_unlock(witness_tsdn_t *witness_tsdn, witness_t *witness) {
    witness_tsd_t *witness_tsd;
    witness_list_t *witnesses;
    if (!config_debug) {
        return;
    }
    if (witness_tsdn_null(witness_tsdn)) {
        return;
    }
    witness_tsd = witness_tsdn_tsd(witness_tsdn);
    if (witness->rank == WITNESS_RANK_OMIT) {
        return;
    }
    /*
     * Check whether owner before removal, rather than relying on
     * witness_assert_owner() to abort, so that unit tests can test this
     * function's failure mode without causing undefined behavior.
     */
    if (witness_owner(witness_tsd, witness)) {
        witnesses = &witness_tsd->witnesses;
        ql_remove(witnesses, witness, link);
    } else {
        witness_assert_owner(witness_tsdn, witness);
    }
}
#endif /* JEMALLOC_INTERNAL_WITNESS_H */
jemalloc/x64/include/jemalloc/jemalloc.h
New file
@@ -0,0 +1,428 @@
#ifndef JEMALLOC_H_
#define JEMALLOC_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Defined if __attribute__((...)) syntax is supported. */
/* #undef JEMALLOC_HAVE_ATTR */
/* Defined if alloc_size attribute is supported. */
/* #undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE */
/* Defined if format_arg(...) attribute is supported. */
/* #undef JEMALLOC_HAVE_ATTR_FORMAT_ARG */
/* Defined if format(gnu_printf, ...) attribute is supported. */
/* #undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF */
/* Defined if format(printf, ...) attribute is supported. */
/* #undef JEMALLOC_HAVE_ATTR_FORMAT_PRINTF */
/*
 * Define overrides for non-standard allocator-related functions if they are
 * present on the system.
 */
/* #undef JEMALLOC_OVERRIDE_MEMALIGN */
/* #undef JEMALLOC_OVERRIDE_VALLOC */
/*
 * At least Linux omits the "const" in:
 *
 *   size_t malloc_usable_size(const void *ptr);
 *
 * Match the operating system's prototype.
 */
#define JEMALLOC_USABLE_SIZE_CONST const
/*
 * If defined, specify throw() for the public function prototypes when compiling
 * with C++.  The only justification for this is to match the prototypes that
 * glibc defines.
 */
/* #undef JEMALLOC_USE_CXX_THROW */
#ifdef _MSC_VER
#  ifdef _WIN64
#    define LG_SIZEOF_PTR_WIN 3
#  else
#    define LG_SIZEOF_PTR_WIN 2
#  endif
#endif
/* sizeof(void *) == 2^LG_SIZEOF_PTR. */
#define LG_SIZEOF_PTR LG_SIZEOF_PTR_WIN
/*
 * Name mangling for public symbols is controlled by --with-mangling and
 * --with-jemalloc-prefix.  With default settings the je_ prefix is stripped by
 * these macro definitions.
 */
#ifndef JEMALLOC_NO_RENAME
#  define je_aligned_alloc je_aligned_alloc
#  define je_calloc je_calloc
#  define je_dallocx je_dallocx
#  define je_free je_free
#  define je_mallctl je_mallctl
#  define je_mallctlbymib je_mallctlbymib
#  define je_mallctlnametomib je_mallctlnametomib
#  define je_malloc je_malloc
#  define je_malloc_conf je_malloc_conf
#  define je_malloc_message je_malloc_message
#  define je_malloc_stats_print je_malloc_stats_print
#  define je_malloc_usable_size je_malloc_usable_size
#  define je_mallocx je_mallocx
#  define je_smallocx_0000000000000000000000000000000000000000 je_smallocx_0000000000000000000000000000000000000000
#  define je_nallocx je_nallocx
#  define je_posix_memalign je_posix_memalign
#  define je_rallocx je_rallocx
#  define je_realloc je_realloc
#  define je_sallocx je_sallocx
#  define je_sdallocx je_sdallocx
#  define je_xallocx je_xallocx
#endif
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <strings.h>
#define JEMALLOC_VERSION "0.0.0-0-g0000000000000000000000000000000000000000"
#define JEMALLOC_VERSION_MAJOR 0
#define JEMALLOC_VERSION_MINOR 0
#define JEMALLOC_VERSION_BUGFIX 0
#define JEMALLOC_VERSION_NREV 0
#define JEMALLOC_VERSION_GID "0000000000000000000000000000000000000000"
#define JEMALLOC_VERSION_GID_IDENT 0000000000000000000000000000000000000000
#define MALLOCX_LG_ALIGN(la)    ((int)(la))
#if LG_SIZEOF_PTR == 2
#  define MALLOCX_ALIGN(a)    ((int)(ffs((int)(a))-1))
#else
#  define MALLOCX_ALIGN(a)                        \
     ((int)(((size_t)(a) < (size_t)INT_MAX) ? ffs((int)(a))-1 :    \
     ffs((int)(((size_t)(a))>>32))+31))
#endif
#define MALLOCX_ZERO    ((int)0x40)
/*
 * Bias tcache index bits so that 0 encodes "automatic tcache management", and 1
 * encodes MALLOCX_TCACHE_NONE.
 */
#define MALLOCX_TCACHE(tc)    ((int)(((tc)+2) << 8))
#define MALLOCX_TCACHE_NONE    MALLOCX_TCACHE(-1)
/*
 * Bias arena index bits so that 0 encodes "use an automatically chosen arena".
 */
#define MALLOCX_ARENA(a)    ((((int)(a))+1) << 20)
/*
 * Use as arena index in "arena.<i>.{purge,decay,dss}" and
 * "stats.arenas.<i>.*" mallctl interfaces to select all arenas.  This
 * definition is intentionally specified in raw decimal format to support
 * cpp-based string concatenation, e.g.
 *
 *   #define STRINGIFY_HELPER(x) #x
 *   #define STRINGIFY(x) STRINGIFY_HELPER(x)
 *
 *   mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", NULL, NULL, NULL,
 *       0);
 */
#define MALLCTL_ARENAS_ALL    4096
/*
 * Use as arena index in "stats.arenas.<i>.*" mallctl interfaces to select
 * destroyed arenas.
 */
#define MALLCTL_ARENAS_DESTROYED    4097
#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW)
#  define JEMALLOC_CXX_THROW throw()
#else
#  define JEMALLOC_CXX_THROW
#endif
#if defined(_MSC_VER)
#  define JEMALLOC_ATTR(s)
#  define JEMALLOC_ALIGNED(s) __declspec(align(s))
#  define JEMALLOC_ALLOC_SIZE(s)
#  define JEMALLOC_ALLOC_SIZE2(s1, s2)
#  ifndef JEMALLOC_EXPORT
#    ifdef DLLEXPORT
#      define JEMALLOC_EXPORT __declspec(dllexport)
#    else
#      define JEMALLOC_EXPORT __declspec(dllimport)
#    endif
#  endif
#  define JEMALLOC_FORMAT_ARG(i)
#  define JEMALLOC_FORMAT_PRINTF(s, i)
#  define JEMALLOC_NOINLINE __declspec(noinline)
#  ifdef __cplusplus
#    define JEMALLOC_NOTHROW __declspec(nothrow)
#  else
#    define JEMALLOC_NOTHROW
#  endif
#  define JEMALLOC_SECTION(s) __declspec(allocate(s))
#  define JEMALLOC_RESTRICT_RETURN __declspec(restrict)
#  if _MSC_VER >= 1900 && !defined(__EDG__)
#    define JEMALLOC_ALLOCATOR __declspec(allocator)
#  else
#    define JEMALLOC_ALLOCATOR
#  endif
#elif defined(JEMALLOC_HAVE_ATTR)
#  define JEMALLOC_ATTR(s) __attribute__((s))
#  define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s))
#  ifdef JEMALLOC_HAVE_ATTR_ALLOC_SIZE
#    define JEMALLOC_ALLOC_SIZE(s) JEMALLOC_ATTR(alloc_size(s))
#    define JEMALLOC_ALLOC_SIZE2(s1, s2) JEMALLOC_ATTR(alloc_size(s1, s2))
#  else
#    define JEMALLOC_ALLOC_SIZE(s)
#    define JEMALLOC_ALLOC_SIZE2(s1, s2)
#  endif
#  ifndef JEMALLOC_EXPORT
#    define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default"))
#  endif
#  ifdef JEMALLOC_HAVE_ATTR_FORMAT_ARG
#    define JEMALLOC_FORMAT_ARG(i) JEMALLOC_ATTR(__format_arg__(3))
#  else
#    define JEMALLOC_FORMAT_ARG(i)
#  endif
#  ifdef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF
#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(gnu_printf, s, i))
#  elif defined(JEMALLOC_HAVE_ATTR_FORMAT_PRINTF)
#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(printf, s, i))
#  else
#    define JEMALLOC_FORMAT_PRINTF(s, i)
#  endif
#  define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline)
#  define JEMALLOC_NOTHROW JEMALLOC_ATTR(nothrow)
#  define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))
#  define JEMALLOC_RESTRICT_RETURN
#  define JEMALLOC_ALLOCATOR
#else
#  define JEMALLOC_ATTR(s)
#  define JEMALLOC_ALIGNED(s)
#  define JEMALLOC_ALLOC_SIZE(s)
#  define JEMALLOC_ALLOC_SIZE2(s1, s2)
#  define JEMALLOC_EXPORT
#  define JEMALLOC_FORMAT_PRINTF(s, i)
#  define JEMALLOC_NOINLINE
#  define JEMALLOC_NOTHROW
#  define JEMALLOC_SECTION(s)
#  define JEMALLOC_RESTRICT_RETURN
#  define JEMALLOC_ALLOCATOR
#endif
/*
 * The je_ prefix on the following public symbol declarations is an artifact
 * of namespace management, and should be omitted in application code unless
 * JEMALLOC_NO_DEMANGLE is defined (see jemalloc_mangle.h).
 */
extern JEMALLOC_EXPORT const char    *je_malloc_conf;
extern JEMALLOC_EXPORT void        (*je_malloc_message)(void *cbopaque,
    const char *s);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_malloc(size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_calloc(size_t num, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    je_posix_memalign(void **memptr,
    size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1));
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_aligned_alloc(size_t alignment,
    size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc)
    JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_realloc(void *ptr, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    je_free(void *ptr)
    JEMALLOC_CXX_THROW;
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_mallocx(size_t size, int flags)
    JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_rallocx(void *ptr, size_t size,
    int flags) JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    je_xallocx(void *ptr, size_t size,
    size_t extra, int flags);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    je_sallocx(const void *ptr,
    int flags) JEMALLOC_ATTR(pure);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    je_dallocx(void *ptr, int flags);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    je_sdallocx(void *ptr, size_t size,
    int flags);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    je_nallocx(size_t size, int flags)
    JEMALLOC_ATTR(pure);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    je_mallctl(const char *name,
    void *oldp, size_t *oldlenp, void *newp, size_t newlen);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    je_mallctlnametomib(const char *name,
    size_t *mibp, size_t *miblenp);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    je_mallctlbymib(const size_t *mib,
    size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    je_malloc_stats_print(
    void (*write_cb)(void *, const char *), void *je_cbopaque,
    const char *opts);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    je_malloc_usable_size(
    JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW;
#ifdef JEMALLOC_OVERRIDE_MEMALIGN
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_memalign(size_t alignment, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc);
#endif
#ifdef JEMALLOC_OVERRIDE_VALLOC
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_valloc(size_t size) JEMALLOC_CXX_THROW
    JEMALLOC_ATTR(malloc);
#endif
typedef struct extent_hooks_s extent_hooks_t;
/*
 * void *
 * extent_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
 *     size_t alignment, bool *zero, bool *commit, unsigned arena_ind);
 */
typedef void *(extent_alloc_t)(extent_hooks_t *, void *, size_t, size_t, bool *,
    bool *, unsigned);
/*
 * bool
 * extent_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     bool committed, unsigned arena_ind);
 */
typedef bool (extent_dalloc_t)(extent_hooks_t *, void *, size_t, bool,
    unsigned);
/*
 * void
 * extent_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     bool committed, unsigned arena_ind);
 */
typedef void (extent_destroy_t)(extent_hooks_t *, void *, size_t, bool,
    unsigned);
/*
 * bool
 * extent_commit(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t offset, size_t length, unsigned arena_ind);
 */
typedef bool (extent_commit_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
    unsigned);
/*
 * bool
 * extent_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t offset, size_t length, unsigned arena_ind);
 */
typedef bool (extent_decommit_t)(extent_hooks_t *, void *, size_t, size_t,
    size_t, unsigned);
/*
 * bool
 * extent_purge(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t offset, size_t length, unsigned arena_ind);
 */
typedef bool (extent_purge_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
    unsigned);
/*
 * bool
 * extent_split(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t size_a, size_t size_b, bool committed, unsigned arena_ind);
 */
typedef bool (extent_split_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
    bool, unsigned);
/*
 * bool
 * extent_merge(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
 *     void *addr_b, size_t size_b, bool committed, unsigned arena_ind);
 */
typedef bool (extent_merge_t)(extent_hooks_t *, void *, size_t, void *, size_t,
    bool, unsigned);
struct extent_hooks_s {
    extent_alloc_t        *alloc;
    extent_dalloc_t        *dalloc;
    extent_destroy_t    *destroy;
    extent_commit_t        *commit;
    extent_decommit_t    *decommit;
    extent_purge_t        *purge_lazy;
    extent_purge_t        *purge_forced;
    extent_split_t        *split;
    extent_merge_t        *merge;
};
/*
 * By default application code must explicitly refer to mangled symbol names,
 * so that it is possible to use jemalloc in conjunction with another allocator
 * in the same application.  Define JEMALLOC_MANGLE in order to cause automatic
 * name mangling that matches the API prefixing that happened as a result of
 * --with-mangling and/or --with-jemalloc-prefix configuration settings.
 */
#ifdef JEMALLOC_MANGLE
#  ifndef JEMALLOC_NO_DEMANGLE
#    define JEMALLOC_NO_DEMANGLE
#  endif
#  define aligned_alloc je_aligned_alloc
#  define calloc je_calloc
#  define dallocx je_dallocx
#  define free je_free
#  define mallctl je_mallctl
#  define mallctlbymib je_mallctlbymib
#  define mallctlnametomib je_mallctlnametomib
#  define malloc je_malloc
#  define malloc_conf je_malloc_conf
#  define malloc_message je_malloc_message
#  define malloc_stats_print je_malloc_stats_print
#  define malloc_usable_size je_malloc_usable_size
#  define mallocx je_mallocx
#  define smallocx_0000000000000000000000000000000000000000 je_smallocx_0000000000000000000000000000000000000000
#  define nallocx je_nallocx
#  define posix_memalign je_posix_memalign
#  define rallocx je_rallocx
#  define realloc je_realloc
#  define sallocx je_sallocx
#  define sdallocx je_sdallocx
#  define xallocx je_xallocx
#endif
/*
 * The je_* macros can be used as stable alternative names for the
 * public jemalloc API if JEMALLOC_NO_DEMANGLE is defined.  This is primarily
 * meant for use in jemalloc itself, but it can be used by application code to
 * provide isolation from the name mangling specified via --with-mangling
 * and/or --with-jemalloc-prefix.
 */
#ifndef JEMALLOC_NO_DEMANGLE
#  undef je_aligned_alloc
#  undef je_calloc
#  undef je_dallocx
#  undef je_free
#  undef je_mallctl
#  undef je_mallctlbymib
#  undef je_mallctlnametomib
#  undef je_malloc
#  undef je_malloc_conf
#  undef je_malloc_message
#  undef je_malloc_stats_print
#  undef je_malloc_usable_size
#  undef je_mallocx
#  undef je_smallocx_0000000000000000000000000000000000000000
#  undef je_nallocx
#  undef je_posix_memalign
#  undef je_rallocx
#  undef je_realloc
#  undef je_sallocx
#  undef je_sdallocx
#  undef je_xallocx
#endif
#ifdef __cplusplus
}
#endif
#endif /* JEMALLOC_H_ */
jemalloc/x64/include/jemalloc/jemalloc.sh
New file
@@ -0,0 +1,27 @@
#!/bin/sh
objroot=$1
cat <<EOF
#ifndef JEMALLOC_H_
#define JEMALLOC_H_
#ifdef __cplusplus
extern "C" {
#endif
EOF
for hdr in jemalloc_defs.h jemalloc_rename.h jemalloc_macros.h \
           jemalloc_protos.h jemalloc_typedefs.h jemalloc_mangle.h ; do
  cat "${objroot}include/jemalloc/${hdr}" \
      | grep -v 'Generated from .* by configure\.' \
      | sed -e 's/ $//g'
  echo
done
cat <<EOF
#ifdef __cplusplus
}
#endif
#endif /* JEMALLOC_H_ */
EOF
jemalloc/x64/include/jemalloc/jemalloc_defs.h
New file
@@ -0,0 +1,49 @@
/* include/jemalloc/jemalloc_defs.h.  Generated from jemalloc_defs.h.in by configure.  */
/* Defined if __attribute__((...)) syntax is supported. */
/* #undef JEMALLOC_HAVE_ATTR */
/* Defined if alloc_size attribute is supported. */
/* #undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE */
/* Defined if format_arg(...) attribute is supported. */
/* #undef JEMALLOC_HAVE_ATTR_FORMAT_ARG */
/* Defined if format(gnu_printf, ...) attribute is supported. */
/* #undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF */
/* Defined if format(printf, ...) attribute is supported. */
/* #undef JEMALLOC_HAVE_ATTR_FORMAT_PRINTF */
/*
 * Define overrides for non-standard allocator-related functions if they are
 * present on the system.
 */
/* #undef JEMALLOC_OVERRIDE_MEMALIGN */
/* #undef JEMALLOC_OVERRIDE_VALLOC */
/*
 * At least Linux omits the "const" in:
 *
 *   size_t malloc_usable_size(const void *ptr);
 *
 * Match the operating system's prototype.
 */
#define JEMALLOC_USABLE_SIZE_CONST const
/*
 * If defined, specify throw() for the public function prototypes when compiling
 * with C++.  The only justification for this is to match the prototypes that
 * glibc defines.
 */
/* #undef JEMALLOC_USE_CXX_THROW */
#ifdef _MSC_VER
#  ifdef _WIN64
#    define LG_SIZEOF_PTR_WIN 3
#  else
#    define LG_SIZEOF_PTR_WIN 2
#  endif
#endif
/* sizeof(void *) == 2^LG_SIZEOF_PTR. */
#define LG_SIZEOF_PTR LG_SIZEOF_PTR_WIN
jemalloc/x64/include/jemalloc/jemalloc_defs.h.in
New file
@@ -0,0 +1,48 @@
/* Defined if __attribute__((...)) syntax is supported. */
#undef JEMALLOC_HAVE_ATTR
/* Defined if alloc_size attribute is supported. */
#undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE
/* Defined if format_arg(...) attribute is supported. */
#undef JEMALLOC_HAVE_ATTR_FORMAT_ARG
/* Defined if format(gnu_printf, ...) attribute is supported. */
#undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF
/* Defined if format(printf, ...) attribute is supported. */
#undef JEMALLOC_HAVE_ATTR_FORMAT_PRINTF
/*
 * Define overrides for non-standard allocator-related functions if they are
 * present on the system.
 */
#undef JEMALLOC_OVERRIDE_MEMALIGN
#undef JEMALLOC_OVERRIDE_VALLOC
/*
 * At least Linux omits the "const" in:
 *
 *   size_t malloc_usable_size(const void *ptr);
 *
 * Match the operating system's prototype.
 */
#undef JEMALLOC_USABLE_SIZE_CONST
/*
 * If defined, specify throw() for the public function prototypes when compiling
 * with C++.  The only justification for this is to match the prototypes that
 * glibc defines.
 */
#undef JEMALLOC_USE_CXX_THROW
#ifdef _MSC_VER
#  ifdef _WIN64
#    define LG_SIZEOF_PTR_WIN 3
#  else
#    define LG_SIZEOF_PTR_WIN 2
#  endif
#endif
/* sizeof(void *) == 2^LG_SIZEOF_PTR. */
#undef LG_SIZEOF_PTR
jemalloc/x64/include/jemalloc/jemalloc_macros.h
New file
@@ -0,0 +1,129 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <strings.h>
#define JEMALLOC_VERSION "0.0.0-0-g0000000000000000000000000000000000000000"
#define JEMALLOC_VERSION_MAJOR 0
#define JEMALLOC_VERSION_MINOR 0
#define JEMALLOC_VERSION_BUGFIX 0
#define JEMALLOC_VERSION_NREV 0
#define JEMALLOC_VERSION_GID "0000000000000000000000000000000000000000"
#define JEMALLOC_VERSION_GID_IDENT 0000000000000000000000000000000000000000
#define MALLOCX_LG_ALIGN(la)    ((int)(la))
#if LG_SIZEOF_PTR == 2
#  define MALLOCX_ALIGN(a)    ((int)(ffs((int)(a))-1))
#else
#  define MALLOCX_ALIGN(a)                        \
     ((int)(((size_t)(a) < (size_t)INT_MAX) ? ffs((int)(a))-1 :    \
     ffs((int)(((size_t)(a))>>32))+31))
#endif
#define MALLOCX_ZERO    ((int)0x40)
/*
 * Bias tcache index bits so that 0 encodes "automatic tcache management", and 1
 * encodes MALLOCX_TCACHE_NONE.
 */
#define MALLOCX_TCACHE(tc)    ((int)(((tc)+2) << 8))
#define MALLOCX_TCACHE_NONE    MALLOCX_TCACHE(-1)
/*
 * Bias arena index bits so that 0 encodes "use an automatically chosen arena".
 */
#define MALLOCX_ARENA(a)    ((((int)(a))+1) << 20)
/*
 * Use as arena index in "arena.<i>.{purge,decay,dss}" and
 * "stats.arenas.<i>.*" mallctl interfaces to select all arenas.  This
 * definition is intentionally specified in raw decimal format to support
 * cpp-based string concatenation, e.g.
 *
 *   #define STRINGIFY_HELPER(x) #x
 *   #define STRINGIFY(x) STRINGIFY_HELPER(x)
 *
 *   mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", NULL, NULL, NULL,
 *       0);
 */
#define MALLCTL_ARENAS_ALL    4096
/*
 * Use as arena index in "stats.arenas.<i>.*" mallctl interfaces to select
 * destroyed arenas.
 */
#define MALLCTL_ARENAS_DESTROYED    4097
#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW)
#  define JEMALLOC_CXX_THROW throw()
#else
#  define JEMALLOC_CXX_THROW
#endif
#if defined(_MSC_VER)
#  define JEMALLOC_ATTR(s)
#  define JEMALLOC_ALIGNED(s) __declspec(align(s))
#  define JEMALLOC_ALLOC_SIZE(s)
#  define JEMALLOC_ALLOC_SIZE2(s1, s2)
#  ifndef JEMALLOC_EXPORT
#    ifdef DLLEXPORT
#      define JEMALLOC_EXPORT __declspec(dllexport)
#    else
#      define JEMALLOC_EXPORT __declspec(dllimport)
#    endif
#  endif
#  define JEMALLOC_FORMAT_ARG(i)
#  define JEMALLOC_FORMAT_PRINTF(s, i)
#  define JEMALLOC_NOINLINE __declspec(noinline)
#  ifdef __cplusplus
#    define JEMALLOC_NOTHROW __declspec(nothrow)
#  else
#    define JEMALLOC_NOTHROW
#  endif
#  define JEMALLOC_SECTION(s) __declspec(allocate(s))
#  define JEMALLOC_RESTRICT_RETURN __declspec(restrict)
#  if _MSC_VER >= 1900 && !defined(__EDG__)
#    define JEMALLOC_ALLOCATOR __declspec(allocator)
#  else
#    define JEMALLOC_ALLOCATOR
#  endif
#elif defined(JEMALLOC_HAVE_ATTR)
#  define JEMALLOC_ATTR(s) __attribute__((s))
#  define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s))
#  ifdef JEMALLOC_HAVE_ATTR_ALLOC_SIZE
#    define JEMALLOC_ALLOC_SIZE(s) JEMALLOC_ATTR(alloc_size(s))
#    define JEMALLOC_ALLOC_SIZE2(s1, s2) JEMALLOC_ATTR(alloc_size(s1, s2))
#  else
#    define JEMALLOC_ALLOC_SIZE(s)
#    define JEMALLOC_ALLOC_SIZE2(s1, s2)
#  endif
#  ifndef JEMALLOC_EXPORT
#    define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default"))
#  endif
#  ifdef JEMALLOC_HAVE_ATTR_FORMAT_ARG
#    define JEMALLOC_FORMAT_ARG(i) JEMALLOC_ATTR(__format_arg__(3))
#  else
#    define JEMALLOC_FORMAT_ARG(i)
#  endif
#  ifdef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF
#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(gnu_printf, s, i))
#  elif defined(JEMALLOC_HAVE_ATTR_FORMAT_PRINTF)
#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(printf, s, i))
#  else
#    define JEMALLOC_FORMAT_PRINTF(s, i)
#  endif
#  define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline)
#  define JEMALLOC_NOTHROW JEMALLOC_ATTR(nothrow)
#  define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))
#  define JEMALLOC_RESTRICT_RETURN
#  define JEMALLOC_ALLOCATOR
#else
#  define JEMALLOC_ATTR(s)
#  define JEMALLOC_ALIGNED(s)
#  define JEMALLOC_ALLOC_SIZE(s)
#  define JEMALLOC_ALLOC_SIZE2(s1, s2)
#  define JEMALLOC_EXPORT
#  define JEMALLOC_FORMAT_PRINTF(s, i)
#  define JEMALLOC_NOINLINE
#  define JEMALLOC_NOTHROW
#  define JEMALLOC_SECTION(s)
#  define JEMALLOC_RESTRICT_RETURN
#  define JEMALLOC_ALLOCATOR
#endif
jemalloc/x64/include/jemalloc/jemalloc_macros.h.in
New file
@@ -0,0 +1,129 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
#include <strings.h>
#define JEMALLOC_VERSION "@jemalloc_version@"
#define JEMALLOC_VERSION_MAJOR @jemalloc_version_major@
#define JEMALLOC_VERSION_MINOR @jemalloc_version_minor@
#define JEMALLOC_VERSION_BUGFIX @jemalloc_version_bugfix@
#define JEMALLOC_VERSION_NREV @jemalloc_version_nrev@
#define JEMALLOC_VERSION_GID "@jemalloc_version_gid@"
#define JEMALLOC_VERSION_GID_IDENT @jemalloc_version_gid@
#define MALLOCX_LG_ALIGN(la)    ((int)(la))
#if LG_SIZEOF_PTR == 2
#  define MALLOCX_ALIGN(a)    ((int)(ffs((int)(a))-1))
#else
#  define MALLOCX_ALIGN(a)                        \
     ((int)(((size_t)(a) < (size_t)INT_MAX) ? ffs((int)(a))-1 :    \
     ffs((int)(((size_t)(a))>>32))+31))
#endif
#define MALLOCX_ZERO    ((int)0x40)
/*
 * Bias tcache index bits so that 0 encodes "automatic tcache management", and 1
 * encodes MALLOCX_TCACHE_NONE.
 */
#define MALLOCX_TCACHE(tc)    ((int)(((tc)+2) << 8))
#define MALLOCX_TCACHE_NONE    MALLOCX_TCACHE(-1)
/*
 * Bias arena index bits so that 0 encodes "use an automatically chosen arena".
 */
#define MALLOCX_ARENA(a)    ((((int)(a))+1) << 20)
/*
 * Use as arena index in "arena.<i>.{purge,decay,dss}" and
 * "stats.arenas.<i>.*" mallctl interfaces to select all arenas.  This
 * definition is intentionally specified in raw decimal format to support
 * cpp-based string concatenation, e.g.
 *
 *   #define STRINGIFY_HELPER(x) #x
 *   #define STRINGIFY(x) STRINGIFY_HELPER(x)
 *
 *   mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", NULL, NULL, NULL,
 *       0);
 */
#define MALLCTL_ARENAS_ALL    4096
/*
 * Use as arena index in "stats.arenas.<i>.*" mallctl interfaces to select
 * destroyed arenas.
 */
#define MALLCTL_ARENAS_DESTROYED    4097
#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW)
#  define JEMALLOC_CXX_THROW throw()
#else
#  define JEMALLOC_CXX_THROW
#endif
#if defined(_MSC_VER)
#  define JEMALLOC_ATTR(s)
#  define JEMALLOC_ALIGNED(s) __declspec(align(s))
#  define JEMALLOC_ALLOC_SIZE(s)
#  define JEMALLOC_ALLOC_SIZE2(s1, s2)
#  ifndef JEMALLOC_EXPORT
#    ifdef DLLEXPORT
#      define JEMALLOC_EXPORT __declspec(dllexport)
#    else
#      define JEMALLOC_EXPORT __declspec(dllimport)
#    endif
#  endif
#  define JEMALLOC_FORMAT_ARG(i)
#  define JEMALLOC_FORMAT_PRINTF(s, i)
#  define JEMALLOC_NOINLINE __declspec(noinline)
#  ifdef __cplusplus
#    define JEMALLOC_NOTHROW __declspec(nothrow)
#  else
#    define JEMALLOC_NOTHROW
#  endif
#  define JEMALLOC_SECTION(s) __declspec(allocate(s))
#  define JEMALLOC_RESTRICT_RETURN __declspec(restrict)
#  if _MSC_VER >= 1900 && !defined(__EDG__)
#    define JEMALLOC_ALLOCATOR __declspec(allocator)
#  else
#    define JEMALLOC_ALLOCATOR
#  endif
#elif defined(JEMALLOC_HAVE_ATTR)
#  define JEMALLOC_ATTR(s) __attribute__((s))
#  define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s))
#  ifdef JEMALLOC_HAVE_ATTR_ALLOC_SIZE
#    define JEMALLOC_ALLOC_SIZE(s) JEMALLOC_ATTR(alloc_size(s))
#    define JEMALLOC_ALLOC_SIZE2(s1, s2) JEMALLOC_ATTR(alloc_size(s1, s2))
#  else
#    define JEMALLOC_ALLOC_SIZE(s)
#    define JEMALLOC_ALLOC_SIZE2(s1, s2)
#  endif
#  ifndef JEMALLOC_EXPORT
#    define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default"))
#  endif
#  ifdef JEMALLOC_HAVE_ATTR_FORMAT_ARG
#    define JEMALLOC_FORMAT_ARG(i) JEMALLOC_ATTR(__format_arg__(3))
#  else
#    define JEMALLOC_FORMAT_ARG(i)
#  endif
#  ifdef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF
#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(gnu_printf, s, i))
#  elif defined(JEMALLOC_HAVE_ATTR_FORMAT_PRINTF)
#    define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(printf, s, i))
#  else
#    define JEMALLOC_FORMAT_PRINTF(s, i)
#  endif
#  define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline)
#  define JEMALLOC_NOTHROW JEMALLOC_ATTR(nothrow)
#  define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))
#  define JEMALLOC_RESTRICT_RETURN
#  define JEMALLOC_ALLOCATOR
#else
#  define JEMALLOC_ATTR(s)
#  define JEMALLOC_ALIGNED(s)
#  define JEMALLOC_ALLOC_SIZE(s)
#  define JEMALLOC_ALLOC_SIZE2(s1, s2)
#  define JEMALLOC_EXPORT
#  define JEMALLOC_FORMAT_PRINTF(s, i)
#  define JEMALLOC_NOINLINE
#  define JEMALLOC_NOTHROW
#  define JEMALLOC_SECTION(s)
#  define JEMALLOC_RESTRICT_RETURN
#  define JEMALLOC_ALLOCATOR
#endif
jemalloc/x64/include/jemalloc/jemalloc_mangle.h
New file
@@ -0,0 +1,64 @@
/*
 * By default application code must explicitly refer to mangled symbol names,
 * so that it is possible to use jemalloc in conjunction with another allocator
 * in the same application.  Define JEMALLOC_MANGLE in order to cause automatic
 * name mangling that matches the API prefixing that happened as a result of
 * --with-mangling and/or --with-jemalloc-prefix configuration settings.
 */
#ifdef JEMALLOC_MANGLE
#  ifndef JEMALLOC_NO_DEMANGLE
#    define JEMALLOC_NO_DEMANGLE
#  endif
#  define aligned_alloc je_aligned_alloc
#  define calloc je_calloc
#  define dallocx je_dallocx
#  define free je_free
#  define mallctl je_mallctl
#  define mallctlbymib je_mallctlbymib
#  define mallctlnametomib je_mallctlnametomib
#  define malloc je_malloc
#  define malloc_conf je_malloc_conf
#  define malloc_message je_malloc_message
#  define malloc_stats_print je_malloc_stats_print
#  define malloc_usable_size je_malloc_usable_size
#  define mallocx je_mallocx
#  define smallocx_0000000000000000000000000000000000000000 je_smallocx_0000000000000000000000000000000000000000
#  define nallocx je_nallocx
#  define posix_memalign je_posix_memalign
#  define rallocx je_rallocx
#  define realloc je_realloc
#  define sallocx je_sallocx
#  define sdallocx je_sdallocx
#  define xallocx je_xallocx
#endif
/*
 * The je_* macros can be used as stable alternative names for the
 * public jemalloc API if JEMALLOC_NO_DEMANGLE is defined.  This is primarily
 * meant for use in jemalloc itself, but it can be used by application code to
 * provide isolation from the name mangling specified via --with-mangling
 * and/or --with-jemalloc-prefix.
 */
#ifndef JEMALLOC_NO_DEMANGLE
#  undef je_aligned_alloc
#  undef je_calloc
#  undef je_dallocx
#  undef je_free
#  undef je_mallctl
#  undef je_mallctlbymib
#  undef je_mallctlnametomib
#  undef je_malloc
#  undef je_malloc_conf
#  undef je_malloc_message
#  undef je_malloc_stats_print
#  undef je_malloc_usable_size
#  undef je_mallocx
#  undef je_smallocx_0000000000000000000000000000000000000000
#  undef je_nallocx
#  undef je_posix_memalign
#  undef je_rallocx
#  undef je_realloc
#  undef je_sallocx
#  undef je_sdallocx
#  undef je_xallocx
#endif
jemalloc/x64/include/jemalloc/jemalloc_mangle.sh
New file
@@ -0,0 +1,45 @@
#!/bin/sh -eu
public_symbols_txt=$1
symbol_prefix=$2
cat <<EOF
/*
 * By default application code must explicitly refer to mangled symbol names,
 * so that it is possible to use jemalloc in conjunction with another allocator
 * in the same application.  Define JEMALLOC_MANGLE in order to cause automatic
 * name mangling that matches the API prefixing that happened as a result of
 * --with-mangling and/or --with-jemalloc-prefix configuration settings.
 */
#ifdef JEMALLOC_MANGLE
#  ifndef JEMALLOC_NO_DEMANGLE
#    define JEMALLOC_NO_DEMANGLE
#  endif
EOF
for nm in `cat ${public_symbols_txt}` ; do
  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
  echo "#  define ${n} ${symbol_prefix}${n}"
done
cat <<EOF
#endif
/*
 * The ${symbol_prefix}* macros can be used as stable alternative names for the
 * public jemalloc API if JEMALLOC_NO_DEMANGLE is defined.  This is primarily
 * meant for use in jemalloc itself, but it can be used by application code to
 * provide isolation from the name mangling specified via --with-mangling
 * and/or --with-jemalloc-prefix.
 */
#ifndef JEMALLOC_NO_DEMANGLE
EOF
for nm in `cat ${public_symbols_txt}` ; do
  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
  echo "#  undef ${symbol_prefix}${n}"
done
cat <<EOF
#endif
EOF
jemalloc/x64/include/jemalloc/jemalloc_mangle_jet.h
New file
@@ -0,0 +1,64 @@
/*
 * By default application code must explicitly refer to mangled symbol names,
 * so that it is possible to use jemalloc in conjunction with another allocator
 * in the same application.  Define JEMALLOC_MANGLE in order to cause automatic
 * name mangling that matches the API prefixing that happened as a result of
 * --with-mangling and/or --with-jemalloc-prefix configuration settings.
 */
#ifdef JEMALLOC_MANGLE
#  ifndef JEMALLOC_NO_DEMANGLE
#    define JEMALLOC_NO_DEMANGLE
#  endif
#  define aligned_alloc jet_aligned_alloc
#  define calloc jet_calloc
#  define dallocx jet_dallocx
#  define free jet_free
#  define mallctl jet_mallctl
#  define mallctlbymib jet_mallctlbymib
#  define mallctlnametomib jet_mallctlnametomib
#  define malloc jet_malloc
#  define malloc_conf jet_malloc_conf
#  define malloc_message jet_malloc_message
#  define malloc_stats_print jet_malloc_stats_print
#  define malloc_usable_size jet_malloc_usable_size
#  define mallocx jet_mallocx
#  define smallocx_0000000000000000000000000000000000000000 jet_smallocx_0000000000000000000000000000000000000000
#  define nallocx jet_nallocx
#  define posix_memalign jet_posix_memalign
#  define rallocx jet_rallocx
#  define realloc jet_realloc
#  define sallocx jet_sallocx
#  define sdallocx jet_sdallocx
#  define xallocx jet_xallocx
#endif
/*
 * The jet_* macros can be used as stable alternative names for the
 * public jemalloc API if JEMALLOC_NO_DEMANGLE is defined.  This is primarily
 * meant for use in jemalloc itself, but it can be used by application code to
 * provide isolation from the name mangling specified via --with-mangling
 * and/or --with-jemalloc-prefix.
 */
#ifndef JEMALLOC_NO_DEMANGLE
#  undef jet_aligned_alloc
#  undef jet_calloc
#  undef jet_dallocx
#  undef jet_free
#  undef jet_mallctl
#  undef jet_mallctlbymib
#  undef jet_mallctlnametomib
#  undef jet_malloc
#  undef jet_malloc_conf
#  undef jet_malloc_message
#  undef jet_malloc_stats_print
#  undef jet_malloc_usable_size
#  undef jet_mallocx
#  undef jet_smallocx_0000000000000000000000000000000000000000
#  undef jet_nallocx
#  undef jet_posix_memalign
#  undef jet_rallocx
#  undef jet_realloc
#  undef jet_sallocx
#  undef jet_sdallocx
#  undef jet_xallocx
#endif
jemalloc/x64/include/jemalloc/jemalloc_protos.h
New file
@@ -0,0 +1,66 @@
/*
 * The je_ prefix on the following public symbol declarations is an artifact
 * of namespace management, and should be omitted in application code unless
 * JEMALLOC_NO_DEMANGLE is defined (see jemalloc_mangle.h).
 */
extern JEMALLOC_EXPORT const char    *je_malloc_conf;
extern JEMALLOC_EXPORT void        (*je_malloc_message)(void *cbopaque,
    const char *s);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_malloc(size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_calloc(size_t num, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    je_posix_memalign(void **memptr,
    size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1));
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_aligned_alloc(size_t alignment,
    size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc)
    JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_realloc(void *ptr, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    je_free(void *ptr)
    JEMALLOC_CXX_THROW;
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_mallocx(size_t size, int flags)
    JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_rallocx(void *ptr, size_t size,
    int flags) JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    je_xallocx(void *ptr, size_t size,
    size_t extra, int flags);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    je_sallocx(const void *ptr,
    int flags) JEMALLOC_ATTR(pure);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    je_dallocx(void *ptr, int flags);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    je_sdallocx(void *ptr, size_t size,
    int flags);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    je_nallocx(size_t size, int flags)
    JEMALLOC_ATTR(pure);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    je_mallctl(const char *name,
    void *oldp, size_t *oldlenp, void *newp, size_t newlen);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    je_mallctlnametomib(const char *name,
    size_t *mibp, size_t *miblenp);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    je_mallctlbymib(const size_t *mib,
    size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    je_malloc_stats_print(
    void (*write_cb)(void *, const char *), void *je_cbopaque,
    const char *opts);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    je_malloc_usable_size(
    JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW;
#ifdef JEMALLOC_OVERRIDE_MEMALIGN
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_memalign(size_t alignment, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc);
#endif
#ifdef JEMALLOC_OVERRIDE_VALLOC
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *je_valloc(size_t size) JEMALLOC_CXX_THROW
    JEMALLOC_ATTR(malloc);
#endif
jemalloc/x64/include/jemalloc/jemalloc_protos.h.in
New file
@@ -0,0 +1,66 @@
/*
 * The @je_@ prefix on the following public symbol declarations is an artifact
 * of namespace management, and should be omitted in application code unless
 * JEMALLOC_NO_DEMANGLE is defined (see jemalloc_mangle@install_suffix@.h).
 */
extern JEMALLOC_EXPORT const char    *@je_@malloc_conf;
extern JEMALLOC_EXPORT void        (*@je_@malloc_message)(void *cbopaque,
    const char *s);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *@je_@malloc(size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *@je_@calloc(size_t num, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    @je_@posix_memalign(void **memptr,
    size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1));
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *@je_@aligned_alloc(size_t alignment,
    size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc)
    JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *@je_@realloc(void *ptr, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    @je_@free(void *ptr)
    JEMALLOC_CXX_THROW;
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *@je_@mallocx(size_t size, int flags)
    JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *@je_@rallocx(void *ptr, size_t size,
    int flags) JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    @je_@xallocx(void *ptr, size_t size,
    size_t extra, int flags);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    @je_@sallocx(const void *ptr,
    int flags) JEMALLOC_ATTR(pure);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    @je_@dallocx(void *ptr, int flags);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    @je_@sdallocx(void *ptr, size_t size,
    int flags);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    @je_@nallocx(size_t size, int flags)
    JEMALLOC_ATTR(pure);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    @je_@mallctl(const char *name,
    void *oldp, size_t *oldlenp, void *newp, size_t newlen);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    @je_@mallctlnametomib(const char *name,
    size_t *mibp, size_t *miblenp);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    @je_@mallctlbymib(const size_t *mib,
    size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    @je_@malloc_stats_print(
    void (*write_cb)(void *, const char *), void *@je_@cbopaque,
    const char *opts);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    @je_@malloc_usable_size(
    JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW;
#ifdef JEMALLOC_OVERRIDE_MEMALIGN
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *@je_@memalign(size_t alignment, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc);
#endif
#ifdef JEMALLOC_OVERRIDE_VALLOC
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *@je_@valloc(size_t size) JEMALLOC_CXX_THROW
    JEMALLOC_ATTR(malloc);
#endif
jemalloc/x64/include/jemalloc/jemalloc_protos_jet.h
New file
@@ -0,0 +1,66 @@
/*
 * The jet_ prefix on the following public symbol declarations is an artifact
 * of namespace management, and should be omitted in application code unless
 * JEMALLOC_NO_DEMANGLE is defined (see jemalloc_mangle@install_suffix@.h).
 */
extern JEMALLOC_EXPORT const char    *jet_malloc_conf;
extern JEMALLOC_EXPORT void        (*jet_malloc_message)(void *cbopaque,
    const char *s);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *jet_malloc(size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *jet_calloc(size_t num, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    jet_posix_memalign(void **memptr,
    size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1));
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *jet_aligned_alloc(size_t alignment,
    size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc)
    JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *jet_realloc(void *ptr, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    jet_free(void *ptr)
    JEMALLOC_CXX_THROW;
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *jet_mallocx(size_t size, int flags)
    JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *jet_rallocx(void *ptr, size_t size,
    int flags) JEMALLOC_ALLOC_SIZE(2);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    jet_xallocx(void *ptr, size_t size,
    size_t extra, int flags);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    jet_sallocx(const void *ptr,
    int flags) JEMALLOC_ATTR(pure);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    jet_dallocx(void *ptr, int flags);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    jet_sdallocx(void *ptr, size_t size,
    int flags);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    jet_nallocx(size_t size, int flags)
    JEMALLOC_ATTR(pure);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    jet_mallctl(const char *name,
    void *oldp, size_t *oldlenp, void *newp, size_t newlen);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    jet_mallctlnametomib(const char *name,
    size_t *mibp, size_t *miblenp);
JEMALLOC_EXPORT int JEMALLOC_NOTHROW    jet_mallctlbymib(const size_t *mib,
    size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
JEMALLOC_EXPORT void JEMALLOC_NOTHROW    jet_malloc_stats_print(
    void (*write_cb)(void *, const char *), void *jet_cbopaque,
    const char *opts);
JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW    jet_malloc_usable_size(
    JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW;
#ifdef JEMALLOC_OVERRIDE_MEMALIGN
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *jet_memalign(size_t alignment, size_t size)
    JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc);
#endif
#ifdef JEMALLOC_OVERRIDE_VALLOC
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
    void JEMALLOC_NOTHROW    *jet_valloc(size_t size) JEMALLOC_CXX_THROW
    JEMALLOC_ATTR(malloc);
#endif
jemalloc/x64/include/jemalloc/jemalloc_rename.h
New file
@@ -0,0 +1,28 @@
/*
 * Name mangling for public symbols is controlled by --with-mangling and
 * --with-jemalloc-prefix.  With default settings the je_ prefix is stripped by
 * these macro definitions.
 */
#ifndef JEMALLOC_NO_RENAME
#  define je_aligned_alloc je_aligned_alloc
#  define je_calloc je_calloc
#  define je_dallocx je_dallocx
#  define je_free je_free
#  define je_mallctl je_mallctl
#  define je_mallctlbymib je_mallctlbymib
#  define je_mallctlnametomib je_mallctlnametomib
#  define je_malloc je_malloc
#  define je_malloc_conf je_malloc_conf
#  define je_malloc_message je_malloc_message
#  define je_malloc_stats_print je_malloc_stats_print
#  define je_malloc_usable_size je_malloc_usable_size
#  define je_mallocx je_mallocx
#  define je_smallocx_0000000000000000000000000000000000000000 je_smallocx_0000000000000000000000000000000000000000
#  define je_nallocx je_nallocx
#  define je_posix_memalign je_posix_memalign
#  define je_rallocx je_rallocx
#  define je_realloc je_realloc
#  define je_sallocx je_sallocx
#  define je_sdallocx je_sdallocx
#  define je_xallocx je_xallocx
#endif
jemalloc/x64/include/jemalloc/jemalloc_rename.sh
New file
@@ -0,0 +1,22 @@
#!/bin/sh
public_symbols_txt=$1
cat <<EOF
/*
 * Name mangling for public symbols is controlled by --with-mangling and
 * --with-jemalloc-prefix.  With default settings the je_ prefix is stripped by
 * these macro definitions.
 */
#ifndef JEMALLOC_NO_RENAME
EOF
for nm in `cat ${public_symbols_txt}` ; do
  n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
  m=`echo ${nm} |tr ':' ' ' |awk '{print $2}'`
  echo "#  define je_${n} ${m}"
done
cat <<EOF
#endif
EOF
jemalloc/x64/include/jemalloc/jemalloc_typedefs.h
New file
@@ -0,0 +1,77 @@
typedef struct extent_hooks_s extent_hooks_t;
/*
 * void *
 * extent_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
 *     size_t alignment, bool *zero, bool *commit, unsigned arena_ind);
 */
typedef void *(extent_alloc_t)(extent_hooks_t *, void *, size_t, size_t, bool *,
    bool *, unsigned);
/*
 * bool
 * extent_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     bool committed, unsigned arena_ind);
 */
typedef bool (extent_dalloc_t)(extent_hooks_t *, void *, size_t, bool,
    unsigned);
/*
 * void
 * extent_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     bool committed, unsigned arena_ind);
 */
typedef void (extent_destroy_t)(extent_hooks_t *, void *, size_t, bool,
    unsigned);
/*
 * bool
 * extent_commit(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t offset, size_t length, unsigned arena_ind);
 */
typedef bool (extent_commit_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
    unsigned);
/*
 * bool
 * extent_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t offset, size_t length, unsigned arena_ind);
 */
typedef bool (extent_decommit_t)(extent_hooks_t *, void *, size_t, size_t,
    size_t, unsigned);
/*
 * bool
 * extent_purge(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t offset, size_t length, unsigned arena_ind);
 */
typedef bool (extent_purge_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
    unsigned);
/*
 * bool
 * extent_split(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t size_a, size_t size_b, bool committed, unsigned arena_ind);
 */
typedef bool (extent_split_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
    bool, unsigned);
/*
 * bool
 * extent_merge(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
 *     void *addr_b, size_t size_b, bool committed, unsigned arena_ind);
 */
typedef bool (extent_merge_t)(extent_hooks_t *, void *, size_t, void *, size_t,
    bool, unsigned);
struct extent_hooks_s {
    extent_alloc_t        *alloc;
    extent_dalloc_t        *dalloc;
    extent_destroy_t    *destroy;
    extent_commit_t        *commit;
    extent_decommit_t    *decommit;
    extent_purge_t        *purge_lazy;
    extent_purge_t        *purge_forced;
    extent_split_t        *split;
    extent_merge_t        *merge;
};
jemalloc/x64/include/jemalloc/jemalloc_typedefs.h.in
New file
@@ -0,0 +1,77 @@
typedef struct extent_hooks_s extent_hooks_t;
/*
 * void *
 * extent_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
 *     size_t alignment, bool *zero, bool *commit, unsigned arena_ind);
 */
typedef void *(extent_alloc_t)(extent_hooks_t *, void *, size_t, size_t, bool *,
    bool *, unsigned);
/*
 * bool
 * extent_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     bool committed, unsigned arena_ind);
 */
typedef bool (extent_dalloc_t)(extent_hooks_t *, void *, size_t, bool,
    unsigned);
/*
 * void
 * extent_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     bool committed, unsigned arena_ind);
 */
typedef void (extent_destroy_t)(extent_hooks_t *, void *, size_t, bool,
    unsigned);
/*
 * bool
 * extent_commit(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t offset, size_t length, unsigned arena_ind);
 */
typedef bool (extent_commit_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
    unsigned);
/*
 * bool
 * extent_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t offset, size_t length, unsigned arena_ind);
 */
typedef bool (extent_decommit_t)(extent_hooks_t *, void *, size_t, size_t,
    size_t, unsigned);
/*
 * bool
 * extent_purge(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t offset, size_t length, unsigned arena_ind);
 */
typedef bool (extent_purge_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
    unsigned);
/*
 * bool
 * extent_split(extent_hooks_t *extent_hooks, void *addr, size_t size,
 *     size_t size_a, size_t size_b, bool committed, unsigned arena_ind);
 */
typedef bool (extent_split_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
    bool, unsigned);
/*
 * bool
 * extent_merge(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
 *     void *addr_b, size_t size_b, bool committed, unsigned arena_ind);
 */
typedef bool (extent_merge_t)(extent_hooks_t *, void *, size_t, void *, size_t,
    bool, unsigned);
struct extent_hooks_s {
    extent_alloc_t        *alloc;
    extent_dalloc_t        *dalloc;
    extent_destroy_t    *destroy;
    extent_commit_t        *commit;
    extent_decommit_t    *decommit;
    extent_purge_t        *purge_lazy;
    extent_purge_t        *purge_forced;
    extent_split_t        *split;
    extent_merge_t        *merge;
};
jemalloc/x64/include/msvc_compat/C99/stdbool.h
New file
@@ -0,0 +1,20 @@
#ifndef stdbool_h
#define stdbool_h
#include <wtypes.h>
/* MSVC doesn't define _Bool or bool in C, but does have BOOL */
/* Note this doesn't pass autoconf's test because (bool) 0.5 != true */
/* Clang-cl uses MSVC headers, so needs msvc_compat, but has _Bool as
 * a built-in type. */
#ifndef __clang__
typedef BOOL _Bool;
#endif
#define bool _Bool
#define true 1
#define false 0
#define __bool_true_false_are_defined 1
#endif /* stdbool_h */
jemalloc/x64/include/msvc_compat/C99/stdint.h
New file
@@ -0,0 +1,247 @@
// ISO C9x  compliant stdint.h for Microsoft Visual Studio
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
//
//  Copyright (c) 2006-2008 Alexander Chemeris
//
// 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. The name of the author may be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _MSC_VER // [
#error "Use this header only with Microsoft Visual C++ compilers!"
#endif // _MSC_VER ]
#ifndef _MSC_STDINT_H_ // [
#define _MSC_STDINT_H_
#if _MSC_VER > 1000
#pragma once
#endif
#include <limits.h>
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
// or compiler give many errors like this:
//   error C2733: second C linkage of overloaded function 'wmemchr' not allowed
#ifdef __cplusplus
extern "C" {
#endif
#  include <wchar.h>
#ifdef __cplusplus
}
#endif
// Define _W64 macros to mark types changing their size, like intptr_t.
#ifndef _W64
#  if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
#     define _W64 __w64
#  else
#     define _W64
#  endif
#endif
// 7.18.1 Integer types
// 7.18.1.1 Exact-width integer types
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
// realize that, e.g. char has the same size as __int8
// so we give up on __intX for them.
#if (_MSC_VER < 1300)
   typedef signed char       int8_t;
   typedef signed short      int16_t;
   typedef signed int        int32_t;
   typedef unsigned char     uint8_t;
   typedef unsigned short    uint16_t;
   typedef unsigned int      uint32_t;
#else
   typedef signed __int8     int8_t;
   typedef signed __int16    int16_t;
   typedef signed __int32    int32_t;
   typedef unsigned __int8   uint8_t;
   typedef unsigned __int16  uint16_t;
   typedef unsigned __int32  uint32_t;
#endif
typedef signed __int64       int64_t;
typedef unsigned __int64     uint64_t;
// 7.18.1.2 Minimum-width integer types
typedef int8_t    int_least8_t;
typedef int16_t   int_least16_t;
typedef int32_t   int_least32_t;
typedef int64_t   int_least64_t;
typedef uint8_t   uint_least8_t;
typedef uint16_t  uint_least16_t;
typedef uint32_t  uint_least32_t;
typedef uint64_t  uint_least64_t;
// 7.18.1.3 Fastest minimum-width integer types
typedef int8_t    int_fast8_t;
typedef int16_t   int_fast16_t;
typedef int32_t   int_fast32_t;
typedef int64_t   int_fast64_t;
typedef uint8_t   uint_fast8_t;
typedef uint16_t  uint_fast16_t;
typedef uint32_t  uint_fast32_t;
typedef uint64_t  uint_fast64_t;
// 7.18.1.4 Integer types capable of holding object pointers
#ifdef _WIN64 // [
   typedef signed __int64    intptr_t;
   typedef unsigned __int64  uintptr_t;
#else // _WIN64 ][
   typedef _W64 signed int   intptr_t;
   typedef _W64 unsigned int uintptr_t;
#endif // _WIN64 ]
// 7.18.1.5 Greatest-width integer types
typedef int64_t   intmax_t;
typedef uint64_t  uintmax_t;
// 7.18.2 Limits of specified-width integer types
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [   See footnote 220 at page 257 and footnote 221 at page 259
// 7.18.2.1 Limits of exact-width integer types
#define INT8_MIN     ((int8_t)_I8_MIN)
#define INT8_MAX     _I8_MAX
#define INT16_MIN    ((int16_t)_I16_MIN)
#define INT16_MAX    _I16_MAX
#define INT32_MIN    ((int32_t)_I32_MIN)
#define INT32_MAX    _I32_MAX
#define INT64_MIN    ((int64_t)_I64_MIN)
#define INT64_MAX    _I64_MAX
#define UINT8_MAX    _UI8_MAX
#define UINT16_MAX   _UI16_MAX
#define UINT32_MAX   _UI32_MAX
#define UINT64_MAX   _UI64_MAX
// 7.18.2.2 Limits of minimum-width integer types
#define INT_LEAST8_MIN    INT8_MIN
#define INT_LEAST8_MAX    INT8_MAX
#define INT_LEAST16_MIN   INT16_MIN
#define INT_LEAST16_MAX   INT16_MAX
#define INT_LEAST32_MIN   INT32_MIN
#define INT_LEAST32_MAX   INT32_MAX
#define INT_LEAST64_MIN   INT64_MIN
#define INT_LEAST64_MAX   INT64_MAX
#define UINT_LEAST8_MAX   UINT8_MAX
#define UINT_LEAST16_MAX  UINT16_MAX
#define UINT_LEAST32_MAX  UINT32_MAX
#define UINT_LEAST64_MAX  UINT64_MAX
// 7.18.2.3 Limits of fastest minimum-width integer types
#define INT_FAST8_MIN    INT8_MIN
#define INT_FAST8_MAX    INT8_MAX
#define INT_FAST16_MIN   INT16_MIN
#define INT_FAST16_MAX   INT16_MAX
#define INT_FAST32_MIN   INT32_MIN
#define INT_FAST32_MAX   INT32_MAX
#define INT_FAST64_MIN   INT64_MIN
#define INT_FAST64_MAX   INT64_MAX
#define UINT_FAST8_MAX   UINT8_MAX
#define UINT_FAST16_MAX  UINT16_MAX
#define UINT_FAST32_MAX  UINT32_MAX
#define UINT_FAST64_MAX  UINT64_MAX
// 7.18.2.4 Limits of integer types capable of holding object pointers
#ifdef _WIN64 // [
#  define INTPTR_MIN   INT64_MIN
#  define INTPTR_MAX   INT64_MAX
#  define UINTPTR_MAX  UINT64_MAX
#else // _WIN64 ][
#  define INTPTR_MIN   INT32_MIN
#  define INTPTR_MAX   INT32_MAX
#  define UINTPTR_MAX  UINT32_MAX
#endif // _WIN64 ]
// 7.18.2.5 Limits of greatest-width integer types
#define INTMAX_MIN   INT64_MIN
#define INTMAX_MAX   INT64_MAX
#define UINTMAX_MAX  UINT64_MAX
// 7.18.3 Limits of other integer types
#ifdef _WIN64 // [
#  define PTRDIFF_MIN  _I64_MIN
#  define PTRDIFF_MAX  _I64_MAX
#else  // _WIN64 ][
#  define PTRDIFF_MIN  _I32_MIN
#  define PTRDIFF_MAX  _I32_MAX
#endif  // _WIN64 ]
#define SIG_ATOMIC_MIN  INT_MIN
#define SIG_ATOMIC_MAX  INT_MAX
#ifndef SIZE_MAX // [
#  ifdef _WIN64 // [
#     define SIZE_MAX  _UI64_MAX
#  else // _WIN64 ][
#     define SIZE_MAX  _UI32_MAX
#  endif // _WIN64 ]
#endif // SIZE_MAX ]
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
#ifndef WCHAR_MIN // [
#  define WCHAR_MIN  0
#endif  // WCHAR_MIN ]
#ifndef WCHAR_MAX // [
#  define WCHAR_MAX  _UI16_MAX
#endif  // WCHAR_MAX ]
#define WINT_MIN  0
#define WINT_MAX  _UI16_MAX
#endif // __STDC_LIMIT_MACROS ]
// 7.18.4 Limits of other integer types
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [   See footnote 224 at page 260
// 7.18.4.1 Macros for minimum-width integer constants
#define INT8_C(val)  val##i8
#define INT16_C(val) val##i16
#define INT32_C(val) val##i32
#define INT64_C(val) val##i64
#define UINT8_C(val)  val##ui8
#define UINT16_C(val) val##ui16
#define UINT32_C(val) val##ui32
#define UINT64_C(val) val##ui64
// 7.18.4.2 Macros for greatest-width integer constants
#define INTMAX_C   INT64_C
#define UINTMAX_C  UINT64_C
#endif // __STDC_CONSTANT_MACROS ]
#endif // _MSC_STDINT_H_ ]
jemalloc/x64/include/msvc_compat/strings.h
New file
@@ -0,0 +1,58 @@
#ifndef strings_h
#define strings_h
/* MSVC doesn't define ffs/ffsl. This dummy strings.h header is provided
 * for both */
#ifdef _MSC_VER
#  include <intrin.h>
#  pragma intrinsic(_BitScanForward)
static __forceinline int ffsl(long x) {
    unsigned long i;
    if (_BitScanForward(&i, x)) {
        return i + 1;
    }
    return 0;
}
static __forceinline int ffs(int x) {
    return ffsl(x);
}
#  ifdef  _M_X64
#    pragma intrinsic(_BitScanForward64)
#  endif
static __forceinline int ffsll(unsigned __int64 x) {
    unsigned long i;
#ifdef  _M_X64
    if (_BitScanForward64(&i, x)) {
        return i + 1;
    }
    return 0;
#else
// Fallback for 32-bit build where 64-bit version not available
// assuming little endian
    union {
        unsigned __int64 ll;
        unsigned   long l[2];
    } s;
    s.ll = x;
    if (_BitScanForward(&i, s.l[0])) {
        return i + 1;
    } else if(_BitScanForward(&i, s.l[1])) {
        return i + 33;
    }
    return 0;
#endif
}
#else
#  define ffsll(x) __builtin_ffsll(x)
#  define ffsl(x) __builtin_ffsl(x)
#  define ffs(x) __builtin_ffs(x)
#endif
#endif /* strings_h */
jemalloc/x64/include/msvc_compat/windows_extra.h
New file
@@ -0,0 +1,6 @@
#ifndef MSVC_COMPAT_WINDOWS_EXTRA_H
#define MSVC_COMPAT_WINDOWS_EXTRA_H
#include <errno.h>
#endif /* MSVC_COMPAT_WINDOWS_EXTRA_H */