From 08fa92c1c94d9faddee48590a1a20506e89c191c Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Thu, 1 Dec 2022 13:43:10 +0100 Subject: 2022-12-01 12:41:00 --- .../source/libraries/mimalloc/CMakeLists.txt | 70 +- .../libraries/mimalloc/cmake/JoinPaths.cmake | 23 + .../mimalloc/cmake/mimalloc-config-version.cmake | 2 +- .../libraries/mimalloc/include/mimalloc-internal.h | 90 ++- .../mimalloc/include/mimalloc-new-delete.h | 4 +- .../libraries/mimalloc/include/mimalloc-track.h | 43 ++ .../libraries/mimalloc/include/mimalloc-types.h | 8 +- .../source/libraries/mimalloc/include/mimalloc.h | 20 +- .../luametatex/source/libraries/mimalloc/readme.md | 47 +- .../source/libraries/mimalloc/src/alloc-aligned.c | 81 ++- .../libraries/mimalloc/src/alloc-override-osx.c | 2 +- .../source/libraries/mimalloc/src/alloc-override.c | 10 +- .../source/libraries/mimalloc/src/alloc-posix.c | 9 +- .../source/libraries/mimalloc/src/alloc.c | 258 ++++--- .../source/libraries/mimalloc/src/arena.c | 161 ++++- .../source/libraries/mimalloc/src/bitmap.c | 21 +- .../source/libraries/mimalloc/src/bitmap.h | 4 + .../source/libraries/mimalloc/src/heap.c | 23 +- .../source/libraries/mimalloc/src/init.c | 12 +- .../source/libraries/mimalloc/src/options.c | 10 +- .../luametatex/source/libraries/mimalloc/src/os.c | 8 +- .../source/libraries/mimalloc/src/page.c | 67 +- .../source/libraries/mimalloc/src/random.c | 2 + .../source/libraries/mimalloc/src/region.c | 13 +- .../source/libraries/mimalloc/src/segment-cache.c | 18 +- .../source/libraries/mimalloc/src/segment.c | 41 +- .../source/libraries/mimalloc/src/stats.c | 2 +- .../luametatex/source/libraries/miniz/ChangeLog.md | 39 + source/luametatex/source/libraries/miniz/miniz.c | 334 ++++++--- source/luametatex/source/libraries/miniz/miniz.h | 130 +++- source/luametatex/source/libraries/miniz/readme.md | 14 +- source/luametatex/source/lua/lmtcallbacklib.h | 26 +- source/luametatex/source/lua/lmtnodelib.c | 58 +- source/luametatex/source/lua/lmttexlib.c | 10 +- source/luametatex/source/lua/lmttokenlib.c | 21 +- source/luametatex/source/luametatex.h | 2 +- source/luametatex/source/luarest/lmtstrlibext.c | 6 +- source/luametatex/source/mp/mpc/mp.c | 6 +- source/luametatex/source/mp/mpc/mpmathdouble.c | 6 +- source/luametatex/source/mp/mpw/mpmathdouble.w | 6 +- source/luametatex/source/tex/texcommands.c | 7 +- source/luametatex/source/tex/texcommands.h | 3 + source/luametatex/source/tex/texdumpdata.h | 2 +- source/luametatex/source/tex/texequivalents.c | 2 +- source/luametatex/source/tex/texequivalents.h | 11 +- source/luametatex/source/tex/texexpand.c | 19 +- source/luametatex/source/tex/texfont.c | 804 ++++++++++++--------- source/luametatex/source/tex/texfont.h | 3 + source/luametatex/source/tex/texinputstack.h | 4 +- source/luametatex/source/tex/texinserts.c | 2 +- source/luametatex/source/tex/texlinebreak.c | 6 +- source/luametatex/source/tex/texmaincontrol.c | 57 +- source/luametatex/source/tex/texmath.c | 183 +++-- source/luametatex/source/tex/texmath.h | 7 +- source/luametatex/source/tex/texmathcodes.c | 12 +- source/luametatex/source/tex/texmathcodes.h | 2 + source/luametatex/source/tex/texmlist.c | 39 +- source/luametatex/source/tex/texnesting.c | 7 +- source/luametatex/source/tex/texnesting.h | 2 +- source/luametatex/source/tex/texnodes.c | 18 + source/luametatex/source/tex/texnodes.h | 136 ++-- source/luametatex/source/tex/texprimitive.c | 4 +- source/luametatex/source/tex/texprinting.h | 5 +- source/luametatex/source/tex/texscanning.c | 9 +- source/luametatex/source/tex/texscanning.h | 2 +- source/luametatex/source/tex/texstringpool.c | 4 +- source/luametatex/source/tex/texstringpool.h | 64 +- source/luametatex/source/tex/textextcodes.c | 51 +- source/luametatex/source/tex/textextcodes.h | 2 + source/luametatex/source/tex/textoken.c | 45 +- source/luametatex/source/tex/textoken.h | 39 +- 71 files changed, 2202 insertions(+), 1056 deletions(-) create mode 100644 source/luametatex/source/libraries/mimalloc/cmake/JoinPaths.cmake create mode 100644 source/luametatex/source/libraries/mimalloc/include/mimalloc-track.h (limited to 'source/luametatex') diff --git a/source/luametatex/source/libraries/mimalloc/CMakeLists.txt b/source/luametatex/source/libraries/mimalloc/CMakeLists.txt index 8127e0965..6cd826650 100644 --- a/source/luametatex/source/libraries/mimalloc/CMakeLists.txt +++ b/source/luametatex/source/libraries/mimalloc/CMakeLists.txt @@ -6,14 +6,16 @@ set(CMAKE_CXX_STANDARD 17) option(MI_SECURE "Use full security mitigations (like guard pages, allocation randomization, double-free mitigation, and free-list corruption detection)" OFF) option(MI_DEBUG_FULL "Use full internal heap invariant checking in DEBUG mode (expensive)" OFF) -option(MI_PADDING "Enable padding to detect heap block overflow (used only in DEBUG mode)" ON) +option(MI_PADDING "Enable padding to detect heap block overflow (used only in DEBUG mode or with Valgrind)" ON) option(MI_OVERRIDE "Override the standard malloc interface (e.g. define entry points for malloc() etc)" ON) option(MI_XMALLOC "Enable abort() call on memory allocation failure by default" OFF) option(MI_SHOW_ERRORS "Show error and warning messages by default (only enabled by default in DEBUG mode)" OFF) +option(MI_VALGRIND "Compile with Valgrind support (adds a small overhead)" OFF) option(MI_USE_CXX "Use the C++ compiler to compile the library (instead of the C compiler)" OFF) option(MI_SEE_ASM "Generate assembly files" OFF) option(MI_OSX_INTERPOSE "Use interpose to override standard malloc on macOS" ON) option(MI_OSX_ZONE "Use malloc zone to override standard malloc on macOS" ON) +option(MI_WIN_REDIRECT "Use redirection module ('mimalloc-redirect') on Windows if compiling mimalloc as a DLL" ON) option(MI_LOCAL_DYNAMIC_TLS "Use slightly slower, dlopen-compatible TLS mechanism (Unix)" OFF) option(MI_BUILD_SHARED "Build shared library" ON) option(MI_BUILD_STATIC "Build static library" ON) @@ -28,6 +30,7 @@ option(MI_CHECK_FULL "Use full internal invariant checking in DEBUG mode option(MI_INSTALL_TOPLEVEL "Install directly into $CMAKE_INSTALL_PREFIX instead of PREFIX/lib/mimalloc-version (deprecated)" OFF) option(MI_USE_LIBATOMIC "Explicitly link with -latomic (on older systems) (deprecated and detected automatically)" OFF) +include(CheckIncludeFiles) include(GNUInstallDirs) include("cmake/mimalloc-config-version.cmake") @@ -103,9 +106,37 @@ if(MI_OVERRIDE) endif() endif() +if(WIN32) + if (MI_WIN_REDIRECT) + if (MSVC_C_ARCHITECTURE_ID MATCHES "ARM") + message(STATUS "Cannot use redirection on Windows ARM (MI_WIN_REDIRECT=OFF)") + set(MI_WIN_REDIRECT OFF) + endif() + endif() + if (NOT MI_WIN_REDIRECT) + # use a negative define for backward compatibility + list(APPEND mi_defines MI_WIN_NOREDIRECT=1) + endif() +endif() + if(MI_SECURE) message(STATUS "Set full secure build (MI_SECURE=ON)") list(APPEND mi_defines MI_SECURE=4) + #if (MI_VALGRIND) + # message(WARNING "Secure mode is a bit weakened when compiling with Valgrind support as buffer overflow detection is no longer byte-precise (if running without valgrind)") + #endif() +endif() + +if(MI_VALGRIND) + CHECK_INCLUDE_FILES("valgrind/valgrind.h;valgrind/memcheck.h" MI_HAS_VALGRINDH) + if (NOT MI_HAS_VALGRINDH) + set(MI_VALGRIND OFF) + message(WARNING "Cannot find the 'valgrind/valgrind.h' and 'valgrind/memcheck.h' -- install valgrind first") + message(STATUS "Compile **without** Valgrind support (MI_VALGRIND=OFF)") + else() + message(STATUS "Compile with Valgrind support (MI_VALGRIND=ON)") + list(APPEND mi_defines MI_VALGRIND=1) + endif() endif() if(MI_SEE_ASM) @@ -179,7 +210,7 @@ if(MI_USE_CXX) if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang|Clang") list(APPEND mi_cflags -Wno-deprecated) endif() - if(CMAKE_CXX_COMPILER_ID MATCHES "Intel") + if(CMAKE_CXX_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM") list(APPEND mi_cflags -Kc++) endif() endif() @@ -217,18 +248,26 @@ endif() # extra needed libraries if(WIN32) list(APPEND mi_libraries psapi shell32 user32 advapi32 bcrypt) + set(pc_libraries "-lpsapi -lshell32 -luser32 -ladvapi32 -lbcrypt") else() + set(pc_libraries "") find_library(MI_LIBPTHREAD pthread) if (MI_LIBPTHREAD) list(APPEND mi_libraries ${MI_LIBPTHREAD}) + set(pc_libraries "${pc_libraries} -pthread") endif() find_library(MI_LIBRT rt) if(MI_LIBRT) list(APPEND mi_libraries ${MI_LIBRT}) + set(pc_libraries "${pc_libraries} -lrt") endif() find_library(MI_LIBATOMIC atomic) - if (MI_LIBATOMIC OR MI_USE_LIBATOMIC) - list(APPEND mi_libraries atomic) + if (NOT MI_LIBATOMIC AND MI_USE_LIBATOMIC) + set(MI_LIBATOMIC atomic) + endif() + if (MI_LIBATOMIC) + list(APPEND mi_libraries ${MI_LIBATOMIC}) + set(pc_libraries "${pc_libraries} -latomic") endif() endif() @@ -251,16 +290,18 @@ else() set(mi_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/mimalloc-${mi_version}") # for cmake package info endif() -if(MI_SECURE) - set(mi_basename "mimalloc-secure") -else() set(mi_basename "mimalloc") +if(MI_SECURE) + set(mi_basename "${mi_basename}-secure") +endif() +if(MI_VALGRIND) + set(mi_basename "${mi_basename}-valgrind") endif() - string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LC) if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel|none)$")) set(mi_basename "${mi_basename}-${CMAKE_BUILD_TYPE_LC}") #append build type (e.g. -debug) if not a release version endif() + if(MI_BUILD_SHARED) list(APPEND mi_build_targets "shared") endif() @@ -304,8 +345,8 @@ if(MI_BUILD_SHARED) $ $ ) - if(WIN32) - # On windows copy the mimalloc redirection dll too. + if(WIN32 AND MI_WIN_REDIRECT) + # On windows, link and copy the mimalloc redirection dll too. if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(MIMALLOC_REDIRECT_SUFFIX "32") else() @@ -376,6 +417,15 @@ if (MI_BUILD_OBJECT) RENAME ${mi_basename}${CMAKE_C_OUTPUT_EXTENSION} ) endif() +# pkg-config file support +include("cmake/JoinPaths.cmake") +join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") +join_paths(libdir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_LIBDIR}") + +configure_file(mimalloc.pc.in mimalloc.pc @ONLY) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mimalloc.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/") + # ----------------------------------------------------------------------------- # API surface testing # ----------------------------------------------------------------------------- diff --git a/source/luametatex/source/libraries/mimalloc/cmake/JoinPaths.cmake b/source/luametatex/source/libraries/mimalloc/cmake/JoinPaths.cmake new file mode 100644 index 000000000..c68d91b84 --- /dev/null +++ b/source/luametatex/source/libraries/mimalloc/cmake/JoinPaths.cmake @@ -0,0 +1,23 @@ +# This module provides function for joining paths +# known from most languages +# +# SPDX-License-Identifier: (MIT OR CC0-1.0) +# Copyright 2020 Jan Tojnar +# https://github.com/jtojnar/cmake-snips +# +# Modelled after Python’s os.path.join +# https://docs.python.org/3.7/library/os.path.html#os.path.join +# Windows not supported +function(join_paths joined_path first_path_segment) + set(temp_path "${first_path_segment}") + foreach(current_segment IN LISTS ARGN) + if(NOT ("${current_segment}" STREQUAL "")) + if(IS_ABSOLUTE "${current_segment}") + set(temp_path "${current_segment}") + else() + set(temp_path "${temp_path}/${current_segment}") + endif() + endif() + endforeach() + set(${joined_path} "${temp_path}" PARENT_SCOPE) +endfunction() diff --git a/source/luametatex/source/libraries/mimalloc/cmake/mimalloc-config-version.cmake b/source/luametatex/source/libraries/mimalloc/cmake/mimalloc-config-version.cmake index 8063afe6b..f0669c84d 100644 --- a/source/luametatex/source/libraries/mimalloc/cmake/mimalloc-config-version.cmake +++ b/source/luametatex/source/libraries/mimalloc/cmake/mimalloc-config-version.cmake @@ -1,6 +1,6 @@ set(mi_version_major 2) set(mi_version_minor 0) -set(mi_version_patch 6) +set(mi_version_patch 7) set(mi_version ${mi_version_major}.${mi_version_minor}) set(PACKAGE_VERSION ${mi_version}) diff --git a/source/luametatex/source/libraries/mimalloc/include/mimalloc-internal.h b/source/luametatex/source/libraries/mimalloc/include/mimalloc-internal.h index d691eca58..550b65433 100644 --- a/source/luametatex/source/libraries/mimalloc/include/mimalloc-internal.h +++ b/source/luametatex/source/libraries/mimalloc/include/mimalloc-internal.h @@ -9,6 +9,7 @@ terms of the MIT license. A copy of the license can be found in the file #define MIMALLOC_INTERNAL_H #include "mimalloc-types.h" +#include "mimalloc-track.h" #if (MI_DEBUG>0) #define mi_trace_message(...) _mi_trace_message(__VA_ARGS__) @@ -88,12 +89,14 @@ size_t _mi_os_good_alloc_size(size_t size); bool _mi_os_has_overcommit(void); // arena.c -void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld); -void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld); +void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld); +void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld); void _mi_arena_free(void* p, size_t size, size_t memid, bool is_committed, mi_os_tld_t* tld); +mi_arena_id_t _mi_arena_id_none(void); +bool _mi_arena_memid_is_suitable(size_t memid, mi_arena_id_t req_arena_id); // "segment-cache.c" -void* _mi_segment_cache_pop(size_t size, mi_commit_mask_t* commit_mask, mi_commit_mask_t* decommit_mask, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld); +void* _mi_segment_cache_pop(size_t size, mi_commit_mask_t* commit_mask, mi_commit_mask_t* decommit_mask, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld); bool _mi_segment_cache_push(void* start, size_t size, size_t memid, const mi_commit_mask_t* commit_mask, const mi_commit_mask_t* decommit_mask, bool is_large, bool is_pinned, mi_os_tld_t* tld); void _mi_segment_cache_collect(bool force, mi_os_tld_t* tld); void _mi_segment_map_allocated_at(const mi_segment_t* segment); @@ -115,16 +118,18 @@ void _mi_abandoned_collect(mi_heap_t* heap, bool force, mi_segments_tld_t* // "page.c" -void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_attr_malloc; +void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept mi_attr_malloc; void _mi_page_retire(mi_page_t* page) mi_attr_noexcept; // free the page if there are no other pages with many free blocks void _mi_page_unfull(mi_page_t* page); void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force); // free the page void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq); // abandon the page, to be picked up by another thread... -void _mi_heap_delayed_free(mi_heap_t* heap); +void _mi_heap_delayed_free_all(mi_heap_t* heap); +bool _mi_heap_delayed_free_partial(mi_heap_t* heap); void _mi_heap_collect_retired(mi_heap_t* heap, bool force); void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never); +bool _mi_page_try_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never); size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue_t* append); void _mi_deferred_free(mi_heap_t* heap, bool force); @@ -138,6 +143,7 @@ uint8_t _mi_bin(size_t size); // for stats void _mi_heap_destroy_pages(mi_heap_t* heap); void _mi_heap_collect_abandon(mi_heap_t* heap); void _mi_heap_set_default_direct(mi_heap_t* heap); +bool _mi_heap_memid_is_suitable(mi_heap_t* heap, size_t memid); // "stats.c" void _mi_stats_done(mi_stats_t* stats); @@ -147,12 +153,11 @@ mi_msecs_t _mi_clock_end(mi_msecs_t start); mi_msecs_t _mi_clock_start(void); // "alloc.c" -void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept; // called from `_mi_malloc_generic` +void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept; // called from `_mi_malloc_generic` void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept; void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) mi_attr_noexcept; mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* page, const void* p); bool _mi_free_delayed_block(mi_block_t* block); -void _mi_block_zero_init(const mi_page_t* page, void* p, size_t size); #if MI_DEBUG>1 bool _mi_page_is_valid(mi_page_t* page); @@ -164,8 +169,11 @@ bool _mi_page_is_valid(mi_page_t* page); // ------------------------------------------------------ #if defined(__GNUC__) || defined(__clang__) -#define mi_unlikely(x) __builtin_expect(!!(x),false) -#define mi_likely(x) __builtin_expect(!!(x),true) +#define mi_unlikely(x) (__builtin_expect(!!(x),false)) +#define mi_likely(x) (__builtin_expect(!!(x),true)) +#elif (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define mi_unlikely(x) (x) [[unlikely]] +#define mi_likely(x) (x) [[likely]] #else #define mi_unlikely(x) (x) #define mi_likely(x) (x) @@ -224,6 +232,12 @@ static inline bool _mi_is_power_of_two(uintptr_t x) { return ((x & (x - 1)) == 0); } +// Is a pointer aligned? +static inline bool _mi_is_aligned(void* p, size_t alignment) { + mi_assert_internal(alignment != 0); + return (((uintptr_t)p % alignment) == 0); +} + // Align upwards static inline uintptr_t _mi_align_up(uintptr_t sz, size_t alignment) { mi_assert_internal(alignment != 0); @@ -289,8 +303,8 @@ static inline bool mi_mul_overflow(size_t count, size_t size, size_t* total) { static inline bool mi_mul_overflow(size_t count, size_t size, size_t* total) { #define MI_MUL_NO_OVERFLOW ((size_t)1 << (4*sizeof(size_t))) // sqrt(SIZE_MAX) *total = count * size; - return ((size >= MI_MUL_NO_OVERFLOW || count >= MI_MUL_NO_OVERFLOW) - && size > 0 && (SIZE_MAX / size) < count); + // note: gcc/clang optimize this to directly check the overflow flag + return ((size >= MI_MUL_NO_OVERFLOW || count >= MI_MUL_NO_OVERFLOW) && size > 0 && (SIZE_MAX / size) < count); } #endif @@ -300,8 +314,10 @@ static inline bool mi_count_size_overflow(size_t count, size_t size, size_t* tot *total = size; return false; } - else if (mi_unlikely(mi_mul_overflow(count, size, total))) { + else if mi_unlikely(mi_mul_overflow(count, size, total)) { + #if MI_DEBUG > 0 _mi_error_message(EOVERFLOW, "allocation request is too large (%zu * %zu bytes)\n", count, size); + #endif *total = SIZE_MAX; return true; } @@ -372,7 +388,7 @@ extern mi_decl_thread mi_heap_t* _mi_heap_default; // default heap to allocate static inline mi_heap_t* mi_get_default_heap(void) { #if defined(MI_TLS_SLOT) mi_heap_t* heap = (mi_heap_t*)mi_tls_slot(MI_TLS_SLOT); - if (mi_unlikely(heap == NULL)) { + if mi_unlikely(heap == NULL) { #ifdef __GNUC__ __asm(""); // prevent conditional load of the address of _mi_heap_empty #endif @@ -486,7 +502,7 @@ static inline mi_page_t* _mi_ptr_page(void* p) { static inline size_t mi_page_block_size(const mi_page_t* page) { const size_t bsize = page->xblock_size; mi_assert_internal(bsize > 0); - if (mi_likely(bsize < MI_HUGE_BLOCK_SIZE)) { + if mi_likely(bsize < MI_HUGE_BLOCK_SIZE) { return bsize; } else { @@ -649,30 +665,36 @@ static inline uintptr_t mi_rotr(uintptr_t x, uintptr_t shift) { static inline void* mi_ptr_decode(const void* null, const mi_encoded_t x, const uintptr_t* keys) { void* p = (void*)(mi_rotr(x - keys[0], keys[0]) ^ keys[1]); - return (mi_unlikely(p==null) ? NULL : p); + return (p==null ? NULL : p); } static inline mi_encoded_t mi_ptr_encode(const void* null, const void* p, const uintptr_t* keys) { - uintptr_t x = (uintptr_t)(mi_unlikely(p==NULL) ? null : p); + uintptr_t x = (uintptr_t)(p==NULL ? null : p); return mi_rotl(x ^ keys[1], keys[0]) + keys[0]; } static inline mi_block_t* mi_block_nextx( const void* null, const mi_block_t* block, const uintptr_t* keys ) { + mi_track_mem_defined(block,sizeof(mi_block_t)); + mi_block_t* next; #ifdef MI_ENCODE_FREELIST - return (mi_block_t*)mi_ptr_decode(null, block->next, keys); + next = (mi_block_t*)mi_ptr_decode(null, block->next, keys); #else MI_UNUSED(keys); MI_UNUSED(null); - return (mi_block_t*)block->next; + next = (mi_block_t*)block->next; #endif + mi_track_mem_noaccess(block,sizeof(mi_block_t)); + return next; } static inline void mi_block_set_nextx(const void* null, mi_block_t* block, const mi_block_t* next, const uintptr_t* keys) { + mi_track_mem_undefined(block,sizeof(mi_block_t)); #ifdef MI_ENCODE_FREELIST block->next = mi_ptr_encode(null, next, keys); #else MI_UNUSED(keys); MI_UNUSED(null); block->next = (mi_encoded_t)next; #endif + mi_track_mem_noaccess(block,sizeof(mi_block_t)); } static inline mi_block_t* mi_block_next(const mi_page_t* page, const mi_block_t* block) { @@ -680,7 +702,7 @@ static inline mi_block_t* mi_block_next(const mi_page_t* page, const mi_block_t* mi_block_t* next = mi_block_nextx(page,block,page->keys); // check for free list corruption: is `next` at least in the same page? // TODO: check if `next` is `page->block_size` aligned? - if (mi_unlikely(next!=NULL && !mi_is_in_same_page(block, next))) { + if mi_unlikely(next!=NULL && !mi_is_in_same_page(block, next)) { _mi_error_message(EFAULT, "corrupted free list entry of size %zub at %p: value 0x%zx\n", mi_page_block_size(page), block, (uintptr_t)next); next = NULL; } @@ -779,12 +801,12 @@ size_t _mi_os_numa_node_count_get(void); extern _Atomic(size_t) _mi_numa_node_count; static inline int _mi_os_numa_node(mi_os_tld_t* tld) { - if (mi_likely(mi_atomic_load_relaxed(&_mi_numa_node_count) == 1)) return 0; + if mi_likely(mi_atomic_load_relaxed(&_mi_numa_node_count) == 1) { return 0; } else return _mi_os_numa_node_get(tld); } static inline size_t _mi_os_numa_node_count(void) { const size_t count = mi_atomic_load_relaxed(&_mi_numa_node_count); - if (mi_likely(count>0)) return count; + if mi_likely(count > 0) { return count; } else return _mi_os_numa_node_count_get(); } @@ -1003,7 +1025,7 @@ static inline size_t mi_bsr(uintptr_t x) { // (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017). See also issue #201 and pr #253. // --------------------------------------------------------------------------------- -#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) +#if !MI_TRACK_ENABLED && defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) #include #include extern bool _mi_cpu_has_fsrm; @@ -1012,7 +1034,15 @@ static inline void _mi_memcpy(void* dst, const void* src, size_t n) { __movsb((unsigned char*)dst, (const unsigned char*)src, n); } else { - memcpy(dst, src, n); // todo: use noinline? + memcpy(dst, src, n); + } +} +static inline void _mi_memzero(void* dst, size_t n) { + if (_mi_cpu_has_fsrm) { + __stosb((unsigned char*)dst, 0, n); + } + else { + memset(dst, 0, n); } } #else @@ -1020,6 +1050,9 @@ static inline void _mi_memcpy(void* dst, const void* src, size_t n) { static inline void _mi_memcpy(void* dst, const void* src, size_t n) { memcpy(dst, src, n); } +static inline void _mi_memzero(void* dst, size_t n) { + memset(dst, 0, n); +} #endif @@ -1037,12 +1070,23 @@ static inline void _mi_memcpy_aligned(void* dst, const void* src, size_t n) { const void* asrc = __builtin_assume_aligned(src, MI_INTPTR_SIZE); _mi_memcpy(adst, asrc, n); } + +static inline void _mi_memzero_aligned(void* dst, size_t n) { + mi_assert_internal((uintptr_t)dst % MI_INTPTR_SIZE == 0); + void* adst = __builtin_assume_aligned(dst, MI_INTPTR_SIZE); + _mi_memzero(adst, n); +} #else // Default fallback on `_mi_memcpy` static inline void _mi_memcpy_aligned(void* dst, const void* src, size_t n) { mi_assert_internal(((uintptr_t)dst % MI_INTPTR_SIZE == 0) && ((uintptr_t)src % MI_INTPTR_SIZE == 0)); _mi_memcpy(dst, src, n); } + +static inline void _mi_memzero_aligned(void* dst, size_t n) { + mi_assert_internal((uintptr_t)dst % MI_INTPTR_SIZE == 0); + _mi_memzero(dst, n); +} #endif diff --git a/source/luametatex/source/libraries/mimalloc/include/mimalloc-new-delete.h b/source/luametatex/source/libraries/mimalloc/include/mimalloc-new-delete.h index 2749a0be9..1c12fad2f 100644 --- a/source/luametatex/source/libraries/mimalloc/include/mimalloc-new-delete.h +++ b/source/luametatex/source/libraries/mimalloc/include/mimalloc-new-delete.h @@ -44,8 +44,8 @@ terms of the MIT license. A copy of the license can be found in the file void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast(al)); } void operator delete (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast(al)); }; void operator delete[](void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast(al)); }; - void operator delete (void* p, std::align_val_t al, const std::nothrow_t& tag) noexcept { mi_free_aligned(p, static_cast(al)); } - void operator delete[](void* p, std::align_val_t al, const std::nothrow_t& tag) noexcept { mi_free_aligned(p, static_cast(al)); } + void operator delete (void* p, std::align_val_t al, const std::nothrow_t&) noexcept { mi_free_aligned(p, static_cast(al)); } + void operator delete[](void* p, std::align_val_t al, const std::nothrow_t&) noexcept { mi_free_aligned(p, static_cast(al)); } void* operator new (std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast(al)); } void* operator new[](std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast(al)); } diff --git a/source/luametatex/source/libraries/mimalloc/include/mimalloc-track.h b/source/luametatex/source/libraries/mimalloc/include/mimalloc-track.h new file mode 100644 index 000000000..bb9df4fa3 --- /dev/null +++ b/source/luametatex/source/libraries/mimalloc/include/mimalloc-track.h @@ -0,0 +1,43 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2021, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ +#pragma once +#ifndef MIMALLOC_TRACK_H +#define MIMALLOC_TRACK_H + +// ------------------------------------------------------ +// Track memory ranges with macros for tools like Valgrind +// or other memory checkers. +// ------------------------------------------------------ + +#if MI_VALGRIND + +#define MI_TRACK_ENABLED 1 + +#include +#include + +#define mi_track_malloc(p,size,zero) VALGRIND_MALLOCLIKE_BLOCK(p,size,MI_PADDING_SIZE /*red zone*/,zero) +#define mi_track_resize(p,oldsize,newsize) VALGRIND_RESIZEINPLACE_BLOCK(p,oldsize,newsize,MI_PADDING_SIZE /*red zone*/) +#define mi_track_free(p) VALGRIND_FREELIKE_BLOCK(p,MI_PADDING_SIZE /*red zone*/) +#define mi_track_mem_defined(p,size) VALGRIND_MAKE_MEM_DEFINED(p,size) +#define mi_track_mem_undefined(p,size) VALGRIND_MAKE_MEM_UNDEFINED(p,size) +#define mi_track_mem_noaccess(p,size) VALGRIND_MAKE_MEM_NOACCESS(p,size) + +#else + +#define MI_TRACK_ENABLED 0 + +#define mi_track_malloc(p,size,zero) +#define mi_track_resize(p,oldsize,newsize) +#define mi_track_free(p) +#define mi_track_mem_defined(p,size) +#define mi_track_mem_undefined(p,size) +#define mi_track_mem_noaccess(p,size) + +#endif + +#endif diff --git a/source/luametatex/source/libraries/mimalloc/include/mimalloc-types.h b/source/luametatex/source/libraries/mimalloc/include/mimalloc-types.h index fb75ea464..1387a7200 100644 --- a/source/luametatex/source/libraries/mimalloc/include/mimalloc-types.h +++ b/source/luametatex/source/libraries/mimalloc/include/mimalloc-types.h @@ -29,6 +29,9 @@ terms of the MIT license. A copy of the license can be found in the file // Define NDEBUG in the release version to disable assertions. // #define NDEBUG +// Define MI_VALGRIND to enable valgrind support +// #define MI_VALGRIND 1 + // Define MI_STAT as 1 to maintain statistics; set it to 2 to have detailed statistics (but costs some performance). // #define MI_STAT 1 @@ -56,14 +59,14 @@ terms of the MIT license. A copy of the license can be found in the file // Reserve extra padding at the end of each block to be more resilient against heap block overflows. // The padding can detect byte-precise buffer overflow on free. -#if !defined(MI_PADDING) && (MI_DEBUG>=1) +#if !defined(MI_PADDING) && (MI_DEBUG>=1 || MI_VALGRIND) #define MI_PADDING 1 #endif // Encoded free lists allow detection of corrupted free lists // and can detect buffer overflows, modify after free, and double `free`s. -#if (MI_SECURE>=3 || MI_DEBUG>=1 || MI_PADDING > 0) +#if (MI_SECURE>=3 || MI_DEBUG>=1) #define MI_ENCODE_FREELIST 1 #endif @@ -435,6 +438,7 @@ struct mi_heap_s { mi_page_queue_t pages[MI_BIN_FULL + 1]; // queue of pages for each size class (or "bin") _Atomic(mi_block_t*) thread_delayed_free; mi_threadid_t thread_id; // thread this heap belongs too + mi_arena_id_t arena_id; // arena id if the heap belongs to a specific arena (or 0) uintptr_t cookie; // random cookie to verify pointers (see `_mi_ptr_cookie`) uintptr_t keys[2]; // two random keys used to encode the `thread_delayed_free` list mi_random_ctx_t random; // random number context used for secure allocation diff --git a/source/luametatex/source/libraries/mimalloc/include/mimalloc.h b/source/luametatex/source/libraries/mimalloc/include/mimalloc.h index c752ac247..32eab19ea 100644 --- a/source/luametatex/source/libraries/mimalloc/include/mimalloc.h +++ b/source/luametatex/source/libraries/mimalloc/include/mimalloc.h @@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file #ifndef MIMALLOC_H #define MIMALLOC_H -#define MI_MALLOC_VERSION 206 // major + 2 digits minor +#define MI_MALLOC_VERSION 207 // major + 2 digits minor // ------------------------------------------------------ // Compiler specific attributes @@ -95,6 +95,7 @@ terms of the MIT license. A copy of the license can be found in the file #include // size_t #include // bool +#include // INTPTR_MAX #ifdef __cplusplus extern "C" { @@ -166,7 +167,11 @@ mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, s // Note that `alignment` always follows `size` for consistency with unaligned // allocation, but unfortunately this differs from `posix_memalign` and `aligned_alloc`. // ------------------------------------------------------------------------------------- -#define MI_ALIGNMENT_MAX (1024*1024UL) // maximum supported alignment is 1MiB +#if (INTPTR_MAX > INT32_MAX) +#define MI_ALIGNMENT_MAX (16*1024*1024UL) // maximum supported alignment is 16MiB +#else +#define MI_ALIGNMENT_MAX (1024*1024UL) // maximum supported alignment for 32-bit systems is 1MiB +#endif mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1) mi_attr_alloc_align(2); mi_decl_nodiscard mi_decl_export mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept mi_attr_malloc mi_attr_alloc_size(1); @@ -275,6 +280,17 @@ mi_decl_export bool mi_manage_os_memory(void* start, size_t size, bool is_commit mi_decl_export void mi_debug_show_arenas(void) mi_attr_noexcept; +// Experimental: heaps associated with specific memory arena's +typedef int mi_arena_id_t; +mi_decl_export void* mi_arena_area(mi_arena_id_t arena_id, size_t* size); +mi_decl_export int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_msecs, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; +mi_decl_export int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; +mi_decl_export bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept; + +#if MI_MALLOC_VERSION >= 200 +mi_decl_nodiscard mi_decl_export mi_heap_t* mi_heap_new_in_arena(mi_arena_id_t arena_id); +#endif + // deprecated mi_decl_export int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserved) mi_attr_noexcept; diff --git a/source/luametatex/source/libraries/mimalloc/readme.md b/source/luametatex/source/libraries/mimalloc/readme.md index 6142dbc5e..588630992 100644 --- a/source/luametatex/source/libraries/mimalloc/readme.md +++ b/source/luametatex/source/libraries/mimalloc/readme.md @@ -12,8 +12,8 @@ is a general purpose allocator with excellent [performance](#performance) charac Initially developed by Daan Leijen for the run-time systems of the [Koka](https://koka-lang.github.io) and [Lean](https://github.com/leanprover/lean) languages. -Latest release tag: `v2.0.6` (2022-04-14). -Latest stable tag: `v1.7.6` (2022-02-14). +Latest release tag: `v2.0.7` (2022-11-03). +Latest stable tag: `v1.7.7` (2022-11-03). mimalloc is a drop-in replacement for `malloc` and can be used in other programs without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as: @@ -73,10 +73,13 @@ Enjoy! ### Releases -Note: the `v2.x` version has a new algorithm for managing internal mimalloc pages that tends to use reduce memory usage +Note: the `v2.x` version has a new algorithm for managing internal mimalloc pages that tends to reduce memory usage and fragmentation compared to mimalloc `v1.x` (especially for large workloads). Should otherwise have similar performance (see [below](#performance)); please report if you observe any significant performance regression. +* 2022-11-03, `v1.7.7`, `v2.0.7`: Initial support for [Valgrind](#valgrind) for leak testing and heap block overflow detection. Initial + support for attaching heaps to a speficic memory area (only in v2). Fix `realloc` behavior for zero size blocks, remove restriction to integral multiple of the alignment in `alloc_align`, improved aligned allocation performance, reduced contention with many threads on few processors (thank you @dposluns!), vs2022 support, support `pkg-config`, . + * 2022-04-14, `v1.7.6`, `v2.0.6`: fix fallback path for aligned OS allocation on Windows, improve Windows aligned allocation even when compiling with older SDK's, fix dynamic overriding on macOS Monterey, fix MSVC C++ dynamic overriding, fix warnings under Clang 14, improve performance if many OS threads are created and destroyed, fix statistics for large object @@ -337,6 +340,44 @@ When _mimalloc_ is built using debug mode, various checks are done at runtime to - Double free's, and freeing invalid heap pointers are detected. - Corrupted free-lists and some forms of use-after-free are detected. +## Valgrind + +Generally, we recommend using the standard allocator with the amazing [Valgrind] tool (and +also for other address sanitizers). +However, it is possible to build mimalloc with Valgrind support. This has a small performance +overhead but does allow detecting memory leaks and byte-precise buffer overflows directly on final +executables. To build with valgrind support, use the `MI_VALGRIND=ON` cmake option: + +``` +> cmake ../.. -DMI_VALGRIND=ON +``` + +This can also be combined with secure mode or debug mode. +You can then run your programs directly under valgrind: + +``` +> valgrind +``` + +If you rely on overriding `malloc`/`free` by mimalloc (instead of using the `mi_malloc`/`mi_free` API directly), +you also need to tell `valgrind` to not intercept those calls itself, and use: + +``` +> MIMALLOC_SHOW_STATS=1 valgrind --soname-synonyms=somalloc=*mimalloc* -- +``` + +By setting the `MIMALLOC_SHOW_STATS` environment variable you can check that mimalloc is indeed +used and not the standard allocator. Even though the [Valgrind option][valgrind-soname] +is called `--soname-synonyms`, this also +works when overriding with a static library or object file. Unfortunately, it is not possible to +dynamically override mimalloc using `LD_PRELOAD` together with `valgrind`. +See also the `test/test-wrong.c` file to test with `valgrind`. + +Valgrind support is in its initial development -- please report any issues. + +[Valgrind]: https://valgrind.org/ +[valgrind-soname]: https://valgrind.org/docs/manual/manual-core.html#opt.soname-synonyms + # Overriding Standard Malloc diff --git a/source/luametatex/source/libraries/mimalloc/src/alloc-aligned.c b/source/luametatex/source/libraries/mimalloc/src/alloc-aligned.c index fce0fd749..9614aa092 100644 --- a/source/luametatex/source/libraries/mimalloc/src/alloc-aligned.c +++ b/source/luametatex/source/libraries/mimalloc/src/alloc-aligned.c @@ -31,7 +31,8 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_fallback(mi_heap_t* } // otherwise over-allocate - void* p = _mi_heap_malloc_zero(heap, size + alignment - 1, zero); + const size_t oversize = size + alignment - 1; + void* p = _mi_heap_malloc_zero(heap, oversize, zero); if (p == NULL) return NULL; // .. and align within the allocation @@ -41,6 +42,16 @@ static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_fallback(mi_heap_t* if (aligned_p != p) mi_page_set_has_aligned(_mi_ptr_page(p), true); mi_assert_internal(((uintptr_t)aligned_p + offset) % alignment == 0); mi_assert_internal(p == _mi_page_ptr_unalign(_mi_ptr_segment(aligned_p), _mi_ptr_page(aligned_p), aligned_p)); + + #if MI_TRACK_ENABLED + if (p != aligned_p) { + mi_track_free(p); + mi_track_malloc(aligned_p,size,zero); + } + else { + mi_track_resize(aligned_p,oversize,size); + } + #endif return aligned_p; } @@ -49,19 +60,19 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t { // note: we don't require `size > offset`, we just guarantee that the address at offset is aligned regardless of the allocated size. mi_assert(alignment > 0); - if (mi_unlikely(alignment==0 || !_mi_is_power_of_two(alignment))) { // require power-of-two (see ) + if mi_unlikely(alignment==0 || !_mi_is_power_of_two(alignment)) { // require power-of-two (see ) #if MI_DEBUG > 0 _mi_error_message(EOVERFLOW, "aligned allocation requires the alignment to be a power-of-two (size %zu, alignment %zu)\n", size, alignment); #endif return NULL; } - if (mi_unlikely(alignment > MI_ALIGNMENT_MAX)) { // we cannot align at a boundary larger than this (or otherwise we cannot find segment headers) + if mi_unlikely(alignment > MI_ALIGNMENT_MAX) { // we cannot align at a boundary larger than this (or otherwise we cannot find segment headers) #if MI_DEBUG > 0 _mi_error_message(EOVERFLOW, "aligned allocation has a maximum alignment of %zu (size %zu, alignment %zu)\n", MI_ALIGNMENT_MAX, size, alignment); #endif return NULL; } - if (mi_unlikely(size > PTRDIFF_MAX)) { // we don't allocate more than PTRDIFF_MAX (see ) + if mi_unlikely(size > PTRDIFF_MAX) { // we don't allocate more than PTRDIFF_MAX (see ) #if MI_DEBUG > 0 _mi_error_message(EOVERFLOW, "aligned allocation request is too large (size %zu, alignment %zu)\n", size, alignment); #endif @@ -71,18 +82,18 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t const size_t padsize = size + MI_PADDING_SIZE; // note: cannot overflow due to earlier size > PTRDIFF_MAX check // try first if there happens to be a small block available with just the right alignment - if (mi_likely(padsize <= MI_SMALL_SIZE_MAX)) { + if mi_likely(padsize <= MI_SMALL_SIZE_MAX) { mi_page_t* page = _mi_heap_get_free_small_page(heap, padsize); const bool is_aligned = (((uintptr_t)page->free+offset) & align_mask)==0; - if (mi_likely(page->free != NULL && is_aligned)) + if mi_likely(page->free != NULL && is_aligned) { #if MI_STAT>1 mi_heap_stat_increase(heap, malloc, size); #endif - void* p = _mi_page_malloc(heap, page, padsize); // TODO: inline _mi_page_malloc + void* p = _mi_page_malloc(heap, page, padsize, zero); // TODO: inline _mi_page_malloc mi_assert_internal(p != NULL); mi_assert_internal(((uintptr_t)p + offset) % alignment == 0); - if (zero) { _mi_block_zero_init(page, p, size); } + mi_track_malloc(p,size,zero); return p; } } @@ -95,19 +106,19 @@ static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t // Optimized mi_heap_malloc_aligned / mi_malloc_aligned // ------------------------------------------------------ -mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, false); } -mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { #if !MI_PADDING // without padding, any small sized allocation is naturally aligned (see also `_mi_segment_page_start`) if (!_mi_is_power_of_two(alignment)) return NULL; - if (mi_likely(_mi_is_power_of_two(size) && size >= alignment && size <= MI_SMALL_SIZE_MAX)) + if mi_likely(_mi_is_power_of_two(size) && size >= alignment && size <= MI_SMALL_SIZE_MAX) #else // with padding, we can only guarantee this for fixed alignments - if (mi_likely((alignment == sizeof(void*) || (alignment == MI_MAX_ALIGN_SIZE && size > (MI_MAX_ALIGN_SIZE/2))) - && size <= MI_SMALL_SIZE_MAX)) + if mi_likely((alignment == sizeof(void*) || (alignment == MI_MAX_ALIGN_SIZE && size > (MI_MAX_ALIGN_SIZE/2))) + && size <= MI_SMALL_SIZE_MAX) #endif { // fast path for common alignment and size @@ -122,45 +133,45 @@ mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size // Aligned Allocation // ------------------------------------------------------ -mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, true); } -mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept { return mi_heap_zalloc_aligned_at(heap, size, alignment, 0); } -mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; return mi_heap_zalloc_aligned_at(heap, total, alignment, offset); } -mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept { return mi_heap_calloc_aligned_at(heap,count,size,alignment,0); } -mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_malloc_aligned_at(mi_get_default_heap(), size, alignment, offset); } -mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { return mi_heap_malloc_aligned(mi_get_default_heap(), size, alignment); } -mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_zalloc_aligned_at(mi_get_default_heap(), size, alignment, offset); } -mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept { return mi_heap_zalloc_aligned(mi_get_default_heap(), size, alignment); } -mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_calloc_aligned_at(mi_get_default_heap(), count, size, alignment, offset); } -mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept { return mi_heap_calloc_aligned(mi_get_default_heap(), count, size, alignment); } @@ -207,55 +218,55 @@ static void* mi_heap_realloc_zero_aligned(mi_heap_t* heap, void* p, size_t newsi return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,zero); } -void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,false); } -void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { return mi_heap_realloc_zero_aligned(heap,p,newsize,alignment,false); } -void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_realloc_zero_aligned_at(heap, p, newsize, alignment, offset, true); } -void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept { return mi_heap_realloc_zero_aligned(heap, p, newsize, alignment, true); } -void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(newcount, size, &total)) return NULL; return mi_heap_rezalloc_aligned_at(heap, p, total, alignment, offset); } -void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(newcount, size, &total)) return NULL; return mi_heap_rezalloc_aligned(heap, p, total, alignment); } -void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_realloc_aligned_at(mi_get_default_heap(), p, newsize, alignment, offset); } -void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { return mi_heap_realloc_aligned(mi_get_default_heap(), p, newsize, alignment); } -void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_rezalloc_aligned_at(mi_get_default_heap(), p, newsize, alignment, offset); } -void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept { return mi_heap_rezalloc_aligned(mi_get_default_heap(), p, newsize, alignment); } -void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { +mi_decl_nodiscard void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { return mi_heap_recalloc_aligned_at(mi_get_default_heap(), p, newcount, size, alignment, offset); } -void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { return mi_heap_recalloc_aligned(mi_get_default_heap(), p, newcount, size, alignment); } diff --git a/source/luametatex/source/libraries/mimalloc/src/alloc-override-osx.c b/source/luametatex/source/libraries/mimalloc/src/alloc-override-osx.c index 41d0a386e..ba2313a2a 100644 --- a/source/luametatex/source/libraries/mimalloc/src/alloc-override-osx.c +++ b/source/luametatex/source/libraries/mimalloc/src/alloc-override-osx.c @@ -254,7 +254,7 @@ static malloc_zone_t mi_malloc_zone = { static inline malloc_zone_t* mi_get_default_zone(void) { static bool init; - if (mi_unlikely(!init)) { + if mi_unlikely(!init) { init = true; malloc_zone_register(&mi_malloc_zone); // by calling register we avoid a zone error on free (see ) } diff --git a/source/luametatex/source/libraries/mimalloc/src/alloc-override.c b/source/luametatex/source/libraries/mimalloc/src/alloc-override.c index e29cb4b23..6b9845d39 100644 --- a/source/luametatex/source/libraries/mimalloc/src/alloc-override.c +++ b/source/luametatex/source/libraries/mimalloc/src/alloc-override.c @@ -29,7 +29,7 @@ typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t; // Override system malloc // ------------------------------------------------------ -#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) && !defined(MI_VALGRIND) +#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) && !MI_TRACK_ENABLED // gcc, clang: use aliasing to alias the exported function to one of our `mi_` functions #if (defined(__GNUC__) && __GNUC__ >= 9) #pragma GCC diagnostic ignored "-Wattributes" // or we get warnings that nodiscard is ignored on a forward @@ -123,10 +123,10 @@ typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t; // we just override new/delete which does work in a static library. #else // On all other systems forward to our API - void* malloc(size_t size) MI_FORWARD1(mi_malloc, size) - void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n) - void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize) - void free(void* p) MI_FORWARD0(mi_free, p) + mi_decl_export void* malloc(size_t size) MI_FORWARD1(mi_malloc, size) + mi_decl_export void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n) + mi_decl_export void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize) + mi_decl_export void free(void* p) MI_FORWARD0(mi_free, p) #endif #if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) diff --git a/source/luametatex/source/libraries/mimalloc/src/alloc-posix.c b/source/luametatex/source/libraries/mimalloc/src/alloc-posix.c index 176e7ec30..214a97345 100644 --- a/source/luametatex/source/libraries/mimalloc/src/alloc-posix.c +++ b/source/luametatex/source/libraries/mimalloc/src/alloc-posix.c @@ -83,13 +83,16 @@ mi_decl_nodiscard mi_decl_restrict void* mi_pvalloc(size_t size) mi_attr_noexcep } mi_decl_nodiscard mi_decl_restrict void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept { - if (mi_unlikely((size&(alignment-1)) != 0)) { // C11 requires alignment>0 && integral multiple, see + // C11 requires the size to be an integral multiple of the alignment, see . + // unfortunately, it turns out quite some programs pass a size that is not an integral multiple so skip this check.. + /* if mi_unlikely((size & (alignment - 1)) != 0) { // C11 requires alignment>0 && integral multiple, see #if MI_DEBUG > 0 _mi_error_message(EOVERFLOW, "(mi_)aligned_alloc requires the size to be an integral multiple of the alignment (size %zu, alignment %zu)\n", size, alignment); #endif return NULL; } - // C11 also requires alignment to be a power-of-two which is checked in mi_malloc_aligned + */ + // C11 also requires alignment to be a power-of-two (and > 0) which is checked in mi_malloc_aligned void* p = mi_malloc_aligned(size, alignment); mi_assert_internal(((uintptr_t)p % alignment) == 0); return p; @@ -109,7 +112,7 @@ mi_decl_nodiscard int mi_reallocarr( void* p, size_t count, size_t size ) mi_att } void** op = (void**)p; void* newp = mi_reallocarray(*op, count, size); - if (mi_unlikely(newp == NULL)) return errno; + if mi_unlikely(newp == NULL) { return errno; } *op = newp; return 0; } diff --git a/source/luametatex/source/libraries/mimalloc/src/alloc.c b/source/luametatex/source/libraries/mimalloc/src/alloc.c index 1a36b5da8..348218246 100644 --- a/source/luametatex/source/libraries/mimalloc/src/alloc.c +++ b/source/luametatex/source/libraries/mimalloc/src/alloc.c @@ -12,6 +12,7 @@ terms of the MIT license. A copy of the license can be found in the file #include "mimalloc-internal.h" #include "mimalloc-atomic.h" + #include // memset, strlen #include // malloc, exit @@ -25,11 +26,11 @@ terms of the MIT license. A copy of the license can be found in the file // Fast allocation in a page: just pop from the free list. // Fall back to generic allocation only if the list is empty. -extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size) mi_attr_noexcept { +extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept { mi_assert_internal(page->xblock_size==0||mi_page_block_size(page) >= size); mi_block_t* const block = page->free; - if (mi_unlikely(block == NULL)) { - return _mi_malloc_generic(heap, size); + if mi_unlikely(block == NULL) { + return _mi_malloc_generic(heap, size, zero); } mi_assert_internal(block != NULL && _mi_ptr_page(block) == page); // pop from the free list @@ -37,10 +38,22 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz page->free = mi_block_next(page, block); mi_assert_internal(page->free == NULL || _mi_ptr_page(page->free) == page); -#if (MI_DEBUG>0) - if (!page->is_zero) { memset(block, MI_DEBUG_UNINIT, size); } + // allow use of the block internally + // note: when tracking we need to avoid ever touching the MI_PADDING since + // that is tracked by valgrind etc. as non-accessible (through the red-zone, see `mimalloc-track.h`) + mi_track_mem_undefined(block, mi_page_usable_block_size(page)); + + // zero the block? note: we need to zero the full block size (issue #63) + if mi_unlikely(zero) { + mi_assert_internal(page->xblock_size != 0); // do not call with zero'ing for huge blocks (see _mi_malloc_generic) + const size_t zsize = (page->is_zero ? sizeof(block->next) + MI_PADDING_SIZE : page->xblock_size); + _mi_memzero_aligned(block, zsize - MI_PADDING_SIZE); + } + +#if (MI_DEBUG>0) && !MI_TRACK_ENABLED + if (!page->is_zero && !zero) { memset(block, MI_DEBUG_UNINIT, mi_page_usable_block_size(page)); } #elif (MI_SECURE!=0) - block->next = 0; // don't leak internal data + if (!zero) { block->next = 0; } // don't leak internal data #endif #if (MI_STAT>0) @@ -55,10 +68,13 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz } #endif -#if (MI_PADDING > 0) && defined(MI_ENCODE_FREELIST) +#if (MI_PADDING > 0) && defined(MI_ENCODE_FREELIST) && !MI_TRACK_ENABLED mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + mi_page_usable_block_size(page)); ptrdiff_t delta = ((uint8_t*)padding - (uint8_t*)block - (size - MI_PADDING_SIZE)); + #if (MI_DEBUG>1) mi_assert_internal(delta >= 0 && mi_page_usable_block_size(page) >= (size - MI_PADDING_SIZE + delta)); + mi_track_mem_defined(padding,sizeof(mi_padding_t)); // note: re-enable since mi_page_usable_block_size may set noaccess + #endif padding->canary = (uint32_t)(mi_ptr_encode(page,block,page->keys)); padding->delta = (uint32_t)(delta); uint8_t* fill = (uint8_t*)padding - delta; @@ -69,8 +85,7 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz return block; } -// allocate a small block -extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept { +static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { mi_assert(heap!=NULL); mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local mi_assert(size <= MI_SMALL_SIZE_MAX); @@ -80,7 +95,7 @@ extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_ } #endif mi_page_t* page = _mi_heap_get_free_small_page(heap,size + MI_PADDING_SIZE); - void* p = _mi_page_malloc(heap, page, size + MI_PADDING_SIZE); + void* p = _mi_page_malloc(heap, page, size + MI_PADDING_SIZE, zero); mi_assert_internal(p==NULL || mi_usable_size(p) >= size); #if MI_STAT>1 if (p != NULL) { @@ -88,22 +103,28 @@ extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_ mi_heap_stat_increase(heap, malloc, mi_usable_size(p)); } #endif + mi_track_malloc(p,size,zero); return p; } -extern inline mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept { +// allocate a small block +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept { + return mi_heap_malloc_small_zero(heap, size, false); +} + +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept { return mi_heap_malloc_small(mi_get_default_heap(), size); } // The main allocation function -extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { - if (mi_likely(size <= MI_SMALL_SIZE_MAX)) { - return mi_heap_malloc_small(heap, size); +extern inline void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { + if mi_likely(size <= MI_SMALL_SIZE_MAX) { + return mi_heap_malloc_small_zero(heap, size, zero); } else { mi_assert(heap!=NULL); mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local - void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE); // note: size can overflow but it is detected in malloc_generic + void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero); // note: size can overflow but it is detected in malloc_generic mi_assert_internal(p == NULL || mi_usable_size(p) >= size); #if MI_STAT>1 if (p != NULL) { @@ -111,55 +132,29 @@ extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size mi_heap_stat_increase(heap, malloc, mi_usable_size(p)); } #endif + mi_track_malloc(p,size,zero); return p; } } -extern inline mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept { - return mi_heap_malloc(mi_get_default_heap(), size); +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { + return _mi_heap_malloc_zero(heap, size, false); } - -void _mi_block_zero_init(const mi_page_t* page, void* p, size_t size) { - // note: we need to initialize the whole usable block size to zero, not just the requested size, - // or the recalloc/rezalloc functions cannot safely expand in place (see issue #63) - MI_UNUSED(size); - mi_assert_internal(p != NULL); - mi_assert_internal(mi_usable_size(p) >= size); // size can be zero - mi_assert_internal(_mi_ptr_page(p)==page); - if (page->is_zero && size > sizeof(mi_block_t)) { - // already zero initialized memory - ((mi_block_t*)p)->next = 0; // clear the free list pointer - mi_assert_expensive(mi_mem_is_zero(p, mi_usable_size(p))); - } - else { - // otherwise memset - memset(p, 0, mi_usable_size(p)); - } +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept { + return mi_heap_malloc(mi_get_default_heap(), size); } // zero initialized small block -mi_decl_restrict void* mi_zalloc_small(size_t size) mi_attr_noexcept { - void* p = mi_malloc_small(size); - if (p != NULL) { - _mi_block_zero_init(_mi_ptr_page(p), p, size); // todo: can we avoid getting the page again? - } - return p; -} - -void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { - void* p = mi_heap_malloc(heap,size); - if (zero && p != NULL) { - _mi_block_zero_init(_mi_ptr_page(p),p,size); // todo: can we avoid getting the page again? - } - return p; +mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_small(size_t size) mi_attr_noexcept { + return mi_heap_malloc_small_zero(mi_get_default_heap(), size, true); } -extern inline mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept { return _mi_heap_malloc_zero(heap, size, true); } -mi_decl_restrict void* mi_zalloc(size_t size) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_zalloc(size_t size) mi_attr_noexcept { return mi_heap_zalloc(mi_get_default_heap(),size); } @@ -192,16 +187,19 @@ static mi_decl_noinline bool mi_check_is_double_freex(const mi_page_t* page, con return false; } +#define mi_track_page(page,access) { size_t psize; void* pstart = _mi_page_start(_mi_page_segment(page),page,&psize); mi_track_mem_##access( pstart, psize); } + static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) { + bool is_double_free = false; mi_block_t* n = mi_block_nextx(page, block, page->keys); // pretend it is freed, and get the decoded first field if (((uintptr_t)n & (MI_INTPTR_SIZE-1))==0 && // quick check: aligned pointer? (n==NULL || mi_is_in_same_page(block, n))) // quick check: in same page or NULL? { // Suspicous: decoded value a in block is in the same page (or NULL) -- maybe a double free? // (continue in separate function to improve code generation) - return mi_check_is_double_freex(page, block); + is_double_free = mi_check_is_double_freex(page, block); } - return false; + return is_double_free; } #else static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) { @@ -215,12 +213,19 @@ static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block // Check for heap block overflow by setting up padding at the end of the block // --------------------------------------------------------------------------- -#if (MI_PADDING>0) && defined(MI_ENCODE_FREELIST) +#if (MI_PADDING>0) && defined(MI_ENCODE_FREELIST) && !MI_TRACK_ENABLED static bool mi_page_decode_padding(const mi_page_t* page, const mi_block_t* block, size_t* delta, size_t* bsize) { *bsize = mi_page_usable_block_size(page); const mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + *bsize); + mi_track_mem_defined(padding,sizeof(mi_padding_t)); *delta = padding->delta; - return ((uint32_t)mi_ptr_encode(page,block,page->keys) == padding->canary && *delta <= *bsize); + uint32_t canary = padding->canary; + uintptr_t keys[2]; + keys[0] = page->keys[0]; + keys[1] = page->keys[1]; + bool ok = ((uint32_t)mi_ptr_encode(page,block,keys) == canary && *delta <= *bsize); + mi_track_mem_noaccess(padding,sizeof(mi_padding_t)); + return ok; } // Return the exact usable size of a block. @@ -242,13 +247,16 @@ static bool mi_verify_padding(const mi_page_t* page, const mi_block_t* block, si *size = bsize - delta; uint8_t* fill = (uint8_t*)block + bsize - delta; const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // check at most the first N padding bytes + mi_track_mem_defined(fill,maxpad); for (size_t i = 0; i < maxpad; i++) { if (fill[i] != MI_DEBUG_PADDING) { *wrong = bsize - delta + i; - return false; + ok = false; + break; } } - return true; + mi_track_mem_noaccess(fill,maxpad); + return ok; } static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) { @@ -347,14 +355,14 @@ static void mi_stat_huge_free(const mi_page_t* page) { // Free // ------------------------------------------------------ -// multi-threaded free +// multi-threaded free (or free in huge block) static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* block) { // The padding check may access the non-thread-owned page for the key values. // that is safe as these are constant and the page won't be freed (as the block is not freed yet). mi_check_padding(page, block); mi_padding_shrink(page, block, sizeof(mi_block_t)); // for small size, ensure we can fit the delayed thread pointers without triggering overflow detection - #if (MI_DEBUG!=0) + #if (MI_DEBUG!=0) && !MI_TRACK_ENABLED // note: when tracking, cannot use mi_usable_size with multi-threading memset(block, MI_DEBUG_FREED, mi_usable_size(block)); #endif @@ -372,7 +380,7 @@ static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* bloc mi_thread_free_t tfree = mi_atomic_load_relaxed(&page->xthread_free); do { use_delayed = (mi_tf_delayed(tfree) == MI_USE_DELAYED_FREE); - if (mi_unlikely(use_delayed)) { + if mi_unlikely(use_delayed) { // unlikely: this only happens on the first concurrent free in a page that is in the full list tfreex = mi_tf_set_delayed(tfree,MI_DELAYED_FREEING); } @@ -383,7 +391,7 @@ static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* bloc } } while (!mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex)); - if (mi_unlikely(use_delayed)) { + if mi_unlikely(use_delayed) { // racy read on `heap`, but ok because MI_DELAYED_FREEING is set (see `mi_heap_delete` and `mi_heap_collect_abandon`) mi_heap_t* const heap = (mi_heap_t*)(mi_atomic_load_acquire(&page->xheap)); //mi_page_heap(page); mi_assert_internal(heap != NULL); @@ -409,20 +417,21 @@ static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* bloc static inline void _mi_free_block(mi_page_t* page, bool local, mi_block_t* block) { // and push it on the free list - if (mi_likely(local)) { + //const size_t bsize = mi_page_block_size(page); + if mi_likely(local) { // owning thread can free a block directly - if (mi_unlikely(mi_check_is_double_free(page, block))) return; + if mi_unlikely(mi_check_is_double_free(page, block)) return; mi_check_padding(page, block); - #if (MI_DEBUG!=0) + #if (MI_DEBUG!=0) && !MI_TRACK_ENABLED memset(block, MI_DEBUG_FREED, mi_page_block_size(page)); #endif mi_block_set_next(page, block, page->local_free); page->local_free = block; page->used--; - if (mi_unlikely(mi_page_all_free(page))) { + if mi_unlikely(mi_page_all_free(page)) { _mi_page_retire(page); } - else if (mi_unlikely(mi_page_is_in_full(page))) { + else if mi_unlikely(mi_page_is_in_full(page)) { _mi_page_unfull(page); } } @@ -444,7 +453,8 @@ mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* p static void mi_decl_noinline mi_free_generic(const mi_segment_t* segment, bool local, void* p) mi_attr_noexcept { mi_page_t* const page = _mi_segment_page_of(segment, p); mi_block_t* const block = (mi_page_has_aligned(page) ? _mi_page_ptr_unalign(segment, page, p) : (mi_block_t*)p); - mi_stat_free(page, block); + mi_stat_free(page, block); // stat_free may access the padding + mi_track_free(p); _mi_free_block(page, local, block); } @@ -455,26 +465,26 @@ static inline mi_segment_t* mi_checked_ptr_segment(const void* p, const char* ms { MI_UNUSED(msg); #if (MI_DEBUG>0) - if (mi_unlikely(((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0)) { + if mi_unlikely(((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0) { _mi_error_message(EINVAL, "%s: invalid (unaligned) pointer: %p\n", msg, p); return NULL; } #endif mi_segment_t* const segment = _mi_ptr_segment(p); - if (mi_unlikely(segment == NULL)) return NULL; // checks also for (p==NULL) + if mi_unlikely(segment == NULL) return NULL; // checks also for (p==NULL) #if (MI_DEBUG>0) - if (mi_unlikely(!mi_is_in_heap_region(p))) { + if mi_unlikely(!mi_is_in_heap_region(p)) { _mi_warning_message("%s: pointer might not point to a valid heap region: %p\n" "(this may still be a valid very large allocation (over 64MiB))\n", msg, p); - if (mi_likely(_mi_ptr_cookie(segment) == segment->cookie)) { + if mi_likely(_mi_ptr_cookie(segment) == segment->cookie) { _mi_warning_message("(yes, the previous pointer %p was valid after all)\n", p); } } #endif #if (MI_DEBUG>0 || MI_SECURE>=4) - if (mi_unlikely(_mi_ptr_cookie(segment) != segment->cookie)) { + if mi_unlikely(_mi_ptr_cookie(segment) != segment->cookie) { _mi_error_message(EINVAL, "%s: pointer does not point to a valid heap space: %p\n", msg, p); return NULL; } @@ -486,23 +496,24 @@ static inline mi_segment_t* mi_checked_ptr_segment(const void* p, const char* ms void mi_free(void* p) mi_attr_noexcept { mi_segment_t* const segment = mi_checked_ptr_segment(p,"mi_free"); - if (mi_unlikely(segment == NULL)) return; + if mi_unlikely(segment == NULL) return; mi_threadid_t tid = _mi_thread_id(); mi_page_t* const page = _mi_segment_page_of(segment, p); - if (mi_likely(tid == mi_atomic_load_relaxed(&segment->thread_id) && page->flags.full_aligned == 0)) { // the thread id matches and it is not a full page, nor has aligned blocks + if mi_likely(tid == mi_atomic_load_relaxed(&segment->thread_id) && page->flags.full_aligned == 0) { // the thread id matches and it is not a full page, nor has aligned blocks // local, and not full or aligned mi_block_t* block = (mi_block_t*)(p); - if (mi_unlikely(mi_check_is_double_free(page,block))) return; + if mi_unlikely(mi_check_is_double_free(page,block)) return; mi_check_padding(page, block); mi_stat_free(page, block); - #if (MI_DEBUG!=0) + #if (MI_DEBUG!=0) && !MI_TRACK_ENABLED memset(block, MI_DEBUG_FREED, mi_page_block_size(page)); #endif + mi_track_free(p); mi_block_set_next(page, block, page->local_free); page->local_free = block; - if (mi_unlikely(--page->used == 0)) { // using this expression generates better code than: page->used--; if (mi_page_all_free(page)) + if mi_unlikely(--page->used == 0) { // using this expression generates better code than: page->used--; if (mi_page_all_free(page)) _mi_page_retire(page); } } @@ -513,6 +524,7 @@ void mi_free(void* p) mi_attr_noexcept } } +// return true if successful bool _mi_free_delayed_block(mi_block_t* block) { // get segment and page const mi_segment_t* const segment = _mi_ptr_segment(block); @@ -525,7 +537,9 @@ bool _mi_free_delayed_block(mi_block_t* block) { // some blocks may end up in the page `thread_free` list with no blocks in the // heap `thread_delayed_free` list which may cause the page to be never freed! // (it would only be freed if we happen to scan it in `mi_page_queue_find_free_ex`) - _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, false /* dont overwrite never delayed */); + if (!_mi_page_try_use_delayed_free(page, MI_USE_DELAYED_FREE, false /* dont overwrite never delayed */)) { + return false; + } // collect all other non-local frees to ensure up-to-date `used` count _mi_page_free_collect(page, false); @@ -548,7 +562,7 @@ static inline size_t _mi_usable_size(const void* p, const char* msg) mi_attr_noe const mi_segment_t* const segment = mi_checked_ptr_segment(p, msg); if (segment==NULL) return 0; // also returns 0 if `p == NULL` const mi_page_t* const page = _mi_segment_page_of(segment, p); - if (mi_likely(!mi_page_has_aligned(page))) { + if mi_likely(!mi_page_has_aligned(page)) { const mi_block_t* block = (const mi_block_t*)p; return mi_page_usable_size_of(page, block); } @@ -558,7 +572,7 @@ static inline size_t _mi_usable_size(const void* p, const char* msg) mi_attr_noe } } -size_t mi_usable_size(const void* p) mi_attr_noexcept { +mi_decl_nodiscard size_t mi_usable_size(const void* p) mi_attr_noexcept { return _mi_usable_size(p, "mi_usable_size"); } @@ -570,6 +584,7 @@ size_t mi_usable_size(const void* p) mi_attr_noexcept { #ifdef __cplusplus void* _mi_externs[] = { (void*)&_mi_page_malloc, + (void*)&_mi_heap_malloc_zero, (void*)&mi_malloc, (void*)&mi_malloc_small, (void*)&mi_zalloc_small, @@ -602,24 +617,24 @@ void mi_free_aligned(void* p, size_t alignment) mi_attr_noexcept { mi_free(p); } -extern inline mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count,size,&total)) return NULL; return mi_heap_zalloc(heap,total); } -mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi_attr_noexcept { return mi_heap_calloc(mi_get_default_heap(),count,size); } // Uninitialized `calloc` -extern mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard extern mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; return mi_heap_malloc(heap, total); } -mi_decl_restrict void* mi_mallocn(size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_mallocn(size_t count, size_t size) mi_attr_noexcept { return mi_heap_mallocn(mi_get_default_heap(),count,size); } @@ -638,31 +653,40 @@ void* mi_expand(void* p, size_t newsize) mi_attr_noexcept { } void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) mi_attr_noexcept { - const size_t size = _mi_usable_size(p,"mi_realloc"); // also works if p == NULL - if (mi_unlikely(newsize <= size && newsize >= (size / 2))) { + // if p == NULL then behave as malloc. + // else if size == 0 then reallocate to a zero-sized block (and don't return NULL, just as mi_malloc(0)). + // (this means that returning NULL always indicates an error, and `p` will not have been freed in that case.) + const size_t size = _mi_usable_size(p,"mi_realloc"); // also works if p == NULL (with size 0) + if mi_unlikely(newsize <= size && newsize >= (size / 2) && newsize > 0) { // note: newsize must be > 0 or otherwise we return NULL for realloc(NULL,0) // todo: adjust potential padding to reflect the new size? + mi_track_free(p); + mi_track_malloc(p,newsize,true); return p; // reallocation still fits and not more than 50% waste } void* newp = mi_heap_malloc(heap,newsize); - if (mi_likely(newp != NULL)) { + if mi_likely(newp != NULL) { if (zero && newsize > size) { // also set last word in the previous allocation to zero to ensure any padding is zero-initialized const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0); memset((uint8_t*)newp + start, 0, newsize - start); } - if (mi_likely(p != NULL)) { - _mi_memcpy_aligned(newp, p, (newsize > size ? size : newsize)); + if mi_likely(p != NULL) { + if mi_likely(_mi_is_aligned(p, sizeof(uintptr_t))) { // a client may pass in an arbitrary pointer `p`.. + const size_t copysize = (newsize > size ? size : newsize); + mi_track_mem_defined(p,copysize); // _mi_useable_size may be too large for byte precise memory tracking.. + _mi_memcpy_aligned(newp, p, copysize); + } mi_free(p); // only free the original pointer if successful } } return newp; } -void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { return _mi_heap_realloc_zero(heap, p, newsize, false); } -void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; return mi_heap_realloc(heap, p, total); @@ -670,41 +694,41 @@ void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_a // Reallocate but free `p` on errors -void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { void* newp = mi_heap_realloc(heap, p, newsize); if (newp==NULL && p!=NULL) mi_free(p); return newp; } -void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept { return _mi_heap_realloc_zero(heap, p, newsize, true); } -void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept { size_t total; if (mi_count_size_overflow(count, size, &total)) return NULL; return mi_heap_rezalloc(heap, p, total); } -void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept { +mi_decl_nodiscard void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept { return mi_heap_realloc(mi_get_default_heap(),p,newsize); } -void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept { return mi_heap_reallocn(mi_get_default_heap(),p,count,size); } // Reallocate but free `p` on errors -void* mi_reallocf(void* p, size_t newsize) mi_attr_noexcept { +mi_decl_nodiscard void* mi_reallocf(void* p, size_t newsize) mi_attr_noexcept { return mi_heap_reallocf(mi_get_default_heap(),p,newsize); } -void* mi_rezalloc(void* p, size_t newsize) mi_attr_noexcept { +mi_decl_nodiscard void* mi_rezalloc(void* p, size_t newsize) mi_attr_noexcept { return mi_heap_rezalloc(mi_get_default_heap(), p, newsize); } -void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept { +mi_decl_nodiscard void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept { return mi_heap_recalloc(mi_get_default_heap(), p, count, size); } @@ -715,7 +739,7 @@ void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept { // ------------------------------------------------------ // `strdup` using mi_malloc -mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept { if (s == NULL) return NULL; size_t n = strlen(s); char* t = (char*)mi_heap_malloc(heap,n+1); @@ -723,12 +747,12 @@ mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_no return t; } -mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept { return mi_heap_strdup(mi_get_default_heap(), s); } // `strndup` using mi_malloc -mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept { if (s == NULL) return NULL; const char* end = (const char*)memchr(s, 0, n); // find end of string in the first `n` characters (returns NULL if not found) const size_t m = (end != NULL ? (size_t)(end - s) : n); // `m` is the minimum of `n` or the end-of-string @@ -740,7 +764,7 @@ mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) return t; } -mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept { return mi_heap_strndup(mi_get_default_heap(),s,n); } @@ -751,7 +775,7 @@ mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept { #define PATH_MAX MAX_PATH #endif #include -mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { // todo: use GetFullPathNameW to allow longer file names char buf[PATH_MAX]; DWORD res = GetFullPathNameA(fname, PATH_MAX, (resolved_name == NULL ? buf : resolved_name), NULL); @@ -797,7 +821,7 @@ char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) } #endif -mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept { return mi_heap_realpath(mi_get_default_heap(),fname,resolved_name); } #endif @@ -835,8 +859,8 @@ static bool mi_try_new_handler(bool nothrow) { #else typedef void (*std_new_handler_t)(void); -#if (defined(__GNUC__) || defined(__clang__)) -std_new_handler_t __attribute((weak)) _ZSt15get_new_handlerv(void) { +#if (defined(__GNUC__) || (defined(__clang__) && !defined(_MSC_VER))) // exclude clang-cl, see issue #631 +std_new_handler_t __attribute__((weak)) _ZSt15get_new_handlerv(void) { return NULL; } static std_new_handler_t mi_get_new_handler(void) { @@ -873,19 +897,19 @@ static mi_decl_noinline void* mi_try_new(size_t size, bool nothrow ) { return p; } -mi_decl_restrict void* mi_new(size_t size) { +mi_decl_nodiscard mi_decl_restrict void* mi_new(size_t size) { void* p = mi_malloc(size); - if (mi_unlikely(p == NULL)) return mi_try_new(size,false); + if mi_unlikely(p == NULL) return mi_try_new(size,false); return p; } -mi_decl_restrict void* mi_new_nothrow(size_t size) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_new_nothrow(size_t size) mi_attr_noexcept { void* p = mi_malloc(size); - if (mi_unlikely(p == NULL)) return mi_try_new(size, true); + if mi_unlikely(p == NULL) return mi_try_new(size, true); return p; } -mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) { +mi_decl_nodiscard mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) { void* p; do { p = mi_malloc_aligned(size, alignment); @@ -894,7 +918,7 @@ mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) { return p; } -mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_attr_noexcept { +mi_decl_nodiscard mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_attr_noexcept { void* p; do { p = mi_malloc_aligned(size, alignment); @@ -903,9 +927,9 @@ mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_ return p; } -mi_decl_restrict void* mi_new_n(size_t count, size_t size) { +mi_decl_nodiscard mi_decl_restrict void* mi_new_n(size_t count, size_t size) { size_t total; - if (mi_unlikely(mi_count_size_overflow(count, size, &total))) { + if mi_unlikely(mi_count_size_overflow(count, size, &total)) { mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc return NULL; } @@ -914,7 +938,7 @@ mi_decl_restrict void* mi_new_n(size_t count, size_t size) { } } -void* mi_new_realloc(void* p, size_t newsize) { +mi_decl_nodiscard void* mi_new_realloc(void* p, size_t newsize) { void* q; do { q = mi_realloc(p, newsize); @@ -922,9 +946,9 @@ void* mi_new_realloc(void* p, size_t newsize) { return q; } -void* mi_new_reallocn(void* p, size_t newcount, size_t size) { +mi_decl_nodiscard void* mi_new_reallocn(void* p, size_t newcount, size_t size) { size_t total; - if (mi_unlikely(mi_count_size_overflow(newcount, size, &total))) { + if mi_unlikely(mi_count_size_overflow(newcount, size, &total)) { mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc return NULL; } diff --git a/source/luametatex/source/libraries/mimalloc/src/arena.c b/source/luametatex/source/libraries/mimalloc/src/arena.c index 6b1e951f3..56b87d083 100644 --- a/source/luametatex/source/libraries/mimalloc/src/arena.c +++ b/source/luametatex/source/libraries/mimalloc/src/arena.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2019-2021, Microsoft Research, Daan Leijen +Copyright (c) 2019-2022, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -45,16 +45,17 @@ bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats); Arena allocation ----------------------------------------------------------- */ - // Block info: bit 0 contains the `in_use` bit, the upper bits the // size in count of arena blocks. typedef uintptr_t mi_block_info_t; #define MI_ARENA_BLOCK_SIZE (MI_SEGMENT_SIZE) // 8MiB (must be at least MI_SEGMENT_ALIGN) #define MI_ARENA_MIN_OBJ_SIZE (MI_ARENA_BLOCK_SIZE/2) // 4MiB -#define MI_MAX_ARENAS (64) // not more than 256 (since we use 8 bits in the memid) +#define MI_MAX_ARENAS (64) // not more than 126 (since we use 7 bits in the memid and an arena index + 1) // A memory arena descriptor typedef struct mi_arena_s { + mi_arena_id_t id; // arena id; 0 for non-specific + bool exclusive; // only allow allocations if specifically for this arena _Atomic(uint8_t*) start; // the start of the memory area size_t block_count; // size of the area in arena blocks (of `MI_ARENA_BLOCK_SIZE`) size_t field_count; // number of bitmap fields (where `field_count * MI_BITMAP_FIELD_BITS >= block_count`) @@ -74,24 +75,59 @@ static mi_decl_cache_align _Atomic(mi_arena_t*) mi_arenas[MI_MAX_ARENAS]; static mi_decl_cache_align _Atomic(size_t) mi_arena_count; // = 0 +/* ----------------------------------------------------------- + Arena id's + 0 is used for non-arena's (like OS memory) + id = arena_index + 1 +----------------------------------------------------------- */ + +static size_t mi_arena_id_index(mi_arena_id_t id) { + return (size_t)(id <= 0 ? MI_MAX_ARENAS : id - 1); +} + +static mi_arena_id_t mi_arena_id_create(size_t arena_index) { + mi_assert_internal(arena_index < MI_MAX_ARENAS); + mi_assert_internal(MI_MAX_ARENAS <= 126); + int id = (int)arena_index + 1; + mi_assert_internal(id >= 1 && id <= 127); + return id; +} + +mi_arena_id_t _mi_arena_id_none(void) { + return 0; +} + +static bool mi_arena_id_is_suitable(mi_arena_id_t arena_id, bool arena_is_exclusive, mi_arena_id_t req_arena_id) { + return ((!arena_is_exclusive && req_arena_id == _mi_arena_id_none()) || + (arena_id == req_arena_id)); +} + + /* ----------------------------------------------------------- Arena allocations get a memory id where the lower 8 bits are - the arena index +1, and the upper bits the block index. + the arena id, and the upper bits the block index. ----------------------------------------------------------- */ // Use `0` as a special id for direct OS allocated memory. #define MI_MEMID_OS 0 -static size_t mi_arena_id_create(size_t arena_index, mi_bitmap_index_t bitmap_index) { - mi_assert_internal(arena_index < 0xFE); +static size_t mi_arena_memid_create(mi_arena_id_t id, bool exclusive, mi_bitmap_index_t bitmap_index) { mi_assert_internal(((bitmap_index << 8) >> 8) == bitmap_index); // no overflow? - return ((bitmap_index << 8) | ((arena_index+1) & 0xFF)); + mi_assert_internal(id >= 0 && id <= 0x7F); + return ((bitmap_index << 8) | ((uint8_t)id & 0x7F) | (exclusive ? 0x80 : 0)); +} + +static bool mi_arena_memid_indices(size_t arena_memid, size_t* arena_index, mi_bitmap_index_t* bitmap_index) { + *bitmap_index = (arena_memid >> 8); + mi_arena_id_t id = (int)(arena_memid & 0x7F); + *arena_index = mi_arena_id_index(id); + return ((arena_memid & 0x80) != 0); } -static void mi_arena_id_indices(size_t memid, size_t* arena_index, mi_bitmap_index_t* bitmap_index) { - mi_assert_internal(memid != MI_MEMID_OS); - *arena_index = (memid & 0xFF) - 1; - *bitmap_index = (memid >> 8); +bool _mi_arena_memid_is_suitable(size_t arena_memid, mi_arena_id_t request_arena_id) { + mi_arena_id_t id = (int)(arena_memid & 0x7F); + bool exclusive = ((arena_memid & 0x80) != 0); + return mi_arena_id_is_suitable(id, exclusive, request_arena_id); } static size_t mi_block_count_of_size(size_t size) { @@ -117,14 +153,19 @@ static bool mi_arena_alloc(mi_arena_t* arena, size_t blocks, mi_bitmap_index_t* ----------------------------------------------------------- */ static mi_decl_noinline void* mi_arena_alloc_from(mi_arena_t* arena, size_t arena_index, size_t needed_bcount, - bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld) + bool* commit, bool* large, bool* is_pinned, bool* is_zero, + mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld) { + MI_UNUSED(arena_index); + mi_assert_internal(mi_arena_id_index(arena->id) == arena_index); + if (!mi_arena_id_is_suitable(arena->id, arena->exclusive, req_arena_id)) return NULL; + mi_bitmap_index_t bitmap_index; if (!mi_arena_alloc(arena, needed_bcount, &bitmap_index)) return NULL; // claimed it! set the dirty bits (todo: no need for an atomic op here?) void* p = arena->start + (mi_bitmap_index_bit(bitmap_index)*MI_ARENA_BLOCK_SIZE); - *memid = mi_arena_id_create(arena_index, bitmap_index); + *memid = mi_arena_memid_create(arena->id, arena->exclusive, bitmap_index); *is_zero = _mi_bitmap_claim_across(arena->blocks_dirty, arena->field_count, needed_bcount, bitmap_index, NULL); *large = arena->is_large; *is_pinned = (arena->is_large || !arena->allow_decommit); @@ -149,15 +190,31 @@ static mi_decl_noinline void* mi_arena_alloc_from(mi_arena_t* arena, size_t aren return p; } -static mi_decl_noinline void* mi_arena_allocate(int numa_node, size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld) +static mi_decl_noinline void* mi_arena_allocate(int numa_node, size_t size, size_t alignment, bool* commit, bool* large, + bool* is_pinned, bool* is_zero, + mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld) { MI_UNUSED_RELEASE(alignment); mi_assert_internal(alignment <= MI_SEGMENT_ALIGN); const size_t max_arena = mi_atomic_load_relaxed(&mi_arena_count); const size_t bcount = mi_block_count_of_size(size); - if (mi_likely(max_arena == 0)) return NULL; + if mi_likely(max_arena == 0) return NULL; mi_assert_internal(size <= bcount*MI_ARENA_BLOCK_SIZE); + size_t arena_index = mi_arena_id_index(req_arena_id); + if (arena_index < MI_MAX_ARENAS) { + // try a specific arena if requested + mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[arena_index]); + if (arena != NULL && + (arena->numa_node < 0 || arena->numa_node == numa_node) && // numa local? + (*large || !arena->is_large)) // large OS pages allowed, or arena is not large OS pages + { + void* p = mi_arena_alloc_from(arena, arena_index, bcount, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); + mi_assert_internal((uintptr_t)p % alignment == 0); + if (p != NULL) return p; + } + } + else { // try numa affine allocation for (size_t i = 0; i < max_arena; i++) { mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[i]); @@ -165,11 +222,9 @@ static mi_decl_noinline void* mi_arena_allocate(int numa_node, size_t size, size if ((arena->numa_node<0 || arena->numa_node==numa_node) && // numa local? (*large || !arena->is_large)) // large OS pages allowed, or arena is not large OS pages { - void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_pinned, is_zero, memid, tld); + void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); mi_assert_internal((uintptr_t)p % alignment == 0); - if (p != NULL) { - return p; - } + if (p != NULL) return p; } } @@ -180,10 +235,9 @@ static mi_decl_noinline void* mi_arena_allocate(int numa_node, size_t size, size if ((arena->numa_node>=0 && arena->numa_node!=numa_node) && // not numa local! (*large || !arena->is_large)) // large OS pages allowed, or arena is not large OS pages { - void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_pinned, is_zero, memid, tld); + void* p = mi_arena_alloc_from(arena, i, bcount, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); mi_assert_internal((uintptr_t)p % alignment == 0); - if (p != NULL) { - return p; + if (p != NULL) return p; } } } @@ -192,7 +246,7 @@ static mi_decl_noinline void* mi_arena_allocate(int numa_node, size_t size, size void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, - size_t* memid, mi_os_tld_t* tld) + mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld) { mi_assert_internal(commit != NULL && is_pinned != NULL && is_zero != NULL && memid != NULL && tld != NULL); mi_assert_internal(size > 0); @@ -206,12 +260,12 @@ void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* // try to allocate in an arena if the alignment is small enough and the object is not too small (as for heap meta data) if (size >= MI_ARENA_MIN_OBJ_SIZE && alignment <= MI_SEGMENT_ALIGN) { - void* p = mi_arena_allocate(numa_node, size, alignment, commit, large, is_pinned, is_zero, memid, tld); + void* p = mi_arena_allocate(numa_node, size, alignment, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); if (p != NULL) return p; } // finally, fall back to the OS - if (mi_option_is_enabled(mi_option_limit_os_alloc)) { + if (mi_option_is_enabled(mi_option_limit_os_alloc) || req_arena_id != _mi_arena_id_none()) { errno = ENOMEM; return NULL; } @@ -222,9 +276,19 @@ void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* return p; } -void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld) +void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld) { - return _mi_arena_alloc_aligned(size, MI_ARENA_BLOCK_SIZE, commit, large, is_pinned, is_zero, memid, tld); + return _mi_arena_alloc_aligned(size, MI_ARENA_BLOCK_SIZE, commit, large, is_pinned, is_zero, req_arena_id, memid, tld); +} + +void* mi_arena_area(mi_arena_id_t arena_id, size_t* size) { + if (size != NULL) *size = 0; + size_t arena_index = mi_arena_id_index(arena_id); + if (arena_index >= MI_MAX_ARENAS) return NULL; + mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[arena_index]); + if (arena == NULL) return NULL; + if (size != NULL) *size = arena->block_count * MI_ARENA_BLOCK_SIZE; + return arena->start; } /* ----------------------------------------------------------- @@ -244,7 +308,7 @@ void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_o // allocated in an arena size_t arena_idx; size_t bitmap_idx; - mi_arena_id_indices(memid, &arena_idx, &bitmap_idx); + mi_arena_memid_indices(memid, &arena_idx, &bitmap_idx); mi_assert_internal(arena_idx < MI_MAX_ARENAS); mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t,&mi_arenas[arena_idx]); mi_assert_internal(arena != NULL); @@ -281,10 +345,11 @@ void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_o Add an arena. ----------------------------------------------------------- */ -static bool mi_arena_add(mi_arena_t* arena) { +static bool mi_arena_add(mi_arena_t* arena, mi_arena_id_t* arena_id) { mi_assert_internal(arena != NULL); mi_assert_internal((uintptr_t)mi_atomic_load_ptr_relaxed(uint8_t,&arena->start) % MI_SEGMENT_ALIGN == 0); mi_assert_internal(arena->block_count > 0); + if (arena_id != NULL) *arena_id = -1; size_t i = mi_atomic_increment_acq_rel(&mi_arena_count); if (i >= MI_MAX_ARENAS) { @@ -292,11 +357,14 @@ static bool mi_arena_add(mi_arena_t* arena) { return false; } mi_atomic_store_ptr_release(mi_arena_t,&mi_arenas[i], arena); + arena->id = mi_arena_id_create(i); + if (arena_id != NULL) *arena_id = arena->id; return true; } -bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept +bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { + if (arena_id != NULL) *arena_id = _mi_arena_id_none(); if (size < MI_ARENA_BLOCK_SIZE) return false; if (is_large) { @@ -311,6 +379,8 @@ bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_la mi_arena_t* arena = (mi_arena_t*)_mi_os_alloc(asize, &_mi_stats_main); // TODO: can we avoid allocating from the OS? if (arena == NULL) return false; + arena->id = _mi_arena_id_none(); + arena->exclusive = exclusive; arena->block_count = bcount; arena->field_count = fields; arena->start = (uint8_t*)start; @@ -335,18 +405,19 @@ bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_la _mi_bitmap_claim(arena->blocks_inuse, fields, post, postidx, NULL); } - mi_arena_add(arena); - return true; + return mi_arena_add(arena, arena_id); + } // Reserve a range of regular OS memory -int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept +int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { + if (arena_id != NULL) *arena_id = _mi_arena_id_none(); size = _mi_align_up(size, MI_ARENA_BLOCK_SIZE); // at least one block bool large = allow_large; void* start = _mi_os_alloc_aligned(size, MI_SEGMENT_ALIGN, commit, &large, &_mi_stats_main); if (start==NULL) return ENOMEM; - if (!mi_manage_os_memory(start, size, (large || commit), large, true, -1)) { + if (!mi_manage_os_memory_ex(start, size, (large || commit), large, true, -1, exclusive, arena_id)) { _mi_os_free_ex(start, size, commit, &_mi_stats_main); _mi_verbose_message("failed to reserve %zu k memory\n", _mi_divide_up(size,1024)); return ENOMEM; @@ -355,6 +426,19 @@ int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noe return 0; } +bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept { + return mi_manage_os_memory_ex(start, size, is_committed, is_large, is_zero, numa_node, false, NULL); +} + +int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept { + return mi_reserve_os_memory_ex(size, commit, allow_large, false, NULL); +} + + +/* ----------------------------------------------------------- + Debugging +----------------------------------------------------------- */ + static size_t mi_debug_show_bitmap(const char* prefix, mi_bitmap_field_t* fields, size_t field_count ) { size_t inuse_count = 0; for (size_t i = 0; i < field_count; i++) { @@ -383,11 +467,13 @@ void mi_debug_show_arenas(void) mi_attr_noexcept { } } + /* ----------------------------------------------------------- Reserve a huge page arena. ----------------------------------------------------------- */ // reserve at a specific numa node -int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept { +int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_msecs, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept { + if (arena_id != NULL) *arena_id = -1; if (pages==0) return 0; if (numa_node < -1) numa_node = -1; if (numa_node >= 0) numa_node = numa_node % _mi_os_numa_node_count(); @@ -400,13 +486,16 @@ int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msec } _mi_verbose_message("numa node %i: reserved %zu GiB huge pages (of the %zu GiB requested)\n", numa_node, pages_reserved, pages); - if (!mi_manage_os_memory(p, hsize, true, true, true, numa_node)) { + if (!mi_manage_os_memory_ex(p, hsize, true, true, true, numa_node, exclusive, arena_id)) { _mi_os_free_huge_pages(p, hsize, &_mi_stats_main); return ENOMEM; } return 0; } +int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept { + return mi_reserve_huge_os_pages_at_ex(pages, numa_node, timeout_msecs, false, NULL); +} // reserve huge pages evenly among the given number of numa nodes (or use the available ones as detected) int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs) mi_attr_noexcept { diff --git a/source/luametatex/source/libraries/mimalloc/src/bitmap.c b/source/luametatex/source/libraries/mimalloc/src/bitmap.c index af6de0a12..4fc7a1f3d 100644 --- a/source/luametatex/source/libraries/mimalloc/src/bitmap.c +++ b/source/luametatex/source/libraries/mimalloc/src/bitmap.c @@ -108,6 +108,25 @@ bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fiel return false; } +// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled +bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields, + const size_t start_field_idx, const size_t count, + mi_bitmap_pred_fun_t pred_fun, void* pred_arg, + mi_bitmap_index_t* bitmap_idx) { + size_t idx = start_field_idx; + for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) { + if (idx >= bitmap_fields) idx = 0; // wrap + if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) { + if (pred_fun == NULL || pred_fun(*bitmap_idx, pred_arg)) { + return true; + } + // predicate returned false, unclaim and look further + _mi_bitmap_unclaim(bitmap, bitmap_fields, count, *bitmap_idx); + } + } + return false; +} + /* // Find `count` bits of 0 and set them to 1 atomically; returns `true` on success. // For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never span fields. @@ -283,7 +302,7 @@ bool _mi_bitmap_try_find_from_claim_across(mi_bitmap_t bitmap, const size_t bitm static size_t mi_bitmap_mask_across(mi_bitmap_index_t bitmap_idx, size_t bitmap_fields, size_t count, size_t* pre_mask, size_t* mid_mask, size_t* post_mask) { MI_UNUSED_RELEASE(bitmap_fields); const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx); - if (mi_likely(bitidx + count <= MI_BITMAP_FIELD_BITS)) { + if mi_likely(bitidx + count <= MI_BITMAP_FIELD_BITS) { *pre_mask = mi_bitmap_mask_(count, bitidx); *mid_mask = 0; *post_mask = 0; diff --git a/source/luametatex/source/libraries/mimalloc/src/bitmap.h b/source/luametatex/source/libraries/mimalloc/src/bitmap.h index 7bd3106c9..0c501ec1f 100644 --- a/source/luametatex/source/libraries/mimalloc/src/bitmap.h +++ b/source/luametatex/source/libraries/mimalloc/src/bitmap.h @@ -72,6 +72,10 @@ bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_ // For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields. bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx); +// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled +typedef bool (mi_cdecl *mi_bitmap_pred_fun_t)(mi_bitmap_index_t bitmap_idx, void* pred_arg); +bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_pred_fun_t pred_fun, void* pred_arg, mi_bitmap_index_t* bitmap_idx); + // Set `count` bits at `bitmap_idx` to 0 atomically // Returns `true` if all `count` bits were 1 previously. bool _mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx); diff --git a/source/luametatex/source/libraries/mimalloc/src/heap.c b/source/luametatex/source/libraries/mimalloc/src/heap.c index 816d961ae..15ca36031 100644 --- a/source/luametatex/source/libraries/mimalloc/src/heap.c +++ b/source/luametatex/source/libraries/mimalloc/src/heap.c @@ -139,9 +139,9 @@ static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect) mi_heap_visit_pages(heap, &mi_heap_page_never_delayed_free, NULL, NULL); } - // free thread delayed blocks. + // free all current thread delayed blocks. // (if abandoning, after this there are no more thread-delayed references into the pages.) - _mi_heap_delayed_free(heap); + _mi_heap_delayed_free_all(heap); // collect retired pages _mi_heap_collect_retired(heap, force); @@ -200,13 +200,14 @@ mi_heap_t* mi_heap_get_backing(void) { return bheap; } -mi_heap_t* mi_heap_new(void) { +mi_decl_nodiscard mi_heap_t* mi_heap_new_in_arena( mi_arena_id_t arena_id ) { mi_heap_t* bheap = mi_heap_get_backing(); mi_heap_t* heap = mi_heap_malloc_tp(bheap, mi_heap_t); // todo: OS allocate in secure mode? if (heap==NULL) return NULL; _mi_memcpy_aligned(heap, &_mi_heap_empty, sizeof(mi_heap_t)); heap->tld = bheap->tld; heap->thread_id = _mi_thread_id(); + heap->arena_id = arena_id; _mi_random_split(&bheap->random, &heap->random); heap->cookie = _mi_heap_random_next(heap) | 1; heap->keys[0] = _mi_heap_random_next(heap); @@ -218,6 +219,14 @@ mi_heap_t* mi_heap_new(void) { return heap; } +mi_decl_nodiscard mi_heap_t* mi_heap_new(void) { + return mi_heap_new_in_arena(_mi_arena_id_none()); +} + +bool _mi_heap_memid_is_suitable(mi_heap_t* heap, size_t memid) { + return _mi_arena_memid_is_suitable(memid, heap->arena_id); +} + uintptr_t _mi_heap_random_next(mi_heap_t* heap) { return _mi_random_next(&heap->random); } @@ -350,7 +359,7 @@ static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) { if (from==NULL || from->page_count == 0) return; // reduce the size of the delayed frees - _mi_heap_delayed_free(from); + _mi_heap_delayed_free_partial(from); // transfer all pages by appending the queues; this will set a new heap field // so threads may do delayed frees in either heap for a while. @@ -369,7 +378,7 @@ static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) { // note: be careful here as the `heap` field in all those pages no longer point to `from`, // turns out to be ok as `_mi_heap_delayed_free` only visits the list and calls a // the regular `_mi_free_delayed_block` which is safe. - _mi_heap_delayed_free(from); + _mi_heap_delayed_free_all(from); #if !defined(_MSC_VER) || (_MSC_VER > 1900) // somehow the following line gives an error in VS2015, issue #353 mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_block_t,&from->thread_delayed_free) == NULL); #endif @@ -421,7 +430,7 @@ static mi_heap_t* mi_heap_of_block(const void* p) { mi_segment_t* segment = _mi_ptr_segment(p); bool valid = (_mi_ptr_cookie(segment) == segment->cookie); mi_assert_internal(valid); - if (mi_unlikely(!valid)) return NULL; + if mi_unlikely(!valid) return NULL; return mi_page_heap(_mi_segment_page_of(segment,p)); } @@ -543,7 +552,7 @@ static bool mi_heap_visit_areas_page(mi_heap_t* heap, mi_page_queue_t* pq, mi_pa xarea.area.reserved = page->reserved * bsize; xarea.area.committed = page->capacity * bsize; xarea.area.blocks = _mi_page_start(_mi_page_segment(page), page, NULL); - xarea.area.used = page->used * bsize; + xarea.area.used = page->used; // number of blocks in use (#553) xarea.area.block_size = ubsize; xarea.area.full_block_size = bsize; return fun(heap, &xarea, arg); diff --git a/source/luametatex/source/libraries/mimalloc/src/init.c b/source/luametatex/source/libraries/mimalloc/src/init.c index 19124afef..4f37b7176 100644 --- a/source/luametatex/source/libraries/mimalloc/src/init.c +++ b/source/luametatex/source/libraries/mimalloc/src/init.c @@ -109,6 +109,7 @@ mi_decl_cache_align const mi_heap_t _mi_heap_empty = { MI_ATOMIC_VAR_INIT(NULL), 0, // tid 0, // cookie + 0, // arena id { 0, 0 }, // keys { {0}, {0}, 0 }, 0, // page count @@ -149,6 +150,7 @@ mi_heap_t _mi_heap_main = { MI_ATOMIC_VAR_INIT(NULL), 0, // thread id 0, // initial cookie + 0, // arena id { 0, 0 }, // the key of the main heap can be fixed (unlike page keys that need to be secure!) { {0x846ca68b}, {0}, 0 }, // random 0, // page count @@ -475,7 +477,7 @@ void _mi_heap_set_default_direct(mi_heap_t* heap) { // -------------------------------------------------------- // Run functions on process init/done, and thread init/done // -------------------------------------------------------- -static void mi_process_done(void); +static void mi_cdecl mi_process_done(void); static bool os_preloading = true; // true until this module is initialized static bool mi_redirected = false; // true if malloc redirects to mi_malloc @@ -490,7 +492,7 @@ mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept { } // Communicate with the redirection module on Windows -#if defined(_WIN32) && defined(MI_SHARED_LIB) +#if defined(_WIN32) && defined(MI_SHARED_LIB) && !defined(MI_WIN_NOREDIRECT) #ifdef __cplusplus extern "C" { #endif @@ -506,8 +508,8 @@ mi_decl_export void _mi_redirect_entry(DWORD reason) { mi_thread_done(); } } -__declspec(dllimport) bool mi_allocator_init(const char** message); -__declspec(dllimport) void mi_allocator_done(void); +__declspec(dllimport) bool mi_cdecl mi_allocator_init(const char** message); +__declspec(dllimport) void mi_cdecl mi_allocator_done(void); #ifdef __cplusplus } #endif @@ -606,7 +608,7 @@ void mi_process_init(void) mi_attr_noexcept { } // Called when the process is done (through `at_exit`) -static void mi_process_done(void) { +static void mi_cdecl mi_process_done(void) { // only shutdown if we were initialized if (!_mi_process_is_initialized) return; // ensure we are called once diff --git a/source/luametatex/source/libraries/mimalloc/src/options.c b/source/luametatex/source/libraries/mimalloc/src/options.c index 6b2379322..0182671ce 100644 --- a/source/luametatex/source/libraries/mimalloc/src/options.c +++ b/source/luametatex/source/libraries/mimalloc/src/options.c @@ -120,7 +120,7 @@ mi_decl_nodiscard long mi_option_get(mi_option_t option) { if (option < 0 || option >= _mi_option_last) return 0; mi_option_desc_t* desc = &options[option]; mi_assert(desc->option == option); // index should match the option - if (mi_unlikely(desc->init == UNINIT)) { + if mi_unlikely(desc->init == UNINIT) { mi_option_init(desc); } return desc->value; @@ -170,7 +170,7 @@ void mi_option_disable(mi_option_t option) { } -static void mi_out_stderr(const char* msg, void* arg) { +static void mi_cdecl mi_out_stderr(const char* msg, void* arg) { MI_UNUSED(arg); if (msg == NULL) return; #ifdef _WIN32 @@ -203,7 +203,7 @@ static void mi_out_stderr(const char* msg, void* arg) { static char out_buf[MI_MAX_DELAY_OUTPUT+1]; static _Atomic(size_t) out_len; -static void mi_out_buf(const char* msg, void* arg) { +static void mi_cdecl mi_out_buf(const char* msg, void* arg) { MI_UNUSED(arg); if (msg==NULL) return; if (mi_atomic_load_relaxed(&out_len)>=MI_MAX_DELAY_OUTPUT) return; @@ -235,7 +235,7 @@ static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) { // Once this module is loaded, switch to this routine // which outputs to stderr and the delayed output buffer. -static void mi_out_buf_stderr(const char* msg, void* arg) { +static void mi_cdecl mi_out_buf_stderr(const char* msg, void* arg) { mi_out_stderr(msg,arg); mi_out_buf(msg,arg); } @@ -346,7 +346,7 @@ void _mi_fprintf( mi_output_fun* out, void* arg, const char* fmt, ... ) { static void mi_vfprintf_thread(mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args) { if (prefix != NULL && strlen(prefix) <= 32 && !_mi_is_main_thread()) { char tprefix[64]; - snprintf(tprefix, sizeof(tprefix), "%sthread 0x%x: ", prefix, (unsigned) _mi_thread_id()); /* HH: %z is unknown */ +/* HH */ snprintf(tprefix, sizeof(tprefix), "%sthread 0x%x: ", prefix, (unsigned) _mi_thread_id()); /* HH: %z is unknown */ mi_vfprintf(out, arg, tprefix, fmt, args); } else { diff --git a/source/luametatex/source/libraries/mimalloc/src/os.c b/source/luametatex/source/libraries/mimalloc/src/os.c index 72959d818..6d7249873 100644 --- a/source/luametatex/source/libraries/mimalloc/src/os.c +++ b/source/luametatex/source/libraries/mimalloc/src/os.c @@ -122,7 +122,7 @@ size_t _mi_os_good_alloc_size(size_t size) { else if (size < 8*MI_MiB) align_size = 256*MI_KiB; else if (size < 32*MI_MiB) align_size = 1*MI_MiB; else align_size = 4*MI_MiB; - if (mi_unlikely(size >= (SIZE_MAX - align_size))) return size; // possible overflow? + if mi_unlikely(size >= (SIZE_MAX - align_size)) return size; // possible overflow? return _mi_align_up(size, align_size); } @@ -362,9 +362,9 @@ static bool mi_os_mem_free(void* addr, size_t size, bool was_committed, mi_stats // In mi_os_mem_alloc_aligned the fallback path may have returned a pointer inside // the memory region returned by VirtualAlloc; in that case we need to free using // the start of the region. - MEMORY_BASIC_INFORMATION info = { 0, 0 }; + MEMORY_BASIC_INFORMATION info = { 0 }; VirtualQuery(addr, &info, sizeof(info)); - if (info.AllocationBase < addr && ((uint8_t*)addr - (uint8_t*)info.AllocationBase) < MI_SEGMENT_SIZE) { + if (info.AllocationBase < addr && ((uint8_t*)addr - (uint8_t*)info.AllocationBase) < (ptrdiff_t)MI_SEGMENT_SIZE) { errcode = 0; err = (VirtualFree(info.AllocationBase, 0, MEM_RELEASE) == 0); if (err) { errcode = GetLastError(); } @@ -986,7 +986,7 @@ static bool mi_os_resetx(void* addr, size_t size, bool reset, mi_stats_t* stats) else _mi_stat_decrease(&stats->reset, csize); if (!reset) return true; // nothing to do on unreset! - #if (MI_DEBUG>1) + #if (MI_DEBUG>1) && !MI_TRACK_ENABLED if (MI_SECURE==0) { memset(start, 0, csize); // pretend it is eagerly reset } diff --git a/source/luametatex/source/libraries/mimalloc/src/page.c b/source/luametatex/source/libraries/mimalloc/src/page.c index fd6c5397d..4b321156c 100644 --- a/source/luametatex/source/libraries/mimalloc/src/page.c +++ b/source/luametatex/source/libraries/mimalloc/src/page.c @@ -124,14 +124,23 @@ bool _mi_page_is_valid(mi_page_t* page) { #endif void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) { + while (!_mi_page_try_use_delayed_free(page, delay, override_never)) { + mi_atomic_yield(); + } +} + +bool _mi_page_try_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) { mi_thread_free_t tfreex; mi_delayed_t old_delay; mi_thread_free_t tfree; + size_t yield_count = 0; do { tfree = mi_atomic_load_acquire(&page->xthread_free); // note: must acquire as we can break/repeat this loop and not do a CAS; tfreex = mi_tf_set_delayed(tfree, delay); old_delay = mi_tf_delayed(tfree); - if (mi_unlikely(old_delay == MI_DELAYED_FREEING)) { + if mi_unlikely(old_delay == MI_DELAYED_FREEING) { + if (yield_count >= 4) return false; // give up after 4 tries + yield_count++; mi_atomic_yield(); // delay until outstanding MI_DELAYED_FREEING are done. // tfree = mi_tf_set_delayed(tfree, MI_NO_DELAYED_FREE); // will cause CAS to busy fail } @@ -143,6 +152,8 @@ void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool overrid } } while ((old_delay == MI_DELAYED_FREEING) || !mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex)); + + return true; // success } /* ----------------------------------------------------------- @@ -199,7 +210,7 @@ void _mi_page_free_collect(mi_page_t* page, bool force) { // and the local free list if (page->local_free != NULL) { - if (mi_likely(page->free == NULL)) { + if mi_likely(page->free == NULL) { // usual case page->free = page->local_free; page->local_free = NULL; @@ -272,10 +283,18 @@ static mi_page_t* mi_page_fresh(mi_heap_t* heap, mi_page_queue_t* pq) { Do any delayed frees (put there by other threads if they deallocated in a full page) ----------------------------------------------------------- */ -void _mi_heap_delayed_free(mi_heap_t* heap) { +void _mi_heap_delayed_free_all(mi_heap_t* heap) { + while (!_mi_heap_delayed_free_partial(heap)) { + mi_atomic_yield(); + } +} + +// returns true if all delayed frees were processed +bool _mi_heap_delayed_free_partial(mi_heap_t* heap) { // take over the list (note: no atomic exchange since it is often NULL) mi_block_t* block = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free); while (block != NULL && !mi_atomic_cas_ptr_weak_acq_rel(mi_block_t, &heap->thread_delayed_free, &block, NULL)) { /* nothing */ }; + bool all_freed = true; // and free them all while(block != NULL) { @@ -283,7 +302,9 @@ void _mi_heap_delayed_free(mi_heap_t* heap) { // use internal free instead of regular one to keep stats etc correct if (!_mi_free_delayed_block(block)) { // we might already start delayed freeing while another thread has not yet - // reset the delayed_freeing flag; in that case delay it further by reinserting. + // reset the delayed_freeing flag; in that case delay it further by reinserting the current block + // into the delayed free list + all_freed = false; mi_block_t* dfree = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free); do { mi_block_set_nextx(heap, block, dfree, heap->keys); @@ -291,6 +312,7 @@ void _mi_heap_delayed_free(mi_heap_t* heap) { } block = next; } + return all_freed; } /* ----------------------------------------------------------- @@ -403,7 +425,7 @@ void _mi_page_retire(mi_page_t* page) mi_attr_noexcept { // how to check this efficiently though... // for now, we don't retire if it is the only page left of this size class. mi_page_queue_t* pq = mi_page_queue_of(page); - if (mi_likely(page->xblock_size <= MI_MAX_RETIRE_SIZE && !mi_page_is_in_full(page))) { + if mi_likely(page->xblock_size <= MI_MAX_RETIRE_SIZE && !mi_page_is_in_full(page)) { if (pq->last==page && pq->first==page) { // the only page in the queue? mi_stat_counter_increase(_mi_stats_main.page_no_retire,1); page->retire_expire = 1 + (page->xblock_size <= MI_SMALL_OBJ_SIZE_MAX ? MI_RETIRE_CYCLES : MI_RETIRE_CYCLES/4); @@ -619,7 +641,9 @@ static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi mi_page_set_heap(page, heap); page->xblock_size = (block_size < MI_HUGE_BLOCK_SIZE ? (uint32_t)block_size : MI_HUGE_BLOCK_SIZE); // initialize before _mi_segment_page_start size_t page_size; - _mi_segment_page_start(segment, page, &page_size); + const void* page_start = _mi_segment_page_start(segment, page, &page_size); + MI_UNUSED(page_start); + mi_track_mem_noaccess(page_start,page_size); mi_assert_internal(mi_page_block_size(page) <= page_size); mi_assert_internal(page_size <= page->slice_count*MI_SEGMENT_SLICE_SIZE); mi_assert_internal(page_size / block_size < (1L<<16)); @@ -812,8 +836,8 @@ static mi_page_t* mi_large_huge_page_alloc(mi_heap_t* heap, size_t size) { static mi_page_t* mi_find_page(mi_heap_t* heap, size_t size) mi_attr_noexcept { // huge allocation? const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size` - if (mi_unlikely(req_size > (MI_MEDIUM_OBJ_SIZE_MAX - MI_PADDING_SIZE) )) { - if (mi_unlikely(req_size > PTRDIFF_MAX)) { // we don't allocate more than PTRDIFF_MAX (see ) + if mi_unlikely(req_size > (MI_MEDIUM_OBJ_SIZE_MAX - MI_PADDING_SIZE)) { + if mi_unlikely(req_size > PTRDIFF_MAX) { // we don't allocate more than PTRDIFF_MAX (see ) _mi_error_message(EOVERFLOW, "allocation request is too large (%zu bytes)\n", req_size); return NULL; } @@ -830,32 +854,32 @@ static mi_page_t* mi_find_page(mi_heap_t* heap, size_t size) mi_attr_noexcept { // Generic allocation routine if the fast path (`alloc.c:mi_page_malloc`) does not succeed. // Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed. -void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept +void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept { mi_assert_internal(heap != NULL); // initialize if necessary - if (mi_unlikely(!mi_heap_is_initialized(heap))) { + if mi_unlikely(!mi_heap_is_initialized(heap)) { mi_thread_init(); // calls `_mi_heap_init` in turn heap = mi_get_default_heap(); - if (mi_unlikely(!mi_heap_is_initialized(heap))) { return NULL; } + if mi_unlikely(!mi_heap_is_initialized(heap)) { return NULL; } } mi_assert_internal(mi_heap_is_initialized(heap)); // call potential deferred free routines _mi_deferred_free(heap, false); - // free delayed frees from other threads - _mi_heap_delayed_free(heap); + // free delayed frees from other threads (but skip contended ones) + _mi_heap_delayed_free_partial(heap); // find (or allocate) a page of the right size mi_page_t* page = mi_find_page(heap, size); - if (mi_unlikely(page == NULL)) { // first time out of memory, try to collect and retry the allocation once more + if mi_unlikely(page == NULL) { // first time out of memory, try to collect and retry the allocation once more mi_heap_collect(heap, true /* force */); page = mi_find_page(heap, size); } - if (mi_unlikely(page == NULL)) { // out of memory + if mi_unlikely(page == NULL) { // out of memory const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size` _mi_error_message(ENOMEM, "unable to allocate memory (%zu bytes)\n", req_size); return NULL; @@ -864,6 +888,15 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size) mi_attr_noexcept mi_assert_internal(mi_page_immediate_available(page)); mi_assert_internal(mi_page_block_size(page) >= size); - // and try again, this time succeeding! (i.e. this should never recurse) - return _mi_page_malloc(heap, page, size); + // and try again, this time succeeding! (i.e. this should never recurse through _mi_page_malloc) + if mi_unlikely(zero && page->xblock_size == 0) { + // note: we cannot call _mi_page_malloc with zeroing for huge blocks; we zero it afterwards in that case. + void* p = _mi_page_malloc(heap, page, size, false); + mi_assert_internal(p != NULL); + _mi_memzero_aligned(p, mi_page_usable_block_size(page)); + return p; + } + else { + return _mi_page_malloc(heap, page, size, zero); + } } diff --git a/source/luametatex/source/libraries/mimalloc/src/random.c b/source/luametatex/source/libraries/mimalloc/src/random.c index d474a53a0..a5f5e6b82 100644 --- a/source/luametatex/source/libraries/mimalloc/src/random.c +++ b/source/luametatex/source/libraries/mimalloc/src/random.c @@ -172,6 +172,8 @@ If we cannot get good randomness, we fall back to weak randomness based on a tim // We prefer to use BCryptGenRandom instead of (the unofficial) RtlGenRandom but when using // dynamic overriding, we observed it can raise an exception when compiled with C++, and // sometimes deadlocks when also running under the VS debugger. +// In contrast, issue #623 implies that on Windows Server 2019 we need to use BCryptGenRandom. +// To be continued.. #pragma comment (lib,"advapi32.lib") #define RtlGenRandom SystemFunction036 #ifdef __cplusplus diff --git a/source/luametatex/source/libraries/mimalloc/src/region.c b/source/luametatex/source/libraries/mimalloc/src/region.c index 72ce84947..57d11fe8d 100644 --- a/source/luametatex/source/libraries/mimalloc/src/region.c +++ b/source/luametatex/source/libraries/mimalloc/src/region.c @@ -49,9 +49,10 @@ bool _mi_os_reset(void* p, size_t size, mi_stats_t* stats); bool _mi_os_unreset(void* p, size_t size, bool* is_zero, mi_stats_t* stats); // arena.c +mi_arena_id_t _mi_arena_id_none(void); void _mi_arena_free(void* p, size_t size, size_t memid, bool all_committed, mi_stats_t* stats); -void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld); -void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld); +void* _mi_arena_alloc(size_t size, bool* commit, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld); +void* _mi_arena_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t req_arena_id, size_t* memid, mi_os_tld_t* tld); @@ -180,7 +181,7 @@ static bool mi_region_try_alloc_os(size_t blocks, bool commit, bool allow_large, bool is_zero = false; bool is_pinned = false; size_t arena_memid = 0; - void* const start = _mi_arena_alloc_aligned(MI_REGION_SIZE, MI_SEGMENT_ALIGN, ®ion_commit, ®ion_large, &is_pinned, &is_zero, &arena_memid, tld); + void* const start = _mi_arena_alloc_aligned(MI_REGION_SIZE, MI_SEGMENT_ALIGN, ®ion_commit, ®ion_large, &is_pinned, &is_zero, _mi_arena_id_none(), & arena_memid, tld); if (start == NULL) return false; mi_assert_internal(!(region_large && !allow_large)); mi_assert_internal(!region_large || region_commit); @@ -330,7 +331,7 @@ static void* mi_region_try_alloc(size_t blocks, bool* commit, bool* large, bool* } mi_assert_internal(!_mi_bitmap_is_any_claimed(®ion->reset, 1, blocks, bit_idx)); - #if (MI_DEBUG>=2) + #if (MI_DEBUG>=2) && !MI_TRACK_ENABLED if (*commit) { ((uint8_t*)p)[0] = 0; } #endif @@ -370,13 +371,13 @@ void* _mi_mem_alloc_aligned(size_t size, size_t alignment, bool* commit, bool* l } if (p == NULL) { // and otherwise fall back to the OS - p = _mi_arena_alloc_aligned(size, alignment, commit, large, is_pinned, is_zero, &arena_memid, tld); + p = _mi_arena_alloc_aligned(size, alignment, commit, large, is_pinned, is_zero, _mi_arena_id_none(), & arena_memid, tld); *memid = mi_memid_create_from_arena(arena_memid); } if (p != NULL) { mi_assert_internal((uintptr_t)p % alignment == 0); -#if (MI_DEBUG>=2) + #if (MI_DEBUG>=2) && !MI_TRACK_ENABLED if (*commit) { ((uint8_t*)p)[0] = 0; } // ensure the memory is committed #endif } diff --git a/source/luametatex/source/libraries/mimalloc/src/segment-cache.c b/source/luametatex/source/libraries/mimalloc/src/segment-cache.c index aacdbc11d..da726716a 100644 --- a/source/luametatex/source/libraries/mimalloc/src/segment-cache.c +++ b/source/luametatex/source/libraries/mimalloc/src/segment-cache.c @@ -39,8 +39,13 @@ static mi_decl_cache_align mi_bitmap_field_t cache_available[MI_CACHE_FIELDS] = static mi_decl_cache_align mi_bitmap_field_t cache_available_large[MI_CACHE_FIELDS] = { MI_CACHE_BITS_SET }; static mi_decl_cache_align mi_bitmap_field_t cache_inuse[MI_CACHE_FIELDS]; // zero bit = free +static bool mi_cdecl mi_segment_cache_is_suitable(mi_bitmap_index_t bitidx, void* arg) { + mi_arena_id_t req_arena_id = *((mi_arena_id_t*)arg); + mi_cache_slot_t* slot = &cache[mi_bitmap_index_bit(bitidx)]; + return _mi_arena_memid_is_suitable(slot->memid, req_arena_id); +} -mi_decl_noinline void* _mi_segment_cache_pop(size_t size, mi_commit_mask_t* commit_mask, mi_commit_mask_t* decommit_mask, bool* large, bool* is_pinned, bool* is_zero, size_t* memid, mi_os_tld_t* tld) +mi_decl_noinline void* _mi_segment_cache_pop(size_t size, mi_commit_mask_t* commit_mask, mi_commit_mask_t* decommit_mask, bool* large, bool* is_pinned, bool* is_zero, mi_arena_id_t _req_arena_id, size_t* memid, mi_os_tld_t* tld) { #ifdef MI_CACHE_DISABLE return NULL; @@ -60,12 +65,15 @@ mi_decl_noinline void* _mi_segment_cache_pop(size_t size, mi_commit_mask_t* comm // find an available slot mi_bitmap_index_t bitidx = 0; bool claimed = false; + mi_arena_id_t req_arena_id = _req_arena_id; + mi_bitmap_pred_fun_t pred_fun = &mi_segment_cache_is_suitable; // cannot pass NULL as the arena may be exclusive itself; todo: do not put exclusive arenas in the cache? + if (*large) { // large allowed? - claimed = _mi_bitmap_try_find_from_claim(cache_available_large, MI_CACHE_FIELDS, start_field, 1, &bitidx); + claimed = _mi_bitmap_try_find_from_claim_pred(cache_available_large, MI_CACHE_FIELDS, start_field, 1, pred_fun, &req_arena_id, &bitidx); if (claimed) *large = true; } if (!claimed) { - claimed = _mi_bitmap_try_find_from_claim(cache_available, MI_CACHE_FIELDS, start_field, 1, &bitidx); + claimed = _mi_bitmap_try_find_from_claim_pred (cache_available, MI_CACHE_FIELDS, start_field, 1, pred_fun, &req_arena_id, &bitidx); if (claimed) *large = false; } @@ -283,7 +291,7 @@ static mi_segment_t* _mi_segment_of(const void* p) { size_t index = mi_segment_map_index_of(segment, &bitidx); // fast path: for any pointer to valid small/medium/large object or first MI_SEGMENT_SIZE in huge const uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]); - if (mi_likely((mask & ((uintptr_t)1 << bitidx)) != 0)) { + if mi_likely((mask & ((uintptr_t)1 << bitidx)) != 0) { return segment; // yes, allocated by us } if (index==MI_SEGMENT_MAP_WSIZE) return NULL; @@ -324,7 +332,7 @@ static mi_segment_t* _mi_segment_of(const void* p) { mi_assert_internal((void*)segment < p); bool cookie_ok = (_mi_ptr_cookie(segment) == segment->cookie); mi_assert_internal(cookie_ok); - if (mi_unlikely(!cookie_ok)) return NULL; + if mi_unlikely(!cookie_ok) return NULL; if (((uint8_t*)segment + mi_segment_size(segment)) <= (uint8_t*)p) return NULL; // outside the range mi_assert_internal(p >= (void*)segment && (uint8_t*)p < (uint8_t*)segment + mi_segment_size(segment)); return segment; diff --git a/source/luametatex/source/libraries/mimalloc/src/segment.c b/source/luametatex/source/libraries/mimalloc/src/segment.c index 800d4fc31..c76c2259e 100644 --- a/source/luametatex/source/libraries/mimalloc/src/segment.c +++ b/source/luametatex/source/libraries/mimalloc/src/segment.c @@ -721,7 +721,7 @@ static mi_page_t* mi_segment_span_allocate(mi_segment_t* segment, size_t slice_i return page; } -static mi_page_t* mi_segments_page_find_and_allocate(size_t slice_count, mi_segments_tld_t* tld) { +static mi_page_t* mi_segments_page_find_and_allocate(size_t slice_count, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld) { mi_assert_internal(slice_count*MI_SEGMENT_SLICE_SIZE <= MI_LARGE_OBJ_SIZE_MAX); // search from best fit up mi_span_queue_t* sq = mi_span_queue_for(slice_count, tld); @@ -730,8 +730,11 @@ static mi_page_t* mi_segments_page_find_and_allocate(size_t slice_count, mi_segm for (mi_slice_t* slice = sq->first; slice != NULL; slice = slice->next) { if (slice->slice_count >= slice_count) { // found one - mi_span_queue_delete(sq, slice); mi_segment_t* segment = _mi_ptr_segment(slice); + if (_mi_arena_memid_is_suitable(segment->memid, req_arena_id)) { + // found a suitable page span + mi_span_queue_delete(sq, slice); + if (slice->slice_count > slice_count) { mi_segment_slice_split(segment, slice, slice_count, tld); } @@ -745,6 +748,7 @@ static mi_page_t* mi_segments_page_find_and_allocate(size_t slice_count, mi_segm return page; } } + } sq++; } // could not find a page.. @@ -757,7 +761,7 @@ static mi_page_t* mi_segments_page_find_and_allocate(size_t slice_count, mi_segm ----------------------------------------------------------- */ // Allocate a segment from the OS aligned to `MI_SEGMENT_SIZE` . -static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_segments_tld_t* tld, mi_os_tld_t* os_tld, mi_page_t** huge_page) +static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld, mi_os_tld_t* os_tld, mi_page_t** huge_page) { mi_assert_internal((required==0 && huge_page==NULL) || (required>0 && huge_page != NULL)); mi_assert_internal((segment==NULL) || (segment!=NULL && required==0)); @@ -793,9 +797,9 @@ static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_ bool mem_large = (!eager_delay && (MI_SECURE==0)); // only allow large OS pages once we are no longer lazy bool is_pinned = false; size_t memid = 0; - segment = (mi_segment_t*)_mi_segment_cache_pop(segment_size, &commit_mask, &decommit_mask, &mem_large, &is_pinned, &is_zero, &memid, os_tld); + segment = (mi_segment_t*)_mi_segment_cache_pop(segment_size, &commit_mask, &decommit_mask, &mem_large, &is_pinned, &is_zero, req_arena_id, &memid, os_tld); if (segment==NULL) { - segment = (mi_segment_t*)_mi_arena_alloc_aligned(segment_size, MI_SEGMENT_SIZE, &commit, &mem_large, &is_pinned, &is_zero, &memid, os_tld); + segment = (mi_segment_t*)_mi_arena_alloc_aligned(segment_size, MI_SEGMENT_SIZE, &commit, &mem_large, &is_pinned, &is_zero, req_arena_id, &memid, os_tld); if (segment == NULL) return NULL; // failed to allocate if (commit) { mi_commit_mask_create_full(&commit_mask); @@ -817,6 +821,7 @@ static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_ if (!ok) return NULL; // failed to commit mi_commit_mask_set(&commit_mask, &commit_needed_mask); } + mi_track_mem_undefined(segment,commit_needed); segment->memid = memid; segment->mem_is_pinned = is_pinned; segment->mem_is_large = mem_large; @@ -907,8 +912,8 @@ static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_ // Allocate a segment from the OS aligned to `MI_SEGMENT_SIZE` . -static mi_segment_t* mi_segment_alloc(size_t required, mi_segments_tld_t* tld, mi_os_tld_t* os_tld, mi_page_t** huge_page) { - return mi_segment_init(NULL, required, tld, os_tld, huge_page); +static mi_segment_t* mi_segment_alloc(size_t required, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld, mi_os_tld_t* os_tld, mi_page_t** huge_page) { + return mi_segment_init(NULL, required, req_arena_id, tld, os_tld, huge_page); } @@ -1149,8 +1154,8 @@ static mi_segment_t* mi_abandoned_pop(void) { // Check efficiently if it is empty (or if the visited list needs to be moved) mi_tagged_segment_t ts = mi_atomic_load_relaxed(&abandoned); segment = mi_tagged_segment_ptr(ts); - if (mi_likely(segment == NULL)) { - if (mi_likely(!mi_abandoned_visited_revisit())) { // try to swap in the visited list on NULL + if mi_likely(segment == NULL) { + if mi_likely(!mi_abandoned_visited_revisit()) { // try to swap in the visited list on NULL return NULL; } } @@ -1367,6 +1372,9 @@ static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t needed_slice long max_tries = mi_option_get_clamp(mi_option_max_segment_reclaim, 8, 1024); // limit the work to bound allocation times while ((max_tries-- > 0) && ((segment = mi_abandoned_pop()) != NULL)) { segment->abandoned_visits++; + // todo: an arena exclusive heap will potentially visit many abandoned unsuitable segments + // and push them into the visited list and use many tries. Perhaps we can skip non-suitable ones in a better way? + bool is_suitable = _mi_heap_memid_is_suitable(heap, segment->memid); bool has_page = mi_segment_check_free(segment,needed_slices,block_size,tld); // try to free up pages (due to concurrent frees) if (segment->used == 0) { // free the segment (by forced reclaim) to make it available to other threads. @@ -1376,13 +1384,13 @@ static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t needed_slice // freeing but that would violate some invariants temporarily) mi_segment_reclaim(segment, heap, 0, NULL, tld); } - else if (has_page) { + else if (has_page && is_suitable) { // found a large enough free span, or a page of the right block_size with free space // we return the result of reclaim (which is usually `segment`) as it might free // the segment due to concurrent frees (in which case `NULL` is returned). return mi_segment_reclaim(segment, heap, block_size, reclaimed, tld); } - else if (segment->abandoned_visits > 3) { + else if (segment->abandoned_visits > 3 && is_suitable) { // always reclaim on 3rd visit to limit the abandoned queue length. mi_segment_reclaim(segment, heap, 0, NULL, tld); } @@ -1442,7 +1450,7 @@ static mi_segment_t* mi_segment_reclaim_or_alloc(mi_heap_t* heap, size_t needed_ return segment; } // 2. otherwise allocate a fresh segment - return mi_segment_alloc(0, tld, os_tld, NULL); + return mi_segment_alloc(0, heap->arena_id, tld, os_tld, NULL); } @@ -1458,7 +1466,7 @@ static mi_page_t* mi_segments_page_alloc(mi_heap_t* heap, mi_page_kind_t page_ki size_t page_size = _mi_align_up(required, (required > MI_MEDIUM_PAGE_SIZE ? MI_MEDIUM_PAGE_SIZE : MI_SEGMENT_SLICE_SIZE)); size_t slices_needed = page_size / MI_SEGMENT_SLICE_SIZE; mi_assert_internal(slices_needed * MI_SEGMENT_SLICE_SIZE == page_size); - mi_page_t* page = mi_segments_page_find_and_allocate(slices_needed, tld); //(required <= MI_SMALL_SIZE_MAX ? 0 : slices_needed), tld); + mi_page_t* page = mi_segments_page_find_and_allocate(slices_needed, heap->arena_id, tld); //(required <= MI_SMALL_SIZE_MAX ? 0 : slices_needed), tld); if (page==NULL) { // no free page, allocate a new segment and try again if (mi_segment_reclaim_or_alloc(heap, slices_needed, block_size, tld, os_tld) == NULL) { @@ -1482,10 +1490,10 @@ static mi_page_t* mi_segments_page_alloc(mi_heap_t* heap, mi_page_kind_t page_ki Huge page allocation ----------------------------------------------------------- */ -static mi_page_t* mi_segment_huge_page_alloc(size_t size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) +static mi_page_t* mi_segment_huge_page_alloc(size_t size, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) { mi_page_t* page = NULL; - mi_segment_t* segment = mi_segment_alloc(size,tld,os_tld,&page); + mi_segment_t* segment = mi_segment_alloc(size,req_arena_id,tld,os_tld,&page); if (segment == NULL || page==NULL) return NULL; mi_assert_internal(segment->used==1); mi_assert_internal(mi_page_block_size(page) >= size); @@ -1535,8 +1543,9 @@ mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, mi_segment page = mi_segments_page_alloc(heap,MI_PAGE_LARGE,block_size,block_size,tld, os_tld); } else { - page = mi_segment_huge_page_alloc(block_size,tld,os_tld); + page = mi_segment_huge_page_alloc(block_size,heap->arena_id,tld,os_tld); } + mi_assert_internal(page == NULL || _mi_heap_memid_is_suitable(heap, _mi_page_segment(page)->memid)); mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld)); return page; } diff --git a/source/luametatex/source/libraries/mimalloc/src/stats.c b/source/luametatex/source/libraries/mimalloc/src/stats.c index 134a7bcb6..f82c7c67f 100644 --- a/source/luametatex/source/libraries/mimalloc/src/stats.c +++ b/source/luametatex/source/libraries/mimalloc/src/stats.c @@ -267,7 +267,7 @@ static void mi_buffered_flush(buffered_t* buf) { buf->used = 0; } -static void mi_buffered_out(const char* msg, void* arg) { +static void mi_cdecl mi_buffered_out(const char* msg, void* arg) { buffered_t* buf = (buffered_t*)arg; if (msg==NULL || buf==NULL) return; for (const char* src = msg; *src != 0; src++) { diff --git a/source/luametatex/source/libraries/miniz/ChangeLog.md b/source/luametatex/source/libraries/miniz/ChangeLog.md index 4ae15a8cd..f88df3313 100644 --- a/source/luametatex/source/libraries/miniz/ChangeLog.md +++ b/source/luametatex/source/libraries/miniz/ChangeLog.md @@ -1,5 +1,44 @@ ## Changelog +### 3.0.1 + + - Fix compilation error with MINIZ_USE_UNALIGNED_LOADS_AND_STORES=1 + +### 3.0.0 + + - Reduce memory usage for inflate. This changes `struct tinfl_decompressor_tag` and therefore requires a major version bump (breaks ABI compatibility) + - Add padding to structures so it continues to work if features differ. This also changes some structures + - Use _ftelli64, _fseeki64 and stat with MinGW32 and OpenWatcom + - Fix varios warnings with OpenWatcom compiler + - Avoid using unaligned memory access in UBSan builds + - Set MINIZ_LITTLE_ENDIAN only if not set + - Add MINIZ_NO_DEFLATE_APIS and MINIZ_NO_INFLATE_APIS + - Fix use of uninitialized memory in tinfl_decompress_mem_to_callback() + - Use wfopen on windows + - Use _wstat64 instead _stat64 on windows + - Use level_and_flags after MZ_DEFAULT_COMPRESSION has been handled + - Improve endianess detection + - Don't use unaligned stores and loads per default + - Fix function declaration if MINIZ_NO_STDIO is used + - Fix MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 not being set + - Remove total files check (its 32-bit uint) + - tinfl_decompress: avoid NULL ptr arithmetic UB + - miniz_zip: fix mz_zip_reader_extract_to_heap to read correct sizes + - Eliminate 64-bit operations on 32-bit machines + - Disable treating warnings as error with MSVC + - Disable building shared lib via CMake by default + - Fixed alignment problems on MacOS + - Fixed get error string for MZ_ZIP_TOTAL_ERRORS + - Write correct FLEVEL 2-bit value in zlib header + - miniz.pc.in: fix include path not containing the "miniz" suffix + - Fix compatibility with FreeBSD + - pkg-config tweaks + - Fix integer overflow in header corruption check + - Fix some warnings + - tdefl_compress_normal: Avoid NULL ptr arithmetic UB + - replace use of stdint.h types with mz_ variants + + ### 2.2.0 - Fix examples with amalgamation diff --git a/source/luametatex/source/libraries/miniz/miniz.c b/source/luametatex/source/libraries/miniz/miniz.c index 87bdedb18..c197c181c 100644 --- a/source/luametatex/source/libraries/miniz/miniz.c +++ b/source/luametatex/source/libraries/miniz/miniz.c @@ -187,6 +187,8 @@ const char *mz_version(void) #ifndef MINIZ_NO_ZLIB_APIS +#ifndef MINIZ_NO_DEFLATE_APIS + int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); @@ -321,7 +323,7 @@ int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ - if ((source_len | *pDest_len) > 0xFFFFFFFFU) + if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; @@ -354,6 +356,10 @@ mz_ulong mz_compressBound(mz_ulong source_len) return mz_deflateBound(NULL, source_len); } +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + typedef struct { tinfl_decompressor m_decomp; @@ -560,7 +566,7 @@ int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned cha memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ - if ((*pSource_len | *pDest_len) > 0xFFFFFFFFU) + if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; @@ -589,6 +595,8 @@ int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char return mz_uncompress2(pDest, pDest_len, pSource, &source_len); } +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + const char *mz_error(int err) { static struct @@ -666,6 +674,8 @@ const char *mz_error(int err) +#ifndef MINIZ_NO_DEFLATE_APIS + #ifdef __cplusplus extern "C" { #endif @@ -744,7 +754,7 @@ static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *p { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; - MZ_CLEAR_OBJ(hist); + MZ_CLEAR_ARR(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; @@ -862,7 +872,7 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; - MZ_CLEAR_OBJ(num_codes); + MZ_CLEAR_ARR(num_codes); if (static_table) { for (i = 0; i < table_len; i++) @@ -888,8 +898,8 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); - MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); - MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_ARR(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); @@ -975,7 +985,7 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int } \ } -static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; +static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { @@ -1113,7 +1123,8 @@ static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + mz_uint match_len = pLZ_codes[0]; + mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); @@ -1158,7 +1169,7 @@ static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; - *(mz_uint64 *)pOutput_buf = bit_buffer; + memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; @@ -1240,6 +1251,8 @@ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) return tdefl_compress_lz_codes(d); } +static const mz_uint s_tdefl_num_probes[11]; + static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; @@ -1260,8 +1273,27 @@ static int tdefl_flush_block(tdefl_compressor *d, int flush) if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { - TDEFL_PUT_BITS(0x78, 8); - TDEFL_PUT_BITS(0x01, 8); + const mz_uint8 cmf = 0x78; + mz_uint8 flg, flevel = 3; + mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); + + /* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */ + for (i = 0; i < mz_un; i++) + if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; + + if (i < 2) + flevel = 0; + else if (i < 6) + flevel = 1; + else if (i == 6) + flevel = 2; + + header = cmf << 8 | (flevel << 6); + header += 31 - (header % 31); + flg = header & 0xFF; + + TDEFL_PUT_BITS(cmf, 8); + TDEFL_PUT_BITS(flg, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); @@ -1732,7 +1764,7 @@ static mz_bool tdefl_compress_normal(tdefl_compressor *d) mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); - const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) @@ -1942,8 +1974,8 @@ tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pI d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { - MZ_CLEAR_OBJ(d->m_hash); - MZ_CLEAR_OBJ(d->m_next); + MZ_CLEAR_ARR(d->m_hash); + MZ_CLEAR_ARR(d->m_next); d->m_dict_size = 0; } } @@ -1966,7 +1998,7 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) - MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_ARR(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; @@ -1987,7 +2019,7 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) - MZ_CLEAR_OBJ(d->m_dict); + MZ_CLEAR_ARR(d->m_dict); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; @@ -2197,7 +2229,7 @@ void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ -tdefl_compressor *tdefl_compressor_alloc() +tdefl_compressor *tdefl_compressor_alloc(void) { return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } @@ -2215,6 +2247,8 @@ void tdefl_compressor_free(tdefl_compressor *pComp) #ifdef __cplusplus } #endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software @@ -2243,6 +2277,8 @@ void tdefl_compressor_free(tdefl_compressor *pComp) +#ifndef MINIZ_NO_INFLATE_APIS + #ifdef __cplusplus extern "C" { #endif @@ -2323,10 +2359,10 @@ extern "C" { /* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ /* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ /* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ -#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ +#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ do \ { \ - temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) \ { \ code_len = temp >> 9; \ @@ -2338,7 +2374,7 @@ extern "C" { code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) \ break; \ @@ -2354,7 +2390,7 @@ extern "C" { /* The slow path is only executed at the very end of the input buffer. */ /* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ /* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ -#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ +#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ do \ { \ int temp; \ @@ -2363,7 +2399,7 @@ extern "C" { { \ if ((pIn_buf_end - pIn_buf_cur) < 2) \ { \ - TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ } \ else \ { \ @@ -2372,14 +2408,14 @@ extern "C" { num_bits += 16; \ } \ } \ - if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while (temp < 0); \ } \ sym = temp; \ @@ -2388,20 +2424,33 @@ extern "C" { } \ MZ_MACRO_END +static void tinfl_clear_tree(tinfl_decompressor *r) +{ + if (r->m_type == 0) + MZ_CLEAR_ARR(r->m_tree_0); + else if (r->m_type == 1) + MZ_CLEAR_ARR(r->m_tree_1); + else + MZ_CLEAR_ARR(r->m_tree_2); +} + tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { - static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; - static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; - static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; - static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - static const int s_min_table_sizes[3] = { 257, 1, 4 }; + static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 }; + + mz_int16 *pTrees[3]; + mz_uint8 *pCode_sizes[3]; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ @@ -2411,6 +2460,13 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex return TINFL_STATUS_BAD_PARAM; } + pTrees[0] = r->m_tree_0; + pTrees[1] = r->m_tree_1; + pTrees[2] = r->m_tree_2; + pCode_sizes[0] = r->m_code_size_0; + pCode_sizes[1] = r->m_code_size_1; + pCode_sizes[2] = r->m_code_size_2; + num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; @@ -2427,7 +2483,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) - counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); @@ -2488,11 +2544,11 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex { if (r->m_type == 1) { - mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint8 *p = r->m_code_size_0; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; - TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + TINFL_MEMSET(r->m_code_size_1, 5, 32); for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) @@ -2509,26 +2565,30 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + MZ_CLEAR_ARR(r->m_code_size_2); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); - r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for (; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; - tinfl_huff_table *pTable; + mz_int16 *pLookUp; + mz_int16 *pTree; + mz_uint8 *pCode_size; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; - pTable = &r->m_tables[r->m_type]; - MZ_CLEAR_OBJ(total_syms); - MZ_CLEAR_OBJ(pTable->m_look_up); - MZ_CLEAR_OBJ(pTable->m_tree); + pLookUp = r->m_look_up[r->m_type]; + pTree = pTrees[r->m_type]; + pCode_size = pCode_sizes[r->m_type]; + MZ_CLEAR_ARR(total_syms); + TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); + tinfl_clear_tree(r); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) - total_syms[pTable->m_code_size[i]]++; + total_syms[pCode_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) @@ -2542,7 +2602,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { - mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; + mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; @@ -2553,14 +2613,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { - pTable->m_look_up[rev_code] = k; + pLookUp[rev_code] = k; rev_code += (1 << code_size); } continue; } - if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { - pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } @@ -2568,24 +2628,24 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); - if (!pTable->m_tree[-tree_cur - 1]) + if (!pTree[-tree_cur - 1]) { - pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + pTree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else - tree_cur = pTable->m_tree[-tree_cur - 1]; + tree_cur = pTree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); - pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + pTree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { mz_uint s; - TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; @@ -2605,8 +2665,8 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } - TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); - TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for (;;) @@ -2616,7 +2676,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { - TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) @@ -2644,14 +2704,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex num_bits += 16; } #endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { - sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; @@ -2668,14 +2728,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex num_bits += 16; } #endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { - sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; @@ -2704,7 +2764,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex counter += extra_bits; } - TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) @@ -2789,7 +2849,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex --pIn_buf_cur; num_bits -= 8; } - bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) @@ -2821,7 +2881,7 @@ common_exit: } } r->m_num_bits = num_bits; - r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; @@ -2916,6 +2976,7 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; + memset(pDict,0,TINFL_LZ_DICT_SIZE); tinfl_init(&decomp); for (;;) { @@ -2938,7 +2999,7 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, } #ifndef MINIZ_NO_MALLOC -tinfl_decompressor *tinfl_decompressor_alloc() +tinfl_decompressor *tinfl_decompressor_alloc(void) { tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); if (pDecomp) @@ -2955,6 +3016,8 @@ void tinfl_decompressor_free(tinfl_decompressor *pDecomp) #ifdef __cplusplus } #endif + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software @@ -2997,19 +3060,48 @@ extern "C" { #include #if defined(_MSC_VER) || defined(__MINGW64__) + +#define WIN32_LEAN_AND_MEAN +#include + +static WCHAR* mz_utf8z_to_widechar(const char* str) +{ + int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + WCHAR* wStr = (WCHAR*)malloc(reqChars * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, sizeof(WCHAR) * reqChars); + return wStr; +} + static FILE *mz_fopen(const char *pFilename, const char *pMode) { - FILE *pFile = NULL; - fopen_s(&pFile, pFilename, pMode); - return pFile; + WCHAR* wFilename = mz_utf8z_to_widechar(pFilename); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfopen_s(&pFile, wFilename, wMode); + free(wFilename); + free(wMode); + return err ? NULL : pFile; } + static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { - FILE *pFile = NULL; - if (freopen_s(&pFile, pPath, pMode, pStream)) - return NULL; - return pFile; + WCHAR* wPath = mz_utf8z_to_widechar(pPath); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream); + free(wPath); + free(wMode); + return err ? NULL : pFile; +} + +static int mz_stat64(const char *path, struct __stat64 *buffer) +{ + WCHAR* wPath = mz_utf8z_to_widechar(path); + int res = _wstat64(wPath, buffer); + free(wPath); + return res; } + #ifndef MINIZ_NO_TIME #include #endif @@ -3020,11 +3112,12 @@ static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat64 -#define MZ_FILE_STAT _stat64 +#define MZ_FILE_STAT mz_stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove -#elif defined(__MINGW32__) + +#elif defined(__MINGW32__) || defined(__WATCOMC__) #ifndef MINIZ_NO_TIME #include #endif @@ -3032,13 +3125,14 @@ static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite -#define MZ_FTELL64 ftello64 -#define MZ_FSEEK64 fseeko64 -#define MZ_FILE_STAT_STRUCT _stat -#define MZ_FILE_STAT _stat +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove + #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include @@ -3054,6 +3148,7 @@ static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove + #elif defined(__USE_LARGEFILE64) /* gcc, clang */ #ifndef MINIZ_NO_TIME #include @@ -3069,7 +3164,8 @@ static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove -#elif defined(__APPLE__) + +#elif defined(__APPLE__) || defined(__FreeBSD__) #ifndef MINIZ_NO_TIME #include #endif @@ -3215,7 +3311,7 @@ struct mz_zip_internal_state_tag mz_zip_array m_sorted_central_dir_offsets; /* The flags passed in when the archive is initially opened. */ - uint32_t m_init_flags; + mz_uint32 m_init_flags; /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ mz_bool m_zip64; @@ -3651,7 +3747,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flag if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); - if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + if (cdir_size < (mz_uint64)pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) @@ -3802,7 +3898,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flag void mz_zip_zero_struct(mz_zip_archive *pZip) { if (pZip) - MZ_CLEAR_OBJ(*pZip); + MZ_CLEAR_PTR(pZip); } static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) @@ -4276,7 +4372,7 @@ static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const uint32_t size = pZip->m_total_files; + const mz_uint32 size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); if (pIndex) @@ -4291,7 +4387,7 @@ static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char while (l <= h) { mz_int64 m = l + ((h - l) >> 1); - uint32_t file_index = pIndices[(uint32_t)m]; + mz_uint32 file_index = pIndices[(mz_uint32)m]; int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) @@ -4384,7 +4480,8 @@ mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, co return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); } -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +static +mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; @@ -4397,6 +4494,9 @@ mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + if (st) { + file_stat = *st; + } else if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; @@ -4527,17 +4627,22 @@ mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file return status == TINFL_STATUS_DONE; } +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); +} + mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { mz_uint32 file_index; if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) @@ -4547,23 +4652,17 @@ mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFil void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { - mz_uint64 comp_size, uncomp_size, alloc_size; - const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + mz_zip_archive_file_stat file_stat; + mz_uint64 alloc_size; void *pBuf; if (pSize) *pSize = 0; - if (!p) - { - mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return NULL; - } - - comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) { mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); @@ -4576,7 +4675,7 @@ void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, si return NULL; } - if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; @@ -5037,7 +5136,7 @@ size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); /* Copy data to caller's buffer */ - memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + memcpy( (mz_uint8*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS /* Perform CRC */ @@ -5406,7 +5505,7 @@ handle_failure: mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) { mz_zip_internal_state *pState; - uint32_t i; + mz_uint32 i; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); @@ -5424,9 +5523,6 @@ mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) } else { - if (pZip->m_total_files >= MZ_UINT32_MAX) - return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); - if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } @@ -5788,7 +5884,7 @@ mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 cur_ofs = 0; char buf[4096]; - MZ_CLEAR_OBJ(buf); + MZ_CLEAR_ARR(buf); do { @@ -6151,7 +6247,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } - if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) { pState->m_zip64 = MZ_TRUE; /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ @@ -6244,7 +6340,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n } cur_archive_file_ofs += num_alignment_padding_bytes; - MZ_CLEAR_OBJ(local_dir_header); + MZ_CLEAR_ARR(local_dir_header); if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { @@ -6394,7 +6490,7 @@ mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_n mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { - mz_uint16 gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + mz_uint16 gen_flags; mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; @@ -6406,13 +6502,15 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA mz_zip_internal_state *pState; mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; - if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) - gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; - if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; + gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + /* Sanity checks */ if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); @@ -6497,7 +6595,7 @@ mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pA method = MZ_DEFLATED; } - MZ_CLEAR_OBJ(local_dir_header); + MZ_CLEAR_ARR(local_dir_header); if (pState->m_zip64) { if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) @@ -6801,7 +6899,7 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, } #endif /* #ifndef MINIZ_NO_STDIO */ -static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) { /* + 64 should be enough for any new zip64 data */ if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) @@ -7117,10 +7215,10 @@ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive * if (pZip->m_pState->m_zip64) { /* dest is zip64, so upgrade the data descriptor */ - const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); - const mz_uint32 src_crc32 = pSrc_descriptor[0]; - const mz_uint64 src_comp_size = pSrc_descriptor[1]; - const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; + const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); + const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor); + const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32)); + const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2*sizeof(mz_uint32)); mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); @@ -7256,7 +7354,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) if (pState->m_zip64) { - if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) + if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else @@ -7284,7 +7382,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) /* Write zip64 end of central directory header */ mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; - MZ_CLEAR_OBJ(hdr); + MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ @@ -7299,7 +7397,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; /* Write zip64 end of central directory locator */ - MZ_CLEAR_OBJ(hdr); + MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); @@ -7310,7 +7408,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) } /* Write end of central directory record */ - MZ_CLEAR_OBJ(hdr); + MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); @@ -7626,7 +7724,9 @@ const char *mz_zip_get_error_string(mz_zip_error mz_err) case MZ_ZIP_VALIDATION_FAILED: return "validation failed"; case MZ_ZIP_WRITE_CALLBACK_FAILED: - return "write calledback failed"; + return "write callback failed"; + case MZ_ZIP_TOTAL_ERRORS: + return "total errors"; default: break; } diff --git a/source/luametatex/source/libraries/miniz/miniz.h b/source/luametatex/source/libraries/miniz/miniz.h index 0e65e38b1..35c740c72 100644 --- a/source/luametatex/source/libraries/miniz/miniz.h +++ b/source/luametatex/source/libraries/miniz/miniz.h @@ -1,5 +1,7 @@ +#ifndef MINIZ_EXPORT #define MINIZ_EXPORT -/* miniz.c 2.2.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing +#endif +/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt @@ -115,8 +117,8 @@ -/* Defines to completely disable specific portions of miniz.c: - If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ /* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ /*#define MINIZ_NO_STDIO */ @@ -126,6 +128,12 @@ /* The current downside is the times written to your archives will be from 1979. */ /*#define MINIZ_NO_TIME */ +/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ +/*#define MINIZ_NO_DEFLATE_APIS */ + +/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ +/*#define MINIZ_NO_INFLATE_APIS */ + /* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_APIS */ @@ -138,12 +146,20 @@ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ /*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ -/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ /*#define MINIZ_NO_MALLOC */ +#ifdef MINIZ_NO_INFLATE_APIS +#define MINIZ_NO_ARCHIVE_APIS +#endif + +#ifdef MINIZ_NO_DEFLATE_APIS +#define MINIZ_NO_ARCHIVE_WRITING_APIS +#endif + #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) /* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ #define MINIZ_NO_TIME @@ -162,18 +178,40 @@ #define MINIZ_X86_OR_X64_CPU 0 #endif -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN only if not set */ +#if !defined(MINIZ_LITTLE_ENDIAN) +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif +#else + +#if MINIZ_X86_OR_X64_CPU +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#endif +#endif + +/* Using unaligned loads and stores causes errors when using UBSan */ +#if defined(__has_feature) +#if __has_feature(undefined_behavior_sanitizer) +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) #if MINIZ_X86_OR_X64_CPU /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #define MINIZ_UNALIGNED_USE_MEMCPY #else #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 @@ -237,10 +275,10 @@ enum MZ_DEFAULT_COMPRESSION = -1 }; -#define MZ_VERSION "10.2.0" -#define MZ_VERNUM 0xA100 -#define MZ_VER_MAJOR 10 -#define MZ_VER_MINOR 2 +#define MZ_VERSION "11.0.1" +#define MZ_VERNUM 0xB001 +#define MZ_VER_MAJOR 11 +#define MZ_VER_MINOR 1 #define MZ_VER_REVISION 0 #define MZ_VER_SUBREVISION 0 @@ -305,6 +343,8 @@ typedef mz_stream *mz_streamp; /* Returns the version string of miniz.c. */ MINIZ_EXPORT const char *mz_version(void); +#ifndef MINIZ_NO_DEFLATE_APIS + /* mz_deflateInit() initializes a compressor with default options: */ /* Parameters: */ /* pStream must point to an initialized mz_stream struct. */ @@ -357,6 +397,10 @@ MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const u /* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + /* Initializes a decompressor. */ MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); @@ -390,6 +434,7 @@ MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); /* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /* Returns a string description of the specified error code, or NULL if the error code is invalid. */ MINIZ_EXPORT const char *mz_error(int err); @@ -440,6 +485,8 @@ typedef void *const voidpc; #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream + +#ifndef MINIZ_NO_DEFLATE_APIS #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset @@ -449,6 +496,9 @@ typedef void *const voidpc; #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflateReset mz_inflateReset @@ -456,6 +506,8 @@ typedef void *const voidpc; #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define uncompress2 mz_uncompress2 +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 @@ -519,7 +571,8 @@ typedef int mz_bool; #ifdef MINIZ_NO_TIME typedef struct mz_dummy_time_t_tag { - int m_dummy; + mz_uint32 m_dummy1; + mz_uint32 m_dummy2; } mz_dummy_time_t; #define MZ_TIME_T mz_dummy_time_t #else @@ -541,6 +594,8 @@ typedef struct mz_dummy_time_t_tag #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) +#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) +#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) @@ -577,6 +632,8 @@ extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, si #pragma once +#ifndef MINIZ_NO_DEFLATE_APIS + #ifdef __cplusplus extern "C" { #endif @@ -764,10 +821,14 @@ MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); #ifdef __cplusplus } #endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #pragma once /* ------------------- Low-level Decompression API Definitions */ +#ifndef MINIZ_NO_INFLATE_APIS + #ifdef __cplusplus extern "C" { #endif @@ -876,12 +937,6 @@ enum TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; -typedef struct -{ - mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; - mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; -} tinfl_huff_table; - #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #else @@ -901,7 +956,13 @@ struct tinfl_decompressor_tag mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; - tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; + mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; + mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; + mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; + mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; + mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; @@ -909,6 +970,8 @@ struct tinfl_decompressor_tag } #endif +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + #pragma once @@ -942,10 +1005,6 @@ typedef struct mz_uint16 m_bit_flag; mz_uint16 m_method; -#ifndef MINIZ_NO_TIME - MZ_TIME_T m_time; -#endif - /* CRC-32 of uncompressed data. */ mz_uint32 m_crc32; @@ -982,6 +1041,11 @@ typedef struct /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +#ifdef MINIZ_NO_TIME + MZ_TIME_T m_padding; +#else + MZ_TIME_T m_time; +#endif } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); @@ -1093,9 +1157,7 @@ typedef struct mz_uint flags; int status; -#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS - mz_uint file_crc32; -#endif + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf; @@ -1105,6 +1167,12 @@ typedef struct tinfl_decompressor inflator; +#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint padding; +#else + mz_uint file_crc32; +#endif + } mz_zip_reader_extract_iter_state; /* -------- ZIP reading */ @@ -1228,9 +1296,9 @@ MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, c /* TODO */ typedef void *mz_zip_streaming_extract_state_ptr; mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); - uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); - uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); - mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); + mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); #endif @@ -1244,7 +1312,9 @@ MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags /* Misc utils/helpers, valid for ZIP reading or writing */ MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); +#endif /* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); @@ -1318,7 +1388,7 @@ MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_ /* An archive must be manually finalized by calling this function for it to be valid. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); -/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ +/* Finalizes a heap archive, returning a pointer to the heap block and its size. */ /* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); @@ -1335,11 +1405,13 @@ MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO /* Reads a single file from an archive into a heap block. */ /* If pComment is not NULL, only the file with the specified comment will be extracted. */ /* Returns NULL on failure. */ MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); +#endif #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ diff --git a/source/luametatex/source/libraries/miniz/readme.md b/source/luametatex/source/libraries/miniz/readme.md index 3f8fd7324..9734435fa 100644 --- a/source/luametatex/source/libraries/miniz/readme.md +++ b/source/luametatex/source/libraries/miniz/readme.md @@ -4,7 +4,7 @@ Miniz is a lossless, high performance data compression library in a single sourc ## Usage -Please use the files from the [releases page](https://github.com/richgel999/miniz/releases) in your projects. Do not use the git checkout directly! The different source and header files are [amalgamated](https://www.sqlite.org/amalgamation.html) into one `miniz.c`/`miniz.h` pair in a build step (`amalgamate.sh`). Include `miniz.c` and `miniz.h` in your project to use Miniz. +Releases are available at the [releases page](https://github.com/richgel999/miniz/releases) as a pair of `miniz.c`/`miniz.h` files which can be simply added to a project. To create this file pair the different source and header files are [amalgamated](https://www.sqlite.org/amalgamation.html) during build. Alternatively use as cmake or meson module (or build system of your choice). ## Features @@ -18,6 +18,18 @@ Please use the files from the [releases page](https://github.com/richgel999/mini * Entire inflater (including optional zlib header parsing and Adler-32 checking) is implemented in a single function as a coroutine, which is separately available in a small (~550 line) source file: miniz_tinfl.c * A fairly complete (but totally optional) set of .ZIP archive manipulation and extraction API's. The archive functionality is intended to solve common problems encountered in embedded, mobile, or game development situations. (The archive API's are purposely just powerful enough to write an entire archiver given a bit of additional higher-level logic.) +## Building miniz - Using vcpkg + +You can download and install miniz using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + ./vcpkg install miniz + +The miniz port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + ## Known Problems * No support for encrypted archives. Not sure how useful this stuff is in practice. diff --git a/source/luametatex/source/lua/lmtcallbacklib.h b/source/luametatex/source/lua/lmtcallbacklib.h index 6faa4ddac..7801e1a70 100644 --- a/source/luametatex/source/lua/lmtcallbacklib.h +++ b/source/luametatex/source/lua/lmtcallbacklib.h @@ -83,23 +83,23 @@ typedef enum callback_keys { callback_result_key = 'R', /*tex a string (return value) but nil is also okay */ } callback_keys; -inline static int lmt_callback_defined (int a) { return lmt_callback_state.values[a]; } -inline static int lmt_callback_call (lua_State *L, int i, int o, int top) { return lua_pcallk(L, i, o, top + 2, 0, NULL); } +inline static int lmt_callback_defined (int a) { return lmt_callback_state.values[a]; } +inline static int lmt_callback_call (lua_State *L, int i, int o, int top) { return lua_pcallk(L, i, o, top + 2, 0, NULL); } -extern int lmt_callback_okay (lua_State *L, int i, int *top); -extern void lmt_callback_error (lua_State *L, int top, int i); -inline void lmt_callback_wrapup (lua_State *L, int top) { lua_settop(L, top); } +extern int lmt_callback_okay (lua_State *L, int i, int *top); +extern void lmt_callback_error (lua_State *L, int top, int i); +inline static void lmt_callback_wrapup (lua_State *L, int top) { lua_settop(L, top); } + +extern int lmt_run_callback (lua_State *L, int i, const char *values, ...); +extern int lmt_run_and_save_callback (lua_State *L, int i, const char *values, ...); +extern int lmt_run_saved_callback_line (lua_State *L, int i, int firstpos); +extern int lmt_run_saved_callback_close (lua_State *L, int i); -extern int lmt_run_callback (lua_State *L, int i, const char *values, ...); -extern int lmt_run_and_save_callback (lua_State *L, int i, const char *values, ...); -extern int lmt_run_saved_callback_line (lua_State *L, int i, int firstpos); -extern int lmt_run_saved_callback_close (lua_State *L, int i); +extern void lmt_destroy_saved_callback (lua_State *L, int i); -extern void lmt_destroy_saved_callback (lua_State *L, int i); +extern void lmt_run_memory_callback (const char *what, int success); -extern void lmt_run_memory_callback (const char *what, int success); - -extern void lmt_push_callback_usage (lua_State *L); +extern void lmt_push_callback_usage (lua_State *L); # endif diff --git a/source/luametatex/source/lua/lmtnodelib.c b/source/luametatex/source/lua/lmtnodelib.c index 4f0c7d73a..2a02fbd11 100644 --- a/source/luametatex/source/lua/lmtnodelib.c +++ b/source/luametatex/source/lua/lmtnodelib.c @@ -1363,12 +1363,12 @@ static int nodelib_direct_getanchors(lua_State *L) lua_pushnil(L); } /* bonus detail: source, target */ - if (box_source_anchor(n)) { + if (box_anchor(n)) { lua_pushinteger(L, box_anchor(n) & 0x0FFF); } else { lua_pushnil(L); } - if (box_target_anchor(n)) { + if (box_anchor(n)) { lua_pushinteger(L, (box_anchor(n) >> 16) & 0x0FFF); } else { lua_pushnil(L); @@ -7861,6 +7861,57 @@ static int nodelib_direct_unprotectglyphs(lua_State *L) return 0; } +/*tex This is an experiment. */ + +inline static void nodelib_aux_protect_all_none(halfword h) +{ + while (h) { + if (node_type(h) == glyph_node) { + halfword f = glyph_font(h); + if (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f] && has_font_text_control(f, text_control_none_protected)) { + glyph_protected(h) = glyph_protected_text_code; + } + } + h = node_next(h); + } +} + +inline static void nodelib_aux_protect_node_none(halfword n) +{ + switch (node_type(n)) { + case glyph_node: + { + halfword f = glyph_font(n); + if (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f] && has_font_text_control(f, text_control_none_protected)) { + glyph_protected(n) = glyph_protected_text_code; + } + } + break; + case disc_node: + nodelib_aux_protect_all_none(disc_no_break_head(n)); + nodelib_aux_protect_all_none(disc_pre_break_head(n)); + nodelib_aux_protect_all_none(disc_post_break_head(n)); + break; + } +} + +static int nodelib_direct_protectglyphs_none(lua_State *L) +{ + halfword head = nodelib_valid_direct_from_index(L, 1); + halfword tail = nodelib_valid_direct_from_index(L, 2); + if (head) { + while (head) { + nodelib_aux_protect_node_none(head); + if (head == tail) { + break; + } else { + head = node_next(head); + } + } + } + return 0; +} + /* node.direct.first_glyph */ static int nodelib_direct_firstglyph(lua_State *L) @@ -7946,7 +7997,7 @@ static int nodelib_direct_hasglyph(lua_State *L) /* node.getword */ -static inline int nodelib_aux_in_word(halfword n) +inline static int nodelib_aux_in_word(halfword n) { switch (node_type(n)) { case glyph_node: @@ -9644,6 +9695,7 @@ static const struct luaL_Reg nodelib_direct_function_list[] = { { "newmathglyph", nodelib_direct_newmathglyph }, { "protectglyph", nodelib_direct_protectglyph }, { "protectglyphs", nodelib_direct_protectglyphs }, + { "protectglyphsnone", nodelib_direct_protectglyphs_none }, { "protrusionskippable", nodelib_direct_protrusionskipable }, { "rangedimensions", nodelib_direct_rangedimensions }, /* maybe get... */ { "getglyphdimensions", nodelib_direct_getglyphdimensions }, diff --git a/source/luametatex/source/lua/lmttexlib.c b/source/luametatex/source/lua/lmttexlib.c index c88d13490..7d1f3c32f 100644 --- a/source/luametatex/source/lua/lmttexlib.c +++ b/source/luametatex/source/lua/lmttexlib.c @@ -2173,7 +2173,7 @@ return 0; static int texlib_getmathcode(lua_State* L) { - mathcodeval mval = { 0, 0, 0 }; + mathcodeval mval = tex_no_math_code(); int ch = lmt_checkinteger(L, -1); if (character_in_range(ch)) { mval = tex_get_math_code(ch); @@ -2192,7 +2192,7 @@ static int texlib_getmathcode(lua_State* L) static int texlib_getmathcodes(lua_State* L) { - mathcodeval mval = { 0, 0, 0 }; + mathcodeval mval = tex_no_math_code(); int ch = lmt_checkinteger(L, -1); if (character_in_range(ch)) { mval = tex_get_math_code(ch); @@ -2603,6 +2603,7 @@ static int texlib_aux_convert(lua_State *L, int cur_code) /* case lua_token_string_code: */ /* arg token list */ case string_code: /* arg token */ case cs_string_code: /* arg token */ + case cs_active_code: /* arg token */ case detokenized_code: /* arg token */ case meaning_code: /* arg token */ case to_mathstyle_code: @@ -5191,8 +5192,11 @@ static int texlib_getmathcontrolvalues(lua_State *L) static int texlib_gettextcontrolvalues(lua_State *L) { - lua_createtable(L, 1, 0); + lua_createtable(L, 2, 2); lua_set_string_by_index(L, text_control_collapse_hyphens, "collapsehyphens"); + lua_set_string_by_index(L, text_control_base_ligaturing, "baseligaturing"); + lua_set_string_by_index(L, text_control_base_kerning, "basekerning"); + lua_set_string_by_index(L, text_control_none_protected, "noneprotected"); return 1; } diff --git a/source/luametatex/source/lua/lmttokenlib.c b/source/luametatex/source/lua/lmttokenlib.c index e15b2de5a..97ca1b144 100644 --- a/source/luametatex/source/lua/lmttokenlib.c +++ b/source/luametatex/source/lua/lmttokenlib.c @@ -1874,6 +1874,7 @@ static int tokenlib_scan_next_char(lua_State *L) break; case letter_cmd: case other_char_cmd: + case active_char_cmd: /* needs testing */ { char buffer[6]; char *uindex = aux_uni2string((char *) buffer, (unsigned int) cur_chr); @@ -3069,6 +3070,8 @@ static int tokenlib_push_macro(lua_State *L) // todo: just store cmd and flag to /*tex We need to check for a valid hit, but what is best here, for instance using |(cmd >= call_cmd)| is not okay as we miss a lot then. + + Active characters: maybe when we pass a number ... */ if (lua_type(L, 1) == LUA_TSTRING) { size_t lname = 0; @@ -3088,6 +3091,15 @@ static int tokenlib_push_macro(lua_State *L) // todo: just store cmd and flag to return 0; } +static int tokenlib_pop_macro(lua_State *L) +{ + lua_token_package *p = tokenlib_aux_check_ispackage(L, 1); + if (p) { + tex_forced_define(p->how, p->cs, p->flag, p->cmd, p->chr); + } + return 0; +} + char *lmt_get_expansion(halfword head, int *len) { char *str = NULL; @@ -3127,15 +3139,6 @@ static int tokenlib_get_expansion(lua_State* L) return 1; } -static int tokenlib_pop_macro(lua_State *L) -{ - lua_token_package *p = tokenlib_aux_check_ispackage(L, 1); - if (p) { - tex_forced_define(p->how, p->cs, p->flag, p->cmd, p->chr); - } - return 0; -} - static int tokenlib_save_lua(lua_State *L) { halfword f = lmt_tohalfword(L, 1); diff --git a/source/luametatex/source/luametatex.h b/source/luametatex/source/luametatex.h index 514eb191d..6bb728234 100644 --- a/source/luametatex/source/luametatex.h +++ b/source/luametatex/source/luametatex.h @@ -89,7 +89,7 @@ # define luametatex_version 210 # define luametatex_revision 02 # define luametatex_version_string "2.10.02" -# define luametatex_development_id 20221105 +# define luametatex_development_id 20221118 # define luametatex_name_camelcase "LuaMetaTeX" # define luametatex_name_lowercase "luametatex" diff --git a/source/luametatex/source/luarest/lmtstrlibext.c b/source/luametatex/source/luarest/lmtstrlibext.c index 78d7f760c..5478c4b67 100644 --- a/source/luametatex/source/luarest/lmtstrlibext.c +++ b/source/luametatex/source/luarest/lmtstrlibext.c @@ -15,8 +15,6 @@ /*tex Helpers */ -# define utf_fffd "\xEF\xBF\xBD" - inline static int strlib_aux_tounicode(const char *s, size_t l, size_t *p) { unsigned char i = s[*p]; @@ -183,7 +181,7 @@ static int strlib_aux_utf_failed(lua_State *L, int new_ind) { lua_pushinteger(L, new_ind); lua_replace(L, lua_upvalueindex(2)); - lua_pushliteral(L, utf_fffd); + lua_pushliteral(L, utf_fffd_string); return 1; } @@ -356,7 +354,7 @@ static int strlib_utfcharactertable(lua_State *L) lua_pushlstring(L, s + p, b); p += b; } else { - lua_pushliteral(L, utf_fffd); + lua_pushliteral(L, utf_fffd_string); p += 1; } lua_rawseti(L, -2, n++); diff --git a/source/luametatex/source/mp/mpc/mp.c b/source/luametatex/source/mp/mpc/mp.c index beb836de6..a79f7db3f 100644 --- a/source/luametatex/source/mp/mpc/mp.c +++ b/source/luametatex/source/mp/mpc/mp.c @@ -54,9 +54,9 @@ # define max_num_knot_nodes 1000 # define max_num_value_nodes 1000 # define max_num_symbolic_nodes 1000 -//define mp_link(A) (A)->link -//define mp_type(A) (A)->type -//define mp_name_type(A) (A)->name_type +# define mp_link(A) (A)->link +# define mp_type(A) (A)->type +# define mp_name_type(A) (A)->name_type # define mp_set_link(A,B) (A)->link = (mp_node) (B) # define mp_max_command_code mp_stop # define mp_max_pre_command mp_etex_command diff --git a/source/luametatex/source/mp/mpc/mpmathdouble.c b/source/luametatex/source/mp/mpc/mpmathdouble.c index 8349a3b66..ab661d96b 100644 --- a/source/luametatex/source/mp/mpc/mpmathdouble.c +++ b/source/luametatex/source/mp/mpc/mpmathdouble.c @@ -117,9 +117,9 @@ static void mp_set_double_half_from_addition (mp_number *A, mp_number *B, mp static void mp_set_double_half_from_subtraction(mp_number *A, mp_number *B, mp_number *C); static void mp_wrapup_numeric_token (MP mp, unsigned char *start, unsigned char *stop); static char *mp_double_number_tostring (MP mp, mp_number *n); -inline double mp_double_make_fraction (double p, double q) { return (p / q) * fraction_multiplier; } -inline double mp_double_take_fraction (double p, double q) { return (p * q) / fraction_multiplier; } -inline double mp_double_make_scaled (double p, double q) { return p / q; } +inline static double mp_double_make_fraction (double p, double q) { return (p / q) * fraction_multiplier; } +inline static double mp_double_take_fraction (double p, double q) { return (p * q) / fraction_multiplier; } +inline static double mp_double_make_scaled (double p, double q) { return p / q; } math_data *mp_initialize_double_math(MP mp) { diff --git a/source/luametatex/source/mp/mpw/mpmathdouble.w b/source/luametatex/source/mp/mpw/mpmathdouble.w index ab9caf460..6f0f8df99 100644 --- a/source/luametatex/source/mp/mpw/mpmathdouble.w +++ b/source/luametatex/source/mp/mpw/mpmathdouble.w @@ -147,9 +147,9 @@ static void mp_set_double_half_from_subtraction(mp_number *A, mp_number *B, mp static void mp_wrapup_numeric_token (MP mp, unsigned char *start, unsigned char *stop); static char *mp_double_number_tostring (MP mp, mp_number *n); -inline double mp_double_make_fraction (double p, double q) { return (p / q) * fraction_multiplier; } -inline double mp_double_take_fraction (double p, double q) { return (p * q) / fraction_multiplier; } -inline double mp_double_make_scaled (double p, double q) { return p / q; } +inline static double mp_double_make_fraction (double p, double q) { return (p / q) * fraction_multiplier; } +inline static double mp_double_take_fraction (double p, double q) { return (p * q) / fraction_multiplier; } +inline static double mp_double_make_scaled (double p, double q) { return p / q; } @c math_data *mp_initialize_double_math(MP mp) diff --git a/source/luametatex/source/tex/texcommands.c b/source/luametatex/source/tex/texcommands.c index 5a4cc48b3..0e9bb7ac9 100644 --- a/source/luametatex/source/tex/texcommands.c +++ b/source/luametatex/source/tex/texcommands.c @@ -138,7 +138,6 @@ void tex_initialize_commands(void) tex_primitive(tex_command, "exhyphenchar", internal_int_cmd, ex_hyphen_char_code, internal_int_base); tex_primitive(tex_command, "exhyphenpenalty", internal_int_cmd, ex_hyphen_penalty_code, internal_int_base); tex_primitive(tex_command, "fam", internal_int_cmd, family_code, internal_int_base); - tex_primitive(luatex_command, "variablefam", internal_int_cmd, variable_family_code, internal_int_base); tex_primitive(tex_command, "finalhyphendemerits", internal_int_cmd, final_hyphen_demerits_code, internal_int_base); tex_primitive(tex_command, "floatingpenalty", internal_int_cmd, floating_penalty_code, internal_int_base); tex_primitive(tex_command, "globaldefs", internal_int_cmd, global_defs_code, internal_int_base); @@ -198,7 +197,8 @@ void tex_initialize_commands(void) tex_primitive(luatex_command, "tracingnodes", internal_int_cmd, tracing_nodes_code, internal_int_base); tex_primitive(luatex_command, "tracingfullboxes", internal_int_cmd, tracing_full_boxes_code, internal_int_base); tex_primitive(luatex_command, "tracingpenalties", internal_int_cmd, tracing_penalties_code, internal_int_base); - tex_primitive(tex_command, "uchyph", internal_int_cmd, uc_hyph_code, internal_int_base); /* obsolete */ + tex_primitive(tex_command, "uchyph", internal_int_cmd, uc_hyph_code, internal_int_base); /* obsolete, not needed */ + tex_primitive(luatex_command, "variablefam", internal_int_cmd, variable_family_code, internal_int_base); /* obsolete, not used */ tex_primitive(tex_command, "vbadness", internal_int_cmd, vbadness_code, internal_int_base); tex_primitive(tex_command, "widowpenalty", internal_int_cmd, widow_penalty_code, internal_int_base); tex_primitive(tex_command, "year", internal_int_cmd, year_code, internal_int_base); @@ -386,6 +386,7 @@ void tex_initialize_commands(void) tex_primitive(luatex_command, "expandtoken", expand_after_cmd, expand_token_code, 0); tex_primitive(luatex_command, "expandcstoken", expand_after_cmd, expand_cs_token_code, 0); tex_primitive(luatex_command, "expand", expand_after_cmd, expand_code, 0); + tex_primitive(luatex_command, "expandactive", expand_after_cmd, expand_active_code, 0); tex_primitive(luatex_command, "semiexpand", expand_after_cmd, semi_expand_code, 0); tex_primitive(luatex_command, "expandedafter", expand_after_cmd, expand_after_toks_code, 0); /* tex_primitive(luatex_command, "expandafterfi", expand_after_cmd, expand_after_fi, 0); */ @@ -655,6 +656,7 @@ void tex_initialize_commands(void) tex_primitive(tex_command, "string", convert_cmd, string_code, 0); tex_primitive(luatex_command, "directlua", convert_cmd, lua_code, 0); tex_primitive(luatex_command, "csstring", convert_cmd, cs_string_code, 0); + tex_primitive(luatex_command, "csactive", convert_cmd, cs_active_code, 0); tex_primitive(luatex_command, "detokenized", convert_cmd, detokenized_code, 0); tex_primitive(luatex_command, "expanded", convert_cmd, expanded_code, 0); tex_primitive(luatex_command, "semiexpanded", convert_cmd, semi_expanded_code, 0); @@ -759,6 +761,7 @@ void tex_initialize_commands(void) tex_primitive(tex_command, "sfcode", define_char_code_cmd, sfcode_charcode, 0); tex_primitive(luatex_command, "hccode", define_char_code_cmd, hccode_charcode, 0); tex_primitive(luatex_command, "hmcode", define_char_code_cmd, hmcode_charcode, 0); + tex_primitive(luatex_command, "amcode", define_char_code_cmd, amcode_charcode, 0); tex_primitive(tex_command, "mathcode", define_char_code_cmd, mathcode_charcode, 0); tex_primitive(tex_command, "delcode", define_char_code_cmd, delcode_charcode, 0); diff --git a/source/luametatex/source/tex/texcommands.h b/source/luametatex/source/tex/texcommands.h index 6d85f1ed7..a5c157a44 100644 --- a/source/luametatex/source/tex/texcommands.h +++ b/source/luametatex/source/tex/texcommands.h @@ -442,6 +442,7 @@ typedef enum convert_codes { semi_expanded_code, /*tex command code for |\constantexpanded| */ string_code, /*tex command code for |\string| */ cs_string_code, /*tex command code for |\csstring| */ + cs_active_code, /*tex command code for |\csactive| */ detokenized_code, /*tex command code for |\detokenized| */ roman_numeral_code, /*tex command code for |\romannumeral| */ meaning_code, /*tex command code for |\meaning| */ @@ -743,6 +744,7 @@ typedef enum expand_after_codes { expand_token_code, expand_cs_token_code, expand_code, + expand_active_code, semi_expand_code, expand_after_toks_code, /* expand_after_fi, */ @@ -1034,6 +1036,7 @@ typedef enum charcode_codes { sfcode_charcode, hccode_charcode, hmcode_charcode, + amcode_charcode, mathcode_charcode, extmathcode_charcode, delcode_charcode, diff --git a/source/luametatex/source/tex/texdumpdata.h b/source/luametatex/source/tex/texdumpdata.h index e6f51d323..d9b4e5cdd 100644 --- a/source/luametatex/source/tex/texdumpdata.h +++ b/source/luametatex/source/tex/texdumpdata.h @@ -55,7 +55,7 @@ */ -# define luametatex_format_fingerprint 675 +# define luametatex_format_fingerprint 676 /* These end up in the string pool. */ diff --git a/source/luametatex/source/tex/texequivalents.c b/source/luametatex/source/tex/texequivalents.c index 4de7617c9..bdf21446e 100644 --- a/source/luametatex/source/tex/texequivalents.c +++ b/source/luametatex/source/tex/texequivalents.c @@ -1125,7 +1125,7 @@ void tex_geq_word_define(halfword p, int w) a side effect of looking at the code through a visual studio lense.) */ -static inline void tex_aux_set_eq_data(halfword p, singleword t, halfword e, singleword f, quarterword l) +inline static void tex_aux_set_eq_data(halfword p, singleword t, halfword e, singleword f, quarterword l) { singleword flag = eq_flag(p); set_eq_level(p, l); diff --git a/source/luametatex/source/tex/texequivalents.h b/source/luametatex/source/tex/texequivalents.h index 2feab6858..1a6c41b2a 100644 --- a/source/luametatex/source/tex/texequivalents.h +++ b/source/luametatex/source/tex/texequivalents.h @@ -571,7 +571,12 @@ typedef enum int_codes { alignment_wrap_source_code, /* page_boundary_penalty_code, */ line_break_criterium_code, - variable_family_code, + /* + This one was added as experiment to \LUATEX\ (answer to a forwarded question) but as it + didn't get tested it will go away. \CONTEXT\ doesn't need it and we don't need to be + compatible anyway. Lesson learned. + */ + variable_family_code, /* those below these are not interfaced via primitives */ internal_par_state_code, internal_dir_state_code, @@ -601,7 +606,7 @@ typedef enum int_codes { } int_codes; # define first_int_code pre_tolerance_code -# define last_int_code line_break_criterium_code +# define last_int_code variable_family_code typedef enum dimen_codes { par_indent_code, /*tex indentation of paragraphs */ @@ -836,7 +841,7 @@ extern save_state_info lmt_save_state; # define saved_value(A) lmt_save_state.save_stack[lmt_save_state.save_stack_data.ptr + (A)].saved_value # define saved_word(A) lmt_save_state.save_stack[lmt_save_state.save_stack_data.ptr + (A)].saved_word -inline void tex_set_saved_record(halfword ptr, quarterword type, quarterword level, halfword value) +inline static void tex_set_saved_record(halfword ptr, quarterword type, quarterword level, halfword value) { saved_type(ptr) = type; saved_level(ptr) = level; diff --git a/source/luametatex/source/tex/texexpand.c b/source/luametatex/source/tex/texexpand.c index e74a9c08d..cec254d2b 100644 --- a/source/luametatex/source/tex/texexpand.c +++ b/source/luametatex/source/tex/texexpand.c @@ -251,7 +251,7 @@ void tex_expand_current_token(void) case expand_token_code: { /* we can share code with lmtokenlib .. todo */ - halfword cat = tex_scan_category_code(); + halfword cat = tex_scan_category_code(0); halfword chr = tex_scan_char_number(0); /* too fragile: halfword tok = null; @@ -304,6 +304,7 @@ void tex_expand_current_token(void) case spacer_cmd: case letter_cmd: case other_char_cmd: + case active_char_cmd: /* new */ cur_tok = token_val(cmd, eq_value(cur_cs)); break; } @@ -322,6 +323,20 @@ void tex_expand_current_token(void) } break; } + case expand_active_code: + { + tex_get_token(); + if (cur_cmd == active_char_cmd) { + cur_cs = tex_active_to_cs(cur_chr, ! lmt_hash_state.no_new_cs); + if (cur_cs) { + cur_tok = cs_token_flag + cur_cs; + } else { + cur_tok = token_val(cur_cmd, cur_chr); + } + } + tex_back_input(cur_tok); + break; + } case semi_expand_code: { tex_get_token(); @@ -618,12 +633,12 @@ static int tex_aux_collect_cs_tokens(halfword *p, int *n) case spacer_cmd: case letter_cmd: case other_char_cmd: + case active_char_cmd: /* new */ // cur_tok = token_val(cur_cmd, cur_chr); // *p = tex_store_new_token(*p, cur_tok); *p = tex_store_new_token(*p, token_val(cur_cmd, cur_chr)); *n += 1; break; - /* case active_char_cmd: */ /* case comment_cmd: */ /* case invalid_char_cmd: */ /* diff --git a/source/luametatex/source/tex/texfont.c b/source/luametatex/source/tex/texfont.c index f3b26d99a..ac0ea1290 100644 --- a/source/luametatex/source/tex/texfont.c +++ b/source/luametatex/source/tex/texfont.c @@ -26,6 +26,65 @@ # include "luametatex.h" +/*tex + Finally the base mode ligaturing and kerning code has also been made more consistent with the + rest: abstraction, more tight equality testing, helpers, merged some experiments, etc. It will + probably evolve a bit more; not that we use basemode frequently in \CONTEXT. Keep in mind that + it is not that hard to mess up the list when using \LUA\ but we do little checking here. + + From now on base mode ligaturing and kerning will only be applied when |text_font_control| + have the |text_control_base_ligaturing| and |text_control_base_kerning| bits set. +*/ + +inline static halfword tex_aux_discretionary_node(halfword target, int location) +{ + switch (location) { + case pre_break_code : return disc_pre_break_node(target); + case post_break_code: return disc_post_break_node(target); + case no_break_code : return disc_no_break_node(target); + default : return null; + } +} + +inline static int tex_aux_same_font_properties(halfword a, halfword b) // also in kern +{ + return node_type(a) == glyph_node && node_type(b) == glyph_node + && glyph_font(a) == glyph_font(b) + && glyph_x_scale(a) == glyph_x_scale(b) + && glyph_y_scale(a) == glyph_y_scale(b) + && glyph_scale(a) == glyph_scale(b); +} + +inline static int tex_aux_apply_base_kerning(halfword n) +{ + if (glyph_protected(n)) { + return 0; + } else { + halfword f = glyph_font(n); + if (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f]) { + return has_font_text_control(f, text_control_base_kerning); + } else { + return 0; + } + } +} + +inline static int tex_aux_apply_base_ligaturing(halfword n) +{ + if (glyph_protected(n)) { + return 0; + } else { + halfword f = glyph_font(n); + if (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f]) { + return has_font_text_control(f, text_control_base_ligaturing); + } else { + return 0; + } + } +} + +/* */ + inline static scaled tex_aux_font_x_scaled(scaled v) { return v ? scaledround(0.000001 * (glyph_scale_par ? glyph_scale_par : 1000) * (glyph_x_scale_par ? glyph_x_scale_par : 1000) * v) : 0; @@ -135,34 +194,34 @@ void tex_undump_font_data(dumpstream f) { void tex_set_charinfo_extensible_recipe(charinfo *ci, extinfo *ext) { if (ci->math) { - extinfo *lst = ci->math->extensible_recipe; - if (lst) { - while (lst) { - extinfo *c = lst->next; - lmt_memory_free(lst); - lst = c; + extinfo *list = ci->math->extensible_recipe; + if (list) { + while (list) { + extinfo *c = list->next; + lmt_memory_free(list); + list = c; } } ci->math->extensible_recipe = ext; } } -void tex_set_font_parameters(halfword f, int b) +void tex_set_font_parameters(halfword f, int index) { int i = font_parameter_count(f); - if (b > i) { + if (index > i) { /*tex If really needed this can be a calloc. */ - int s = (b + 2) * (int) sizeof(int); - int *a = lmt_memory_realloc(font_parameter_base(f), (size_t) s); - if (a) { - lmt_font_state.font_data.allocated += (b - i + 1) * (int) sizeof(scaled); - font_parameter_base(f) = a; - font_parameter_count(f) = b; - while (i < b) { + int size = (index + 2) * (int) sizeof(int); + int *list = lmt_memory_realloc(font_parameter_base(f), (size_t) size); + if (list) { + lmt_font_state.font_data.allocated += (index - i + 1) * (int) sizeof(scaled); + font_parameter_base(f) = list; + font_parameter_count(f) = index; + while (i < index) { font_parameter(f, ++i) = 0; } } else { - tex_overflow_error("font", s); + tex_overflow_error("font", size); } } } @@ -174,14 +233,14 @@ int tex_new_font(void) int size = sizeof(charinfo); charinfo *ci = lmt_memory_calloc(1, (size_t) size); if (ci) { - texfont *t = NULL; + texfont *tf = NULL; size = sizeof(texfont); - t = lmt_memory_calloc(1, (size_t) size); - if (t) { + tf = lmt_memory_calloc(1, (size_t) size); + if (tf) { sa_tree_item sa_value = { 0 }; int id = tex_new_font_id(); lmt_font_state.font_data.allocated += size; - lmt_font_state.fonts[id] = t; + lmt_font_state.fonts[id] = tf; set_font_name(id, NULL); set_font_original(id, NULL); set_font_left_boundary(id, NULL); @@ -194,13 +253,13 @@ int tex_new_font(void) set_font_skew_char(id, -1); /*tex allocate eight values including 0 */ tex_set_font_parameters(id, 7); - for (int k = 0; k <= 7; k++) { - tex_set_font_parameter(id, k, 0); + for (int i = 0; i <= 7; i++) { + tex_set_font_parameter(id, i, 0); } /*tex character info zero is reserved for |notdef|. The stack size 1, default item value 0. */ - t->characters = sa_new_tree(1, 4, sa_value); - t->chardata = ci; - t->chardata_size = 1; + tf->characters = sa_new_tree(1, 4, sa_value); + tf->chardata = ci; + tf->chardata_size = 1; return id; } } @@ -208,16 +267,16 @@ int tex_new_font(void) return 0; } -void tex_font_malloc_charinfo(halfword f, int num) +void tex_font_malloc_charinfo(halfword f, int index) { int glyph = lmt_font_state.fonts[f]->chardata_size; - int size = (glyph + num) * sizeof(charinfo); + int size = (glyph + index) * sizeof(charinfo); charinfo *data = lmt_memory_realloc(lmt_font_state.fonts[f]->chardata , (size_t) size); if (data) { - lmt_font_state.font_data.allocated += num * sizeof(charinfo); + lmt_font_state.font_data.allocated += index * sizeof(charinfo); lmt_font_state.fonts[f]->chardata = data; - memset(&data[glyph], 0, (size_t) num * sizeof(charinfo)); - lmt_font_state.fonts[f]->chardata_size += num; + memset(&data[glyph], 0, (size_t) index * sizeof(charinfo)); + lmt_font_state.fonts[f]->chardata_size += index; } else { tex_overflow_error("font", size); } @@ -690,10 +749,8 @@ halfword tex_get_font_identifier(halfword fontspec) } /*tex - Here come some subroutines to deal with expanded fonts. Returning 1 means that they are identical. - */ ligatureinfo tex_get_ligature(halfword f, int lc, int rc) @@ -793,11 +850,7 @@ halfword tex_checked_font_adjust(halfword adjust_spacing, halfword adjust_spacin return adjust_spacing; } -/*tex - - This returns the multiple of |font_step(f)| that is nearest to |e|. - -*/ +/*tex This returns the multiple of |font_step(f)| that is nearest to |e|. */ int tex_fix_expand_value(halfword f, int e) { @@ -936,34 +989,39 @@ halfword tex_get_parameter_glue(quarterword p, quarterword s) /*tex Ligaturing starts here */ -static void tex_aux_nesting_append(halfword nest1, halfword newn) +static void tex_aux_discretionary_append(halfword target, int location, halfword n) { - halfword tail = node_tail(nest1); - tex_couple_nodes(tail ? tail : nest1, newn); - node_tail(nest1) = newn; + halfword node = tex_aux_discretionary_node(target, location); + if (node_tail(node)) { + tex_couple_nodes(node_tail(node), n); + } else { + node_head(node) = n; + } + node_tail(node) = n; } -static void tex_aux_nesting_prepend(halfword nest1, halfword newn) +static void tex_aux_discretionary_prepend(halfword target, int location, halfword n) { - halfword head = node_next(nest1); - tex_couple_nodes(nest1, newn); - if (head) { - tex_couple_nodes(newn, head); + halfword node = tex_aux_discretionary_node(target, location); + if (node_head(node)) { + tex_couple_nodes(n, node_head(node)); } else { - node_tail(nest1) = newn; - } + node_tail(node) = n; + } + node_head(node) = n; } -static void tex_aux_nesting_prepend_list(halfword nest1, halfword newn) +static void tex_aux_nesting_prepend_list(halfword target, int location, halfword n) /* n is prepended to target */ { - halfword head = node_next(nest1); - halfword tail = tex_tail_of_node_list(newn); - tex_couple_nodes(nest1, newn); - if (head) { - tex_couple_nodes(tail, head); - } else { - node_tail(nest1) = tail; + halfword node = tex_aux_discretionary_node(target, location); + halfword copy = tex_copy_node_list(n, null); + halfword tail = tex_tail_of_node_list(copy); + if (node_head(node)) { + tex_couple_nodes(tail, node_head(node)); + } else { + node_tail(node) = tail; } + node_head(node) = copy; } int tex_valid_ligature(halfword left, halfword right, int *slot) @@ -987,7 +1045,9 @@ int tex_valid_ligature(halfword left, halfword right, int *slot) static int tex_aux_found_ligature(halfword left, halfword right) { - if (node_type(left) != glyph_node) { + if (! left || ! right) { + return 0; + } else if (node_type(left) != glyph_node || node_type(right) != glyph_node) { return 0; } else if (glyph_font(left) != glyph_font(right)) { return 0; @@ -999,77 +1059,101 @@ static int tex_aux_found_ligature(halfword left, halfword right) } /*tex - We could be more efficient and reuse the possibly later removed node but it takes more code and - we don't have that many ligatures anyway. + In principle we only support simple ligatures, i.e.\ |move_after|, |keep_right| and |keep_left| + are zero. At some point we might even drop special ones, including those with boundaries because + the likelyhood of encountering these in the \OPENTYPE\ arena is close to zero. */ -static int tex_aux_try_ligature(halfword *first, halfword forward) -{ - halfword cur = *first; - if (glyph_scale(cur) == glyph_scale(forward) && glyph_x_scale(cur) == glyph_x_scale(forward) && glyph_y_scale(cur) == glyph_y_scale(forward)) { - halfword slot; - halfword type = tex_valid_ligature(cur, forward, &slot); - if (type >= 0) { - int move_after = (type & 0x0C) >> 2; - int keep_right = (type & 0x01) != 0; - int keep_left = (type & 0x02) != 0; - halfword parent = (glyph_character(cur) >= 0) ? cur : ((glyph_character(forward) >= 0) ? forward : null); - halfword ligature = tex_new_glyph_node(glyph_ligature_subtype, glyph_font(cur), slot, parent); - if (keep_left) { - tex_couple_nodes(cur, ligature); - if (move_after) { - move_after--; - cur = ligature; - } - } else { - halfword prev = node_prev(cur); - tex_uncouple_node(cur); - tex_flush_node(cur); - tex_couple_nodes(prev, ligature); - cur = ligature; +static int tex_aux_try_ligature(halfword *first, halfword second, halfword *nextone) +{ + halfword current = *first; + halfword slot; + halfword type = tex_valid_ligature(current, second, &slot); + if (type >= 0) { + int move_after = (type & 0x0C) >> 2; + int keep_right = (type & 0x01) != 0; + int keep_left = (type & 0x02) != 0; + halfword next = node_next(second); + if (keep_left && keep_right) { + halfword ligature = tex_copy_node(current); + glyph_character(ligature) = slot; + tex_couple_nodes(*first, ligature); + tex_couple_nodes(ligature, second); + if (nextone) { + *nextone = second; } - if (keep_right) { - tex_couple_nodes(ligature, forward); - if (move_after) { - move_after--; - cur = forward; - } - } else { - halfword next = node_next(forward); - tex_uncouple_node(forward); - tex_flush_node(forward); - if (next) { - tex_couple_nodes(ligature, next); - } + } else if (keep_right) { + glyph_character(*first) = slot; + if (nextone) { + *nextone = second; + } + } else if (keep_left) { + glyph_character(second) = slot; + if (nextone) { + *nextone = second; + } + } else { + glyph_character(*first) = slot; + tex_uncouple_node(second); + tex_flush_node(second); + tex_try_couple_nodes(*first, next); + if (nextone) { + *nextone = *first; + } + } + /* untested */ + if (nextone) { + while (move_after-- > 0 && *nextone) { + *nextone = node_next(*nextone); } - *first = cur; - return 1; } + return 1; + } else { + return 0; } - return 0; } -/*tex - - There shouldn't be any ligatures here - we only add them at the end of |xxx_break| in a |DISC-1 - - DISC-2| situation and we stop processing |DISC-1| (we continue with |DISC-1|'s |post_| and - |no_break|. - -*/ +static void tex_aux_handle_ligature_list(halfword target, int location) +{ + halfword node = tex_aux_discretionary_node(target, location); + halfword head = node_head(node); + halfword tail = node_tail(node); + if (head && head != tail) { + halfword current = head; + while (node_next(current)) { + halfword next = node_next(current); + int ishead = current == head; + halfword nextone = next; + if (tex_aux_same_font_properties(current, next) && tex_aux_try_ligature(¤t, next, &nextone)) { + if (ishead) { + head = current; + node_head(node) = current; + } + current = nextone; + } else { + current = next; + } + } + node_tail(node) = current; + } +} -static halfword tex_aux_handle_ligature_nesting(halfword root, halfword cur) +static void tex_aux_handle_ligature_pair(halfword target, int location) { - if (cur) { - while (node_next(cur)) { - halfword fwd = node_next(cur); - if (node_type(cur) == glyph_node && node_type(fwd) == glyph_node && glyph_font(cur) == glyph_font(fwd) && tex_aux_try_ligature(&cur, fwd)) { - continue; + halfword node = tex_aux_discretionary_node(target, location); + halfword head = node_head(node); + halfword tail = node_tail(node); + if (head && head != tail) { + halfword previous = node_prev(tail); + int ishead = previous == head; + if (tex_aux_same_font_properties(previous, tail) && tex_aux_try_ligature(&previous, tail, NULL)) { + if (ishead) { + head = previous; + node_head(node) = previous; } - cur = node_next(cur); + node_tail(node) = previous; } - node_tail(root) = cur; } - return root; } /*tex @@ -1083,207 +1167,204 @@ static halfword tex_aux_handle_ligature_nesting(halfword root, halfword cur) have (any kind of) discretionaries. It is still on my agenda to look into nested discretionaries i.e. discs nodes in disc fields but it might never result in useable code. + Todo: check this boundary mess (check for subtype too). + Todo: maybe get rid of weird ligatures, turn boundary into space and such. + */ -static halfword tex_aux_handle_ligature_word(halfword cur) +static halfword tex_aux_handle_ligature_word(halfword current) { halfword right = null; - if (node_type(cur) == boundary_node) { - halfword prev = node_prev(cur); - halfword fwd = node_next(cur); + halfword last = null; /* cf LuaTeX patch, an ancient border case buglet. */ + if (node_type(current) == boundary_node) { + halfword previous = node_prev(current); + halfword next = node_next(current); /*tex There is no need to uncouple |cur|, it is freed. */ - tex_flush_node(cur); - if (fwd) { - tex_couple_nodes(prev, fwd); - if (node_type(fwd) != glyph_node) { - return prev; + tex_flush_node(current); + if (next) { + tex_couple_nodes(previous, next); + if (node_type(next) != glyph_node) { + return previous; } else { - cur = fwd; + current = next; } } else { - node_next(prev) = fwd; - return prev; + node_next(previous) = next; + return previous; } - } else if (font_has_left_boundary(glyph_font(cur))) { - halfword prev = node_prev(cur); - halfword p = tex_new_glyph_node(glyph_unset_subtype, glyph_font(cur), left_boundary_char, cur); - tex_couple_nodes(prev, p); - tex_couple_nodes(p, cur); - cur = p; + } else if (node_type(current) == glyph_node && font_has_left_boundary(glyph_font(current))) { + halfword previous = node_prev(current); + halfword glyph = tex_new_glyph_node(glyph_unset_subtype, glyph_font(current), left_boundary_char, current); + tex_couple_nodes(previous, glyph); + tex_couple_nodes(glyph, current); + current = glyph; } - if (font_has_right_boundary(glyph_font(cur))) { - right = tex_new_glyph_node(glyph_unset_subtype, glyph_font(cur), right_boundary_char, cur); + if (node_type(current) == glyph_node && font_has_right_boundary(glyph_font(current))) { + right = tex_new_glyph_node(glyph_unset_subtype, glyph_font(current), right_boundary_char, current); } - /* todo: switch */ + // tex_print_node_list(current, "GOING",max_integer, max_integer); while (1) { - halfword t = node_type(cur); + halfword currenttype = node_type(current); /*tex A glyph followed by \unknown */ - if (t == glyph_node) { - halfword fwd = node_next(cur); - if (fwd) { - t = node_type(fwd); - if (t == glyph_node) { - /*tex a glyph followed by a glyph */ - if (glyph_font(cur) != glyph_font(fwd)) { + if (currenttype == glyph_node) { + if (tex_aux_apply_base_ligaturing(current)) { + halfword forward = node_next(current); + if (forward) { + halfword forwardtype = node_type(forward); + if (forwardtype == glyph_node) { + if (! tex_aux_apply_base_ligaturing(forward)) { + // break; + } else if (! tex_aux_same_font_properties(current, forward)) { + // break; + } else { + halfword nextone = current; + if (tex_aux_try_ligature(¤t, forward, &nextone)) { + current = nextone; + continue; + } + } + } else if (forwardtype == disc_node) { + /*tex a glyph followed by a disc */ + halfword pre = disc_pre_break_head(forward); + halfword replace = disc_no_break_head(forward); + halfword next; + /*tex Check on: |a{b?}{?}{?}| and |a+b=>B| : |{B?}{?}{a?}| */ + /*tex Check on: |a{?}{?}{b?}| and |a+b=>B| : |{a?}{?}{B?}| */ + if (tex_aux_found_ligature(current, pre) || tex_aux_found_ligature(current, replace)) { + /*tex Move |cur| from before disc to skipped part */ + halfword previous = node_prev(current); + tex_uncouple_node(current); + tex_couple_nodes(previous, forward); + tex_aux_discretionary_prepend(forward, no_break_code, current); + tex_aux_discretionary_prepend(forward, pre_break_code, tex_copy_node(current)); + /*tex As we have removed cur, we need to start again. */ + current = previous; + } + /*tex Check on: |a{?}{?}{}b| and |a+b=>B| : |{a?}{?b}{B}|. */ + next = node_next(forward); + if (! replace && tex_aux_found_ligature(current, next)) { + /*tex Move |cur| from before |disc| to |no_break| part. */ + halfword previous = node_prev(current); + halfword tail = node_next(next); + tex_uncouple_node(current); + tex_couple_nodes(previous, forward); + tex_aux_discretionary_prepend(forward, pre_break_code, tex_copy_node(current)); + /*tex Move next from after disc to |no_break| part. */ + tex_uncouple_node(next); + tex_try_couple_nodes(forward, tail); + /*tex We {\em know} this works. */ + tex_couple_nodes(current, next); + /*tex Make sure the list is correct. */ + tex_aux_discretionary_append(forward, post_break_code, tex_copy_node(next)); + /*tex As we have removed cur, we need to start again. */ + current = previous; + } + /*tex We are finished with the |pre_break|. */ + tex_aux_handle_ligature_list(forward, pre_break_code); + } else if (forwardtype == boundary_node) { + halfword next = node_next(forward); + tex_try_couple_nodes(current, next); + tex_flush_node(forward); + if (right) { + /*tex Shame, didn't need it. */ + tex_flush_node(right); + /*tex No need to reset |right|, we're going to leave the loop anyway. */ + } break; - } else if (tex_aux_try_ligature(&cur, fwd)) { + } else if (right) { + tex_couple_nodes(current, right); + tex_couple_nodes(right, forward); + right = null; continue; + } else { + break; } - } else if (t == disc_node) { - /*tex a glyph followed by a disc */ - halfword pre = disc_pre_break_head(fwd); - halfword nob = disc_no_break_head(fwd); - halfword next, tail; - /*tex Check on: |a{b?}{?}{?}| and |a+b=>B| : |{B?}{?}{a?}| */ - /*tex Check on: |a{?}{?}{b?}| and |a+b=>B| : |{a?}{?}{B?}| */ - if ((pre && node_type(pre) == glyph_node && tex_aux_found_ligature(cur, pre)) - || (nob && node_type(nob) == glyph_node && tex_aux_found_ligature(cur, nob))) { - /*tex Move |cur| from before disc to skipped part */ - halfword prev = node_prev(cur); - tex_uncouple_node(cur); - tex_couple_nodes(prev, fwd); - tex_aux_nesting_prepend(disc_no_break(fwd), cur); - /*tex Now ligature the |pre_break|. */ - tex_aux_nesting_prepend(disc_pre_break(fwd), tex_copy_node(cur)); - /*tex As we have removed cur, we need to start again. */ - cur = prev; - } - /*tex Check on: |a{?}{?}{}b| and |a+b=>B| : |{a?}{?b}{B}|. */ - next = node_next(fwd); - if ((! nob) && next && node_type(next) == glyph_node && tex_aux_found_ligature(cur, next)) { - /*tex Move |cur| from before |disc| to |no_break| part. */ - halfword prev = node_prev(cur); - tex_uncouple_node(cur); - tex_couple_nodes(prev, fwd); - /*tex We {\em know} it's empty. */ - tex_couple_nodes(disc_no_break(fwd), cur); - /*tex Now copy |cur| the |pre_break|. */ - tex_aux_nesting_prepend(disc_pre_break(fwd), tex_copy_node(cur)); - /*tex Move next from after disc to |no_break| part. */ - tail = node_next(next); - tex_uncouple_node(next); - tex_try_couple_nodes(fwd, tail); - /*tex We {\em know} this works. */ - tex_couple_nodes(cur, next); - /*tex Make sure the list is correct. */ - disc_no_break_tail(fwd) = next; - /*tex Now copy next to the |post_break|. */ - tex_aux_nesting_append(disc_post_break(fwd), tex_copy_node(next)); - /*tex As we have removed cur, we need to start again. */ - cur = prev; - } - /*tex We are finished with the |pre_break|. */ - tex_aux_handle_ligature_nesting(disc_pre_break(fwd), disc_pre_break_head(fwd)); - } else if (t == boundary_node) { - halfword next = node_next(fwd); - tex_try_couple_nodes(cur, next); - tex_flush_node(fwd); + } else { + /*tex The last character of a paragraph. */ if (right) { - /*tex Shame, didn't need it. */ - tex_flush_node(right); - /*tex No need to reset |right|, we're going to leave the loop anyway. */ + /*tex |par| prohibits the use of |couple_nodes| here. */ + tex_try_couple_nodes(current, right); + right = null; + continue; + } else { + break; } - break; - } else if (right) { - tex_couple_nodes(cur, right); - tex_couple_nodes(right, fwd); - right = null; - continue; - } else { - break; - } - } else { - /*tex The last character of a paragraph. */ - if (right) { - /*tex |par| prohibits the use of |couple_nodes| here. */ - tex_try_couple_nodes(cur, right); - right = null; - continue; - } else { - break; } + /*tex A discretionary followed by \unknown */ } - /*tex A discretionary followed by \unknown */ - } else if (t == disc_node) { + } else if (currenttype == disc_node) { /*tex If |{?}{x}{?}| or |{?}{?}{y}| then: */ - if (disc_no_break_head(cur) || disc_post_break_head(cur)) { - halfword fwd; - if (disc_post_break_head(cur)) { - tex_aux_handle_ligature_nesting(disc_post_break(cur), disc_post_break_head(cur)); + if (disc_no_break_head(current) || disc_post_break_head(current)) { + /*tex Is this nesting okay (and needed)? */ + halfword forward; + if (disc_post_break_head(current)) { + tex_aux_handle_ligature_list(current, post_break_code); } - if (disc_no_break_head(cur)) { - tex_aux_handle_ligature_nesting(disc_no_break(cur), disc_no_break_head(cur)); + if (disc_no_break_head(current)) { + tex_aux_handle_ligature_list(current, no_break_code); } - fwd = node_next(cur); - while (fwd) { - if (node_type(fwd) == glyph_node) { - halfword nob = disc_no_break_tail(cur); - halfword pst = disc_post_break_tail(cur); - if ((! nob || ! tex_aux_found_ligature(nob, fwd)) && (! pst || ! tex_aux_found_ligature(pst, fwd))) { - break; - } else { - halfword next = node_next(fwd); - tex_aux_nesting_append(disc_no_break(cur), tex_copy_node(fwd)); - tex_aux_handle_ligature_nesting(disc_no_break(cur), nob); - tex_uncouple_node(fwd); - tex_try_couple_nodes(cur, next); - tex_aux_nesting_append(disc_post_break(cur), fwd); - tex_aux_handle_ligature_nesting(disc_post_break(cur), pst); - fwd = node_next(cur); - } + forward = node_next(current); + while (forward && node_type(forward) == glyph_node && tex_aux_apply_base_ligaturing(forward)) { + halfword replace = disc_no_break_tail(current); + halfword post = disc_post_break_tail(current); + if (tex_aux_found_ligature(replace, forward) || tex_aux_found_ligature(post, forward)) { + tex_try_couple_nodes(current, node_next(forward)); + tex_uncouple_node(forward); + tex_aux_discretionary_append(current, no_break_code, tex_copy_node(forward)); + tex_aux_handle_ligature_pair(current, no_break_code); + tex_aux_handle_ligature_pair(current, post_break_code); + forward = node_next(current); } else { break; } } - if (fwd && node_type(fwd) == disc_node) { + if (forward && node_type(forward) == disc_node) { /*tex This only deals with simple pre-only discretionaries and a following glyph. */ - halfword next = node_next(fwd); + halfword next = node_next(forward); if (next - && ! disc_no_break_head(fwd) - && ! disc_post_break_head(fwd) + && ! disc_no_break_head(forward) + && ! disc_post_break_head(forward) && node_type(next) == glyph_node - && ((disc_post_break_tail(cur) && tex_aux_found_ligature(disc_post_break_tail(cur), next)) || - (disc_no_break_tail (cur) && tex_aux_found_ligature(disc_no_break_tail (cur), next)))) { + && tex_aux_apply_base_ligaturing(next) + && ((disc_post_break_tail(current) && tex_aux_found_ligature(disc_post_break_tail(current), next)) || + (disc_no_break_tail (current) && tex_aux_found_ligature(disc_no_break_tail (current), next)))) { halfword last = node_next(next); tex_uncouple_node(next); - tex_try_couple_nodes(fwd, last); + tex_try_couple_nodes(forward, last); /*tex Just a hidden flag, used for (base mode) experiments. */ if (hyphenation_permitted(hyphenation_mode_par, lazy_ligatures_hyphenation_mode)) { /*tex f-f-i -> f-fi */ - halfword tail = disc_no_break_tail(cur); - tex_aux_nesting_append(disc_no_break(cur), tex_copy_node(next)); - tex_aux_handle_ligature_nesting(disc_no_break(cur), tail); - tail = disc_post_break_tail(cur); - tex_aux_nesting_append(disc_post_break(cur), next); - tex_aux_handle_ligature_nesting(disc_post_break(cur), tail); - tex_try_couple_nodes(node_prev(fwd), node_next(fwd)); - tex_flush_node(fwd); + tex_aux_discretionary_append(current, no_break_code, tex_copy_node(next)); + tex_aux_handle_ligature_pair(current,no_break_code); + tex_aux_discretionary_append(current, post_break_code, next); + tex_aux_handle_ligature_pair(current,post_break_code); + tex_try_couple_nodes(node_prev(forward), node_next(forward)); + tex_flush_node(forward); } else { /*tex f-f-i -> ff-i : |{a-}{b}{AB} {-}{c}{}| => |{AB-}{c}{ABc}| */ - tex_aux_nesting_append(disc_post_break(fwd), tex_copy_node(next)); - if (disc_no_break_head(cur)) { - halfword tail; - tex_aux_nesting_prepend_list(disc_no_break(fwd), tex_copy_node_list(disc_no_break_head(cur), null)); - tail = disc_no_break_tail(fwd); - tex_aux_nesting_append(disc_no_break(fwd), next); - tex_aux_handle_ligature_nesting(disc_no_break(fwd), tail); - tex_aux_nesting_prepend_list(disc_pre_break(fwd), tex_copy_node_list(disc_no_break_head(cur), null)); + tex_aux_discretionary_append(forward, post_break_code, tex_copy_node(next)); + if (disc_no_break_head(current)) { + tex_aux_nesting_prepend_list(forward, no_break_code, disc_no_break_head(current)); + tex_aux_discretionary_append(forward, no_break_code, next); + tex_aux_handle_ligature_pair(forward, no_break_code); + tex_aux_nesting_prepend_list(forward, pre_break_code, disc_no_break_head(current)); } - tex_try_couple_nodes(node_prev(cur), node_next(cur)); - tex_flush_node(cur); - cur = fwd; + tex_try_couple_nodes(node_prev(current), node_next(current)); + tex_flush_node(current); + current = forward; } } } } } else { /*tex We have glyph nor disc. */ - return cur; + return last; } /*tex Goto the next node, where |\par| allows |node_next(cur)| to be NULL. */ - cur = node_next(cur); + last = current; + current = node_next(current); } - return cur; + return current; } /*tex The return value is the new tail, head should be a dummy: */ @@ -1293,27 +1374,35 @@ halfword tex_handle_ligaturing(halfword head, halfword tail) if (node_next(head)) { /*tex A trick to allow explicit |node == null| tests. */ halfword save_tail = null; - halfword cur, prev; + halfword current, previous; if (tail) { save_tail = node_next(tail); node_next(tail) = null; } - prev = head; - cur = node_next(prev); - while (cur) { - if (node_type(cur) == glyph_node || node_type(cur) == boundary_node) { - cur = tex_aux_handle_ligature_word(cur); + previous = head; + current = node_next(previous); + while (current) { + switch(node_type(current)) { + case glyph_node: + if (tex_aux_apply_base_ligaturing(current)) { + current = tex_aux_handle_ligature_word(current); + } + break; + case disc_node: + case boundary_node: + current = tex_aux_handle_ligature_word(current); + break; + } + previous = current; + if (current) { + current = node_next(current); } - prev = cur; - cur = node_next(cur); } - if (! prev) { - prev = tail; + if (! previous) { + previous = tex_tail_of_node_list(head); } - tex_try_couple_nodes(prev, save_tail); - // if (tail) { - // } - return prev; + tex_try_couple_nodes(previous, save_tail); + return previous; } else { return tail; } @@ -1321,13 +1410,9 @@ halfword tex_handle_ligaturing(halfword head, halfword tail) /*tex Kerning starts here: */ -static void tex_aux_add_kern_before(halfword left, halfword right) +static halfword tex_aux_add_kern_before(halfword left, halfword right) { - if ( - glyph_font(left) == glyph_font(right) && - glyph_scale(left) == glyph_scale(right) && - glyph_x_scale(left) == glyph_x_scale(right) && - glyph_y_scale(left) == glyph_y_scale(right) && + if (tex_aux_same_font_properties(left, right) && ! tex_has_glyph_option(left, glyph_option_no_right_kern) && ! tex_has_glyph_option(right, glyph_option_no_left_kern) && tex_has_kern(glyph_font(left), glyph_character(left)) @@ -1335,21 +1420,19 @@ static void tex_aux_add_kern_before(halfword left, halfword right) scaled k = tex_raw_get_kern(glyph_font(left), glyph_character(left), glyph_character(right)); if (k) { scaled kern = tex_new_kern_node(k, font_kern_subtype); - halfword prev = node_prev(right); - tex_couple_nodes(prev, kern); + halfword previous = node_prev(right); + tex_couple_nodes(previous, kern); tex_couple_nodes(kern, right); tex_attach_attribute_list_copy(kern, left); + return kern; } } + return null; } -static void tex_aux_add_kern_after(halfword left, halfword right, halfword aft) +static halfword tex_aux_add_kern_after(halfword left, halfword right, halfword after) { - if ( - glyph_font(left) == glyph_font(right) && - glyph_scale(left) == glyph_scale(right) && - glyph_x_scale(left) == glyph_x_scale(right) && - glyph_y_scale(left) == glyph_y_scale(right) && + if (tex_aux_same_font_properties(left, right) && ! tex_has_glyph_option(left, glyph_option_no_right_kern) && ! tex_has_glyph_option(right, glyph_option_no_left_kern) && tex_has_kern(glyph_font(left), glyph_character(left)) @@ -1357,78 +1440,96 @@ static void tex_aux_add_kern_after(halfword left, halfword right, halfword aft) scaled k = tex_raw_get_kern(glyph_font(left), glyph_character(left), glyph_character(right)); if (k) { scaled kern = tex_new_kern_node(k, font_kern_subtype); - halfword next = node_next(aft); - tex_couple_nodes(aft, kern); + halfword next = node_next(after); + tex_couple_nodes(after, kern); tex_try_couple_nodes(kern, next); - tex_attach_attribute_list_copy(kern, aft); + tex_attach_attribute_list_copy(kern, after); + return kern; } } + return null; } -static void tex_aux_do_handle_kerning(halfword root, halfword init_left, halfword init_right) +static halfword tex_aux_do_handle_kerning(halfword root, halfword init_left, halfword init_right); + +static void tex_aux_handle_discretionary_kerning(halfword target, int location, halfword left, halfword right) { - halfword cur = node_next(root); - if (cur) { + halfword node = tex_aux_discretionary_node(target, location); + if (node_head(node)) { + halfword kern = tex_aux_do_handle_kerning(node_head(node), left, right); + if (kern) { + node_head(node) = kern; + node_tail(node) = tex_tail_of_node_list(node_head(node)); + } + } +} + +static halfword tex_aux_do_handle_kerning(halfword root, halfword init_left, halfword init_right) +{ + // halfword head = node_next(root); // todo: get rid of this one + halfword head = root; // todo: get rid of this one + halfword current = head; + halfword initial = null; + if (current) { halfword left = null; - if (node_type(cur) == glyph_node) { + if (node_type(current) == glyph_node && tex_aux_apply_base_kerning(current)) { if (init_left) { - tex_aux_add_kern_before(init_left, cur); + halfword kern = tex_aux_add_kern_before(init_left, current); + if (current == head) { + initial = kern; + } } - left = cur; + left = current; } - cur = node_next(cur); - while (cur) { - halfword t = node_type(cur); - if (t == glyph_node) { - if (left) { - tex_aux_add_kern_before(left, cur); - if (glyph_character(left) < 0) { - halfword prev = node_prev(left); - tex_couple_nodes(prev, cur); - tex_flush_node(left); + current = node_next(current); + while (current) { + halfword currenttype = node_type(current); + if (currenttype == glyph_node) { + if (tex_aux_apply_base_kerning(current)) { + if (left) { + tex_aux_add_kern_before(left, current); + if (glyph_character(left) < 0) { + halfword previous = node_prev(left); + tex_couple_nodes(previous, current); + tex_flush_node(left); + } } + left = current; + } else { + left = null; } - left = cur; } else { - if (t == disc_node) { - halfword right = node_type(node_next(cur)) == glyph_node ? node_next(cur) : null; - tex_aux_do_handle_kerning(disc_pre_break(cur), left, null); - if (disc_pre_break_head(cur)) { - disc_pre_break_tail(cur) = tex_tail_of_node_list(disc_pre_break_head(cur)); - } - tex_aux_do_handle_kerning(disc_post_break(cur), null, right); - if (disc_post_break_head(cur)) { - disc_post_break_tail(cur) = tex_tail_of_node_list(disc_post_break_head(cur)); - } - tex_aux_do_handle_kerning(disc_no_break(cur), left, right); - if (disc_no_break_head(cur)) { - disc_no_break_tail(cur) = tex_tail_of_node_list(disc_no_break_head(cur)); - } + if (currenttype == disc_node) { + halfword next = node_next(current); + halfword right = node_type(next) == glyph_node && tex_aux_apply_base_kerning(next) ? next : null; + tex_aux_handle_discretionary_kerning(current, pre_break_code, left, null); + tex_aux_handle_discretionary_kerning(current, post_break_code, null, right); + tex_aux_handle_discretionary_kerning(current, no_break_code, left, right); } if (left) { - if (glyph_character(left) < 0) { - halfword prev = node_prev(left); - tex_couple_nodes(prev, cur); + if (glyph_character(left) < 0) { /* boundary ? */ + halfword previous = node_prev(left); + tex_couple_nodes(previous, current); tex_flush_node(left); } left = null; } } - cur = node_next(cur); + current = node_next(current); } if (left) { if (init_right) { tex_aux_add_kern_after(left, init_right, left); } if (glyph_character(left) < 0) { - halfword prev = node_prev(left); + halfword previous = node_prev(left); halfword next = node_next(left); if (next) { - tex_couple_nodes(prev, next); + tex_couple_nodes(previous, next); node_tail(root) = next; - } else if (prev != root) { - node_next(prev) = null; - node_tail(root) = prev; + } else if (previous != root) { + node_next(previous) = null; + node_tail(root) = previous; } else { node_next(root) = null; node_tail(root) = null; @@ -1440,6 +1541,7 @@ static void tex_aux_do_handle_kerning(halfword root, halfword init_left, halfwor tex_aux_add_kern_after(init_left, init_right, root); node_tail(root) = node_next(root); } + return initial; } halfword tex_handle_kerning(halfword head, halfword tail) @@ -1449,7 +1551,7 @@ halfword tex_handle_kerning(halfword head, halfword tail) save_link = node_next(tail); node_next(tail) = null; node_tail(head) = tail; - tex_aux_do_handle_kerning(head, null, null); + tex_aux_do_handle_kerning(node_next(head), null, null); /*tex There is no need to check initial here. */ tail = node_tail(head); if (tex_valid_node(save_link)) { /* no need for check */ @@ -1457,7 +1559,7 @@ halfword tex_handle_kerning(halfword head, halfword tail) } } else { node_tail(head) = null; - tex_aux_do_handle_kerning(head, null, null); + tex_aux_do_handle_kerning(node_next(head), null, null); /*tex There is no need to check initial here. */ } return tail; } @@ -1494,19 +1596,17 @@ halfword tex_handle_glyphrun(halfword head, halfword group, halfword direction) if (callback_id) { head = tex_aux_run_lua_ligkern_callback(lmt_lua_state.lua_instance, head, group, direction, callback_id); } else { + // what if disc at start tex_handle_ligaturing(head, null); } callback_id = lmt_callback_defined(kerning_callback); if (callback_id) { head = tex_aux_run_lua_ligkern_callback(lmt_lua_state.lua_instance, head, group, direction, callback_id); } else { - halfword nest = tex_new_node(nesting_node, unset_nesting_code); - tex_couple_nodes(nest, head); - tex_aux_do_handle_kerning(nest, null, null); - head = node_next(nest); - node_prev(head) = null; - node_next(nest) = null; - tex_flush_node(nest); + halfword kern = tex_aux_do_handle_kerning(head, null, null); + if (kern) { + head = kern; + } } } } @@ -1534,10 +1634,8 @@ void tex_set_cur_font(halfword g, halfword f) } /*tex - Because we do fonts in \LUA\ we can decide to drop this one and assume a definition using the token scanner. It also avoids the filename (split) mess. - */ int tex_tex_def_font(int a) @@ -1555,7 +1653,7 @@ int tex_tex_def_font(int a) /*tex Stated 'at' size, or negative of scaled magnification. */ scaled s = -1000; char *fn; - /*tex Here |a| detemines if we define global or not. */ + /*tex Here |a| determines if we define global or not. */ if (is_global(a)) { update_tex_font_global(u, null_font); } else { @@ -1603,11 +1701,9 @@ int tex_tex_def_font(int a) } /*tex - When \TEX\ wants to typeset a character that doesn't exist, the character node is not created; thus the output routine can assume that characters exist when it sees them. The following procedure prints a warning message unless the user has suppressed it. - */ void tex_char_warning(halfword f, int c) diff --git a/source/luametatex/source/tex/texfont.h b/source/luametatex/source/tex/texfont.h index 994cdd894..947cdf446 100644 --- a/source/luametatex/source/tex/texfont.h +++ b/source/luametatex/source/tex/texfont.h @@ -324,6 +324,9 @@ extern font_state_info lmt_font_state; typedef enum text_control_codes { text_control_collapse_hyphens = 0x00001, + text_control_base_ligaturing = 0x00002, + text_control_base_kerning = 0x00004, + text_control_none_protected = 0x00008, } text_control_codes; # define has_font_text_control(f,c) ((font_textcontrol(f) & c) == c) diff --git a/source/luametatex/source/tex/texinputstack.h b/source/luametatex/source/tex/texinputstack.h index 7ae677d56..51ff2ef56 100644 --- a/source/luametatex/source/tex/texinputstack.h +++ b/source/luametatex/source/tex/texinputstack.h @@ -68,12 +68,12 @@ typedef struct input_file_state_info { extern input_file_state_info input_file_state; -static inline int input_file_value(void) +inline static int input_file_value(void) { return input_file_state.forced_file ? input_file_state.forced_file : lmt_input_state.cur_input.state_file; } -static inline int input_line_value(void) +inline static int input_line_value(void) { return input_file_state.forced_line ? input_file_state.forced_line : (input_file_state.line ? input_file_state.line : lmt_input_state.input_line); } diff --git a/source/luametatex/source/tex/texinserts.c b/source/luametatex/source/tex/texinserts.c index dbd164926..3d634dd39 100644 --- a/source/luametatex/source/tex/texinserts.c +++ b/source/luametatex/source/tex/texinserts.c @@ -155,7 +155,7 @@ halfword tex_get_insert_distance(halfword i) } } -static inline halfword tex_aux_insert_box(halfword i) +inline static halfword tex_aux_insert_box(halfword i) { if (tex_valid_insert_id(i)) { return lmt_insert_state.mode == index_insert_mode ? insert_content(i) : lmt_insert_state.inserts[i].content; diff --git a/source/luametatex/source/tex/texlinebreak.c b/source/luametatex/source/tex/texlinebreak.c index 2e0c945a7..7c9e6b768 100644 --- a/source/luametatex/source/tex/texlinebreak.c +++ b/source/luametatex/source/tex/texlinebreak.c @@ -1372,7 +1372,7 @@ halfword tex_badness(scaled t, scaled s) } } -static inline void tex_split_line_break_criterium(halfword criterium, halfword *semi_tight, halfword *decent, halfword *semi_loose, halfword *loose) { +inline static void tex_split_line_break_criterium(halfword criterium, halfword *semi_tight, halfword *decent, halfword *semi_loose, halfword *loose) { *semi_tight = (criterium >> 24) & 0x7F; *decent = (criterium >> 16) & 0x7F; *semi_loose = (criterium >> 8) & 0x7F; @@ -1391,7 +1391,7 @@ static inline void tex_split_line_break_criterium(halfword criterium, halfword * } } -static inline halfword tex_normalized_loose_badness(halfword b, halfword loose, halfword semi_loose, halfword decent) +inline static halfword tex_normalized_loose_badness(halfword b, halfword loose, halfword semi_loose, halfword decent) { // if (b > loose_criterium) { // return very_loose_fit; @@ -1411,7 +1411,7 @@ static inline halfword tex_normalized_loose_badness(halfword b, halfword loose, } } -static inline halfword tex_normalized_tight_badness(halfword b, halfword decent, halfword semi_tight) +inline static halfword tex_normalized_tight_badness(halfword b, halfword decent, halfword semi_tight) { // if (b > decent_criterium) { // return tight_fit; diff --git a/source/luametatex/source/tex/texmaincontrol.c b/source/luametatex/source/tex/texmaincontrol.c index b0a9d2a4b..6de36d7ee 100644 --- a/source/luametatex/source/tex/texmaincontrol.c +++ b/source/luametatex/source/tex/texmaincontrol.c @@ -68,7 +68,7 @@ main_control_state_info lmt_main_control_state = { A few helpers: */ -inline scaled tex_aux_checked_dimen1(scaled v) +inline static scaled tex_aux_checked_dimen1(scaled v) { if (v > max_dimen) { return max_dimen; @@ -79,7 +79,7 @@ inline scaled tex_aux_checked_dimen1(scaled v) } } -inline scaled tex_aux_checked_dimen2(scaled v) +inline static scaled tex_aux_checked_dimen2(scaled v) { if (v > max_dimen) { return max_dimen; @@ -475,6 +475,25 @@ static void tex_aux_run_relax(void) { return; } +static void tex_aux_run_active(void) { +// if (lmt_input_state.scanner_status == scanner_is_tolerant || lmt_input_state.scanner_status == scanner_is_matching) { +// cur_cs = tex_active_to_cs(cur_chr, ! lmt_hash_state.no_new_cs); +// cur_cmd = eq_type(cur_cs); +// cur_chr = eq_value(cur_cs); +// tex_x_token(); +// } else + if ((cur_mode == mmode || lmt_nest_state.math_mode) && tex_check_active_math_char(cur_chr)) { + /*tex We have an intercept. */ + tex_back_input(cur_tok); + } else { + cur_cs = tex_active_to_cs(cur_chr, ! lmt_hash_state.no_new_cs); + cur_cmd = eq_type(cur_cs); + cur_chr = eq_value(cur_cs); + tex_x_token(); + tex_back_input(cur_tok); + } +} + /*tex |ignore_spaces| is a special case: after it has acted, |get_x_token| has already fetched the @@ -1664,7 +1683,7 @@ void tex_local_control(int obeymode) tex_unsave_full_scanner_status(saved_full_status); } -inline int tex_aux_is_iterator_value(halfword tokeninfo) +inline static int tex_aux_is_iterator_value(halfword tokeninfo) { if (tokeninfo >= cs_token_flag) { halfword cs = tokeninfo - cs_token_flag; @@ -3543,7 +3562,7 @@ inline static void tex_aux_update_register(int a, int level, halfword index, hal if ((register_attribute_number(index)) > lmt_node_memory_state.max_used_attribute) { lmt_node_memory_state.max_used_attribute = register_attribute_number(index); } - change_attribute_register(a, index, value); + tex_change_attribute_register(a, index, value); tex_word_define(a, index, value); break; case dimen_val_level: @@ -4497,9 +4516,9 @@ static void tex_aux_set_let(int a, int force) case let_charcode_code: /*tex |\letcharcode| (todo: protection) */ { - halfword v = tex_scan_int(0, NULL); - if (v > 0) { - p = tex_active_to_cs(v, 1); + halfword character = tex_scan_int(0, NULL); + if (character > 0) { + p = tex_active_to_cs(character, 1); do { tex_get_token(); } while (cur_cmd == spacer_cmd); @@ -4773,6 +4792,13 @@ static void tex_aux_set_define_char_code(int a) /* maybe make |a| already a bool tex_set_hm_code(chr, val, global_or_local(a)); } break; + case amcode_charcode: + { + halfword chr = tex_scan_char_number(0); + halfword val = tex_scan_category_code(1); + tex_set_am_code(chr, val, global_or_local(a)); + } + break; case mathcode_charcode: tex_scan_extdef_math_code((is_global(a)) ? level_one: cur_level, tex_mathcode); break; @@ -5181,7 +5207,7 @@ static void tex_aux_set_internal_attr(int a) if (internal_attribute_number(p) > lmt_node_memory_state.max_used_attribute) { lmt_node_memory_state.max_used_attribute = internal_attribute_number(p); } - change_attribute_register(a, p, v); + tex_change_attribute_register(a, p, v); tex_word_define(a, p, v); } @@ -5192,7 +5218,7 @@ static void tex_aux_set_register_attr(int a) if (register_attribute_number(p) > lmt_node_memory_state.max_used_attribute) { lmt_node_memory_state.max_used_attribute = register_attribute_number(p); } - change_attribute_register(a, p, v); + tex_change_attribute_register(a, p, v); tex_word_define(a, p, v); } @@ -5488,6 +5514,15 @@ void tex_get_r_token(void) tex_get_token(); } while (cur_tok == space_token); if (eqtb_invalid_cs(cur_cs)) { + if (cur_cmd == active_char_cmd) { + cur_cs = tex_active_to_cs(cur_chr, 1); + cur_cmd = eq_type(cur_cs); + cur_chr = eq_value(cur_cs); + // tex_x_token(); + // if (! eqtb_invalid_cs(cur_cs)) { + return; + // } + } if (cur_cs == 0) { tex_back_input(cur_tok); } @@ -5777,7 +5812,7 @@ void tex_assign_internal_attribute_value(int a, halfword p, int val) if (register_attribute_number(p) > lmt_node_memory_state.max_used_attribute) { lmt_node_memory_state.max_used_attribute = register_attribute_number(p); } - change_attribute_register(a, p, val); + tex_change_attribute_register(a, p, val); tex_word_define(a, p, val); } @@ -6349,6 +6384,8 @@ inline static void tex_aux_big_switch(int mode, int cmd) register_runner(ignore_cmd, tex_aux_run_relax, tex_aux_run_relax, tex_aux_run_relax); + register_runner(active_char_cmd, tex_aux_run_active, tex_aux_run_active, tex_aux_run_active); + /*tex The next is unlikely to happen but compilers like the check. */ # if (main_control_mode == 0) diff --git a/source/luametatex/source/tex/texmath.c b/source/luametatex/source/tex/texmath.c index f650704cd..1424e5e03 100644 --- a/source/luametatex/source/tex/texmath.c +++ b/source/luametatex/source/tex/texmath.c @@ -138,7 +138,7 @@ static void tex_aux_math_math_component (halfword n, int append); inline static mathdictval tex_fake_math_dict(halfword chr) { - mathdictval d = { 0, 0, 0 }; + mathdictval d = tex_no_dict_code(); if (math_dict_properties_par || math_dict_group_par) { d.properties = (unsigned short) math_dict_properties_par; d.group = (unsigned short) math_dict_group_par; @@ -957,7 +957,7 @@ int tex_show_math_node(halfword n, int threshold, int max) return 1; } -inline halfword tex_aux_valid_delimiter(halfword d) +inline static halfword tex_aux_valid_delimiter(halfword d) { return (d && (delimiter_small_family(d) || delimiter_small_character(d) || delimiter_large_family(d) || delimiter_large_character(d))) ? d : null; } @@ -1040,6 +1040,9 @@ static void tex_aux_display_radical_noad(halfword n, int threshold, int max) if (radical_depth(n)) { tex_print_format(", depth %D", radical_depth(n), pt_unit); } + if (radical_size(n)) { + tex_print_format(", size %i", radical_size(n)); + } if (noad_source(n) != 0) { tex_print_format(", source %i", noad_source(n)); } @@ -1246,7 +1249,9 @@ void tex_run_math_initialize(void) switch(cur_cmd) { case math_shift_cmd: /*tex |get_x_token| would fail on |\ifmmode|! */ + lmt_nest_state.math_mode = 1; tex_get_token(); + lmt_nest_state.math_mode = 0; if (cur_cmd == math_shift_cmd && cur_list.mode > nomode) { tex_aux_enter_display_math(math_shift_cmd); } else { @@ -1513,7 +1518,7 @@ void tex_scan_extdef_del_code(int level, int extcode) mathdictval tex_scan_mathdict(void) { - mathdictval d = { 0, 0, 0 }; /* use this one directly */ + mathdictval d = tex_no_dict_code(); /* use this one directly */ d.properties = (unsigned short) tex_scan_math_properties_number(); d.group = (unsigned short) tex_scan_math_group_number(); d.index = (unsigned int) tex_scan_math_index_number(); @@ -1522,7 +1527,7 @@ mathdictval tex_scan_mathdict(void) mathcodeval tex_scan_mathchar(int extcode) { - mathcodeval d = { 0, 0, 0 }; /* use this one directly */ + mathcodeval d = tex_no_math_code(); /* use this one directly */ switch (extcode) { case tex_mathcode: /*tex |"<4bits><4bits><8bits>| */ @@ -1600,7 +1605,7 @@ halfword tex_new_math_dict_spec(mathdictval d, mathcodeval m, quarterword code) mathcodeval tex_get_math_spec(halfword s) { - mathcodeval m = { 0, 0, 0 }; + mathcodeval m = tex_no_math_code(); if (s) { m.class_value = math_spec_class(s); m.family_value = math_spec_family(s); @@ -1611,7 +1616,7 @@ mathcodeval tex_get_math_spec(halfword s) mathdictval tex_get_math_dict(halfword s) { - mathdictval d = { 0, 0, 0 }; + mathdictval d = tex_no_dict_code(); if (s) { d.properties = math_spec_properties(s); d.group = math_spec_group(s); @@ -1666,19 +1671,114 @@ mathcodeval tex_scan_delimiter_as_mathchar(int extcode) For some reason |$\char44$| gives an undefined |$| when we made that character active in math. */ -static void tex_aux_scan_active_math_char(void) +static void tex_aux_report_active(int where, const char *what, int code, int character) +{ + tex_begin_diagnostic(); + tex_print_format("[active: location %i, %s, code %i, char %i]",where, what, code, character); + tex_end_diagnostic(); +} + +static void tex_aux_append_math_char(mathcodeval mval, mathdictval dval, int automatic); + +int tex_check_active_math_char(int character) +{ + halfword code = tex_get_am_code(character); + if (code) { + switch (code) { + case alignment_tab_cmd: + case superscript_cmd: + case subscript_cmd: + cur_cmd = code; + cur_chr = character; + cur_tok = token_val(cur_cmd, cur_chr); + if (tracing_commands_par >= 4) { + tex_aux_report_active(4, "control", code, character); + } + return 1; + case letter_cmd: + case other_char_cmd: + cur_cmd = code; + cur_chr = character; + cur_tok = token_val(cur_cmd, cur_chr); + if (tracing_commands_par >= 4) { + tex_aux_report_active(4, "inject", code, character); + } + return 1; + default: + if (tracing_commands_par >= 4) { + tex_aux_report_active(4, "ignore", code, character); + } + return 1; + } + } else { + return 0; + } +} + +int tex_pass_active_math_char(int character) { - cur_cs = tex_active_to_cs(cur_chr, 1); - cur_cmd = eq_type(cur_cs); - cur_chr = eq_value(cur_cs); - tex_x_token(); - tex_back_input(cur_tok); + halfword code = tex_get_am_code(character); + if (code) { + return 1; + } else { + return 0; + } +} + +static int tex_aux_scan_active_math_char(mathcodeval *mval, int where) +{ + halfword character = mval->character_value; + halfword code = tex_get_am_code(character); + if (code) { + switch (code) { + case alignment_tab_cmd: + case superscript_cmd: + case subscript_cmd: + cur_cmd = code; + cur_chr = character; + cur_tok = token_val(cur_cmd, cur_chr); + tex_back_input(cur_tok); + if (tracing_commands_par >= 4) { + tex_aux_report_active(where, "control", code, character); + } + return 1; + case letter_cmd: + case other_char_cmd: + cur_cmd = code; + cur_chr = character; + cur_tok = token_val(cur_cmd, cur_chr); + if (tracing_commands_par >= 4) { + tex_aux_report_active(where, "inject", code, character); + } + return 0; + default: + if (tracing_commands_par >= 4) { + tex_aux_report_active(where, "ignore", code, character); + } + return 1; + } + } else if (mval->class_value == active_math_class_value) { + cur_cs = tex_active_to_cs(cur_chr, 1); + cur_cmd = eq_type(cur_cs); + cur_chr = eq_value(cur_cs); + tex_x_token(); + tex_back_input(cur_tok); + if (tracing_commands_par >= 4) { + tex_aux_report_active(where, "active", code, character); + } + return 1; + } else { + // if (tracing_commands_par >= 4) { + // tex_aux_report_active(where, "keep", code, mval->character_value); + // } + return 0; + } } static int tex_aux_scan_math(halfword target, halfword style, int usetextfont, halfword toks, halfword toks_text, int nocomponent, halfword cls, halfword all) { - mathcodeval mval = { 0, 0, 0 }; - mathdictval dval = { 0, 0, 0 }; + mathcodeval mval = tex_no_math_code(); + mathdictval dval = tex_no_dict_code(); lmt_math_state.last_atom = cls; RESTART: do { @@ -1694,13 +1794,12 @@ static int tex_aux_scan_math(halfword target, halfword style, int usetextfont, h case other_char_cmd: case char_given_cmd: mval = tex_get_math_code(cur_chr); - if (mval.class_value == active_math_class_value) { - /*tex An active character is allowed here. */ - tex_aux_scan_active_math_char(); - goto RESTART; + if (tex_aux_scan_active_math_char(&mval, 1)) { + goto RESTART; /* rescan pushed back token */ + } else { + dval = tex_fake_math_dict(mval.character_value); + break; } - dval = tex_fake_math_dict(mval.character_value); - break; // case char_number_cmd: // /* The |\glyph| variant is accepted but no keywords here. */ // cur_chr = tex_scan_char_number(); @@ -1875,18 +1974,16 @@ static void tex_aux_append_math_fence_val(mathcodeval mval, mathdictval dval, qu /* todo : share the next three with the regular fences */ noad_options(fence) |= noad_option_no_check; if (class == middle_noad_subtype && cur_group != math_fence_group) { - tex_aux_append_math_fence_val((mathcodeval) { 0, 0, 0 }, (mathdictval) { 0, 0, 0 }, open_noad_subtype); + tex_aux_append_math_fence_val(tex_no_math_code(), tex_no_dict_code(), open_noad_subtype); } tex_aux_append_math_fence(fence, class); } static void tex_aux_append_math_char(mathcodeval mval, mathdictval dval, int automatic) { - if (mval.class_value == active_math_class_value) { - /*tex An active character is allowed here */ - tex_aux_scan_active_math_char(); - return; - } else { + if (tex_aux_scan_active_math_char(&mval, 2)) { + return; /* rescan pushed back token */ + } else { if (automatic && tex_math_has_class_option(mval.class_value, auto_inject_class_option)) { switch (mval.class_value) { case accent_noad_subtype: @@ -1923,10 +2020,9 @@ static void tex_aux_append_math_char(mathcodeval mval, mathdictval dval, int aut static void tex_aux_append_math_char_in_text(mathcodeval mval, mathdictval dval) { (void) dval; - if (mval.class_value == active_math_class_value) { - /*tex An active character is allowed here. But why in text mode too. */ - tex_aux_scan_active_math_char(); - } else { + if (tex_aux_scan_active_math_char(&mval, 3)) { + return; /* rescan pushed back token */ + } else { halfword p = tex_new_char_node(glyph_character_subtype, tex_fam_fnt(mval.family_value, text_size), mval.character_value, 1); /* todo: data */ tex_tail_append(p); } @@ -1942,8 +2038,8 @@ void tex_run_math_char_number(void) { Both |\char| and |\glyph| get the same treatment. Scanning can change |cur_chr| so we do that first. We no longer check for active here! */ - mathcodeval mval = { 0, 0, 0 }; - mathdictval dval = { 0, 0, 0 }; + mathcodeval mval = tex_no_math_code(); + mathdictval dval = tex_no_dict_code(); cur_chr = tex_scan_char_number(0); mval.character_value = cur_chr; mval.family_value = (short) cur_fam_par; @@ -2057,16 +2153,16 @@ int tex_scan_math_code_val(halfword code, mathcodeval *mval, mathdictval *dval) } void tex_run_text_math_char_number(void) { - mathcodeval mval = { 0, 0, 0 }; - mathdictval dval = { 0, 0, 0 }; + mathcodeval mval = tex_no_math_code(); + mathdictval dval = tex_no_dict_code(); if (tex_scan_math_code_val(cur_chr, &mval, &dval)) { tex_aux_append_math_char_in_text(mval, dval); } } void tex_run_math_math_char_number(void) { - mathcodeval mval = { 0, 0, 0 }; - mathdictval dval = { 0, 0, 0 }; + mathcodeval mval = tex_no_math_code(); + mathdictval dval = tex_no_dict_code(); if (tex_scan_math_code_val(cur_chr, &mval, &dval)) { tex_aux_append_math_char(mval, dval, 1); } @@ -2075,10 +2171,10 @@ void tex_run_math_math_char_number(void) { void tex_run_math_delimiter_number(void) { switch (cur_chr) { case math_delimiter_code: - tex_aux_append_math_char(tex_scan_delimiter_as_mathchar(tex_mathcode), (mathdictval) { 0, 0, 0 }, 0); + tex_aux_append_math_char(tex_scan_delimiter_as_mathchar(tex_mathcode), tex_no_dict_code(), 0); break; case math_udelimiter_code: - tex_aux_append_math_char(tex_scan_delimiter_as_mathchar(umath_mathcode), (mathdictval) { 0, 0, 0 }, 0); + tex_aux_append_math_char(tex_scan_delimiter_as_mathchar(umath_mathcode), tex_no_dict_code(), 0); break; } } @@ -2532,7 +2628,7 @@ void tex_run_math_radical(void) } break; case 's': case 'S': - switch (tex_scan_character("toTO", 0, 0, 0)) { + switch (tex_scan_character("itoITO", 0, 0, 0)) { case 't': case 'T': if (tex_scan_mandate_keyword("style", 2)) { switch (code) { @@ -2554,6 +2650,11 @@ void tex_run_math_radical(void) noad_source(radical) = tex_scan_int(0, NULL); } break; + case 'i': case 'I': + if (tex_scan_mandate_keyword("size", 2)) { + radical_size(radical) = tex_scan_int(0, NULL); + } + break; default: tex_aux_show_keyword_error("style|source"); goto DONE; @@ -3931,7 +4032,7 @@ void tex_run_math_fence(void) break; default: if (cur_group != math_fence_group) { - tex_aux_append_math_fence_val((mathcodeval) { 0, 0, 0 }, (mathdictval) { 0, 0, 0 }, open_noad_subtype); + tex_aux_append_math_fence_val(tex_no_math_code(), tex_no_dict_code(), open_noad_subtype); } switch (cur_group) { case math_fence_group: @@ -5578,7 +5679,7 @@ halfword tex_to_math_rules_parameter(halfword left, halfword right) void tex_set_default_math_codes(void) { - mathcodeval mval = { 0, 0, 0 }; + mathcodeval mval = tex_no_math_code(); /*tex This will remap old font families at runtime. */ mval.class_value = math_use_current_family_code; /*tex Upright math digts come from family 0. */ diff --git a/source/luametatex/source/tex/texmath.h b/source/luametatex/source/tex/texmath.h index a522008f3..79f40c13b 100644 --- a/source/luametatex/source/tex/texmath.h +++ b/source/luametatex/source/tex/texmath.h @@ -282,7 +282,7 @@ typedef enum math_atom_font_options { math_atom_math_font_option = 2, } math_atom_font_options; -inline int math_parameter_value_type(int n) +inline static int math_parameter_value_type(int n) { if (n < last_math_parameter) { return lmt_interface.math_parameter_values[n].type; @@ -563,7 +563,7 @@ extern scaled tex_get_math_x_parameter_default (int style, int param, scaled d extern scaled tex_get_math_y_parameter (int style, int param); extern scaled tex_get_math_y_parameter_checked (int style, int param); -extern scaled tex_get_math_y_parameter_default (int style, int paramm, scaled dflt); +extern scaled tex_get_math_y_parameter_default (int style, int param, scaled dflt); extern scaled tex_get_font_math_parameter (int font, int size, int param); extern scaled tex_get_font_math_x_parameter (int font, int size, int param); @@ -624,6 +624,9 @@ extern void tex_run_text_math_spec (void); extern void tex_set_default_math_codes (void); +extern int tex_check_active_math_char (int character); +extern int tex_pass_active_math_char (int character); + /*tex The runners in maincontrol: */ extern void tex_run_math_left_brace (void); diff --git a/source/luametatex/source/tex/texmathcodes.c b/source/luametatex/source/tex/texmathcodes.c index f19cde91c..33aac71ee 100644 --- a/source/luametatex/source/tex/texmathcodes.c +++ b/source/luametatex/source/tex/texmathcodes.c @@ -82,7 +82,7 @@ mathcodeval tex_mathchar_from_integer(int value, int extcode) mathcodeval tex_mathchar_from_spec(int value) { - mathcodeval mval = { 0, 0, 0 }; + mathcodeval mval = tex_no_math_code(); if (value) { mval.class_value = math_spec_class(value); mval.family_value = math_spec_family(value); @@ -165,7 +165,7 @@ void tex_set_math_code(int n, mathcodeval v, int level) mathcodeval tex_get_math_code(int n) { sa_tree_item item; - mathcodeval m = { 0, 0, 0 }; + mathcodeval m = tex_no_math_code(); sa_get_item_4(lmt_mathcode_state.mathcode_head, n, &item); if (item.uint_value == MATHCODEDEFAULT) { m.character_value = n; @@ -282,6 +282,14 @@ delcodeval tex_get_del_code(int n) return d; } +/*tex */ + +mathdictval tex_no_dict_code(void) +{ + return (mathdictval) { 0, 0, 0 }; +} + + /*tex This really only works for old-style delcodes! */ int tex_get_del_code_number(int n) diff --git a/source/luametatex/source/tex/texmathcodes.h b/source/luametatex/source/tex/texmathcodes.h index a45132171..8017c2c65 100644 --- a/source/luametatex/source/tex/texmathcodes.h +++ b/source/luametatex/source/tex/texmathcodes.h @@ -74,4 +74,6 @@ extern void tex_undump_math_codes (dumpstream f); extern void tex_free_math_codes (void); +extern mathdictval tex_no_dict_code (void); + # endif diff --git a/source/luametatex/source/tex/texmlist.c b/source/luametatex/source/tex/texmlist.c index 0ea1f82e8..b5412872a 100644 --- a/source/luametatex/source/tex/texmlist.c +++ b/source/luametatex/source/tex/texmlist.c @@ -222,7 +222,7 @@ typedef enum limits_modes { limits_horizontal_mode, // no limits } limits_modes; -inline void tex_math_wipe_kerns(kernset *kerns) { +inline static void tex_math_wipe_kerns(kernset *kerns) { if (kerns) { kerns->topright = 0; kerns->topleft = 0; @@ -239,7 +239,7 @@ inline void tex_math_wipe_kerns(kernset *kerns) { } } -inline void tex_math_copy_kerns(kernset *kerns, kernset *parent) { +inline static void tex_math_copy_kerns(kernset *kerns, kernset *parent) { if (kerns && parent) { kerns->topright = parent->topright; kerns->topleft = parent->topleft; @@ -511,7 +511,7 @@ static int tex_aux_math_followed_by_italic_kern(halfword current, const char *tr return 0; } -static inline int tex_aux_checked_left_kern_fnt_chr(halfword fnt, halfword chr, halfword state, halfword subtype) +inline static int tex_aux_checked_left_kern_fnt_chr(halfword fnt, halfword chr, halfword state, halfword subtype) { halfword top = 0; halfword bot = 0; @@ -532,7 +532,7 @@ static inline int tex_aux_checked_left_kern_fnt_chr(halfword fnt, halfword chr, } } -static inline int tex_aux_checked_left_kern(halfword list, halfword state, halfword subtype) +inline static int tex_aux_checked_left_kern(halfword list, halfword state, halfword subtype) { if (list && node_type(list) == glyph_node) { return tex_aux_checked_left_kern_fnt_chr(glyph_font(list), glyph_character(list), state, subtype); @@ -541,7 +541,7 @@ static inline int tex_aux_checked_left_kern(halfword list, halfword state, halfw } } -static inline int tex_aux_checked_right_kern_fnt_chr(halfword fnt, halfword chr, halfword state, halfword subtype) +inline static int tex_aux_checked_right_kern_fnt_chr(halfword fnt, halfword chr, halfword state, halfword subtype) { halfword top = 0; halfword bot = 0; @@ -562,7 +562,7 @@ static inline int tex_aux_checked_right_kern_fnt_chr(halfword fnt, halfword chr, } } -static inline int tex_aux_checked_right_kern(halfword list, halfword state, halfword subtype) +inline static int tex_aux_checked_right_kern(halfword list, halfword state, halfword subtype) { if (list && node_type(list) == glyph_node) { return tex_aux_checked_right_kern_fnt_chr(glyph_font(list), glyph_character(list), state, subtype); @@ -2472,6 +2472,12 @@ static void tex_aux_make_delimited_radical(halfword target, int style, int size, halfword total = height + depth; delimiterextremes extremes = { .tfont = null_font, .tchar = 0, .bfont = null_font, .bchar = 0, .height = 0, .depth = 0 }; noad_new_hlist(target) = null; + size += radical_size(target); + if (size < text_size) { + size = text_size; + } else if (size > script_script_size) { + size = script_script_size; + } delimiter = tex_aux_make_delimiter(target, delimiter, size, total, 0, style, 2, NULL, NULL, 0, has_noad_option_nooverflow(target), &extremes, depth); if (companion) { /*tex For now we assume symmetry and same height and depth! */ @@ -4771,7 +4777,7 @@ inline static scaled tex_aux_insert_italic_now(halfword target, halfword kernel, return italic; } -static inline int tex_aux_raise_prime_composed(halfword target) +inline static int tex_aux_raise_prime_composed(halfword target) { int mainclass = -1 ; /* maybe also mainclass */ @@ -4891,12 +4897,14 @@ static void tex_aux_make_scripts(halfword target, halfword kernel, scaled italic primedata.node = tex_aux_analyze_script(noad_prime(target), &primedata); maxleftkern = tex_aux_math_left_kern(glyph_font(kernel), glyph_character(kernel)); // maxrightkern = tex_aux_math_right_kern(glyph_font(kernel), glyph_character(kernel)); - prime_up = tex_get_math_y_parameter_default(style, math_parameter_prime_shift_drop, 0); - shift_up = tex_get_math_y_parameter_checked(style, math_parameter_superscript_shift_drop); - shift_down = tex_get_math_y_parameter_checked(style, math_parameter_subscript_shift_drop); - break; // fallthrough + prime_up = 0; + shift_up = 0; + shift_down = 0; + break; default: - kernelsize.ht -= supdrop; /* new */ + /*tex Used for optimizing accents. */ + kernelsize.ht -= supdrop; + /*tex These parameters are only applied in an assembly (and often some 0.5 .. 1.5 pt on 12pt). */ prime_up = kernelsize.ht - tex_get_math_y_parameter_default(style, math_parameter_prime_shift_drop, 0); shift_up = kernelsize.ht - tex_get_math_y_parameter_checked(style, math_parameter_superscript_shift_drop); shift_down = kernelsize.dp + tex_get_math_y_parameter_checked(style, math_parameter_subscript_shift_drop); @@ -5372,7 +5380,7 @@ static void tex_aux_make_scripts(halfword target, halfword kernel, scaled italic */ -// static inline int tex_aux_is_extensible(halfword result) +// inline static int tex_aux_is_extensible(halfword result) // { // if (result) { // switch (node_type(result)) { @@ -6119,7 +6127,7 @@ static halfword tex_aux_unroll_noad(halfword tail, halfword l, quarterword s) while (l) { halfword n = node_next(l); node_next(l) = null; - if (node_type(l) == hlist_node && (node_subtype(l) == s) && ! box_source_anchor(l)) { + if (node_type(l) == hlist_node && node_subtype(l) == s && ! box_source_anchor(l)) { if (box_list(l)) { tex_couple_nodes(tail, box_list(l)); tail = tex_tail_of_node_list(tail); @@ -7052,6 +7060,9 @@ static void tex_mlist_to_hlist_finalize_list(mliststate *state) } else { tex_couple_nodes(p, l); } + } else if ((current_subtype == open_noad_subtype || current_subtype == fenced_noad_subtype) && tex_math_has_class_option(fenced_noad_subtype, unpack_class_option)) { + /*tex tricky as we have an open subtype for spacing now. */ + p = tex_aux_unroll_noad(p, l, math_fence_list); } else if (has_noad_option_unpacklist(current) || tex_math_has_class_option(current_subtype, unpack_class_option)) { /*tex So here we only unpack a math list. */ p = tex_aux_unroll_noad(p, l, math_list_list); diff --git a/source/luametatex/source/tex/texnesting.c b/source/luametatex/source/tex/texnesting.c index d699d58fc..f281eee2e 100644 --- a/source/luametatex/source/tex/texnesting.c +++ b/source/luametatex/source/tex/texnesting.c @@ -182,7 +182,7 @@ nest_state_info lmt_nest_state = { .offset = 0, }, .shown_mode = 0, - .padding = 0, + .math_mode = 0, }; /*tex @@ -239,7 +239,8 @@ void tex_initialize_nesting(void) { lmt_nest_state.nest_data.ptr = 0; lmt_nest_state.nest_data.top = 0; - lmt_nest_state.shown_mode = 0; + // lmt_nest_state.shown_mode = 0; + // lmt_nest_state.math_mode = 0; cur_list.mode = vmode; cur_list.head = contribute_head; cur_list.tail = contribute_head; @@ -290,6 +291,8 @@ void tex_push_nest(void) { list_state_record *top = &lmt_nest_state.nest[lmt_nest_state.nest_data.ptr]; lmt_nest_state.nest_data.ptr += 1; + // lmt_nest_state.shown_mode = 0; // needs checking + lmt_nest_state.math_mode = 0; if (tex_aux_room_on_nest_stack()) { cur_list.mode = top->mode; cur_list.head = tex_new_temp_node(); diff --git a/source/luametatex/source/tex/texnesting.h b/source/luametatex/source/tex/texnesting.h index f940094a0..91251eee0 100644 --- a/source/luametatex/source/tex/texnesting.h +++ b/source/luametatex/source/tex/texnesting.h @@ -30,7 +30,7 @@ typedef struct nest_state_info { list_state_record *nest; memory_data nest_data; int shown_mode; - int padding; + int math_mode; } nest_state_info; extern nest_state_info lmt_nest_state; diff --git a/source/luametatex/source/tex/texnodes.c b/source/luametatex/source/tex/texnodes.c index 0134eb419..ab2601a43 100644 --- a/source/luametatex/source/tex/texnodes.c +++ b/source/luametatex/source/tex/texnodes.c @@ -1800,6 +1800,24 @@ halfword tex_list_node_mem_usage(void) (actually for each node tyep I guess). */ +extern void tex_change_attribute_register(halfword a, halfword id, halfword value) +{ + if (eq_value(id) != value) { + if (is_global(a)) { + int i; + for (i = (lmt_save_state.save_stack_data.ptr - 1); i >= 0; i--) { + if (save_type(i) == attribute_list_save_type) { + delete_attribute_reference(save_value(i)); + save_value(i) = attribute_cache_disabled; + } + } + } else { + delete_attribute_reference(current_attribute_state); + } + set_current_attribute_state(attribute_cache_disabled); + } +} + inline static halfword tex_aux_new_attribute_list_node(halfword count) { halfword r = tex_get_node(attribute_node_size); diff --git a/source/luametatex/source/tex/texnodes.h b/source/luametatex/source/tex/texnodes.h index 9e24ada27..50dbeaa97 100644 --- a/source/luametatex/source/tex/texnodes.h +++ b/source/luametatex/source/tex/texnodes.h @@ -130,8 +130,9 @@ typedef enum node_types { align_stack_node, noad_state_node, if_node, - unhyphenated_node, /*tex These are both active nodes. */ - hyphenated_node, /*tex These are both active nodes. */ + /*tex These two are active nodes. */ + unhyphenated_node, + hyphenated_node, delta_node, passive_node, } node_types; @@ -988,17 +989,17 @@ typedef enum rule_codes { # define first_rule_code normal_rule_code # define last_rule_code strut_rule_code -# define rule_node_size 7 -# define rule_width(a) vlink(a,2) -# define rule_x_offset(a) vinfo(a,2) -# define rule_depth(a) vlink(a,3) -# define rule_y_offset(a) vinfo(a,3) -# define rule_height(a) vlink(a,4) -# define rule_data(a) vinfo(a,4) -# define rule_left(a) vinfo(a,5) -# define rule_right(a) vlink(a,5) -# define rule_font(a) vinfo(a,6) -# define rule_character(a) vlink(a,6) +# define rule_node_size 7 +# define rule_width(a) vlink(a,2) +# define rule_x_offset(a) vinfo(a,2) +# define rule_depth(a) vlink(a,3) +# define rule_y_offset(a) vinfo(a,3) +# define rule_height(a) vlink(a,4) +# define rule_data(a) vinfo(a,4) +# define rule_left(a) vinfo(a,5) +# define rule_right(a) vlink(a,5) +# define rule_font(a) vinfo(a,6) +# define rule_character(a) vlink(a,6) # define rule_total(a) (rule_height(a) + rule_depth(a)) @@ -1057,36 +1058,36 @@ typedef enum rule_codes { */ -//define glyph_node_size 12 -# define glyph_node_size 13 -# define glyph_character(a) vinfo(a,2) -# define glyph_font(a) vlink(a,2) -# define glyph_data(a) vinfo(a,3) /*tex We had that unused, so now it's like an attribute. */ -# define glyph_state(a) vlink(a,3) /*tex A user field (can be handy in \LUA). */ -# define glyph_language(a) vinfo(a,4) -# define glyph_script(a) vlink(a,4) -# define glyph_options(a) vinfo(a,5) -# define glyph_hyphenate(a) vlink(a,5) -# define glyph_protected(a) vinfo00(a,6) -# define glyph_lhmin(a) vinfo01(a,6) -# define glyph_rhmin(a) vinfo02(a,6) -# define glyph_discpart(a) vinfo03(a,6) -# define glyph_expansion(a) vlink(a,6) -# define glyph_x_scale(a) vinfo(a,7) -# define glyph_y_scale(a) vlink(a,7) -# define glyph_scale(a) vinfo(a,8) -# define glyph_raise(a) vlink(a,8) -# define glyph_left(a) vinfo(a,9) -# define glyph_right(a) vlink(a,9) -# define glyph_x_offset(a) vinfo(a,10) -# define glyph_y_offset(a) vlink(a,10) -//define glyph_input_file(a) vinfo(a,11) /* aka glyph_synctex_tag */ -//define glyph_input_line(a) vlink(a,11) /* aka glyph_synctex_line */ -# define glyph_properties(a) vinfo0(a,11) -# define glyph_group(a) vinfo1(a,11) -# define glyph_index(a) vlink(a,11) -# define glyph_input_file(a) vinfo(a,12) -# define glyph_input_line(a) vlink(a,12) +//define glyph_node_size 12 +# define glyph_node_size 13 +# define glyph_character(a) vinfo(a,2) +# define glyph_font(a) vlink(a,2) +# define glyph_data(a) vinfo(a,3) /*tex We had that unused, so now it's like an attribute. */ +# define glyph_state(a) vlink(a,3) /*tex A user field (can be handy in \LUA). */ +# define glyph_language(a) vinfo(a,4) +# define glyph_script(a) vlink(a,4) +# define glyph_options(a) vinfo(a,5) +# define glyph_hyphenate(a) vlink(a,5) +# define glyph_protected(a) vinfo00(a,6) +# define glyph_lhmin(a) vinfo01(a,6) +# define glyph_rhmin(a) vinfo02(a,6) +# define glyph_discpart(a) vinfo03(a,6) +# define glyph_expansion(a) vlink(a,6) +# define glyph_x_scale(a) vinfo(a,7) +# define glyph_y_scale(a) vlink(a,7) +# define glyph_scale(a) vinfo(a,8) +# define glyph_raise(a) vlink(a,8) +# define glyph_left(a) vinfo(a,9) +# define glyph_right(a) vlink(a,9) +# define glyph_x_offset(a) vinfo(a,10) +# define glyph_y_offset(a) vlink(a,10) +//define glyph_input_file(a) vinfo(a,11) /* aka glyph_synctex_tag */ +//define glyph_input_line(a) vlink(a,11) /* aka glyph_synctex_line */ +# define glyph_properties(a) vinfo0(a,11) +# define glyph_group(a) vinfo1(a,11) +# define glyph_index(a) vlink(a,11) +# define glyph_input_file(a) vinfo(a,12) +# define glyph_input_line(a) vlink(a,12) # define get_glyph_data(a) ((halfword) glyph_data(a)) # define get_glyph_state(a) ((halfword) glyph_state(a)) @@ -1477,6 +1478,10 @@ typedef enum specification_options { # define specification_n(a,n) (specification_repeat(a) ? ((n - 1) % specification_count(a) + 1) : (n > specification_count(a) ? specification_count(a) : n)) +/* interesting: 1Kb smaller bin: */ + +// inline static halfword specification_n(halfword a, halfword n) { return specification_repeat(a) ? ((n - 1) % specification_count(a) + 1) : (n > specification_count(a) ? specification_count(a) : n); } + extern void tex_null_specification_list (halfword a); extern void tex_new_specification_list (halfword a, halfword n, halfword o); extern void tex_dispose_specification_list (halfword a); @@ -1596,15 +1601,15 @@ typedef enum simple_choice_subtypes { */ -# define noad_state_node_size 6 -# define noad_state_topright(a) vlink(a,2) -# define noad_state_bottomright(a) vinfo(a,2) -# define noad_state_topleft(a) vlink(a,3) -# define noad_state_bottomleft(a) vinfo(a,3) -# define noad_state_height(a) vlink(a,4) -# define noad_state_depth(a) vinfo(a,4) -# define noad_state_toptotal(a) vlink(a,5) -# define noad_state_bottomtotal(a) vinfo(a,5) +# define noad_state_node_size 6 +# define noad_state_topright(a) vlink(a,2) +# define noad_state_bottomright(a) vinfo(a,2) +# define noad_state_topleft(a) vlink(a,3) +# define noad_state_bottomleft(a) vinfo(a,3) +# define noad_state_height(a) vlink(a,4) +# define noad_state_depth(a) vinfo(a,4) +# define noad_state_toptotal(a) vlink(a,5) +# define noad_state_bottomtotal(a) vinfo(a,5) # define noad_size 14 # define noad_new_hlist(a) vlink(a,2) /*tex the translation of an mlist; a bit confusing name */ @@ -1754,7 +1759,7 @@ inline static void tex_add_noad_option (halfword a, halfword r) { noad_option inline static void tex_remove_noad_option (halfword a, halfword r) { noad_options(a) &= ~(r | noad_options(a)); } inline static int tex_has_noad_option (halfword a, halfword r) { return (noad_options(a) & r) == r; } -inline int has_noad_no_script_option(halfword n, halfword option) +inline static int has_noad_no_script_option(halfword n, halfword option) { switch (node_type(n)) { case simple_noad: @@ -1880,13 +1885,13 @@ typedef enum math_modifier_types { /*tex accent noads: todo, left and right offsets and options */ -# define accent_noad_size noad_size -# define accent_top_character noad_extra_1 /*tex the |top_accent_chr| field of an accent noad */ -# define accent_bottom_character noad_extra_2 /*tex the |bot_accent_chr| field of an accent noad */ -# define accent_middle_character noad_extra_3 /*tex the |overlay_accent_chr| field of an accent noad */ -# define accent_fraction noad_extra_4 -# define accent_top_overshoot noad_extra_5 -# define accent_bot_overshoot noad_extra_6 +# define accent_noad_size noad_size +# define accent_top_character noad_extra_1 /*tex the |top_accent_chr| field of an accent noad */ +# define accent_bottom_character noad_extra_2 /*tex the |bot_accent_chr| field of an accent noad */ +# define accent_middle_character noad_extra_3 /*tex the |overlay_accent_chr| field of an accent noad */ +# define accent_fraction noad_extra_4 +# define accent_top_overshoot noad_extra_5 +# define accent_bot_overshoot noad_extra_6 typedef enum math_accent_subtypes { bothflexible_accent_subtype, @@ -1965,6 +1970,7 @@ typedef enum fraction_subtypes { # define radical_degree noad_extra_1 # define radical_left_delimiter noad_extra_2 # define radical_right_delimiter noad_extra_3 +# define radical_size noad_extra_4 # define radical_height noad_extra_5 # define radical_depth noad_extra_6 @@ -2513,9 +2519,10 @@ inline static void tex_attach_attribute_list_attribute(halfword target, halfword # define attach_current_attribute_list tex_build_attribute_list /* (target) */ # define set_current_attribute_state(v) do { \ - current_attribute_state = v; \ + current_attribute_state = v; \ } while (0) +/* # define change_attribute_register(a,id,value) do { \ if (eq_value(id) != value) { \ if (is_global(a)) { \ @@ -2532,6 +2539,9 @@ inline static void tex_attach_attribute_list_attribute(halfword target, halfword set_current_attribute_state(attribute_cache_disabled); \ } \ } while (0) +*/ + +extern void tex_change_attribute_register(halfword a, halfword id, halfword value); # define save_attribute_state_before() do { \ halfword c = current_attribute_state; \ @@ -2621,8 +2631,8 @@ typedef enum glue_signs { # define normal_glue_multiplier 0.0 -inline halfword tex_checked_glue_sign (halfword sign) { return ((sign < min_glue_sign ) || (sign > max_glue_sign )) ? normal_glue_sign : sign ; } -inline halfword tex_checked_glue_order (halfword order) { return ((order < min_glue_order) || (order > max_glue_order)) ? normal_glue_order : order; } +inline static halfword tex_checked_glue_sign (halfword sign) { return ((sign < min_glue_sign ) || (sign > max_glue_sign )) ? normal_glue_sign : sign ; } +inline static halfword tex_checked_glue_order (halfword order) { return ((order < min_glue_order) || (order > max_glue_order)) ? normal_glue_order : order; } /*tex These are reserved nodes that sit at the start of main memory. We could actually just allocate diff --git a/source/luametatex/source/tex/texprimitive.c b/source/luametatex/source/tex/texprimitive.c index d5f2b0927..9875e06dd 100644 --- a/source/luametatex/source/tex/texprimitive.c +++ b/source/luametatex/source/tex/texprimitive.c @@ -743,8 +743,10 @@ void tex_print_cmd_chr(singleword cmd, halfword chr) case other_char_cmd: tex_aux_print_chr_cmd("the", cmd, chr); break; - /* case active_char_cmd: + tex_aux_print_chr_cmd("active", cmd, chr); + break; + /* case comment_cmd: case invalid_char_cmd: break; diff --git a/source/luametatex/source/tex/texprinting.h b/source/luametatex/source/tex/texprinting.h index b323b1ae5..5e311a0bb 100644 --- a/source/luametatex/source/tex/texprinting.h +++ b/source/luametatex/source/tex/texprinting.h @@ -105,12 +105,11 @@ inline static int tex_single_letter(strnumber s) inline static int tex_is_active_cs(strnumber s) { if (s && str_length(s) > 3) { - const unsigned char *ss = str_string(s); - return (ss[0] == 0xEF) && (ss[1] == 0xBF) && (ss[2] == 0xBF); + const unsigned char *ss = str_string(s); // or signed and active_character ... + return (ss[0] == active_first) && (ss[1] == active_second) && (ss[2] == active_third); } else { return 0; } } -# define active_cs_value(A) aux_str2uni((str_string((A))+3)) # endif diff --git a/source/luametatex/source/tex/texscanning.c b/source/luametatex/source/tex/texscanning.c index 5480910dc..3c9859de9 100644 --- a/source/luametatex/source/tex/texscanning.c +++ b/source/luametatex/source/tex/texscanning.c @@ -11,7 +11,7 @@ static void tex_aux_scan_expression (int level); A helper. */ -inline void tex_push_back(halfword tok, halfword cmd, halfword chr) +inline static void tex_push_back(halfword tok, halfword cmd, halfword chr) { if (cmd != spacer_cmd && tok != deep_frozen_relax_token && ! (cmd == relax_cmd && chr == no_relax_code)) { tex_back_input(tok); @@ -670,7 +670,7 @@ static int tex_aux_set_cur_val_by_some_cmd(int code) case math_char_slot_code: /* we actually need two commands or we need to look ahead */ { - mathcodeval mval = { 0, 0, 0 }; + mathcodeval mval = tex_no_math_code(); mathdictval dval = { 0, 0, 0 }; if (tex_scan_math_cmd_val(&mval, &dval)) { switch (code) { @@ -1059,6 +1059,9 @@ static void tex_aux_set_cur_val_by_define_char_cmd(int chr) case hmcode_charcode: chr = tex_get_hm_code(index); break; + case amcode_charcode: + chr = tex_get_am_code(index); + break; case mathcode_charcode: case extmathcode_charcode: chr = tex_get_math_code_number(index); @@ -1820,7 +1823,7 @@ halfword tex_scan_math_index_number (void) { return tex_ halfword tex_scan_math_discretionary_number (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_math_discretionary, "Math discretionary"); } singleword tex_scan_box_index (void) { return (singleword) tex_aux_scan_limited_int(0, 0, max_box_index, "Box index"); } singleword tex_scan_box_axis (void) { return (singleword) tex_aux_scan_limited_int(0, 0, max_box_axis, "Box axis"); } -halfword tex_scan_category_code (void) { return tex_aux_scan_limited_int(0, 0, max_category_code,"Category code"); } +halfword tex_scan_category_code (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_category_code,"Category code"); } halfword tex_scan_function_reference (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_function_reference, "Function reference"); } halfword tex_scan_bytecode_reference (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, 0, max_bytecode_index, "Bytecode reference"); } halfword tex_scan_limited_scale (int optional_equal) { return tex_aux_scan_limited_int(optional_equal, -max_limited_scale, max_limited_scale, "Limited scale"); } diff --git a/source/luametatex/source/tex/texscanning.h b/source/luametatex/source/tex/texscanning.h index 90897bf54..303143587 100644 --- a/source/luametatex/source/tex/texscanning.h +++ b/source/luametatex/source/tex/texscanning.h @@ -120,7 +120,7 @@ extern halfword tex_scan_math_properties_number (void); extern halfword tex_scan_math_group_number (void); extern halfword tex_scan_math_index_number (void); extern halfword tex_scan_math_discretionary_number (int optional_equal); -extern halfword tex_scan_category_code (void); +extern halfword tex_scan_category_code (int optional_equal); extern singleword tex_scan_box_index (void); /*tex For local boxes: small for now! */ extern singleword tex_scan_box_axis (void); extern halfword tex_scan_function_reference (int optional_equal); diff --git a/source/luametatex/source/tex/texstringpool.c b/source/luametatex/source/tex/texstringpool.c index fd93758b5..ffe4177bf 100644 --- a/source/luametatex/source/tex/texstringpool.c +++ b/source/luametatex/source/tex/texstringpool.c @@ -82,7 +82,7 @@ string_pool_info lmt_string_pool_state = { # define initial_temp_string_slots 256 # define reserved_temp_string_slots 2 -static inline void tex_aux_increment_pool_string(int n) +inline static void tex_aux_increment_pool_string(int n) { lmt_string_pool_state.string_body_data.allocated += n; if (lmt_string_pool_state.string_body_data.allocated > lmt_string_pool_state.string_body_data.size) { @@ -90,7 +90,7 @@ static inline void tex_aux_increment_pool_string(int n) } } -static inline void tex_aux_decrement_pool_string(int n) +inline static void tex_aux_decrement_pool_string(int n) { lmt_string_pool_state.string_body_data.allocated -= n; } diff --git a/source/luametatex/source/tex/texstringpool.h b/source/luametatex/source/tex/texstringpool.h index 7302e7fb4..a15b9fad5 100644 --- a/source/luametatex/source/tex/texstringpool.h +++ b/source/luametatex/source/tex/texstringpool.h @@ -76,37 +76,37 @@ extern string_pool_info lmt_string_pool_state; /*tex Forget the last character in the pool. */ -inline void tex_flush_char(void) { --lmt_string_pool_state.string_temp_top; } - -extern strnumber tex_make_string (void); -extern strnumber tex_push_string (const unsigned char *s, int l); -extern char *tex_take_string (int *len); -extern int tex_str_eq_buf (strnumber s, int k, int n); -extern int tex_str_eq_str (strnumber s, strnumber t); -extern int tex_str_eq_cstr (strnumber s, const char *, size_t); -extern int tex_get_strings_started (void); -extern void tex_reset_cur_string (void); -/* strnumber tex_search_string (strnumber search); */ -/* int tex_used_strings (void); */ -extern strnumber tex_maketexstring (const char *s); -extern strnumber tex_maketexlstring (const char *s, size_t); -extern void tex_append_char (unsigned char c); -extern void tex_append_string (const unsigned char *s, unsigned l); -extern char *tex_makecstring (int s, int *allocated); -extern char *tex_makeclstring (int s, size_t *len); -extern void tex_dump_string_pool (dumpstream f); -extern void tex_undump_string_pool (dumpstream f); -extern void tex_initialize_string_pool (void); -extern void tex_initialize_string_mem (void); -extern void tex_flush_str (strnumber s); -extern strnumber tex_save_cur_string (void); -extern void tex_restore_cur_string (strnumber u); - -/* void tex_increment_pool_string (int n); */ -/* void tex_decrement_pool_string (int n); */ - -extern void tex_compact_string_pool (void); - -inline char *tex_to_cstring (int s) { return str_length(s) > 0 ? (char *) str_string(s) : ""; } +inline static void tex_flush_char(void) { --lmt_string_pool_state.string_temp_top; } + +extern strnumber tex_make_string (void); +extern strnumber tex_push_string (const unsigned char *s, int l); +extern char *tex_take_string (int *len); +extern int tex_str_eq_buf (strnumber s, int k, int n); +extern int tex_str_eq_str (strnumber s, strnumber t); +extern int tex_str_eq_cstr (strnumber s, const char *, size_t); +extern int tex_get_strings_started (void); +extern void tex_reset_cur_string (void); +/* strnumber tex_search_string (strnumber search); */ +/* int tex_used_strings (void); */ +extern strnumber tex_maketexstring (const char *s); +extern strnumber tex_maketexlstring (const char *s, size_t); +extern void tex_append_char (unsigned char c); +extern void tex_append_string (const unsigned char *s, unsigned l); +extern char *tex_makecstring (int s, int *allocated); +extern char *tex_makeclstring (int s, size_t *len); +extern void tex_dump_string_pool (dumpstream f); +extern void tex_undump_string_pool (dumpstream f); +extern void tex_initialize_string_pool (void); +extern void tex_initialize_string_mem (void); +extern void tex_flush_str (strnumber s); +extern strnumber tex_save_cur_string (void); +extern void tex_restore_cur_string (strnumber u); + +/* void tex_increment_pool_string (int n); */ +/* void tex_decrement_pool_string (int n); */ + +extern void tex_compact_string_pool (void); + +inline static char *tex_to_cstring (int s) { return str_length(s) > 0 ? (char *) str_string(s) : ""; } # endif diff --git a/source/luametatex/source/tex/textextcodes.c b/source/luametatex/source/tex/textextcodes.c index 39fc258c7..2fef9857f 100644 --- a/source/luametatex/source/tex/textextcodes.c +++ b/source/luametatex/source/tex/textextcodes.c @@ -218,12 +218,16 @@ static void tex_aux_free_catcodes(void) # define HMCODESTACK 8 # define HMCODEDEFAULT 0 +# define AMCODESTACK 8 +# define AMCODEDEFAULT 0 + typedef struct luscode_state_info { sa_tree uccode_head; sa_tree lccode_head; sa_tree sfcode_head; sa_tree hccode_head; sa_tree hmcode_head; + sa_tree amcode_head; } luscode_state_info; static luscode_state_info lmt_luscode_state = { @@ -231,7 +235,8 @@ static luscode_state_info lmt_luscode_state = { .lccode_head = NULL, .sfcode_head = NULL, .hccode_head = NULL, - .hmcode_head = NULL + .hmcode_head = NULL, + .amcode_head = NULL }; void tex_set_lc_code(int n, halfword v, int gl) @@ -445,6 +450,45 @@ static void tex_aux_free_hmcodes(void) sa_destroy_tree(lmt_luscode_state.hmcode_head); } +/*tex Experiment. */ + + +void tex_set_am_code(int n, halfword v, int gl) +{ + sa_set_item_1(lmt_luscode_state.amcode_head, n, v, gl); +} + +halfword tex_get_am_code(int n) +{ + return sa_return_item_1(lmt_luscode_state.amcode_head, n); +} + +static void tex_aux_unsave_amcodes(int gl) +{ + sa_restore_stack(lmt_luscode_state.amcode_head, gl); +} + +static void tex_aux_initialize_amcodes(void) +{ + sa_tree_item item = { .int_value = AMCODEDEFAULT }; + lmt_luscode_state.amcode_head = sa_new_tree(AMCODESTACK, 1, item); +} + +static void tex_aux_dump_amcodes(dumpstream f) +{ + sa_dump_tree(f, lmt_luscode_state.amcode_head); +} + +static void tex_aux_undump_amcodes(dumpstream f) +{ + lmt_luscode_state.amcode_head = sa_undump_tree(f); +} + +static void tex_aux_free_amcodes(void) +{ + sa_destroy_tree(lmt_luscode_state.amcode_head); +} + /*tex The hyphenation codes are indeed stored in a tree and are used instead of lowercase codes when @@ -544,6 +588,7 @@ void tex_unsave_text_codes(int grouplevel) tex_aux_unsave_sfcodes(grouplevel); tex_aux_unsave_hccodes(grouplevel); tex_aux_unsave_hmcodes(grouplevel); + tex_aux_unsave_amcodes(grouplevel); } void tex_initialize_text_codes(void) @@ -554,6 +599,7 @@ void tex_initialize_text_codes(void) tex_aux_initialize_sfcodes(); tex_aux_initialize_hccodes(); tex_aux_initialize_hmcodes(); + tex_aux_initialize_amcodes(); /* initializehjcodes(); */ } @@ -565,6 +611,7 @@ void tex_free_text_codes(void) tex_aux_free_sfcodes(); tex_aux_free_hccodes(); tex_aux_free_hmcodes(); + tex_aux_free_amcodes(); /* freehjcodes(); */ } @@ -576,6 +623,7 @@ void tex_dump_text_codes(dumpstream f) tex_aux_dump_sfcodes(f); tex_aux_dump_hccodes(f); tex_aux_dump_hmcodes(f); + tex_aux_dump_amcodes(f); /* dumphjcodes(f); */ } @@ -587,6 +635,7 @@ void tex_undump_text_codes(dumpstream f) tex_aux_undump_sfcodes(f); tex_aux_undump_hccodes(f); tex_aux_undump_hmcodes(f); + tex_aux_undump_amcodes(f); /* undumphjcodes(f); */ } diff --git a/source/luametatex/source/tex/textextcodes.h b/source/luametatex/source/tex/textextcodes.h index 476f0f03e..d9fd7fbe4 100644 --- a/source/luametatex/source/tex/textextcodes.h +++ b/source/luametatex/source/tex/textextcodes.h @@ -29,6 +29,8 @@ extern void tex_set_hc_code (int n, halfword v, int gl); extern halfword tex_get_hc_code (int n); extern void tex_set_hm_code (int n, halfword v, int gl); extern halfword tex_get_hm_code (int n); +extern void tex_set_am_code (int n, halfword v, int gl); +extern halfword tex_get_am_code (int n); extern void tex_set_hj_code (int l, int n, halfword v, int gl); extern halfword tex_get_hj_code (int l, int n); extern void tex_initialize_xx_codes (void); diff --git a/source/luametatex/source/tex/textoken.c b/source/luametatex/source/tex/textoken.c index 7dd9c888b..ba457491a 100644 --- a/source/luametatex/source/tex/textoken.c +++ b/source/luametatex/source/tex/textoken.c @@ -573,7 +573,7 @@ static const char *tex_aux_special_cmd_string(halfword cmd, halfword chr, const case begin_local_cmd : return "[[special cmd: begin local call]]"; case end_local_cmd : return "[[special cmd: end local call]]"; // case prefix_cmd : return "[[special cmd: enforced]]"; - case prefix_cmd : return "\\always"; + case prefix_cmd : return "\\always "; default : printf("[[unknown cmd: (%i,%i)]\n", cmd, chr); return unknown; } } @@ -625,6 +625,7 @@ halfword tex_show_token_list(halfword p, halfword q, int l, int asis) case spacer_cmd: case letter_cmd: case other_char_cmd: + case active_char_cmd: /* new */ case ignore_cmd: /* new */ tex_print_tex_str(chr); break; @@ -691,7 +692,7 @@ halfword tex_show_token_list(halfword p, halfword q, int l, int asis) } while (0) */ -inline halfword get_unichar_from_buffer(int *b) +inline static halfword get_unichar_from_buffer(int *b) { halfword a = (halfword) ((const unsigned char) *(lmt_fileio_state.io_buffer + *b)); if (a <= 0x80) { @@ -1032,14 +1033,13 @@ int tex_scan_keyword_case_sensitive(const char *s) halfword tex_active_to_cs(int c, int force) { halfword cs = -1; - if (c > 0) { - /*tex This is not that efficient: we can make a helper that doesn't use an alloc. */ - char utfbytes[8] = { '\xEF', '\xBF', '\xBF', 0 }; + if (c >= 0 && c <= max_character_code) { + char utfbytes[8] = { active_character_first, active_character_second, active_character_third, 0 }; aux_uni2string((char *) &utfbytes[3], c); cs = tex_string_locate(utfbytes, (size_t) utf8_size(c) + 3, force); } if (cs < 0) { - cs = tex_string_locate("\xEF\xBF\xBF", 4, force); /*tex Including the zero sentinel. */ + cs = tex_string_locate(active_character_unknown, 4, force); /*tex Including the zero sentinel. */ } return cs; } @@ -1195,10 +1195,18 @@ static int tex_aux_get_next_file(void) case mid_line_state + active_char_cmd: case new_line_state + active_char_cmd: case skip_blanks_state + active_char_cmd: - /*tex Process an active-character. */ - cur_cs = tex_active_to_cs(cur_chr, ! lmt_hash_state.no_new_cs); - cur_cmd = eq_type(cur_cs); - cur_chr = eq_value(cur_cs); + /*tex Process an active-character. */ + if ((lmt_input_state.scanner_status == scanner_is_tolerant || lmt_input_state.scanner_status == scanner_is_matching) && tex_pass_active_math_char(cur_chr)) { + /*tex We need to intercept a delimiter in arguments. */ + } else if ((lmt_input_state.scanner_status == scanner_is_defining || lmt_input_state.scanner_status == scanner_is_absorbing) && tex_pass_active_math_char(cur_chr)) { + /*tex We are storing stuff in a token list or macro body. */ + } else if ((cur_mode == mmode || lmt_nest_state.math_mode) && tex_check_active_math_char(cur_chr)) { + /*tex We have an intercept. */ + } else { + cur_cs = tex_active_to_cs(cur_chr, ! lmt_hash_state.no_new_cs); + cur_cmd = eq_type(cur_cs); + cur_chr = eq_value(cur_cs); + } lmt_input_state.cur_input.state = mid_line_state; break; case mid_line_state + superscript_cmd: @@ -2730,6 +2738,7 @@ void tex_run_convert_tokens(halfword code) break; } case cs_string_code: + case cs_active_code: { int saved_selector; int saved_scanner_status = lmt_input_state.scanner_status; @@ -2737,7 +2746,18 @@ void tex_run_convert_tokens(halfword code) tex_get_token(); lmt_input_state.scanner_status = saved_scanner_status; push_selector; - if (cur_cs) { + if (code == cs_active_code) { + // tex_print_char(active_first); + // tex_print_char(active_second); + // tex_print_char(active_third); + tex_print_str(active_character_namespace); + if (cur_cmd == active_char_cmd) { + tex_print_char(cur_chr); + } else { + /*tex So anything else will just inject the hash (abstraction, saves a command). */ + tex_back_input(cur_tok); + } + } else if (cur_cs) { tex_print_cs_name(cur_cs); } else { tex_print_tex_str(cur_chr); @@ -3122,6 +3142,7 @@ char *tex_tokenlist_to_tstring(int pp, int inhibit_par, int *siz, int skippreamb case spacer_cmd: case letter_cmd: case other_char_cmd: + case active_char_cmd: if (! skip) { tex_aux_append_uchar_to_buffer(chr); } @@ -3335,7 +3356,7 @@ void tex_set_tex_attribute_register(int j, halfword v, int flags, int internal) if (j > lmt_node_memory_state.max_used_attribute) { lmt_node_memory_state.max_used_attribute = j; } - change_attribute_register(flags, register_attribute_location(j), v); + tex_change_attribute_register(flags, register_attribute_location(j), v); tex_word_define(flags, internal ? internal_attribute_location(j) : register_attribute_location(j), v); } diff --git a/source/luametatex/source/tex/textoken.h b/source/luametatex/source/tex/textoken.h index ad67dfcb5..843304405 100644 --- a/source/luametatex/source/tex/textoken.h +++ b/source/luametatex/source/tex/textoken.h @@ -390,6 +390,43 @@ extern halfword tex_copy_token_list (halfword h, halfword *t); extern halfword tex_parse_str_to_tok (halfword head, halfword *tail, halfword ct, const char *str, size_t lstr, int option); -inline int tex_valid_token (int t) { return ((t >= 0) && (t <= (int) lmt_token_memory_state.tokens_data.top)); } +inline static int tex_valid_token (int t) { return ((t >= 0) && (t <= (int) lmt_token_memory_state.tokens_data.top)); } + +/*tex + + This is also a sort of documentation. Active characters are stored in the hash using a prefix + which assumes that users don't use that one. So far we've seen no clashes which is due to the + fact that the namespace prefix U+FFFF is an invalid \UNICODE\ character and it's kind of hard + to get that one into the input anyway. + + The replacement character U+FFFD is a kind of fallback when we run into some troubles or when + a control sequence is expected (and undefined is unacceptable). + + U+FFFD REPLACEMENT CHARACTER + U+FFFE NOT A CHARACTER + U+FFFF NOT A CHARACTER + + I experimented with a namespace character (catcodtable id) as fourth character but there are + some unwanted side effects, for instance in testing an active character as separator (in + arguments) so that code waa eventually removed. I might come back to this one day (active + characters in the catcode regime namespace). + +*/ + +# define utf_fffd_string "\xEF\xBF\xBD" /* U+FFFD : 65533 */ + +# define active_character_namespace "\xEF\xBF\xBF" /* U+FFFF : 65535 */ + +# define active_character_first '\xEF' +# define active_character_second '\xBF' +# define active_character_third '\xBF' + +# define active_first 0xEF +# define active_second 0xBF +# define active_third 0xBF + +# define active_character_unknown "\xEF\xBF\xBD" /* utf_fffd_string */ + +# define active_cs_value(A) aux_str2uni(str_string(A)+3) # endif -- cgit v1.2.3